applemedia: vtdec: improve negotiation
authorAlessandro Decina <alessandro.d@gmail.com>
Tue, 17 Nov 2015 00:21:27 +0000 (11:21 +1100)
committerAlessandro Decina <alessandro.d@gmail.com>
Tue, 17 Nov 2015 01:59:58 +0000 (12:59 +1100)
Rework negotiation implementing GstVideoDecoder::negotiate. Make it possible to
switch texture sharing on and off at runtime. Useful to (eventually) turn
texture sharing on in pipelines where glimagesink is linked only after
decoding has already started (for example OWR).

sys/applemedia/vtdec.c
sys/applemedia/vtdec.h

index 36137c8..fe436cd 100644 (file)
@@ -59,8 +59,7 @@ static void gst_vtdec_finalize (GObject * object);
 
 static gboolean gst_vtdec_start (GstVideoDecoder * decoder);
 static gboolean gst_vtdec_stop (GstVideoDecoder * decoder);
-static gboolean gst_vtdec_decide_allocation (GstVideoDecoder * decoder,
-    GstQuery * query);
+static gboolean gst_vtdec_negotiate (GstVideoDecoder * decoder);
 static gboolean gst_vtdec_set_format (GstVideoDecoder * decoder,
     GstVideoCodecState * state);
 static gboolean gst_vtdec_flush (GstVideoDecoder * decoder);
@@ -123,7 +122,7 @@ CFSTR ("RequireHardwareAcceleratedVideoDecoder");
     "height = " GST_VIDEO_SIZE_RANGE ", "                               \
     "framerate = " GST_VIDEO_FPS_RANGE ", "                             \
     "texture-target = (string) 2D "                                     \
-    " ; " \
+    " ; "                                                               \
     GST_VIDEO_CAPS_MAKE(GST_VTDEC_VIDEO_FORMAT_STR) ";"
 #else
 #define VIDEO_SRC_CAPS \
@@ -156,8 +155,7 @@ gst_vtdec_class_init (GstVtdecClass * klass)
   gobject_class->finalize = gst_vtdec_finalize;
   video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_vtdec_start);
   video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_vtdec_stop);
-  video_decoder_class->decide_allocation =
-      GST_DEBUG_FUNCPTR (gst_vtdec_decide_allocation);
+  video_decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_vtdec_negotiate);
   video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_vtdec_set_format);
   video_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_vtdec_flush);
   video_decoder_class->finish = GST_DEBUG_FUNCPTR (gst_vtdec_finish);
@@ -199,6 +197,10 @@ gst_vtdec_stop (GstVideoDecoder * decoder)
 {
   GstVtdec *vtdec = GST_VTDEC (decoder);
 
+  if (vtdec->input_state)
+    gst_video_codec_state_unref (vtdec->input_state);
+  vtdec->input_state = NULL;
+
   if (vtdec->session)
     gst_vtdec_invalidate_session (vtdec);
 
@@ -211,112 +213,112 @@ gst_vtdec_stop (GstVideoDecoder * decoder)
   return TRUE;
 }
 
-static gboolean
-gst_vtdec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
+static GstGLContext *
+query_gl_context (GstVtdec * vtdec)
 {
-  gboolean ret;
-  GstCaps *caps;
-  GstCapsFeatures *features;
-  GstVtdec *vtdec = GST_VTDEC (decoder);
+  GstGLContext *gl_context = NULL;
+  GstContext *context = NULL;
+  GstQuery *query;
+
+  query = gst_query_new_context ("gst.gl.local_context");
+  if (gst_pad_peer_query (GST_VIDEO_DECODER_SRC_PAD (vtdec), query)) {
+    gst_query_parse_context (query, &context);
+    if (context) {
+      const GstStructure *s = gst_context_get_structure (context);
+      gst_structure_get (s, "context", GST_GL_TYPE_CONTEXT, &gl_context, NULL);
+    }
+  }
+  gst_query_unref (query);
 
-  ret =
-      GST_VIDEO_DECODER_CLASS (gst_vtdec_parent_class)->decide_allocation
-      (decoder, query);
-  if (!ret)
-    goto out;
+  return gl_context;
+}
 
