From 29e3d0901447e74af864ec58e65e68938afdccaa Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 31 Jan 2020 01:33:51 +1100 Subject: [PATCH] androidmedia: Allow dynamic bitrate changes on Android >= 19 Android 19 added an API for dynamically changing the bitrate in a running codec. Also make it so that even when not update-able at runtime, parameters will at least be stored so that they take effect the next the codec is restarted. --- sys/androidmedia/gstamc-codec.h | 2 ++ sys/androidmedia/gstamcvideoenc.c | 38 ++++++++++++++++++-- sys/androidmedia/gstamcvideoenc.h | 1 + sys/androidmedia/jni/gstamc-codec-jni.c | 22 ++++++++++++ sys/androidmedia/magicleap/gstamc-codec-ml.c | 16 +++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/sys/androidmedia/gstamc-codec.h b/sys/androidmedia/gstamc-codec.h index 398214e2cc..9c40f58da8 100644 --- a/sys/androidmedia/gstamc-codec.h +++ b/sys/androidmedia/gstamc-codec.h @@ -61,6 +61,8 @@ gboolean gst_amc_codec_stop (GstAmcCodec * codec, GError **err); gboolean gst_amc_codec_flush (GstAmcCodec * codec, GError **err); gboolean gst_amc_codec_release (GstAmcCodec * codec, GError **err); gboolean gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError **err); +gboolean gst_amc_codec_have_dynamic_bitrate (void); +gboolean gst_amc_codec_set_dynamic_bitrate (GstAmcCodec * codec, GError **err, gint bitrate); GstAmcBuffer * gst_amc_codec_get_output_buffer (GstAmcCodec * codec, gint index, GError **err); GstAmcBuffer * gst_amc_codec_get_input_buffer (GstAmcCodec * codec, gint index, GError **err); diff --git a/sys/androidmedia/gstamcvideoenc.c b/sys/androidmedia/gstamcvideoenc.c index 3422978109..e387f45719 100644 --- a/sys/androidmedia/gstamcvideoenc.c +++ b/sys/androidmedia/gstamcvideoenc.c @@ -466,21 +466,40 @@ gst_amc_video_enc_set_property (GObject * object, guint prop_id, { GstAmcVideoEnc *encoder; GstState state; + gboolean codec_active; + GError *err = NULL; encoder = GST_AMC_VIDEO_ENC (object); GST_OBJECT_LOCK (encoder); state = GST_STATE (encoder); - if (state != GST_STATE_READY && state != GST_STATE_NULL) - goto wrong_state; + codec_active = (encoder->codec && state != GST_STATE_READY + && state != GST_STATE_NULL); switch (prop_id) { case PROP_BIT_RATE: encoder->bitrate = g_value_get_uint (value); + + g_mutex_lock (&encoder->codec_lock); + if (encoder->codec) { + if (!gst_amc_codec_set_dynamic_bitrate (encoder->codec, &err, + encoder->bitrate)) { + g_mutex_unlock (&encoder->codec_lock); + goto wrong_state; + } + } + g_mutex_unlock (&encoder->codec_lock); + if (err) { + GST_ELEMENT_WARNING_FROM_ERROR (encoder, err); + g_clear_error (&err); + } + break; case PROP_I_FRAME_INTERVAL: encoder->i_frame_int = g_value_get_uint (value); + if (codec_active) + goto wrong_state; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -527,6 +546,7 @@ gst_amc_video_enc_class_init (GstAmcVideoEncClass * klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass); + GParamFlags dynamic_flag = 0; parent_class = g_type_class_peek_parent (klass); @@ -547,10 +567,15 @@ gst_amc_video_enc_class_init (GstAmcVideoEncClass * klass) GST_DEBUG_FUNCPTR (gst_amc_video_enc_handle_frame); videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_amc_video_enc_finish); + // On Android >= 19, we can set bitrate dynamically + // so add the flag so apps can detect it. + if (gst_amc_codec_have_dynamic_bitrate ()) + dynamic_flag = GST_PARAM_MUTABLE_PLAYING; + g_object_class_install_property (gobject_class, PROP_BIT_RATE, g_param_spec_uint ("bitrate", "Bitrate", "Bitrate in bit/sec", 1, G_MAXINT, BIT_RATE_DEFAULT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + dynamic_flag | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_I_FRAME_INTERVAL, g_param_spec_uint ("i-frame-interval", "I-frame interval", @@ -562,6 +587,7 @@ gst_amc_video_enc_class_init (GstAmcVideoEncClass * klass) static void gst_amc_video_enc_init (GstAmcVideoEnc * self) { + g_mutex_init (&self->codec_lock); g_mutex_init (&self->drain_lock); g_cond_init (&self->drain_cond); @@ -578,11 +604,14 @@ gst_amc_video_enc_open (GstVideoEncoder * encoder) GST_DEBUG_OBJECT (self, "Opening encoder"); + g_mutex_lock (&self->codec_lock); self->codec = gst_amc_codec_new (klass->codec_info->name, TRUE, &err); if (!self->codec) { + g_mutex_unlock (&self->codec_lock); GST_ELEMENT_ERROR_FROM_ERROR (self, err); return FALSE; } + g_mutex_unlock (&self->codec_lock); self->started = FALSE; self->flushing = TRUE; @@ -598,6 +627,7 @@ gst_amc_video_enc_close (GstVideoEncoder * encoder) GST_DEBUG_OBJECT (self, "Closing encoder"); + g_mutex_lock (&self->codec_lock); if (self->codec) { GError *err = NULL; @@ -608,6 +638,7 @@ gst_amc_video_enc_close (GstVideoEncoder * encoder) gst_amc_codec_free (self->codec); } self->codec = NULL; + g_mutex_unlock (&self->codec_lock); self->started = FALSE; self->flushing = TRUE; @@ -622,6 +653,7 @@ gst_amc_video_enc_finalize (GObject * object) { GstAmcVideoEnc *self = GST_AMC_VIDEO_ENC (object); + g_mutex_clear (&self->codec_lock); g_mutex_clear (&self->drain_lock); g_cond_clear (&self->drain_cond); diff --git a/sys/androidmedia/gstamcvideoenc.h b/sys/androidmedia/gstamcvideoenc.h index f16924897e..92833de9c0 100644 --- a/sys/androidmedia/gstamcvideoenc.h +++ b/sys/androidmedia/gstamcvideoenc.h @@ -52,6 +52,7 @@ struct _GstAmcVideoEnc GstVideoEncoder parent; /* < private > */ + GMutex codec_lock; // Protect creation / destruction of the codec GstAmcCodec *codec; GstAmcFormat *amc_format; diff --git a/sys/androidmedia/jni/gstamc-codec-jni.c b/sys/androidmedia/jni/gstamc-codec-jni.c index 7d1cbadbd4..dcc1314f64 100644 --- a/sys/androidmedia/jni/gstamc-codec-jni.c +++ b/sys/androidmedia/jni/gstamc-codec-jni.c @@ -31,6 +31,7 @@ #include "gstamcsurface.h" #define PARAMETER_KEY_REQUEST_SYNC_FRAME "request-sync" +#define PARAMETER_KEY_VIDEO_BITRATE "video-bitrate" struct _GstAmcCodec { @@ -796,6 +797,27 @@ gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err) PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); } +gboolean +gst_amc_codec_have_dynamic_bitrate () +{ + /* Dynamic bitrate scaling is supported on Android >= 19, + * where the setParameters() call is available */ + return (media_codec.setParameters != NULL); +} + +gboolean +gst_amc_codec_set_dynamic_bitrate (GstAmcCodec * codec, GError ** err, + gint bitrate) +{ + JNIEnv *env; + + g_return_val_if_fail (codec != NULL, FALSE); + + env = gst_amc_jni_get_env (); + return gst_amc_codec_set_parameter (codec, env, err, + PARAMETER_KEY_VIDEO_BITRATE, bitrate); +} + gboolean gst_amc_codec_release (GstAmcCodec * codec, GError ** err) { diff --git a/sys/androidmedia/magicleap/gstamc-codec-ml.c b/sys/androidmedia/magicleap/gstamc-codec-ml.c index 46cc67f7a6..e40df33ca8 100644 --- a/sys/androidmedia/magicleap/gstamc-codec-ml.c +++ b/sys/androidmedia/magicleap/gstamc-codec-ml.c @@ -201,6 +201,22 @@ gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err) return FALSE; } +gboolean +gst_amc_codec_set_dynamic_bitrate (GstAmcCodec * codec, GError ** err, + gint bitrate) +{ + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Dynamic bitrate control isn't available on MagicLeap"); + return FALSE; +} + +gboolean +gst_amc_codec_have_dynamic_bitrate () +{ + /* If MagicLeap ever provides an API for scaling bitrate, change this to TRUE */ + return FALSE; +} + gboolean gst_amc_codec_release (GstAmcCodec * codec, GError ** err) { -- 2.34.1