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