-  gst_query_parse_allocation (query, &caps, NULL);
-  if (caps) {
-    GstGLContext *gl_context = NULL;
-    features = gst_caps_get_features (caps, 0);
-
-    if (gst_caps_features_contains (features,
-            GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
-      GstContext *context = NULL;
-      GstQuery *query = gst_query_new_context ("gst.gl.local_context");
-      if (gst_pad_peer_query (GST_VIDEO_DECODER_SRC_PAD (decoder), query)) {
-
-        gst_query_parse_context (query, &context);
-        if (context) {
-          const GstStructure *s = gst_context_get_structure (context);
-          gst_structure_get (s, "context", GST_GL_TYPE_CONTEXT, &gl_context,
-              NULL);
-        }
-      }
-      gst_query_unref (query);
-
-      if (context) {
-        GstVideoFormat internal_format;
-        GstVideoCodecState *output_state =
-            gst_video_decoder_get_output_state (decoder);
-
-        GST_INFO_OBJECT (decoder, "pushing textures. GL context %p", context);
-        if (vtdec->texture_cache)
-          gst_core_video_texture_cache_free (vtdec->texture_cache);
+static void
+setup_texture_cache (GstVtdec * vtdec, GstGLContext * context)
+{
+  GstVideoFormat internal_format;
+  GstVideoCodecState *output_state =
+      gst_video_decoder_get_output_state (GST_VIDEO_DECODER (vtdec));
+
+  GST_INFO_OBJECT (vtdec, "pushing textures. GL context %p", context);
+  if (vtdec->texture_cache)
+    gst_core_video_texture_cache_free (vtdec->texture_cache);
 
 #ifdef HAVE_IOS
-        internal_format = GST_VIDEO_FORMAT_NV12;
+  internal_format = GST_VIDEO_FORMAT_NV12;
 #else
-        internal_format = GST_VIDEO_FORMAT_UYVY;
+  internal_format = GST_VIDEO_FORMAT_UYVY;
 #endif
-        vtdec->texture_cache = gst_core_video_texture_cache_new (gl_context);
-        gst_core_video_texture_cache_set_format (vtdec->texture_cache,
-            internal_format, output_state->caps);
-        gst_video_codec_state_unref (output_state);
-        gst_object_unref (gl_context);
-      } else {
-        GST_WARNING_OBJECT (decoder,
-            "got memory:GLMemory caps but not GL context from downstream element");
-      }
-    }
-  }
+  vtdec->texture_cache = gst_core_video_texture_cache_new (context);
+  gst_core_video_texture_cache_set_format (vtdec->texture_cache,
+      internal_format, output_state->caps);
+  gst_video_codec_state_unref (output_state);
+}
 
-out:
-  return ret;
+static gboolean
+caps_filter_out_gl_memory (GstCapsFeatures * features, GstStructure * structure,
+    gpointer user_data)
+{
+  return !gst_caps_features_contains (features,
+      GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
 }
 
 static gboolean
-gst_vtdec_negotiate_output_format (GstVtdec * vtdec,
-    GstVideoCodecState * input_state)
+gst_vtdec_negotiate (GstVideoDecoder * decoder)
 {
-  GstCaps *caps = NULL, *peercaps = NULL, *templcaps;
-  GstVideoFormat output_format;
   GstVideoCodecState *output_state = NULL;
-  GstCapsFeatures *features;
+  GstCaps *caps = NULL, *prevcaps = NULL;
+  GstVideoFormat format;
   GstStructure *structure;
   const gchar *s;
+  GstGLContext *context;
+  GstVtdec *vtdec;
+  gboolean ret = TRUE;
 
-  peercaps = gst_pad_peer_query_caps (GST_VIDEO_DECODER_SRC_PAD (vtdec), NULL);
-
-  /* Check if output supports GL caps by preference */
-  templcaps = gst_pad_get_pad_template_caps (GST_VIDEO_DECODER_SRC_PAD (vtdec));
+  vtdec = GST_VTDEC (decoder);
   caps =
-      gst_caps_intersect_full (templcaps, peercaps, GST_CAPS_INTERSECT_FIRST);
-
-  gst_caps_unref (peercaps);
-  gst_caps_unref (templcaps);
+      gst_caps_make_writable (gst_pad_query_caps (GST_VIDEO_DECODER_SRC_PAD
+          (vtdec), NULL));
+  context = query_gl_context (vtdec);
+  if (!context)
+    gst_caps_filter_and_map_in_place (caps, caps_filter_out_gl_memory, NULL);
 
   caps = gst_caps_truncate (caps);
   structure = gst_caps_get_structure (caps, 0);
   s = gst_structure_get_string (structure, "format");
-  output_format = gst_video_format_from_string (s);
-  features = gst_caps_features_copy (gst_caps_get_features (caps, 0));
-
+  format = gst_video_format_from_string (s);
   gst_caps_unref (caps);
 
-  if (!gst_vtdec_create_session (vtdec, output_format)) {
-    gst_caps_features_free (features);
-    return FALSE;
+  output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (vtdec),
+      format, vtdec->video_info.width, vtdec->video_info.height,
+      vtdec->input_state);
+  output_state->caps = gst_video_info_to_caps (&output_state->info);
+  if (output_state->info.finfo->format == GST_VIDEO_FORMAT_RGBA) {
+    setup_texture_cache (vtdec, context);
+    gst_caps_set_features (output_state->caps, 0,
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, NULL));
   }
+  if (context)
+    gst_object_unref (context);
 
-  output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (vtdec),
-      output_format, vtdec->video_info.width, vtdec->video_info.height,
-      input_state);
+  GST_INFO_OBJECT (vtdec, "negotiated output format %" GST_PTR_FORMAT,
+      output_state->caps);
 
-  output_state->caps = gst_video_info_to_caps (&output_state->info);
-  gst_caps_set_features (output_state->caps, 0, features);
+  prevcaps = gst_pad_get_current_caps (decoder->srcpad);
+  if (!prevcaps || !gst_caps_is_equal (prevcaps, output_state->caps)) {
+    if (vtdec->session) {
+      gst_vtdec_push_frames_if_needed (vtdec, TRUE, FALSE);
+      gst_vtdec_invalidate_session (vtdec);
+    }
+    ret = gst_vtdec_create_session (vtdec, format);
+  }
+  if (prevcaps)
+    gst_caps_unref (prevcaps);
 
-  return TRUE;
+  if (!ret)
+    return ret;
+
+  return GST_VIDEO_DECODER_CLASS (gst_vtdec_parent_class)->negotiate (decoder);
 }
 
 static gboolean
