androidmedia: Handle force-keyunit requests

Use API from Android 19 to request a keyframe from the MediaCodec
when indicated by the base class.
This commit is contained in:
Jan Schmidt 2020-01-31 01:21:34 +11:00
parent cfe318ea03
commit 1b8bf1be01
4 changed files with 122 additions and 0 deletions

View file

@ -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);

View file

@ -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

View file

@ -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, "<init>", "()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)
{

View file

@ -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)
{