ogg: Implement Ogg VP8 mapping
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Wed, 5 May 2010 11:59:57 +0000 (13:59 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Wed, 19 May 2010 17:23:07 +0000 (19:23 +0200)
ext/ogg/gstoggdemux.c
ext/ogg/gstoggmux.c
ext/ogg/gstoggstream.c
ext/ogg/gstoggstream.h

index c29e0e4..36b71eb 100644 (file)
@@ -437,7 +437,7 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
       GST_BUFFER_SIZE (buf) = bytes;
 
       tags = gst_tag_list_from_vorbiscomment_buffer (buf,
-          (guint8 *) "\003vorbis", 7, NULL);
+          (const guint8 *) "\003vorbis", 7, NULL);
       gst_buffer_unref (buf);
       buf = NULL;
 
@@ -465,6 +465,39 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
         bytes--;
       }
     }
+  } else if (pad->map.is_vp8) {
+    /* packet 0 is from the BOS page, packet 1 is the vorbiscomment page */
+    if (packet->packetno == 1 && packet->bytes >= 7
+        && memcmp (packet->packet, "VP8_TAG", 7) == 0) {
+      GstTagList *tags;
+
+      buf = gst_buffer_new ();
+
+      GST_BUFFER_DATA (buf) = (guint8 *) packet->packet;
+      GST_BUFFER_SIZE (buf) = packet->bytes;
+
+      tags = gst_tag_list_from_vorbiscomment_buffer (buf,
+          (const guint8 *) "VP8_TAG", 7, NULL);
+      gst_buffer_unref (buf);
+      buf = NULL;
+
+      if (tags) {
+        GST_DEBUG_OBJECT (ogg, "tags = %" GST_PTR_FORMAT, tags);
+        gst_element_found_tags_for_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad),
+            tags);
+      } else {
+        GST_DEBUG_OBJECT (ogg, "failed to extract tags from vorbis comment");
+      }
+      /* We don't push header packets for VP8 */
+      cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
+      goto done;
+    } else if (packet->b_o_s) {
+      /* We don't push header packets for VP8 */
+      cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
+      goto done;
+    }
+    offset = 0;
+    trim = 0;
   } else {
     offset = 0;
     trim = 0;
@@ -524,8 +557,8 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
             pad->current_granule) - out_timestamp;
       }
       out_offset_end =
-          gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule,
-          pad->keyframe_granule);
+          gst_ogg_stream_granule_to_granulepos (&pad->map,
+          pad->current_granule, pad->keyframe_granule);
       out_offset =
           gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
     }
@@ -629,12 +662,14 @@ empty_packet:
     cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
     goto done;
   }
+
 no_timestamp:
   {
     GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
     cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
     goto done;
   }
+
 no_buffer:
   {
     GST_DEBUG_OBJECT (ogg,
@@ -1485,8 +1520,8 @@ error:
  * is.
  */
 static GstFlowReturn
-gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
-    gint64 * offset)
+gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
+    gint64 boundary, gint64 * offset)
 {
   gint64 end_offset = -1;
   GstFlowReturn ret;
@@ -1511,8 +1546,8 @@ gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
     if (more < 0) {
       /* skipped n bytes */
       ogg->offset -= more;
-      GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT, more,
-          ogg->offset);
+      GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
+          more, ogg->offset);
     } else if (more == 0) {
       /* we need more data */
       if (boundary == 0)
@@ -1783,8 +1818,8 @@ do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
   best = begin;
 
   GST_DEBUG_OBJECT (ogg,
-      "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin,
-      end);
+      "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
+      begin, end);
   GST_DEBUG_OBJECT (ogg,
       "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
       GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
@@ -2048,8 +2083,8 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
       continue;
 
     /* convert granule of this pad to the granule of the keyframe */
-    pad->keyframe_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map,
-        granulepos);
+    pad->keyframe_granule =
+        gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
     GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
         pad->keyframe_granule);
 
@@ -2894,8 +2929,8 @@ gst_ogg_demux_find_chains (GstOggDemux * ogg)
     /* we still call this function here but with an empty range so that
      * we can reuse the setup code in this routine. */
     ret =
-        gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length,
-        chain, 0);
+        gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
+        ogg->length, chain, 0);
   }
   if (ret != GST_FLOW_OK)
     goto done;
@@ -3550,8 +3585,9 @@ gst_ogg_print (GstOggDemux * ogg)
     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
 
     GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
-    GST_INFO_OBJECT (ogg, "  offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT,
-        chain->offset, chain->end_offset);
+    GST_INFO_OBJECT (ogg,
+        "  offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
+        chain->end_offset);
     GST_INFO_OBJECT (ogg, "  begin time: %" GST_TIME_FORMAT,
         GST_TIME_ARGS (chain->begin_time));
     GST_INFO_OBJECT (ogg, "  total time: %" GST_TIME_FORMAT,
index bdcc441..f46a128 100644 (file)
@@ -41,6 +41,7 @@
 
 #include <gst/gst.h>
 #include <gst/base/gstcollectpads.h>
+#include <gst/tag/tag.h>
 
 #include "gstoggmux.h"
 
@@ -100,7 +101,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
     GST_STATIC_CAPS ("video/x-theora; "
         "audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; "
         "application/x-ogm-video; application/x-ogm-audio; video/x-dirac; "
-        "video/x-smoke; text/x-cmml, encoded = (boolean) TRUE; "
+        "video/x-smoke; video/x-vp8; text/x-cmml, encoded = (boolean) TRUE; "
         "subtitle/x-kate; application/x-kate")
     );
 
