--- /dev/null
+/* GStreamer
+ *
+ * FFMpeg Configuration
+ *
+ * Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstffmpeg.h"
+#include "gstffmpegenc.h"
+
+#include <string.h>
+
+/* some enums used in property declarations */
+
+#define GST_TYPE_FFMPEG_PASS (gst_ffmpeg_pass_get_type ())
+static GType
+gst_ffmpeg_pass_get_type (void)
+{
+ static GType ffmpeg_pass_type = 0;
+
+ if (!ffmpeg_pass_type) {
+ static const GEnumValue ffmpeg_passes[] = {
+ {0, "Constant Bitrate Encoding", "cbr"},
+ {CODEC_FLAG_QSCALE, "Constant Quantizer", "quant"},
+ {CODEC_FLAG_PASS1, "VBR Encoding - Pass 1", "pass1"},
+ {CODEC_FLAG_PASS2, "VBR Encoding - Pass 2", "pass2"},
+ {0, NULL, NULL},
+ };
+
+ ffmpeg_pass_type =
+ g_enum_register_static ("GstFFMpegEncPass", ffmpeg_passes);
+ }
+
+ return ffmpeg_pass_type;
+}
+
+#if 0
+/* some do not support 2-pass */
+#define GST_TYPE_FFMPEG_LIM_PASS (gst_ffmpeg_lim_pass_get_type ())
+static GType
+gst_ffmpeg_lim_pass_get_type (void)
+{
+ static GType ffmpeg_lim_pass_type = 0;
+
+ if (!ffmpeg_lim_pass_type) {
+ static const GEnumValue ffmpeg_lim_passes[] = {
+ {0, "Constant Bitrate Encoding", "cbr"},
+ {CODEC_FLAG_QSCALE, "Constant Quantizer", "quant"},
+ {0, NULL, NULL},
+ };
+
+ ffmpeg_lim_pass_type =
+ g_enum_register_static ("GstFFMpegEncLimPass", ffmpeg_lim_passes);
+ }
+
+ return ffmpeg_lim_pass_type;
+}
+#endif
+
+#define GST_TYPE_FFMPEG_MB_DECISION (gst_ffmpeg_mb_decision_get_type ())
+static GType
+gst_ffmpeg_mb_decision_get_type (void)
+{
+ static GType ffmpeg_mb_decision_type = 0;
+
+ if (!ffmpeg_mb_decision_type) {
+ static const GEnumValue ffmpeg_mb_decisions[] = {
+ {FF_MB_DECISION_SIMPLE, "Use method set by mb-cmp", "simple"},
+ {FF_MB_DECISION_BITS, "Chooses the one which needs the fewest bits aka vhq mode", "bits"},
+ {FF_MB_DECISION_RD, "Rate Distortion", "rd"},
+ {0, NULL, NULL},
+ };
+
+ ffmpeg_mb_decision_type =
+ g_enum_register_static ("GstFFMpegEncMBDecision", ffmpeg_mb_decisions);
+ }
+
+ return ffmpeg_mb_decision_type;
+}
+
+#define GST_TYPE_FFMPEG_CMP_FUNCTION (gst_ffmpeg_mb_cmp_get_type ())
+static GType
+gst_ffmpeg_mb_cmp_get_type (void)
+{
+ static GType ffmpeg_mb_cmp_type = 0;
+
+ /* TODO fill out remaining values */
+ if (!ffmpeg_mb_cmp_type) {
+ static const GEnumValue ffmpeg_mb_cmps[] = {
+ {FF_CMP_SAD, "Sum of Absolute Differences", "sad"},
+ {FF_CMP_SSE, "Sum of Squared Errors", "sse"},
+ {FF_CMP_SATD, "Sum of Absolute Hadamard Transformed Differences", "satd"},
+ {FF_CMP_DCT, "Sum of Absolute DCT Transformed Differences", "dct"},
+ {FF_CMP_PSNR, "Sum of the Squared Quantization Errors", "psnr"},
+ {FF_CMP_BIT, "Sum of the Bits needed for the block", "bit"},
+ {FF_CMP_RD, "Rate Distortion optimal", "rd"},
+ {FF_CMP_ZERO, "ZERO", "zero"},
+ {FF_CMP_VSAD, "VSAD", "vsad"},
+ {FF_CMP_VSSE, "VSSE", "vsse"},
+#if 0
+/* economize a bit for now */
+ {FF_CMP_NSSE, "NSSE", "nsse"},
+ {FF_CMP_W53, "W53", "w53"},
+ {FF_CMP_W97, "W97", "w97"},
+#endif
+ {0, NULL, NULL},
+ };
+
+ ffmpeg_mb_cmp_type =
+ g_enum_register_static ("GstFFMpegCMPFunction", ffmpeg_mb_cmps);
+ }
+
+ return ffmpeg_mb_cmp_type;
+}
+
+#define GST_TYPE_FFMPEG_DCT_ALGO (gst_ffmpeg_dct_algo_get_type ())
+static GType
+gst_ffmpeg_dct_algo_get_type (void)
+{
+ static GType ffmpeg_dct_algo_type = 0;
+
+ if (!ffmpeg_dct_algo_type) {
+ static const GEnumValue ffmpeg_dct_algos[] = {
+ {FF_DCT_AUTO, "Automatically select a good one", "auto"},
+ {FF_DCT_FASTINT, "Fast Integer", "fastint"},
+ {FF_DCT_INT, "Accurate Integer", "int"},
+ {FF_DCT_MMX, "MMX", "mmx"},
+ {FF_DCT_MLIB, "MLIB", "mlib"},
+ {FF_DCT_ALTIVEC, "ALTIVEC", "altivec"},
+ {FF_DCT_FAAN, "FAAN", "faan"},
+ {0, NULL, NULL},
+ };
+
+ ffmpeg_dct_algo_type =
+ g_enum_register_static ("GstFFMpegDCTAlgo", ffmpeg_dct_algos);
+ }
+
+ return ffmpeg_dct_algo_type;
+}
+
+#define GST_TYPE_FFMPEG_IDCT_ALGO (gst_ffmpeg_idct_algo_get_type ())
+static GType
+gst_ffmpeg_idct_algo_get_type (void)
+{
+ static GType ffmpeg_idct_algo_type = 0;
+
+ if (!ffmpeg_idct_algo_type) {
+ static const GEnumValue ffmpeg_idct_algos[] = {
+ {FF_IDCT_AUTO, "Automatically select a good one", "auto"},
+ {FF_IDCT_INT, "JPEG reference Integer", "int"},
+ {FF_IDCT_SIMPLE, "Simple", "simple"},
+ {FF_IDCT_SIMPLEMMX, "Simple MMX", "simplemmx"},
+ {FF_IDCT_LIBMPEG2MMX, "LIBMPEG2MMX", "libmpeg2mmx"},
+ {FF_IDCT_PS2, "PS2", "ps2"},
+ {FF_IDCT_MLIB, "MLIB", "mlib"},
+ {FF_IDCT_ARM, "ARM", "arm"},
+ {FF_IDCT_ALTIVEC, "ALTIVEC", "altivec"},
+ {FF_IDCT_SH4, "SH4", "sh4"},
+ {FF_IDCT_SIMPLEARM, "SIMPLEARM", "simplearm"},
+ {FF_IDCT_H264, "H264", "h264"},
+ {FF_IDCT_VP3, "VP3", "vp3"},
+ {FF_IDCT_IPP, "IPP", "ipp"},
+ {FF_IDCT_XVIDMMX, "XVIDMMX", "xvidmmx"},
+ {0, NULL, NULL},
+ };
+
+ ffmpeg_idct_algo_type =
+ g_enum_register_static ("GstFFMpegIDCTAlgo", ffmpeg_idct_algos);
+ }
+
+ return ffmpeg_idct_algo_type;
+}
+
+#define GST_TYPE_FFMPEG_QUANT_TYPE (gst_ffmpeg_quant_type_get_type ())
+static GType
+gst_ffmpeg_quant_type_get_type (void)
+{
+ static GType ffmpeg_quant_type_type = 0;
+
+ if (!ffmpeg_quant_type_type) {
+ static const GEnumValue ffmpeg_quant_types[] = {
+ {0, "H263 quantization", "h263"},
+ {1, "MPEG quantization", "mpeg"},
+ {0, NULL, NULL},
+ };
+
+ ffmpeg_quant_type_type =
+ g_enum_register_static ("GstFFMpegEncQuantTypes", ffmpeg_quant_types);
+ }
+
+ return ffmpeg_quant_type_type;
+}
+
+#define GST_TYPE_FFMPEG_PRE_ME (gst_ffmpeg_pre_me_get_type ())
+static GType
+gst_ffmpeg_pre_me_get_type (void)
+{
+ static GType ffmpeg_pre_me_type = 0;
+
+ if (!ffmpeg_pre_me_type) {
+ static const GEnumValue ffmpeg_pre_mes[] = {
+ {0, "Disabled", "off"},
+ {1, "Only after I-frames", "key"},
+ {2, "Always", "all"},
+ };
+
+ ffmpeg_pre_me_type =
+ g_enum_register_static ("GstFFMpegEncPreME", ffmpeg_pre_mes);
+ }
+
+ return ffmpeg_pre_me_type;
+}
+
+#define GST_TYPE_FFMPEG_PRED_METHOD (gst_ffmpeg_pred_method_get_type ())
+static GType
+gst_ffmpeg_pred_method_get_type (void)
+{
+ static GType ffmpeg_pred_method = 0;
+
+ if (!ffmpeg_pred_method) {
+ static const GEnumValue ffmpeg_pred_methods[] = {
+ {FF_PRED_LEFT, "Left", "left"},
+ {FF_PRED_PLANE, "Plane", "plane"},
+ {FF_PRED_MEDIAN, "Median", "median"},
+ };
+
+ ffmpeg_pred_method =
+ g_enum_register_static ("GstFFMpegEncPredMethod", ffmpeg_pred_methods);
+ }
+
+ return ffmpeg_pred_method;
+}
+
+#define GST_TYPE_FFMPEG_FLAGS (gst_ffmpeg_flags_get_type())
+static GType
+gst_ffmpeg_flags_get_type (void)
+{
+ static GType ffmpeg_flags_type = 0;
+
+ if (!ffmpeg_flags_type) {
+ static const GFlagsValue ffmpeg_flags[] = {
+ {CODEC_FLAG_4MV, "Allow 4 MV per MB", "4mv"},
+ {CODEC_FLAG_QPEL, "Quartel Pel Motion Compensation", "qpel"},
+ {CODEC_FLAG_GMC, "GMC", "gmc"},
+ {CODEC_FLAG_MV0, "Always try a MB with MV (0,0)", "mv0"},
+ {CODEC_FLAG_PART, "Store MV, DC and AC coefficients in seperate partitions", "part"},
+ {CODEC_FLAG_GRAY, "Only decode/encode grayscale", "gray"},
+ {CODEC_FLAG_NORMALIZE_AQP, "Normalize Adaptive Quantization (masking, etc)", "aqp"},
+ {CODEC_FLAG_TRELLIS_QUANT, "Trellis Quantization", "trellis"},
+ {CODEC_FLAG_AC_PRED, "H263 Advanced Intra Coding / MPEG4 AC prediction", "aic"},
+ {CODEC_FLAG_H263P_UMV, "Unlimited Motion Vector", "umv"},
+ {CODEC_FLAG_CBP_RD, "Rate Distoration Optimization for CBP", "cbp-rd"},
+ {CODEC_FLAG_QP_RD, "Rate Distoration Optimization for QP selection", "qp-rd"},
+ {CODEC_FLAG_SVCD_SCAN_OFFSET, "Reserve space for SVC scan offset user data", "scanoffset"},
+ {CODEC_FLAG_CLOSED_GOP, "Closed GOP", "closedgop"},
+ {0, NULL, NULL},
+ };
+
+ ffmpeg_flags_type =
+ g_flags_register_static ("GstFFMpegFlags", ffmpeg_flags);
+ }
+
+ return ffmpeg_flags_type;
+}
+
+/* provides additional info to attach to a property */
+
+typedef struct _GParamSpecData GParamSpecData;
+
+struct _GParamSpecData
+{
+ /* offset of member in the element struct that stores the property */
+ guint offset;
+
+ /* size of the above member */
+ guint size;
+
+ /* if TRUE, try to get the default from lavc and ignore the paramspec default */
+ gboolean lavc_default;
+
+ /* these lists are arrays terminated by CODEC_ID_NONE entry:
+ * property applies to a codec if it's not in the exclude_list
+ * and in exclude_list (or the latter is NULL) */
+ gint *include_list;
+ gint *exclude_list;
+};
+
+/* properties whose member offset is higher than the config base
+ * can be copied directly at context configuration time;
+ * and can also retrieve a default value from lavc */
+#define CONTEXT_CONFIG_OFFSET G_STRUCT_OFFSET (GstFFMpegEnc, config)
+
+/* additional info is named pointer specified by the quark */
+static GQuark quark;
+
+/* central configuration store:
+ * list of GParamSpec's with GParamSpecData attached as named pointer */
+static GList *property_list;
+
+/* add the GParamSpec pspec to store with GParamSpecData
+ * constructed from struct_type, member, default and include and exclude */
+#define gst_ffmpeg_add_pspec_full(pspec, store, struct_type, member, \
+ default, include, exclude) \
+G_STMT_START { \
+ GParamSpecData *_qdata = g_new0 (GParamSpecData, 1); \
+ GstFFMpegEnc _enc; \
+ _qdata->offset = G_STRUCT_OFFSET (struct_type, member); \
+ _qdata->size = sizeof (_enc.member); \
+ _qdata->lavc_default = default; \
+ _qdata->include_list = include; \
+ _qdata->exclude_list = exclude; \
+ g_param_spec_set_qdata_full (pspec, quark, _qdata, g_free); \
+ store = g_list_append (store, pspec); \
+} G_STMT_END
+
+#define gst_ffmpeg_add_pspec(pspec, member, default, include, exclude) \
+ gst_ffmpeg_add_pspec_full (pspec, property_list, GstFFMpegEnc, member, \
+ default, include, exclude)
+
+/* ==== BEGIN CONFIGURATION SECTION ==== */
+
+/* some typical include and exclude lists; modify and/or add where needed */
+#if 0
+static gint mpeg12[] = {
+ CODEC_ID_MPEG1VIDEO,
+ CODEC_ID_MPEG2VIDEO,
+ CODEC_ID_NONE
+};
+#endif
+
+static gint mpeg4[] = {
+ CODEC_ID_MPEG4,
+ CODEC_ID_MSMPEG4V1,
+ CODEC_ID_MSMPEG4V2,
+ CODEC_ID_MSMPEG4V3,
+ CODEC_ID_NONE
+};
+
+static gint huffyuv[] = {
+ CODEC_ID_HUFFYUV,
+ CODEC_ID_FFVHUFF,
+ CODEC_ID_NONE
+};
+
+/* Properties should be added here for registration into the config store.
+ * Note that some may occur more than once, with different include/exclude lists,
+ * as some may require different defaults for different codecs,
+ * or some may have slightly varying enum-types with more or less options.
+ * The enum-types themselves should be declared above. */
+void
+gst_ffmpeg_cfg_init ()
+{
+ GParamSpec *pspec;
+
+ /* initialize global config vars */
+ quark = g_quark_from_static_string ("ffmpeg-cfg-param-spec-data");
+ property_list = NULL;
+
+ /* list properties here */
+ pspec = g_param_spec_enum ("pass", "Encoding pass/type",
+ "Encoding pass/type", GST_TYPE_FFMPEG_PASS,
+ 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, pass, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("quantizer", "Constant Quantizer",
+ "Constant Quantizer", 0, 30, 0.01f,
+ G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, quantizer, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_string ("statsfile", "Statistics Filename",
+ "Filename to store data for 2-pass encoding", "stats.log",
+ G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, filename, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("bitrate-tolerance", "Bitrate Tolerance",
+ "Number of bits the bitstream is allowed to diverge from the reference",
+ 0, 100000000, 8000000, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.bit_rate_tolerance, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("mb-decision", "Macroblock Decision",
+ "Macroblok Decision Mode",
+ GST_TYPE_FFMPEG_MB_DECISION, FF_CMP_SAD, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.mb_decision, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("mb-cmp", "Macroblock Compare Function",
+ "Macroblok Compare Function",
+ GST_TYPE_FFMPEG_CMP_FUNCTION, FF_CMP_SAD, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.mb_cmp, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("me-pre-cmp", "Motion Estimation Pre Pass Compare Function",
+ "Motion Estimation Pre Pass Compare Function",
+ GST_TYPE_FFMPEG_CMP_FUNCTION, FF_CMP_SAD, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.me_pre_cmp, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("me-cmp", "Motion Estimation Compare Function",
+ "Motion Estimation Compare Function",
+ GST_TYPE_FFMPEG_CMP_FUNCTION, FF_CMP_SAD, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.me_cmp, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("me-sub-cmp",
+ "Subpixel Motion Estimation Compare Function",
+ "Subpixel Motion Estimation Compare Function",
+ GST_TYPE_FFMPEG_CMP_FUNCTION, FF_CMP_SAD, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.me_sub_cmp, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("ildct-cmp", "Interlaced DCT Compare Function",
+ "Interlaced DCT Compare Function",
+ GST_TYPE_FFMPEG_CMP_FUNCTION, FF_CMP_VSAD, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.ildct_cmp, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("dct-algo", "DCT Algorithm",
+ "DCT Algorithm",
+ GST_TYPE_FFMPEG_DCT_ALGO, FF_DCT_AUTO, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.dct_algo, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("idct-algo", "IDCT Algorithm",
+ "IDCT Algorithm",
+ GST_TYPE_FFMPEG_IDCT_ALGO, FF_IDCT_AUTO, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.idct_algo, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("quant-type", "Quantizer Type",
+ "Quantizer Type",
+ GST_TYPE_FFMPEG_QUANT_TYPE, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.mpeg_quant, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("qmin", "Minimum Quantizer",
+ "Minimum Quantizer",
+ 1, 31, 2, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.qmin, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("qmax", "Maximum Quantizer",
+ "Maximum Quantizer",
+ 1, 31, 31, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.qmax, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("max-qdiff", "Maximum Quantizer Difference",
+ "Maximum Quantizer Difference between frames",
+ 1, 31, 3, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.max_qdiff, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("mb_qmin", "Minimum MB Quantizer",
+ "Minimum MB Quantizer",
+ 0, 31, 2, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.mb_qmin, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("mb_qmax", "Maximum MB Quantizer",
+ "Maximum MB Quantizer",
+ 0, 31, 31, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.mb_qmax, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("lmin", "Minimum Lagrange Multiplier",
+ "Minimum Lagrange Multiplier",
+ 1, 31, 2, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, lmin, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("lmax", "Maximum Lagrange Multiplier",
+ "Maximum Lagrange Multiplier",
+ 1, 31, 31, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, lmax, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("qcompress", "Quantizer Change",
+ "Quantizer Change between easy and hard scenes",
+ 0, 1.0f, 0.5f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.qcompress, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("qblur", "Quantizer Smoothing",
+ "Quantizer Smoothing over time",
+ 0, 1.0f, 0.5f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.qblur, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("rc-qsquish", "Ratecontrol Limiting Method",
+ "0 means limit by clipping, otherwise use nice continuous function",
+ 0, 99.0f, 1.0f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_qsquish, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("rc-qmod-amp", "Ratecontrol Mod",
+ "Ratecontrol Mod",
+ 0, 99.0f, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_qmod_amp, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("rc-qmod-freq", "Ratecontrol Freq",
+ "Ratecontrol Freq",
+ 0, 0, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_qmod_freq, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("rc-buffer-size", "Ratecontrol Buffer Size",
+ "Decoder bitstream buffer size",
+ 0, G_MAXINT, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_buffer_size, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("rc-buffer-aggressivity", "Ratecontrol Buffer Aggressivity",
+ "Ratecontrol Buffer Aggressivity",
+ 0, 99.0f, 1.0f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_buffer_aggressivity, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("rc-max-rate", "Ratecontrol Maximum Bitrate",
+ "Ratecontrol Maximum Bitrate",
+ 0, G_MAXINT, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_max_rate, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("rc-min-rate", "Ratecontrol Minimum Bitrate",
+ "Ratecontrol Minimum Bitrate",
+ 0, G_MAXINT, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_min_rate, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("rc-initial-cplx", "Initial Complexity for Pass 1 Ratecontrol",
+ "Initial Complexity for Pass 1 Ratecontrol",
+ 0, 9999999.0f, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_initial_cplx, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_string ("rc-eq", "Ratecontrol Equation",
+ "Ratecontrol Equation",
+ "tex^qComp", G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.rc_eq, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("b-quant-factor", "B-Quantizer Factor",
+ "Factor in B-Frame Quantizer Computation",
+ -31.0f, 31.0f, 1.25f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.b_quant_factor, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("b-quant-offset", "B-Quantizer Offset",
+ "Offset in B-Frame Quantizer Computation",
+ 0.0f, 31.0f, 1.25f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.b_quant_offset, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("i-quant-factor", "I-Quantizer Factor",
+ "Factor in P-Frame Quantizer Computation",
+ -31.0f, 31.0f, 0.8f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.i_quant_factor, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("i-quant-offset", "I-Quantizer Offset",
+ "Offset in P-Frame Quantizer Computation",
+ 0.0f, 31.0f, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.i_quant_offset, FALSE, mpeg4, NULL);
+
+ /* note overlap with gop-size; 0 means do not override */
+ pspec = g_param_spec_int ("max-key-interval", "Maximum Key Interval",
+ "Maximum number of frames between two keyframes (< 0 is in sec)",
+ -100, G_MAXINT, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, max_key_interval, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("luma-elim-threshold",
+ "Luma Elimination Threshold",
+ "Luma Single Coefficient Elimination Threshold",
+ -99, 99, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.luma_elim_threshold, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("chroma-elim-threshold",
+ "Chroma Elimination Threshold",
+ "Chroma Single Coefficient Elimination Threshold",
+ -99, 99, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.chroma_elim_threshold, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("lumi-masking", "Luminance Masking",
+ "Luminance Masking",
+ -1.0f, 1.0f, 0.0f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.lumi_masking, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("dark-masking", "Darkness Masking",
+ "Darkness Masking",
+ -1.0f, 1.0f, 0.0f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.dark_masking, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("temporal-cplx-masking",
+ "Temporal Complexity Masking",
+ "Temporal Complexity Masking",
+ -1.0f, 1.0f, 0.0f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.temporal_cplx_masking, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("spatial-cplx-masking",
+ "Spatial Complexity Masking",
+ "Spatial Complexity Masking",
+ -1.0f, 1.0f, 0.0f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.spatial_cplx_masking, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_float ("p-masking", "P Block Masking",
+ "P Block Masking",
+ -1.0f, 1.0f, 0.0f, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.p_masking, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("dia-size",
+ "Motion Estimation Diamond Size/Shape",
+ "Motion Estimation Diamond Size/Shape",
+ -2000, 2000, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.dia_size, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("pre-dia-size",
+ "Motion Estimation Pre Pass Diamond Size/Shape",
+ "Motion Estimation Diamond Size/Shape",
+ -2000, 2000, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.pre_dia_size, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("last-predictor-count",
+ "Last Predictor Count",
+ "Amount of previous Motion Vector predictors",
+ 0, 2000, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.last_predictor_count, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("pre-me",
+ "Pre Pass for Motion Estimation",
+ "Pre Pass for Motion Estimation",
+ GST_TYPE_FFMPEG_PRE_ME, 1, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.pre_me, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("me-subpel-quality",
+ "Motion Estimation Subpixel Quality",
+ "Motion Estimation Subpixel Refinement Quality",
+ 0, 8, 8, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.me_subpel_quality, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("me-range",
+ "Motion Estimation Range",
+ "Motion Estimation search range in subpel units",
+ 0, 16000, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.me_range, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("intra-quant-bias",
+ "Intra Quantizer Bias",
+ "Intra Quantizer Bias",
+ -1000000, 1000000, FF_DEFAULT_QUANT_BIAS, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.intra_quant_bias, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("inter-quant-bias",
+ "Inter Quantizer Bias",
+ "Inter Quantizer Bias",
+ -1000000, 1000000, FF_DEFAULT_QUANT_BIAS, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.inter_quant_bias, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("noise-reduction",
+ "Noise Reduction",
+ "Noise Reduction Strength",
+ 0, 1000000, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.noise_reduction, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("intra-dc-precision",
+ "Intra DC precision",
+ "Precision of the Intra DC coefficient - 8",
+ 0, 16, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.intra_dc_precision, FALSE, mpeg4, NULL);
+
+ /* TODO skipped coder_type, context_model, inter_threshold, scenechange_threshold */
+
+ pspec = g_param_spec_flags ("flags", "Flags",
+ "Flags",
+ GST_TYPE_FFMPEG_FLAGS, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.flags, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_boolean ("interlaced", "Interlaced Material",
+ "Interlaced Material",
+ FALSE, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, interlaced, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_int ("max-bframes", "Max B-Frames",
+ "Maximum B-frames in a row",
+ 0, FF_MAX_B_FRAMES, 0, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.max_b_frames, FALSE, mpeg4, NULL);
+
+ pspec = g_param_spec_enum ("prediction-method", "Prediction Method",
+ "Prediction Method",
+ GST_TYPE_FFMPEG_PRED_METHOD, FF_PRED_LEFT, G_PARAM_READWRITE);
+ gst_ffmpeg_add_pspec (pspec, config.prediction_method, FALSE, huffyuv, NULL);
+}
+
+/* ==== END CONFIGURATION SECTION ==== */
+
+
+/* return TRUE if property described by pspec applies to the codec with codec_id */
+static gboolean
+gst_ffmpeg_cfg_codec_has_pspec (enum CodecID codec_id, GParamSpec *pspec)
+{
+ GParamSpecData *qdata;
+ gint* codec;
+ gboolean ret = FALSE;
+
+ qdata = g_param_spec_get_qdata (pspec, quark);
+
+ /* check if excluded first */
+ if ((codec = qdata->exclude_list)) {
+ for ( ; *codec != CODEC_ID_NONE; ++codec) {
+ if (*codec == codec_id)
+ return FALSE;
+ }
+ }
+
+ /* no include list means it is accepted */
+ if ((codec = qdata->include_list)) {
+ for ( ; *codec != CODEC_ID_NONE; ++codec) {
+ if (*codec == codec_id)
+ ret = TRUE;
+ }
+ } else {
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+/* install all properties for klass that have been registered in property_list */
+void
+gst_ffmpeg_cfg_install_property (GstFFMpegEncClass * klass, guint base)
+{
+ GParamSpec *pspec;
+ GList *list;
+ gint prop_id;
+ AVCodecContext *ctx;
+
+ prop_id = base;
+ g_return_if_fail (base > 0);
+
+ ctx = avcodec_alloc_context();
+ if (ctx)
+ avcodec_get_context_defaults (ctx);
+ else
+ g_warning ("could not get context");
+
+ for (list = property_list; list; list = list->next) {
+ pspec = G_PARAM_SPEC (list->data);
+ if (gst_ffmpeg_cfg_codec_has_pspec (klass->in_plugin->id, pspec)) {
+ /* 'clone' the paramspec for the various codecs,
+ * since a single paramspec cannot be owned by distinct types */
+
+ const gchar* name = g_param_spec_get_name (pspec);
+ const gchar* nick = g_param_spec_get_nick (pspec);
+ const gchar* blurb = g_param_spec_get_blurb (pspec);
+ GParamSpecData *qdata = g_param_spec_get_qdata (pspec, quark);
+ gint ctx_offset = 0;
+ gboolean lavc_default;
+
+ /* cannot obtain lavc default if no context */
+ if (!ctx)
+ lavc_default = FALSE;
+ else {
+ ctx_offset = qdata->offset - CONTEXT_CONFIG_OFFSET;
+ /* safety check; is it really member of the avcodec context */
+ if (ctx_offset < 0)
+ lavc_default = FALSE;
+ else
+ lavc_default = qdata->lavc_default;
+ }
+
+ switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) {
+ case G_TYPE_STRING: {
+ GParamSpecString* pstring = G_PARAM_SPEC_STRING (pspec);
+ pspec = g_param_spec_string (name, nick, blurb,
+ lavc_default ? G_STRUCT_MEMBER (gchar*, ctx, ctx_offset)
+ : pstring->default_value,
+ pspec->flags);
+ break;
+ }
+ case G_TYPE_INT: {
+ GParamSpecInt* pint = G_PARAM_SPEC_INT (pspec);
+ pspec = g_param_spec_int (name, nick, blurb,
+ pint->minimum, pint->maximum,
+ lavc_default ? G_STRUCT_MEMBER (gint, ctx, ctx_offset)
+ : pint->default_value,
+ pspec->flags);
+ break;
+ }
+ case G_TYPE_UINT: {
+ GParamSpecUInt* puint = G_PARAM_SPEC_UINT (pspec);
+ pspec = g_param_spec_uint (name, nick, blurb,
+ puint->minimum, puint->maximum,
+ lavc_default ? G_STRUCT_MEMBER (guint, ctx, ctx_offset)
+ : puint->default_value,
+ pspec->flags);
+ break;
+ }
+ case G_TYPE_ULONG: {
+ GParamSpecULong* pulong = G_PARAM_SPEC_ULONG (pspec);
+ pspec = g_param_spec_ulong (name, nick, blurb,
+ pulong->minimum, pulong->maximum,
+ lavc_default ? G_STRUCT_MEMBER (gulong, ctx, ctx_offset)
+ : pulong->default_value,
+ pspec->flags);
+ break;
+ }
+ case G_TYPE_FLOAT: {
+ GParamSpecFloat* pfloat = G_PARAM_SPEC_FLOAT (pspec);
+ pspec = g_param_spec_float (name, nick, blurb,
+ pfloat->minimum, pfloat->maximum,
+ lavc_default ? G_STRUCT_MEMBER (gfloat, ctx, ctx_offset)
+ : pfloat->default_value,
+ pspec->flags);
+ break;
+ }
+ case G_TYPE_BOOLEAN: {
+ GParamSpecBoolean* pboolean = G_PARAM_SPEC_BOOLEAN (pspec);
+ pspec = g_param_spec_boolean (name, nick, blurb,
+ lavc_default ? G_STRUCT_MEMBER (gboolean, ctx, ctx_offset)
+ : pboolean->default_value,
+ pspec->flags);
+ break;
+ }
+ default:
+ if (G_IS_PARAM_SPEC_ENUM (pspec)) {
+ GParamSpecEnum* penum = G_PARAM_SPEC_ENUM (pspec);
+ pspec = g_param_spec_enum (name, nick, blurb,
+ pspec->value_type,
+ lavc_default ? G_STRUCT_MEMBER (gint, ctx, ctx_offset)
+ : penum->default_value, pspec->flags);
+ } else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
+ GParamSpecFlags* pflags = G_PARAM_SPEC_FLAGS (pspec);
+ pspec = g_param_spec_flags (name, nick, blurb,
+ pspec->value_type,
+ lavc_default ? G_STRUCT_MEMBER (guint, ctx, ctx_offset)
+ : pflags->default_value, pspec->flags);
+ } else {
+ g_critical ("%s does not yet support type %s", GST_FUNCTION,
+ g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+ continue;
+ }
+ break;
+ }
+ g_param_spec_set_qdata (pspec, quark, qdata);
+ g_object_class_install_property (G_OBJECT_CLASS (klass), prop_id, pspec);
+ ++prop_id;
+ }
+ }
+
+ if (ctx)
+ av_free (ctx);
+}
+
+/* returns TRUE if it is a known property for this config system,
+ * FALSE otherwise */
+gboolean
+gst_ffmpeg_cfg_set_property (GObject * object,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (object);
+ GParamSpecData *qdata;
+
+ qdata = g_param_spec_get_qdata (pspec, quark);
+
+ /* our param specs should have such qdata */
+ if (!qdata)
+ return FALSE;
+
+ /* set the member using the offset, also mild type check based on size */
+ switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) {
+ case G_TYPE_BOOLEAN:
+ g_return_val_if_fail (qdata->size == sizeof (gboolean), TRUE);
+ G_STRUCT_MEMBER (gboolean, ffmpegenc, qdata->offset) =
+ g_value_get_boolean (value);
+ break;
+ case G_TYPE_UINT:
+ g_return_val_if_fail (qdata->size == sizeof (guint), TRUE);
+ G_STRUCT_MEMBER (guint, ffmpegenc, qdata->offset) =
+ g_value_get_uint (value);
+ break;
+ case G_TYPE_INT:
+ g_return_val_if_fail (qdata->size == sizeof (gint), TRUE);
+ G_STRUCT_MEMBER (gint, ffmpegenc, qdata->offset) =
+ g_value_get_int (value);
+ break;
+ case G_TYPE_ULONG:
+ g_return_val_if_fail (qdata->size == sizeof (gulong), TRUE);
+ G_STRUCT_MEMBER (glong, ffmpegenc, qdata->offset) =
+ g_value_get_ulong (value);
+ break;
+ case G_TYPE_FLOAT:
+ g_return_val_if_fail (qdata->size == sizeof (gfloat), TRUE);
+ G_STRUCT_MEMBER (gfloat, ffmpegenc, qdata->offset) =
+ g_value_get_float (value);
+ break;
+ case G_TYPE_STRING:
+ g_return_val_if_fail (qdata->size == sizeof (gchar*), TRUE);
+ g_free (G_STRUCT_MEMBER (gchar*, ffmpegenc, qdata->offset));
+ G_STRUCT_MEMBER (gchar*, ffmpegenc, qdata->offset) =
+ g_value_dup_string (value);
+ break;
+ default: /* must be enum, given the check above */
+ if (G_IS_PARAM_SPEC_ENUM (pspec)) {
+ g_return_val_if_fail (qdata->size == sizeof (gint), TRUE);
+ G_STRUCT_MEMBER (gint, ffmpegenc, qdata->offset) =
+ g_value_get_enum (value);
+ } else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
+ g_return_val_if_fail (qdata->size == sizeof (guint), TRUE);
+ G_STRUCT_MEMBER (guint, ffmpegenc, qdata->offset) =
+ g_value_get_flags (value);
+ } else { /* oops, bit lazy we don't cover this case yet */
+ g_critical ("%s does not yet support type %s",GST_FUNCTION,
+ g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+ }
+
+ break;
+ }
+
+ return TRUE;
+}
+
+/* returns TRUE if it is a known property for this config system,
+ * FALSE otherwise */
+gboolean
+gst_ffmpeg_cfg_get_property (GObject * object,
+ GValue * value, GParamSpec * pspec)
+{
+ GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (object);
+ GParamSpecData *qdata;
+
+ qdata = g_param_spec_get_qdata (pspec, quark);
+
+ /* our param specs should have such qdata */
+ if (!qdata)
+ return FALSE;
+
+ /* get the member using the offset, also mild type check based on size */
+ switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) {
+ case G_TYPE_BOOLEAN:
+ g_return_val_if_fail (qdata->size == sizeof (gboolean), TRUE);
+ g_value_set_boolean (value,
+ G_STRUCT_MEMBER (gboolean, ffmpegenc, qdata->offset));
+ break;
+ case G_TYPE_UINT:
+ g_return_val_if_fail (qdata->size == sizeof (guint), TRUE);
+ g_value_set_uint (value,
+ G_STRUCT_MEMBER (guint, ffmpegenc, qdata->offset));
+ break;
+ case G_TYPE_INT:
+ g_return_val_if_fail (qdata->size == sizeof (gint), TRUE);
+ g_value_set_int (value,
+ G_STRUCT_MEMBER (gint, ffmpegenc, qdata->offset));
+ break;
+ case G_TYPE_ULONG:
+ g_return_val_if_fail (qdata->size == sizeof (gulong), TRUE);
+ g_value_set_ulong (value,
+ G_STRUCT_MEMBER (glong, ffmpegenc, qdata->offset));
+ break;
+ case G_TYPE_FLOAT:
+ g_return_val_if_fail (qdata->size == sizeof (gfloat), TRUE);
+ g_value_set_float (value,
+ G_STRUCT_MEMBER (gfloat, ffmpegenc, qdata->offset));
+ break;
+ case G_TYPE_STRING:
+ g_return_val_if_fail (qdata->size == sizeof (gchar*), TRUE);
+ g_value_take_string (value,
+ g_strdup (G_STRUCT_MEMBER (gchar*, ffmpegenc, qdata->offset)));
+ break;
+ default: /* must be enum, given the check above */
+ if (G_IS_PARAM_SPEC_ENUM (pspec)) {
+ g_return_val_if_fail (qdata->size == sizeof (gint), TRUE);
+ g_value_set_enum (value,
+ G_STRUCT_MEMBER (gint, ffmpegenc, qdata->offset));
+ } else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
+ g_return_val_if_fail (qdata->size == sizeof (guint), TRUE);
+ g_value_set_flags (value,
+ G_STRUCT_MEMBER (guint, ffmpegenc, qdata->offset));
+ } else { /* oops, bit lazy we don't cover this case yet */
+ g_critical ("%s does not yet support type %s", GST_FUNCTION,
+ g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+ }
+ break;
+ }
+
+ return TRUE;
+}
+
+void
+gst_ffmpeg_cfg_set_defaults (GstFFMpegEnc * ffmpegenc)
+{
+ GParamSpec **pspecs;
+ guint num_props, i;
+
+ pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (ffmpegenc),
+ &num_props);
+
+ for (i = 0; i < num_props; ++i) {
+ GValue val = {0, };
+ GParamSpec *pspec = pspecs[i];
+ /* only touch those that are really ours; i.e. should have some qdata */
+ if (!g_param_spec_get_qdata (pspec, quark))
+ continue;
+ g_value_init (&val, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ g_param_value_set_default (pspec, &val);
+ g_object_set_property (G_OBJECT (ffmpegenc),
+ g_param_spec_get_name (pspec), &val);
+ g_value_unset (&val);
+ }
+
+ g_free (pspecs);
+
+}
+
+
+void
+gst_ffmpeg_cfg_fill_context (GstFFMpegEnc * ffmpegenc, AVCodecContext * context)
+{
+ GstFFMpegEncClass *klass
+ = (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc);
+ GParamSpec *pspec;
+ GParamSpecData *qdata;
+ GList *list;
+
+ list = property_list;
+
+ while (list) {
+ gint context_offset;
+
+ pspec = G_PARAM_SPEC (list->data);
+ qdata = g_param_spec_get_qdata (pspec, quark);
+ context_offset = qdata->offset - CONTEXT_CONFIG_OFFSET;
+ if (gst_ffmpeg_cfg_codec_has_pspec (klass->in_plugin->id, pspec)
+ && context_offset >= 0) {
+ /* memcpy a bit heavy for a small copy, but hardly part of 'inner loop' */
+ memcpy (G_STRUCT_MEMBER_P (context, context_offset),
+ G_STRUCT_MEMBER_P (ffmpegenc, qdata->offset),
+ qdata->size);
+ }
+ list = list->next;
+ }
+}
#include <assert.h>
#include <string.h>
+/* for stats file handling */
+#include <stdio.h>
+#include <glib/gstdio.h>
+#include <errno.h>
#ifdef HAVE_FFMPEG_UNINSTALLED
#include <avcodec.h>
#include "gstffmpeg.h"
#include "gstffmpegcodecmap.h"
+#include "gstffmpegenc.h"
+#include "gstffmpegcfg.h"
-#define DEFAULT_VIDEO_BITRATE 300000 /* in bps */
+#define DEFAULT_VIDEO_BITRATE 300000 /* in bps */
#define DEFAULT_VIDEO_GOP_SIZE 15
#define DEFAULT_AUDIO_BITRATE 128000
#define DEFAULT_WIDTH 352
#define DEFAULT_HEIGHT 288
-typedef struct _GstFFMpegEnc GstFFMpegEnc;
-
-struct _GstFFMpegEnc
-{
- GstElement element;
-
- /* We need to keep track of our pads, so we do so here. */
- GstPad *srcpad;
- GstPad *sinkpad;
-
- AVCodecContext *context;
- AVFrame *picture;
- gboolean opened;
- GstBuffer *cache;
-
- /* cache */
- gulong bitrate;
- gint me_method;
- gint gop_size;
- gulong buffer_size;
- gulong rtp_payload_size;
-};
-
-typedef struct _GstFFMpegEncClass GstFFMpegEncClass;
-
-struct _GstFFMpegEncClass
-{
- GstElementClass parent_class;
-
- AVCodec *in_plugin;
- GstPadTemplate *srctempl, *sinktempl;
- GstCaps *sinkcaps;
-};
-
-typedef struct
-{
- AVCodec *in_plugin;
- GstCaps *srccaps, *sinkcaps;
-} GstFFMpegEncClassParams;
-
-#define GST_TYPE_FFMPEGENC \
- (gst_ffmpegenc_get_type())
-#define GST_FFMPEGENC(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGENC,GstFFMpegEnc))
-#define GST_FFMPEGENC_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGENC,GstFFMpegEncClass))
-#define GST_IS_FFMPEGENC(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGENC))
-#define GST_IS_FFMPEGENC_CLASS(obj) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGENC))
#define VIDEO_BUFFER_SIZE (1024*1024)
ARG_GOP_SIZE,
ARG_ME_METHOD,
ARG_BUFSIZE,
- ARG_RTP_PAYLOAD_SIZE
- /* FILL ME */
+ ARG_RTP_PAYLOAD_SIZE,
+ ARG_CFG_BASE
};
#define GST_TYPE_ME_METHOD (gst_ffmpegenc_me_method_get_type())
static void gst_ffmpegenc_dispose (GObject * object);
static gboolean gst_ffmpegenc_setcaps (GstPad * pad, GstCaps * caps);
-static GstCaps * gst_ffmpegenc_getcaps (GstPad * pad);
-static GstFlowReturn gst_ffmpegenc_chain_video (GstPad * pad, GstBuffer *buffer);
-static GstFlowReturn gst_ffmpegenc_chain_audio (GstPad * pad, GstBuffer *buffer);
+static GstCaps *gst_ffmpegenc_getcaps (GstPad * pad);
+static GstFlowReturn gst_ffmpegenc_chain_video (GstPad * pad,
+ GstBuffer * buffer);
+static GstFlowReturn gst_ffmpegenc_chain_audio (GstPad * pad,
+ GstBuffer * buffer);
+static gboolean gst_ffmpegenc_event_video (GstPad * pad, GstEvent * event);
static void gst_ffmpegenc_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
if (klass->in_plugin->type == CODEC_TYPE_VIDEO) {
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE,
g_param_spec_ulong ("bitrate", "Bit Rate",
- "Target Video Bitrate", 0, G_MAXULONG, DEFAULT_VIDEO_BITRATE, G_PARAM_READWRITE));
+ "Target Video Bitrate", 0, G_MAXULONG, DEFAULT_VIDEO_BITRATE,
+ G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GOP_SIZE,
g_param_spec_int ("gop_size", "GOP Size",
- "Number of frames within one GOP",
- 0, G_MAXINT, DEFAULT_VIDEO_GOP_SIZE, G_PARAM_READWRITE));
+ "Number of frames within one GOP", 0, G_MAXINT,
+ DEFAULT_VIDEO_GOP_SIZE, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ME_METHOD,
- g_param_spec_enum ("me_method", "ME Method",
- "Motion Estimation Method",
+ g_param_spec_enum ("me_method", "ME Method", "Motion Estimation Method",
GST_TYPE_ME_METHOD, ME_LOG, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFSIZE,
g_param_spec_ulong ("buffer_size", "Buffer Size",
"Size of the video buffers", 0, G_MAXULONG, 0, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass),
- ARG_RTP_PAYLOAD_SIZE,
- g_param_spec_ulong ("rtp_payload_size", "RTP Payload Size",
- "Target GOB length", 0, G_MAXULONG, 0, G_PARAM_READWRITE));
+ ARG_RTP_PAYLOAD_SIZE, g_param_spec_ulong ("rtp_payload_size",
+ "RTP Payload Size", "Target GOB length", 0, G_MAXULONG, 0,
+ G_PARAM_READWRITE));
+
+ /* register additional properties, possibly dependent on the exact CODEC */
+ gst_ffmpeg_cfg_install_property (klass, ARG_CFG_BASE);
} else if (klass->in_plugin->type == CODEC_TYPE_AUDIO) {
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE,
g_param_spec_ulong ("bitrate", "Bit Rate",
- "Target Audio Bitrate", 0, G_MAXULONG, DEFAULT_AUDIO_BITRATE, G_PARAM_READWRITE));
+ "Target Audio Bitrate", 0, G_MAXULONG, DEFAULT_AUDIO_BITRATE,
+ G_PARAM_READWRITE));
}
gstelement_class->change_state = gst_ffmpegenc_change_state;
ffmpegenc->picture = avcodec_alloc_frame ();
ffmpegenc->opened = FALSE;
+ ffmpegenc->file = NULL;
+ ffmpegenc->delay = g_queue_new ();
+
if (oclass->in_plugin->type == CODEC_TYPE_VIDEO) {
gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_video);
+ /* so we know when to flush the buffers on EOS */
+ gst_pad_set_event_function (ffmpegenc->sinkpad, gst_ffmpegenc_event_video);
ffmpegenc->bitrate = DEFAULT_VIDEO_BITRATE;
ffmpegenc->buffer_size = 512 * 1024;
ffmpegenc->gop_size = DEFAULT_VIDEO_GOP_SIZE;
ffmpegenc->rtp_payload_size = 0;
+
+ gst_ffmpeg_cfg_set_defaults (ffmpegenc);
} else if (oclass->in_plugin->type == CODEC_TYPE_AUDIO) {
gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_audio);
av_free (ffmpegenc->context);
av_free (ffmpegenc->picture);
+ g_queue_free (ffmpegenc->delay);
+ g_free (ffmpegenc->filename);
+
G_OBJECT_CLASS (parent_class)->dispose (object);
}
/* makes it silent */
ctx->strict_std_compliance = -1;
- /* shut up the logging while we autoprobe; we don't want warnings and
- * errors about unsupported formats */
- /* FIXME: if someone cares about this disabling the logging for other
- * instances/threads/..., one could investigate if there is a way to
- * set this as a struct member on the av context, and check it from the
- * log handler */
+ /* shut up the logging while we autoprobe; we don't want warnings and
+ * errors about unsupported formats */
+ /* FIXME: if someone cares about this disabling the logging for other
+ * instances/threads/..., one could investigate if there is a way to
+ * set this as a struct member on the av context, and check it from the
+ * log handler */
#ifndef GST_DISABLE_GST_DEBUG
_shut_up_I_am_probing = TRUE;
#endif
ffmpegenc->context->rtp_payload_size = ffmpegenc->rtp_payload_size;
}
- /* general properties */
- ffmpegenc->context->qmin = 1;
- ffmpegenc->context->qmax = 31;
- ffmpegenc->context->max_qdiff = 15;
+ /* additional avcodec settings */
+ /* first fill in the majority by copying over */
+ gst_ffmpeg_cfg_fill_context (ffmpegenc, ffmpegenc->context);
+
+ /* then handle some special cases */
+ ffmpegenc->context->lmin = (ffmpegenc->lmin * FF_QP2LAMBDA + 0.5);
+ ffmpegenc->context->lmax = (ffmpegenc->lmax * FF_QP2LAMBDA + 0.5);
+
+ if (ffmpegenc->interlaced) {
+ ffmpegenc->context->flags |=
+ CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME;
+ ffmpegenc->picture->interlaced_frame = TRUE;
+ /* if this is not the case, a filter element should be used to swap fields */
+ ffmpegenc->picture->top_field_first = TRUE;
+ }
+
+ /* some other defaults */
+ ffmpegenc->context->rc_strategy = 2;
+ ffmpegenc->context->b_frame_strategy = 0;
+ ffmpegenc->context->coder_type = 0;
+ ffmpegenc->context->context_model = 0;
+ ffmpegenc->context->scenechange_threshold = 0;
+ ffmpegenc->context->inter_threshold = 0;
+
+ /* and last but not least the pass; CBR, 2-pass, etc */
+ ffmpegenc->context->flags |= ffmpegenc->pass;
+ switch (ffmpegenc->pass) {
+ /* some additional action depends on type of pass */
+ case CODEC_FLAG_QSCALE:
+ ffmpegenc->context->global_quality
+ = ffmpegenc->picture->quality = FF_QP2LAMBDA * ffmpegenc->quantizer;
+ break;
+ case CODEC_FLAG_PASS1: /* need to prepare a stats file */
+ /* we don't close when changing caps, fingers crossed */
+ if (!ffmpegenc->file)
+ ffmpegenc->file = g_fopen (ffmpegenc->filename, "w");
+ if (!ffmpegenc->file) {
+ GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, OPEN_WRITE,
+ (("Could not open file \"%s\" for writing."), ffmpegenc->filename),
+ GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+ break;
+ case CODEC_FLAG_PASS2:
+ { /* need to read the whole stats file ! */
+ gsize size;
+
+ if (!g_file_get_contents (ffmpegenc->filename,
+ &ffmpegenc->context->stats_in, &size, NULL)) {
+ GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, READ,
+ (("Could not get contents of file \"%s\"."), ffmpegenc->filename),
+ GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
/* fetch pix_fmt and so on */
gst_ffmpeg_caps_with_codectype (oclass->in_plugin->type,
pix_fmt = ffmpegenc->context->pix_fmt;
+ /* max-key-interval may need the framerate set above */
+ if (ffmpegenc->max_key_interval) {
+ AVCodecContext *ctx;
+
+ /* override gop-size */
+ ctx = ffmpegenc->context;
+ ctx->gop_size = (ffmpegenc->max_key_interval < 0) ?
+ (-ffmpegenc->max_key_interval
+ * (ctx->time_base.den / ctx->time_base.num))
+ : ffmpegenc->max_key_interval;
+ }
+
/* open codec */
if (gst_ffmpeg_avcodec_open (ffmpegenc->context, oclass->in_plugin) < 0) {
if (ffmpegenc->context->priv_data)
gst_ffmpeg_avcodec_close (ffmpegenc->context);
+ if (ffmpegenc->context->stats_in)
+ g_free (ffmpegenc->context->stats_in);
GST_DEBUG_OBJECT (ffmpegenc, "ffenc_%s: Failed to open FFMPEG codec",
oclass->in_plugin->name);
return FALSE;
}
+ /* second pass stats buffer no longer needed */
+ if (ffmpegenc->context->stats_in)
+ g_free (ffmpegenc->context->stats_in);
+
/* is the colourspace correct? */
if (pix_fmt != ffmpegenc->context->pix_fmt) {
gst_ffmpeg_avcodec_close (ffmpegenc->context);
- GST_DEBUG_OBJECT (ffmpegenc, "ffenc_%s: AV wants different colourspace (%d given, %d wanted)",
+ GST_DEBUG_OBJECT (ffmpegenc,
+ "ffenc_%s: AV wants different colourspace (%d given, %d wanted)",
oclass->in_plugin->name, pix_fmt, ffmpegenc->context->pix_fmt);
return FALSE;
}
GST_DEBUG_OBJECT (ffmpegenc, "... but no peer, using template caps");
/* we need to copy because get_allowed_caps returns a ref, and
* get_pad_template_caps doesn't */
- allowed_caps = gst_caps_copy (
- gst_pad_get_pad_template_caps (ffmpegenc->srcpad));
+ allowed_caps =
+ gst_caps_copy (gst_pad_get_pad_template_caps (ffmpegenc->srcpad));
}
GST_DEBUG_OBJECT (ffmpegenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
newcaps =
gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
- 0)), NULL);
+ 0)), NULL);
gst_caps_unref (icaps);
icaps = newcaps;
}
ffmpegenc->picture->pts =
gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (inbuf),
- ffmpegenc->context->time_base);
+ ffmpegenc->context->time_base);
outbuf = gst_buffer_new_and_alloc (ffmpegenc->buffer_size);
ret_size = avcodec_encode_video (ffmpegenc->context,
- GST_BUFFER_DATA (outbuf),
- GST_BUFFER_SIZE (outbuf), ffmpegenc->picture);
+ GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf), ffmpegenc->picture);
if (ret_size < 0) {
#ifndef GST_DISABLE_GST_DEBUG
GstFFMpegEncClass *oclass =
- (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
+ (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
GST_ERROR_OBJECT (ffmpegenc,
"ffenc_%s: failed to encode buffer", oclass->in_plugin->name);
#endif /* GST_DISABLE_GST_DEBUG */
return GST_FLOW_OK;
}
+ /* handle b-frame delay when no output, so we don't output empty frames;
+ * timestamps and so can permute a bit between coding and display order
+ * but keyframes should still end up with the proper metadata */
+ g_queue_push_tail (ffmpegenc->delay, inbuf);
+ if (ret_size)
+ inbuf = g_queue_pop_head (ffmpegenc->delay);
+ else
+ return GST_FLOW_OK;
+
+ /* save stats info if there is some as well as a stats file */
+ if (ffmpegenc->file && ffmpegenc->context->stats_out)
+ if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0)
+ GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE,
+ (("Could not write to file \"%s\"."), ffmpegenc->filename),
+ GST_ERROR_SYSTEM);
+
GST_BUFFER_SIZE (outbuf) = ret_size;
GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
outbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (subbuf));
ret_size = avcodec_encode_audio (ffmpegenc->context,
- GST_BUFFER_DATA (outbuf),
- GST_BUFFER_SIZE (outbuf), (const short int *)
+ GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf), (const short int *)
GST_BUFFER_DATA (subbuf));
if (ret_size < 0) {
}
static void
+gst_ffmpegenc_flush_buffers (GstFFMpegEnc * ffmpegenc, gboolean send)
+{
+ GstBuffer *outbuf, *inbuf;
+ gint ret_size;
+
+ GST_DEBUG_OBJECT (ffmpegenc, "flushing buffers with sending %d", send);
+
+ /* no need to empty codec if there is none */
+ if (!ffmpegenc->opened)
+ goto flush;
+
+ while (!g_queue_is_empty (ffmpegenc->delay)) {
+
+ outbuf = gst_buffer_new_and_alloc (ffmpegenc->buffer_size);
+ ret_size = avcodec_encode_video (ffmpegenc->context,
+ GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf), NULL);
+
+ if (ret_size < 0) { /* there should be something, notify and give up */
+#ifndef GST_DISABLE_GST_DEBUG
+ GstFFMpegEncClass *oclass =
+ (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
+ GST_WARNING_OBJECT (ffmpegenc,
+ "ffenc_%s: failed to flush buffer", oclass->in_plugin->name);
+#endif /* GST_DISABLE_GST_DEBUG */
+ break;
+ }
+
+ /* save stats info if there is some as well as a stats file */
+ if (ffmpegenc->file && ffmpegenc->context->stats_out)
+ if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0)
+ GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE,
+ (("Could not write to file \"%s\"."), ffmpegenc->filename),
+ GST_ERROR_SYSTEM);
+
+ /* handle b-frame delay when no output, so we don't output empty frames */
+ inbuf = g_queue_pop_head (ffmpegenc->delay);
+
+ GST_BUFFER_SIZE (outbuf) = ret_size;
+ GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
+ GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
+ if (!ffmpegenc->context->coded_frame->key_frame)
+ GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
+ gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegenc->srcpad));
+
+ gst_buffer_unref (inbuf);
+
+ if (send)
+ gst_pad_push (ffmpegenc->srcpad, outbuf);
+ else
+ gst_buffer_unref (outbuf);
+ }
+
+flush:
+ {
+ /* make sure that we empty the queue, is still needed if we had to break */
+ while (!g_queue_is_empty (ffmpegenc->delay))
+ gst_buffer_unref (g_queue_pop_head (ffmpegenc->delay));
+ }
+}
+
+static gboolean
+gst_ffmpegenc_event_video (GstPad * pad, GstEvent * event)
+{
+ GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (GST_PAD_PARENT (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ gst_ffmpegenc_flush_buffers (ffmpegenc, TRUE);
+ break;
+ /* no flushing if flush received,
+ * buffers in encoder are considered (in the) past */
+ default:
+ break;
+ }
+
+ return gst_pad_push_event (ffmpegenc->srcpad, event);
+}
+
+static void
gst_ffmpegenc_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
ffmpegenc->rtp_payload_size = g_value_get_ulong (value);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ if (!gst_ffmpeg_cfg_set_property (object, value, pspec))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
g_value_set_ulong (value, ffmpegenc->rtp_payload_size);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ if (!gst_ffmpeg_cfg_get_property (object, value, pspec))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
+ gst_ffmpegenc_flush_buffers (ffmpegenc, FALSE);
if (ffmpegenc->opened) {
gst_ffmpeg_avcodec_close (ffmpegenc->context);
ffmpegenc->opened = FALSE;
gst_buffer_unref (ffmpegenc->cache);
ffmpegenc->cache = NULL;
}
+ if (ffmpegenc->file) {
+ fclose (ffmpegenc->file);
+ ffmpegenc->file = NULL;
+ }
break;
default:
break;
enc_global_plugins = g_hash_table_new (NULL, NULL);
+ /* build global ffmpeg param/property info */
+ gst_ffmpeg_cfg_init ();
+
while (in_plugin) {
gchar *type_name;
GstCaps *srccaps, *sinkcaps;
/* name */
if (!gst_ffmpeg_get_codecid_longname (in_plugin->id)) {
- g_warning ("Add encoder %s (%d) please",
- in_plugin->name, in_plugin->id);
+ g_warning ("Add encoder %s (%d) please", in_plugin->name, in_plugin->id);
goto next;
}