Add -Wmissing-declarations -Wmissing-prototypes to warning flags
[platform/upstream/gstreamer.git] / ext / ogg / gstogmparse.c
1 /* GStreamer OGM parsing
2  * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <string.h>
27
28 #include <gst/gst.h>
29 #include <gst/tag/tag.h>
30 #include <gst/riff/riff-media.h>
31 #include <gst/riff/riff-read.h>
32
33 #include "gstogg.h"
34
35 GST_DEBUG_CATEGORY_STATIC (gst_ogm_parse_debug);
36 #define GST_CAT_DEFAULT gst_ogm_parse_debug
37
38 #define GST_TYPE_OGM_VIDEO_PARSE (gst_ogm_video_parse_get_type())
39 #define GST_IS_OGM_VIDEO_PARSE(obj) \
40   (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_VIDEO_PARSE))
41
42 #define GST_TYPE_OGM_AUDIO_PARSE (gst_ogm_audio_parse_get_type())
43 #define GST_IS_OGM_AUDIO_PARSE(obj) \
44   (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_AUDIO_PARSE))
45
46 #define GST_TYPE_OGM_TEXT_PARSE (gst_ogm_text_parse_get_type())
47 #define GST_IS_OGM_TEXT_PARSE(obj) \
48   (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_TEXT_PARSE))
49
50 #define GST_TYPE_OGM_PARSE (gst_ogm_parse_get_type())
51 #define GST_OGM_PARSE(obj) \
52   (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_OGM_PARSE, GstOgmParse))
53 #define GST_OGM_PARSE_CLASS(klass) \
54   (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_OGM_PARSE, GstOgmParse))
55 #define GST_IS_OGM_PARSE(obj) \
56   (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_PARSE))
57 #define GST_IS_OGM_PARSE_CLASS(klass) \
58   (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_OGM_PARSE))
59 #define GST_OGM_PARSE_GET_CLASS(obj) \
60   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OGM_PARSE, GstOgmParseClass))
61
62 static const GstElementDetails gst_ogm_audio_parse_details =
63 GST_ELEMENT_DETAILS ("OGM audio stream parser",
64     "Codec/Decoder/Audio",
65     "parse an OGM audio header and stream",
66     "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
67
68 static const GstElementDetails gst_ogm_video_parse_details =
69 GST_ELEMENT_DETAILS ("OGM video stream parser",
70     "Codec/Decoder/Video",
71     "parse an OGM video header and stream",
72     "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
73
74 static const GstElementDetails gst_ogm_text_parse_details =
75 GST_ELEMENT_DETAILS ("OGM text stream parser",
76     "Codec/Decoder/Subtitle",
77     "parse an OGM text header and stream",
78     "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
79
80 typedef struct _stream_header_video
81 {
82   gint32 width;
83   gint32 height;
84 } stream_header_video;
85
86 typedef struct _stream_header_audio
87 {
88   gint16 channels;
89   gint16 blockalign;
90   gint32 avgbytespersec;
91 } stream_header_audio;
92
93 /* sizeof(stream_header) might differ due to structure packing and
94  * alignment differences on some architectures, so not using that */
95 #define OGM_STREAM_HEADER_SIZE (8+4+4+8+8+4+4+4+8)
96
97 typedef struct _stream_header
98 {
99   gchar streamtype[8];
100   gchar subtype[4 + 1];
101
102   /* size of the structure */
103   gint32 size;
104
105   /* in reference time */
106   gint64 time_unit;
107
108   gint64 samples_per_unit;
109
110   /* in media time */
111   gint32 default_len;
112
113   gint32 buffersize;
114   gint32 bits_per_sample;
115
116   union
117   {
118     stream_header_video video;
119     stream_header_audio audio;
120     /* text has no additional data */
121   } s;
122 } stream_header;
123
124 typedef struct _GstOgmParse
125 {
126   GstElement element;
127
128   /* pads */
129   GstPad *srcpad, *sinkpad;
130   GstPadTemplate *srcpadtempl;
131
132   /* we need to cache events that we receive before creating the source pad */
133   GList *cached_events;
134
135   /* audio or video */
136   stream_header hdr;
137
138   /* expected next granulepos (used for timestamp guessing) */
139   guint64 next_granulepos;
140 } GstOgmParse;
141
142 typedef struct _GstOgmParseClass
143 {
144   GstElementClass parent_class;
145 } GstOgmParseClass;
146
147 static GstStaticPadTemplate sink_factory_video =
148 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
149     GST_STATIC_CAPS ("application/x-ogm-video"));
150 static GstStaticPadTemplate sink_factory_audio =
151 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
152     GST_STATIC_CAPS ("application/x-ogm-audio"));
153 static GstStaticPadTemplate sink_factory_text =
154 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
155     GST_STATIC_CAPS ("application/x-ogm-text"));
156 static GstPadTemplate *video_src_templ, *audio_src_templ, *text_src_templ;
157
158 static GType gst_ogm_audio_parse_get_type (void);
159 static GType gst_ogm_video_parse_get_type (void);
160 static GType gst_ogm_text_parse_get_type (void);
161 static GType gst_ogm_parse_get_type (void);
162
163 static void gst_ogm_audio_parse_base_init (GstOgmParseClass * klass);
164 static void gst_ogm_video_parse_base_init (GstOgmParseClass * klass);
165 static void gst_ogm_text_parse_base_init (GstOgmParseClass * klass);
166 static void gst_ogm_parse_class_init (GstOgmParseClass * klass);
167 static void gst_ogm_parse_init (GstOgmParse * ogm);
168 static void gst_ogm_video_parse_init (GstOgmParse * ogm);
169 static void gst_ogm_audio_parse_init (GstOgmParse * ogm);
170 static void gst_ogm_text_parse_init (GstOgmParse * ogm);
171
172 static const GstQueryType *gst_ogm_parse_get_sink_querytypes (GstPad * pad);
173 static gboolean gst_ogm_parse_sink_event (GstPad * pad, GstEvent * event);
174 static gboolean gst_ogm_parse_sink_query (GstPad * pad, GstQuery * query);
175 static gboolean gst_ogm_parse_sink_convert (GstPad * pad, GstFormat src_format,
176     gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
177
178 static GstFlowReturn gst_ogm_parse_chain (GstPad * pad, GstBuffer * buffer);
179
180 static GstStateChangeReturn gst_ogm_parse_change_state (GstElement * element,
181     GstStateChange transition);
182
183 static GstElementClass *parent_class = NULL;
184
185 static GType
186 gst_ogm_parse_get_type (void)
187 {
188   static GType ogm_parse_type = 0;
189
190   if (!ogm_parse_type) {
191     static const GTypeInfo ogm_parse_info = {
192       sizeof (GstOgmParseClass),
193       NULL,
194       NULL,
195       (GClassInitFunc) gst_ogm_parse_class_init,
196       NULL,
197       NULL,
198       sizeof (GstOgmParse),
199       0,
200       (GInstanceInitFunc) gst_ogm_parse_init,
201     };
202
203     ogm_parse_type =
204         g_type_register_static (GST_TYPE_ELEMENT,
205         "GstOgmParse", &ogm_parse_info, 0);
206   }
207
208   return ogm_parse_type;
209 }
210
211 static GType
212 gst_ogm_audio_parse_get_type (void)
213 {
214   static GType ogm_audio_parse_type = 0;
215
216   if (!ogm_audio_parse_type) {
217     static const GTypeInfo ogm_audio_parse_info = {
218       sizeof (GstOgmParseClass),
219       (GBaseInitFunc) gst_ogm_audio_parse_base_init,
220       NULL,
221       NULL,
222       NULL,
223       NULL,
224       sizeof (GstOgmParse),
225       0,
226       (GInstanceInitFunc) gst_ogm_audio_parse_init,
227     };
228
229     ogm_audio_parse_type =
230         g_type_register_static (GST_TYPE_OGM_PARSE,
231         "GstOgmAudioParse", &ogm_audio_parse_info, 0);
232   }
233
234   return ogm_audio_parse_type;
235 }
236
237 static GType
238 gst_ogm_video_parse_get_type (void)
239 {
240   static GType ogm_video_parse_type = 0;
241
242   if (!ogm_video_parse_type) {
243     static const GTypeInfo ogm_video_parse_info = {
244       sizeof (GstOgmParseClass),
245       (GBaseInitFunc) gst_ogm_video_parse_base_init,
246       NULL,
247       NULL,
248       NULL,
249       NULL,
250       sizeof (GstOgmParse),
251       0,
252       (GInstanceInitFunc) gst_ogm_video_parse_init,
253     };
254
255     ogm_video_parse_type =
256         g_type_register_static (GST_TYPE_OGM_PARSE,
257         "GstOgmVideoParse", &ogm_video_parse_info, 0);
258   }
259
260   return ogm_video_parse_type;
261 }
262
263 static GType
264 gst_ogm_text_parse_get_type (void)
265 {
266   static GType ogm_text_parse_type = 0;
267
268   if (!ogm_text_parse_type) {
269     static const GTypeInfo ogm_text_parse_info = {
270       sizeof (GstOgmParseClass),
271       (GBaseInitFunc) gst_ogm_text_parse_base_init,
272       NULL,
273       NULL,
274       NULL,
275       NULL,
276       sizeof (GstOgmParse),
277       0,
278       (GInstanceInitFunc) gst_ogm_text_parse_init,
279     };
280
281     ogm_text_parse_type =
282         g_type_register_static (GST_TYPE_OGM_PARSE,
283         "GstOgmTextParse", &ogm_text_parse_info, 0);
284   }
285
286   return ogm_text_parse_type;
287 }
288
289 static void
290 gst_ogm_audio_parse_base_init (GstOgmParseClass * klass)
291 {
292   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
293   GstCaps *caps = gst_riff_create_audio_template_caps ();
294
295   gst_element_class_set_details (element_class, &gst_ogm_audio_parse_details);
296
297   gst_element_class_add_pad_template (element_class,
298       gst_static_pad_template_get (&sink_factory_audio));
299   audio_src_templ = gst_pad_template_new ("src",
300       GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
301   gst_element_class_add_pad_template (element_class, audio_src_templ);
302 }
303
304 static void
305 gst_ogm_video_parse_base_init (GstOgmParseClass * klass)
306 {
307   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
308   GstCaps *caps = gst_riff_create_video_template_caps ();
309
310   gst_element_class_set_details (element_class, &gst_ogm_video_parse_details);
311
312   gst_element_class_add_pad_template (element_class,
313       gst_static_pad_template_get (&sink_factory_video));
314   video_src_templ = gst_pad_template_new ("src",
315       GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
316   gst_element_class_add_pad_template (element_class, video_src_templ);
317 }
318
319 static void
320 gst_ogm_text_parse_base_init (GstOgmParseClass * klass)
321 {
322   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
323   GstCaps *caps = gst_caps_new_simple ("text/plain", NULL, NULL);
324
325   gst_element_class_set_details (element_class, &gst_ogm_text_parse_details);
326
327   gst_element_class_add_pad_template (element_class,
328       gst_static_pad_template_get (&sink_factory_text));
329   text_src_templ = gst_pad_template_new ("src",
330       GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
331   gst_element_class_add_pad_template (element_class, text_src_templ);
332 }
333
334 static void
335 gst_ogm_parse_class_init (GstOgmParseClass * klass)
336 {
337   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
338
339   parent_class = g_type_class_peek_parent (klass);
340
341   gstelement_class->change_state =
342       GST_DEBUG_FUNCPTR (gst_ogm_parse_change_state);
343 }
344
345 static void
346 gst_ogm_parse_init (GstOgmParse * ogm)
347 {
348   memset (&ogm->hdr, 0, sizeof (ogm->hdr));
349   ogm->next_granulepos = 0;
350   ogm->srcpad = NULL;
351   ogm->cached_events = NULL;
352 }
353
354 static void
355 gst_ogm_audio_parse_init (GstOgmParse * ogm)
356 {
357   ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_audio, "sink");
358   gst_pad_set_query_function (ogm->sinkpad,
359       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
360   gst_pad_set_chain_function (ogm->sinkpad,
361       GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
362   gst_pad_set_event_function (ogm->sinkpad,
363       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
364   gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
365
366   ogm->srcpad = NULL;
367   ogm->srcpadtempl = audio_src_templ;
368 }
369
370 static void
371 gst_ogm_video_parse_init (GstOgmParse * ogm)
372 {
373   ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_video, "sink");
374   gst_pad_set_query_function (ogm->sinkpad,
375       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
376   gst_pad_set_chain_function (ogm->sinkpad,
377       GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
378   gst_pad_set_event_function (ogm->sinkpad,
379       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
380   gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
381
382   ogm->srcpad = NULL;
383   ogm->srcpadtempl = video_src_templ;
384 }
385
386 static void
387 gst_ogm_text_parse_init (GstOgmParse * ogm)
388 {
389   ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_text, "sink");
390   gst_pad_set_query_type_function (ogm->sinkpad,
391       gst_ogm_parse_get_sink_querytypes);
392   gst_pad_set_query_function (ogm->sinkpad,
393       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
394   gst_pad_set_chain_function (ogm->sinkpad,
395       GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
396   gst_pad_set_event_function (ogm->sinkpad,
397       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
398   gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
399
400   ogm->srcpad = NULL;
401   ogm->srcpadtempl = text_src_templ;
402 }
403
404 static const GstQueryType *
405 gst_ogm_parse_get_sink_querytypes (GstPad * pad)
406 {
407   static const GstQueryType types[] = {
408     GST_QUERY_POSITION,
409     0
410   };
411
412   return types;
413 }
414
415 static gboolean
416 gst_ogm_parse_sink_convert (GstPad * pad,
417     GstFormat src_format, gint64 src_value,
418     GstFormat * dest_format, gint64 * dest_value)
419 {
420   gboolean res = FALSE;
421   GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad));
422
423   switch (src_format) {
424     case GST_FORMAT_DEFAULT:
425       switch (*dest_format) {
426         case GST_FORMAT_TIME:
427           switch (ogm->hdr.streamtype[0]) {
428             case 'a':
429               *dest_value = GST_SECOND * src_value / ogm->hdr.samples_per_unit;
430               res = TRUE;
431               break;
432             case 'v':
433             case 't':
434               *dest_value = (GST_SECOND / 10000000) *
435                   ogm->hdr.time_unit * src_value;
436               res = TRUE;
437               break;
438             default:
439               break;
440           }
441           break;
442         default:
443           break;
444       }
445       break;
446     case GST_FORMAT_TIME:
447       switch (*dest_format) {
448         case GST_FORMAT_DEFAULT:
449           switch (ogm->hdr.streamtype[0]) {
450             case 'a':
451               *dest_value = ogm->hdr.samples_per_unit * src_value / GST_SECOND;
452               res = TRUE;
453               break;
454             case 'v':
455             case 't':
456               *dest_value = src_value /
457                   ((GST_SECOND / 10000000) * ogm->hdr.time_unit);
458               res = TRUE;
459               break;
460             default:
461               break;
462           }
463           break;
464         default:
465           break;
466       }
467       break;
468     default:
469       break;
470   }
471
472   gst_object_unref (ogm);
473   return res;
474 }
475
476 static gboolean
477 gst_ogm_parse_sink_query (GstPad * pad, GstQuery * query)
478 {
479   GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad));
480   GstFormat format;
481   gboolean res = FALSE;
482
483   switch (GST_QUERY_TYPE (query)) {
484     case GST_QUERY_POSITION:
485     {
486       gint64 val;
487
488       gst_query_parse_position (query, &format, NULL);
489
490       if (format != GST_FORMAT_DEFAULT && format != GST_FORMAT_TIME)
491         break;
492
493       if ((res = gst_ogm_parse_sink_convert (pad,
494                   GST_FORMAT_DEFAULT, ogm->next_granulepos, &format, &val))) {
495         /* don't know the total length here.. */
496         gst_query_set_position (query, format, val);
497       }
498       break;
499     }
500     case GST_QUERY_CONVERT:
501     {
502       GstFormat src_fmt, dest_fmt;
503       gint64 src_val, dest_val;
504
505       /* peel off input */
506       gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
507       if ((res = gst_ogm_parse_sink_convert (pad, src_fmt, src_val,
508                   &dest_fmt, &dest_val))) {
509         gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
510       }
511       break;
512     }
513     default:
514       res = gst_pad_query_default (pad, query);
515       break;
516   }
517
518   gst_object_unref (ogm);
519   return res;
520 }
521
522 static GstFlowReturn
523 gst_ogm_parse_stream_header (GstOgmParse * ogm, const guint8 * data, guint size)
524 {
525   GstCaps *caps = NULL;
526
527   /* stream header */
528   if (size < OGM_STREAM_HEADER_SIZE)
529     goto buffer_too_small;
530
531   if (!memcmp (data, "video\000\000\000", 8)) {
532     ogm->hdr.s.video.width = GST_READ_UINT32_LE (&data[44]);
533     ogm->hdr.s.video.height = GST_READ_UINT32_LE (&data[48]);
534   } else if (!memcmp (data, "audio\000\000\000", 8)) {
535     ogm->hdr.s.audio.channels = GST_READ_UINT32_LE (&data[44]);
536     ogm->hdr.s.audio.blockalign = GST_READ_UINT32_LE (&data[46]);
537     ogm->hdr.s.audio.avgbytespersec = GST_READ_UINT32_LE (&data[48]);
538   } else if (!memcmp (data, "text\000\000\000\000", 8)) {
539     /* nothing here */
540   } else {
541     goto cannot_decode;
542   }
543   memcpy (ogm->hdr.streamtype, &data[0], 8);
544   memcpy (ogm->hdr.subtype, &data[8], 4);
545   ogm->hdr.subtype[4] = '\0';
546   ogm->hdr.size = GST_READ_UINT32_LE (&data[12]);
547   ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[16]);
548   ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[24]);
549   ogm->hdr.default_len = GST_READ_UINT32_LE (&data[32]);
550   ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[36]);
551   ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[40]);
552
553   switch (ogm->hdr.streamtype[0]) {
554     case 'a':{
555       guint codec_id = 0;
556
557       if (sscanf (ogm->hdr.subtype, "%04x", &codec_id) != 1) {
558         GST_WARNING_OBJECT (ogm, "cannot parse subtype %s", ogm->hdr.subtype);
559       }
560
561       caps =
562           gst_riff_create_audio_caps (codec_id, NULL, NULL, NULL, NULL, NULL);
563
564       if (caps == NULL) {
565         GST_WARNING_OBJECT (ogm, "no audio caps for codec %u found", codec_id);
566         caps = gst_caps_new_simple ("audio/x-ogm-unknown", "codec_id",
567             G_TYPE_INT, (gint) codec_id, NULL);
568       }
569
570       gst_caps_set_simple (caps,
571           "channels", G_TYPE_INT, ogm->hdr.s.audio.channels,
572           "rate", G_TYPE_INT, ogm->hdr.samples_per_unit, NULL);
573
574       GST_LOG_OBJECT (ogm, "Type: %s, subtype: 0x%04x, channels: %d, "
575           "samplerate: %d, blockalign: %d, bps: %d, caps = %" GST_PTR_FORMAT,
576           ogm->hdr.streamtype, codec_id, ogm->hdr.s.audio.channels,
577           (gint) ogm->hdr.samples_per_unit, ogm->hdr.s.audio.blockalign,
578           ogm->hdr.s.audio.avgbytespersec, caps);
579       break;
580     }
581     case 'v':{
582       guint32 fourcc;
583       gint time_unit;
584
585       fourcc = GST_MAKE_FOURCC (ogm->hdr.subtype[0],
586           ogm->hdr.subtype[1], ogm->hdr.subtype[2], ogm->hdr.subtype[3]);
587
588       caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
589
590       if (caps == NULL) {
591         GST_WARNING_OBJECT (ogm, "could not find video caps for fourcc %"
592             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
593         caps = gst_caps_new_simple ("video/x-ogm-unknown", "fourcc",
594             GST_TYPE_FOURCC, fourcc, NULL);
595         break;
596       }
597
598       GST_LOG_OBJECT (ogm, "Type: %s, subtype: %" GST_FOURCC_FORMAT
599           ", size: %dx%d, timeunit: %" G_GINT64_FORMAT
600           " (fps: %lf), s/u: %" G_GINT64_FORMAT ", "
601           "def.len: %d, bufsize: %d, bps: %d, caps = %" GST_PTR_FORMAT,
602           ogm->hdr.streamtype, GST_FOURCC_ARGS (fourcc),
603           ogm->hdr.s.video.width, ogm->hdr.s.video.height,
604           ogm->hdr.time_unit, 10000000. / ogm->hdr.time_unit,
605           ogm->hdr.samples_per_unit, ogm->hdr.default_len,
606           ogm->hdr.buffersize, ogm->hdr.bits_per_sample, caps);
607
608       /* GST_TYPE_FRACTION contains gint */
609       if (ogm->hdr.time_unit > G_MAXINT || ogm->hdr.time_unit < G_MININT)
610         GST_WARNING_OBJECT (ogm, "timeunit is out of range");
611
612       time_unit = (gint) CLAMP (ogm->hdr.time_unit, G_MININT, G_MAXINT);
613       gst_caps_set_simple (caps,
614           "width", G_TYPE_INT, ogm->hdr.s.video.width,
615           "height", G_TYPE_INT, ogm->hdr.s.video.height,
616           "framerate", GST_TYPE_FRACTION, 10000000, time_unit, NULL);
617       break;
618     }
619     case 't':{
620       GST_LOG_OBJECT (ogm, "Type: %s, s/u: %" G_GINT64_FORMAT
621           ", timeunit=%" G_GINT64_FORMAT,
622           ogm->hdr.streamtype, ogm->hdr.samples_per_unit, ogm->hdr.time_unit);
623       caps = gst_caps_new_simple ("text/plain", NULL);
624       break;
625     }
626     default:
627       g_assert_not_reached ();
628   }
629
630   if (caps == NULL)
631     goto cannot_decode;
632
633   if (ogm->srcpad) {
634     GstCaps *current_caps = GST_PAD_CAPS (ogm->srcpad);
635
636     if (current_caps && caps && !gst_caps_is_equal (current_caps, caps)) {
637       GST_WARNING_OBJECT (ogm, "Already an existing pad %s:%s",
638           GST_DEBUG_PAD_NAME (ogm->srcpad));
639       gst_pad_set_active (ogm->srcpad, FALSE);
640       gst_element_remove_pad (GST_ELEMENT (ogm), ogm->srcpad);
641       ogm->srcpad = NULL;
642     } else {
643       GST_DEBUG_OBJECT (ogm, "Existing pad has the same caps, do nothing");
644     }
645   }
646
647   if (ogm->srcpad == NULL) {
648     GList *l, *cached_events;
649
650     ogm->srcpad = gst_pad_new_from_template (ogm->srcpadtempl, "src");
651     gst_pad_use_fixed_caps (ogm->srcpad);
652     gst_pad_set_caps (ogm->srcpad, caps);
653     gst_pad_set_active (ogm->srcpad, TRUE);
654     gst_element_add_pad (GST_ELEMENT (ogm), ogm->srcpad);
655     GST_INFO_OBJECT (ogm, "Added pad %s:%s with caps %" GST_PTR_FORMAT,
656         GST_DEBUG_PAD_NAME (ogm->srcpad), caps);
657
658     GST_OBJECT_LOCK (ogm);
659     cached_events = ogm->cached_events;
660     ogm->cached_events = NULL;
661     GST_OBJECT_UNLOCK (ogm);
662
663     for (l = cached_events; l; l = l->next) {
664       GstEvent *event = GST_EVENT_CAST (l->data);
665
666       GST_DEBUG_OBJECT (ogm, "Pushing cached event %" GST_PTR_FORMAT, event);
667       gst_pad_push_event (ogm->srcpad, event);
668     }
669     g_list_free (cached_events);
670
671     {
672       GstTagList *tags;
673
674       tags = gst_tag_list_new ();
675       gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_SUBTITLE_CODEC,
676           "Ogm", NULL);
677       gst_element_found_tags_for_pad (GST_ELEMENT (ogm), ogm->srcpad, tags);
678     }
679   }
680
681   gst_caps_unref (caps);
682
683   return GST_FLOW_OK;
684
685 /* ERRORS */
686 buffer_too_small:
687   {
688     GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE, ("Buffer too small"), (NULL));
689     return GST_FLOW_ERROR;
690   }
691 cannot_decode:
692   {
693     GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("unknown ogm format"));
694     return GST_FLOW_ERROR;
695   }
696 }
697
698 static GstFlowReturn
699 gst_ogm_parse_comment_packet (GstOgmParse * ogm, GstBuffer * buf)
700 {
701   GstFlowReturn ret;
702
703   if (ogm->srcpad == NULL) {
704     GST_DEBUG ("no source pad");
705     return GST_FLOW_WRONG_STATE;
706   }
707
708   /* if this is not a subtitle stream, push the vorbiscomment packet
709    * on downstream, the respective decoder will handle it; if it is
710    * a subtitle stream, we will have to handle the comment ourself */
711   if (ogm->hdr.streamtype[0] == 't') {
712     GstTagList *tags;
713
714     tags = gst_tag_list_from_vorbiscomment_buffer (buf,
715         (guint8 *) "\003vorbis", 7, NULL);
716
717     if (tags) {
718       GST_DEBUG_OBJECT (ogm, "tags = %" GST_PTR_FORMAT, tags);
719       gst_element_found_tags_for_pad (GST_ELEMENT (ogm), ogm->srcpad, tags);
720     } else {
721       GST_DEBUG_OBJECT (ogm, "failed to extract tags from vorbis comment");
722     }
723     /* do not push packet downstream, just let parent unref it */
724     ret = GST_FLOW_OK;
725   } else {
726     buf = gst_buffer_copy (buf);
727     gst_buffer_set_caps (buf, GST_PAD_CAPS (ogm->srcpad));
728     ret = gst_pad_push (ogm->srcpad, buf);
729   }
730
731   return ret;
732 }
733
734 static void
735 gst_ogm_text_parse_strip_trailing_zeroes (GstOgmParse * ogm, GstBuffer * buf)
736 {
737   const guint8 *data;
738   guint size;
739
740   g_assert (gst_buffer_is_metadata_writable (buf));
741
742   /* zeroes are not valid UTF-8 characters, so strip them from output */
743   data = GST_BUFFER_DATA (buf);
744   size = GST_BUFFER_SIZE (buf);
745   while (size > 0 && data[size - 1] == '\0') {
746     --size;
747   }
748
749   GST_BUFFER_SIZE (buf) = size;
750 }
751
752 static GstFlowReturn
753 gst_ogm_parse_data_packet (GstOgmParse * ogm, GstBuffer * buf)
754 {
755   GstFlowReturn ret;
756   const guint8 *data;
757   GstBuffer *sbuf;
758   gboolean keyframe;
759   guint size, len, n, xsize = 0;
760
761   data = GST_BUFFER_DATA (buf);
762   size = GST_BUFFER_SIZE (buf);
763
764   if ((data[0] & 0x01) != 0)
765     goto invalid_startcode;
766
767   /* data - push on */
768   len = ((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1);
769   keyframe = (((data[0] & 0x08) >> 3) != 0);
770
771   if ((1 + len) > size)
772     goto buffer_too_small;
773
774   for (n = len; n > 0; n--) {
775     xsize = (xsize << 8) | data[n];
776   }
777
778   GST_LOG_OBJECT (ogm, "[0x%02x] samples: %d, hdrbytes: %d, datasize: %d",
779       data[0], xsize, len, size - len - 1);
780
781   sbuf = gst_buffer_create_sub (buf, len + 1, size - len - 1);
782
783   if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
784     ogm->next_granulepos = GST_BUFFER_OFFSET_END (buf);
785
786   switch (ogm->hdr.streamtype[0]) {
787     case 't':
788     case 'v':{
789       GstClockTime ts, next_ts;
790       guint samples;
791
792       samples = (ogm->hdr.streamtype[0] == 'v') ? 1 : xsize;
793
794       if (!keyframe) {
795         GST_BUFFER_FLAG_SET (sbuf, GST_BUFFER_FLAG_DELTA_UNIT);
796       }
797
798       /* shouldn't this be granulepos - samples? (tpm) */
799       ts = gst_util_uint64_scale (ogm->next_granulepos,
800           ogm->hdr.time_unit * GST_SECOND, 10000000);
801       next_ts = gst_util_uint64_scale (ogm->next_granulepos + samples,
802           ogm->hdr.time_unit * GST_SECOND, 10000000);
803
804       GST_BUFFER_TIMESTAMP (sbuf) = ts;
805       GST_BUFFER_DURATION (sbuf) = next_ts - ts;
806
807       ogm->next_granulepos += samples;
808
809       if (ogm->hdr.streamtype[0] == 't') {
810         gst_ogm_text_parse_strip_trailing_zeroes (ogm, sbuf);
811       }
812       break;
813     }
814     case 'a':{
815       GstClockTime ts, next_ts;
816
817       /* shouldn't this be granulepos - samples? (tpm) */
818       ts = gst_util_uint64_scale_int (ogm->next_granulepos,
819           GST_SECOND, ogm->hdr.samples_per_unit);
820       next_ts = gst_util_uint64_scale_int (ogm->next_granulepos + xsize,
821           GST_SECOND, ogm->hdr.samples_per_unit);
822
823       GST_BUFFER_TIMESTAMP (sbuf) = ts;
824       GST_BUFFER_DURATION (sbuf) = next_ts - ts;
825
826       ogm->next_granulepos += xsize;
827       break;
828     }
829     default:
830       g_assert_not_reached ();
831       break;
832   }
833
834   if (ogm->srcpad) {
835     gst_buffer_set_caps (sbuf, GST_PAD_CAPS (ogm->srcpad));
836     GST_LOG_OBJECT (ogm, "Pushing buffer with ts=%" GST_TIME_FORMAT,
837         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sbuf)));
838     ret = gst_pad_push (ogm->srcpad, sbuf);
839     if (ret != GST_FLOW_OK) {
840       GST_DEBUG_OBJECT (ogm, "Flow on %s:%s = %s",
841           GST_DEBUG_PAD_NAME (ogm->srcpad), gst_flow_get_name (ret));
842     }
843   } else {
844     ret = GST_FLOW_WRONG_STATE;
845   }
846
847   return ret;
848
849 /* ERRORS */
850 invalid_startcode:
851   {
852     GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL),
853         ("unexpected packet startcode 0x%02x", data[0]));
854     return GST_FLOW_ERROR;
855   }
856 buffer_too_small:
857   {
858     GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL),
859         ("buffer too small, len+1=%u, size=%u", len + 1, size));
860     return GST_FLOW_ERROR;
861   }
862 }
863
864 static GstFlowReturn
865 gst_ogm_parse_chain (GstPad * pad, GstBuffer * buf)
866 {
867   GstFlowReturn ret = GST_FLOW_OK;
868   GstOgmParse *ogm = GST_OGM_PARSE (GST_PAD_PARENT (pad));
869   guint8 *data = GST_BUFFER_DATA (buf);
870   guint size = GST_BUFFER_SIZE (buf);
871
872   if (size < 1)
873     goto buffer_too_small;
874
875   GST_LOG_OBJECT (ogm, "Packet with start code 0x%02x", data[0]);
876
877   switch (data[0]) {
878     case 0x01:{
879       ret = gst_ogm_parse_stream_header (ogm, data + 1, size - 1);
880       break;
881     }
882     case 0x03:{
883       ret = gst_ogm_parse_comment_packet (ogm, buf);
884       break;
885     }
886     default:{
887       ret = gst_ogm_parse_data_packet (ogm, buf);
888       break;
889     }
890   }
891
892   gst_buffer_unref (buf);
893
894   if (ret != GST_FLOW_OK) {
895     GST_DEBUG_OBJECT (ogm, "Flow: %s", gst_flow_get_name (ret));
896   }
897
898   return ret;
899
900 /* ERRORS */
901 buffer_too_small:
902   {
903     GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("buffer too small"));
904     gst_buffer_unref (buf);
905     return GST_FLOW_ERROR;
906   }
907 }
908
909 static gboolean
910 gst_ogm_parse_sink_event (GstPad * pad, GstEvent * event)
911 {
912   GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad));
913   gboolean res;
914
915   GST_LOG_OBJECT (ogm, "processing %s event", GST_EVENT_TYPE_NAME (event));
916
917   GST_OBJECT_LOCK (ogm);
918   if (ogm->srcpad == NULL) {
919     ogm->cached_events = g_list_append (ogm->cached_events, event);
920     GST_OBJECT_UNLOCK (ogm);
921     res = TRUE;
922   } else {
923     GST_OBJECT_UNLOCK (ogm);
924     res = gst_pad_event_default (pad, event);
925   }
926
927   gst_object_unref (ogm);
928   return res;
929 }
930
931 static GstStateChangeReturn
932 gst_ogm_parse_change_state (GstElement * element, GstStateChange transition)
933 {
934   GstStateChangeReturn ret;
935   GstOgmParse *ogm = GST_OGM_PARSE (element);
936
937   ret = parent_class->change_state (element, transition);
938   if (ret != GST_STATE_CHANGE_SUCCESS)
939     return ret;
940
941   switch (transition) {
942     case GST_STATE_CHANGE_PAUSED_TO_READY:
943       if (ogm->srcpad) {
944         gst_pad_set_active (ogm->srcpad, FALSE);
945         gst_element_remove_pad (element, ogm->srcpad);
946         ogm->srcpad = NULL;
947       }
948       memset (&ogm->hdr, 0, sizeof (ogm->hdr));
949       ogm->next_granulepos = 0;
950       g_list_foreach (ogm->cached_events, (GFunc) gst_mini_object_unref, NULL);
951       g_list_free (ogm->cached_events);
952       ogm->cached_events = NULL;
953       break;
954     default:
955       break;
956   }
957
958   return ret;
959 }
960
961 gboolean
962 gst_ogm_parse_plugin_init (GstPlugin * plugin)
963 {
964   gst_riff_init ();
965
966   GST_DEBUG_CATEGORY_INIT (gst_ogm_parse_debug, "ogmparse", 0, "ogm parser");
967
968   return gst_element_register (plugin, "ogmaudioparse", GST_RANK_PRIMARY,
969       GST_TYPE_OGM_AUDIO_PARSE) &&
970       gst_element_register (plugin, "ogmvideoparse", GST_RANK_PRIMARY,
971       GST_TYPE_OGM_VIDEO_PARSE) &&
972       gst_element_register (plugin, "ogmtextparse", GST_RANK_PRIMARY,
973       GST_TYPE_OGM_TEXT_PARSE);
974 }