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