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.
This commit is contained in:
Jan Schmidt 2020-01-31 01:33:51 +11:00
parent 1b8bf1be01
commit 29e3d09014
5 changed files with 76 additions and 3 deletions

View 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_flush (GstAmcCodec * codec, GError **err);
gboolean gst_amc_codec_release (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_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_output_buffer (GstAmcCodec * codec, gint index, GError **err);
GstAmcBuffer * gst_amc_codec_get_input_buffer (GstAmcCodec * codec, gint index, GError **err); GstAmcBuffer * gst_amc_codec_get_input_buffer (GstAmcCodec * codec, gint index, GError **err);

View file

@ -466,21 +466,40 @@ gst_amc_video_enc_set_property (GObject * object, guint prop_id,
{ {
GstAmcVideoEnc *encoder; GstAmcVideoEnc *encoder;
GstState state; GstState state;
gboolean codec_active;
GError *err = NULL;
encoder = GST_AMC_VIDEO_ENC (object); encoder = GST_AMC_VIDEO_ENC (object);
GST_OBJECT_LOCK (encoder); GST_OBJECT_LOCK (encoder);
state = GST_STATE (encoder); state = GST_STATE (encoder);
if (state != GST_STATE_READY && state != GST_STATE_NULL) codec_active = (encoder->codec && state != GST_STATE_READY
goto wrong_state; && state != GST_STATE_NULL);
switch (prop_id) { switch (prop_id) {
case PROP_BIT_RATE: case PROP_BIT_RATE:
encoder->bitrate = g_value_get_uint (value); 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; break;
case PROP_I_FRAME_INTERVAL: case PROP_I_FRAME_INTERVAL:
encoder->i_frame_int = g_value_get_uint (value); encoder->i_frame_int = g_value_get_uint (value);
if (codec_active)
goto wrong_state;
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 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); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass); GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
GParamFlags dynamic_flag = 0;
parent_class = g_type_class_peek_parent (klass); 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); GST_DEBUG_FUNCPTR (gst_amc_video_enc_handle_frame);
videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_amc_video_enc_finish); 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_object_class_install_property (gobject_class, PROP_BIT_RATE,
g_param_spec_uint ("bitrate", "Bitrate", "Bitrate in bit/sec", 1, g_param_spec_uint ("bitrate", "Bitrate", "Bitrate in bit/sec", 1,
G_MAXINT, BIT_RATE_DEFAULT, 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_object_class_install_property (gobject_class, PROP_I_FRAME_INTERVAL,
g_param_spec_uint ("i-frame-interval", "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 static void
gst_amc_video_enc_init (GstAmcVideoEnc * self) gst_amc_video_enc_init (GstAmcVideoEnc * self)
{ {
g_mutex_init (&self->codec_lock);
g_mutex_init (&self->drain_lock); g_mutex_init (&self->drain_lock);
g_cond_init (&self->drain_cond); g_cond_init (&self->drain_cond);
@ -578,11 +604,14 @@ gst_amc_video_enc_open (GstVideoEncoder * encoder)
GST_DEBUG_OBJECT (self, "Opening 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); self->codec = gst_amc_codec_new (klass->codec_info->name, TRUE, &err);
if (!self->codec) { if (!self->codec) {
g_mutex_unlock (&self->codec_lock);
GST_ELEMENT_ERROR_FROM_ERROR (self, err); GST_ELEMENT_ERROR_FROM_ERROR (self, err);
return FALSE; return FALSE;
} }
g_mutex_unlock (&self->codec_lock);
self->started = FALSE; self->started = FALSE;
self->flushing = TRUE; self->flushing = TRUE;
@ -598,6 +627,7 @@ gst_amc_video_enc_close (GstVideoEncoder * encoder)
GST_DEBUG_OBJECT (self, "Closing encoder"); GST_DEBUG_OBJECT (self, "Closing encoder");
g_mutex_lock (&self->codec_lock);
if (self->codec) { if (self->codec) {
GError *err = NULL; GError *err = NULL;
@ -608,6 +638,7 @@ gst_amc_video_enc_close (GstVideoEncoder * encoder)
gst_amc_codec_free (self->codec); gst_amc_codec_free (self->codec);
} }
self->codec = NULL; self->codec = NULL;
g_mutex_unlock (&self->codec_lock);
self->started = FALSE; self->started = FALSE;
self->flushing = TRUE; self->flushing = TRUE;
@ -622,6 +653,7 @@ gst_amc_video_enc_finalize (GObject * object)
{ {
GstAmcVideoEnc *self = GST_AMC_VIDEO_ENC (object); GstAmcVideoEnc *self = GST_AMC_VIDEO_ENC (object);
g_mutex_clear (&self->codec_lock);
g_mutex_clear (&self->drain_lock); g_mutex_clear (&self->drain_lock);
g_cond_clear (&self->drain_cond); g_cond_clear (&self->drain_cond);

View file

@ -52,6 +52,7 @@ struct _GstAmcVideoEnc
GstVideoEncoder parent; GstVideoEncoder parent;
/* < private > */ /* < private > */
GMutex codec_lock; // Protect creation / destruction of the codec
GstAmcCodec *codec; GstAmcCodec *codec;
GstAmcFormat *amc_format; GstAmcFormat *amc_format;

View file

@ -31,6 +31,7 @@
#include "gstamcsurface.h" #include "gstamcsurface.h"
#define PARAMETER_KEY_REQUEST_SYNC_FRAME "request-sync" #define PARAMETER_KEY_REQUEST_SYNC_FRAME "request-sync"
#define PARAMETER_KEY_VIDEO_BITRATE "video-bitrate"
struct _GstAmcCodec struct _GstAmcCodec
{ {
@ -796,6 +797,27 @@ gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err)
PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); 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 gboolean
gst_amc_codec_release (GstAmcCodec * codec, GError ** err) gst_amc_codec_release (GstAmcCodec * codec, GError ** err)
{ {

View file

@ -201,6 +201,22 @@ gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err)
return FALSE; 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 gboolean
gst_amc_codec_release (GstAmcCodec * codec, GError ** err) gst_amc_codec_release (GstAmcCodec * codec, GError ** err)
{ {