gst_plugins_base/ext/ogg/gstoggaviparse.c
branchRCL_3
changeset 30 7e817e7e631c
parent 0 0e761a78d257
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
       
     1 /* GStreamer
       
     2  * Copyright (C) 2006 Wim Taymans <wim@fluendo.com>
       
     3  *
       
     4  * gstoggaviparse.c: ogg avi stream parser
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Library General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Library General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Library General Public
       
    17  * License along with this library; if not, write to the
       
    18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    19  * Boston, MA 02111-1307, USA.
       
    20  */
       
    21 
       
    22 /*
       
    23  * Ogg in AVI is mostly done for vorbis audio. In the codec_data we receive the
       
    24  * first 3 packets of the raw vorbis data. On the sinkpad we receive full-blown Ogg
       
    25  * pages. 
       
    26  * Before extracting the packets out of the ogg pages, we push the raw vorbis
       
    27  * header packets to the decoder.
       
    28  * We don't use the incomming timestamps but use the ganulepos on the ogg pages
       
    29  * directly.
       
    30  * This parser only does ogg/vorbis for now.
       
    31  */
       
    32 
       
    33 #ifdef HAVE_CONFIG_H
       
    34 #include "config.h"
       
    35 #endif
       
    36 #include <gst/gst.h>
       
    37 #include <ogg/ogg.h>
       
    38 #include <string.h>
       
    39 
       
    40 static const GstElementDetails gst_ogg_avi_parse_details =
       
    41 GST_ELEMENT_DETAILS ("Ogg AVI parser",
       
    42     "Codec/Parser",
       
    43     "parse an ogg avi stream into pages (info about ogg: http://xiph.org)",
       
    44     "Wim Taymans <wim@fluendo.com>");
       
    45 
       
    46 GST_DEBUG_CATEGORY_STATIC (gst_ogg_avi_parse_debug);
       
    47 #define GST_CAT_DEFAULT gst_ogg_avi_parse_debug
       
    48 
       
    49 #define GST_TYPE_OGG_AVI_PARSE (gst_ogg_avi_parse_get_type())
       
    50 #define GST_OGG_AVI_PARSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_AVI_PARSE, GstOggAviParse))
       
    51 #define GST_OGG_AVI_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_AVI_PARSE, GstOggAviParse))
       
    52 #define GST_IS_OGG_AVI_PARSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_AVI_PARSE))
       
    53 #define GST_IS_OGG_AVI_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_AVI_PARSE))
       
    54 
       
    55 static GType gst_ogg_avi_parse_get_type (void);
       
    56 
       
    57 typedef struct _GstOggAviParse GstOggAviParse;
       
    58 typedef struct _GstOggAviParseClass GstOggAviParseClass;
       
    59 
       
    60 struct _GstOggAviParse
       
    61 {
       
    62   GstElement element;
       
    63 
       
    64   GstPad *sinkpad;
       
    65   GstPad *srcpad;
       
    66 
       
    67   gboolean discont;
       
    68   gint serial;
       
    69 
       
    70   ogg_sync_state sync;
       
    71   ogg_stream_state stream;
       
    72 };
       
    73 
       
    74 struct _GstOggAviParseClass
       
    75 {
       
    76   GstElementClass parent_class;
       
    77 };
       
    78 
       
    79 static void gst_ogg_avi_parse_base_init (gpointer g_class);
       
    80 static void gst_ogg_avi_parse_class_init (GstOggAviParseClass * klass);
       
    81 static void gst_ogg_avi_parse_init (GstOggAviParse * ogg);
       
    82 static GstElementClass *parent_class = NULL;
       
    83 
       
    84 static GType
       
    85 gst_ogg_avi_parse_get_type (void)
       
    86 {
       
    87   static GType ogg_avi_parse_type = 0;
       
    88 
       
    89   if (!ogg_avi_parse_type) {
       
    90     static const GTypeInfo ogg_avi_parse_info = {
       
    91       sizeof (GstOggAviParseClass),
       
    92       gst_ogg_avi_parse_base_init,
       
    93       NULL,
       
    94       (GClassInitFunc) gst_ogg_avi_parse_class_init,
       
    95       NULL,
       
    96       NULL,
       
    97       sizeof (GstOggAviParse),
       
    98       0,
       
    99       (GInstanceInitFunc) gst_ogg_avi_parse_init,
       
   100     };
       
   101 
       
   102     ogg_avi_parse_type =
       
   103         g_type_register_static (GST_TYPE_ELEMENT, "GstOggAviParse",
       
   104         &ogg_avi_parse_info, 0);
       
   105   }
       
   106   return ogg_avi_parse_type;
       
   107 }
       
   108 
       
   109 enum
       
   110 {
       
   111   PROP_0
       
   112 };
       
   113 
       
   114 static GstStaticPadTemplate ogg_avi_parse_src_template_factory =
       
   115 GST_STATIC_PAD_TEMPLATE ("src",
       
   116     GST_PAD_SRC,
       
   117     GST_PAD_ALWAYS,
       
   118     GST_STATIC_CAPS ("audio/x-vorbis")
       
   119     );
       
   120 
       
   121 static GstStaticPadTemplate ogg_avi_parse_sink_template_factory =
       
   122 GST_STATIC_PAD_TEMPLATE ("sink",
       
   123     GST_PAD_SINK,
       
   124     GST_PAD_ALWAYS,
       
   125     GST_STATIC_CAPS ("application/x-ogg-avi")
       
   126     );
       
   127 
       
   128 static void gst_ogg_avi_parse_finalize (GObject * object);
       
   129 static GstStateChangeReturn gst_ogg_avi_parse_change_state (GstElement *
       
   130     element, GstStateChange transition);
       
   131 static gboolean gst_ogg_avi_parse_event (GstPad * pad, GstEvent * event);
       
   132 static GstFlowReturn gst_ogg_avi_parse_chain (GstPad * pad, GstBuffer * buffer);
       
   133 static gboolean gst_ogg_avi_parse_setcaps (GstPad * pad, GstCaps * caps);
       
   134 
       
   135 static void
       
   136 gst_ogg_avi_parse_base_init (gpointer g_class)
       
   137 {
       
   138   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   139 
       
   140   gst_element_class_set_details (element_class, &gst_ogg_avi_parse_details);
       
   141 
       
   142   gst_element_class_add_pad_template (element_class,
       
   143       gst_static_pad_template_get (&ogg_avi_parse_sink_template_factory));
       
   144   gst_element_class_add_pad_template (element_class,
       
   145       gst_static_pad_template_get (&ogg_avi_parse_src_template_factory));
       
   146 }
       
   147 
       
   148 static void
       
   149 gst_ogg_avi_parse_class_init (GstOggAviParseClass * klass)
       
   150 {
       
   151   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
       
   152   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
       
   153 
       
   154   parent_class = g_type_class_peek_parent (klass);
       
   155 
       
   156   gstelement_class->change_state = gst_ogg_avi_parse_change_state;
       
   157 
       
   158   gobject_class->finalize = gst_ogg_avi_parse_finalize;
       
   159 }
       
   160 
       
   161 static void
       
   162 gst_ogg_avi_parse_init (GstOggAviParse * ogg)
       
   163 {
       
   164   /* create the sink and source pads */
       
   165   ogg->sinkpad =
       
   166       gst_pad_new_from_static_template (&ogg_avi_parse_sink_template_factory,
       
   167       "sink");
       
   168   gst_pad_set_setcaps_function (ogg->sinkpad, gst_ogg_avi_parse_setcaps);
       
   169   gst_pad_set_event_function (ogg->sinkpad, gst_ogg_avi_parse_event);
       
   170   gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_avi_parse_chain);
       
   171   gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
       
   172 
       
   173   ogg->srcpad =
       
   174       gst_pad_new_from_static_template (&ogg_avi_parse_src_template_factory,
       
   175       "src");
       
   176   gst_pad_use_fixed_caps (ogg->srcpad);
       
   177   gst_element_add_pad (GST_ELEMENT (ogg), ogg->srcpad);
       
   178 }
       
   179 
       
   180 static void
       
   181 gst_ogg_avi_parse_finalize (GObject * object)
       
   182 {
       
   183   GstOggAviParse *ogg = GST_OGG_AVI_PARSE (object);
       
   184 
       
   185   GST_LOG_OBJECT (ogg, "Disposing of object %p", ogg);
       
   186 
       
   187   ogg_sync_clear (&ogg->sync);
       
   188   ogg_stream_clear (&ogg->stream);
       
   189 
       
   190   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   191 }
       
   192 
       
   193 static gboolean
       
   194 gst_ogg_avi_parse_setcaps (GstPad * pad, GstCaps * caps)
       
   195 {
       
   196   GstOggAviParse *ogg;
       
   197   GstStructure *structure;
       
   198   const GValue *codec_data;
       
   199   GstBuffer *buffer;
       
   200   guint8 *data;
       
   201   guint size;
       
   202   guint32 sizes[3];
       
   203   GstCaps *outcaps;
       
   204   gint i, offs;
       
   205 
       
   206   ogg = GST_OGG_AVI_PARSE (GST_OBJECT_PARENT (pad));
       
   207 
       
   208   structure = gst_caps_get_structure (caps, 0);
       
   209 
       
   210   /* take codec data */
       
   211   codec_data = gst_structure_get_value (structure, "codec_data");
       
   212   if (codec_data == NULL)
       
   213     goto no_data;
       
   214 
       
   215   /* only buffers are valid */
       
   216   if (G_VALUE_TYPE (codec_data) != GST_TYPE_BUFFER)
       
   217     goto wrong_format;
       
   218 
       
   219   /* Now parse the data */
       
   220   buffer = gst_value_get_buffer (codec_data);
       
   221 
       
   222   /* first 22 bytes are bits_per_sample, channel_mask, GUID
       
   223    * Then we get 3 LE guint32 with the 3 header sizes
       
   224    * then we get the bytes of the 3 headers. */
       
   225   data = GST_BUFFER_DATA (buffer);
       
   226   size = GST_BUFFER_SIZE (buffer);
       
   227 
       
   228   GST_LOG_OBJECT (ogg, "configuring codec_data of size %u", size);
       
   229 
       
   230   /* skip headers */
       
   231   data += 22;
       
   232   size -= 22;
       
   233 
       
   234   /* we need at least 12 bytes for the packet sizes of the 3 headers */
       
   235   if (size < 12)
       
   236     goto buffer_too_small;
       
   237 
       
   238   /* read sizes of the 3 headers */
       
   239   sizes[0] = GST_READ_UINT32_LE (data);
       
   240   sizes[1] = GST_READ_UINT32_LE (data + 4);
       
   241   sizes[2] = GST_READ_UINT32_LE (data + 8);
       
   242 
       
   243   GST_DEBUG_OBJECT (ogg, "header sizes: %u %u %u", sizes[0], sizes[1],
       
   244       sizes[2]);
       
   245 
       
   246   data += 12;
       
   247   size -= 12;
       
   248 
       
   249   /* and we need at least enough data for all the headers */
       
   250   if (size < sizes[0] + sizes[1] + sizes[2])
       
   251     goto buffer_too_small;
       
   252 
       
   253   /* set caps */
       
   254   outcaps = gst_caps_new_simple ("audio/x-vorbis", NULL);
       
   255   gst_pad_set_caps (ogg->srcpad, outcaps);
       
   256 
       
   257   /* copy header data */
       
   258   offs = 34;
       
   259   for (i = 0; i < 3; i++) {
       
   260     GstBuffer *out;
       
   261 
       
   262     /* now output the raw vorbis header packets */
       
   263     out = gst_buffer_create_sub (buffer, offs, sizes[i]);
       
   264     gst_buffer_set_caps (out, outcaps);
       
   265     gst_pad_push (ogg->srcpad, out);
       
   266 
       
   267     offs += sizes[i];
       
   268   }
       
   269   gst_caps_unref (outcaps);
       
   270 
       
   271   return TRUE;
       
   272 
       
   273   /* ERRORS */
       
   274 no_data:
       
   275   {
       
   276     GST_DEBUG_OBJECT (ogg, "no codec_data found in caps");
       
   277     return FALSE;
       
   278   }
       
   279 wrong_format:
       
   280   {
       
   281     GST_DEBUG_OBJECT (ogg, "codec_data is not a buffer");
       
   282     return FALSE;
       
   283   }
       
   284 buffer_too_small:
       
   285   {
       
   286     GST_DEBUG_OBJECT (ogg, "codec_data is too small");
       
   287     return FALSE;
       
   288   }
       
   289 }
       
   290 
       
   291 static gboolean
       
   292 gst_ogg_avi_parse_event (GstPad * pad, GstEvent * event)
       
   293 {
       
   294   GstOggAviParse *ogg;
       
   295   gboolean ret;
       
   296 
       
   297   ogg = GST_OGG_AVI_PARSE (GST_OBJECT_PARENT (pad));
       
   298 
       
   299   switch (GST_EVENT_TYPE (event)) {
       
   300     case GST_EVENT_FLUSH_START:
       
   301       ret = gst_pad_push_event (ogg->srcpad, event);
       
   302       break;
       
   303     case GST_EVENT_FLUSH_STOP:
       
   304       ogg_sync_reset (&ogg->sync);
       
   305       ogg_stream_reset (&ogg->stream);
       
   306       ogg->discont = TRUE;
       
   307       ret = gst_pad_push_event (ogg->srcpad, event);
       
   308       break;
       
   309     default:
       
   310       ret = gst_pad_push_event (ogg->srcpad, event);
       
   311       break;
       
   312   }
       
   313   return ret;
       
   314 }
       
   315 
       
   316 static GstFlowReturn
       
   317 gst_ogg_avi_parse_push_packet (GstOggAviParse * ogg, ogg_packet * packet)
       
   318 {
       
   319   GstBuffer *buffer;
       
   320   GstFlowReturn result;
       
   321 
       
   322   /* allocate space for header and body */
       
   323   buffer = gst_buffer_new_and_alloc (packet->bytes);
       
   324   memcpy (GST_BUFFER_DATA (buffer), packet->packet, packet->bytes);
       
   325 
       
   326   GST_LOG_OBJECT (ogg, "created buffer %p from page", buffer);
       
   327 
       
   328   GST_BUFFER_OFFSET_END (buffer) = packet->granulepos;
       
   329 
       
   330   if (ogg->discont) {
       
   331     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
       
   332     ogg->discont = FALSE;
       
   333   }
       
   334 
       
   335   result = gst_pad_push (ogg->srcpad, buffer);
       
   336 
       
   337   return result;
       
   338 }
       
   339 
       
   340 static GstFlowReturn
       
   341 gst_ogg_avi_parse_chain (GstPad * pad, GstBuffer * buffer)
       
   342 {
       
   343   GstFlowReturn result = GST_FLOW_OK;
       
   344   GstOggAviParse *ogg;
       
   345   guint8 *data;
       
   346   guint size;
       
   347   gchar *oggbuf;
       
   348   gint ret = -1;
       
   349 
       
   350   ogg = GST_OGG_AVI_PARSE (GST_OBJECT_PARENT (pad));
       
   351 
       
   352   data = GST_BUFFER_DATA (buffer);
       
   353   size = GST_BUFFER_SIZE (buffer);
       
   354 
       
   355   GST_LOG_OBJECT (ogg, "Chain function received buffer of size %d", size);
       
   356 
       
   357   if (GST_BUFFER_IS_DISCONT (buffer)) {
       
   358     ogg_sync_reset (&ogg->sync);
       
   359     ogg->discont = TRUE;
       
   360   }
       
   361 
       
   362   /* write data to sync layer */
       
   363   oggbuf = ogg_sync_buffer (&ogg->sync, size);
       
   364   memcpy (oggbuf, data, size);
       
   365   ogg_sync_wrote (&ogg->sync, size);
       
   366   gst_buffer_unref (buffer);
       
   367 
       
   368   /* try to get as many packets out of the stream as possible */
       
   369   do {
       
   370     ogg_page page;
       
   371 
       
   372     /* try to swap out a page */
       
   373     ret = ogg_sync_pageout (&ogg->sync, &page);
       
   374     if (ret == 0) {
       
   375       GST_DEBUG_OBJECT (ogg, "need more data");
       
   376       break;
       
   377     } else if (ret == -1) {
       
   378       GST_DEBUG_OBJECT (ogg, "discont in pages");
       
   379       ogg->discont = TRUE;
       
   380     } else {
       
   381       /* new unknown stream, init the ogg stream with the serial number of the
       
   382        * page. */
       
   383       if (ogg->serial == -1) {
       
   384         ogg->serial = ogg_page_serialno (&page);
       
   385         ogg_stream_init (&ogg->stream, ogg->serial);
       
   386       }
       
   387 
       
   388       /* submit page */
       
   389       if (ogg_stream_pagein (&ogg->stream, &page) != 0) {
       
   390         GST_WARNING_OBJECT (ogg, "ogg stream choked on page resetting stream");
       
   391         ogg_sync_reset (&ogg->sync);
       
   392         ogg->discont = TRUE;
       
   393         continue;
       
   394       }
       
   395 
       
   396       /* try to get as many packets as possible out of the page */
       
   397       do {
       
   398         ogg_packet packet;
       
   399 
       
   400         ret = ogg_stream_packetout (&ogg->stream, &packet);
       
   401         GST_LOG_OBJECT (ogg, "packetout gave %d", ret);
       
   402         switch (ret) {
       
   403           case 0:
       
   404             break;
       
   405           case -1:
       
   406             /* out of sync, We mark a DISCONT. */
       
   407             ogg->discont = TRUE;
       
   408             break;
       
   409           case 1:
       
   410             result = gst_ogg_avi_parse_push_packet (ogg, &packet);
       
   411             if (GST_FLOW_IS_FATAL (result))
       
   412               goto done;
       
   413             break;
       
   414           default:
       
   415             GST_WARNING_OBJECT (ogg,
       
   416                 "invalid return value %d for ogg_stream_packetout, resetting stream",
       
   417                 ret);
       
   418             break;
       
   419         }
       
   420       }
       
   421       while (ret != 0);
       
   422     }
       
   423   }
       
   424   while (ret != 0);
       
   425 
       
   426 done:
       
   427   return result;
       
   428 }
       
   429 
       
   430 static GstStateChangeReturn
       
   431 gst_ogg_avi_parse_change_state (GstElement * element, GstStateChange transition)
       
   432 {
       
   433   GstOggAviParse *ogg;
       
   434   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
       
   435 
       
   436   ogg = GST_OGG_AVI_PARSE (element);
       
   437 
       
   438   switch (transition) {
       
   439     case GST_STATE_CHANGE_NULL_TO_READY:
       
   440       ogg_sync_init (&ogg->sync);
       
   441       break;
       
   442     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
   443       ogg_sync_reset (&ogg->sync);
       
   444       ogg_stream_reset (&ogg->stream);
       
   445       ogg->serial = -1;
       
   446       ogg->discont = TRUE;
       
   447       break;
       
   448     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       
   449       break;
       
   450     default:
       
   451       break;
       
   452   }
       
   453 
       
   454   result = parent_class->change_state (element, transition);
       
   455 
       
   456   switch (transition) {
       
   457     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       
   458       break;
       
   459     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
   460       break;
       
   461     case GST_STATE_CHANGE_READY_TO_NULL:
       
   462       ogg_sync_clear (&ogg->sync);
       
   463       break;
       
   464     default:
       
   465       break;
       
   466   }
       
   467   return result;
       
   468 }
       
   469 
       
   470 gboolean
       
   471 gst_ogg_avi_parse_plugin_init (GstPlugin * plugin)
       
   472 {
       
   473   GST_DEBUG_CATEGORY_INIT (gst_ogg_avi_parse_debug, "oggaviparse", 0,
       
   474       "ogg avi parser");
       
   475 
       
   476   return gst_element_register (plugin, "oggaviparse", GST_RANK_PRIMARY,
       
   477       GST_TYPE_OGG_AVI_PARSE);
       
   478 }