ext/ffmpeg/: Added helper object to handle extra properties.
authorMark Nauwelaerts <manauw@skynet.be>
Wed, 6 Sep 2006 14:51:13 +0000 (14:51 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Wed, 6 Sep 2006 14:51:13 +0000 (14:51 +0000)
Original commit message from CVS:
Patch by: Mark Nauwelaerts <manauw at skynet dot be>
* ext/ffmpeg/Makefile.am:
* ext/ffmpeg/gstffmpegcfg.c: (gst_ffmpeg_pass_get_type),
(gst_ffmpeg_lim_pass_get_type), (gst_ffmpeg_mb_decision_get_type),
(gst_ffmpeg_mb_cmp_get_type), (gst_ffmpeg_dct_algo_get_type),
(gst_ffmpeg_idct_algo_get_type), (gst_ffmpeg_quant_type_get_type),
(gst_ffmpeg_pre_me_get_type), (gst_ffmpeg_pred_method_get_type),
(gst_ffmpeg_flags_get_type), (gst_ffmpeg_cfg_init),
(gst_ffmpeg_cfg_codec_has_pspec),
(gst_ffmpeg_cfg_install_property), (gst_ffmpeg_cfg_set_property),
(gst_ffmpeg_cfg_get_property), (gst_ffmpeg_cfg_set_defaults),
(gst_ffmpeg_cfg_fill_context):
* ext/ffmpeg/gstffmpegcfg.h:
Added helper object to handle extra properties.
* ext/ffmpeg/gstffmpegenc.c: (gst_ffmpegenc_class_init),
(gst_ffmpegenc_init), (gst_ffmpegenc_dispose),
(gst_ffmpegenc_getcaps), (gst_ffmpegenc_setcaps),
(gst_ffmpegenc_chain_video), (gst_ffmpegenc_chain_audio),
(gst_ffmpegenc_flush_buffers), (gst_ffmpegenc_event_video),
(gst_ffmpegenc_set_property), (gst_ffmpegenc_get_property),
(gst_ffmpegenc_change_state), (gst_ffmpegenc_register):
* ext/ffmpeg/gstffmpegenc.h:
Exposes (quite some of) the AVCodecContext configurable
settings, as well as some additional (though simple) logic for b-frame
delay handling.
Most of the properties are only enabled for mpeg4-like codecs.
Fixes #344583.

ChangeLog
ext/ffmpeg/Makefile.am
ext/ffmpeg/gstffmpegcfg.c [new file with mode: 0644]
ext/ffmpeg/gstffmpegcfg.h [new file with mode: 0644]
ext/ffmpeg/gstffmpegenc.c
ext/ffmpeg/gstffmpegenc.h [new file with mode: 0644]

index 91ce8c6..994ead6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2006-09-06  Wim Taymans  <wim@fluendo.com>
+
+       Patch by: Mark Nauwelaerts <manauw at skynet dot be>
+
+       * ext/ffmpeg/Makefile.am:
+       * ext/ffmpeg/gstffmpegcfg.c: (gst_ffmpeg_pass_get_type),
+       (gst_ffmpeg_lim_pass_get_type), (gst_ffmpeg_mb_decision_get_type),
+       (gst_ffmpeg_mb_cmp_get_type), (gst_ffmpeg_dct_algo_get_type),
+       (gst_ffmpeg_idct_algo_get_type), (gst_ffmpeg_quant_type_get_type),
+       (gst_ffmpeg_pre_me_get_type), (gst_ffmpeg_pred_method_get_type),
+       (gst_ffmpeg_flags_get_type), (gst_ffmpeg_cfg_init),
+       (gst_ffmpeg_cfg_codec_has_pspec),
+       (gst_ffmpeg_cfg_install_property), (gst_ffmpeg_cfg_set_property),
+       (gst_ffmpeg_cfg_get_property), (gst_ffmpeg_cfg_set_defaults),
+       (gst_ffmpeg_cfg_fill_context):
+       * ext/ffmpeg/gstffmpegcfg.h:
+       Added helper object to handle extra properties.
+
+       * ext/ffmpeg/gstffmpegenc.c: (gst_ffmpegenc_class_init),
+       (gst_ffmpegenc_init), (gst_ffmpegenc_dispose),
+       (gst_ffmpegenc_getcaps), (gst_ffmpegenc_setcaps),
+       (gst_ffmpegenc_chain_video), (gst_ffmpegenc_chain_audio),
+       (gst_ffmpegenc_flush_buffers), (gst_ffmpegenc_event_video),
+       (gst_ffmpegenc_set_property), (gst_ffmpegenc_get_property),
+       (gst_ffmpegenc_change_state), (gst_ffmpegenc_register):
+       * ext/ffmpeg/gstffmpegenc.h:
+       Exposes (quite some of) the AVCodecContext configurable
+       settings, as well as some additional (though simple) logic for b-frame
+       delay handling.
+       Most of the properties are only enabled for mpeg4-like codecs.
+       Fixes #344583.
+
 2006-09-05  Wim Taymans  <wim@fluendo.com>
 
        * ext/ffmpeg/gstffmpegdec.c: (gst_ffmpegdec_chain):
index 6634125..927c8b3 100644 (file)
@@ -5,6 +5,7 @@ libgstffmpeg_la_SOURCES = gstffmpeg.c   \
                          gstffmpegcodecmap.c   \
                          gstffmpegenc.c        \
                          gstffmpegdec.c        \
+                         gstffmpegcfg.c        \
                          gstffmpegdemux.c      \
                          gstffmpegdeinterlace.c        \
                          gstffmpegscale.c
@@ -21,4 +22,6 @@ libgstffmpeg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
 noinst_HEADERS = \
        gstffmpeg.h \
-       gstffmpegcodecmap.h
+       gstffmpegcodecmap.h \
+       gstffmpegenc.h \
+       gstffmpegcfg.h
diff --git a/ext/ffmpeg/gstffmpegcfg.c b/ext/ffmpeg/gstffmpegcfg.c
new file mode 100644 (file)
index 0000000..2280c5e
--- /dev/null
@@ -0,0 +1,1031 @@
+/* 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;
+  }
+}
diff --git a/ext/ffmpeg/gstffmpegcfg.h b/ext/ffmpeg/gstffmpegcfg.h
new file mode 100644 (file)
index 0000000..1f442ba
--- /dev/null
@@ -0,0 +1,42 @@
+/* GStreamer
+ * 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.
+ */
+
+
+#ifndef __GST_FFMPEGCFG_H__
+#define __GST_FFMPEGCFG_H__
+
+G_BEGIN_DECLS
+
+void gst_ffmpeg_cfg_init ();
+
+void gst_ffmpeg_cfg_install_property (GstFFMpegEncClass * klass, guint base);
+
+gboolean gst_ffmpeg_cfg_set_property (GObject * object,
+    const GValue * value, GParamSpec * pspec);
+
+gboolean gst_ffmpeg_cfg_get_property (GObject * object,
+    GValue * value, GParamSpec * pspec);
+
+void gst_ffmpeg_cfg_fill_context (GstFFMpegEnc * ffmpegenc, AVCodecContext * context);
+void gst_ffmpeg_cfg_set_defaults (GstFFMpegEnc * ffmpegenc);
+
+G_END_DECLS
+
+
+#endif /* __GST_FFMPEGCFG_H__ */
index 0a16c38..e0eba05 100644 (file)
 
 #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)
 
@@ -108,8 +64,8 @@ enum
   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())
