summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/src/transcode.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/transcode/transcode-1.1.7/src/transcode.c')
-rw-r--r--debian/transcode/transcode-1.1.7/src/transcode.c2863
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:
+ */