diff options
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.c | 1024 |
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, + ¶ms->i_fps_num, + ¶ms->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, + ¶ms->vui.i_sar_width, + ¶ms->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: + */ + |