@@ -142,9 +98,12 @@ static void gst_ffmpegenc_init (GstFFMpegEnc * ffmpegenc);
 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);
@@ -221,26 +180,30 @@ gst_ffmpegenc_class_init (GstFFMpegEncClass * klass)
   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;
@@ -266,13 +229,20 @@ gst_ffmpegenc_init (GstFFMpegEnc * ffmpegenc)
   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);
 
@@ -298,6 +268,9 @@ gst_ffmpegenc_dispose (GObject * object)
   av_free (ffmpegenc->context);
   av_free (ffmpegenc->picture);
 
+  g_queue_free (ffmpegenc->delay);
+  g_free (ffmpegenc->filename);
+
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -336,12 +309,12 @@ gst_ffmpegenc_getcaps (GstPad * pad)
   /* 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
@@ -411,10 +384,66 @@ gst_ffmpegenc_setcaps (GstPad * pad, GstCaps * caps)
     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,
@@ -426,19 +455,38 @@ gst_ffmpegenc_setcaps (GstPad * pad, GstCaps * caps)
 
   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;
   }
@@ -450,8 +498,8 @@ gst_ffmpegenc_setcaps (GstPad * pad, GstCaps * caps)
     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,
@@ -480,7 +528,7 @@ gst_ffmpegenc_setcaps (GstPad * pad, GstCaps * caps)
 
     newcaps =
         gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
-            0)), NULL);
+                0)), NULL);
     gst_caps_unref (icaps);
     icaps = newcaps;
   }
@@ -517,17 +565,16 @@ gst_ffmpegenc_chain_video (GstPad * pad, GstBuffer * inbuf)
 
   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 */
