From 1b8bf1be010fd7a125d73b5c73d4d139b8d7b52c Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 31 Jan 2020 01:21:34 +1100 Subject: [PATCH] androidmedia: Handle force-keyunit requests Use API from Android 19 to request a keyframe from the MediaCodec when indicated by the base class. --- sys/androidmedia/gstamc-codec.h | 1 + sys/androidmedia/gstamcvideoenc.c | 10 ++ sys/androidmedia/jni/gstamc-codec-jni.c | 102 +++++++++++++++++++ sys/androidmedia/magicleap/gstamc-codec-ml.c | 9 ++ 4 files changed, 122 insertions(+) diff --git a/sys/androidmedia/gstamc-codec.h b/sys/androidmedia/gstamc-codec.h index 0f57802be9..398214e2cc 100644 --- a/sys/androidmedia/gstamc-codec.h +++ b/sys/androidmedia/gstamc-codec.h @@ -60,6 +60,7 @@ gboolean gst_amc_codec_start (GstAmcCodec * codec, GError **err); 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); 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 291bd6f3dc..3422978109 100644 --- a/sys/androidmedia/gstamcvideoenc.c +++ b/sys/androidmedia/gstamcvideoenc.c @@ -1426,6 +1426,16 @@ gst_amc_video_enc_handle_frame (GstVideoEncoder * encoder, timestamp = frame->pts; duration = frame->duration; + if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) { + if (gst_amc_codec_request_key_frame (self->codec, &err)) { + GST_DEBUG_OBJECT (self, "Passed keyframe request to MediaCodec"); + } + if (err) { + GST_ELEMENT_WARNING_FROM_ERROR (self, err); + g_clear_error (&err); + } + } + again: /* Make sure to release the base class stream lock, otherwise * _loop() can't call _finish_frame() and we might block forever diff --git a/sys/androidmedia/jni/gstamc-codec-jni.c b/sys/androidmedia/jni/gstamc-codec-jni.c index 87eb45139f..7d1cbadbd4 100644 --- a/sys/androidmedia/jni/gstamc-codec-jni.c +++ b/sys/androidmedia/jni/gstamc-codec-jni.c @@ -30,6 +30,8 @@ #include "gstamcsurfacetexture-jni.h" #include "gstamcsurface.h" +#define PARAMETER_KEY_REQUEST_SYNC_FRAME "request-sync" + struct _GstAmcCodec { jobject object; /* global reference */ @@ -58,6 +60,7 @@ static struct jmethodID release_output_buffer; jmethodID start; jmethodID stop; + jmethodID setParameters; } media_codec; static struct @@ -70,6 +73,13 @@ static struct jfieldID size; } media_codec_buffer_info; +static struct +{ + jclass klass; + jmethodID constructor; + jmethodID putInt; +} bundle_class; + static struct { jclass klass; @@ -263,6 +273,11 @@ gst_amc_codec_static_init (void) } goto done; } + media_codec.setParameters = + (*env)->GetMethodID (env, media_codec.klass, "setParameters", + "(Landroid/os/Bundle;)V"); + if ((*env)->ExceptionCheck (env)) + (*env)->ExceptionClear (env); /* Android >= 21 */ media_codec.get_output_buffer = @@ -278,6 +293,47 @@ gst_amc_codec_static_init (void) if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); + if (media_codec.setParameters != NULL) { + /* Bundle needed for parameter setting on Android >= 19 */ + tmp = (*env)->FindClass (env, "android/os/Bundle"); + if (!tmp) { + ret = FALSE; + GST_ERROR ("Failed to get Bundle class"); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + } + goto done; + } + bundle_class.klass = (*env)->NewGlobalRef (env, tmp); + if (!bundle_class.klass) { + ret = FALSE; + GST_ERROR ("Failed to get Bundle class global reference"); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + } + goto done; + } + (*env)->DeleteLocalRef (env, tmp); + tmp = NULL; + + bundle_class.constructor = + (*env)->GetMethodID (env, bundle_class.klass, "", "()V"); + bundle_class.putInt = + (*env)->GetMethodID (env, bundle_class.klass, "putInt", + "(Ljava/lang/String;I)V"); + if (!bundle_class.constructor || !bundle_class.putInt) { + ret = FALSE; + GST_ERROR ("Failed to get Bundle methods"); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + } + goto done; + } + } + done: if (tmp) (*env)->DeleteLocalRef (env, tmp); @@ -694,6 +750,52 @@ gst_amc_codec_flush (GstAmcCodec * codec, GError ** err) media_codec.flush); } +static gboolean +gst_amc_codec_set_parameter (GstAmcCodec * codec, JNIEnv * env, + GError ** err, const gchar * key, int value) +{ + gboolean ret = FALSE; + jobject bundle = NULL; + jstring jkey = NULL; + + if (media_codec.setParameters == NULL) + goto done; // Not available means we're on Android < 19 + + bundle = gst_amc_jni_new_object (env, err, FALSE, bundle_class.klass, + bundle_class.constructor); + if (!bundle) + goto done; + + jkey = (*env)->NewStringUTF (env, key); + if (!gst_amc_jni_call_void_method (env, err, + bundle, bundle_class.putInt, jkey, value)) + goto done; + + if (!gst_amc_jni_call_void_method (env, err, codec->object, + media_codec.setParameters, bundle)) + goto done; + + ret = TRUE; +done: + if (jkey) + (*env)->DeleteLocalRef (env, jkey); + if (bundle) + (*env)->DeleteLocalRef (env, bundle); + return ret; +} + +gboolean +gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err) +{ + 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_REQUEST_SYNC_FRAME, 0); +} + 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 67ef3def08..46cc67f7a6 100644 --- a/sys/androidmedia/magicleap/gstamc-codec-ml.c +++ b/sys/androidmedia/magicleap/gstamc-codec-ml.c @@ -192,6 +192,15 @@ gst_amc_codec_flush (GstAmcCodec * codec, GError ** err) return TRUE; } +gboolean +gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err) +{ + /* If MagicLeap adds an API for requesting a keyframe, call it here */ + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Keyframe requests are not available on MagicLeap"); + return FALSE; +} + gboolean gst_amc_codec_release (GstAmcCodec * codec, GError ** err) {