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