theoradec: rearrange code in preparation for 422 and 444 support.
authorMichael Smith <msmith@syncword.(none)>
Tue, 28 Apr 2009 04:01:51 +0000 (21:01 -0700)
committerMichael Smith <msmith@syncword.(none)>
Mon, 11 May 2009 01:08:12 +0000 (18:08 -0700)
ext/theora/gsttheoradec.h
ext/theora/theoradec.c

index 30646ef..ed7b722 100644 (file)
@@ -72,6 +72,7 @@ struct _GstTheoraDec
   gboolean need_keyframe;
   gint width, height;
   gint offset_x, offset_y;
+  gint output_bpp;
 
   gboolean crop;
 
index 78b5e08..64b07fb 100644 (file)
@@ -66,7 +66,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("video/x-raw-yuv, "
-        "format = (fourcc) I420, "
+        "format = (fourcc) { I420, YUY2, Y444 }, "
         "framerate = (fraction) [0/1, MAX], "
         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
     );
@@ -346,8 +346,8 @@ theora_dec_src_convert (GstPad * pad,
     case GST_FORMAT_BYTES:
       switch (*dest_format) {
         case GST_FORMAT_DEFAULT:
-          *dest_value = gst_util_uint64_scale_int (src_value, 2,
-              dec->info.height * dec->info.width * 3);
+          *dest_value = gst_util_uint64_scale_int (src_value, 8,
+              dec->info.height * dec->info.width * dec->output_bpp);
           break;
         case GST_FORMAT_TIME:
           /* seems like a rather silly conversion, implement me if you like */
@@ -358,7 +358,7 @@ theora_dec_src_convert (GstPad * pad,
     case GST_FORMAT_TIME:
       switch (*dest_format) {
         case GST_FORMAT_BYTES:
-          scale = 3 * (dec->info.width * dec->info.height) / 2;
+          scale = dec->output_bpp * (dec->info.width * dec->info.height) / 8;
         case GST_FORMAT_DEFAULT:
           *dest_value = scale * gst_util_uint64_scale (src_value,
               dec->info.fps_numerator, dec->info.fps_denominator * GST_SECOND);
@@ -375,7 +375,7 @@ theora_dec_src_convert (GstPad * pad,
           break;
         case GST_FORMAT_BYTES:
           *dest_value = gst_util_uint64_scale_int (src_value,
-              3 * dec->info.width * dec->info.height, 2);
+              dec->output_bpp * dec->info.width * dec->info.height, 8);
           break;
         default:
           res = FALSE;
@@ -853,6 +853,7 @@ theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
   GstFlowReturn ret = GST_FLOW_OK;
   guint32 bitstream_version;
   GList *walk;
+  guint32 fourcc;
 
   GST_DEBUG_OBJECT (dec, "fps %d/%d, PAR %d/%d",
       dec->info.fps_numerator, dec->info.fps_denominator,
@@ -888,10 +889,18 @@ theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
   GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, offset %d:%d",
       dec->info.frame_width, dec->info.frame_height,
       dec->info.offset_x, dec->info.offset_y);
-  if (dec->info.pixelformat != OC_PF_420) {
-    GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
-        (NULL), ("pixel formats other than 4:2:0 not yet supported"));
 
+  if (dec->info.pixelformat == OC_PF_420) {
+    dec->output_bpp = 12;       /* Average bits per pixel. */
+    fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
+  } else if (dec->info.pixelformat == OC_PF_422) {
+    dec->output_bpp = 16;
+    fourcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
+  } else if (dec->info.pixelformat == OC_PF_444) {
+    dec->output_bpp = 24;
+    fourcc = GST_MAKE_FOURCC ('Y', '4', '4', '4');
+  } else {
+    GST_ERROR_OBJECT (dec, "Invalid pixel format %d", dec->info.pixelformat);
     return GST_FLOW_ERROR;
   }
 
@@ -931,7 +940,7 @@ theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
   theora_decode_init (&dec->state, &dec->info);
 
   caps = gst_caps_new_simple ("video/x-raw-yuv",
-      "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
+      "format", GST_TYPE_FOURCC, fourcc,
       "framerate", GST_TYPE_FRACTION,
       dec->info.fps_numerator, dec->info.fps_denominator,
       "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den,
@@ -1107,72 +1116,16 @@ theora_dec_push_reverse (GstTheoraDec * dec, GstBuffer * buf)
 }
 
 static GstFlowReturn
-theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
-    GstClockTime outtime)
+theora_handle_420_image (GstTheoraDec * dec, yuv_buffer * yuv, GstBuffer ** out)
 {
-  /* normal data packet */
-  yuv_buffer yuv;
-  GstBuffer *out;
-  guint i;
-  gboolean keyframe;
+  gint width = dec->width;
+  gint height = dec->height;
+  gint cwidth = width / 2;
+  gint cheight = height / 2;
   gint out_size;
   gint stride_y, stride_uv;
-  gint width, height;
-  gint cwidth, cheight;
   GstFlowReturn result;
-
-  if (G_UNLIKELY (!dec->have_header))
-    goto not_initialized;
-
-  /* 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)) {
-    GST_DEBUG_OBJECT (dec, "we have a keyframe");
-    dec->need_keyframe = FALSE;
-  } else if (G_UNLIKELY (dec->need_keyframe)) {
-    goto dropping;
-  }
-
-  GST_DEBUG_OBJECT (dec, "parsing data packet");
-
-  /* this does the decoding */
-  if (G_UNLIKELY (theora_decode_packetin (&dec->state, packet)))
-    goto decode_error;
-
-  if (outtime != -1) {
-    gboolean need_skip;
-    GstClockTime qostime;
-
-    /* qos needs to be done on running time */
-    qostime = gst_segment_to_running_time (&dec->segment, GST_FORMAT_TIME,
-        outtime);
-
-    GST_OBJECT_LOCK (dec);
-    /* check for QoS, don't perform the last steps of getting and
-     * pushing the buffers that are known to be late. */
-    /* FIXME, we can also entirely skip decoding if the next valid buffer is 
-     * known to be after a keyframe (using the granule_shift) */
-    need_skip = dec->earliest_time != -1 && qostime <= dec->earliest_time;
-    GST_OBJECT_UNLOCK (dec);
-
-    if (need_skip)
-      goto dropping_qos;
-  }
-
-  /* this does postprocessing and set up the decoded frame
-   * pointers in our yuv variable */
-  if (G_UNLIKELY (theora_decode_YUVout (&dec->state, &yuv) < 0))
-    goto no_yuv;
-
-  if (G_UNLIKELY ((yuv.y_width != dec->info.width)
-          || (yuv.y_height != dec->info.height)))
-    goto wrong_dimensions;
-
-  width = dec->width;
-  height = dec->height;
-  cwidth = width / 2;
-  cheight = height / 2;
+  int i;
 
   /* should get the stride from the caps, for now we round up to the nearest
    * multiple of 4 because some element needs it. chroma needs special 
@@ -1187,7 +1140,7 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
    * frame_width, frame_height */
   result =
       gst_pad_alloc_buffer_and_set_caps (dec->srcpad, GST_BUFFER_OFFSET_NONE,
-      out_size, GST_PAD_CAPS (dec->srcpad), &out);
+      out_size, GST_PAD_CAPS (dec->srcpad), out);
   if (G_UNLIKELY (result != GST_FLOW_OK))
     goto no_buffer;
 
@@ -1204,7 +1157,7 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
     guchar *dest_v, *src_v;
     gint offset;
 
-    dest_y = GST_BUFFER_DATA (out);
+    dest_y = GST_BUFFER_DATA (*out);
     dest_u = dest_y + stride_y * GST_ROUND_UP_2 (height);
     dest_v = dest_u + stride_uv * GST_ROUND_UP_2 (height) / 2;
 
@@ -1212,30 +1165,110 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
     GST_LOG_OBJECT (dec, "plane 1, offset %d", dest_u - dest_y);
     GST_LOG_OBJECT (dec, "plane 2, offset %d", dest_v - dest_y);
 
-    src_y = yuv.y + dec->offset_x + dec->offset_y * yuv.y_stride;
+    src_y = yuv->y + dec->offset_x + dec->offset_y * yuv->y_stride;
 
     for (i = 0; i < height; i++) {
       memcpy (dest_y, src_y, width);
 
       dest_y += stride_y;
-      src_y += yuv.y_stride;
+      src_y += yuv->y_stride;
     }
 
-    offset = dec->offset_x / 2 + dec->offset_y / 2 * yuv.uv_stride;
+    offset = dec->offset_x / 2 + dec->offset_y / 2 * yuv->uv_stride;
 
-    src_u = yuv.u + offset;
-    src_v = yuv.v + offset;
+    src_u = yuv->u + offset;
+    src_v = yuv->v + offset;
 
     for (i = 0; i < cheight; i++) {
       memcpy (dest_u, src_u, cwidth);
       memcpy (dest_v, src_v, cwidth);
 
       dest_u += stride_uv;
-      src_u += yuv.uv_stride;
+      src_u += yuv->uv_stride;
       dest_v += stride_uv;
-      src_v += yuv.uv_stride;
+      src_v += yuv->uv_stride;
     }
   }
+no_buffer:
+  {
+    GST_DEBUG_OBJECT (dec, "could not get buffer, reason: %s",
+        gst_flow_get_name (result));
+    return result;
+  }
+}
+
+static GstFlowReturn
+theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
+    GstClockTime outtime)
+{
+  /* normal data packet */
+  yuv_buffer yuv;
+  GstBuffer *out;
+  gboolean keyframe;
+  GstFlowReturn result;
+
+  if (G_UNLIKELY (!dec->have_header))
+    goto not_initialized;
+
+  /* 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)) {
+    GST_DEBUG_OBJECT (dec, "we have a keyframe");
+    dec->need_keyframe = FALSE;
+  } else if (G_UNLIKELY (dec->need_keyframe)) {
+    goto dropping;
+  }
+
+  GST_DEBUG_OBJECT (dec, "parsing data packet");
+
+  /* this does the decoding */
+  if (G_UNLIKELY (theora_decode_packetin (&dec->state, packet)))
+    goto decode_error;
+
+  if (outtime != -1) {
+    gboolean need_skip;
+    GstClockTime qostime;
+
+    /* qos needs to be done on running time */
+    qostime = gst_segment_to_running_time (&dec->segment, GST_FORMAT_TIME,
+        outtime);
+
+    GST_OBJECT_LOCK (dec);
+    /* check for QoS, don't perform the last steps of getting and
+     * pushing the buffers that are known to be late. */
+    /* FIXME, we can also entirely skip decoding if the next valid buffer is 
+     * known to be after a keyframe (using the granule_shift) */
+    need_skip = dec->earliest_time != -1 && qostime <= dec->earliest_time;
+    GST_OBJECT_UNLOCK (dec);
+
+    if (need_skip)
+      goto dropping_qos;
+  }
+
+  /* this does postprocessing and set up the decoded frame
+   * pointers in our yuv variable */
+  if (G_UNLIKELY (theora_decode_YUVout (&dec->state, &yuv) < 0))
+    goto no_yuv;
+
+  if (G_UNLIKELY ((yuv.y_width != dec->info.width)
+          || (yuv.y_height != dec->info.height)))
+    goto wrong_dimensions;
+
+  if (dec->info.pixelformat == OC_PF_420) {
+    result = theora_handle_420_image (dec, &yuv, &out);
+#if 0
+  } else if (dec->info.pixelformat == OC_PF_422) {
+    result = theora_handle_422_image (dec, &yuv, &out);
+  } else if (dec->info.pixelformat == OC_PF_444) {
+    result = theora_handle_444_image (dec, &yuv, &out);
+  } else {
+    g_assert_not_reached ();
+#endif
+  }
+
+  if (result != GST_FLOW_OK)
+    return result;
 
   GST_BUFFER_OFFSET (out) = dec->frame_nr;
   if (dec->frame_nr != -1)
@@ -1299,12 +1332,6 @@ wrong_dimensions:
         (NULL), ("dimensions of image do not match header"));
     return GST_FLOW_ERROR;
   }
-no_buffer:
-  {
-    GST_DEBUG_OBJECT (dec, "could not get buffer, reason: %s",
-        gst_flow_get_name (result));
-    return result;
-  }
 }
 
 static GstFlowReturn