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>
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.
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.
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.
29 #include <gst/tag/tag.h>
30 #include <gst/riff/riff-media.h>
31 #include <gst/riff/riff-read.h>
35 GST_DEBUG_CATEGORY_STATIC (gst_ogm_parse_debug);
36 #define GST_CAT_DEFAULT gst_ogm_parse_debug
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))
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))
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))
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))
62 typedef struct _stream_header_video
66 } stream_header_video;
68 typedef struct _stream_header_audio
72 gint32 avgbytespersec;
73 } stream_header_audio;
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)
79 typedef struct _stream_header
84 /* size of the structure */
87 /* in reference time */
90 gint64 samples_per_unit;
96 gint32 bits_per_sample;
100 stream_header_video video;
101 stream_header_audio audio;
102 /* text has no additional data */
106 typedef struct _GstOgmParse
111 GstPad *srcpad, *sinkpad;
112 GstPadTemplate *srcpadtempl;
114 /* we need to cache events that we receive before creating the source pad */
115 GList *cached_events;
120 /* expected next granulepos (used for timestamp guessing) */
121 guint64 next_granulepos;
124 typedef struct _GstOgmParseClass
126 GstElementClass parent_class;
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;
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);
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);
154 static gboolean gst_ogm_parse_sink_event (GstPad * pad, GstObject * parent,
156 static gboolean gst_ogm_parse_sink_query (GstPad * pad, GstObject * parent,
158 static gboolean gst_ogm_parse_sink_convert (GstPad * pad, GstFormat src_format,
159 gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
161 static GstFlowReturn gst_ogm_parse_chain (GstPad * pad, GstObject * parent,
164 static GstStateChangeReturn gst_ogm_parse_change_state (GstElement * element,
165 GstStateChange transition);
167 static GstElementClass *parent_class = NULL;
170 gst_ogm_parse_get_type (void)
172 static GType ogm_parse_type = 0;
174 if (!ogm_parse_type) {
175 static const GTypeInfo ogm_parse_info = {
176 sizeof (GstOgmParseClass),
179 (GClassInitFunc) gst_ogm_parse_class_init,
182 sizeof (GstOgmParse),
184 (GInstanceInitFunc) gst_ogm_parse_init,
188 g_type_register_static (GST_TYPE_ELEMENT,
189 "GstOgmParse", &ogm_parse_info, 0);
192 return ogm_parse_type;
196 gst_ogm_audio_parse_get_type (void)
198 static GType ogm_audio_parse_type = 0;
200 if (!ogm_audio_parse_type) {
201 static const GTypeInfo ogm_audio_parse_info = {
202 sizeof (GstOgmParseClass),
203 (GBaseInitFunc) gst_ogm_audio_parse_base_init,
208 sizeof (GstOgmParse),
210 (GInstanceInitFunc) gst_ogm_audio_parse_init,
213 ogm_audio_parse_type =
214 g_type_register_static (GST_TYPE_OGM_PARSE,
215 "GstOgmAudioParse", &ogm_audio_parse_info, 0);
218 return ogm_audio_parse_type;
222 gst_ogm_video_parse_get_type (void)
224 static GType ogm_video_parse_type = 0;
226 if (!ogm_video_parse_type) {
227 static const GTypeInfo ogm_video_parse_info = {
228 sizeof (GstOgmParseClass),
229 (GBaseInitFunc) gst_ogm_video_parse_base_init,
234 sizeof (GstOgmParse),
236 (GInstanceInitFunc) gst_ogm_video_parse_init,
239 ogm_video_parse_type =
240 g_type_register_static (GST_TYPE_OGM_PARSE,
241 "GstOgmVideoParse", &ogm_video_parse_info, 0);
244 return ogm_video_parse_type;
248 gst_ogm_text_parse_get_type (void)
250 static GType ogm_text_parse_type = 0;
252 if (!ogm_text_parse_type) {
253 static const GTypeInfo ogm_text_parse_info = {
254 sizeof (GstOgmParseClass),
255 (GBaseInitFunc) gst_ogm_text_parse_base_init,
260 sizeof (GstOgmParse),
262 (GInstanceInitFunc) gst_ogm_text_parse_init,
265 ogm_text_parse_type =
266 g_type_register_static (GST_TYPE_OGM_PARSE,
267 "GstOgmTextParse", &ogm_text_parse_info, 0);
270 return ogm_text_parse_type;
274 gst_ogm_audio_parse_base_init (GstOgmParseClass * klass)
276 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
277 GstCaps *caps = gst_riff_create_audio_template_caps ();
279 gst_element_class_set_static_metadata (element_class,
280 "OGM audio stream parser", "Codec/Decoder/Audio",
281 "parse an OGM audio header and stream",
282 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
284 gst_element_class_add_pad_template (element_class,
285 gst_static_pad_template_get (&sink_factory_audio));
286 audio_src_templ = gst_pad_template_new ("src",
287 GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
288 gst_element_class_add_pad_template (element_class, audio_src_templ);
289 gst_caps_unref (caps);
293 gst_ogm_video_parse_base_init (GstOgmParseClass * klass)
295 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
296 GstCaps *caps = gst_riff_create_video_template_caps ();
298 gst_element_class_set_static_metadata (element_class,
299 "OGM video stream parser", "Codec/Decoder/Video",
300 "parse an OGM video header and stream",
301 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
303 gst_element_class_add_pad_template (element_class,
304 gst_static_pad_template_get (&sink_factory_video));
305 video_src_templ = gst_pad_template_new ("src",
306 GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
307 gst_element_class_add_pad_template (element_class, video_src_templ);
308 gst_caps_unref (caps);
312 gst_ogm_text_parse_base_init (GstOgmParseClass * klass)
314 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
315 GstCaps *caps = gst_caps_new_simple ("text/plain", NULL, NULL);
317 gst_element_class_set_static_metadata (element_class,
318 "OGM text stream parser", "Codec/Decoder/Subtitle",
319 "parse an OGM text header and stream",
320 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
322 gst_element_class_add_pad_template (element_class,
323 gst_static_pad_template_get (&sink_factory_text));
324 text_src_templ = gst_pad_template_new ("src",
325 GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
326 gst_element_class_add_pad_template (element_class, text_src_templ);
327 gst_caps_unref (caps);
331 gst_ogm_parse_class_init (GstOgmParseClass * klass)
333 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
335 parent_class = g_type_class_peek_parent (klass);
337 gstelement_class->change_state =
338 GST_DEBUG_FUNCPTR (gst_ogm_parse_change_state);
342 gst_ogm_parse_init (GstOgmParse * ogm)
344 memset (&ogm->hdr, 0, sizeof (ogm->hdr));
345 ogm->next_granulepos = 0;
347 ogm->cached_events = NULL;
351 gst_ogm_audio_parse_init (GstOgmParse * ogm)
353 ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_audio, "sink");
354 gst_pad_set_query_function (ogm->sinkpad,
355 GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
356 gst_pad_set_chain_function (ogm->sinkpad,
357 GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
358 gst_pad_set_event_function (ogm->sinkpad,
359 GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
360 gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
363 ogm->srcpadtempl = audio_src_templ;
367 gst_ogm_video_parse_init (GstOgmParse * ogm)
369 ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_video, "sink");
370 gst_pad_set_query_function (ogm->sinkpad,
371 GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
372 gst_pad_set_chain_function (ogm->sinkpad,
373 GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
374 gst_pad_set_event_function (ogm->sinkpad,
375 GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
376 gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
379 ogm->srcpadtempl = video_src_templ;
383 gst_ogm_text_parse_init (GstOgmParse * ogm)
385 ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_text, "sink");
386 gst_pad_set_query_function (ogm->sinkpad,
387 GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
388 gst_pad_set_chain_function (ogm->sinkpad,
389 GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
390 gst_pad_set_event_function (ogm->sinkpad,
391 GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
392 gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
395 ogm->srcpadtempl = text_src_templ;
399 gst_ogm_parse_sink_convert (GstPad * pad,
400 GstFormat src_format, gint64 src_value,
401 GstFormat * dest_format, gint64 * dest_value)
403 gboolean res = FALSE;
404 GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad));
406 switch (src_format) {
407 case GST_FORMAT_DEFAULT:
408 switch (*dest_format) {
409 case GST_FORMAT_TIME:
410 switch (ogm->hdr.streamtype[0]) {
412 *dest_value = GST_SECOND * src_value / ogm->hdr.samples_per_unit;
417 *dest_value = (GST_SECOND / 10000000) *
418 ogm->hdr.time_unit * src_value;
429 case GST_FORMAT_TIME:
430 switch (*dest_format) {
431 case GST_FORMAT_DEFAULT:
432 switch (ogm->hdr.streamtype[0]) {
434 *dest_value = ogm->hdr.samples_per_unit * src_value / GST_SECOND;
439 *dest_value = src_value /
440 ((GST_SECOND / 10000000) * ogm->hdr.time_unit);
455 gst_object_unref (ogm);
460 gst_ogm_parse_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
462 GstOgmParse *ogm = GST_OGM_PARSE (parent);
464 gboolean res = FALSE;
466 switch (GST_QUERY_TYPE (query)) {
467 case GST_QUERY_POSITION:
471 gst_query_parse_position (query, &format, NULL);
473 if (format != GST_FORMAT_DEFAULT && format != GST_FORMAT_TIME)
476 if ((res = gst_ogm_parse_sink_convert (pad,
477 GST_FORMAT_DEFAULT, ogm->next_granulepos, &format, &val))) {
478 /* don't know the total length here.. */
479 gst_query_set_position (query, format, val);
483 case GST_QUERY_CONVERT:
485 GstFormat src_fmt, dest_fmt;
486 gint64 src_val, dest_val;
489 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
490 if ((res = gst_ogm_parse_sink_convert (pad, src_fmt, src_val,
491 &dest_fmt, &dest_val))) {
492 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
497 res = gst_pad_query_default (pad, parent, query);
505 gst_ogm_parse_stream_header (GstOgmParse * ogm, const guint8 * data, guint size)
507 GstCaps *caps = NULL;
510 if (size < OGM_STREAM_HEADER_SIZE)
511 goto buffer_too_small;
513 if (!memcmp (data, "video\000\000\000", 8)) {
514 ogm->hdr.s.video.width = GST_READ_UINT32_LE (&data[44]);
515 ogm->hdr.s.video.height = GST_READ_UINT32_LE (&data[48]);
516 } else if (!memcmp (data, "audio\000\000\000", 8)) {
517 ogm->hdr.s.audio.channels = GST_READ_UINT32_LE (&data[44]);
518 ogm->hdr.s.audio.blockalign = GST_READ_UINT32_LE (&data[46]);
519 ogm->hdr.s.audio.avgbytespersec = GST_READ_UINT32_LE (&data[48]);
520 } else if (!memcmp (data, "text\000\000\000\000", 8)) {
525 memcpy (ogm->hdr.streamtype, &data[0], 8);
526 memcpy (ogm->hdr.subtype, &data[8], 4);
527 ogm->hdr.subtype[4] = '\0';
528 ogm->hdr.size = GST_READ_UINT32_LE (&data[12]);
529 ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[16]);
530 ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[24]);
531 ogm->hdr.default_len = GST_READ_UINT32_LE (&data[32]);
532 ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[36]);
533 ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[40]);
535 switch (ogm->hdr.streamtype[0]) {
539 if (sscanf (ogm->hdr.subtype, "%04x", &codec_id) != 1) {
540 GST_WARNING_OBJECT (ogm, "cannot parse subtype %s", ogm->hdr.subtype);
543 /* FIXME: Need to do something with the reorder map */
545 gst_riff_create_audio_caps (codec_id, NULL, NULL, NULL, NULL, NULL,
549 GST_WARNING_OBJECT (ogm, "no audio caps for codec %u found", codec_id);
550 caps = gst_caps_new_simple ("audio/x-ogm-unknown", "codec_id",
551 G_TYPE_INT, (gint) codec_id, NULL);
554 gst_caps_set_simple (caps,
555 "channels", G_TYPE_INT, ogm->hdr.s.audio.channels,
556 "rate", G_TYPE_INT, ogm->hdr.samples_per_unit, NULL);
558 GST_LOG_OBJECT (ogm, "Type: %s, subtype: 0x%04x, channels: %d, "
559 "samplerate: %d, blockalign: %d, bps: %d, caps = %" GST_PTR_FORMAT,
560 ogm->hdr.streamtype, codec_id, ogm->hdr.s.audio.channels,
561 (gint) ogm->hdr.samples_per_unit, ogm->hdr.s.audio.blockalign,
562 ogm->hdr.s.audio.avgbytespersec, caps);
569 fourcc = GST_MAKE_FOURCC (ogm->hdr.subtype[0],
570 ogm->hdr.subtype[1], ogm->hdr.subtype[2], ogm->hdr.subtype[3]);
572 caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
575 GST_WARNING_OBJECT (ogm, "could not find video caps for fourcc %"
576 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
577 caps = gst_caps_new_simple ("video/x-ogm-unknown", "fourcc",
578 G_TYPE_STRING, ogm->hdr.subtype, NULL);
582 GST_LOG_OBJECT (ogm, "Type: %s, subtype: %" GST_FOURCC_FORMAT
583 ", size: %dx%d, timeunit: %" G_GINT64_FORMAT
584 " (fps: %lf), s/u: %" G_GINT64_FORMAT ", "
585 "def.len: %d, bufsize: %d, bps: %d, caps = %" GST_PTR_FORMAT,
586 ogm->hdr.streamtype, GST_FOURCC_ARGS (fourcc),
587 ogm->hdr.s.video.width, ogm->hdr.s.video.height,
588 ogm->hdr.time_unit, 10000000. / ogm->hdr.time_unit,
589 ogm->hdr.samples_per_unit, ogm->hdr.default_len,
590 ogm->hdr.buffersize, ogm->hdr.bits_per_sample, caps);
592 /* GST_TYPE_FRACTION contains gint */
593 if (ogm->hdr.time_unit > G_MAXINT || ogm->hdr.time_unit < G_MININT)
594 GST_WARNING_OBJECT (ogm, "timeunit is out of range");
596 time_unit = (gint) CLAMP (ogm->hdr.time_unit, G_MININT, G_MAXINT);
597 gst_caps_set_simple (caps,
598 "width", G_TYPE_INT, ogm->hdr.s.video.width,
599 "height", G_TYPE_INT, ogm->hdr.s.video.height,
600 "framerate", GST_TYPE_FRACTION, 10000000, time_unit, NULL);
604 GST_LOG_OBJECT (ogm, "Type: %s, s/u: %" G_GINT64_FORMAT
605 ", timeunit=%" G_GINT64_FORMAT,
606 ogm->hdr.streamtype, ogm->hdr.samples_per_unit, ogm->hdr.time_unit);
607 caps = gst_caps_new_empty_simple ("text/plain");
611 g_assert_not_reached ();
618 GstCaps *current_caps = gst_pad_get_current_caps (ogm->srcpad);
621 if (caps && !gst_caps_is_equal (current_caps, caps)) {
622 GST_WARNING_OBJECT (ogm, "Already an existing pad %s:%s",
623 GST_DEBUG_PAD_NAME (ogm->srcpad));
624 gst_pad_set_active (ogm->srcpad, FALSE);
625 gst_element_remove_pad (GST_ELEMENT (ogm), ogm->srcpad);
628 GST_DEBUG_OBJECT (ogm, "Existing pad has the same caps, do nothing");
630 gst_caps_unref (current_caps);
634 if (ogm->srcpad == NULL) {
635 GList *l, *cached_events;
637 ogm->srcpad = gst_pad_new_from_template (ogm->srcpadtempl, "src");
638 gst_pad_use_fixed_caps (ogm->srcpad);
639 gst_pad_set_active (ogm->srcpad, TRUE);
640 gst_pad_set_caps (ogm->srcpad, caps);
641 gst_element_add_pad (GST_ELEMENT (ogm), ogm->srcpad);
642 GST_INFO_OBJECT (ogm, "Added pad %s:%s with caps %" GST_PTR_FORMAT,
643 GST_DEBUG_PAD_NAME (ogm->srcpad), caps);
645 GST_OBJECT_LOCK (ogm);
646 cached_events = ogm->cached_events;
647 ogm->cached_events = NULL;
648 GST_OBJECT_UNLOCK (ogm);
650 for (l = cached_events; l; l = l->next) {
651 GstEvent *event = GST_EVENT_CAST (l->data);
653 GST_DEBUG_OBJECT (ogm, "Pushing cached event %" GST_PTR_FORMAT, event);
654 gst_pad_push_event (ogm->srcpad, event);
656 g_list_free (cached_events);
661 tags = gst_tag_list_new (GST_TAG_SUBTITLE_CODEC, "Ogm", NULL);
662 gst_pad_push_event (ogm->srcpad, gst_event_new_tag (tags));
666 gst_caps_unref (caps);
673 GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE, ("Buffer too small"), (NULL));
674 return GST_FLOW_ERROR;
678 GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("unknown ogm format"));
679 return GST_FLOW_ERROR;
684 gst_ogm_parse_comment_packet (GstOgmParse * ogm, GstBuffer * buf)
688 if (ogm->srcpad == NULL) {
689 GST_DEBUG ("no source pad");
690 return GST_FLOW_FLUSHING;
693 /* if this is not a subtitle stream, push the vorbiscomment packet
694 * on downstream, the respective decoder will handle it; if it is
695 * a subtitle stream, we will have to handle the comment ourself */
696 if (ogm->hdr.streamtype[0] == 't') {
699 tags = gst_tag_list_from_vorbiscomment_buffer (buf,
700 (guint8 *) "\003vorbis", 7, NULL);
703 GST_DEBUG_OBJECT (ogm, "tags = %" GST_PTR_FORMAT, tags);
704 gst_pad_push_event (ogm->srcpad, gst_event_new_tag (tags));
706 GST_DEBUG_OBJECT (ogm, "failed to extract tags from vorbis comment");
708 /* do not push packet downstream, just let parent unref it */
711 ret = gst_pad_push (ogm->srcpad, buf);
718 gst_ogm_text_parse_strip_trailing_zeroes (GstOgmParse * ogm, GstBuffer * buf)
723 g_assert (gst_buffer_is_writable (buf));
725 /* zeroes are not valid UTF-8 characters, so strip them from output */
726 gst_buffer_map (buf, &map, GST_MAP_WRITE);
728 while (size > 0 && map.data[size - 1] == '\0') {
731 gst_buffer_unmap (buf, &map);
735 gst_ogm_parse_data_packet (GstOgmParse * ogm, GstBuffer * buf,
736 const guint8 * data, gsize size)
741 guint len, n, xsize = 0;
743 if ((data[0] & 0x01) != 0)
744 goto invalid_startcode;
747 len = ((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1);
748 keyframe = (((data[0] & 0x08) >> 3) != 0);
750 if ((1 + len) > size)
751 goto buffer_too_small;
753 for (n = len; n > 0; n--) {
754 xsize = (xsize << 8) | data[n];
757 GST_LOG_OBJECT (ogm, "[0x%02x] samples: %d, hdrbytes: %d, datasize: %"
758 G_GSIZE_FORMAT, data[0], xsize, len, size - len - 1);
761 gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, len + 1,
764 if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
765 ogm->next_granulepos = GST_BUFFER_OFFSET_END (buf);
767 switch (ogm->hdr.streamtype[0]) {
770 GstClockTime ts, next_ts;
773 samples = (ogm->hdr.streamtype[0] == 'v') ? 1 : xsize;
776 GST_BUFFER_FLAG_SET (sbuf, GST_BUFFER_FLAG_DELTA_UNIT);
779 /* shouldn't this be granulepos - samples? (tpm) */
780 ts = gst_util_uint64_scale (ogm->next_granulepos,
781 ogm->hdr.time_unit * GST_SECOND, 10000000);
782 next_ts = gst_util_uint64_scale (ogm->next_granulepos + samples,
783 ogm->hdr.time_unit * GST_SECOND, 10000000);
785 GST_BUFFER_TIMESTAMP (sbuf) = ts;
786 GST_BUFFER_DURATION (sbuf) = next_ts - ts;
788 ogm->next_granulepos += samples;
790 if (ogm->hdr.streamtype[0] == 't') {
791 gst_ogm_text_parse_strip_trailing_zeroes (ogm, sbuf);
796 GstClockTime ts, next_ts;
798 /* shouldn't this be granulepos - samples? (tpm) */
799 ts = gst_util_uint64_scale_int (ogm->next_granulepos,
800 GST_SECOND, ogm->hdr.samples_per_unit);
801 next_ts = gst_util_uint64_scale_int (ogm->next_granulepos + xsize,
802 GST_SECOND, ogm->hdr.samples_per_unit);
804 GST_BUFFER_TIMESTAMP (sbuf) = ts;
805 GST_BUFFER_DURATION (sbuf) = next_ts - ts;
807 ogm->next_granulepos += xsize;
811 g_assert_not_reached ();
816 GST_LOG_OBJECT (ogm, "Pushing buffer with ts=%" GST_TIME_FORMAT,
817 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sbuf)));
818 ret = gst_pad_push (ogm->srcpad, sbuf);
819 if (ret != GST_FLOW_OK) {
820 GST_DEBUG_OBJECT (ogm, "Flow on %s:%s = %s",
821 GST_DEBUG_PAD_NAME (ogm->srcpad), gst_flow_get_name (ret));
824 ret = GST_FLOW_FLUSHING;
832 GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL),
833 ("unexpected packet startcode 0x%02x", data[0]));
834 return GST_FLOW_ERROR;
838 GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL),
839 ("buffer too small, len+1=%u, size=%" G_GSIZE_FORMAT, len + 1, size));
840 return GST_FLOW_ERROR;
845 gst_ogm_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
847 GstFlowReturn ret = GST_FLOW_OK;
848 GstOgmParse *ogm = GST_OGM_PARSE (parent);
851 gst_buffer_map (buf, &map, GST_MAP_READ);
853 goto buffer_too_small;
855 GST_LOG_OBJECT (ogm, "Packet with start code 0x%02x", map.data[0]);
857 switch (map.data[0]) {
859 ret = gst_ogm_parse_stream_header (ogm, map.data + 1, map.size - 1);
863 ret = gst_ogm_parse_comment_packet (ogm, buf);
867 ret = gst_ogm_parse_data_packet (ogm, buf, map.data, map.size);
872 gst_buffer_unmap (buf, &map);
873 gst_buffer_unref (buf);
875 if (ret != GST_FLOW_OK) {
876 GST_DEBUG_OBJECT (ogm, "Flow: %s", gst_flow_get_name (ret));
884 GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("buffer too small"));
885 gst_buffer_unmap (buf, &map);
886 gst_buffer_unref (buf);
887 return GST_FLOW_ERROR;
892 gst_ogm_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
894 GstOgmParse *ogm = GST_OGM_PARSE (parent);
897 GST_LOG_OBJECT (ogm, "processing %s event", GST_EVENT_TYPE_NAME (event));
899 GST_OBJECT_LOCK (ogm);
900 if (ogm->srcpad == NULL) {
901 ogm->cached_events = g_list_append (ogm->cached_events, event);
902 GST_OBJECT_UNLOCK (ogm);
905 GST_OBJECT_UNLOCK (ogm);
906 res = gst_pad_event_default (pad, parent, event);
912 static GstStateChangeReturn
913 gst_ogm_parse_change_state (GstElement * element, GstStateChange transition)
915 GstStateChangeReturn ret;
916 GstOgmParse *ogm = GST_OGM_PARSE (element);
918 ret = parent_class->change_state (element, transition);
919 if (ret != GST_STATE_CHANGE_SUCCESS)
922 switch (transition) {
923 case GST_STATE_CHANGE_PAUSED_TO_READY:
925 gst_pad_set_active (ogm->srcpad, FALSE);
926 gst_element_remove_pad (element, ogm->srcpad);
929 memset (&ogm->hdr, 0, sizeof (ogm->hdr));
930 ogm->next_granulepos = 0;
931 g_list_foreach (ogm->cached_events, (GFunc) gst_mini_object_unref, NULL);
932 g_list_free (ogm->cached_events);
933 ogm->cached_events = NULL;
943 gst_ogm_parse_plugin_init (GstPlugin * plugin)
947 GST_DEBUG_CATEGORY_INIT (gst_ogm_parse_debug, "ogmparse", 0, "ogm parser");
949 return gst_element_register (plugin, "ogmaudioparse", GST_RANK_PRIMARY,
950 GST_TYPE_OGM_AUDIO_PARSE) &&
951 gst_element_register (plugin, "ogmvideoparse", GST_RANK_PRIMARY,
952 GST_TYPE_OGM_VIDEO_PARSE) &&
953 gst_element_register (plugin, "ogmtextparse", GST_RANK_PRIMARY,
954 GST_TYPE_OGM_TEXT_PARSE);