@@ -345,8 +347,10 @@ gst_vtdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
     return TRUE;
   }
 
-  if (vtdec->session)
+  if (vtdec->session) {
+    gst_vtdec_push_frames_if_needed (vtdec, TRUE, FALSE);
     gst_vtdec_invalidate_session (vtdec);
+  }
 
   gst_video_info_from_caps (&vtdec->video_info, state->caps);
 
@@ -366,8 +370,9 @@ gst_vtdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
     CFRelease (vtdec->format_description);
   vtdec->format_description = format_description;
 
-  if (!gst_vtdec_negotiate_output_format (vtdec, state))
-    return FALSE;
+  if (vtdec->input_state)
+    gst_video_codec_state_unref (vtdec->input_state);
+  vtdec->input_state = gst_video_codec_state_ref (state);
 
   return TRUE;
 }
@@ -444,6 +449,16 @@ error:
   goto out;
 }
 
+static void
+gst_vtdec_invalidate_session (GstVtdec * vtdec)
+{
+  g_return_if_fail (vtdec->session);
+
+  VTDecompressionSessionInvalidate (vtdec->session);
+  CFRelease (vtdec->session);
+  vtdec->session = NULL;
+}
+
 static gboolean
 gst_vtdec_create_session (GstVtdec * vtdec, GstVideoFormat format)
 {
@@ -453,6 +468,8 @@ gst_vtdec_create_session (GstVtdec * vtdec, GstVideoFormat format)
   OSStatus status;
   guint32 cv_format = 0;
 
+  g_return_val_if_fail (vtdec->session == NULL, FALSE);
+
   switch (format) {
     case GST_VIDEO_FORMAT_NV12:
       cv_format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
@@ -514,16 +531,6 @@ gst_vtdec_create_session (GstVtdec * vtdec, GstVideoFormat format)
   return TRUE;
 }
 
-static void
-gst_vtdec_invalidate_session (GstVtdec * vtdec)
-{
-  g_return_if_fail (vtdec->session);
-
-  VTDecompressionSessionInvalidate (vtdec->session);
-  CFRelease (vtdec->session);
-  vtdec->session = NULL;
-}
-
 static CMFormatDescriptionRef
 create_format_description (GstVtdec * vtdec, CMVideoCodecType cm_format)
 {
@@ -793,8 +800,9 @@ gst_vtdec_push_frames_if_needed (GstVtdec * vtdec, gboolean drain,
    */
   /* negotiate now so that we know whether we need to use the GL upload meta or
    * not */
-  if (gst_pad_check_reconfigure (decoder->srcpad))
+  if (gst_pad_check_reconfigure (decoder->srcpad)) {
     gst_video_decoder_negotiate (decoder);
+  }
 
   if (drain)
     VTDecompressionSessionWaitForAsynchronousFrames (vtdec->session);
index 4b9c788..69b4402 100644 (file)
@@ -41,6 +41,7 @@ typedef struct _GstVtdecClass GstVtdecClass;
 struct _GstVtdec
 {
   GstVideoDecoder base_vtdec;
+  GstVideoCodecState *input_state;
   GstVideoInfo video_info;
   CMFormatDescriptionRef format_description;
   VTDecompressionSessionRef session;