You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1025 lines
33 KiB

/*
* 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:
*/