1 /* GStreamer Ogg Granulepos Mapping Utility Functions
2 * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
3 * Copyright (C) 2009 David Schleef <ds@schleef.org>
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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "gstoggstream.h"
26 #include "dirac_parse.h"
27 #include "vorbis_parse.h"
29 #include <gst/riff/riff-media.h>
30 #include <gst/pbutils/pbutils.h>
35 GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug);
36 GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_setup_debug);
37 #define GST_CAT_DEFAULT gst_ogg_demux_debug
39 typedef struct _GstOggMap GstOggMap;
41 typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad,
43 typedef gboolean (*GstOggMapSetupFromCapsFunc) (GstOggStream * pad,
44 const GstCaps * caps);
45 typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad,
47 typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad,
49 typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad,
50 gint64 granule, gint64 keyframe_granule);
52 /* returns TRUE if the granulepos denotes a key frame */
53 typedef gboolean (*GstOggMapIsGranuleposKeyFrameFunc) (GstOggStream * pad,
56 /* returns TRUE if the packet is a key frame */
57 typedef gboolean (*GstOggMapIsPacketKeyFrameFunc) (GstOggStream * pad,
60 /* returns TRUE if the given packet is a stream header packet */
61 typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
63 typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
65 typedef void (*GstOggMapExtractTagsFunc) (GstOggStream * pad,
68 typedef gint64 (*GstOggMapGranuleposToKeyGranuleFunc) (GstOggStream * pad,
71 typedef GstBuffer *(*GstOggMapGetHeadersFunc) (GstOggStream * pad);
72 typedef void (*GstOggMapUpdateStatsFunc) (GstOggStream * pad,
75 #define SKELETON_FISBONE_MIN_SIZE 52
76 #define SKELETON_FISHEAD_3_3_MIN_SIZE 112
77 #define SKELETON_FISHEAD_4_0_MIN_SIZE 80
84 const gchar *media_type;
85 GstOggMapSetupFunc setup_func;
86 GstOggMapSetupFromCapsFunc setup_from_caps_func;
87 GstOggMapToGranuleFunc granulepos_to_granule_func;
88 GstOggMapToGranuleposFunc granule_to_granulepos_func;
89 GstOggMapIsGranuleposKeyFrameFunc is_granulepos_key_frame_func;
90 GstOggMapIsPacketKeyFrameFunc is_packet_key_frame_func;
91 GstOggMapIsHeaderPacketFunc is_header_func;
92 GstOggMapPacketDurationFunc packet_duration_func;
93 GstOggMapGranuleposToKeyGranuleFunc granulepos_to_key_granule_func;
94 GstOggMapExtractTagsFunc extract_tags_func;
95 GstOggMapGetHeadersFunc get_headers_func;
96 GstOggMapUpdateStatsFunc update_stats_func;
99 extern const GstOggMap mappers[];
102 gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet)
106 if (packet->granulepos == -1) {
107 return GST_CLOCK_TIME_NONE;
110 duration = gst_ogg_stream_get_packet_duration (pad, packet);
111 if (duration == -1) {
112 return GST_CLOCK_TIME_NONE;
115 return gst_ogg_stream_granule_to_time (pad,
116 gst_ogg_stream_granulepos_to_granule (pad,
117 packet->granulepos) - duration);
121 gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad,
124 if (pad->frame_size == 0)
125 return GST_CLOCK_TIME_NONE;
127 return gst_ogg_stream_granule_to_time (pad,
128 gst_ogg_stream_granulepos_to_granule (pad, granulepos));
132 gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad,
135 return gst_ogg_stream_granule_to_time (pad,
136 gst_ogg_stream_granulepos_to_granule (pad, granulepos));
140 gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule)
142 if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
145 granule += pad->granule_offset;
149 return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
154 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
156 if (granulepos == -1 || granulepos == 0) {
160 if (mappers[pad->map].granulepos_to_granule_func == NULL) {
161 GST_WARNING ("Failed to convert %s granulepos to granule",
162 gst_ogg_stream_get_media_type (pad));
166 return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
170 gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos)
172 if (mappers[pad->map].granulepos_to_key_granule_func)
173 return mappers[pad->map].granulepos_to_key_granule_func (pad, granulepos);
175 if (granulepos == -1 || granulepos == 0 || pad->granuleshift == G_MAXUINT32) {
179 return granulepos >> pad->granuleshift;
183 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
184 gint64 keyframe_granule)
186 if (granule == -1 || granule == 0) {
190 if (mappers[pad->map].granule_to_granulepos_func == NULL) {
191 GST_WARNING ("Failed to convert %s granule to granulepos",
192 gst_ogg_stream_get_media_type (pad));
196 return mappers[pad->map].granule_to_granulepos_func (pad, granule,
201 gst_ogg_stream_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos)
203 if (granulepos == -1) {
207 if (mappers[pad->map].is_granulepos_key_frame_func == NULL) {
208 GST_WARNING ("Failed to determine keyframeness for %s granulepos",
209 gst_ogg_stream_get_media_type (pad));
213 return mappers[pad->map].is_granulepos_key_frame_func (pad, granulepos);
217 gst_ogg_stream_packet_is_key_frame (GstOggStream * pad, ogg_packet * packet)
219 if (mappers[pad->map].is_packet_key_frame_func == NULL) {
220 GST_WARNING ("Failed to determine keyframeness of %s packet",
221 gst_ogg_stream_get_media_type (pad));
225 return mappers[pad->map].is_packet_key_frame_func (pad, packet);
229 gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
231 if (mappers[pad->map].is_header_func == NULL) {
232 GST_WARNING ("Failed to determine headerness of %s packet",
233 gst_ogg_stream_get_media_type (pad));
237 return mappers[pad->map].is_header_func (pad, packet);
241 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
243 if (mappers[pad->map].packet_duration_func == NULL) {
244 GST_WARNING ("Failed to determine %s packet duration",
245 gst_ogg_stream_get_media_type (pad));
249 return mappers[pad->map].packet_duration_func (pad, packet);
254 gst_ogg_stream_extract_tags (GstOggStream * pad, ogg_packet * packet)
256 if (mappers[pad->map].extract_tags_func == NULL) {
257 GST_DEBUG ("No tag extraction");
261 mappers[pad->map].extract_tags_func (pad, packet);
265 gst_ogg_stream_get_media_type (GstOggStream * pad)
267 const GstCaps *caps = pad->caps;
268 const GstStructure *structure;
271 structure = gst_caps_get_structure (caps, 0);
274 return gst_structure_get_name (structure);
278 gst_ogg_stream_get_headers (GstOggStream * pad)
280 if (!mappers[pad->map].get_headers_func)
283 return mappers[pad->map].get_headers_func (pad);
287 gst_ogg_stream_update_stats (GstOggStream * pad, ogg_packet * packet)
289 if (!mappers[pad->map].get_headers_func)
292 mappers[pad->map].update_stats_func (pad, packet);
295 /* some generic functions */
298 is_granulepos_keyframe_true (GstOggStream * pad, gint64 granulepos)
304 is_packet_keyframe_true (GstOggStream * pad, ogg_packet * packet)
310 granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos)
312 gint64 keyindex, keyoffset;
314 if (pad->granuleshift != 0 && pad->granuleshift != G_MAXUINT32) {
315 keyindex = granulepos >> pad->granuleshift;
316 keyoffset = granulepos - (keyindex << pad->granuleshift);
317 return keyindex + keyoffset;
325 granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
326 gint64 keyframe_granule)
330 if (pad->granuleshift != 0 && pad->granuleshift != G_MAXUINT32) {
331 /* If we don't know where the previous keyframe is yet, assume it is
332 at 0 or 1, depending on bitstream version. If nothing else, this
333 avoids getting negative granpos back. */
334 if (keyframe_granule < 0)
335 keyframe_granule = pad->theora_has_zero_keyoffset ? 0 : 1;
336 keyoffset = granule - keyframe_granule;
337 return (keyframe_granule << pad->granuleshift) | keyoffset;
345 is_header_unknown (GstOggStream * pad, ogg_packet * packet)
347 GST_WARNING ("don't know how to detect header");
353 is_header_true (GstOggStream * pad, ogg_packet * packet)
359 is_header_count (GstOggStream * pad, ogg_packet * packet)
361 if (pad->n_header_packets_seen < pad->n_header_packets) {
368 packet_duration_constant (GstOggStream * pad, ogg_packet * packet)
370 return pad->frame_size;
373 /* helper: extracts tags from vorbis comment ogg packet.
374 * Returns result in *tags after free'ing existing *tags (if any) */
376 tag_list_from_vorbiscomment_packet (ogg_packet * packet,
377 const guint8 * id_data, const guint id_data_length, GstTagList ** tags)
379 gchar *encoder = NULL;
383 g_return_val_if_fail (tags != NULL, FALSE);
385 list = gst_tag_list_from_vorbiscomment (packet->packet, packet->bytes,
386 id_data, id_data_length, &encoder);
389 GST_WARNING ("failed to decode vorbis comments");
395 if (encoder[0] && g_utf8_validate (encoder, -1, NULL))
396 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
403 gst_tag_list_unref (*tags);
412 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
414 guint8 *data = packet->packet;
415 guint w, h, par_d, par_n;
416 guint8 vmaj, vmin, vrev;
422 w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
423 h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
425 pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
426 pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
427 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
428 GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
429 pad->granulerate_n = 0;
430 pad->granulerate_d = 0;
434 par_n = GST_READ_UINT24_BE (data + 30);
435 par_d = GST_READ_UINT24_BE (data + 33);
437 GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
438 pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
440 /* 2 bits + 3 bits = 5 bits KFGSHIFT */
441 pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
442 (GST_READ_UINT8 (data + 41) >> 5);
443 if (pad->granuleshift >= 63) {
444 /* Granuleshift can't be greater than the storage size of a granule */
445 GST_WARNING ("Invalid granuleshift (%u >= 63)", pad->granuleshift);
446 pad->granulerate_n = 0;
447 pad->granulerate_d = 0;
448 pad->granuleshift = -1;
451 GST_LOG ("granshift: %d", pad->granuleshift);
453 pad->is_video = TRUE;
454 pad->n_header_packets = 3;
457 pad->bitrate = GST_READ_UINT24_BE (data + 37);
458 GST_LOG ("bit rate: %d", pad->bitrate);
460 /* The interpretation of the granule position has changed with 3.2.1.
461 The granule is now made from the number of frames encoded, rather than
462 the index of the frame being encoded - so there is a difference of 1. */
463 pad->theora_has_zero_keyoffset =
464 ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201;
466 pad->caps = gst_caps_new_empty_simple ("video/x-theora");
468 if (w > 0 && h > 0) {
469 gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
470 G_TYPE_INT, h, NULL);
473 /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
474 if (par_n == 0 || par_d == 0)
477 /* only add framerate now so caps look prettier, with width/height first */
478 gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
479 pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
480 GST_TYPE_FRACTION, par_n, par_d, NULL);
486 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
488 gint64 keyindex, keyoffset;
490 if (pad->granuleshift != 0 && pad->granuleshift != G_MAXUINT32) {
491 keyindex = granulepos >> pad->granuleshift;
492 keyoffset = granulepos - (keyindex << pad->granuleshift);
493 if (pad->theora_has_zero_keyoffset) {
496 return keyindex + keyoffset;
503 is_granulepos_keyframe_theora (GstOggStream * pad, gint64 granulepos)
507 if (granulepos == (gint64) - 1 || pad->granuleshift == G_MAXUINT32)
510 frame_mask = (G_GUINT64_CONSTANT (1) << pad->granuleshift) - 1;
512 return ((granulepos & frame_mask) == 0);
516 is_packet_keyframe_theora (GstOggStream * pad, ogg_packet * packet)
518 if (packet->bytes == 0)
520 return (packet->packet[0] & 0xc0) == 0x00;
524 is_header_theora (GstOggStream * pad, ogg_packet * packet)
526 return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
530 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
532 if (packet->bytes > 0 && packet->packet[0] == 0x81) {
533 tag_list_from_vorbiscomment_packet (packet,
534 (const guint8 *) "\201theora", 7, &pad->taglist);
537 pad->taglist = gst_tag_list_new_empty ();
539 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
540 GST_TAG_VIDEO_CODEC, "Theora", NULL);
543 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
544 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
551 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
554 DiracSequenceHeader header;
556 ret = gst_dirac_sequence_header_parse (&header, packet->packet + 13,
559 GST_DEBUG ("Failed to parse Dirac sequence header");
563 if (header.interlaced_coding != 0) {
564 GST_DEBUG ("non-progressive Dirac coding not implemented");
568 pad->is_video = TRUE;
569 pad->always_flush_page = TRUE;
570 pad->granulerate_n = header.frame_rate_numerator * 2;
571 pad->granulerate_d = header.frame_rate_denominator;
572 pad->granuleshift = 22;
573 pad->n_header_packets = 1;
576 pad->caps = gst_caps_new_simple ("video/x-dirac",
577 "width", G_TYPE_INT, header.width,
578 "height", G_TYPE_INT, header.height,
579 "interlace-mode", G_TYPE_STRING,
580 (header.interlaced ? "mixed" : "progressive"), "pixel-aspect-ratio",
581 GST_TYPE_FRACTION, header.aspect_ratio_numerator,
582 header.aspect_ratio_denominator, "framerate", GST_TYPE_FRACTION,
583 header.frame_rate_numerator, header.frame_rate_denominator, NULL);
588 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
590 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
596 if (granulepos == -1)
599 dist_h = (granulepos >> 22) & 0xff;
600 dist_l = granulepos & 0xff;
601 dist = (dist_h << 8) | dist_l;
607 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
613 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
614 delay = (gp >> 9) & 0x1fff;
617 GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
623 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
624 gint64 keyframe_granule)
626 /* This conversion requires knowing more details about the Dirac
632 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
641 if (gp == -1 || gp == 0)
644 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
645 dist_h = (gp >> 22) & 0xff;
647 dist = (dist_h << 8) | dist_l;
648 delay = (gp >> 9) & 0x1fff;
651 return dt - 2 * dist + 4;
657 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
659 gint width, height, par_n, par_d, fps_n, fps_d;
661 if (packet->bytes < 26) {
662 GST_DEBUG ("Failed to parse VP8 BOS page");
666 width = GST_READ_UINT16_BE (packet->packet + 8);
667 height = GST_READ_UINT16_BE (packet->packet + 10);
668 par_n = GST_READ_UINT24_BE (packet->packet + 12);
669 par_d = GST_READ_UINT24_BE (packet->packet + 15);
670 fps_n = GST_READ_UINT32_BE (packet->packet + 18);
671 fps_d = GST_READ_UINT32_BE (packet->packet + 22);
673 pad->is_video = TRUE;
675 pad->granulerate_n = fps_n;
676 pad->granulerate_d = fps_d;
677 pad->n_header_packets = 2;
680 /* PAR of 0:N, N:0 and 0:0 is not explicitely allowed, but the best we can do
681 * here is to map to 1:1 so that caps negotiation doesn't break downstream. */
682 if (par_n == 0 || par_d == 0)
685 pad->caps = gst_caps_new_simple ("video/x-vp8",
686 "width", G_TYPE_INT, width,
687 "height", G_TYPE_INT, height,
688 "pixel-aspect-ratio", GST_TYPE_FRACTION,
689 par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
695 vp8_fill_header (GstOggStream * pad, const GstCaps * caps, guint8 * data)
697 gint width, height, par_n, par_d, fps_n, fps_d;
698 GstStructure *structure = gst_caps_get_structure (caps, 0);
700 if (!(gst_structure_get_int (structure, "width", &width) &&
701 gst_structure_get_int (structure, "height", &height) &&
702 gst_structure_get_fraction (structure, "framerate", &fps_n,
704 GST_DEBUG ("Failed to get width, height or framerate from caps %"
705 GST_PTR_FORMAT, caps);
708 if (!gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_n,
713 memcpy (data, "OVP80\1\1", 8);
714 GST_WRITE_UINT16_BE (data + 8, width);
715 GST_WRITE_UINT16_BE (data + 10, height);
716 GST_WRITE_UINT24_BE (data + 12, par_n);
717 GST_WRITE_UINT24_BE (data + 15, par_d);
718 GST_WRITE_UINT32_BE (data + 18, fps_n);
719 GST_WRITE_UINT32_BE (data + 22, fps_d);
725 setup_vp8_mapper_from_caps (GstOggStream * pad, const GstCaps * caps)
730 if (!vp8_fill_header (pad, caps, data))
733 packet.packet = data;
735 return setup_vp8_mapper (pad, &packet);
739 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
741 guint64 gpos = granulepos;
743 if (granulepos == -1)
746 /* Get rid of flags */
749 return ((gpos & 0x07ffffff) == 0);
753 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
755 guint64 gp = (guint64) gpos;
760 dist = (gp >> 3) & 0x07ffffff;
762 GST_DEBUG ("pt %u, dist %u", pt, dist);
768 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
769 gint64 keyframe_granule)
774 inv = (pad->invisible_count <= 0) ? 0x3 : pad->invisible_count - 1;
777 (granule << 32) | (inv << 30) | ((granule - keyframe_granule) << 3);
781 /* Check if this packet contains an invisible frame or not */
783 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
787 if (packet->bytes < 3)
790 hdr = GST_READ_UINT24_LE (packet->packet);
792 return (((hdr >> 4) & 1) != 0) ? 1 : 0;
796 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
798 guint64 gp = granulepos;
799 guint64 pts = (gp >> 32);
800 guint32 dist = (gp >> 3) & 0x07ffffff;
802 if (granulepos == -1 || granulepos == 0)
812 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
814 if (packet->bytes >= 5 && packet->packet[0] == 0x4F &&
815 packet->packet[1] == 0x56 && packet->packet[2] == 0x50 &&
816 packet->packet[3] == 0x38 && packet->packet[4] == 0x30)
822 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
824 if (packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) {
825 tag_list_from_vorbiscomment_packet (packet,
826 (const guint8 *) "OVP80\2 ", 7, &pad->taglist);
829 pad->taglist = gst_tag_list_new_empty ();
831 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
832 GST_TAG_VIDEO_CODEC, "VP8", NULL);
837 get_headers_vp8 (GstOggStream * pad)
839 guint8 *data = g_malloc (26);
841 if (vp8_fill_header (pad, pad->caps, data)) {
842 return gst_buffer_new_wrapped (data, 26);
849 update_stats_vp8 (GstOggStream * pad, ogg_packet * packet)
851 if (packet_duration_vp8 (pad, packet)) {
852 /* set to -1 as when we get thefirst invisible it should be
854 pad->invisible_count = -1;
856 pad->invisible_count++;
863 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
865 guint8 *data = packet->packet;
869 pad->version = GST_READ_UINT32_LE (data);
871 chans = GST_READ_UINT8 (data);
874 pad->granulerate_n = GST_READ_UINT32_LE (data);
875 pad->granulerate_d = 1;
876 if (pad->granulerate_n == 0) {
877 pad->granulerate_n = 0;
878 pad->granulerate_d = 0;
882 pad->granuleshift = 0;
885 GST_LOG ("sample rate: %d", pad->granulerate_n);
888 pad->bitrate_upper = GST_READ_UINT32_LE (data);
890 pad->bitrate_nominal = GST_READ_UINT32_LE (data);
892 pad->bitrate_lower = GST_READ_UINT32_LE (data);
894 if (pad->bitrate_nominal > 0)
895 pad->bitrate = pad->bitrate_nominal;
897 if (pad->bitrate_upper > 0 && !pad->bitrate)
898 pad->bitrate = pad->bitrate_upper;
900 if (pad->bitrate_lower > 0 && !pad->bitrate)
901 pad->bitrate = pad->bitrate_lower;
903 GST_LOG ("bit rate: %d", pad->bitrate);
905 pad->n_header_packets = 3;
907 gst_parse_vorbis_header_packet (pad, packet);
909 pad->caps = gst_caps_new_simple ("audio/x-vorbis",
910 "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
917 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
919 if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
922 if (packet->packet[0] == 5) {
923 gst_parse_vorbis_setup_packet (pad, packet);
930 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
932 if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
935 if (((guint8 *) (packet->packet))[0] == 0x03) {
936 tag_list_from_vorbiscomment_packet (packet,
937 (const guint8 *) "\003vorbis", 7, &pad->taglist);
940 pad->taglist = gst_tag_list_new_empty ();
942 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
943 GST_TAG_ENCODER_VERSION, pad->version,
944 GST_TAG_AUDIO_CODEC, "Vorbis", NULL);
946 if (pad->bitrate_nominal > 0)
947 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
948 GST_TAG_NOMINAL_BITRATE, (guint) pad->bitrate_nominal, NULL);
950 if (pad->bitrate_upper > 0)
951 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
952 GST_TAG_MAXIMUM_BITRATE, (guint) pad->bitrate_upper, NULL);
954 if (pad->bitrate_lower > 0)
955 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
956 GST_TAG_MINIMUM_BITRATE, (guint) pad->bitrate_lower, NULL);
959 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
960 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
965 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
971 if (packet->bytes == 0 || packet->packet[0] & 1)
974 mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
975 size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
977 if (pad->last_size == 0) {
980 duration = pad->last_size / 4 + size / 4;
982 pad->last_size = size;
984 GST_DEBUG ("duration %d", (int) duration);
993 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
995 guint8 *data = packet->packet;
998 data += 8 + 20 + 4 + 4;
1000 pad->granulerate_n = GST_READ_UINT32_LE (data);
1001 pad->granulerate_d = 1;
1002 if (pad->granulerate_n == 0) {
1003 pad->granulerate_n = 0;
1004 pad->granulerate_d = 0;
1008 pad->granuleshift = 0;
1011 chans = GST_READ_UINT32_LE (data);
1013 pad->bitrate = GST_READ_UINT32_LE (data);
1015 GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
1016 GST_LOG ("bit rate: %d", pad->bitrate);
1018 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
1019 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
1020 GST_READ_UINT32_LE (packet->packet + 56);
1022 pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
1023 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
1029 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
1031 /* packet 2 must be comment packet */
1032 if (packet->bytes > 0 && pad->n_header_packets_seen == 1) {
1033 tag_list_from_vorbiscomment_packet (packet, NULL, 0, &pad->taglist);
1036 pad->taglist = gst_tag_list_new_empty ();
1038 if (pad->is_video) {
1039 gst_pb_utils_add_codec_description_to_tag_list (pad->taglist,
1040 GST_TAG_VIDEO_CODEC, pad->caps);
1041 } else if (!pad->is_sparse && !pad->is_ogm_text && !pad->is_ogm) {
1042 gst_pb_utils_add_codec_description_to_tag_list (pad->taglist,
1043 GST_TAG_AUDIO_CODEC, pad->caps);
1045 GST_FIXME ("not adding codec tag, not sure about codec type");
1049 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
1050 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
1058 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
1060 pad->granulerate_n = 0;
1061 pad->granulerate_d = 1;
1062 pad->granuleshift = 0;
1064 pad->n_header_packets = 3;
1066 pad->caps = gst_caps_new_empty_simple ("audio/x-flac");
1072 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
1074 if (pad->n_header_packets_seen == 1) {
1075 if (packet->bytes < 17)
1078 pad->granulerate_n = (packet->packet[14] << 12) |
1079 (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
1082 if (pad->n_header_packets_seen < pad->n_header_packets) {
1090 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
1092 guint8 *data = packet->packet;
1095 /* see http://flac.sourceforge.net/ogg_mapping.html */
1097 pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
1098 pad->granulerate_d = 1;
1100 if (pad->granulerate_n == 0) {
1101 pad->granulerate_n = 0;
1102 pad->granulerate_d = 0;
1106 pad->granuleshift = 0;
1107 chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
1109 GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
1111 pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
1113 pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
1114 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
1120 is_header_flac (GstOggStream * pad, ogg_packet * packet)
1122 return (packet->bytes > 0 && (packet->packet[0] != 0xff));
1126 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
1128 int block_size_index;
1130 if (packet->bytes < 4)
1133 block_size_index = packet->packet[2] >> 4;
1134 if (block_size_index == 1)
1136 if (block_size_index >= 2 && block_size_index <= 5) {
1137 return 576 << (block_size_index - 2);
1139 if (block_size_index >= 8) {
1140 return G_GUINT64_CONSTANT (256) << (block_size_index - 8);
1142 if (block_size_index == 6 || block_size_index == 7) {
1143 guint len, bytes = (block_size_index - 6) + 1;
1146 if (packet->bytes < 4 + 1 + bytes)
1148 tmp = packet->packet[4];
1151 while (tmp & 0x80) {
1159 if (packet->bytes < 4 + len + bytes)
1162 return packet->packet[4 + len] + 1;
1164 return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
1171 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
1173 if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
1174 tag_list_from_vorbiscomment_packet (packet,
1175 packet->packet, 4, &pad->taglist);
1178 pad->taglist = gst_tag_list_new_empty ();
1180 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
1181 GST_TAG_AUDIO_CODEC, "FLAC", NULL);
1188 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
1191 gint64 prestime_n, prestime_d;
1192 gint64 basetime_n, basetime_d;
1194 if (packet->bytes < 44) {
1195 GST_DEBUG ("Not enough data for fishead header");
1199 data = packet->packet;
1201 data += 8; /* header */
1203 pad->skeleton_major = GST_READ_UINT16_LE (data);
1205 pad->skeleton_minor = GST_READ_UINT16_LE (data);
1208 prestime_n = (gint64) GST_READ_UINT64_LE (data);
1210 prestime_d = (gint64) GST_READ_UINT64_LE (data);
1212 basetime_n = (gint64) GST_READ_UINT64_LE (data);
1214 basetime_d = (gint64) GST_READ_UINT64_LE (data);
1217 /* FIXME: we don't use basetime anywhere in the demuxer! */
1218 if (basetime_d != 0)
1219 pad->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
1223 if (prestime_d != 0)
1224 pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1228 /* Ogg Skeleton 3.3+ streams provide additional information in the header */
1229 if (packet->bytes >= SKELETON_FISHEAD_3_3_MIN_SIZE && pad->skeleton_major == 3
1230 && pad->skeleton_minor > 0) {
1231 gint64 firstsampletime_n, firstsampletime_d;
1232 gint64 lastsampletime_n, lastsampletime_d;
1233 gint64 firstsampletime, lastsampletime;
1234 guint64 segment_length, content_offset;
1236 firstsampletime_n = GST_READ_UINT64_LE (data + 64);
1237 firstsampletime_d = GST_READ_UINT64_LE (data + 72);
1238 lastsampletime_n = GST_READ_UINT64_LE (data + 80);
1239 lastsampletime_d = GST_READ_UINT64_LE (data + 88);
1240 segment_length = GST_READ_UINT64_LE (data + 96);
1241 content_offset = GST_READ_UINT64_LE (data + 104);
1243 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1244 firstsampletime_n, firstsampletime_d);
1245 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1246 lastsampletime_n, lastsampletime_d);
1247 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1248 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1250 if (firstsampletime_d > 0)
1251 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1252 firstsampletime_n, firstsampletime_d);
1254 firstsampletime = 0;
1256 if (lastsampletime_d > 0)
1257 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1258 lastsampletime_n, lastsampletime_d);
1262 if (lastsampletime > firstsampletime)
1263 pad->total_time = lastsampletime - firstsampletime;
1265 pad->total_time = -1;
1267 GST_INFO ("skeleton fishead parsed total: %" GST_TIME_FORMAT,
1268 GST_TIME_ARGS (pad->total_time));
1269 } else if (packet->bytes >= SKELETON_FISHEAD_4_0_MIN_SIZE
1270 && pad->skeleton_major == 4) {
1271 guint64 segment_length, content_offset;
1273 segment_length = GST_READ_UINT64_LE (data + 64);
1274 content_offset = GST_READ_UINT64_LE (data + 72);
1276 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1277 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1279 pad->total_time = -1;
1282 GST_INFO ("skeleton fishead %u.%u parsed (basetime: %" GST_TIME_FORMAT
1283 ", prestime: %" GST_TIME_FORMAT ")", pad->skeleton_major,
1284 pad->skeleton_minor, GST_TIME_ARGS (pad->basetime),
1285 GST_TIME_ARGS (pad->prestime));
1287 pad->is_skeleton = TRUE;
1288 pad->is_sparse = TRUE;
1290 pad->caps = gst_caps_new_empty_simple ("application/x-ogg-skeleton");
1296 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1297 guint32 * serialno, GstOggSkeleton * type)
1299 GstOggSkeleton stype;
1300 guint serial_offset;
1302 if (size != 0 && size < SKELETON_FISBONE_MIN_SIZE) {
1303 GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1308 /* Skeleton EOS packet is zero bytes */
1310 } else if (memcmp (data, "fisbone\0", 8) == 0) {
1311 GST_INFO ("got fisbone packet");
1312 stype = GST_OGG_SKELETON_FISBONE;
1314 } else if (memcmp (data, "index\0", 6) == 0) {
1315 GST_INFO ("got index packet");
1316 stype = GST_OGG_SKELETON_INDEX;
1318 } else if (memcmp (data, "fishead\0", 8) == 0) {
1321 GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1326 *serialno = GST_READ_UINT32_LE (data + serial_offset);
1335 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1336 const guint8 * data, guint size, GstClockTime * p_start_time)
1338 GstClockTime start_time;
1339 gint64 start_granule;
1341 if (pad->have_fisbone) {
1342 GST_DEBUG ("already have fisbone, ignoring second one");
1346 /* skip "fisbone\0" + headers offset + serialno + num headers */
1347 data += 8 + 4 + 4 + 4;
1349 /* We don't overwrite whatever was set before by the format-specific
1350 setup: skeleton contains wrong information sometimes, and the codec
1351 headers are authoritative.
1352 So we only gather information that was not already filled out by
1353 the mapper setup. This should hopefully allow handling unknown
1354 streams a bit better, while not trashing correct setup from bad
1356 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
1357 pad->granulerate_n = GST_READ_UINT64_LE (data);
1358 pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
1360 if (pad->granuleshift == G_MAXUINT32) {
1361 pad->granuleshift = GST_READ_UINT8 (data + 28);
1362 if (pad->granuleshift >= 63) {
1363 /* Granuleshift can't be greater than the storage size of a granule */
1364 GST_WARNING ("Invalid granuleshift (%u >= 63)", pad->granuleshift);
1365 pad->granulerate_n = 0;
1366 pad->granulerate_d = 0;
1367 pad->granuleshift = -1;
1372 pad->have_fisbone = TRUE;
1374 start_granule = GST_READ_UINT64_LE (data + 16);
1375 pad->preroll = GST_READ_UINT32_LE (data + 24);
1377 start_time = granulepos_to_granule_default (pad, start_granule);
1379 GST_INFO ("skeleton fisbone parsed "
1380 "(start time: %" GST_TIME_FORMAT
1381 " granulerate_n: %d granulerate_d: %d "
1382 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
1383 GST_TIME_ARGS (start_time),
1384 pad->granulerate_n, pad->granulerate_d, pad->preroll, pad->granuleshift);
1387 *p_start_time = start_time;
1393 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1401 if (G_UNLIKELY (*size < 1))
1405 *result |= ((byte & 0x7f) << shift);
1410 } while ((byte & 0x80) != 0x80);
1416 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1417 const guint8 * data, guint size)
1419 guint64 i, n_keypoints, isize;
1420 guint64 offset, timestamp;
1421 guint64 offset_d, timestamp_d;
1424 GST_DEBUG ("already have index, ignoring second one");
1428 if ((skel_pad->skeleton_major == 3 && size < 26) ||
1429 (skel_pad->skeleton_major == 4 && size < 62)) {
1430 GST_WARNING ("small index packet of size %u, ignoring", size);
1434 /* skip "index\0" + serialno */
1438 n_keypoints = GST_READ_UINT64_LE (data);
1443 pad->kp_denom = GST_READ_UINT64_LE (data);
1444 if (pad->kp_denom == 0)
1450 if (skel_pad->skeleton_major == 4) {
1451 gint64 firstsampletime_n;
1452 gint64 lastsampletime_n;
1453 gint64 firstsampletime, lastsampletime;
1455 firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1456 lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1458 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1459 firstsampletime_n, pad->kp_denom);
1460 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1461 lastsampletime_n, pad->kp_denom);
1463 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1464 firstsampletime_n, pad->kp_denom);
1465 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1466 lastsampletime_n, pad->kp_denom);
1468 if (lastsampletime > firstsampletime)
1469 pad->total_time = lastsampletime - firstsampletime;
1471 pad->total_time = -1;
1473 GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1474 GST_TIME_ARGS (pad->total_time));
1480 GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1481 G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1483 pad->index = g_try_new (GstOggIndex, n_keypoints);
1491 for (i = 0; i < n_keypoints; i++) {
1493 if (!read_vlc (&data, &size, &offset_d))
1495 if (!read_vlc (&data, &size, ×tamp_d))
1499 timestamp += timestamp_d;
1501 pad->index[i].offset = offset;
1502 pad->index[i].timestamp = timestamp;
1505 GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1508 if (isize != n_keypoints) {
1509 GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1510 G_GUINT64_FORMAT, n_keypoints, isize);
1512 pad->n_index = isize;
1513 /* try to use the index to estimate the bitrate */
1515 guint64 so, eo, st, et, b, t;
1517 /* get start and end offset and timestamps */
1518 so = pad->index[0].offset;
1519 st = pad->index[0].timestamp;
1520 eo = pad->index[isize - 1].offset;
1521 et = pad->index[isize - 1].timestamp;
1526 GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1528 /* this is the total stream bitrate according to this index */
1529 pad->idx_bitrate = gst_util_uint64_scale (8 * b, pad->kp_denom, t);
1531 GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1538 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1541 if (index->timestamp < *ts)
1543 else if (index->timestamp > *ts)
1550 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1551 guint64 * timestamp, guint64 * offset)
1557 g_return_val_if_fail (timestamp != NULL, FALSE);
1558 g_return_val_if_fail (offset != NULL, FALSE);
1560 n_index = pad->n_index;
1561 if (n_index == 0 || pad->index == NULL)
1564 ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1565 GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1568 gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1569 (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1575 GST_INFO ("found at index %u", (guint) (best - pad->index));
1577 *offset = best->offset;
1579 gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1584 /* Do we need these for something?
1585 * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
1586 * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
1587 * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
1588 * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
1589 * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
1590 * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
1594 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1596 if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1603 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1605 if (!(packet->packet[0] & 1) && (packet->packet[0] & 3 && pad->is_ogm_text)) {
1606 tag_list_from_vorbiscomment_packet (packet,
1607 (const guint8 *) "\003vorbis", 7, &pad->taglist);
1612 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1619 data = packet->packet;
1620 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1622 if (offset > packet->bytes) {
1623 GST_WARNING ("buffer too small");
1628 for (n = offset - 1; n > 0; n--) {
1629 samples = (samples << 8) | data[n];
1636 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1638 guint8 *data = packet->packet;
1642 pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1643 pad->granulerate_d = 1;
1645 GST_LOG ("sample rate: %d", pad->granulerate_n);
1646 if (pad->granulerate_n == 0) {
1647 pad->granulerate_n = 0;
1648 pad->granulerate_d = 0;
1652 fourcc = GST_READ_UINT32_LE (data + 9);
1653 fstr = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1654 GST_DEBUG ("fourcc: %s", fstr);
1656 /* FIXME: Need to do something with the reorder map */
1658 gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL, NULL);
1661 gst_caps_set_simple (pad->caps,
1662 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1664 pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
1665 "fourcc", G_TYPE_STRING, fstr,
1666 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1670 pad->n_header_packets = 1;
1677 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1679 guint8 *data = packet->packet;
1685 GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
1686 GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
1688 pad->is_video = TRUE;
1689 pad->granulerate_n = 10000000;
1690 time_unit = GST_READ_UINT64_LE (data + 17);
1691 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1692 GST_WARNING ("timeunit is out of range");
1694 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1696 GST_LOG ("fps = %d/%d = %.3f",
1697 pad->granulerate_n, pad->granulerate_d,
1698 (double) pad->granulerate_n / pad->granulerate_d);
1700 fourcc = GST_READ_UINT32_LE (data + 9);
1701 width = GST_READ_UINT32_LE (data + 45);
1702 height = GST_READ_UINT32_LE (data + 49);
1703 fstr = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1704 GST_DEBUG ("fourcc: %s", fstr);
1706 pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1708 if (pad->caps == NULL) {
1709 pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
1710 "fourcc", G_TYPE_STRING, fstr,
1711 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1712 pad->granulerate_d, NULL);
1714 gst_caps_set_simple (pad->caps,
1715 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1717 "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
1719 GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
1722 pad->n_header_packets = 1;
1723 pad->frame_size = 1;
1730 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1732 guint8 *data = packet->packet;
1735 pad->granulerate_n = 10000000;
1736 time_unit = GST_READ_UINT64_LE (data + 17);
1737 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1738 GST_WARNING ("timeunit is out of range");
1740 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1742 GST_LOG ("fps = %d/%d = %.3f",
1743 pad->granulerate_n, pad->granulerate_d,
1744 (double) pad->granulerate_n / pad->granulerate_d);
1746 if (pad->granulerate_d <= 0) {
1747 pad->granulerate_n = 0;
1748 pad->granulerate_d = 0;
1752 pad->caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
1755 pad->n_header_packets = 1;
1757 pad->is_ogm_text = TRUE;
1758 pad->is_sparse = TRUE;
1765 #define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */
1766 #define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */
1767 #define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */
1768 #define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */
1769 #define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */
1770 #define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */
1771 #define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */
1772 #define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */
1774 #define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */
1775 #define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */
1777 #define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */
1778 #define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */
1779 #define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */
1780 #define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */
1784 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1786 guint8 *data = packet->packet;
1791 pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1792 pad->granulerate_d = 1;
1793 GST_LOG ("sample rate: %d", pad->granulerate_n);
1795 if (pad->granulerate_n == 0) {
1796 pad->granulerate_n = 0;
1797 pad->granulerate_d = 0;
1801 format = GST_READ_UINT32_LE (data + 12);
1802 channels = GST_READ_UINT8 (data + 21);
1805 caps = gst_caps_new_simple ("audio/x-raw",
1806 "format", G_TYPE_STRING, "S8", NULL);
1809 caps = gst_caps_new_simple ("audio/x-raw",
1810 "format", G_TYPE_STRING, "U8", NULL);
1812 case OGGPCM_FMT_S16_LE:
1813 caps = gst_caps_new_simple ("audio/x-raw",
1814 "format", G_TYPE_STRING, "S16LE", NULL);
1816 case OGGPCM_FMT_S16_BE:
1817 caps = gst_caps_new_simple ("audio/x-raw",
1818 "format", G_TYPE_STRING, "S16BE", NULL);
1820 case OGGPCM_FMT_S24_LE:
1821 caps = gst_caps_new_simple ("audio/x-raw",
1822 "format", G_TYPE_STRING, "S24LE", NULL);
1824 case OGGPCM_FMT_S24_BE:
1825 caps = gst_caps_new_simple ("audio/x-raw",
1826 "format", G_TYPE_STRING, "S24BE", NULL);
1828 case OGGPCM_FMT_S32_LE:
1829 caps = gst_caps_new_simple ("audio/x-raw",
1830 "format", G_TYPE_STRING, "S32LE", NULL);
1832 case OGGPCM_FMT_S32_BE:
1833 caps = gst_caps_new_simple ("audio/x-raw",
1834 "format", G_TYPE_STRING, "S32BE", NULL);
1836 case OGGPCM_FMT_ULAW:
1837 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
1839 case OGGPCM_FMT_ALAW:
1840 caps = gst_caps_new_empty_simple ("audio/x-alaw");
1842 case OGGPCM_FMT_FLT32_LE:
1843 caps = gst_caps_new_simple ("audio/x-raw",
1844 "format", G_TYPE_STRING, "F32LE", NULL);
1846 case OGGPCM_FMT_FLT32_BE:
1847 caps = gst_caps_new_simple ("audio/x-raw",
1848 "format", G_TYPE_STRING, "F32BE", NULL);
1850 case OGGPCM_FMT_FLT64_LE:
1851 caps = gst_caps_new_simple ("audio/x-raw",
1852 "format", G_TYPE_STRING, "F64LE", NULL);
1854 case OGGPCM_FMT_FLT64_BE:
1855 caps = gst_caps_new_simple ("audio/x-raw",
1856 "format", G_TYPE_STRING, "F64BE", NULL);
1859 pad->granulerate_n = 0;
1860 pad->granulerate_d = 0;
1864 pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1866 gst_caps_set_simple (caps,
1867 "layout", G_TYPE_STRING, "interleaved",
1868 "rate", G_TYPE_INT, pad->granulerate_n,
1869 "channels", G_TYPE_INT, channels, NULL);
1878 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1880 guint8 *data = packet->packet;
1882 pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1883 pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1884 pad->granuleshift = data[28];
1886 if (pad->granuleshift >= 63) {
1887 /* Granuleshift can't be greater than the storage size of a granule */
1888 GST_WARNING ("Invalid granuleshift (%u >= 63)", pad->granuleshift);
1889 pad->granulerate_n = 0;
1890 pad->granulerate_d = 0;
1891 pad->granuleshift = -1;
1894 GST_LOG ("sample rate: %d", pad->granulerate_n);
1896 if (pad->granulerate_n == 0) {
1897 pad->granulerate_n = 0;
1898 pad->granulerate_d = 0;
1899 pad->granuleshift = -1;
1903 pad->n_header_packets = 3;
1905 data += 4 + (4 + 4 + 4);
1906 GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1907 GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1909 pad->caps = gst_caps_new_empty_simple ("text/x-cmml");
1910 pad->always_flush_page = TRUE;
1911 pad->is_sparse = TRUE;
1912 pad->is_cmml = TRUE;
1920 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1922 guint8 *data = packet->packet;
1924 pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1925 pad->granulerate_d = 1;
1926 pad->granuleshift = 0;
1927 GST_LOG ("sample rate: %d", pad->granulerate_n);
1929 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1930 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1932 if (pad->granulerate_n == 0) {
1933 pad->granulerate_n = 0;
1934 pad->granulerate_d = 0;
1935 pad->granuleshift = -1;
1939 pad->caps = gst_caps_new_simple ("audio/x-celt",
1940 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1948 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1950 guint8 *data = packet->packet;
1951 const char *category;
1953 if (packet->bytes < 64)
1956 pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1957 pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1958 pad->granuleshift = GST_READ_UINT8 (data + 15);
1959 if (pad->granuleshift >= 63) {
1960 /* Granuleshift can't be greater than the storage size of a granule */
1961 GST_WARNING ("Invalid granuleshift (%u >= 63)", pad->granuleshift);
1962 pad->granulerate_n = 0;
1963 pad->granulerate_d = 0;
1964 pad->granuleshift = -1;
1967 GST_LOG ("sample rate: %d", pad->granulerate_n);
1969 if (pad->granulerate_n == 0) {
1970 pad->granulerate_n = 0;
1971 pad->granulerate_d = 0;
1972 pad->granuleshift = -1;
1976 pad->n_header_packets = GST_READ_UINT8 (data + 11);
1977 GST_LOG ("kate header packets: %d", pad->n_header_packets);
1979 category = (const char *) data + 48;
1980 if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1981 strcmp (category, "spu-subtitles") == 0 ||
1982 strcmp (category, "K-SPU") == 0) {
1983 pad->caps = gst_caps_new_empty_simple ("subtitle/x-kate");
1985 pad->caps = gst_caps_new_empty_simple ("application/x-kate");
1988 pad->is_sparse = TRUE;
1989 pad->always_flush_page = TRUE;
1995 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1999 if (packet->bytes < 1)
2002 switch (packet->packet[0]) {
2003 case 0x00: /* text data */
2004 if (packet->bytes < 1 + 8 * 2) {
2007 duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
2013 duration = GST_CLOCK_TIME_NONE;
2021 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
2023 GstTagList *list = NULL;
2025 if (packet->bytes <= 0)
2028 switch (packet->packet[0]) {
2030 const gchar *canonical;
2033 if (packet->bytes < 64) {
2034 GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
2038 /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
2039 memcpy (language, packet->packet + 32, 16);
2042 /* language is an ISO 639-1 code or RFC 3066 language code, we
2043 * truncate to ISO 639-1 */
2044 g_strdelimit (language, NULL, '\0');
2045 canonical = gst_tag_get_language_code_iso_639_1 (language);
2047 list = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, canonical, NULL);
2049 GST_WARNING ("Unknown or invalid language code %s, ignored", language);
2054 tag_list_from_vorbiscomment_packet (packet,
2055 (const guint8 *) "\201kate\0\0\0\0", 9, &list);
2058 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
2059 GST_TAG_SUBTITLE_CODEC, "Kate", NULL);
2068 /* ensure the comment packet cannot override the category/language
2069 from the identification header */
2070 gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL);
2071 gst_tag_list_unref (list);
2073 pad->taglist = list;
2080 setup_opus_mapper (GstOggStream * pad, ogg_packet * packet)
2084 if (packet->bytes < 19)
2087 pad->granulerate_n = 48000;
2088 pad->granulerate_d = 1;
2089 pad->granuleshift = 0;
2090 pad->n_header_packets = 2;
2091 pad->first_granpos = -1;
2092 pad->audio_clipping = TRUE;
2094 /* pre-skip is in samples at 48000 Hz, which matches granule one for one */
2095 pad->granule_offset = -GST_READ_UINT16_LE (packet->packet + 10);
2096 GST_INFO ("Opus has a pre-skip of %" G_GINT64_FORMAT " samples",
2097 -pad->granule_offset);
2100 gst_buffer_new_wrapped (g_memdup (packet->packet, packet->bytes),
2102 pad->caps = gst_codec_utils_opus_create_caps_from_header (buffer, NULL);
2103 gst_buffer_unref (buffer);
2109 is_header_opus (GstOggStream * pad, ogg_packet * packet)
2111 return packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4);
2115 granulepos_to_granule_opus (GstOggStream * pad, gint64 granulepos)
2117 if (granulepos == -1)
2120 if (pad->first_granpos < 0 || granulepos < pad->first_granpos)
2121 pad->first_granpos = granulepos;
2127 packet_duration_opus (GstOggStream * pad, ogg_packet * packet)
2129 static const guint64 durations[32] = {
2130 480, 960, 1920, 2880, /* Silk NB */
2131 480, 960, 1920, 2880, /* Silk MB */
2132 480, 960, 1920, 2880, /* Silk WB */
2133 480, 960, /* Hybrid SWB */
2134 480, 960, /* Hybrid FB */
2135 120, 240, 480, 960, /* CELT NB */
2136 120, 240, 480, 960, /* CELT NB */
2137 120, 240, 480, 960, /* CELT NB */
2138 120, 240, 480, 960, /* CELT NB */
2142 gint64 frame_duration;
2146 if (packet->bytes < 1)
2150 if (is_header_opus (pad, packet))
2153 toc = packet->packet[0];
2155 frame_duration = durations[toc >> 3];
2167 if (packet->bytes < 2) {
2168 GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
2171 nframes = packet->packet[1] & 63;
2175 duration = nframes * frame_duration;
2176 if (duration > 5760) {
2177 GST_WARNING ("Opus packet duration > 120 ms, invalid");
2180 GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
2181 frame_duration / 48.f, nframes, duration / 48.f);
2186 extract_tags_opus (GstOggStream * pad, ogg_packet * packet)
2188 if (packet->bytes >= 8 && memcmp (packet->packet, "OpusTags", 8) == 0) {
2189 tag_list_from_vorbiscomment_packet (packet,
2190 (const guint8 *) "OpusTags", 8, &pad->taglist);
2193 pad->taglist = gst_tag_list_new_empty ();
2195 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
2196 GST_TAG_AUDIO_CODEC, "Opus", NULL);
2203 setup_daala_mapper (GstOggStream * pad, ogg_packet * packet)
2205 guint8 *data = packet->packet;
2206 guint w, h, par_d, par_n;
2207 guint8 vmaj, vmin, vrev;
2208 guint frame_duration;
2214 GST_LOG ("daala %d.%d.%d", vmaj, vmin, vrev);
2216 w = GST_READ_UINT32_LE (data + 9);
2217 h = GST_READ_UINT32_LE (data + 13);
2219 par_n = GST_READ_UINT32_LE (data + 17);
2220 par_d = GST_READ_UINT32_LE (data + 21);
2222 pad->granulerate_n = GST_READ_UINT32_LE (data + 25);
2223 pad->granulerate_d = GST_READ_UINT32_LE (data + 29);
2224 frame_duration = GST_READ_UINT32_LE (data + 33);
2226 GST_LOG ("fps = %d/%d, dur %d, PAR = %u/%u, width = %u, height = %u",
2227 pad->granulerate_n, pad->granulerate_d, frame_duration, par_n, par_d, w,
2230 pad->granuleshift = GST_READ_UINT8 (data + 37);
2231 if (pad->granuleshift >= 63) {
2232 /* Granuleshift can't be greater than the storage size of a granule */
2233 GST_WARNING ("Invalid granuleshift (%u >= 63)", pad->granuleshift);
2234 pad->granulerate_n = 0;
2235 pad->granulerate_d = 0;
2236 pad->granuleshift = -1;
2239 GST_LOG ("granshift: %d", pad->granuleshift);
2241 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
2242 GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
2243 pad->granulerate_n = 0;
2244 pad->granulerate_d = 0;
2245 pad->granuleshift = -1;
2249 pad->is_video = TRUE;
2250 pad->n_header_packets = 3;
2251 pad->frame_size = 1;
2253 pad->caps = gst_caps_new_empty_simple ("video/x-daala");
2255 if (w > 0 && h > 0) {
2256 gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
2257 G_TYPE_INT, h, NULL);
2260 /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
2261 if (par_n == 0 || par_d == 0)
2264 /* only add framerate now so caps look prettier, with width/height first */
2265 gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
2266 pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
2267 GST_TYPE_FRACTION, par_n, par_d, NULL);
2273 granulepos_to_granule_daala (GstOggStream * pad, gint64 granulepos)
2275 gint64 keyindex, keyoffset;
2277 if (pad->granuleshift != 0 && pad->granuleshift != G_MAXUINT32) {
2278 keyindex = granulepos >> pad->granuleshift;
2279 keyoffset = granulepos - (keyindex << pad->granuleshift);
2280 return keyindex + keyoffset;
2287 is_granulepos_keyframe_daala (GstOggStream * pad, gint64 granulepos)
2291 if (granulepos == (gint64) - 1 || pad->granuleshift == G_MAXUINT32)
2294 frame_mask = (G_GUINT64_CONSTANT (1) << pad->granuleshift) - 1;
2296 return ((granulepos & frame_mask) == 0);
2300 is_packet_keyframe_daala (GstOggStream * pad, ogg_packet * packet)
2302 if (packet->bytes == 0)
2304 return (packet->packet[0] & 0x40);
2308 is_header_daala (GstOggStream * pad, ogg_packet * packet)
2310 return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
2314 extract_tags_daala (GstOggStream * pad, ogg_packet * packet)
2316 if (packet->bytes > 0 && packet->packet[0] == 0x81) {
2317 tag_list_from_vorbiscomment_packet (packet,
2318 (const guint8 *) "\201daala", 5, &pad->taglist);
2321 pad->taglist = gst_tag_list_new_empty ();
2323 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
2324 GST_TAG_VIDEO_CODEC, "Daala", NULL);
2327 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
2328 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
2333 /* indent hates our freedoms */
2334 const GstOggMap mappers[] = {
2336 /* Empty mapper for uninitialized pads/streams */
2337 NULL, 0, G_MAXINT32,
2353 "\200theora", 7, 42,
2355 setup_theora_mapper,
2357 granulepos_to_granule_theora,
2358 granule_to_granulepos_default,
2359 is_granulepos_keyframe_theora,
2360 is_packet_keyframe_theora,
2362 packet_duration_constant,
2364 extract_tags_theora,
2369 "\001vorbis", 7, 22,
2371 setup_vorbis_mapper,
2373 granulepos_to_granule_default,
2374 granule_to_granulepos_default,
2375 is_granulepos_keyframe_true,
2376 is_packet_keyframe_true,
2378 packet_duration_vorbis,
2380 extract_tags_vorbis,
2389 granulepos_to_granule_default,
2390 granule_to_granulepos_default,
2391 is_granulepos_keyframe_true,
2392 is_packet_keyframe_true,
2394 packet_duration_constant,
2417 "CMML\0\0\0\0", 8, 0,
2434 "application/x-annodex",
2435 setup_fishead_mapper,
2437 granulepos_to_granule_default,
2438 granule_to_granulepos_default,
2450 "application/octet-stream",
2451 setup_fishead_mapper,
2469 granulepos_to_granule_default,
2470 granule_to_granulepos_default,
2471 is_granulepos_keyframe_true,
2472 is_packet_keyframe_true,
2474 packet_duration_flac,
2485 granulepos_to_granule_default,
2486 granule_to_granulepos_default,
2487 is_granulepos_keyframe_true,
2488 is_packet_keyframe_true,
2490 packet_duration_flac,
2498 "application/octet-stream",
2516 granulepos_to_granule_default,
2517 granule_to_granulepos_default,
2521 packet_duration_constant,
2528 "\200kate\0\0\0", 8, 0,
2532 granulepos_to_granule_default,
2533 granule_to_granulepos_default,
2537 packet_duration_kate,
2548 granulepos_to_granule_dirac,
2549 granule_to_granulepos_dirac,
2553 packet_duration_constant,
2554 granulepos_to_key_granule_dirac,
2563 setup_vp8_mapper_from_caps,
2564 granulepos_to_granule_vp8,
2565 granule_to_granulepos_vp8,
2569 packet_duration_vp8,
2570 granulepos_to_key_granule_vp8,
2580 granulepos_to_granule_opus,
2581 granule_to_granulepos_default,
2583 is_packet_keyframe_true,
2585 packet_duration_opus,
2592 "\001audio\0\0\0", 9, 53,
2593 "application/x-ogm-audio",
2594 setup_ogmaudio_mapper,
2596 granulepos_to_granule_default,
2597 granule_to_granulepos_default,
2598 is_granulepos_keyframe_true,
2599 is_packet_keyframe_true,
2601 packet_duration_ogm,
2608 "\001video\0\0\0", 9, 53,
2609 "application/x-ogm-video",
2610 setup_ogmvideo_mapper,
2612 granulepos_to_granule_default,
2613 granule_to_granulepos_default,
2617 packet_duration_constant,
2624 "\001text\0\0\0", 9, 9,
2625 "application/x-ogm-text",
2626 setup_ogmtext_mapper,
2628 granulepos_to_granule_default,
2629 granule_to_granulepos_default,
2630 is_granulepos_keyframe_true,
2631 is_packet_keyframe_true,
2633 packet_duration_ogm,
2644 granulepos_to_granule_daala,
2645 granule_to_granulepos_default,
2646 is_granulepos_keyframe_daala,
2647 is_packet_keyframe_daala,
2649 packet_duration_constant,
2660 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2665 for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2666 if (packet->bytes >= mappers[i].min_packet_size &&
2667 packet->bytes >= mappers[i].id_length &&
2668 memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
2670 GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2672 if (mappers[i].setup_func)
2673 ret = mappers[i].setup_func (pad, packet);
2678 GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2682 GST_WARNING ("mapper '%s' did not accept setup header",
2683 mappers[i].media_type);
2692 gst_ogg_stream_setup_map_from_caps (GstOggStream * pad, const GstCaps * caps)
2696 GstStructure *structure;
2698 g_return_val_if_fail (caps != NULL, FALSE);
2700 structure = gst_caps_get_structure (caps, 0);
2702 for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2703 if (mappers[i].setup_from_caps_func &&
2704 gst_structure_has_name (structure, mappers[i].media_type)) {
2706 GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2708 if (mappers[i].setup_from_caps_func)
2709 ret = mappers[i].setup_from_caps_func (pad, caps);
2714 GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2718 GST_WARNING ("mapper '%s' did not accept caps %" GST_PTR_FORMAT,
2719 mappers[i].media_type, caps);
2728 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2729 const GstCaps * caps)
2732 const GstStructure *structure;
2733 const GValue *streamheader;
2734 const GValue *first_element;
2739 GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2744 structure = gst_caps_get_structure (caps, 0);
2745 streamheader = gst_structure_get_value (structure, "streamheader");
2747 if (streamheader == NULL) {
2748 GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2752 if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2753 GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2757 if (gst_value_array_get_size (streamheader) == 0) {
2758 GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2762 first_element = gst_value_array_get_value (streamheader, 0);
2764 if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2765 GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2769 buf = gst_value_get_buffer (first_element);
2771 GST_ERROR ("no first streamheader buffer");
2775 if (!gst_buffer_map (buf, &map, GST_MAP_READ) || map.size == 0) {
2776 GST_ERROR ("invalid first streamheader buffer");
2780 GST_MEMDUMP ("streamheader", map.data, map.size);
2782 packet.packet = map.data;
2783 packet.bytes = map.size;
2785 GST_INFO ("Found headers on caps, using those to determine type");
2786 ret = gst_ogg_stream_setup_map (pad, &packet);
2788 gst_buffer_unmap (buf, &map);