summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/encode/encode_x264.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/transcode/transcode-1.1.7/encode/encode_x264.c')
-rw-r--r--debian/transcode/transcode-1.1.7/encode/encode_x264.c1024
1 files changed, 1024 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/encode/encode_x264.c b/debian/transcode/transcode-1.1.7/encode/encode_x264.c
new file mode 100644
index 00000000..b8eac09a
--- /dev/null
+++ b/debian/transcode/transcode-1.1.7/encode/encode_x264.c
@@ -0,0 +1,1024 @@
+/*
+ * encode_x264.c - encodes video using the x264 library
+ * Written by Christian Bodenstedt, with NMS adaptation and other changes
+ * by Andrew Church
+ *
+ * This file is part of transcode, a video stream processing tool.
+ * transcode is free software, distributable under the terms of the GNU
+ * General Public License (version 2 or later). See the file COPYING
+ * for details.
+ */
+
+/*
+ * Many parts of this file are taken from FFMPEGs "libavcodec/x264.c",
+ * which is licensed under LGPL. Other sources of information were
+ * "export_ffmpeg.c", X264s "x264.c" and MPlayers "libmpcodecs/ve_x264.c"
+ * (all licensed GPL afaik).
+ */
+
+
+#include "transcode.h"
+#include "aclib/ac.h"
+#include "libtc/libtc.h"
+#include "libtc/cfgfile.h"
+#include "libtc/optstr.h"
+#include "libtc/tcmodule-plugin.h"
+#include "libtc/ratiocodes.h"
+
+#include <x264.h>
+#if X264_BUILD < 65
+# error x264 version 65 or later is required
+#endif
+
+
+#define MOD_NAME "encode_x264.so"
+#define MOD_VERSION "v0.3.2s (2010-01-02)"
+#define MOD_CAP "x264 encoder"
+
+#define MOD_FEATURES \
+ TC_MODULE_FEATURE_ENCODE|TC_MODULE_FEATURE_VIDEO
+
+#define MOD_FLAGS \
+ TC_MODULE_FLAG_RECONFIGURABLE
+
+
+/* Module configuration file */
+#define X264_CONFIG_FILE "x264.cfg"
+
+/* Private data for this module */
+typedef struct {
+ int framenum;
+ int interval;
+ int width;
+ int height;
+ int flush_flag;
+ x264_param_t x264params;
+ x264_t *enc;
+ int twopass_bug_workaround; // Work around x264 logfile generation bug?
+ char twopass_log_path[4096]; // Logfile path (for 2-pass bug workaround)
+} X264PrivateData;
+
+/* Static structure to provide pointers for configuration entries */
+static struct confdata_struct {
+ x264_param_t x264params;
+ /* Dummy fields for obsolete options */
+ int dummy_direct_8x8;
+ int dummy_bidir_me;
+ int dummy_brdo;
+ /* Local parameters */
+ int twopass_bug_workaround;
+} confdata;
+
+/*************************************************************************/
+
+/* This array describes all option-names, pointers to where their
+ * values are stored and the allowed ranges. It's needed to parse the
+ * x264.cfg file using libtc. */
+
+/* Use e.g. OPTION("overscan", vui.i_overscan) for x264params.vui.i_overscan */
+#define OPTION(field,name,type,flag,low,high) \
+ {name, &confdata.x264params.field, (type), (flag), (low), (high)},
+
+/* Option to turn a flag on or off; the off version will have "no" prepended */
+#define OPT_FLAG(field,name) \
+ OPTION(field, name, TCCONF_TYPE_FLAG, 0, 0, 1) \
+ OPTION(field, "no" name, TCCONF_TYPE_FLAG, 0, 1, 0)
+/* Integer option with range */
+#define OPT_RANGE(field,name,low,high) \
+ OPTION(field, name, TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, (low), (high))
+/* Floating-point option */
+#define OPT_FLOAT(field,name) \
+ OPTION(field, name, TCCONF_TYPE_FLOAT, 0, 0, 0)
+/* Floating-point option with range */
+#define OPT_RANGF(field,name,low,high) \
+ OPTION(field, name, TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, (low), (high))
+/* String option */
+#define OPT_STR(field,name) \
+ OPTION(field, name, TCCONF_TYPE_STRING, 0, 0, 0)
+/* Dummy entry that doesn't generate an option (placeholder) */
+#define OPT_NONE(field) /*nothing*/
+
+static TCConfigEntry conf[] ={
+
+ /* CPU flags */
+
+ /* CPU acceleration flags (we leave the x264 default alone) */
+ OPT_NONE (cpu)
+ /* Divide each frame into multiple slices, encode in parallel */
+ OPT_RANGE(i_threads, "threads", 1, 4)
+
+ /* Video Properties */
+
+ OPT_NONE (i_width)
+ OPT_NONE (i_height)
+ OPT_NONE (i_csp) /* CSP of encoded bitstream, only i420 supported */
+ /* H.264 level (1.0 ... 5.1) */
+ OPT_RANGE(i_level_idc, "level_idc", 10, 51)
+ OPT_NONE (i_frame_total) /* number of frames to encode if known, else 0 */
+
+ /* they will be reduced to be 0 < x <= 65535 and prime */
+ OPT_NONE (vui.i_sar_height)
+ OPT_NONE (vui.i_sar_width)
+
+ /* 0=undef, 1=show, 2=crop */
+ OPT_RANGE(vui.i_overscan, "overscan", 0, 2)
+
+ /* 0=component 1=PAL 2=NTSC 3=SECAM 4=Mac 5=undef */
+ OPT_RANGE(vui.i_vidformat, "vidformat", 0, 5)
+ OPT_FLAG (vui.b_fullrange, "fullrange")
+ /* 1=bt709 2=undef 4=bt470m 5=bt470bg 6=smpte170m 7=smpte240m 8=film */
+ OPT_RANGE(vui.i_colorprim, "colorprim", 0, 8)
+ /* 1..7 as above, 8=linear, 9=log100, 10=log316 */
+ OPT_RANGE(vui.i_transfer, "transfer", 0, 10)
+ /* 0=GBR 1=bt709 2=undef 4=fcc 5=bt470bg 6=smpte170m 7=smpte240m 8=YCgCo */
+ OPT_RANGE(vui.i_colmatrix, "colmatrix", 0, 8)
+ /* ??? */
+ OPT_RANGE(vui.i_chroma_loc, "chroma_loc", 0, 5)
+
+ OPT_NONE (i_fps_num)
+ OPT_NONE (i_fps_den)
+
+ /* Bitstream parameters */
+
+ /* Maximum number of reference frames */
+ OPT_RANGE(i_frame_reference, "frameref", 1, 16)
+ /* Force an IDR keyframe at this interval */
+ OPT_RANGE(i_keyint_max, "keyint", 1,999999)
+ OPT_RANGE(i_keyint_max, "keyint_max", 1,999999)
+ /* Scenecuts closer together than this are coded as I, not IDR. */
+ OPT_RANGE(i_keyint_min, "keyint_min", 1,999999)
+ /* How aggressively to insert extra I frames */
+ OPT_RANGE(i_scenecut_threshold, "scenecut", -1, 100)
+ /* How many B-frames between 2 reference pictures */
+ OPT_RANGE(i_bframe, "bframes", 0, 16)
+ /* Use adaptive B-frame encoding */
+ OPT_FLAG (i_bframe_adaptive, "b_adapt")
+
+ /* How often B-frames are used */
+ OPT_RANGE(i_bframe_bias, "b_bias", -90, 100)
+ /* Keep some B-frames as references */
+#if X264_BUILD >= 78
+ OPT_RANGE(i_bframe_pyramid, "b_pyramid", 0, 2)
+#else
+ OPT_FLAG (b_bframe_pyramid, "b_pyramid")
+#endif
+
+ /* Use deblocking filter */
+ OPT_FLAG (b_deblocking_filter, "deblock")
+ /* [-6, 6] -6 light filter, 6 strong */
+ OPT_RANGE(i_deblocking_filter_alphac0,"deblockalpha", -6, 6)
+ /* [-6, 6] idem */
+ OPT_RANGE(i_deblocking_filter_beta, "deblockbeta", -6, 6)
+
+ /* Use context-adaptive binary arithmetic coding */
+ OPT_FLAG (b_cabac, "cabac")
+ /* Initial data for CABAC? */
+ OPT_RANGE(i_cabac_init_idc, "cabac_init_idc", 0, 2)
+
+#if X264_BUILD >= 89
+ /* Add NAL HRD parameters to the bitstream */
+ OPT_FLAG (i_nal_hrd, "nal_hrd")
+#endif
+
+ /* Enable interlaced encoding (--encode_fields) */
+ OPT_NONE (b_interlaced)
+#if X264_BUILD >= 89
+ /* First field (1=top, 0=bottom) (--encode_fields) */
+ OPT_NONE (b_tff)
+#endif
+
+ /* Quantization matrix selection: 0=flat 1=JVT 2=custom */
+ OPT_RANGE(i_cqm_preset, "cqm", 0, 2)
+ /* Custom quant matrix filename */
+ OPT_STR (psz_cqm_file, "cqm_file")
+ /* Quant matrix arrays set up by library */
+
+ /* Logging */
+
+ OPT_NONE (pf_log)
+ OPT_NONE (p_log_private)
+ OPT_NONE (i_log_level)
+ OPT_NONE (b_visualize)
+
+ /* Encoder analyser parameters */
+
+ /* Partition selection (we always enable everything) */
+ OPT_NONE (analyse.intra)
+ OPT_NONE (analyse.inter)
+ /* Allow integer 8x8 DCT transforms */
+ OPT_FLAG (analyse.b_transform_8x8, "8x8dct")
+ /* Implicit weighting for B-frames */
+ OPT_FLAG (analyse.b_weighted_bipred, "weight_b")
+ /* Spatial vs temporal MV prediction, 0=none 1=spatial 2=temporal 3=auto */
+ OPT_RANGE(analyse.i_direct_mv_pred, "direct_pred", 0, 3)
+ /* QP difference between chroma and luma */
+ OPT_RANGE(analyse.i_chroma_qp_offset, "chroma_qp_offset",-12, 12)
+
+ /* Motion estimation algorithm to use (X264_ME_*) 0=dia 1=hex 2=umh 3=esa*/
+ OPT_RANGE(analyse.i_me_method, "me", 0, 3)
+ /* Integer pixel motion estimation search range (from predicted MV) */
+ OPT_RANGE(analyse.i_me_range, "me_range", 4, 64)
+ /* Maximum length of a MV (in pixels), 32-2048 or -1=auto */
+ OPT_RANGE(analyse.i_mv_range, "mv_range", -1, 2048)
+ /* Subpixel motion estimation quality: 1=fast, 9=best */
+ OPT_RANGE(analyse.i_subpel_refine, "subq", 1, 9)
+ /* Chroma ME for subpel and mode decision in P-frames */
+ OPT_FLAG (analyse.b_chroma_me, "chroma_me")
+ /* Allow each MB partition in P-frames to have its own reference number */
+ OPT_FLAG (analyse.b_mixed_references, "mixed_refs")
+ /* Trellis RD quantization */
+ OPT_RANGE(analyse.i_trellis, "trellis", 0, 2)
+ /* Early SKIP detection on P-frames */
+ OPT_FLAG (analyse.b_fast_pskip, "fast_pskip")
+ /* Transform coefficient thresholding on P-frames */
+ OPT_FLAG (analyse.b_dct_decimate, "dct_decimate")
+ /* Noise reduction */
+ OPT_RANGE(analyse.i_noise_reduction, "nr", 0, 65536)
+ /* Compute PSNR stats, at the cost of a few % of CPU time */
+ OPT_FLAG (analyse.b_psnr, "psnr")
+ /* Compute SSIM stats, at the cost of a few % of CPU time */
+ OPT_FLAG (analyse.b_ssim, "ssim")
+
+ /* Rate control parameters */
+
+ /* QP value for constant-quality encoding (to be a transcode option,
+ * eventually--FIXME) */
+ OPT_NONE (rc.i_qp_constant)
+ /* Minimum allowed QP value */
+ OPT_RANGE(rc.i_qp_min, "qp_min", 0, 51)
+ /* Maximum allowed QP value */
+ OPT_RANGE(rc.i_qp_max, "qp_max", 0, 51)
+ /* Maximum QP difference between frames */
+ OPT_RANGE(rc.i_qp_step, "qp_step", 0, 50)
+ /* Bitrate (transcode -w) */
+ OPT_NONE (rc.i_bitrate)
+ /* Nominal QP for 1-pass VBR */
+ OPT_RANGF(rc.f_rf_constant, "crf", 0, 51)
+ /* Allowed variance from average bitrate */
+ OPT_FLOAT(rc.f_rate_tolerance, "ratetol")
+ /* Maximum local bitrate (kbit/s) */
+ OPT_RANGE(rc.i_vbv_max_bitrate, "vbv_maxrate", 0,240000)
+ /* Size of VBV buffer for CBR encoding */
+ OPT_RANGE(rc.i_vbv_buffer_size, "vbv_bufsize", 0,240000)
+ /* Initial occupancy of VBV buffer */
+ OPT_RANGF(rc.f_vbv_buffer_init, "vbv_init", 0.0, 1.0)
+ /* QP ratio between I and P frames */
+ OPT_FLOAT(rc.f_ip_factor, "ip_ratio")
+ /* QP ratio between P and B frames */
+ OPT_FLOAT(rc.f_pb_factor, "pb_ratio")
+
+ /* Complexity blurring before QP compression */
+ OPT_RANGF(rc.f_complexity_blur, "cplx_blur", 0.0, 999.0)
+ /* QP curve compression: 0.0 = constant bitrate, 1.0 = constant quality */
+ OPT_RANGF(rc.f_qcompress, "qcomp", 0.0, 1.0)
+ /* QP blurring after compression */
+ OPT_RANGF(rc.f_qblur, "qblur", 0.0, 99.0)
+ /* Rate control override zones (not supported by transcode) */
+ OPT_NONE (rc.zones)
+ OPT_NONE (rc.i_zones)
+ /* Alternate method of specifying zones */
+ OPT_STR (rc.psz_zones, "zones")
+
+ /* Other parameters */
+
+ OPT_FLAG (b_aud, "aud")
+ OPT_NONE (b_repeat_headers)
+ OPT_NONE (i_sps_id)
+
+ /* Module configuration options (which do not affect encoding) */
+
+ {"2pass_bug_workaround", &confdata.twopass_bug_workaround,
+ TCCONF_TYPE_FLAG, 0, 0, 1},
+ {"no2pass_bug_workaround", &confdata.twopass_bug_workaround,
+ TCCONF_TYPE_FLAG, 0, 1, 0},
+
+ /* Obsolete options (scheduled for future removal) */
+
+ {"direct_8x8", &confdata.dummy_direct_8x8, TCCONF_TYPE_FLAG, 0, 0, 1},
+ {"nodirect_8x8", &confdata.dummy_direct_8x8, TCCONF_TYPE_FLAG, 0, 1, 0},
+ {"bidir_me", &confdata.dummy_bidir_me, TCCONF_TYPE_FLAG, 0, 0, 1},
+ {"nobidir_me", &confdata.dummy_bidir_me, TCCONF_TYPE_FLAG, 0, 1, 0},
+ {"brdo", &confdata.dummy_brdo, TCCONF_TYPE_FLAG, 0, 0, 1},
+ {"nobrdo", &confdata.dummy_brdo, TCCONF_TYPE_FLAG, 0, 1, 0},
+
+ {NULL}
+};
+
+/*************************************************************************/
+/*************************************************************************/
+
+/**
+ * x264_log: Logging routine for x264 library.
+ *
+ * Parameters:
+ * userdata: Unused.
+ * level: x264 log level (X264_LOG_*).
+ * format: Log message format string.
+ * args: Log message format arguments.
+ * Return value:
+ * None.
+ */
+
+static void x264_log(void *userdata, int level, const char *format,
+ va_list args)
+{
+ TCLogLevel tclevel;
+ char buf[TC_BUF_MAX];
+
+ if (!format)
+ return;
+ switch (level) {
+ case X264_LOG_ERROR:
+ tclevel = TC_LOG_ERR;
+ break;
+ case X264_LOG_WARNING:
+ tclevel = TC_LOG_WARN;
+ break;
+ case X264_LOG_INFO:
+ if (!(verbose & TC_INFO))
+ return;
+ tclevel = TC_LOG_INFO;
+ break;
+ case X264_LOG_DEBUG:
+ if (!(verbose & TC_DEBUG))
+ return;
+ tclevel = TC_LOG_MSG;
+ break;
+ default:
+ return;
+ }
+ tc_vsnprintf(buf, sizeof(buf), format, args);
+ buf[strcspn(buf,"\r\n")] = 0; /* delete trailing newline */
+ tc_log(tclevel, MOD_NAME, "%s", buf);
+}
+
+/*************************************************************************/
+
+/**
+ * x264params_set_multipass: Does all settings related to multipass.
+ *
+ * Parameters:
+ * pass: 0 = single pass
+ * 1 = 1st pass
+ * 2 = 2nd pass (final pass of multipass encoding)
+ * 3 = Nth pass (intermediate passes of multipass encoding)
+ * statsfilename: where to read and write multipass stat data.
+ * Return value:
+ * Always 0.
+ * Preconditions:
+ * params != NULL
+ * pass == 0 || statsfilename != NULL
+ */
+
+static int x264params_set_multipass(x264_param_t *params,
+ int pass, const char *statsfilename)
+{
+ /* Drop the const and hope that x264 treats it as const anyway */
+ params->rc.psz_stat_in = (char *)statsfilename;
+ params->rc.psz_stat_out = (char *)statsfilename;
+
+ switch (pass) {
+ default:
+ params->rc.b_stat_write = 0;
+ params->rc.b_stat_read = 0;
+ break;
+ case 1:
+ params->rc.b_stat_write = 1;
+ params->rc.b_stat_read = 0;
+ break;
+ case 2:
+ params->rc.b_stat_write = 0;
+ params->rc.b_stat_read = 1;
+ break;
+ case 3:
+ params->rc.b_stat_write = 1;
+ params->rc.b_stat_read = 1;
+ break;
+ }
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * x264params_check: Checks or corrects some strange combinations of
+ * settings done in x264params.
+ *
+ * Parameters:
+ * params: x264_param_t structure to check
+ * Return value:
+ * 0 on success, nonzero otherwise.
+ */
+
+static int x264params_check(x264_param_t *params)
+{
+ /* don't know if these checks are really needed, but they won't hurt */
+ if (params->rc.i_qp_min > params->rc.i_qp_constant) {
+ params->rc.i_qp_min = params->rc.i_qp_constant;
+ }
+ if (params->rc.i_qp_max < params->rc.i_qp_constant) {
+ params->rc.i_qp_max = params->rc.i_qp_constant;
+ }
+
+ if (params->rc.i_rc_method == X264_RC_ABR) {
+ if ((params->rc.i_vbv_max_bitrate > 0)
+ != (params->rc.i_vbv_buffer_size > 0)
+ ) {
+ tc_log_error(MOD_NAME,
+ "VBV requires both vbv_maxrate and vbv_bufsize.");
+ return TC_ERROR;
+ }
+ }
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * x264params_set_by_vob: Handle transcode CLI and tc-autodetection
+ * dependent entries in x264_param_t.
+ *
+ * This method copies various values from transcodes vob_t structure to
+ * x264 $params. That means all settings that can be done through
+ * transcodes CLI or autodetection are applied to x264s $params here
+ * (and I hope nowhere else).
+ *
+ * Parameters:
+ * params: x264_param_t structure to apply changes to
+ * vob: transcodes vob_t structure to copy values from
+ * Return value:
+ * 0 on success, nonzero otherwise.
+ * Preconditions:
+ * params != NULL
+ * vob != NULL
+ */
+
+static int x264params_set_by_vob(x264_param_t *params, const vob_t *vob)
+{
+ /* Set video/bitstream parameters */
+
+ params->i_width = vob->ex_v_width;
+ params->i_height = vob->ex_v_height;
+ params->b_interlaced = (vob->encode_fields==TC_ENCODE_FIELDS_TOP_FIRST
+ || vob->encode_fields==TC_ENCODE_FIELDS_BOTTOM_FIRST);
+#if X264_BUILD >= 89
+ params->b_tff = (vob->encode_fields==TC_ENCODE_FIELDS_TOP_FIRST);
+#endif
+
+ if (params->rc.f_rf_constant != 0) {
+ params->rc.i_rc_method = X264_RC_CRF;
+ } else {
+ params->rc.i_rc_method = X264_RC_ABR;
+ }
+ params->rc.i_bitrate = vob->divxbitrate; /* what a name */
+
+ if (TC_NULL_MATCH == tc_frc_code_to_ratio(vob->ex_frc,
+ &params->i_fps_num,
+ &params->i_fps_den)
+ ) {
+ if (vob->ex_fps > 29.9 && vob->ex_fps < 30) {
+ params->i_fps_num = 30000;
+ params->i_fps_den = 1001;
+ } else if (vob->ex_fps > 23.9 && vob->ex_fps < 24) {
+ params->i_fps_num = 24000;
+ params->i_fps_den = 1001;
+ } else if (vob->ex_fps > 59.9 && vob->ex_fps < 60) {
+ params->i_fps_num = 60000;
+ params->i_fps_den = 1001;
+ } else {
+ params->i_fps_num = vob->ex_fps * 1000;
+ params->i_fps_den = 1000;
+ }
+ }
+
+ if (0 != tc_find_best_aspect_ratio(vob,
+ &params->vui.i_sar_width,
+ &params->vui.i_sar_height,
+ MOD_NAME)
+ ) {
+ tc_log_error(MOD_NAME, "unable to find sane value for SAR");
+ return TC_ERROR;
+ }
+
+ /* Set logging function and acceleration flags */
+ params->pf_log = x264_log;
+ params->p_log_private = NULL;
+ params->cpu &= ~(X264_CPU_MMX
+ | X264_CPU_MMXEXT
+ | X264_CPU_SSE
+ | X264_CPU_SSE2
+ | X264_CPU_SSE3
+ | X264_CPU_SSSE3
+ | X264_CPU_SSE4
+ | X264_CPU_SSE42
+ | X264_CPU_LZCNT);
+ if (tc_accel & AC_MMX) params->cpu |= X264_CPU_MMX;
+ if (tc_accel & AC_MMXEXT) params->cpu |= X264_CPU_MMXEXT;
+ if (tc_accel & AC_SSE) params->cpu |= X264_CPU_SSE;
+ if (tc_accel & AC_SSE2) params->cpu |= X264_CPU_SSE2;
+ if (tc_accel & AC_SSE3) params->cpu |= X264_CPU_SSE3;
+ if (tc_accel & AC_SSSE3) params->cpu |= X264_CPU_SSSE3;
+ if (tc_accel & AC_SSE41) params->cpu |= X264_CPU_SSE4;
+ if (tc_accel & AC_SSE42) params->cpu |= X264_CPU_SSE42;
+ if (tc_accel & AC_SSE4A) params->cpu |= X264_CPU_LZCNT;
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * do_2pass_bug_workaround: Work around a bug present in at least x264
+ * versions 65 through 67 which causes invalid frame numbers to be written
+ * to the 2-pass logfile.
+ *
+ * Parameters:
+ * path: Logfile pathname.
+ * Return value:
+ * 0 on success, nonzero otherwise.
+ * Preconditions:
+ * path != NULL
+ */
+
+static int do_2pass_bug_workaround(const char *path)
+{
+ FILE *fp;
+ char *buffer;
+ long filesize, nread, offset;
+ long nframes;
+
+ fp = fopen(path, "r+");
+ if (!fp) {
+ tc_log_warn(MOD_NAME, "Failed to open 2-pass logfile '%s': %s",
+ path, strerror(errno));
+ goto error_return;
+ }
+
+ /* x264 treats the logfile as a single, semicolon-separated buffer
+ * rather than a series of lines, so do the same here. */
+
+ /* Read in the logfile data */
+ if (fseek(fp, 0, SEEK_END) != 0) {
+ tc_log_warn(MOD_NAME, "Seek to end of 2-pass logfile failed: %s",
+ strerror(errno));
+ goto error_close_file;
+ }
+ filesize = ftell(fp);
+ if (filesize < 0) {
+ tc_log_warn(MOD_NAME, "Get size of 2-pass logfile failed: %s",
+ strerror(errno));
+ goto error_close_file;
+ }
+ buffer = malloc(filesize);
+ if (!buffer) {
+ tc_log_warn(MOD_NAME, "No memory for 2-pass logfile buffer"
+ " (%ld bytes)", filesize);
+ goto error_close_file;
+ }
+ if (fseek(fp, 0, SEEK_SET) != 0) {
+ tc_log_warn(MOD_NAME, "Seek to beginning of 2-pass logfile failed: %s",
+ strerror(errno));
+ goto error_free_buffer;
+ }
+ nread = fread(buffer, 1, filesize, fp);
+ if (nread != filesize) {
+ tc_log_warn(MOD_NAME, "Short read on 2-pass logfile (expected %ld"
+ " bytes, got %ld)", filesize, nread);
+ goto error_free_buffer;
+ }
+
+ /* Count the number of frames */
+ nframes = 0;
+ offset = 0;
+ if (strncmp(buffer, "#options:", 9) == 0) { // just like x264
+ offset = strcspn(buffer, "\n") + 1;
+ }
+ for (; offset < filesize; offset++) {
+ if (buffer[offset] == ';') {
+ nframes++;
+ }
+ }
+
+ /* Go through the frame list and check for out-of-range frame numbers */
+ offset = 0;
+ if (strncmp(buffer, "#options:", 9) == 0) {
+ offset = strcspn(buffer, "\n") + 1;
+ }
+ while (offset < filesize) {
+ long framenum;
+ char *s;
+ if (strncmp(&buffer[offset], "in:", 3) != 0) {
+ tc_log_warn(MOD_NAME, "Can't parse 2-pass logfile at offset %ld,"
+ " giving up.", offset);
+ offset = filesize; // Don't truncate the file
+ break;
+ }
+ framenum = strtol(&buffer[offset+3], &s, 10);
+ if ((s && *s != ' ') || framenum < 0) {
+ tc_log_warn(MOD_NAME, "Can't parse 2-pass logfile at offset %ld,"
+ " giving up.", offset+3);
+ offset = filesize; // Don't truncate the file
+ break;
+ }
+ if (framenum >= nframes) {
+ tc_log_warn(MOD_NAME, "Truncating corrupt x264 logfile:");
+ tc_log_warn(MOD_NAME, " in(%ld) >= nframes(%ld) at offset %ld",
+ framenum, nframes, offset);
+ tc_log_warn(MOD_NAME, "Please report this bug to the x264"
+ " developers.");
+ break; // Truncate the file here
+ }
+ offset += strcspn(&buffer[offset], ";");
+ offset += strspn(&buffer[offset], ";\n");
+ }
+
+ /* Truncate the file if the bug was detected */
+ if (offset < filesize) {
+ if (ftruncate(fileno(fp), offset) != 0) {
+ tc_log_warn(MOD_NAME, "Failed to truncate 2-pass logfile: %s",
+ strerror(errno));
+ goto error_free_buffer;
+ }
+ }
+
+ /* Successful return */
+ free(buffer);
+ fclose(fp);
+ return 0;
+
+ /* Error handling */
+ error_free_buffer:
+ free(buffer);
+ error_close_file:
+ fclose(fp);
+ error_return:
+ return -1;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* Module interface routines and data. */
+
+/*************************************************************************/
+
+/**
+ * x264_init: Initialize this instance of the module. See tcmodule-data.h
+ * for function details.
+ */
+
+static int x264_init(TCModuleInstance *self, uint32_t features)
+{
+ X264PrivateData *pd = NULL;
+
+ TC_MODULE_SELF_CHECK(self, "init");
+ TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
+
+ pd = tc_malloc(sizeof(X264PrivateData));
+ if (!pd) {
+ tc_log_error(MOD_NAME, "init: out of memory!");
+ return TC_ERROR;
+ }
+ pd->framenum = 0;
+ pd->enc = NULL;
+
+ if (verbose) {
+ tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
+ }
+ self->userdata = pd;
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * x264_fini: Clean up after this instance of the module. See
+ * tcmodule-data.h for function details.
+ */
+
+static int x264_fini(TCModuleInstance *self)
+{
+ X264PrivateData *pd = NULL;
+
+ TC_MODULE_SELF_CHECK(self, "fini");
+
+ pd = self->userdata;
+
+ if (pd->enc) {
+ x264_encoder_close(pd->enc);
+ pd->enc = NULL;
+ }
+
+ tc_free(self->userdata);
+ self->userdata = NULL;
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * x264_configure: Configure this instance of the module. See
+ * tcmodule-data.h for function details.
+ */
+
+static int x264_configure(TCModuleInstance *self,
+ const char *options, vob_t *vob)
+{
+ X264PrivateData *pd = NULL;
+ char *s;
+
+ TC_MODULE_SELF_CHECK(self, "configure");
+
+ pd = self->userdata;
+
+ pd->flush_flag = vob->encoder_flush;
+
+ /* Initialize parameter block */
+ memset(&confdata, 0, sizeof(confdata));
+ x264_param_default(&confdata.x264params);
+
+ /* Parameters not (yet) settable via options: */
+ confdata.x264params.analyse.intra = ~0;
+ confdata.x264params.analyse.inter = ~0;
+
+ /* Watch for obsolete options being set */
+ confdata.dummy_direct_8x8 = -1;
+ confdata.dummy_bidir_me = -1;
+ confdata.dummy_brdo = -1;
+
+ /* Read settings from configuration file */
+ module_read_config(X264_CONFIG_FILE, NULL, conf, MOD_NAME);
+
+ /* Parse options given in -y option string (format:
+ * "name1=value1:name2=value2:...") */
+ for (s = (vob->ex_v_string ? strtok(vob->ex_v_string,":") : NULL);
+ s != NULL;
+ s = strtok(NULL,":")
+ ) {
+ if (!module_read_config_line(s, conf, MOD_NAME)) {
+ tc_log_error(MOD_NAME, "Error parsing module options");
+ return TC_ERROR;
+ }
+ }
+
+ /* Complain about obsolete options being set */
+ if (confdata.dummy_direct_8x8 != -1) {
+ tc_log_warn(MOD_NAME, "Option direct_8x8 is obsolete, and is now"
+ " always active.");
+ }
+ if (confdata.dummy_bidir_me != -1) {
+ tc_log_warn(MOD_NAME,
+ "Option bidir_me is obsolete in x264 version 65.\n"
+ " bidir_me will be automatically applied when"
+ " subq >= 5.");
+ }
+ if (confdata.dummy_brdo != -1) {
+ tc_log_warn(MOD_NAME,
+ "Option bidir_me is obsolete in x264 version 65.\n"
+ " brdo will be automatically applied when subq >= 7.");
+ }
+
+ /* Save multipass logfile name if 2-pass bug workaround was requested */
+ if (confdata.twopass_bug_workaround
+ && (vob->divxmultipass == 1 || vob->divxmultipass == 3)
+ ) {
+ const size_t strsize = strlen(vob->divxlogfile) + 1;
+ if (strsize > sizeof(pd->twopass_log_path)) {
+ tc_log_error(MOD_NAME, "2-pass logfile path too long.\n"
+ " Use a shorter pathname or disable the"
+ " 2pass_bug_workaround option.");
+ return TC_ERROR;
+ }
+ ac_memcpy(pd->twopass_log_path, vob->divxlogfile, strsize);
+ pd->twopass_bug_workaround = 1;
+ } else {
+ pd->twopass_bug_workaround = 0;
+ }
+
+ /* Apply extra settings to $x264params */
+ if (0 != x264params_set_multipass(&confdata.x264params, vob->divxmultipass,
+ vob->divxlogfile)
+ ) {
+ tc_log_error(MOD_NAME, "Failed to apply multipass settings.");
+ return TC_ERROR;
+ }
+
+ /* Copy parameter block to module private data */
+ ac_memcpy(&pd->x264params, &confdata.x264params, sizeof(pd->x264params));
+
+ /* Apply transcode CLI and autodetected values from $vob to
+ * $x264params. This is done as the last step to make transcode CLI
+ * override any settings done before. */
+ if (0 != x264params_set_by_vob(&pd->x264params, vob)) {
+ tc_log_error(MOD_NAME, "Failed to evaluate vob_t values.");
+ return TC_ERROR;
+ }
+
+ /* Test if the set parameters fit together. */
+ if (0 != x264params_check(&pd->x264params)) {
+ return TC_ERROR;
+ }
+
+ /* Now we've set all parameters gathered from transcode and the config
+ * file to $x264params. Let's give some status report and finally open
+ * the encoder. */
+ if (verbose >= TC_DEBUG) {
+ module_print_config(conf, MOD_NAME);
+ }
+
+ pd->enc = x264_encoder_open(&pd->x264params);
+ if (!pd->enc) {
+ tc_log_error(MOD_NAME, "x264_encoder_open() returned NULL - sorry.");
+ return TC_ERROR;
+ }
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * x264_stop: Reset this instance of the module. See tcmodule-data.h for
+ * function details.
+ */
+
+static int x264_stop(TCModuleInstance *self)
+{
+ X264PrivateData *pd = NULL;
+
+ TC_MODULE_SELF_CHECK(self, "stop");
+
+ pd = self->userdata;
+
+ if (pd->enc) {
+ x264_encoder_close(pd->enc);
+ pd->enc = NULL;
+ }
+
+ if (pd->twopass_bug_workaround) {
+ do_2pass_bug_workaround(pd->twopass_log_path);
+ }
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * x264_inspect: Return the value of an option in this instance of the
+ * module. See tcmodule-data.h for function details.
+ */
+
+static int x264_inspect(TCModuleInstance *self,
+ const char *param, const char **value)
+{
+ X264PrivateData *pd = NULL;
+ static char buf[TC_BUF_MAX];
+
+ TC_MODULE_SELF_CHECK(self, "inspect");
+ TC_MODULE_SELF_CHECK(param, "inspect");
+ TC_MODULE_SELF_CHECK(value, "inspect");
+
+ pd = self->userdata;
+
+ if (optstr_lookup(param, "help")) {
+ tc_snprintf(buf, sizeof(buf),
+"Overview:\n"
+" Encodes video in h.264 format using the x264 library.\n"
+"Options available:\n"
+" All options in x264.cfg can be specified on the command line\n"
+" using the format: -y x264=name1=value1:name2=value2:...\n");
+ *value = buf;
+ }
+ /* FIXME: go through the option list to find a match to param */
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+/**
+ * x264_encode_video: Decode a frame of data. See tcmodule-data.h for
+ * function details.
+ */
+
+static int x264_encode_video(TCModuleInstance *self,
+ vframe_list_t *inframe, vframe_list_t *outframe)
+{
+ X264PrivateData *pd;
+ x264_nal_t *nal;
+ x264_picture_t pic, pic_out;
+ int nnal, i, ret;
+
+ TC_MODULE_SELF_CHECK(self, "encode_video");
+
+ pd = self->userdata;
+
+ pd->framenum++;
+
+ memset(&pic, 0, sizeof(pic));
+ memset(&pic_out, 0, sizeof(pic_out));
+
+ if (inframe == NULL) {
+ outframe->video_len = 0;
+ return TC_OK;
+ }
+
+ pic.img.i_csp = X264_CSP_I420;
+ pic.img.i_plane = 3;
+
+ pic.img.plane[0] = inframe->video_buf;
+ pic.img.i_stride[0] = inframe->v_width;
+
+ pic.img.plane[1] = pic.img.plane[0]
+ + inframe->v_width*inframe->v_height;
+ pic.img.i_stride[1] = inframe->v_width / 2;
+
+ pic.img.plane[2] = pic.img.plane[1]
+ + (inframe->v_width/2)*(inframe->v_height/2);
+ pic.img.i_stride[2] = inframe->v_width / 2;
+
+
+ pic.i_type = X264_TYPE_AUTO;
+ pic.i_qpplus1 = 0;
+ /* FIXME: Is this pts-handling ok? I don't have a clue how
+ * PTS/DTS handling works. Does it matter, when no muxing is
+ * done? */
+ pic.i_pts = (int64_t) pd->framenum * pd->x264params.i_fps_den;
+
+ ret = x264_encoder_encode(pd->enc, &nal, &nnal, &pic, &pic_out);
+#if X264_BUILD >= 76
+ if (ret < 0) {
+#else
+ if (ret != 0) {
+#endif
+ return TC_ERROR;
+ }
+
+ outframe->video_len = 0;
+ for (i = 0; i < nnal; i++) {
+ int size = outframe->video_size - outframe->video_len;
+ if (size <= 0) {
+ tc_log_error(MOD_NAME, "output buffer overflow");
+ return TC_ERROR;
+ }
+#if X264_BUILD >= 76
+ ac_memcpy(outframe->video_buf + outframe->video_len, nal[i].p_payload, nal[i].i_payload);
+ outframe->video_len += nal[i].i_payload;
+#else
+ ret = x264_nal_encode(outframe->video_buf + outframe->video_len,
+ &size, 1, &nal[i]);
+ if (ret < 0 || size > outframe->video_size - outframe->video_len) {
+ tc_log_error(MOD_NAME, "output buffer overflow");
+ break;
+ }
+ outframe->video_len += size;
+#endif
+ }
+
+ /* FIXME: ok, that sucks. How to reformat it ina better way? -- fromani */
+ if ((pic_out.i_type == X264_TYPE_IDR)
+ || (pic_out.i_type == X264_TYPE_I
+ && pd->x264params.i_frame_reference == 1
+ && !pd->x264params.i_bframe)) {
+ outframe->attributes |= TC_FRAME_IS_KEYFRAME;
+ }
+
+ return TC_OK;
+}
+
+/*************************************************************************/
+
+static const TCCodecID x264_codecs_in[] = { TC_CODEC_YUV420P, TC_CODEC_ERROR };
+static const TCCodecID x264_codecs_out[] = { TC_CODEC_H264, TC_CODEC_ERROR };
+TC_MODULE_CODEC_FORMATS(x264);
+
+TC_MODULE_INFO(x264);
+
+static const TCModuleClass x264_class = {
+ TC_MODULE_CLASS_HEAD(x264),
+
+ .init = x264_init,
+ .fini = x264_fini,
+ .configure = x264_configure,
+ .stop = x264_stop,
+ .inspect = x264_inspect,
+
+ .encode_video = x264_encode_video,
+};
+
+TC_MODULE_ENTRY_POINT(x264)
+
+/*************************************************************************/
+
+/*
+ * Local variables:
+ * c-file-style: "stroustrup"
+ * c-file-offsets: ((case-label . *) (statement-case-intro . *))
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+ */
+