diff options
Diffstat (limited to 'debian/transcode/transcode-1.1.7/src/transcode.c')
| -rw-r--r-- | debian/transcode/transcode-1.1.7/src/transcode.c | 2863 |
1 files changed, 2863 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/src/transcode.c b/debian/transcode/transcode-1.1.7/src/transcode.c new file mode 100644 index 00000000..0c7c797e --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/transcode.c @@ -0,0 +1,2863 @@ +/* + * transcode.c + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * 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 "decoder.h" +#include "encoder.h" +#include "dl_loader.h" +#include "framebuffer.h" +#include "counter.h" +#include "frame_threads.h" +#include "filter.h" +#include "probe.h" +#include "socket.h" +#include "split.h" + +#include "libtc/xio.h" +#include "libtc/libtc.h" +#include "libtc/cfgfile.h" +#include "libtc/tccodecs.h" +#include "libtc/ratiocodes.h" + +#include <ctype.h> +#include <math.h> + +#include "cmdline.h" + + +/* ------------------------------------------------------------ + * + * default options + * + * ------------------------------------------------------------*/ + + +int pcmswap = TC_FALSE; +int rgbswap = TC_FALSE; +int rescale = TC_FALSE; +int im_clip = TC_FALSE; +int ex_clip = TC_FALSE; +int pre_im_clip = TC_FALSE; +int post_ex_clip= TC_FALSE; +int flip = TC_FALSE; +int mirror = TC_FALSE; +int resize1 = TC_FALSE; +int resize2 = TC_FALSE; +int decolor = TC_FALSE; +int zoom = TC_FALSE; +int dgamma = TC_FALSE; +int keepasr = TC_FALSE; +int fast_resize = TC_FALSE; + +// global information structure +static vob_t *vob = NULL; +int verbose = TC_INFO; + +//------------------------------------------------------------- +// core parameter + +int tc_buffer_delay_dec = -1; +int tc_buffer_delay_enc = -1; +int tc_cluster_mode = 0; +int tc_decoder_delay = 0; +int tc_progress_meter = -1; // so we know whether it's set by the user +int tc_progress_rate = 1; +int tc_accel = AC_ALL; //acceleration code +unsigned int tc_avi_limit = (unsigned int)-1; +pid_t tc_probe_pid = 0; +int tc_niceness = 0; + +int max_frame_buffer = TC_FRAME_BUFFER; +int max_frame_threads = TC_FRAME_THREADS; + +//------------------------------------------------------------- + + +/*************************************************************************/ +/*********************** Exported utility routines ***********************/ +/*************************************************************************/ + +/** + * version: Print a version message. The message is only printed for the + * first call. + * + * Parameters: + * None. + * Return value: + * None. + */ + +void version(void) +{ + static int tcversion = 0; + if (tcversion++) + return; + fprintf(stderr, "%s v%s (C) 2001-2003 Thomas Oestreich," + " 2003-2010 Transcode Team\n", + PACKAGE, VERSION); +} + +/*************************************************************************/ + +/** + * tc_get_vob: Return a pointer to the global vob_t data structure. + * + * Parameters: + * None. + * Return value: + * A pointer to the global vob_t data structure. + */ + +vob_t *tc_get_vob() +{ + return vob; +} + +/*************************************************************************/ + +/** + * validate_source_path: Check whether the given string represents a valid + * source pathname. + * + * Parameters: + * path: String to check. + * Return value: + * Nonzero if the string is a valid source pathname, else zero. + * Side effects: + * Prints an error message using tc_error() if the string is not a + * valid pathname. + */ + +static int validate_source_path(const char *path) +{ + struct stat st; + + if (!path || !*path) { + tc_error("No filename given"); + return 0; + } + if (strcmp(path, "-") == 0) // allow stdin (maybe also /dev/stdin? FIXME) + return 1; + if (*path == '!' || *path == ':') /* from transcode.c -- why? */ + return 1; + if (xio_stat(path, &st) == 0) + return 1; + tc_error("Invalid filename \"%s\": %s", path, strerror(errno)); + return 0; +} + +/*************************************************************************/ + +int tc_next_video_in_file(vob_t *vob) +{ + vob->video_in_file = tc_glob_next(vob->video_in_files); + if (vob->video_in_file != NULL) { + return TC_OK; + } + return TC_ERROR; +} + +int tc_next_audio_in_file(vob_t *vob) +{ + vob->audio_in_file = tc_glob_next(vob->audio_in_files); + if (vob->audio_in_file != NULL) { + return TC_OK; + } + return TC_ERROR; +} + +int tc_has_more_video_in_file(vob_t *vob) +{ + int ret = TC_FALSE; + + if (core_mode == TC_MODE_DIRECTORY) { + ret = tc_glob_has_more(vob->video_in_files); + } + return ret; +} + +int tc_has_more_audio_in_file(vob_t *vob) +{ + int ret = TC_FALSE; + + if (core_mode == TC_MODE_DIRECTORY) { + ret = tc_glob_has_more(vob->audio_in_files); + } + return ret; +} + +/*************************************************************************/ +/*********************** Internal utility routines ***********************/ +/*************************************************************************/ + +/*************************************************************************/ +/*********************** Event thread support ****************************/ +/*************************************************************************/ + +static pthread_t event_thread_id = (pthread_t)0; +static const char *signame = "unknown signal"; + +static void tc_stop_all(void) +{ + tc_stop(); + tc_framebuffer_interrupt(); +} + +static void event_handler(int sig) +{ + switch (sig) { + case SIGINT: + signame = "SIGINT"; + break; + case SIGTERM: + signame = "SIGTERM"; + break; + case SIGPIPE: + signame = "SIGPIPE"; + break; + } + + tc_interrupt(); + tc_framebuffer_interrupt(); +} + +/** + * event_thread: Thread that watches for certain termination signals, + * terminating the transcode process cleanly. + * + * Parameters: + * None. + * Return value: + * None. + */ + +static void *event_thread(void* blocked_) +{ + struct sigaction handler; + sigset_t *blocked = blocked_; + + /* catch everything */ + pthread_sigmask(SIG_UNBLOCK, blocked, NULL); /* XXX */ + + /* install handlers, mutually exclusive */ + memset(&handler, 0, sizeof(struct sigaction)); + handler.sa_flags = 0; + handler.sa_mask = *blocked; + handler.sa_handler = event_handler; + + sigaction(SIGINT, &handler, NULL); // XXX + sigaction(SIGTERM, &handler, NULL); // XXX +/* sigaction(SIGPIPE, &handler, NULL); */// XXX + + /* Loop waiting for external events */ + for (;;) { + pthread_testcancel(); + + tc_socket_wait(); + + if (tc_interrupted()) { + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "(sighandler) %s received", signame); + + /* Kill the tcprobe process if it's running */ + if (tc_probe_pid > 0) + kill(tc_probe_pid, SIGTERM); + } + pthread_testcancel(); + } + return NULL; +} + +/*************************************************************************/ + +/** + * stop_event_thread: Ensure that the event handling thread is destroyed. + * + * Parameters: + * None. + * Return value: + * None. + */ + +static void stop_event_thread(void) +{ + if (event_thread_id) { + void *thread_status = NULL; + +// pthread_kill(event_thread_id, SIGINT); + pthread_cancel(event_thread_id); + pthread_join(event_thread_id, &thread_status); + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * load_all_filters: Loads all filters specified by the -J option. + * + * Parameters: + * filter_list: String containing filter list from -J option. + * Return value: + * None. + * Side effects: + * Destroys `filter_list'. + */ + +static void load_all_filters(char *filter_list) +{ + if (!filter_list) + return; + + while (*filter_list) { + char *s = filter_list + strcspn(filter_list, ","); + char *options; + + while (*s && s > filter_list && s[-1] == '\\') + s += 1 + strcspn(s+1, ","); + if (*s) + *s++ = 0; + options = strchr(filter_list, '='); + if (options) + *options++ = 0; + tc_filter_add(filter_list, options); + filter_list = s; + } +} + +/*************************************************************************/ + +/** + * transcode_init: initialize the transcoding engine. + * + * Parameters: + * vob: Pointer to the global vob_t data structure. + * Return value: + * 0 on success, -1 on error. + */ + +static int transcode_init(vob_t *vob, TCEncoderBuffer *tc_ringbuffer) +{ + /* load import modules and check capabilities */ + if (tc_import_init(vob, im_aud_mod, im_vid_mod) < 0) { + tc_log_error(PACKAGE, "failed to init import modules"); + return -1; + } + + /* load and initialize filters */ + tc_filter_init(); + load_all_filters(plugins_string); + + /* load export modules and check capabilities + * (only create a TCModule factory if a multiplex module was given) */ + if (tc_export_init(tc_ringbuffer, + ex_mplex_mod + ? tc_new_module_factory(vob->mod_path,verbose) + : NULL) != TC_OK + ) { + tc_log_error(PACKAGE, "failed to init export layer"); + return -1; + } + if (tc_export_setup(vob, ex_aud_mod, ex_vid_mod, ex_mplex_mod) != TC_OK) { + tc_log_error(PACKAGE, "failed to init export modules"); + return -1; + } + return 0; +} + +/** + * transcode_fini: finalize (shutdown) the transcoding engine. + * + * Parameters: + * vob: Pointer to the global vob_t data structure. + * Return value: + * 0 on success, -1 on error. + */ + +static int transcode_fini(vob_t *vob) +{ + /* unload import modules */ + tc_import_shutdown(); + /* unload filters */ + tc_filter_fini(); + /* unload export modules */ + tc_export_shutdown(); + + return 0; +} + +/* ------------------------------------------------------------- + * single file continuous or interval mode + * ------------------------------------------------------------*/ + +/* globals: frame_a, frame_b */ +static int transcode_mode_default(vob_t *vob) +{ + struct fc_time *tstart = NULL; + + tc_start(); + + // init decoder and open the source + if (0 != vob->ttime->vob_offset) { + vob->vob_offset = vob->ttime->vob_offset; + } + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + + // start the AV import threads that load the frames into transcode + // this must be called after tc_import_open + tc_import_threads_create(vob); + + // init encoder + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + + // open output files + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + + // tell counter about all encoding ranges + counter_reset_ranges(); + if (!tc_cluster_mode) { + int last_etf = 0; + for (tstart = vob->ttime; tstart; tstart = tstart->next) { + if (tstart->etf == TC_FRAME_LAST) { + // variable length range, oh well + counter_reset_ranges(); + break; + } + if (tstart->stf > last_etf) + counter_add_range(last_etf, tstart->stf-1, 0); + counter_add_range(tstart->stf, tstart->etf-1, 1); + last_etf = tstart->etf; + } + } + + // get start interval + tstart = vob->ttime; + + while (tstart) { + // set frame range (in cluster mode these will already be set) + if (!tc_cluster_mode) { + frame_a = tstart->stf; + frame_b = tstart->etf; + } + // main encoding loop, returns when done with all frames + tc_encoder_loop(vob, frame_a, frame_b); + + // check for user cancelation request + if (tc_interrupted()) { + break; + } + + // next range + tstart = tstart->next; + // see if we're using vob_offset + if ((tstart != NULL) && (tstart->vob_offset != 0)) { + tc_decoder_delay = 3; + tc_import_threads_cancel(); + tc_import_close(); + tc_framebuffer_flush(); + vob->vob_offset = tstart->vob_offset; + vob->sync = sync_seconds; + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + tc_import_threads_create(vob); + } + } + tc_stop_all(); + + // close output files + tc_encoder_close(); + // stop encoder + tc_encoder_stop(); + // cancel import threads + tc_import_threads_cancel(); + // stop decoder and close the source + tc_import_close(); + + return TC_OK; +} + + +/* ------------------------------------------------------------ + * split output AVI file + * ------------------------------------------------------------*/ + +/* globals: frame_a, frame_b, splitavi_frames, base */ +static int transcode_mode_avi_split(vob_t *vob) +{ + char buf[TC_BUF_MAX]; + int fa, fb, ch1 = 0; + + tc_start(); + + // init decoder and open the source + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + + // start the AV import threads that load the frames into transcode + tc_import_threads_create(vob); + + // encoder init + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + + // need to loop for ch1 + ch1 = 0; + + do { + if (!strlen(base)) + strlcpy(base, vob->video_out_file, TC_BUF_MIN); + + // create new filename + tc_snprintf(buf, sizeof(buf), "%s%03d.avi", base, ch1++); + vob->video_out_file = buf; + vob->audio_out_file = buf; + + // open output + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + + fa = frame_a; + fb = frame_a + splitavi_frames; + + tc_encoder_loop(vob, fa, ((fb > frame_b) ? frame_b : fb)); + + // close output + tc_encoder_close(); + + // restart + frame_a += splitavi_frames; + if (frame_a >= frame_b) + break; + + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "import status=%d", tc_import_status()); + + // check for user cancelation request + if (tc_interrupted()) + break; + + } while (tc_import_status()); + + tc_stop_all(); + + tc_encoder_stop(); + + // cancel import threads + tc_import_threads_cancel(); + // stop decoder and close the source + tc_import_close(); + + return TC_OK; +} + + +/* globals: frame_a, frame_b */ +static int transcode_mode_directory(vob_t *vob) +{ + struct fc_time *tstart = NULL; + + tc_start(); + + if (strcmp(vob->audio_in_file, vob->video_in_file) != 0) + tc_error("directory mode DOES NOT support separate audio files (A=%s|V=%s)", + vob->audio_in_file, vob->video_in_file); + + tc_multi_import_threads_create(vob); + + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + + // tell counter about all encoding ranges + counter_reset_ranges(); + if (!tc_cluster_mode) { + int last_etf = 0; + for (tstart = vob->ttime; tstart; tstart = tstart->next) { + if (tstart->etf == TC_FRAME_LAST) { + // variable length range, oh well + counter_reset_ranges(); + break; + } + if (tstart->stf > last_etf) + counter_add_range(last_etf, tstart->stf-1, 0); + counter_add_range(tstart->stf, tstart->etf-1, 1); + last_etf = tstart->etf; + } + } + + // get start interval + for (tstart = vob->ttime; + tstart != NULL && !tc_interrupted(); + tstart = tstart->next) { + // set frame range (in cluster mode these will already be set) + if (!tc_cluster_mode) { + frame_a = tstart->stf; + frame_b = tstart->etf; + } + // main encoding loop, returns when done with all frames + tc_encoder_loop(vob, frame_a, frame_b); + } + + tc_stop_all(); + + tc_encoder_close(); + tc_encoder_stop(); + tc_multi_import_threads_cancel(); + + return TC_OK; +} + +/* --------------------------------------------------------------- + * VOB PSU mode: transcode and split based on program stream units + * --------------------------------------------------------------*/ + +/* globals: frame_a, frame_b */ +static int transcode_mode_psu(vob_t *vob, const char *psubase) +{ + char buf[TC_BUF_MAX]; + int fa, fb, psu_cur = vob->vob_psu_num1; + + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + + // open output + if (no_split) { + vob->video_out_file = psubase; + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + } + + tc_decoder_delay = 3; + + counter_on(); + + for (;;) { + int ret; + + memset(buf, 0, sizeof buf); + if (!no_split) { + // create new filename + tc_snprintf(buf, sizeof(buf), psubase, psu_cur); + // update vob structure + vob->video_out_file = buf; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "using output filename %s", + vob->video_out_file); + } + + // get seek/frame information for next PSU + // need to process whole PSU + vob->vob_chunk = 0; + vob->vob_chunk_max = 1; + + ret = split_stream(vob, nav_seek_file, psu_cur, &fa, &fb, 0); + + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE,"processing PSU %d, -L %d -c %d-%d %s (ret=%d)", + psu_cur, vob->vob_offset, fa, fb, buf, ret); + + // exit condition + if (ret < 0 || psu_cur == vob->vob_psu_num2) + break; + + // do not process units with a small frame number, assume it is junk + if ((fb-fa) > psu_frame_threshold) { + tc_start(); + + // start new decoding session with updated vob structure + // this starts the full decoder setup, including the threads + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + + // start the AV import threads that load the frames into transcode + tc_import_threads_create(vob); + + // frame threads may need a reboot too. + tc_frame_threads_init(vob, max_frame_threads, max_frame_threads); + + // open new output file + if (!no_split) { + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + } + + // core + // we try to encode more frames and let the decoder safely + // drain the queue to avoid threads not stopping + + tc_encoder_loop(vob, fa, TC_FRAME_LAST); + + // close output file + if (!no_split) { + if (tc_encoder_close() != TC_OK) + tc_warn("failed to close encoder - non fatal"); + } + + if (verbose >= TC_CLEANUP) { + // for debug + vframe_dump_status(); + aframe_dump_status(); + } + + // cancel import threads + tc_import_threads_cancel(); + // stop decoder and close the source + tc_import_close(); + + // flush all buffers before we proceed to next PSU + tc_framebuffer_flush(); + + vob->psu_offset += (double) (fb-fa); + } else { + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "skipping PSU %d with %d frame(s)", + psu_cur, fb-fa); + + } + + psu_cur++; + if (tc_interrupted()) + break; + } //next PSU + + // close output + if (no_split) { + if (tc_encoder_close() != TC_OK) + tc_warn("failed to close encoder - non fatal"); + } + + tc_stop_all(); + + tc_encoder_stop(); + + return TC_OK; +} + +/* ------------------------------------------------------------ + * DVD chapter mode + * ------------------------------------------------------------*/ + +/* globals: frame_a, frame_b, chbase */ +static int transcode_mode_dvd(vob_t *vob) +{ +#ifdef HAVE_LIBDVDREAD + char buf[TC_BUF_MAX]; + int ch1, ch2; + + tc_start(); + + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + + // open output + if (no_split) { + // create new filename + tc_snprintf(buf, sizeof(buf), "%s.avi", chbase); + // update vob structure + vob->video_out_file = buf; + vob->audio_out_file = buf; + + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + } + + // 1 sec delay after decoder closing + tc_decoder_delay = 1; + + // loop each chapter + ch1 = vob->dvd_chapter1; + ch2 = vob->dvd_chapter2; + + //ch = -1 is allowed but makes no sense + if (ch1 < 0) + ch1 = 1; + + for (;;) { + vob->dvd_chapter1 = ch1; + vob->dvd_chapter2 = -1; + + if (!no_split) { + // create new filename + tc_snprintf(buf, sizeof(buf), "%s-ch%02d.avi", chbase, ch1); + // update vob structure + vob->video_out_file = buf; + vob->audio_out_file = buf; + } + + // start decoding with updated vob structure + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + + // start the AV import threads that load the frames into transcode + tc_import_threads_create(vob); + + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "%d chapters for title %d detected", + vob->dvd_max_chapters, vob->dvd_title); + + // encode + if (!no_split) { + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to init encoder"); + } + + // main encoding loop, selecting an interval won't work + tc_encoder_loop(vob, frame_a, frame_b); + + if (!no_split) { + if (tc_encoder_close() != TC_OK) + tc_warn("failed to close encoder - non fatal"); + } + + // cancel import threads + tc_import_threads_cancel(); + // stop decoder and close the source + tc_import_close(); + + // flush all buffers before we proceed + tc_framebuffer_flush(); + + // exit, i) if import module could not determine max_chapters + // ii) all chapters are done + // iii) someone hit ^C + + if (vob->dvd_max_chapters ==- 1 + || ch1 == vob->dvd_max_chapters || ch1 == ch2 + || tc_interrupted()) + break; + ch1++; + } + + if (no_split) { + if (tc_encoder_close() != TC_OK) + tc_warn("failed to close encoder - non fatal"); + } + + tc_stop_all(); + tc_encoder_stop(); +#endif + + return TC_OK; +} + +/*************************************************************************/ + +/** + * new_vob: Create a new vob_t structure and fill it with appropriate + * values. + * + * Parameters: + * None. + * Return value: + * A pointer to the newly-created vob_t structure, or NULL on error. + * Notes: + * On error, errno is valid. + */ + +static vob_t *new_vob(void) +{ + vob_t *vob = tc_malloc(sizeof(vob_t)); + if (!vob) + return NULL; + + vob->divxbitrate = VBITRATE; + vob->video_max_bitrate = 0; /* 0 = set by encoder */ + vob->divxkeyframes = VKEYFRAMES; + vob->divxquality = VQUALITY; + vob->divxmultipass = VMULTIPASS; + vob->divxcrispness = VCRISPNESS; + vob->m2v_requant = M2V_REQUANT_FACTOR; + + vob->min_quantizer = VMINQUANTIZER; + vob->max_quantizer = VMAXQUANTIZER; + + vob->rc_period = RC_PERIOD; + vob->rc_reaction_period = RC_REACTION_PERIOD; + vob->rc_reaction_ratio = RC_REACTION_RATIO; + + vob->divx5_vbv_prof = DIVX5_VBV_PROFILE; + vob->divx5_vbv_bitrate = DIVX5_VBV_BITRATE; + vob->divx5_vbv_size = DIVX5_VBV_SIZE; + vob->divx5_vbv_occupancy = DIVX5_VBV_OCCUPANCY; + + vob->mp3bitrate = ABITRATE; + vob->mp3frequency = 0; + vob->mp3quality = AQUALITY; + vob->mp3mode = AMODE; + vob->a_rate = RATE; + vob->a_stream_bitrate = 0; + vob->a_bits = BITS; + vob->a_chan = CHANNELS; + vob->a_padrate = 0; + + vob->dm_bits = 0; + vob->dm_chan = 0; + + vob->im_a_size = SIZE_PCM_FRAME; + vob->im_v_width = PAL_W; + vob->im_v_height = PAL_H; + vob->im_v_size = SIZE_RGB_FRAME; + vob->ex_a_size = SIZE_PCM_FRAME; + vob->ex_v_width = PAL_W; + vob->ex_v_height = PAL_H; + vob->ex_v_size = SIZE_RGB_FRAME; + vob->a_track = 0; + vob->v_track = 0; + vob->volume = 0; + vob->ac3_gain[0] = 1.0; + vob->ac3_gain[1] = 1.0; + vob->ac3_gain[2] = 1.0; + vob->audio_out_file = NULL; + vob->video_out_file = NULL; + vob->avifile_in = NULL; + vob->avifile_out = NULL; + vob->avi_comment_fd = -1; + vob->nav_seek_file = NULL; + vob->audio_file_flag = 0; + vob->audio_in_file = NULL; + vob->video_in_file = NULL; + vob->clip_count = 0; + vob->ex_a_codec = CODEC_MP3; //or fall back to module default + vob->ex_v_codec = CODEC_NULL; //determined by export module type + vob->ex_v_fcc = NULL; + vob->ex_a_fcc = NULL; + vob->ex_profile_name = NULL; + vob->fps = PAL_FPS; + vob->ex_fps = 0; + vob->im_frc = 0; + vob->ex_frc = 0; + vob->pulldown = 0; + vob->im_clip_top = 0; + vob->im_clip_bottom = 0; + vob->im_clip_left = 0; + vob->im_clip_right = 0; + vob->ex_clip_top = 0; + vob->ex_clip_bottom = 0; + vob->ex_clip_left = 0; + vob->ex_clip_right = 0; + vob->resize1_mult = 32; + vob->vert_resize1 = 0; + vob->hori_resize1 = 0; + vob->resize2_mult = 32; + vob->vert_resize2 = 0; + vob->hori_resize2 = 0; + vob->sync = 0; + vob->sync_ms = 0; + vob->sync_samples = 0; + vob->dvd_title = 1; + vob->dvd_chapter1 = 1; + vob->dvd_chapter2 = -1; + vob->dvd_max_chapters = -1; + vob->dvd_angle = 1; + vob->pass_flag = 0; + vob->verbose = TC_QUIET; + vob->antialias = 0; + vob->deinterlace = 0; + vob->decolor = 0; + vob->im_a_codec = CODEC_PCM; + vob->im_v_codec = CODEC_YUV; + vob->mod_path = MOD_PATH; + vob->audiologfile = NULL; + vob->divxlogfile = NULL; + vob->ps_unit = 0; + vob->ps_seq1 = 0; + vob->ps_seq2 = TC_FRAME_LAST; + vob->a_leap_frame = TC_LEAP_FRAME; + vob->a_leap_bytes = 0; + vob->demuxer = -1; + vob->a_codec_flag = CODEC_AC3; + vob->gamma = 0.0; + vob->encoder_flush = TC_TRUE; + vob->has_video = 1; + vob->has_audio = 1; + vob->has_audio_track = 1; + vob->lang_code = 0; + vob->v_format_flag = 0; + vob->v_codec_flag = 0; + vob->a_format_flag = 0; + vob->im_asr = 0; + vob->im_par = 0; + vob->im_par_width = 0; + vob->im_par_height = 0; + vob->ex_asr = -1; + vob->ex_par = 0; + vob->ex_par_width = 0; + vob->ex_par_height = 0; + vob->quality = VQUALITY; + vob->amod_probed = "null"; + vob->vmod_probed = "null"; + vob->amod_probed_xml = NULL; + vob->vmod_probed_xml = NULL; + vob->a_vbr = 0; + vob->pts_start = 0.0f; + vob->vob_offset = 0; + vob->vob_chunk = 0; + vob->vob_chunk_max = 0; + vob->vob_chunk_num1 = 0; + vob->vob_chunk_num2 = 0; + vob->vob_psu_num1 = 0; + vob->vob_psu_num2 = INT_MAX; + vob->vob_info_file = NULL; + vob->vob_percentage = 0; + vob->im_a_string = NULL; + vob->im_v_string = NULL; + vob->ex_a_string = NULL; + vob->ex_v_string = NULL; + vob->ex_m_string = NULL; + + vob->reduce_h = 1; + vob->reduce_w = 1; + + //-Z + vob->zoom_width = 0; + vob->zoom_height = 0; + vob->zoom_filter = TCV_ZOOM_LANCZOS3; + vob->zoom_interlaced = 0; + + vob->frame_interval = 1; // write every frame + + //anti-alias + vob->aa_weight = TC_DEFAULT_AAWEIGHT; + vob->aa_bias = TC_DEFAULT_AABIAS; + + vob->a52_mode = 0; + vob->encode_fields = TC_ENCODE_FIELDS_PROGRESSIVE; + + vob->ttime = NULL; + + vob->psu_offset = 0.0f; + vob->bitreservoir = TC_TRUE; + vob->lame_preset = NULL; + + vob->ts_pid1 = 0x0; + vob->ts_pid2 = 0x0; + + vob->dv_yuy2_mode = 0; + vob->hard_fps_flag = 0; + vob->mpeg_profile = PROF_NONE; + + vob->attributes = 0; + vob->export_attributes = TC_EXPORT_ATTRIBUTE_NONE; + + return vob; +} + +/*************************************************************************/ + +/* + * parse_navigation_file: + * parse navigation data file and setup vob data fields accordingly. + * This function handle both aviindex and tcdemux -W generated files. + * + * Parameters: + * vob: Pointer to the global vob_t data structure. + * nav_seek_file: Path of navigation file. + * Return Value: + * None + */ +static void parse_navigation_file(vob_t *vob, const char *nav_seek_file) +{ + if (nav_seek_file) { + FILE *fp = NULL; + struct fc_time *tmptime = NULL; + char buf[TC_BUF_MIN]; + int line_count = 0; + int flag = 0; + int is_aviindex = 0; + + if (vob->vob_offset) { + tc_warn("-L and --nav_seek are incompatible."); + } + + fp = fopen(nav_seek_file, "r"); + if (NULL == fp) { + tc_error("unable to open: %s", nav_seek_file); + } + + tmptime = vob->ttime; + line_count = 0; + + // check if this is an AVIIDX1 file + if (fgets(buf, sizeof(buf), fp)) { + if (strncasecmp(buf, "AVIIDX1", 7) == 0) + is_aviindex=1; + fseek(fp, 0, SEEK_SET); + } else { + tc_error("An error happend while reading the nav_seek file"); + } + + if (!is_aviindex) { + while (tmptime){ + flag = 0; + for (; fgets(buf, sizeof(buf), fp); line_count++) { + int L, new_frame_a; + + if (2 == sscanf(buf, "%*d %*d %*d %*d %d %d ", &L, &new_frame_a)) { + if (line_count == tmptime->stf) { + int len = tmptime->etf - tmptime->stf; + tmptime->stf = frame_a = new_frame_a; + tmptime->etf = frame_b = new_frame_a + len; + tmptime->vob_offset = L; + flag = 1; + line_count++; + break; + } + } + } + tmptime = tmptime->next; + } + } else { // is_aviindex==1 + fgets(buf, sizeof(buf), fp); // magic + fgets(buf, sizeof(buf), fp); // comment + + while (tmptime) { + int new_frame_a, type, key; + long chunk, chunkptype, last_keyframe = 0; + long long pos, len; + char tag[4]; + double ms = 0.0; + flag = 0; + + for (; fgets(buf, sizeof(buf), fp); line_count++) { + // TAG TYPE CHUNK CHUNK/TYPE POS LEN KEY MS + if (sscanf(buf, "%s %d %ld %ld %lld %lld %d %lf", + tag, &type, &chunk, &chunkptype, &pos, &len, &key, &ms)) { + if (type != 1) + continue; + if (key) + last_keyframe = chunkptype; + if (chunkptype == tmptime->stf) { + int lenf = tmptime->etf - tmptime->stf; + new_frame_a = chunkptype - last_keyframe; + + // If we are doing pass-through, we cannot skip frames, but only start + // passthrough on a keyframe boundary. At least, we respect the + // last frame the user whishes. + if (vob->pass_flag & TC_VIDEO) { + new_frame_a = 0; + lenf += (chunkptype - last_keyframe); + } + + tmptime->stf = frame_a = new_frame_a; + tmptime->etf = frame_b = new_frame_a + lenf; + tmptime->vob_offset = last_keyframe; + flag = 1; + line_count++; + break; + } + } + } + tmptime = tmptime->next; + } + } + fclose(fp); + + if (!flag) { + //frame not found + tc_warn("%s: frame %d out of range (%d frames found)", + nav_seek_file, frame_a, line_count); + tc_error("invalid option parameter for -c / --nav_seek"); + } + } +} + +/*************************************************************************/ + +static void setup_input_sources(vob_t *vob) +{ + if (vob->video_in_file == NULL && vob->audio_in_file == NULL) + tc_error("no input sources avalaible"); + if (vob->audio_in_file == NULL) + vob->audio_in_file = vob->video_in_file; + + /* + * let's try happily both sources independently. + * At least one will succeed, if we're here. + */ + vob->video_in_files = tc_glob_open(vob->video_in_file, 0); + if (vob->video_in_files) { + /* we always have at least one source */ + tc_next_video_in_file(vob); + } + if (!validate_source_path(vob->video_in_file)) { + tc_error("invalid input video file: %s", vob->video_in_file); + } + + vob->audio_in_files = tc_glob_open(vob->audio_in_file, 0); + if (vob->audio_in_files) { + /* we always have at least one source */ + tc_next_audio_in_file(vob); + } + if (!validate_source_path(vob->audio_in_file)) { + tc_error("invalid input audio file: %s", vob->audio_in_file); + } +} + +static void teardown_input_sources(vob_t *vob) +{ + if (vob->video_in_files) { + tc_glob_close(vob->video_in_files); + vob->video_in_files = NULL; + } + if (vob->audio_in_files) { + tc_glob_close(vob->audio_in_files); + vob->audio_in_files = NULL; + } +} + +/*************************************************************************/ + +/* support macros */ + +#define CLIP_CHECK(MODE, NAME, OPTION) do { \ + /* force to even for YUV mode */ \ + if (vob->im_v_codec == CODEC_YUV || vob->im_v_codec == CODEC_YUV422) { \ + if (vob->MODE ## _left % 2 != 0) { \ + tc_warn("left/right %s must be even in YUV/YUV422 mode", NAME); \ + vob->MODE ## _left--; \ + } \ + if (vob->MODE ## _right % 2 != 0) { \ + tc_warn("left/right %s must be even in YUV/YUV422 mode", NAME); \ + vob->MODE ## _right--; \ + } \ + if (vob->im_v_codec == CODEC_YUV && vob->MODE ## _top % 2 != 0) { \ + tc_warn("top/bottom %s must be even in YUV mode", NAME); \ + vob->MODE ## _top--; \ + } \ + if (vob->im_v_codec == CODEC_YUV && vob->MODE ## _bottom % 2 != 0) { \ + tc_warn("top/bottom %s must be even in YUV mode", NAME); \ + vob->MODE ## _bottom--; \ + } \ + } \ + /* check against import parameter, this is pre processing! */ \ + if (vob->ex_v_height - vob->MODE ## _top - vob->MODE ## _bottom <= 0 \ + || vob->ex_v_height - vob->MODE ## _top - vob->MODE ## _bottom > TC_MAX_V_FRAME_HEIGHT) \ + tc_error("invalid top/bottom clip parameter for option %s", OPTION); \ + \ + if (vob->ex_v_width - vob->MODE ## _left - vob->MODE ## _right <= 0 \ + || vob->ex_v_width - vob->MODE ## _left - vob->MODE ## _right > TC_MAX_V_FRAME_WIDTH) \ + tc_error("invalid left/right clip parameter for option %s", OPTION); \ + \ + vob->ex_v_height -= (vob->MODE ## _top + vob->MODE ## _bottom); \ + vob->ex_v_width -= (vob->MODE ## _left + vob->MODE ## _right); \ +} while (0) + + + +#define SHUTDOWN_MARK(STAGE) do { \ + if (verbose & TC_DEBUG) { \ + fprintf(stderr, " %s |", (STAGE)); \ + fflush(stderr); \ + } \ +} while (0) + + +/*************************************************************************/ + +/* common support data */ + +typedef struct ratio_t { + int t, b; +} ratio_t; + +static const ratio_t asrs[] = { + { 1, 1 }, { 1, 1 }, { 4, 3 }, { 16, 9 }, + { 221, 100 }, { 250, 100 }, { 125, 100 } +}; + +static const char *demuxer_desc[] = { + "sync AV at PTS start - demuxer disabled", + "sync AV at initial MPEG sequence", + "initial MPEG sequence / enforce frame rate", + "sync AV at initial PTS", + "initial PTS / enforce frame rate", +}; + +static const char *deinterlace_desc[] = { + "disabled", /* never used */ + "interpolate scanlines (fast)", + "handled by encoder (if available)", + "zoom to full frame (slow)", + "drop field / half height (fast)", + "interpolate scanlines / blend frames", + +}; + + static const char *antialias_desc[] = { + "disabled", /* never used */ + "de-interlace effects only", + "resize effects only", + "process full frame (slow)" +}; + + +/** + * main: transcode main routine. Performs initialization, parses command + * line options, and calls the transcoding routines. + * + * Parameters: + * argc: Command line argument count. + * argv: Command line argument vector. + * Return value: + * Zero on success, nonzero on error (exit code). + */ + +int main(int argc, char *argv[]) +{ + sigset_t sigs_to_block; + + const char *psubase = NULL; + + double fch, asr; + int leap_bytes1, leap_bytes2; + int max_frame_buffer = TC_FRAME_BUFFER; + + struct fc_time *tstart = NULL; + + TCFrameSpecs specs; + + //main thread id + writepid = getpid(); + + /* ------------------------------------------------------------ + * + * (I) set transcode defaults: + * + * ------------------------------------------------------------ */ + + // create global vob structure + vob = new_vob(); + if (!vob) { + tc_error("data initialization failed"); + } + + // prepare for signal catching + sigemptyset(&sigs_to_block); + sigaddset(&sigs_to_block, SIGINT); + sigaddset(&sigs_to_block, SIGTERM); + // enabling this breaks the import_vob module. + //sigaddset(&sigs_to_block, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL); + + // start of the signal handler thread is delayed later + + // close all threads at exit + atexit(stop_event_thread); + + /* ------------------------------------------------------------ + * + * (II) parse command line + * + * ------------------------------------------------------------ */ + + /* + * A *FEW* special options that deserve separate treatment. + * PLEASE keep VERY LOW the number of this special cases. + */ + libtc_init(&argc, &argv); + + if (!parse_cmdline(argc, argv, vob)) + exit(EXIT_FAILURE); + + setup_input_sources(vob); + + if (tc_progress_meter < 0) { + // if we have verbosity disabled, default to no progress meter. + if (verbose) { + tc_progress_meter = 1; + } else { + tc_progress_meter = 0; + } + } + + if (psu_mode) { + if (vob->video_out_file == NULL) + tc_error("please specify output file name for psu mode"); + if (!strchr(vob->video_out_file, '%') && !no_split) { + char *pc = tc_malloc(PATH_MAX); + char *suffix = strrchr(vob->video_out_file, '.'); + if (suffix) { + *suffix = '\0'; + } else { + suffix = ""; + } + tc_snprintf(pc, PATH_MAX, "%s-psu%%02d%s", + vob->video_out_file, suffix); + psubase = pc; + } else { + psubase = vob->video_out_file; + } + } + + // user doesn't want to start at all;-( + if (tc_interrupted()) + goto summary; + + // display program version + if (verbose) + version(); + + if (tc_niceness) { + if (nice(tc_niceness) < 0) { + tc_warn("setting nice to %d failed", tc_niceness); + } + } + + /* ------------------------------------------------------------ + * + * (III) auto probe properties of input stream + * + * ------------------------------------------------------------ */ + + if (auto_probe) { + // interface to "tcprobe" + int result = probe_source(vob->video_in_file, vob->audio_in_file, seek_range, + preset_flag, vob); + if (verbose) { + tc_log_info(PACKAGE, "V: %-16s | %s (%s)", "auto-probing", + (vob->video_in_file != NULL) ?vob->video_in_file :"N/A", + result ? "OK" : "FAILED"); + tc_log_info(PACKAGE, "V: %-16s | %s in %s (module=%s)", + "import format", + tc_codec_to_comment(vob->v_codec_flag), + mformat2str(vob->v_format_flag), + no_vin_codec == 0 ? im_vid_mod : vob->vmod_probed); + tc_log_info(PACKAGE, "A: %-16s | %s (%s)", "auto-probing", + (vob->audio_in_file != NULL) ?vob->audio_in_file :"N/A", + result ? "OK" : "FAILED"); + tc_log_info(PACKAGE, "A: %-16s | %s in %s (module=%s)", + "import format", + tc_codec_to_comment(vob->a_codec_flag), + mformat2str(vob->a_format_flag), + no_ain_codec==0 ? im_aud_mod : vob->amod_probed); + } + } + + if (vob->vmod_probed_xml && strstr(vob->vmod_probed_xml, "xml") != NULL + && vob->video_in_file) { + if (!probe_source_xml(vob, PROBE_XML_VIDEO)) + tc_error("failed to probe video XML source"); + } + if (vob->amod_probed_xml && strstr(vob->amod_probed_xml, "xml") != NULL + && vob->audio_in_file) { + if (!probe_source_xml(vob, PROBE_XML_AUDIO)) + tc_error("failed to probe audio XML source"); + } + + /* ------------------------------------------------------------ + * + * (IV) autosplit stream for cluster processing + * + * currently, only VOB streams are supported + * + * ------------------------------------------------------------*/ + + // set up ttime from -c or default + if (fc_ttime_string) { + // FIXME: should be in -c handler, but we need to know vob->fps first + free_fc_time(vob->ttime); + if (parse_fc_time_string(fc_ttime_string, vob->fps, ",", + (verbose>1 ? 1 : 0), &vob->ttime) == -1) + tc_error("error parsing time specifications"); + } else { + vob->ttime = new_fc_time(); + vob->ttime->fps = vob->fps; + vob->ttime->stf = TC_FRAME_FIRST; + vob->ttime->etf = TC_FRAME_LAST; + vob->ttime->next = NULL; + } + frame_a = vob->ttime->stf; + frame_b = vob->ttime->etf; + vob->ttime->vob_offset = 0; + tstart = vob->ttime; + counter_on(); //activate + + // determine -S,-c,-L option parameter for distributed processing + parse_navigation_file(vob, nav_seek_file); + + if (vob->vob_chunk_max) { + int this_unit = -1; + + // overwrite tcprobe's unit preset: + if (preset_flag & TC_PROBE_NO_SEEK) + this_unit = vob->ps_unit; + + if (split_stream(vob, vob->vob_info_file, this_unit, &frame_a, &frame_b, 1) < 0) + tc_error("cluster mode option -W error"); + } + + /* ------------------------------------------------------------ + * + * some sanity checks for command line parameters + * + * ------------------------------------------------------------*/ + + // -M + if (vob->demuxer == -1) { + vob->demuxer = 1; + } + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: %-16s | (%i) %s", "AV demux/sync", + vob->demuxer, demuxer_desc[vob->demuxer]); + } + + // -P + if (vob->pass_flag & TC_VIDEO) { + vob->im_v_codec = (vob->im_v_codec == CODEC_YUV) ?CODEC_RAW_YUV :CODEC_RAW; + vob->ex_v_codec = CODEC_RAW; + + // suggestion: + if (no_v_out_codec) + ex_vid_mod = "raw"; + no_v_out_codec = 0; + + if (no_a_out_codec) + ex_aud_mod = "raw"; + no_a_out_codec = 0; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "pass-through"); + } + + // -x + if (no_vin_codec && vob->video_in_file != NULL && vob->vmod_probed == NULL) + tc_error("module autoprobe failed, no option -x found"); + + + //overwrite results of autoprobing if modules are provided + if (no_vin_codec && vob->vmod_probed!=NULL) { + im_vid_mod = (char *)vob->vmod_probed_xml; + //need to load the correct module if the input file type is xml + } + + if (no_ain_codec && vob->amod_probed!=NULL) { + im_aud_mod = (char *)vob->amod_probed_xml; + //need to load the correct module if the input file type is xml + } + + // make zero frame size default for no video + if (im_vid_mod != NULL && strcmp(im_vid_mod, "null") == 0) { + vob->im_v_width = 0; + vob->im_v_height = 0; + } + + //initial aspect ratio + asr = (double) vob->im_v_width/vob->im_v_height; + + // -g + + // import size + // force to even for YUV mode + if (vob->im_v_codec == CODEC_YUV || vob->im_v_codec == CODEC_YUV422) { + if (vob->im_v_width % 2 != 0) { + tc_warn("frame width must be even in YUV/YUV422 mode"); + vob->im_v_width--; + } + if (vob->im_v_codec == CODEC_YUV && vob->im_v_height % 2 != 0) { + tc_warn("frame height must be even in YUV mode"); + vob->im_v_height--; + } + } + if (verbose & TC_INFO) { + if (vob->im_v_width && vob->im_v_height) { + tc_log_info(PACKAGE, "V: %-16s | %03dx%03d %4.2f:1 %s", + "import frame", vob->im_v_width, vob->im_v_height, + asr, tc_asr_code_describe(vob->im_asr)); + } else { + tc_log_info(PACKAGE, "V: %-16s | disabled", "import frame"); + } + } + + // init frame size with cmd line frame size + vob->ex_v_height = vob->im_v_height; + vob->ex_v_width = vob->im_v_width; + + // import bytes per frame (RGB 24bits) + vob->im_v_size = vob->im_v_height * vob->im_v_width * BPP/8; + // export bytes per frame (RGB 24bits) + vob->ex_v_size = vob->im_v_size; + + // calc clip settings for encoding to mpeg (vcd,svcd,xvcd,dvd) + // --export_prof {vcd,vcd-pal,vcd-ntsc,svcd,svcd-pal,svcd-ntsc,dvd,dvd-pal,dvd-ntsc} + + if (vob->mpeg_profile != PROF_NONE) { + ratio_t imasr = asrs[0]; + ratio_t exasr = asrs[0]; + + int impal = 0; + int pre_clip; + + // Make an educated guess if this is pal or ntsc + switch (vob->mpeg_profile) { + case VCD: + case SVCD: + case XVCD: + case DVD: + if (vob->im_v_height == 288 || vob->im_v_height == 576) + impal = 1; + if ((int)vob->fps == 25 || vob->im_frc == 3) + impal = 1; + break; + case VCD_PAL: + case SVCD_PAL: + case XVCD_PAL: + case DVD_PAL: + impal = 1; + break; + default: + break; + } + + // choose height dependent on pal or NTSC. + switch (vob->mpeg_profile) { + case VCD: + case VCD_PAL: + case VCD_NTSC: + if (!vob->zoom_height) + vob->zoom_height = impal ?288 :240; + break; + + case SVCD: + case SVCD_PAL: + case SVCD_NTSC: + case XVCD: + case XVCD_PAL: + case XVCD_NTSC: + case DVD: + case DVD_PAL: + case DVD_NTSC: + if (!vob->zoom_height) + vob->zoom_height = impal ?576 :480; + break; + + default: + break; + } + + // choose width if not set by user. + switch (vob->mpeg_profile) { + case VCD: + case VCD_PAL: + case VCD_NTSC: + if (!vob->zoom_width) + vob->zoom_width = 352; + vob->ex_asr = 2; + break; + case SVCD: + case SVCD_PAL: + case SVCD_NTSC: + case XVCD: + case XVCD_PAL: + case XVCD_NTSC: + if (!vob->zoom_width) + vob->zoom_width = 480; + vob->ex_asr = 2; + break; + case DVD: + case DVD_PAL: + case DVD_NTSC: + if (!vob->zoom_width) + vob->zoom_width = 720; + if (vob->ex_asr <= 0) + vob->ex_asr = 2; // assume 4:3 + break; + default: + break; + } + + // an input file without any aspect ratio setting (an AVI maybe?) + // so make a guess. + + if (vob->im_asr == 0) { + int i, mini=0; + const ratio_t *r = &asrs[1]; + double diffs[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + double mindiff = 2.0; + + for (i = 0; i < 6; i++) { + diffs[i] = (double)(r->b*vob->im_v_width) / (double)(r->t*vob->im_v_height); + r++; + } + + // look for the diff which is closest to 1.0 + + for (i = 0; i < 6; i++) { + double a = fabs(1.0 - diffs[i]); + if (a < mindiff) { + mindiff = a; + mini = i+1; + } + } + vob->im_asr = mini; + } + + imasr = asrs[vob->im_asr]; + exasr = asrs[vob->ex_asr]; + + pre_clip = vob->im_v_height - (vob->im_v_height * imasr.t * exasr.b ) / (imasr.b * exasr.t ); + + if (pre_im_clip == TC_FALSE) { + if (pre_clip % 2 != 0) { + vob->pre_im_clip_top = pre_clip/2+1; + vob->pre_im_clip_bottom = pre_clip/2-1; + } else { + vob->pre_im_clip_bottom = vob->pre_im_clip_top = pre_clip/2; + } + if (vob->pre_im_clip_top % 2 != 0 || vob->pre_im_clip_bottom % 2 != 0) { + vob->pre_im_clip_top--; + vob->pre_im_clip_bottom++; + } + } + + //FIXME hack, kludge, etc. EMS + if ((vob->im_v_height != vob->zoom_height) + || ((vob->im_v_width != vob->zoom_width) && (vob->ex_v_width != 704))) + zoom = TC_TRUE; + else + zoom = TC_FALSE; + + if (pre_clip) pre_im_clip = TC_TRUE; + + // shall we really go this far? + // If yes, there can be much more settings adjusted. + if (ex_vid_mod == NULL || !strcmp(ex_vid_mod, "mpeg2enc")) { +#ifdef HAVE_MJPEGTOOLS + if (!ex_aud_mod) + ex_aud_mod = "mp2enc"; + no_v_out_codec = 0; + ex_vid_mod = "mpeg2enc"; + //FIXME this should be in export_mpeg2enc.c + if (!vob->ex_v_fcc) { + switch (vob->mpeg_profile) { + case VCD: + case VCD_PAL: + case VCD_NTSC: + vob->ex_v_fcc = "1"; + break; + case SVCD: + case SVCD_PAL: + case SVCD_NTSC: + case XVCD: + case XVCD_PAL: + case XVCD_NTSC: + vob->ex_v_fcc = "4"; + break; + case DVD: + case DVD_PAL: + case DVD_NTSC: + vob->ex_v_fcc = "8"; + break; + default: + break; + } + } +#endif + } else if(!strcmp(ex_vid_mod, "ffmpeg")) { + if (!ex_aud_mod) + ex_aud_mod = "ffmpeg"; + switch (vob->mpeg_profile) { + case VCD: + vob->ex_v_fcc = "vcd"; + break; + case VCD_PAL: + vob->ex_v_fcc = "vcd-pal"; + break; + case VCD_NTSC: + vob->ex_v_fcc = "vcd-ntsc"; + break; + case SVCD: + vob->ex_v_fcc = "svcd"; + break; + case SVCD_PAL: + vob->ex_v_fcc = "svcd-pal"; + break; + case SVCD_NTSC: + vob->ex_v_fcc = "svcd-ntsc"; + break; + case XVCD: + vob->ex_v_fcc = "xvcd"; + break; + case XVCD_PAL: + vob->ex_v_fcc = "xvcd-pal"; + break; + case XVCD_NTSC: + vob->ex_v_fcc = "xvcd-ntsc"; + break; + case DVD: + vob->ex_v_fcc = "dvd"; + break; + case DVD_PAL: + vob->ex_v_fcc = "dvd-pal"; + break; + case DVD_NTSC: + vob->ex_v_fcc = "dvd-ntsc"; + break; + case PROF_NONE: + break; + } + } // ffmpeg + + if (ex_aud_mod == NULL) { +#ifdef HAVE_MJPEGTOOLS + no_a_out_codec=0; + ex_aud_mod = "mp2enc"; +#endif + } + } // mpeg_profile != PROF_NONE + + + // --PRE_CLIP + if (pre_im_clip) { + CLIP_CHECK(pre_im_clip, "pre_clip", "--pre_clip"); + + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: %-16s | %03dx%03d (%d,%d,%d,%d)", + "pre clip frame", vob->ex_v_width, vob->ex_v_height, + vob->pre_im_clip_top, vob->pre_im_clip_left, + vob->pre_im_clip_bottom, vob->pre_im_clip_right); + } + } + + // -j + if (im_clip) { + CLIP_CHECK(im_clip, "clip", "-j"); + + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: %-16s | %03dx%03d", "clip frame (<-)", + vob->ex_v_width, vob->ex_v_height); + } + } + + // -I + /* can this really happen? */ + if (vob->deinterlace < 0 || vob->deinterlace > 5) { + tc_error("invalid parameter for option -I"); + } + + if ((verbose & TC_INFO) && vob->deinterlace) { + tc_log_info(PACKAGE, + "V: %-16s | (mode=%i) %s", + "de-interlace", vob->deinterlace, + deinterlace_desc[vob->deinterlace]); + } + + if (vob->deinterlace == 4) + vob->ex_v_height /= 2; + + // Calculate the missing w or h based on the ASR + if (zoom && (vob->zoom_width == 0 || vob->zoom_height == 0)) { + enum missing_t { NONE, CALC_W, CALC_H, ALL } missing = ALL; + ratio_t asr = asrs[0]; + float oldr; + + // check if we have at least on width or height + if (vob->zoom_width == 0 && vob->zoom_height == 0) + missing = ALL; + else if (vob->zoom_width == 0 && vob->zoom_height > 0) + missing = CALC_W; + else if (vob->zoom_width > 0 && vob->zoom_height == 0) + missing = CALC_H; + else if (vob->zoom_width > 0 && vob->zoom_height > 0) + missing = NONE; + + // try import + if (vob->im_asr > 0 && vob->im_asr < 5) + asr = asrs[vob->im_asr]; + // try the export aspectratio + else if (vob->ex_asr > 0 && vob->ex_asr < 5) + asr = asrs[vob->ex_asr]; + + switch (missing) { + case ALL: + tc_error("Neither zoom width nor height set, can't guess anything"); + case CALC_W: + vob->zoom_width = vob->zoom_height * asr.t; vob->zoom_width /= asr.b; + break; + case CALC_H: + vob->zoom_height = vob->zoom_width * asr.b; vob->zoom_height /= asr.t; + break; + case NONE: + default: + /* can't happen */ + break; + } + + // for error printout + oldr = (float)vob->zoom_width/(float)vob->zoom_height; + + // align + if (vob->zoom_height % 8 != 0) + vob->zoom_height += 8-(vob->zoom_height%8); + if (vob->zoom_width % 8 != 0) + vob->zoom_width += 8-(vob->zoom_width%8); + oldr = ((float)vob->zoom_width/(float)vob->zoom_height-oldr)*100.0; + oldr = oldr<0?-oldr:oldr; + + tc_log_info(PACKAGE, "V: %-16s | %03dx%03d %4.2f:1 error %.2f%%", + "auto resize", vob->zoom_width, vob->zoom_height, + (float)vob->zoom_width/(float)vob->zoom_height, oldr); + } + + // -Z ...,fast + if (fast_resize) { + int ret = tc_compute_fast_resize_values(vob, TC_FALSE); + if (ret == 0) { + if (vob->hori_resize1 == 0 && vob->vert_resize1 == 0) + resize1 = TC_FALSE; + else + resize1 = TC_TRUE; + if (vob->hori_resize2 == 0 && vob->vert_resize2 == 0) + resize2 = TC_FALSE; + else + resize2 = TC_TRUE; + + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: %-16s | Using -B %d,%d,8 -X %d,%d,8", + "fast resize", + vob->vert_resize1, vob->hori_resize1, + vob->vert_resize2, vob->hori_resize2); + } + zoom = TC_FALSE; + } else { + if(verbose & TC_INFO) { + tc_log_info(PACKAGE, + "V: %-16s | requested but can't be used (W or H mod 8 != 0)", + "fast resize"); + } + } + } + + // -X + if (resize2) { + if (vob->resize2_mult % 8 != 0) + tc_error("resize multiplier for option -X is not a multiple of 8"); + + // works only for frame dimension beeing an integral multiple of vob->resize2_mult: + if (vob->vert_resize2 + && (vob->vert_resize2 * vob->resize2_mult + vob->ex_v_height) % vob->resize2_mult != 0) + tc_error("invalid frame height for option -X, check also option -j"); + + if (vob->hori_resize2 + && (vob->hori_resize2 * vob->resize2_mult + vob->ex_v_width) % vob->resize2_mult != 0) + tc_error("invalid frame width for option -X, check also option -j"); + + vob->ex_v_height += (vob->vert_resize2 * vob->resize2_mult); + vob->ex_v_width += (vob->hori_resize2 * vob->resize2_mult); + + //check2: + + if (vob->ex_v_height > TC_MAX_V_FRAME_HEIGHT + || vob->ex_v_width >TC_MAX_V_FRAME_WIDTH) + tc_error("invalid resize parameter for option -X"); + + if (vob->vert_resize2 <0 || vob->hori_resize2 < 0) + tc_error("invalid resize parameter for option -X"); + + // new aspect ratio: + asr *= (double) vob->ex_v_width * (vob->ex_v_height - vob->vert_resize2*vob->resize2_mult)/ + ((vob->ex_v_width - vob->hori_resize2*vob->resize2_mult) * vob->ex_v_height); + + vob->vert_resize2 *= (vob->resize2_mult/8); + vob->hori_resize2 *= (vob->resize2_mult/8); + + if (verbose & TC_INFO && vob->ex_v_height > 0) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d %4.2f:1 (-X)", + "new aspect ratio", + vob->ex_v_width, vob->ex_v_height, asr); + } + + // -B + if (resize1) { + if (vob->resize1_mult % 8 != 0) + tc_error("resize multiplier for option -B is not a multiple of 8"); + + // works only for frame dimension beeing an integral multiple of vob->resize1_mult: + if (vob->vert_resize1 + && (vob->ex_v_height - vob->vert_resize1*vob->resize1_mult) % vob->resize1_mult != 0) + tc_error("invalid frame height for option -B, check also option -j"); + + if (vob->hori_resize1 + && (vob->ex_v_width - vob->hori_resize1*vob->resize1_mult) % vob->resize1_mult != 0) + tc_error("invalid frame width for option -B, check also option -j"); + + vob->ex_v_height -= (vob->vert_resize1 * vob->resize1_mult); + vob->ex_v_width -= (vob->hori_resize1 * vob->resize1_mult); + + //check: + if (vob->vert_resize1 < 0 || vob->hori_resize1 < 0) + tc_error("invalid resize parameter for option -B"); + + //new aspect ratio: + asr *= (double) vob->ex_v_width * (vob->ex_v_height + vob->vert_resize1*vob->resize1_mult)/ + ((vob->ex_v_width + vob->hori_resize1*vob->resize1_mult) * vob->ex_v_height); + + vob->vert_resize1 *= (vob->resize1_mult/8); + vob->hori_resize1 *= (vob->resize1_mult/8); + + if (verbose & TC_INFO && vob->ex_v_height > 0) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d %4.2f:1 (-B)", + "new aspect ratio", + vob->ex_v_width, vob->ex_v_height, asr); + } + + // -Z + if (zoom) { + // new aspect ratio: + asr *= (double) vob->zoom_width*vob->ex_v_height/(vob->ex_v_width * vob->zoom_height); + + vob->ex_v_width = vob->zoom_width; + vob->ex_v_height = vob->zoom_height; + + if (verbose & TC_INFO && vob->ex_v_height > 0) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d %4.2f:1 (%s)", + "zoom", + vob->ex_v_width, vob->ex_v_height, asr, + tcv_zoom_filter_to_string(vob->zoom_filter)); + } + + // -Y + if (ex_clip) { + CLIP_CHECK(ex_clip, "clip", "-Y"); + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d", "clip frame (->)", + vob->ex_v_width, vob->ex_v_height); + } + + // -r + if (rescale) { + vob->ex_v_height /= vob->reduce_h; + vob->ex_v_width /= vob->reduce_w; + + //new aspect ratio: + asr *= (double)vob->ex_v_width/vob->ex_v_height*(vob->reduce_h*vob->ex_v_height)/ + (vob->reduce_w*vob->ex_v_width); + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d %4.2f:1 (-r)", + "rescale frame", + vob->ex_v_width, vob->ex_v_height,asr); + + // sanity check for YUV + if (vob->im_v_codec == CODEC_YUV || vob->im_v_codec == CODEC_YUV422) { + if (vob->ex_v_width%2 != 0 || (vob->im_v_codec == CODEC_YUV && vob->ex_v_height % 2 != 0)) { + tc_error("rescaled width/height must be even for YUV mode, try -V rgb24"); + } + } + } + + // --keep_asr + if (keepasr) { + int clip, zoomto; + double asr_out = (double)vob->ex_v_width/(double)vob->ex_v_height; + double asr_in = (double)vob->im_v_width/(double)vob->im_v_height; + double delta = 0.01; + double asr_cor = 1.0; + + + if (vob->im_asr) { + switch (vob->im_asr) { + case 1: + asr_cor = (1.0); + break; + case 2: + asr_cor = (4.0/3.0); + break; + case 3: + asr_cor = (16.0/9.0); + break; + case 4: + asr_cor = (2.21); + break; + } + } + + if (!zoom) + tc_error ("keep_asr only works with -Z"); + + if (asr_in-delta < asr_out && asr_out < asr_in+delta) + tc_error ("Aspect ratios are too similar, don't use --keep_asr "); + + if (asr_in > asr_out) { + /* adjust height */ + int clipV = (vob->im_clip_top +vob->im_clip_bottom); + int clipH = (vob->im_clip_left+vob->im_clip_right); + int clip1 = 0; + int clip2 = 0; + + zoomto = (int)((double)(vob->ex_v_width) / + ( ((double)(vob->im_v_width -clipH) / (vob->im_v_width/asr_cor/vob->im_v_height) )/ + (double)(vob->im_v_height-clipV))+.5); + clip = vob->ex_v_height - zoomto; + if (zoomto % 2 != 0) + (clip>0?zoomto--:zoomto++); // XXX + clip = vob->ex_v_height - zoomto; + clip /= 2; + clip1 = clip2 = clip; + + if (clip & 1) { + clip1--; + clip2++; + } + ex_clip = TC_TRUE; + vob->ex_clip_top = -clip1; + vob->ex_clip_bottom = -clip2; + + vob->zoom_height = zoomto; + } else { + /* adjust width */ + int clipV = (vob->im_clip_top +vob->im_clip_bottom); + int clipH = (vob->im_clip_left+vob->im_clip_right); + int clip1 = 0; + int clip2 = 0; + zoomto = (int)((double)vob->ex_v_height * ( + ( ((double)(vob->im_v_width-clipH)) / (vob->im_v_width/asr_cor/vob->im_v_height) ) / + (double)(vob->im_v_height-clipV)) +.5); + + clip = vob->ex_v_width - zoomto; + + if (zoomto % 2 != 0) + (clip>0?zoomto--:zoomto++); // XXX + clip = vob->ex_v_width - zoomto; + clip /= 2; + clip1 = clip2 = clip; + + if (clip & 1) { + clip1--; + clip2++; + } + ex_clip = TC_TRUE; + vob->ex_clip_left = -clip1; + vob->ex_clip_right = -clip2; + + vob->zoom_width = zoomto; + } + + if (vob->ex_v_height - vob->ex_clip_top - vob->ex_clip_bottom <= 0) + tc_error("invalid top/bottom clip parameter calculated from --keep_asr"); + + if (vob->ex_v_width - vob->ex_clip_left - vob->ex_clip_right <= 0) + tc_error("invalid left/right clip parameter calculated from --keep_asr"); + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes (%d,%d,%d,%d)", "keep aspect", + vob->ex_clip_top, vob->ex_clip_left, + vob->ex_clip_bottom, vob->ex_clip_right); + } + + // -z + + if (flip && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "flip frame"); + + // -l + if (mirror && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "mirror frame"); + + // -k + if (rgbswap && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "rgb2bgr"); + + // -K + if (decolor && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "b/w reduction"); + + // -G + if (dgamma && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | %.3f", "gamma correction", vob->gamma); + + // number of bits/pixel + // + // Christoph Lampert writes in transcode-users/2002-July/003670.html + // B*1000 B*1000*asr + // bpp = --------; W^2 = ------------ + // W*H*F bpp * F + // If this number is less than 0.15, you will + // most likely see visual artefacts (e.g. in high motion scenes). If you + // reach 0.2 or more, the visual quality normally is rather good. + // For my tests, this corresponded roughly to a fixed quantizer of 4, + // which is not brilliant, but okay. + + if (vob->divxbitrate > 0 && vob->divxmultipass != 3 + && verbose & TC_INFO) { + double div = vob->ex_v_width * vob->ex_v_height * vob->fps; + double bpp = vob->divxbitrate * 1000; + const char *judge = ""; + + if (div < 1.0) + bpp = 0.0; + else + bpp /= div; + + if (bpp <= 0.0) + judge = " (unknown)"; + else if (bpp > 0.0 && bpp <= 0.15) + judge = " (low)"; + + tc_log_info(PACKAGE, "V: %-16s | %.3f%s", "bits/pixel", bpp, judge); + } + + // -C + if (vob->antialias < 0 || vob->antialias > 3) { + tc_error("invalid parameter for option -C"); + } else { + if ((verbose & TC_INFO) && vob->antialias) { + tc_log_info(PACKAGE, + "V: %-16s | (mode=%d|%.2f|%.2f) %s", + "anti-alias", + vob->antialias, vob->aa_weight, vob->aa_bias, + antialias_desc[vob->antialias]); + } + } + + // --POST_CLIP + + if (post_ex_clip) { + CLIP_CHECK(post_ex_clip, "post_clip", "--post_clip"); + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d", + "post clip frame", + vob->ex_v_width, vob->ex_v_height); + } + + + // -W + if (vob->vob_percentage) { + if (vob->vob_chunk < 0 || vob->vob_chunk < 0) + tc_error("invalid parameter for option -W"); + } else { + if (vob->vob_chunk < 0 || vob->vob_chunk > vob->vob_chunk_max + 1) + tc_error("invalid parameter for option -W"); + } + + // -f + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | %.3f,%d", + "decoding fps,frc", + vob->fps, vob->im_frc); + + // -R + if (vob->divxmultipass && verbose & TC_INFO) { + switch (vob->divxmultipass) { + case 1: + tc_log_info(PACKAGE, + "V: %-16s | (mode=%d) %s %s", + "multi-pass", + vob->divxmultipass, + "writing data (pass 1) to", + vob->divxlogfile); + break; + case 2: + tc_log_info(PACKAGE, + "V: %-16s | (mode=%d) %s %s", + "multi-pass", + vob->divxmultipass, + "reading data (pass2) from", + vob->divxlogfile); + break; + case 3: + if (vob->divxbitrate > VMAXQUANTIZER) + vob->divxbitrate = VQUANTIZER; + tc_log_info(PACKAGE, + "V: %-16s | (mode=%d) %s (quant=%d)", + "single-pass", + vob->divxmultipass, + "constant quantizer/quality", + vob->divxbitrate); + break; + } + } + + // export frame size final check + if (vob->ex_v_height < 0 || vob->ex_v_width < 0) { + tc_warn("invalid export frame combination %dx%d", vob->ex_v_width, vob->ex_v_height); + tc_error("invalid frame processing requested"); + } + + // -V + if (vob->im_v_codec == CODEC_YUV) { + vob->ex_v_size = (3*vob->ex_v_height * vob->ex_v_width)>>1; + vob->im_v_size = (3*vob->im_v_height * vob->im_v_width)>>1; + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | YUV420 (4:2:0) aka I420", + "video format"); + } else if (vob->im_v_codec == CODEC_YUV422) { + vob->ex_v_size = (2*vob->ex_v_height * vob->ex_v_width); + vob->im_v_size = (2*vob->im_v_height * vob->im_v_width); + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | YUV422 (4:2:2)", + "video format"); + } else { + vob->ex_v_size = vob->ex_v_height * vob->ex_v_width * BPP/8; + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | RGB24", + "video format"); + } + + // -m + // different audio/video output files not yet supported + if (vob->audio_out_file == NULL) + vob->audio_out_file = vob->video_out_file; + + // -n + if (no_ain_codec == 1 && vob->has_audio == 0 + && vob->a_codec_flag == CODEC_AC3) { + if (vob->amod_probed == NULL || strcmp(vob->amod_probed,"null") == 0) { + if (verbose & TC_DEBUG) + tc_log_warn(PACKAGE, + "problems detecting audio format - using 'null' module"); + vob->a_codec_flag = 0; + } + } + + if (preset_flag & TC_PROBE_NO_TRACK) { + //tracks specified by user + } else { + if (!vob->has_audio_track && vob->has_audio) { + tc_warn("requested audio track %d not found - using 'null' module", vob->a_track); + vob->a_codec_flag = 0; + } + } + + //audio import disabled + if (vob->a_codec_flag == 0) { + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | disabled", "import"); + im_aud_mod = "null"; + } else { + //audio format, if probed sucessfully + if (verbose & TC_INFO) { + if (vob->a_stream_bitrate) + tc_log_info(PACKAGE, + "A: %-16s | 0x%-5lx %-12s [%4d,%2d,%1d] %4d kbps", + "import format", + vob->a_codec_flag, + tc_codec_to_comment(vob->a_codec_flag), + vob->a_rate, vob->a_bits, vob->a_chan, + vob->a_stream_bitrate); + else + tc_log_info(PACKAGE, + "A: %-16s | 0x%-5lx %-12s [%4d,%2d,%1d]", + "import format", + vob->a_codec_flag, + tc_codec_to_comment(vob->a_codec_flag), + vob->a_rate, vob->a_bits, vob->a_chan); + } + } + + if (vob->im_a_codec == CODEC_PCM && vob->a_chan > 2 && !(vob->pass_flag & TC_AUDIO)) { + // Input is more than 2 channels (i.e. 5.1 AC3) but PCM internal + // representation can't handle that, adjust the channel count to reflect + // what modules will actually have presented to them. + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "A: %-16s | %d channels -> %d channels", + "downmix", vob->a_chan, 2); + vob->a_chan = 2; + } + + if (vob->ex_a_codec == 0 || vob->a_codec_flag == 0 + || ex_aud_mod == NULL || strcmp(ex_aud_mod, "null") == 0) { + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | disabled", "export"); + ex_aud_mod = "null"; + } else { + // audio format + if (ex_aud_mod && strlen(ex_aud_mod) != 0) { + if (strcmp(ex_aud_mod, "mpeg") == 0) + vob->ex_a_codec = CODEC_MP2; + if (strcmp(ex_aud_mod, "mp2enc") == 0) + vob->ex_a_codec = CODEC_MP2; + if (strcmp(ex_aud_mod, "mp1e") == 0) + vob->ex_a_codec=CODEC_MP2; + } + + // calc export bitrate + switch (vob->ex_a_codec) { + case 0x1: // PCM + vob->mp3bitrate = ((vob->mp3frequency > 0) ?vob->mp3frequency :vob->a_rate) * + ((vob->dm_bits > 0) ?vob->dm_bits :vob->a_bits) * + ((vob->dm_chan > 0) ?vob->dm_chan :vob->a_chan) / 1000; + break; + case 0x2000: // PCM + if (vob->im_a_codec == CODEC_AC3) { + vob->mp3bitrate = vob->a_stream_bitrate; + } + break; + } + + if (verbose & TC_INFO) { + if (vob->pass_flag & TC_AUDIO) + tc_log_info(PACKAGE, + "A: %-16s | 0x%-5x %-12s [%4d,%2d,%1d] %4d kbps", + "export format", + vob->im_a_codec, + tc_codec_to_comment(vob->im_a_codec), + vob->a_rate, vob->a_bits, vob->a_chan, + vob->a_stream_bitrate); + else + tc_log_info(PACKAGE, + "A: %-16s | 0x%-5x %-12s [%4d,%2d,%1d] %4d kbps", + "export format", + vob->ex_a_codec, + tc_codec_to_comment(vob->ex_a_codec), + ((vob->mp3frequency > 0) ?vob->mp3frequency :vob->a_rate), + ((vob->dm_bits > 0) ?vob->dm_bits :vob->a_bits), + ((vob->dm_chan > 0) ?vob->dm_chan :vob->a_chan), + vob->mp3bitrate); + tc_log_info(PACKAGE, "V: %-16s | %s%s", "export format", + tc_codec_to_string(vob->ex_v_codec), + (vob->ex_v_codec == 0) ?" (module dependant)" :""); + } + } + + // Do not run out of audio-data + // import_ac3 now correctly probes the channels of the ac3 stream + // (previous versions always returned "2"). This breakes transcode + // when doing -A --tibit + if (vob->im_a_codec == CODEC_AC3) + vob->a_chan = vob->a_chan > 2 ?2 :vob->a_chan; + + // -f and --export_fps/export_frc + // + // set import/export frc/fps + if (vob->im_frc == 0) + tc_frc_code_from_value(&vob->im_frc, vob->fps); + + // ex_fps given, but not ex_frc + if (vob->ex_frc == 0 && (vob->ex_fps != 0.0)) + tc_frc_code_from_value(&vob->ex_frc, vob->ex_fps); + + if (vob->ex_frc == 0 && vob->im_frc != 0) + vob->ex_frc = vob->im_frc; + + // ex_frc always overwrites ex_fps + if (vob->ex_frc > 0) + tc_frc_code_to_value(vob->ex_frc, &vob->ex_fps); + + if (vob->im_frc <= 0 && vob->ex_frc <= 0 && vob->ex_fps == 0) + vob->ex_fps = vob->fps; + + if (vob->im_frc == -1) + vob->im_frc = 0; + if (vob->ex_frc == -1) + vob->ex_frc = 0; + + // --export_fps + + if(verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | %.3f,%d", + "encoding fps,frc", + vob->ex_fps, vob->ex_frc); + + + // --a52_demux + + if ((vob->a52_mode & TC_A52_DEMUX) && (verbose & TC_INFO)) + tc_log_info(PACKAGE, + "A: %-16s | %s", "A52 demuxing", + "(yes) 3 front, 2 rear, 1 LFE (5.1)"); + + //audio language, if probed sucessfully + if(vob->lang_code > 0 && (verbose & TC_INFO)) + tc_log_info(PACKAGE, + "A: %-16s | %c%c", + "language", + vob->lang_code >> 8, vob->lang_code & 0xff); + + // recalculate audio bytes per frame since video frames per second + // may have changed + + // samples per audio frame + fch = vob->a_rate/vob->ex_fps; + + // bytes per audio frame + vob->im_a_size = (int)(fch * (vob->a_bits/8) * vob->a_chan); + vob->im_a_size = (vob->im_a_size >> 2) << 2; + + // rest: + fch *= (vob->a_bits/8) * vob->a_chan; + + leap_bytes1 = TC_LEAP_FRAME * (fch - vob->im_a_size); + leap_bytes2 = - leap_bytes1 + TC_LEAP_FRAME * (vob->a_bits/8) * vob->a_chan; + leap_bytes1 = (leap_bytes1 >> 2) << 2; + leap_bytes2 = (leap_bytes2 >> 2) << 2; + + if(leap_bytes1<leap_bytes2) { + vob->a_leap_bytes = leap_bytes1; + } else { + vob->a_leap_bytes = -leap_bytes2; + vob->im_a_size += (vob->a_bits/8) * vob->a_chan; + } + + // final size in bytes + vob->ex_a_size = vob->im_a_size; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "A: %-16s | %d (%.6f)", + "bytes per frame", vob->im_a_size, fch); + + if(no_audio_adjust) { + vob->a_leap_bytes=0; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | disabled", "adjustment"); + + } else + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "A: %-16s | %d@%d", "adjustment", + vob->a_leap_bytes, vob->a_leap_frame); + + // -s + + if (vob->volume > 0 && vob->a_chan != 2) { + //tc_error("option -s not yet implemented for mono streams"); + } + + if (vob->volume > 0 && (verbose & TC_INFO)) + tc_log_info(PACKAGE, + "A: %-16s | %5.3f", + "rescale stream", vob->volume); + + // -D + if (vob->sync_ms >= (int) (1000.0/vob->ex_fps) + || vob->sync_ms <= - (int) (1000.0/vob->ex_fps)) { + vob->sync = (int) (vob->sync_ms/1000.0*vob->ex_fps); + vob->sync_ms -= vob->sync * (int) (1000.0/vob->ex_fps); + } + + if ((vob->sync || vob->sync_ms) && (verbose & TC_INFO)) + tc_log_info(PACKAGE, + "A: %-16s | %d ms [ %d (A) | %d ms ]", + "AV shift", + vob->sync * (int) (1000.0/vob->ex_fps) + vob->sync_ms, + vob->sync, vob->sync_ms); + + // -d + if (pcmswap) + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | yes", "swap bytes"); + + // -E + + //set export parameter to input parameter, if no re-sampling is requested + if (vob->dm_chan == 0) + vob->dm_chan = vob->a_chan; + if (vob->dm_bits == 0) + vob->dm_bits = vob->a_bits; + + // -P + if (vob->pass_flag & TC_AUDIO) { + vob->im_a_codec = CODEC_RAW; + vob->ex_a_codec = CODEC_RAW; + //suggestion: + if (no_a_out_codec) + ex_aud_mod = "raw"; + no_a_out_codec = 0; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | yes", "pass-through"); + } + + // -m + // different audio/video output files need two export modules + if (no_a_out_codec == 0 && vob->audio_out_file == NULL + && strcmp(ex_vid_mod, ex_aud_mod) != 0) + tc_error("different audio/export modules require use of option -m"); + + + // --accel +#if defined(ARCH_X86) || defined(ARCH_X86_64) + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "V: IA32/AMD64 accel | %s ", + ac_flagstotext(tc_accel & ac_cpuinfo())); +#endif + + ac_init(tc_accel); + + // more checks with warnings + + if (verbose & TC_INFO) { + // -o + if (vob->video_out_file == NULL && vob->audio_out_file == NULL + && core_mode == TC_MODE_DEFAULT) { + vob->video_out_file = TC_DEFAULT_OUT_FILE; + vob->audio_out_file = TC_DEFAULT_OUT_FILE; + tc_warn("no option -o found, encoded frames send to \"%s\"", + vob->video_out_file); + } + + // -y + if (core_mode == TC_MODE_DEFAULT + && vob->video_out_file != NULL && no_v_out_codec) + tc_warn("no option -y found, option -o ignored, writing to \"/dev/null\""); + + if (core_mode == TC_MODE_AVI_SPLIT && no_v_out_codec) + tc_warn("no option -y found, option -t ignored, writing to \"/dev/null\""); + + if (vob->im_v_codec == CODEC_YUV + && (vob->im_clip_left % 2 != 0 || vob->im_clip_right % 2 + || vob->im_clip_top % 2 != 0 || vob->im_clip_bottom % 2 != 0)) + tc_warn ("Odd import clipping paramter(s) detected, may cause distortion"); + + if (vob->im_v_codec == CODEC_YUV + && (vob->ex_clip_left % 2 != 0 || vob->ex_clip_right % 2 + || vob->ex_clip_top % 2 != 0 || vob->ex_clip_bottom % 2 != 0)) + tc_warn ("Odd export clipping paramter(s) detected, may cause distortion"); + } + + // -u + if (tc_buffer_delay_dec == -1) //adjust core parameter + tc_buffer_delay_dec = (vob->pass_flag & TC_VIDEO || ex_vid_mod==NULL || strcmp(ex_vid_mod, "null") == 0) + ?TC_DELAY_MIN :TC_DELAY_MAX; + + if (tc_buffer_delay_enc == -1) //adjust core parameter + tc_buffer_delay_enc = (vob->pass_flag & TC_VIDEO || ex_vid_mod==NULL || strcmp(ex_vid_mod, "null") == 0) + ?TC_DELAY_MIN :TC_DELAY_MAX; + + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "encoder delay = decode=%d encode=%d usec", + tc_buffer_delay_dec, tc_buffer_delay_enc); + + if (core_mode == TC_MODE_AVI_SPLIT && !strlen(base) && !vob->video_out_file) + tc_error("no option -o found, no base for -t given, so what?"); + + /* ------------------------------------------------------------- + * + * OK, so far, now start the support threads, setup buffers, ... + * + * ------------------------------------------------------------- */ + + //this will speed up in pass-through mode + if(vob->pass_flag && !(preset_flag & TC_PROBE_NO_BUFFER)) + max_frame_buffer = 50; + + if (vob->fps >= vob->ex_fps) { + /* worst case -> lesser fps (more audio samples for second) */ + specs.frc = vob->im_frc; + } else { + specs.frc = vob->ex_frc; + } + specs.width = TC_MAX(vob->im_v_width, vob->ex_v_width); + specs.height = TC_MAX(vob->im_v_height, vob->ex_v_height); + specs.format = vob->im_v_codec; + + /* XXX: explain me up */ + specs.rate = TC_MAX(vob->a_rate, vob->mp3frequency); + specs.channels = TC_MAX(vob->a_chan, vob->dm_chan); + specs.bits = TC_MAX(vob->a_bits, vob->dm_bits); + + tc_framebuffer_set_specs(&specs); + + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: video buffer | %i @ %ix%i [0x%x]", + max_frame_buffer, specs.width, specs.height, specs.format); + tc_log_info(PACKAGE, "A: audio buffer | %i @ %ix%ix%i", + max_frame_buffer, specs.rate, specs.channels, specs.bits); + } + +#ifdef STATBUFFER + // allocate buffer + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "allocating %d framebuffers (static)", + max_frame_buffer); + + if (vframe_alloc(max_frame_buffer) < 0) + tc_error("static framebuffer allocation failed"); + if (aframe_alloc(max_frame_buffer) < 0) + tc_error("static framebuffer allocation failed"); + +#else + if(verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "%d framebuffers (dynamic) requested", + max_frame_buffer); +#endif + + // load import/export modules and filters plugins + if (transcode_init(vob, tc_get_ringbuffer(max_frame_threads, max_frame_threads)) < 0) + tc_error("plug-in initialization failed"); + + // start socket stuff + if (socket_file) + if (!tc_socket_init(socket_file)) + tc_error("failed to initialize socket handler"); + + // now we start the signal handler thread + if (pthread_create(&event_thread_id, NULL, event_thread, &sigs_to_block) != 0) + tc_error("failed to start signal handler thread"); + + + // start frame processing threads + tc_frame_threads_init(vob, max_frame_threads, max_frame_threads); + + + /* ------------------------------------------------------------ + * + * transcoder core modes + * + * ------------------------------------------------------------*/ + + switch (core_mode) { + case TC_MODE_DEFAULT: + transcode_mode_default(vob); + break; + + case TC_MODE_AVI_SPLIT: + transcode_mode_avi_split(vob); + break; + + case TC_MODE_PSU: + transcode_mode_psu(vob, psubase); + break; + + case TC_MODE_DIRECTORY: + transcode_mode_directory(vob); + break; + + case TC_MODE_DVD_CHAPTER: + transcode_mode_dvd(vob); + break; + + case TC_MODE_DEBUG: + /* FIXME: get rid of this? */ + tc_log_msg(PACKAGE, "debug \"core\" mode"); + break; + + default: + //should not get here: + tc_error("internal error"); + } + + /* ------------------------------------------------------------ + * shutdown transcode, all cores modes end up here, core modes + * must take care of proper import/export API shutdown. + * + * 1) stop and cancel frame processing threads + * 2) unload all external modules + * 3) cancel internal signal/server thread + * ------------------------------------------------------------*/ + + // turn counter off + counter_off(); + + SHUTDOWN_MARK("clean up"); + + // stop and cancel frame processing threads + tc_frame_threads_close(); + SHUTDOWN_MARK("frame threads"); + + // unload all external modules + transcode_fini(NULL); + SHUTDOWN_MARK("unload modules"); + + // cancel no longer used internal signal handler threads + if (event_thread_id) { + SHUTDOWN_MARK("cancel signal"); + stop_event_thread(); + event_thread_id = (pthread_t)0; + } + + SHUTDOWN_MARK("internal threads"); + + // shut down control socket, if active + tc_socket_fini(); + SHUTDOWN_MARK("control socket"); + + // all done + if (verbose & TC_DEBUG) + fprintf(stderr, " done\n"); + + summary: + // print a summary + if ((verbose & TC_INFO) && vob->clip_count) + tc_log_info(PACKAGE, "clipped %d audio samples", + vob->clip_count/2); + + if (verbose & TC_INFO) { + long drop = - tc_get_frames_dropped(); + + tc_log_info(PACKAGE, "encoded %ld frames (%ld dropped, %ld cloned)," + " clip length %6.2f s", + (long)tc_get_frames_encoded(), drop, + (long)tc_get_frames_cloned(), + tc_get_frames_encoded()/vob->ex_fps); + } + +#ifdef STATBUFFER + // free buffers + vframe_free(); + aframe_free(); + if(verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "buffer released"); +#endif + + teardown_input_sources(vob); + + if (vob) + tc_free(vob); + + //exit at last + if (tc_interrupted()) + return 127; + return 0; +} + +// this Code below here _never_ gets called. +// it is just there to trick the linker to not remove +// unneeded object files from a .a file. + +#include "libtc/static_tclist.h" +#include "libtc/static_optstr.h" +#include "libtc/static_tctimer.h" +#include "avilib/static_avilib.h" +#include "avilib/static_wavlib.h" + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ |
