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