@@ -536,6 +583,22 @@ gst_ffmpegenc_chain_video (GstPad * pad, GstBuffer * inbuf)
     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);
@@ -616,8 +679,7 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstBuffer * 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) {
@@ -643,6 +705,85 @@ gst_ffmpegenc_chain_audio (GstPad * pad, GstBuffer * inbuf)
 }
 
 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)
 {
@@ -669,7 +810,8 @@ gst_ffmpegenc_set_property (GObject * object,
       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;
   }
 }
@@ -701,7 +843,8 @@ gst_ffmpegenc_get_property (GObject * object,
       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;
   }
 }
@@ -721,6 +864,7 @@ gst_ffmpegenc_change_state (GstElement * element, GstStateChange transition)
 
   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;
@@ -729,6 +873,10 @@ gst_ffmpegenc_change_state (GstElement * element, GstStateChange transition)
         gst_buffer_unref (ffmpegenc->cache);
         ffmpegenc->cache = NULL;
       }
+      if (ffmpegenc->file) {
+        fclose (ffmpegenc->file);
+        ffmpegenc->file = NULL;
+      }
       break;
     default:
       break;
@@ -757,6 +905,9 @@ gst_ffmpegenc_register (GstPlugin * plugin)
 
   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;
@@ -777,8 +928,7 @@ gst_ffmpegenc_register (GstPlugin * plugin)
 
     /* 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;
     }
 
diff --git a/ext/ffmpeg/gstffmpegenc.h b/ext/ffmpeg/gstffmpegenc.h
new file mode 100644 (file)
index 0000000..b075378
--- /dev/null
@@ -0,0 +1,101 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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.
+ */
+
+/* First, include the header file for the plugin, to bring in the
+ * object definition and other useful things.
+ */
+
+#ifndef __GST_FFMPEGENC_H__
+#define __GST_FFMPEGENC_H__
+
+G_BEGIN_DECLS
+
+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;
+
+  /* settings with some special handling */
+  guint pass;
+  gfloat quantizer;
+  gchar *filename;
+  guint lmin;
+  guint lmax;
+  gint max_key_interval;
+  gboolean interlaced;
+
+  /* statistics file */
+  FILE *file;
+
+  /* for b-frame delay handling */
+  GQueue *delay;
+
+  /* other settings are copied over straight,
+   * include a context here, rather than copy-and-past it from avcodec.h */
+  AVCodecContext config;
+};
+
+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))
+
+G_END_DECLS
+
+#endif /* __GST_FFMPEGENC_H__ */