theora: If no header packets in stream, look for them in the caps
authorJan Schmidt <jan@centricular.com>
Mon, 2 Feb 2015 14:19:05 +0000 (01:19 +1100)
committerJan Schmidt <jan@centricular.com>
Thu, 5 Feb 2015 17:05:27 +0000 (04:05 +1100)
Makes theora work in cases where the header packets are only in the caps
(because theoradec was connected to oggdemux late and missed the
beginning of the stream)

ext/theora/gsttheoradec.c

index d1a7b24..d2f14b0 100644 (file)
@@ -418,7 +418,7 @@ theora_handle_type_packet (GstTheoraDec * dec)
   }
   /* theora has:
    *
-   *  width/height : dimension of the encoded frame 
+   *  width/height : dimension of the encoded frame
    *  pic_width/pic_height : dimension of the visible part
    *  pic_x/pic_y : offset in encoded frame where visible part starts
    */
@@ -559,6 +559,62 @@ header_read_error:
   }
 }
 
+#define MIN_NUM_HEADERS 3
+static GstFlowReturn
+theoradec_handle_header_caps (GstTheoraDec * dec)
+{
+  GstFlowReturn result = GST_CUSTOM_FLOW_DROP;
+  GstCaps *caps;
+  GstStructure *s = NULL;
+  const GValue *array = NULL;
+
+  GST_DEBUG_OBJECT (dec, "Looking for Theora headers in caps");
+  caps = gst_pad_get_current_caps (GST_VIDEO_DECODER_SINK_PAD (dec));
+  if (caps)
+    s = gst_caps_get_structure (caps, 0);
+  if (s)
+    array = gst_structure_get_value (s, "streamheader");
+
+  if (caps)
+    gst_caps_unref (caps);
+
+  if (array && (gst_value_array_get_size (array) >= MIN_NUM_HEADERS)) {
+    const GValue *value = NULL;
+    GstBuffer *buf = NULL;
+    gint i = 0;
+
+    while (result == GST_CUSTOM_FLOW_DROP
+        && i < gst_value_array_get_size (array)) {
+      value = gst_value_array_get_value (array, i);
+      buf = gst_value_get_buffer (value);
+      if (!buf)
+        goto null_buffer;
+      GST_LOG_OBJECT (dec, "Submitting header packet");
+      result = theora_dec_decode_buffer (dec, buf, NULL);
+      i++;
+    }
+  } else
+    goto array_error;
+
+done:
+  return (result !=
+      GST_CUSTOM_FLOW_DROP ? GST_FLOW_NOT_NEGOTIATED : GST_FLOW_OK);
+
+  /* ERRORS */
+array_error:
+  {
+    GST_WARNING_OBJECT (dec, "streamheader array not found");
+    result = GST_FLOW_ERROR;
+    goto done;
+  }
+null_buffer:
+  {
+    GST_WARNING_OBJECT (dec, "streamheader with null buffer received");
+    result = GST_FLOW_ERROR;
+    goto done;
+  }
+}
+
 /* Allocate buffer and copy image data into Y444 format */
 static GstFlowReturn
 theora_handle_image (GstTheoraDec * dec, th_ycbcr_buffer buf,
@@ -683,10 +739,13 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
   GstFlowReturn result;
   ogg_int64_t gp;
 
-  if (G_UNLIKELY (!dec->have_header))
-    goto not_initialized;
+  if (G_UNLIKELY (!dec->have_header)) {
+    result = theoradec_handle_header_caps (dec);
+    if (result != GST_FLOW_OK)
+      goto not_initialized;
+  }
 
-  /* the second most significant bit of the first data byte is cleared 
+  /* the second most significant bit of the first data byte is cleared
    * for keyframes. We can only check it if it's not a zero-length packet. */
   keyframe = packet->bytes && ((packet->packet[0] & 0x40) == 0);
   if (G_UNLIKELY (keyframe)) {