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