/* * tcprobe.c * * Copyright (C) Thomas Oestreich - June 2001 * updated by * Francesco Romani - April 206 * * This file is part of transcode, a video stream processing tool * * transcode is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * transcode is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "transcode.h" #include "tcinfo.h" #include "libtc/libtc.h" #include "libtc/iodir.h" #include "libtc/xio.h" #include "libtc/ratiocodes.h" #include "ioaux.h" #include "tc.h" #include "demuxer.h" #include "dvd_reader.h" #include "x11source.h" #include #include #include #include #include #define EXE "tcprobe" int verbose = TC_INFO; int bitrate = ABITRATE; int binary_dump = 0; void import_exit(int code) { if (verbose >= TC_DEBUG) { tc_log_msg(EXE, "(pid=%d) exit (code %d)", (int) getpid(), code); } exit(code); } /*************************************************************************/ /* * enc_bitrate: Print bitrate information about the source data. * * Parameters: * frames: Number of frames in the source. * fps: Frames per second of the source. * abitrate: Audio bitrate (bits per second). * discsize: User-specified disc size in bytes, or 0 for none. * Return value: * None. * Notes: * This function is copied from tcscan.c. Ideally, tcprobe should * only print basic source information, and this extended information * should be handled by tcscan (or alternatively, tcscan should be * merged into tcprobe). */ static void enc_bitrate(long frames, double fps, int abitrate, double discsize) { static const int defsize[] = { 650, 700, 1300, 1400 }; double audiosize, videosize, vbitrate; long time; if (frames <= 0 || fps <= 0.0) { return; } time = frames / fps; audiosize = (double)abitrate/8 * time; /* Print basic source information */ printf("V: %ld frames, %ld sec @ %.3f fps\n", frames, time, fps); printf("A: %.2f MB @ %d kbps\n", audiosize/(1024*1024), abitrate/1000); /* Print recommended bitrates for user-specified or default disc sizes */ if (discsize) { videosize = discsize - audiosize; vbitrate = videosize / time; printf("USER CDSIZE: %4d MB | V: %6.1f MB @ %.1f kbps\n", (int)floor(discsize/(1024*1024)), videosize/(1024*1024), vbitrate); } else { int i; for (i = 0; i < sizeof(defsize) / sizeof(*defsize); i++) { videosize = defsize[i] - audiosize; vbitrate = videosize / time; printf("USER CDSIZE: %4d MB | V: %6.1f MB @ %.1f kbps\n", defsize[i], videosize/(1024*1024), vbitrate); } } } /*************************************************************************/ /* * we don't want to scan the full directory since it can be a HUGE number * of entries (think to images -> clip transcoding [i.e. jpeg -> AVI]) */ #define TC_SCAN_MAX_FILES 32 /* informations for one stream found in given directory */ typedef struct tcdirentryinfo_ TCDirEntryInfo; struct tcdirentryinfo_ { uint32_t magic; size_t count; /* how many times a file with this magic was found on directory? */ int fd; /* file descriptor of the FIRST file encountered with this magic */ }; /* * tc_entry_find_magic: * find the element in an array of TCDirEntryInfo strucutes * that has it's magic equals to a given magic id. * * Parameters: * info: pointer to an array of TCDirEntryInfo strucutre to be scanned. * size: number of elements in given array. * magic: magic ID to find * Return value: * -1: given magic ID not found on given array. * >=0: index in array of element with same magic ID as given one. */ static int tc_entry_info_find_magic(const TCDirEntryInfo *infos, size_t size, uint32_t magic) { int ret = -1; size_t i = 0; if (infos != NULL && size > 0) { for (i = 0; i < size; i++) { if (infos[i].magic == magic) { ret = (int)i; break; } } } return ret; } /* * tc_entry_find_max_count: * find the element in an array of TCDirEntryInfo strucutes * that has the maximum count value. * * Parameters: * info: pointer to an array of TCDirEntryInfo strucutre to be scanned. * size: number of elements in given array. * Return value: * index in array of element with most high 'count' value. */ static int tc_entry_info_find_max_count(const TCDirEntryInfo *infos, size_t size) { int ret = 0; /* start pointing to first element */ size_t i = 0; if (infos != NULL && size > 0) { for (i = 1; i < size; i++) { if (infos[i].count > infos[ret].count) { ret = (int)i; } } } return ret; } /* * tc_entry_info_free: * release resources linked to a TCDirEntryInfo structure. * * Parameters: * de: pointer to a TCDirEntryInfo to be released. * Return value: * None */ static void tc_entry_info_free(TCDirEntryInfo *de) { if (de != NULL) { xio_close(de->fd); de->fd = -1; } } /* * tc_scan_directory_info: * Partially scan a given directory and optionally filla TCDirEntryInfo * structure with data about most common stream format found in. * * Partial scanning is done in order to avoid to waste too much * time/resources in scanning phase. Anyway, partial scan ti's supposed * to give results reliable enough. * Filled TCDirEntryInfo structure will have an already open file * descriptor pointing to the first file of the biggest set of files * with the same magic id. * * Use tc_entry_info_free() to release resources acquired using this * function, do not free()/close() things by hand to avoid undefined * behaviours. * * Parameters: * dname: path of dicrectory to scan. * candidate: optional pointer of TCDirEntryInfo structure to be filled. * can safely be NULL. * Return value: * -1: internal error * 0: succesfull, but directory seems to have mixed content * 1: succesfull, and directory seems to have homogeneous content * Side effects: * Some files (first TC_SCAN_MAX_FILES in filesystem order) in directory * are open and scanned to detect their magic number. */ static int tc_scan_directory_info(const char *dname, TCDirEntryInfo *candidate) { TCDirEntryInfo dinfo[TC_SCAN_MAX_FILES]; struct dirent *entry = NULL; size_t i = 0, j = 0, last = 0, probed = 0; int ret = -1; DIR *dir = opendir(dname); /* base sanity check first */ if (dir == NULL) { return -1; } /* round one: collect some stuff */ for (i = 0; i < TC_SCAN_MAX_FILES; i++) { char path_buf[PATH_MAX + 1]; uint32_t magic = TC_MAGIC_UNKNOWN; struct stat stat_buf; int fd = -1, err; entry = readdir(dir); if (entry == NULL) { break; } if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { /* it's safe to skip them */ continue; } tc_snprintf(path_buf, sizeof(path_buf), "%s/%s", dname, entry->d_name); err = stat(path_buf, &stat_buf); if (err || !S_ISREG(stat_buf.st_mode)) { if (verbose >= TC_DEBUG) { /* uhm */ tc_log_warn(EXE, "opening '%s': is not a file", path_buf); } continue; } fd = xio_open(path_buf, O_RDONLY); if (fd == -1) { /* switch to tc_log_perror? */ tc_log_error(EXE, "opening '%s': %s", path_buf, strerror(errno)); continue; /* assume non-fatal error */ } magic = fileinfo(fd, 0); j = tc_entry_info_find_magic(dinfo, last, magic); if (j != -1) { /* entry already encountered */ dinfo[j].count++; xio_close(fd); /* we want only the first file descriptor of a given set */ } else { dinfo[last].fd = fd; dinfo[last].magic = magic; dinfo[last].count = 1; last++; } probed++; } closedir(dir); /* round two: let's make a choice */ if (last > 0) { /* at least one file scanned succesfully */ if (last == 1) { j = 0; /* pretty simple, uh? ;) */ ret = 1; /* obviously homogeneous */ } else { j = tc_entry_info_find_max_count(dinfo, last); /* save only candidate entry info */ for (i = 0; i < last; i++) { if (i != j) { tc_entry_info_free(&dinfo[i]); } } if (dinfo[j].count < probed + 1) { ret = 0; } else { ret = 1; } } if (candidate != NULL) { candidate->fd = dinfo[j].fd; candidate->magic = dinfo[j].magic; candidate->count = dinfo[j].count; } } return ret; } #define OPEN_FILE(ipipe, name) do { \ (ipipe)->fd_in = xio_open((name), O_RDONLY); \ if ((ipipe)->fd_in < 0) { \ tc_log_perror(EXE, "file open"); \ return TC_IMPORT_ERROR; \ } \ } while (0) #define PROBE_DIR(ipipe) do { \ TCDirEntryInfo info; \ int ret = tc_scan_directory_info((ipipe)->name, &info); \ if (ret < 0) { \ tc_log_error(EXE, "unrecognized filetype for '%s'", \ (ipipe)->name); \ return TC_IMPORT_ERROR; \ } else if (ret == 0) { \ tc_log_warn(EXE, "non-homogeneous directory content" \ " (different stream type detected)"); \ } \ (ipipe)->fd_in = info.fd; \ (ipipe)->magic = info.magic; \ } while (0) /* * info_setup: * * perform second-step initialization on a info_t structure. * While first-step setup can be done statically with simple * assignements, intialization performedon this stage is based * on data given previosuly (i.e.: name). * This function is a catch-all for all the black magic things. * Of course there is still a lot of room for cleaning up and * refactoring. * My thought is that doing things in a _really_ clean way means * rewriting almoost from the ground up the probing infrastructure. * * Parameters: * ipipe: info_t structure to (finish to) initialize * skip: amount of bytes to skip when analyzing a regular file. * Ignored otherwise * mplayer_probe: if input it's a regular file, do the real probing through * mplayer (if avalaible); ignored otherwise. * want_dvd: if !0 and the source looks likea DVD, handle it like a DVD. * I know this is a bit obscure and maybe even sick, it's * legacy and should go away in the future. * Return Value: * TC_IMPORT_OK -> succesfull, * TC_IMPORT_ERROR -> otherwise. * messages are sent to user using tc_log_*() in both cases. * Side effects: * quite a lot =) * Input source is open and read to guess the source type. * This function can do (and usually does) several read attempts. * Preconditions: * given info_t structure is already basically initialized (see * first-step setup above). This measn set at least: * ipipe.verbose, ipipe.fd_in, ipipe.name = name; * Postconditions: * given info_t is ready to be used in probe_stream() * */ static int info_setup(info_t *ipipe, int skip, int mplayer_probe, int want_dvd) { int file_kind = tc_probe_path(ipipe->name); switch (file_kind) { case TC_PROBE_PATH_FILE: /* regular file */ if (mplayer_probe) { ipipe->magic = TC_MAGIC_MPLAYER; } else if (want_dvd && dvd_is_valid(ipipe->name)) { ipipe->magic = TC_MAGIC_DVD; } else { OPEN_FILE(ipipe, ipipe->name); ipipe->magic = fileinfo(ipipe->fd_in, skip); ipipe->seek_allowed = 1; } break; case TC_PROBE_PATH_RELDIR: /* relative path to directory */ PROBE_DIR(ipipe); break; case TC_PROBE_PATH_ABSPATH: /* absolute path */ if (dvd_is_valid(ipipe->name)) { ipipe->magic = TC_MAGIC_DVD; } else { PROBE_DIR(ipipe); } break; /* now the easy stuff */ case TC_PROBE_PATH_BKTR: /* bktr device */ ipipe->magic = TC_MAGIC_BKTR_VIDEO; break; case TC_PROBE_PATH_SUNAU: /* sunau device */ ipipe->magic = TC_MAGIC_SUNAU_AUDIO; break; case TC_PROBE_PATH_OSS: /* OSS device */ ipipe->magic = TC_MAGIC_OSS_AUDIO; break; case TC_PROBE_PATH_V4L_VIDEO: /* v4l video device */ ipipe->magic = TC_MAGIC_V4L_VIDEO; break; case TC_PROBE_PATH_V4L_AUDIO: /* v4l audio device */ ipipe->magic = TC_MAGIC_V4L_AUDIO; break; case TC_PROBE_PATH_INVALID: /* non-existent source */ default: /* fallthrough */ tc_log_error(EXE, "can't determine the file kind"); return TC_IMPORT_ERROR; } /* probe_path */ return TC_IMPORT_OK; } #undef OPEN_FILE #undef PROBE_DIR /* * info_teardown: * * reverse initialization done in info_setup * * Parameters: * ipipe: info_t structure to (finish to) initialize * Return Value: * None */ static void info_teardown(info_t *ipipe) { if (ipipe->fd_in != STDIN_FILENO) { xio_close(ipipe->fd_in); } } /*************************************************************************/ /* new fancy output handlers */ /* * generic info dump function handler */ typedef void (*InfoDumpFn)(info_t *ipipe); /* * dump_info_binary: * * dump a ProbeInfo structure in binary (and platform-dependent, * and probably even not fully safe) way to stdout. * This dump mode is used by tcprobe to communicate with transcode. * Legacy, I'd like to change this communication mode in future * releases. * * Parameters: * ipipe: info_t structure holding the ProbeInfo data to dump. * Return Value: * None */ static void dump_info_binary(info_t *ipipe) { pid_t pid = getpid(); tc_pwrite(STDOUT_FILENO, (uint8_t *) &pid, sizeof(pid_t)); tc_pwrite(STDOUT_FILENO, (uint8_t *) ipipe->probe_info, sizeof(ProbeInfo)); } #define PROBED_NEW "(*)" /* value different from tc's defaults */ #define PROBED_STD "" /* value equals to tc's defaults */ /* * user mode: * recommended transcode command line options: */ #define MARK_EXPECTED(ex) ((ex) ?(PROBED_STD) :(PROBED_NEW)) #define CHECK_MARK_EXPECTED(probed, val) \ (((val) == (probed)) ?(PROBED_STD) :(PROBED_NEW)) /* * dump_info_old: * dump a ProbeInfo structure in a human-readable format. * * Parameters: * ipipe: info_t structure holding the ProbeInfo data to dump. * Return Value: * None */ static void dump_info_old(info_t *ipipe) { long frame_time = 0; int is_std = TC_TRUE; /* flag: select PROBED_??? above */ int nsubs = 0, i = 0; char extrabuf[TC_BUF_MIN] = { '\0' }; int extrabuf_ready = TC_FALSE; size_t len = 0; /* full-blown back-compatibility */ fprintf(stderr, "[%s] %s\n", EXE, filetype(ipipe->magic)); printf("[%s] summary for %s, %s = not default, 0 = not detected\n", EXE, ((ipipe->magic == TC_STYPE_STDIN) ?"-" :ipipe->name), PROBED_NEW); if (ipipe->probe_info->width != PAL_W || ipipe->probe_info->height != PAL_H) { is_std = TC_FALSE; } /* video first. */ if (ipipe->probe_info->width > 0 && ipipe->probe_info->height > 0) { int n, d, ret; extrabuf_ready = TC_FALSE; printf("%18s %s %dx%d [%dx%d] %s\n", "import frame size:", "-g", ipipe->probe_info->width, ipipe->probe_info->height, PAL_W, PAL_H, MARK_EXPECTED(is_std)); ret = tc_asr_code_to_ratio(ipipe->probe_info->asr, &n, &d); if (ret != TC_NULL_MATCH && (n > 0 && d > 0)) { /* back compatibility little hack */ printf("%18s %i:%i %s\n", "aspect ratio:", n, d, CHECK_MARK_EXPECTED(ipipe->probe_info->asr, 1)); } frame_time = (ipipe->probe_info->fps != 0) ? (long)(1. / ipipe->probe_info->fps * 1000) : 0; printf("%18s %s %.3f [%.3f] frc=%d %s\n", "frame rate:", "-f", ipipe->probe_info->fps, PAL_FPS, ipipe->probe_info->frc, CHECK_MARK_EXPECTED(ipipe->probe_info->frc, 3)); tc_snprintf(extrabuf, sizeof(extrabuf), "%18s ", ""); /* empty string to have a nice justification */ /* video track extra info */ if (ipipe->probe_info->pts_start) { len = strlen(extrabuf); tc_snprintf(extrabuf + len, sizeof(extrabuf) - len, "PTS=%.4f, frame_time=%ldms", ipipe->probe_info->pts_start, frame_time); if (ipipe->probe_info->bitrate) { len = strlen(extrabuf); tc_snprintf(extrabuf + len, sizeof(extrabuf) - len, "%sbitrate=%li kbps", (extrabuf_ready) ?", " :" ", /* * add seeparator only if we alread * written something in buffer */ ipipe->probe_info->bitrate); } /* at this point extrabuf flag will always be set to on */ extrabuf_ready = TC_TRUE; } if (extrabuf_ready) { printf("%s\n", extrabuf); } } /* audio next. */ for (i = 0; i < TC_MAX_AUD_TRACKS; i++) { int D_arg = 0, D_arg_ms = 0; double pts_diff = 0.; if (ipipe->probe_info->track[i].format != 0 && ipipe->probe_info->track[i].chan > 0) { extrabuf_ready = TC_FALSE; extrabuf[0] = '\0'; if (ipipe->probe_info->track[i].samplerate != RATE || ipipe->probe_info->track[i].chan != CHANNELS || ipipe->probe_info->track[i].bits != BITS || ipipe->probe_info->track[i].format != CODEC_AC3) { is_std = TC_FALSE; } else { is_std = TC_TRUE; } printf("%18s -a %d [0] -e %d,%d,%d [%d,%d,%d] -n 0x%x [0x%x] %s\n", "audio track:", ipipe->probe_info->track[i].tid, ipipe->probe_info->track[i].samplerate, ipipe->probe_info->track[i].bits, ipipe->probe_info->track[i].chan, RATE, BITS, CHANNELS, ipipe->probe_info->track[i].format, CODEC_AC3, MARK_EXPECTED(is_std)); /* audio track extra info */ if (ipipe->probe_info->track[i].pts_start) { tc_snprintf(extrabuf, sizeof(extrabuf), "PTS=%.4f", ipipe->probe_info->track[i].pts_start); extrabuf_ready = TC_TRUE; } if (ipipe->probe_info->track[i].bitrate) { size_t len = strlen(extrabuf); tc_snprintf(extrabuf + len, sizeof(extrabuf) - len, "%sbitrate=%i kbps", (extrabuf_ready) ?", " :"", ipipe->probe_info->track[i].bitrate); extrabuf_ready = TC_TRUE; } if (extrabuf_ready) { printf("%18s %s\n", "", /* empty string for a nice justification */ extrabuf); } /* audio track A/V sync suggestion */ if (ipipe->probe_info->pts_start > 0 && ipipe->probe_info->track[i].pts_start > 0 && ipipe->probe_info->fps != 0) { pts_diff = ipipe->probe_info->pts_start \ - ipipe->probe_info->track[i].pts_start; D_arg = (int)(pts_diff * ipipe->probe_info->fps); D_arg_ms = (int)((pts_diff - D_arg/ipipe->probe_info->fps)*1000); printf("%18s -D %d --av_fine_ms %d (frames & ms) [0] [0]\n", " ", D_arg, D_arg_ms); } } /* have subtitles here? */ if (ipipe->probe_info->track[i].attribute & PACKAGE_SUBTITLE) { nsubs++; } } /* no audio */ if (ipipe->probe_info->num_tracks == 0) { printf("%18s %s", "no audio track:", "(use \"null\" import module for audio)\n"); } if (nsubs > 0) { printf("detected (%d) subtitle(s)\n", nsubs); } /* P-units */ if (ipipe->probe_info->unit_cnt) { printf("detected (%d) presentation unit(s) (SCR reset)\n", ipipe->probe_info->unit_cnt+1); } /* DVD only: coder bitrate infos */ if (ipipe->magic == TC_MAGIC_DVD_PAL || ipipe->magic == TC_MAGIC_DVD_NTSC || ipipe->magic == TC_MAGIC_DVD) { enc_bitrate((long)ceil(ipipe->probe_info->fps * ipipe->probe_info->time), ipipe->probe_info->fps, bitrate*1000, 0); } else { if (ipipe->probe_info->frames > 0) { unsigned long dur_ms; unsigned int dur_h, dur_min, dur_s; if (ipipe->probe_info->fps < 0.100) { dur_ms = (long)ipipe->probe_info->frames*frame_time; } else { dur_ms = (long)((float)ipipe->probe_info->frames * 1000 /ipipe->probe_info->fps); } dur_h = dur_ms/3600000; dur_min = (dur_ms %= 3600000)/60000; dur_s = (dur_ms %= 60000)/1000; dur_ms %= 1000; printf("%18s %ld frames, frame_time=%ld msec," " duration=%u:%02u:%02u.%03lu\n", "length:", ipipe->probe_info->frames, frame_time, dur_h, dur_min, dur_s, dur_ms); } } } /* * dump_track_info_raw: * * dump a ProbeTrackInfo structure in a human-readable but machine-friendly * format, resembling, or identical where feasible, the mplayer -identify * output. * Print one field at line, in the format KEY=value. * * Parameters: * tracks: pointer to ProbeTrackInfo array to be dumped * i: dump only the i-th structure on array. * Return Value: * None */ static void dump_track_info_raw(ProbeTrackInfo *ti, int i) { if (ti != NULL && i >= 0) { /* paranoia */ const char *ext = ""; /* extension to identifiers for non-zero (not first) track */ char ext_buf[24]; if (i > 0) { tc_snprintf(ext_buf, sizeof(ext_buf), "_%i", i); ext = ext_buf; } if (ti[i].format != 0 && ti[i].chan > 0) { printf("ID_AUDIO_CODEC%s=%s\n", ext, tc_codec_to_string(ti[i].format)); printf("ID_AUDIO_FORMAT%s=%i\n", ext, ti[i].format); printf("ID_AUDIO_BITRATE%s=%i\n", ext, ti[i].bitrate); printf("ID_AUDIO_RATE%s=%i\n", ext, ti[i].samplerate); printf("ID_AUDIO_NCH%s=%i\n", ext, ti[i].chan); printf("ID_AUDIO_BITS%s=%i\n", ext, ti[i].bits); } } } /* * dump_info_raw: * * dump a ProbeInfo structure in a human-readable but machine-friendly * format, resembling, or identical where feasible, the mplayer -identify * output. * Print one field at line, in the format KEY=value. * * Parameters: * ipipe: info_t structure holding the ProbeInfo data to dump. * Return Value: * None */ static void dump_info_raw(info_t *ipipe) { int i; double duration = 0.0; /* seconds */ /* general information */ printf("ID_FILENAME=\"%s\"\n", ipipe->name); printf("ID_FILETYPE=\"%s\"\n", filetype(ipipe->magic)); /* video track, only the first */ printf("ID_VIDEO_WIDTH=%i\n", ipipe->probe_info->width); printf("ID_VIDEO_HEIGHT=%i\n", ipipe->probe_info->height); printf("ID_VIDEO_FPS=%.3f\n", ipipe->probe_info->fps); printf("ID_VIDEO_FRC=%i\n", ipipe->probe_info->frc); printf("ID_VIDEO_ASR=%i\n", ipipe->probe_info->asr); printf("ID_VIDEO_FORMAT=%s\n", tc_codec_to_string(ipipe->probe_info->codec)); printf("ID_VIDEO_BITRATE=%li\n", ipipe->probe_info->bitrate); /* audio stuff */ for (i = 0; i < TC_MAX_AUD_TRACKS; i++) { dump_track_info_raw(ipipe->probe_info->track, i); } if (ipipe->probe_info->fps != 0.0) { /* seconds */ duration = ((double)ipipe->probe_info->frames/ipipe->probe_info->fps); } /* general information, reprise */ printf("ID_LENGTH=%.2f\n", duration); } /* * dump_info_new: * dump a ProbeInfo structure in new, better * human-readable format. * * Parameters: * ipipe: info_t structure holding the ProbeInfo data to dump. * Return Value: * None */ static void dump_info_new(info_t *ipipe) { int i = 0, j = 0; unsigned long dur_ms = 0; unsigned int dur_h = 0, dur_min = 0, dur_s = 0; long frame_time = (ipipe->probe_info->fps != 0) ? (long)(1. / ipipe->probe_info->fps * 1000) : 0; if (ipipe->probe_info->fps < 0.100) { dur_ms = (long)ipipe->probe_info->frames * frame_time; } else { dur_ms = (long)((float)ipipe->probe_info->frames * 1000 /ipipe->probe_info->fps); } dur_h = dur_ms / 3600000; dur_min = (dur_ms %= 3600000) / 60000; dur_s = (dur_ms %= 60000) / 1000; dur_ms %= 1000; printf("* container:\n"); printf("%18s: %s\n", "format", filetype(ipipe->probe_info->magic)); printf("%18s: '%s'\n", "source", ((ipipe->magic == TC_STYPE_STDIN) ?"-" :ipipe->name)); printf("%18s: %li\n", "frames", ipipe->probe_info->frames); printf("%18s: %u:%02u:%02u.%03lu\n", "duration", dur_h, dur_min, dur_s, dur_ms); printf("%18s: %i\n", "SCR reset", ipipe->probe_info->unit_cnt + 1); /* video first. */ if (ipipe->probe_info->width > 0 && ipipe->probe_info->height > 0) { int n, d; tc_asr_code_to_ratio(ipipe->probe_info->asr, &n, &d); printf("* video track #0:\n"); printf("%18s: %s\n", "format", tc_codec_to_string(ipipe->probe_info->codec)); printf("%18s: %ix%i\n", "frame size", ipipe->probe_info->width, ipipe->probe_info->height); printf("%18s: %i:%i (asr=%i)\n", "aspect ratio", n, d, ipipe->probe_info->asr); printf("%18s: %.3f (frc=%i)\n", "frame rate", ipipe->probe_info->fps, ipipe->probe_info->frc); printf("%18s: %li kbps\n", "bitrate", ipipe->probe_info->bitrate); printf("%18s: %.4f\n", "starting PTS", ipipe->probe_info->pts_start); printf("%18s: %li ms\n", "frame time", frame_time); } j = 0; for (i = 0; i < TC_MAX_AUD_TRACKS; i++) { if (ipipe->probe_info->track[i].format != 0 && ipipe->probe_info->track[i].chan > 0) { double pts_diff = 0.0; int hint_frames = 0, hint_ms = 0; if (ipipe->probe_info->pts_start > 0 && ipipe->probe_info->track[i].pts_start > 0 && ipipe->probe_info->fps != 0) { pts_diff = ipipe->probe_info->pts_start - ipipe->probe_info->track[i].pts_start; hint_frames = (int)(pts_diff * ipipe->probe_info->fps); hint_ms = (int)((pts_diff - hint_frames / ipipe->probe_info->fps) * 1000); } printf("* audio track #%i:\n", j); /* XXX */ printf("%18s: %i\n", "track id", ipipe->probe_info->track[i].tid); /* XXX */ printf("%18s: 0x%x\n", "format", ipipe->probe_info->track[i].format); printf("%18s: %i\n", "channels", ipipe->probe_info->track[i].chan); printf("%18s: %i Hz\n", "sample rate", ipipe->probe_info->track[i].samplerate); printf("%18s: %i\n", "bits for sample", ipipe->probe_info->track[i].bits); printf("%18s: %i kbps\n", "bitrate", ipipe->probe_info->track[i].bitrate); printf("%18s: %.4f\n", "starting PTS", ipipe->probe_info->track[i].pts_start); printf("%18s: %i frames/%i ms\n", "A/V sync hint", hint_frames, hint_ms); /* have subtitles here? */ printf("%18s: %s\n", "subtitles", (ipipe->probe_info->track[i].attribute & PACKAGE_SUBTITLE) ?"yes" :"no"); j++; } } } /*************************************************************************/ /* ------------------------------------------------------------ * * print a usage/version message * * ------------------------------------------------------------*/ void version(void) { /* print id string to stderr */ printf("%s (%s v%s) (C) 2001-2010 Thomas Oestreich, Transcode team\n", EXE, PACKAGE, VERSION); } static void usage(int status) { version(); printf("Usage: %s [options] [-]\n", EXE); printf(" -i name input file/directory/device/host" " name [stdin]\n"); printf(" -B binary output to stdout" " (used by transcode) [off]\n"); printf(" -M use EXPERIMENTAL mplayer probe [off]\n"); printf(" -R raw mode: produce machine-friendly" " output [off]\n"); printf(" -X new extended output mode [off]\n"); printf(" -H n probe n MB of stream [1]\n"); printf(" -s n skip first n bytes of stream [0]\n"); printf(" -T title probe for DVD title [off]\n"); printf(" -b bitrate audio encoder bitrate kBits/s [%d]\n", ABITRATE); printf(" -f seekfile seek/index file [off]\n"); printf(" -d verbosity verbosity mode [1]\n"); printf(" -v print version\n"); exit(status); } /* ------------------------------------------------------------ * universal probing code frontend * ------------------------------------------------------------*/ /* very basic option sanity check */ #define VALIDATE_OPTION \ if (optarg[0]=='-') { \ usage(EXIT_FAILURE); \ } #define VALIDATE_PARAM(parm, opt, min) \ if ((parm) < (min)) { \ tc_log_error(EXE, "invalid parameter for option %s", (opt)); \ exit(16); \ } int main(int argc, char *argv[]) { info_t ipipe; InfoDumpFn output_handler = dump_info_old; /* standard old style output */ int mplayer_probe = TC_FALSE; int ch, skip = 0, want_dvd = 0, ret; const char *name = NULL; /* proper initialization */ memset(&ipipe, 0, sizeof(info_t)); ipipe.stype = TC_STYPE_UNKNOWN; ipipe.seek_allowed = 0; ipipe.factor = 1; ipipe.dvd_title = 1; libtc_init(&argc, &argv); while ((ch = getopt(argc, argv, "i:vBMRXd:T:f:b:s:H:?h")) != -1) { switch (ch) { case 'b': VALIDATE_OPTION; bitrate = atoi(optarg); VALIDATE_PARAM(bitrate, "-b", 0); break; case 'i': VALIDATE_OPTION; name = optarg; break; case 'f': VALIDATE_OPTION; ipipe.nav_seek_file = optarg; break; case 'd': VALIDATE_OPTION; verbose = atoi(optarg); break; case 's': VALIDATE_OPTION; skip = atoi(optarg); break; case 'H': VALIDATE_OPTION; ipipe.factor = atoi(optarg); /* how much data for probing? */ VALIDATE_PARAM(bitrate, "-H", 0); break; case 'B': output_handler = dump_info_binary; binary_dump = 1; /* XXX: compatibility with probe_mov -- FR */ break; case 'M': mplayer_probe = TC_TRUE; break; case 'R': output_handler = dump_info_raw; break; case 'X': output_handler = dump_info_new; break; case 'T': VALIDATE_OPTION; ipipe.dvd_title = atoi(optarg); want_dvd = 1; break; case 'v': version(); exit(0); break; case 'h': usage(EXIT_SUCCESS); break; default: usage(EXIT_FAILURE); } } /* need at least a file name */ if (argc == 1) { usage(EXIT_FAILURE); } if (optind < argc) { if (strcmp(argv[optind],"-") != 0) { usage(EXIT_FAILURE); } ipipe.stype = TC_STYPE_STDIN; } /* assume defaults */ if (name == NULL) { ipipe.stype = TC_STYPE_STDIN; } else { if (tc_x11source_is_display_name(name)) { ipipe.stype = TC_STYPE_X11; } } ipipe.verbose = verbose; ipipe.fd_out = STDOUT_FILENO; ipipe.codec = TC_CODEC_UNKNOWN; ipipe.name = name; /* do not try to mess with the stream */ if (ipipe.stype == TC_STYPE_STDIN) { ipipe.fd_in = STDIN_FILENO; ipipe.magic = streaminfo(ipipe.fd_in); } else if (ipipe.stype == TC_STYPE_X11) { ipipe.fd_in = STDIN_FILENO; /* XXX */ ipipe.magic = TC_MAGIC_X11; } else { ret = info_setup(&ipipe, skip, mplayer_probe, want_dvd); if (ret != TC_IMPORT_OK) { /* already logged out why */ exit(1); } } /* ------------------------------------------------------------ * codec specific section * note: user provided values overwrite autodetection! * ------------------------------------------------------------*/ probe_stream(&ipipe); if (ipipe.error == 0) { output_handler(&ipipe); } else if (ipipe.error == 1) { if (verbose) { tc_log_error(EXE, "failed to probe source"); } } else if (ipipe.error == 2) { if (verbose) { tc_log_error(EXE, "filetype/codec not yet supported by '%s'", PACKAGE); } } info_teardown(&ipipe); return ipipe.error; } #include "libtc/static_xio.h" /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */