avaudenc: install options generically

https://bugzilla.gnome.org/show_bug.cgi?id=792900
This commit is contained in:
Mathieu Duponchelle 2018-06-30 23:27:06 +02:00
parent 965cd81603
commit 59b9e66542
6 changed files with 70 additions and 130 deletions

View file

@ -30,6 +30,7 @@
#include "gstav.h"
#include "gstavutils.h"
#include "gstavcfg.h"
#ifdef GST_LIBAV_ENABLE_GPL
#define LICENSE "GPL"
@ -153,6 +154,9 @@ plugin_init (GstPlugin * plugin)
gst_ffmpeg_init_pix_fmt_info ();
/* build global ffmpeg param/property info */
gst_ffmpeg_cfg_init ();
gst_ffmpegaudenc_register (plugin);
gst_ffmpegvidenc_register (plugin);
gst_ffmpegauddec_register (plugin);

View file

@ -31,10 +31,12 @@
#include <errno.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <gst/gst.h>
#include "gstav.h"
#include "gstavcfg.h"
#include "gstavcodecmap.h"
#include "gstavutils.h"
#include "gstavaudenc.h"
@ -50,9 +52,7 @@ enum
enum
{
PROP_0,
PROP_BIT_RATE,
PROP_RTP_PAYLOAD_SIZE,
PROP_COMPLIANCE,
PROP_CFG_BASE,
};
/* A number of function prototypes are given so we can refer to them later. */
@ -148,16 +148,8 @@ gst_ffmpegaudenc_class_init (GstFFMpegAudEncClass * klass)
gobject_class->set_property = gst_ffmpegaudenc_set_property;
gobject_class->get_property = gst_ffmpegaudenc_get_property;
/* FIXME: could use -1 for a sensible per-codec defaults */
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BIT_RATE,
g_param_spec_int ("bitrate", "Bit Rate",
"Target Audio Bitrate", 0, G_MAXINT, DEFAULT_AUDIO_BITRATE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COMPLIANCE,
g_param_spec_enum ("compliance", "Compliance",
"Adherence of the encoder to the specifications",
GST_TYPE_FFMPEG_COMPLIANCE, FFMPEG_DEFAULT_COMPLIANCE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_ffmpeg_cfg_install_properties (gobject_class, klass->in_plugin,
PROP_CFG_BASE, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM);
gobject_class->finalize = gst_ffmpegaudenc_finalize;
@ -180,11 +172,10 @@ gst_ffmpegaudenc_init (GstFFMpegAudEnc * ffmpegaudenc)
/* ffmpeg objects */
ffmpegaudenc->context = avcodec_alloc_context3 (klass->in_plugin);
ffmpegaudenc->refcontext = avcodec_alloc_context3 (klass->in_plugin);
ffmpegaudenc->opened = FALSE;
ffmpegaudenc->frame = av_frame_alloc ();
ffmpegaudenc->compliance = FFMPEG_DEFAULT_COMPLIANCE;
gst_audio_encoder_set_drainable (GST_AUDIO_ENCODER (ffmpegaudenc), TRUE);
}
@ -197,6 +188,7 @@ gst_ffmpegaudenc_finalize (GObject * object)
av_frame_free (&ffmpegaudenc->frame);
gst_ffmpeg_avcodec_close (ffmpegaudenc->context);
av_free (ffmpegaudenc->context);
av_free (ffmpegaudenc->refcontext);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -262,31 +254,7 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
}
}
/* if we set it in _getcaps we should set it also in _link */
ffmpegaudenc->context->strict_std_compliance = ffmpegaudenc->compliance;
/* user defined properties */
if (ffmpegaudenc->bitrate > 0) {
GST_INFO_OBJECT (ffmpegaudenc, "Setting avcontext to bitrate %d",
ffmpegaudenc->bitrate);
ffmpegaudenc->context->bit_rate = ffmpegaudenc->bitrate;
ffmpegaudenc->context->bit_rate_tolerance = ffmpegaudenc->bitrate;
} else {
GST_INFO_OBJECT (ffmpegaudenc,
"Using avcontext default bitrate %" G_GINT64_FORMAT,
(gint64) ffmpegaudenc->context->bit_rate);
}
/* RTP payload used for GOB production (for Asterisk) */
if (ffmpegaudenc->rtp_payload_size) {
ffmpegaudenc->context->rtp_payload_size = ffmpegaudenc->rtp_payload_size;
}
/* some other defaults */
ffmpegaudenc->context->b_frame_strategy = 0;
ffmpegaudenc->context->coder_type = 0;
ffmpegaudenc->context->context_model = 0;
ffmpegaudenc->context->scenechange_threshold = 0;
gst_ffmpeg_cfg_fill_context (G_OBJECT (ffmpegaudenc), ffmpegaudenc->context);
/* fetch pix_fmt and so on */
gst_ffmpeg_audioinfo_to_context (info, ffmpegaudenc->context);
@ -330,7 +298,8 @@ gst_ffmpegaudenc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info)
GST_DEBUG_OBJECT (ffmpegaudenc, "Failed to set context defaults");
if ((oclass->in_plugin->capabilities & AV_CODEC_CAP_EXPERIMENTAL) &&
ffmpegaudenc->compliance != GST_FFMPEG_EXPERIMENTAL) {
ffmpegaudenc->context->strict_std_compliance !=
GST_FFMPEG_EXPERIMENTAL) {
GST_ELEMENT_ERROR (ffmpegaudenc, LIBRARY, SETTINGS,
("Codec is experimental, but settings don't allow encoders to "
"produce output of experimental quality"),
@ -703,7 +672,6 @@ gst_ffmpegaudenc_set_property (GObject * object,
{
GstFFMpegAudEnc *ffmpegaudenc;
/* Get a pointer of the right type. */
ffmpegaudenc = (GstFFMpegAudEnc *) (object);
if (ffmpegaudenc->opened) {
@ -712,46 +680,26 @@ gst_ffmpegaudenc_set_property (GObject * object,
return;
}
/* Check the argument id to see which argument we're setting. */
switch (prop_id) {
case PROP_BIT_RATE:
ffmpegaudenc->bitrate = g_value_get_int (value);
break;
case PROP_RTP_PAYLOAD_SIZE:
ffmpegaudenc->rtp_payload_size = g_value_get_int (value);
break;
case PROP_COMPLIANCE:
ffmpegaudenc->compliance = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
if (!gst_ffmpeg_cfg_set_property (ffmpegaudenc->refcontext, value, pspec))
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/* The set function is simply the inverse of the get fuction. */
static void
gst_ffmpegaudenc_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstFFMpegAudEnc *ffmpegaudenc;
/* It's not null if we got it, but it might not be ours */
ffmpegaudenc = (GstFFMpegAudEnc *) (object);
switch (prop_id) {
case PROP_BIT_RATE:
g_value_set_int (value, ffmpegaudenc->bitrate);
break;
break;
case PROP_RTP_PAYLOAD_SIZE:
g_value_set_int (value, ffmpegaudenc->rtp_payload_size);
break;
case PROP_COMPLIANCE:
g_value_set_enum (value, ffmpegaudenc->compliance);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
if (!gst_ffmpeg_cfg_get_property (ffmpegaudenc->refcontext, value, pspec))
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}

View file

@ -37,17 +37,9 @@ struct _GstFFMpegAudEnc
GstAudioEncoder parent;
AVCodecContext *context;
AVCodecContext *refcontext;
gboolean opened;
/* cache */
gint bitrate;
gint rtp_payload_size;
gint compliance;
/* other settings are copied over straight,
* include a context here, rather than copy-and-past it from avcodec.h */
AVCodecContext config;
AVFrame *frame;
GstAudioChannelPosition ffmpeg_layout[64];

View file

@ -33,22 +33,24 @@
#include <libavutil/opt.h>
static GQuark avoption_quark;
static GHashTable *venc_overrides = NULL;
static GHashTable *generic_overrides = NULL;
static void
make_venc_overrides (void)
make_generic_overrides (void)
{
g_assert (!venc_overrides);
venc_overrides = g_hash_table_new_full (g_str_hash, g_str_equal,
g_assert (!generic_overrides);
generic_overrides = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) gst_structure_free);
g_hash_table_insert (venc_overrides, g_strdup ("b"),
g_hash_table_insert (generic_overrides, g_strdup ("b"),
gst_structure_new_empty ("bitrate"));
g_hash_table_insert (venc_overrides, g_strdup ("g"),
g_hash_table_insert (generic_overrides, g_strdup ("ab"),
gst_structure_new_empty ("bitrate"));
g_hash_table_insert (generic_overrides, g_strdup ("g"),
gst_structure_new_empty ("gop-size"));
g_hash_table_insert (venc_overrides, g_strdup ("bt"),
g_hash_table_insert (generic_overrides, g_strdup ("bt"),
gst_structure_new_empty ("bitrate-tolerance"));
g_hash_table_insert (venc_overrides, g_strdup ("bf"),
g_hash_table_insert (generic_overrides, g_strdup ("bf"),
gst_structure_new_empty ("max-bframes"));
}
@ -56,7 +58,7 @@ void
gst_ffmpeg_cfg_init (void)
{
avoption_quark = g_quark_from_static_string ("ffmpeg-cfg-param-spec-data");
make_venc_overrides ();
make_generic_overrides ();
}
static gint
@ -335,7 +337,8 @@ install_opts (GObjectClass * gobject_class, const AVClass ** obj, guint prop_id,
}
void
gst_ffmpeg_cfg_install_properties (GstFFMpegVidEncClass * klass, guint base)
gst_ffmpeg_cfg_install_properties (GObjectClass * klass, AVCodec * in_plugin,
guint base, gint flags)
{
gint prop_id;
AVCodecContext *ctx;
@ -343,18 +346,16 @@ gst_ffmpeg_cfg_install_properties (GstFFMpegVidEncClass * klass, guint base)
prop_id = base;
g_return_if_fail (base > 0);
ctx = avcodec_alloc_context3 (klass->in_plugin);
ctx = avcodec_alloc_context3 (in_plugin);
if (!ctx)
g_warning ("could not get context");
prop_id =
install_opts ((GObjectClass *) klass, &klass->in_plugin->priv_class,
prop_id, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM,
install_opts ((GObjectClass *) klass, &in_plugin->priv_class, prop_id, 0,
" (Private codec option)", NULL);
prop_id =
install_opts ((GObjectClass *) klass, &ctx->av_class, prop_id,
AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM,
" (Generic codec option, might have no effect)", venc_overrides);
install_opts ((GObjectClass *) klass, &ctx->av_class, prop_id, flags,
" (Generic codec option, might have no effect)", generic_overrides);
if (ctx) {
gst_ffmpeg_avcodec_close (ctx);
@ -421,10 +422,9 @@ set_option_value (AVCodecContext * ctx, GParamSpec * pspec,
}
gboolean
gst_ffmpeg_cfg_set_property (GObject * object,
const GValue * value, GParamSpec * pspec)
gst_ffmpeg_cfg_set_property (AVCodecContext * refcontext, const GValue * value,
GParamSpec * pspec)
{
GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) (object);
const AVOption *opt;
opt = g_param_spec_get_qdata (pspec, avoption_quark);
@ -432,14 +432,13 @@ gst_ffmpeg_cfg_set_property (GObject * object,
if (!opt)
return FALSE;
return set_option_value (ffmpegenc->refcontext, pspec, value, opt) >= 0;
return set_option_value (refcontext, pspec, value, opt) >= 0;
}
gboolean
gst_ffmpeg_cfg_get_property (GObject * object,
GValue * value, GParamSpec * pspec)
gst_ffmpeg_cfg_get_property (AVCodecContext * refcontext, GValue * value,
GParamSpec * pspec)
{
GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) (object);
const AVOption *opt;
opt = g_param_spec_get_qdata (pspec, avoption_quark);
@ -455,7 +454,7 @@ gst_ffmpeg_cfg_get_property (GObject * object,
case G_TYPE_INT:
{
int64_t val;
if ((res = av_opt_get_int (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get_int (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
g_value_set_int (value, val);
break;
@ -463,7 +462,7 @@ gst_ffmpeg_cfg_get_property (GObject * object,
case G_TYPE_INT64:
{
int64_t val;
if ((res = av_opt_get_int (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get_int (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
g_value_set_int64 (value, val);
break;
@ -471,7 +470,7 @@ gst_ffmpeg_cfg_get_property (GObject * object,
case G_TYPE_UINT64:
{
int64_t val;
if ((res = av_opt_get_int (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get_int (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
g_value_set_uint64 (value, val);
break;
@ -479,7 +478,7 @@ gst_ffmpeg_cfg_get_property (GObject * object,
case G_TYPE_DOUBLE:
{
gdouble val;
if ((res = av_opt_get_double (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get_double (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
g_value_set_double (value, val);
break;
@ -487,7 +486,7 @@ gst_ffmpeg_cfg_get_property (GObject * object,
case G_TYPE_FLOAT:
{
gdouble val;
if ((res = av_opt_get_double (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get_double (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
g_value_set_float (value, (gfloat) val);
break;
@ -495,7 +494,7 @@ gst_ffmpeg_cfg_get_property (GObject * object,
case G_TYPE_STRING:
{
uint8_t *val;
if ((res = av_opt_get (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN | AV_OPT_ALLOW_NULL, &val) >= 0)) {
g_value_set_string (value, (gchar *) val);
}
@ -504,7 +503,7 @@ gst_ffmpeg_cfg_get_property (GObject * object,
case G_TYPE_BOOLEAN:
{
int64_t val;
if ((res = av_opt_get_int (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get_int (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
g_value_set_boolean (value, val ? TRUE : FALSE);
break;
@ -513,13 +512,13 @@ gst_ffmpeg_cfg_get_property (GObject * object,
if (G_IS_PARAM_SPEC_ENUM (pspec)) {
int64_t val;
if ((res = av_opt_get_int (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get_int (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
g_value_set_enum (value, val);
} else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
int64_t val;
if ((res = av_opt_get_int (ffmpegenc->refcontext, opt->name,
if ((res = av_opt_get_int (refcontext, opt->name,
AV_OPT_SEARCH_CHILDREN, &val) >= 0))
g_value_set_flags (value, val);
} else { /* oops, bit lazy we don't cover this case yet */
@ -534,13 +533,12 @@ gst_ffmpeg_cfg_get_property (GObject * object,
}
void
gst_ffmpeg_cfg_fill_context (GstFFMpegVidEnc * ffmpegenc,
AVCodecContext * context)
gst_ffmpeg_cfg_fill_context (GObject * object, AVCodecContext * context)
{
GParamSpec **pspecs;
guint num_props, i;
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (ffmpegenc),
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
&num_props);
for (i = 0; i < num_props; ++i) {
@ -553,15 +551,16 @@ gst_ffmpeg_cfg_fill_context (GstFFMpegVidEnc * ffmpegenc,
if (!opt)
continue;
g_object_getv (G_OBJECT (ffmpegenc), 1, &pspec->name, &value);
g_object_getv (object, 1, &pspec->name, &value);
set_option_value (context, pspec, &value, opt);
}
g_free (pspecs);
}
void
gst_ffmpeg_cfg_finalize (GstFFMpegVidEnc * ffmpegenc)
gst_ffmpeg_cfg_finalize (void)
{
g_assert (venc_overrides);
g_hash_table_unref (venc_overrides);
GST_ERROR ("Finalizing");
g_assert (generic_overrides);
g_hash_table_unref (generic_overrides);
}

View file

@ -21,21 +21,23 @@
#ifndef __GST_FFMPEGCFG_H__
#define __GST_FFMPEGCFG_H__
#include <glib-object.h>
#include <libavcodec/avcodec.h>
G_BEGIN_DECLS
void gst_ffmpeg_cfg_init (void);
void gst_ffmpeg_cfg_install_properties (GstFFMpegVidEncClass * klass, guint base);
void gst_ffmpeg_cfg_install_properties (GObjectClass * klass, AVCodec *in_plugin, guint base, gint flags);
gboolean gst_ffmpeg_cfg_set_property (GObject * object,
gboolean gst_ffmpeg_cfg_set_property (AVCodecContext *refcontext,
const GValue * value, GParamSpec * pspec);
gboolean gst_ffmpeg_cfg_get_property (GObject * object,
gboolean gst_ffmpeg_cfg_get_property (AVCodecContext *refcontext,
GValue * value, GParamSpec * pspec);
void gst_ffmpeg_cfg_fill_context (GstFFMpegVidEnc * ffmpegenc, AVCodecContext * context);
void gst_ffmpeg_cfg_set_defaults (GstFFMpegVidEnc * ffmpegenc);
void gst_ffmpeg_cfg_finalize (GstFFMpegVidEnc * ffmpegenc);
void gst_ffmpeg_cfg_fill_context (GObject *object, AVCodecContext * context);
void gst_ffmpeg_cfg_finalize (void);
G_END_DECLS

View file

@ -184,7 +184,8 @@ gst_ffmpegvidenc_class_init (GstFFMpegVidEncClass * klass)
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/* register additional properties, possibly dependent on the exact CODEC */
gst_ffmpeg_cfg_install_properties (klass, PROP_CFG_BASE);
gst_ffmpeg_cfg_install_properties (gobject_class, klass->in_plugin,
PROP_CFG_BASE, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM);
venc_class->start = gst_ffmpegvidenc_start;
venc_class->stop = gst_ffmpegvidenc_stop;
@ -217,8 +218,6 @@ gst_ffmpegvidenc_finalize (GObject * object)
{
GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) object;
gst_ffmpeg_cfg_finalize (ffmpegenc);
/* clean up remaining allocated data */
av_frame_free (&ffmpegenc->picture);
gst_ffmpeg_avcodec_close (ffmpegenc->context);
@ -253,7 +252,7 @@ gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder,
}
/* additional avcodec settings */
gst_ffmpeg_cfg_fill_context (ffmpegenc, ffmpegenc->context);
gst_ffmpeg_cfg_fill_context (G_OBJECT (ffmpegenc), ffmpegenc->context);
/* and last but not least the pass; CBR, 2-pass, etc */
ffmpegenc->context->flags |= ffmpegenc->pass;
@ -737,7 +736,7 @@ gst_ffmpegvidenc_set_property (GObject * object,
ffmpegenc->filename = g_value_dup_string (value);
break;
default:
if (!gst_ffmpeg_cfg_set_property (object, value, pspec))
if (!gst_ffmpeg_cfg_set_property (ffmpegenc->refcontext, value, pspec))
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
@ -762,7 +761,7 @@ gst_ffmpegvidenc_get_property (GObject * object,
g_value_take_string (value, g_strdup (ffmpegenc->filename));
break;
default:
if (!gst_ffmpeg_cfg_get_property (object, value, pspec))
if (!gst_ffmpeg_cfg_get_property (ffmpegenc->refcontext, value, pspec))
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
@ -839,12 +838,8 @@ gst_ffmpegvidenc_register (GstPlugin * plugin)
AVCodec *in_plugin;
void *i = 0;
GST_LOG ("Registering encoders");
/* build global ffmpeg param/property info */
gst_ffmpeg_cfg_init ();
while ((in_plugin = (AVCodec *) av_codec_iterate (&i))) {
gchar *type_name;