androidmedia: Handle force-keyunit requests
authorJan Schmidt <jan@centricular.com>
Thu, 30 Jan 2020 14:21:34 +0000 (01:21 +1100)
committerJan Schmidt <jan@centricular.com>
Sat, 8 Feb 2020 15:19:12 +0000 (02:19 +1100)
Use API from Android 19 to request a keyframe from the MediaCodec
when indicated by the base class.

sys/androidmedia/gstamc-codec.h
sys/androidmedia/gstamcvideoenc.c
sys/androidmedia/jni/gstamc-codec-jni.c
sys/androidmedia/magicleap/gstamc-codec-ml.c

index 0f57802..398214e 100644 (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);
index 291bd6f..3422978 100644 (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
index 87eb451..7d1cbad 100644 (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
@@ -73,6 +76,13 @@ static struct
 static struct
 {
   jclass klass;
+  jmethodID constructor;
+  jmethodID putInt;
+} bundle_class;
+
+static struct
+{
+  jclass klass;
   jmethodID get_limit, get_position;
   jmethodID set_limit, set_position;
   jmethodID clear;
@@ -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)
 {
index 67ef3de..46cc67f 100644 (file)
@@ -193,6 +193,15 @@ gst_amc_codec_flush (GstAmcCodec * codec, GError ** err)
 }
 
 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)
 {
   g_return_val_if_fail (codec != NULL, FALSE);