androidmedia: Allow dynamic bitrate changes on Android >= 19
authorJan Schmidt <jan@centricular.com>
Thu, 30 Jan 2020 14:33:51 +0000 (01:33 +1100)
committerJan Schmidt <jan@centricular.com>
Sat, 8 Feb 2020 15:19:12 +0000 (02:19 +1100)
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
sys/androidmedia/gstamcvideoenc.c
sys/androidmedia/gstamcvideoenc.h
sys/androidmedia/jni/gstamc-codec-jni.c
sys/androidmedia/magicleap/gstamc-codec-ml.c

index 398214e..9c40f58 100644 (file)
@@ -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);
index 3422978..e387f45 100644 (file)
@@ -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);
 
index f169248..92833de 100644 (file)
@@ -52,6 +52,7 @@ struct _GstAmcVideoEnc
   GstVideoEncoder parent;
 
   /* < private > */
+  GMutex codec_lock; // Protect creation / destruction of the codec
   GstAmcCodec *codec;
   GstAmcFormat *amc_format;
 
index 7d1cbad..dcc1314 100644 (file)
@@ -31,6 +31,7 @@
 #include "gstamcsurface.h"
 
 #define PARAMETER_KEY_REQUEST_SYNC_FRAME "request-sync"
+#define PARAMETER_KEY_VIDEO_BITRATE "video-bitrate"
 
 struct _GstAmcCodec
 {
@@ -797,6 +798,27 @@ gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err)
 }
 
 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)
 {
   JNIEnv *env;
index 46cc67f..e40df33 100644 (file)
@@ -202,6 +202,22 @@ gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err)
 }
 
 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)
 {
   g_return_val_if_fail (codec != NULL, FALSE);