@@ -1037,7 +1038,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
     GST_LOG_OBJECT (mux, "swapped out page with mime type %s",
         gst_structure_get_name (structure));
 
-    /* quick hack: put Theora and Dirac video pages at the front.
+    /* quick hack: put Theora, VP8 and Dirac video pages at the front.
      * Ideally, we would have a settable enum for which Ogg
      * profile we work with, and order based on that.
      * (FIXME: if there is more than one video stream, shouldn't we only put
@@ -1050,6 +1051,9 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
       GST_DEBUG_OBJECT (thepad, "putting %s page at the front", "Dirac");
       hbufs = g_list_prepend (hbufs, hbuf);
       pad->always_flush_page = TRUE;
+    } else if (gst_structure_has_name (structure, "video/x-vp8")) {
+      GST_DEBUG_OBJECT (thepad, "putting %s page at the front", "VP8");
+      hbufs = g_list_prepend (hbufs, hbuf);
     } else {
       hbufs = g_list_append (hbufs, hbuf);
     }
@@ -1188,7 +1192,6 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
   if (ogg_mux->pulling && best &&
       ogg_mux->pulling != best && ogg_mux->pulling->buffer) {
     GstOggPadData *pad = ogg_mux->pulling;
-
     GstClockTime last_ts = GST_BUFFER_END_TIME (pad->buffer);
 
     /* if the next packet in the current page is going to make the page
index a4e1891..68b3e89 100644 (file)
@@ -56,8 +56,6 @@ typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
 typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
     ogg_packet * packet);
 
-
-
 #define SKELETON_FISBONE_MIN_SIZE  52
 #define SKELETON_FISHEAD_3_3_MIN_SIZE 112
 
@@ -207,9 +205,6 @@ gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
   return mappers[pad->map].packet_duration_func (pad, packet);
 }
 
-
-
-
 /* some generic functions */
 
 static gboolean
@@ -463,6 +458,93 @@ granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
   return -1;
 }
 
+/* VP8 */
+
+static gboolean
+setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+  gint width, height, par_n, par_d, fps_n, fps_d;
+
+  if (packet->bytes < 24) {
+    GST_DEBUG ("Failed to parse VP8 BOS page");
+    return FALSE;
+  }
+
+  width = GST_READ_UINT16_BE (packet->packet + 6);
+  height = GST_READ_UINT16_BE (packet->packet + 8);
+  par_n = GST_READ_UINT24_BE (packet->packet + 10);
+  par_d = GST_READ_UINT24_BE (packet->packet + 13);
+  fps_n = GST_READ_UINT32_BE (packet->packet + 16);
+  fps_d = GST_READ_UINT32_BE (packet->packet + 20);
+
+  pad->is_vp8 = TRUE;
+  pad->granulerate_n = fps_n;
+  pad->granulerate_d = fps_d;
+  pad->n_header_packets = 2;
+  pad->frame_size = 1;
+
+  pad->caps = gst_caps_new_simple ("video/x-vp8",
+      "width", G_TYPE_INT, width,
+      "height", G_TYPE_INT, height,
+      "pixel-aspect-ratio", GST_TYPE_FRACTION,
+      par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
+
+  return TRUE;
+}
+
+static gboolean
+is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
+{
+  guint64 gpos = granulepos;
+
+  if (granulepos == -1)
+    return FALSE;
+
+  /* Get rid of flags */
+  gpos >>= 3;
+
+  return ((gpos & 0x07ffffff) == 0);
+}
+
+static gint64
+granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
+{
+  guint64 gp = (guint64) gpos;
+  guint32 pt;
+  guint32 dist;
+
+  pt = (gp >> 32);
+  dist = (gp >> 3) & 0x07ffffff;
+
+  GST_DEBUG ("pt %u, dist %u", pt, dist);
+
+  return pt;
+}
+
+static gint64
+granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
+    gint64 keyframe_granule)
+{
+  /* FIXME: This requires to look into the content of the packets
+   * because the simple granule counter doesn't know about invisible
+   * frames...
+   */
+  return -1;
+}
+
+/* Check if this packet contains an invisible frame or not */
+static gint64
+packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
+{
+  guint32 hdr;
+
+  if (packet->bytes < 3)
+    return 0;
+
+  hdr = GST_READ_UINT24_LE (packet->packet);
+
+  return (((hdr >> 4) & 1) != 0) ? 1 : 0;
+}
 
 /* vorbis */
 
@@ -1452,7 +1534,7 @@ static const GstOggMap mappers[] = {
     NULL,
     NULL,
     NULL,
-    NULL,
+    NULL
   },
   {
     "CELT    ", 8, 0,
@@ -1485,6 +1567,16 @@ static const GstOggMap mappers[] = {
     packet_duration_constant
   },
   {
+    "VP80\1", 5, 4,
+    "video/x-vp8",
+    setup_vp8_mapper,
+    granulepos_to_granule_vp8,
+    granule_to_granulepos_vp8,
+    is_keyframe_vp8,
+    is_header_count,
+    packet_duration_vp8
+  },
+  {
     "\001audio\0\0\0", 9, 53,
     "application/x-ogm-audio",
     setup_ogmaudio_mapper,
index b70b5b3..1622612 100644 (file)
@@ -79,6 +79,8 @@ struct _GstOggStream
   int last_size;
   /* theora stuff */
   gboolean theora_has_zero_keyoffset;
+  /* VP8 stuff */
+  gboolean is_vp8;
   /* OGM stuff */
   gboolean is_ogm;
   gboolean is_ogm_text;