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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 #include "gstoggstream.h"
26 #include "dirac_parse.h"
27 #include "vorbis_parse.h"
29 #include <gst/riff/riff-media.h>
34 GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug);
35 GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_setup_debug);
36 #define GST_CAT_DEFAULT gst_ogg_demux_debug
38 typedef struct _GstOggMap GstOggMap;
40 typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad,
42 typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad,
44 typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad,
46 typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad,
47 gint64 granule, gint64 keyframe_granule);
49 /* returns TRUE if the granulepos denotes a key frame */
50 typedef gboolean (*GstOggMapIsKeyFrameFunc) (GstOggStream * pad,
53 /* returns TRUE if the given packet is a stream header packet */
54 typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
56 typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
58 typedef void (*GstOggMapExtractTagsFunc) (GstOggStream * pad,
61 typedef gint64 (*GstOggMapGranuleposToKeyGranuleFunc) (GstOggStream * pad,
64 #define SKELETON_FISBONE_MIN_SIZE 52
65 #define SKELETON_FISHEAD_3_3_MIN_SIZE 112
66 #define SKELETON_FISHEAD_4_0_MIN_SIZE 80
73 const gchar *media_type;
74 GstOggMapSetupFunc setup_func;
75 GstOggMapToGranuleFunc granulepos_to_granule_func;
76 GstOggMapToGranuleposFunc granule_to_granulepos_func;
77 GstOggMapIsKeyFrameFunc is_key_frame_func;
78 GstOggMapIsHeaderPacketFunc is_header_func;
79 GstOggMapPacketDurationFunc packet_duration_func;
80 GstOggMapGranuleposToKeyGranuleFunc granulepos_to_key_granule_func;
81 GstOggMapExtractTagsFunc extract_tags_func;
84 extern const GstOggMap mappers[];
87 gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet)
91 if (packet->granulepos == -1) {
92 return GST_CLOCK_TIME_NONE;
95 duration = gst_ogg_stream_get_packet_duration (pad, packet);
97 return GST_CLOCK_TIME_NONE;
100 return gst_ogg_stream_granule_to_time (pad,
101 gst_ogg_stream_granulepos_to_granule (pad,
102 packet->granulepos) - duration);
106 gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad,
109 if (pad->frame_size == 0)
110 return GST_CLOCK_TIME_NONE;
112 return gst_ogg_stream_granule_to_time (pad,
113 gst_ogg_stream_granulepos_to_granule (pad, granulepos));
117 gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad,
120 return gst_ogg_stream_granule_to_time (pad,
121 gst_ogg_stream_granulepos_to_granule (pad, granulepos));
125 gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule)
127 if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
130 return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
135 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
137 if (granulepos == -1 || granulepos == 0) {
141 if (mappers[pad->map].granulepos_to_granule_func == NULL) {
142 GST_WARNING ("Failed to convert granulepos to granule");
146 return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
150 gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos)
152 if (mappers[pad->map].granulepos_to_key_granule_func)
153 return mappers[pad->map].granulepos_to_key_granule_func (pad, granulepos);
155 if (granulepos == -1 || granulepos == 0) {
159 return granulepos >> pad->granuleshift;
163 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
164 gint64 keyframe_granule)
166 if (granule == -1 || granule == 0) {
170 if (mappers[pad->map].granule_to_granulepos_func == NULL) {
171 GST_WARNING ("Failed to convert granule to granulepos");
175 return mappers[pad->map].granule_to_granulepos_func (pad, granule,
180 gst_ogg_stream_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos)
182 if (granulepos == -1) {
186 if (mappers[pad->map].is_key_frame_func == NULL) {
187 GST_WARNING ("Failed to determine key frame");
191 return mappers[pad->map].is_key_frame_func (pad, granulepos);
195 gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
197 if (mappers[pad->map].is_header_func == NULL) {
198 GST_WARNING ("Failed to determine header");
202 return mappers[pad->map].is_header_func (pad, packet);
206 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
208 if (mappers[pad->map].packet_duration_func == NULL) {
209 GST_WARNING ("Failed to determine packet duration");
213 return mappers[pad->map].packet_duration_func (pad, packet);
218 gst_ogg_stream_extract_tags (GstOggStream * pad, ogg_packet * packet)
220 if (mappers[pad->map].extract_tags_func == NULL) {
221 GST_DEBUG ("No tag extraction");
225 mappers[pad->map].extract_tags_func (pad, packet);
228 /* some generic functions */
231 is_keyframe_true (GstOggStream * pad, gint64 granulepos)
237 granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos)
239 gint64 keyindex, keyoffset;
241 if (pad->granuleshift != 0) {
242 keyindex = granulepos >> pad->granuleshift;
243 keyoffset = granulepos - (keyindex << pad->granuleshift);
244 return keyindex + keyoffset;
252 granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
253 gint64 keyframe_granule)
257 if (pad->granuleshift != 0) {
258 /* If we don't know where the previous keyframe is yet, assume it is
259 at 0 or 1, depending on bitstream version. If nothing else, this
260 avoids getting negative granpos back. */
261 if (keyframe_granule < 0)
262 keyframe_granule = pad->theora_has_zero_keyoffset ? 0 : 1;
263 keyoffset = granule - keyframe_granule;
264 return (keyframe_granule << pad->granuleshift) | keyoffset;
272 is_header_unknown (GstOggStream * pad, ogg_packet * packet)
274 GST_WARNING ("don't know how to detect header");
280 is_header_true (GstOggStream * pad, ogg_packet * packet)
286 is_header_count (GstOggStream * pad, ogg_packet * packet)
288 if (pad->n_header_packets_seen < pad->n_header_packets) {
295 packet_duration_constant (GstOggStream * pad, ogg_packet * packet)
297 return pad->frame_size;
300 /* helper: extracts tags from vorbis comment ogg packet.
301 * Returns result in *tags after free'ing existing *tags (if any) */
303 tag_list_from_vorbiscomment_packet (ogg_packet * packet,
304 const guint8 * id_data, const guint id_data_length, GstTagList ** tags)
306 gchar *encoder = NULL;
310 g_return_val_if_fail (tags != NULL, FALSE);
312 list = gst_tag_list_from_vorbiscomment (packet->packet, packet->bytes,
313 id_data, id_data_length, &encoder);
316 GST_WARNING ("failed to decode vorbis comments");
323 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
330 gst_tag_list_free (*tags);
339 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
341 guint8 *data = packet->packet;
342 guint w, h, par_d, par_n;
343 guint8 vmaj, vmin, vrev;
349 w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
350 h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
352 pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
353 pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
355 par_n = GST_READ_UINT24_BE (data + 30);
356 par_d = GST_READ_UINT24_BE (data + 33);
358 GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
359 pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
361 /* 2 bits + 3 bits = 5 bits KFGSHIFT */
362 pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
363 (GST_READ_UINT8 (data + 41) >> 5);
365 pad->is_video = TRUE;
366 pad->n_header_packets = 3;
369 pad->bitrate = GST_READ_UINT24_BE (data + 37);
370 GST_LOG ("bit rate: %d", pad->bitrate);
372 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
373 GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
377 /* The interpretation of the granule position has changed with 3.2.1.
378 The granule is now made from the number of frames encoded, rather than
379 the index of the frame being encoded - so there is a difference of 1. */
380 pad->theora_has_zero_keyoffset =
381 ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201;
383 pad->caps = gst_caps_new_simple ("video/x-theora", NULL);
385 if (w > 0 && h > 0) {
386 gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
387 G_TYPE_INT, h, NULL);
390 /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
391 if (par_n == 0 || par_d == 0)
394 /* only add framerate now so caps look prettier, with width/height first */
395 gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
396 pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
397 GST_TYPE_FRACTION, par_n, par_d, NULL);
403 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
405 gint64 keyindex, keyoffset;
407 if (pad->granuleshift != 0) {
408 keyindex = granulepos >> pad->granuleshift;
409 keyoffset = granulepos - (keyindex << pad->granuleshift);
410 if (pad->theora_has_zero_keyoffset) {
413 return keyindex + keyoffset;
420 is_keyframe_theora (GstOggStream * pad, gint64 granulepos)
424 if (granulepos == (gint64) - 1)
427 frame_mask = (1 << pad->granuleshift) - 1;
429 return ((granulepos & frame_mask) == 0);
433 is_header_theora (GstOggStream * pad, ogg_packet * packet)
435 return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
439 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
441 if (packet->bytes > 0 && packet->packet[0] == 0x81) {
442 tag_list_from_vorbiscomment_packet (packet,
443 (const guint8 *) "\201theora", 7, &pad->taglist);
446 pad->taglist = gst_tag_list_new ();
449 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
450 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
457 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
460 DiracSequenceHeader header;
462 ret = dirac_sequence_header_parse (&header, packet->packet + 13,
465 GST_DEBUG ("Failed to parse Dirac sequence header");
469 pad->is_video = TRUE;
470 pad->granulerate_n = header.frame_rate_numerator * 2;
471 pad->granulerate_d = header.frame_rate_denominator;
472 pad->granuleshift = 22;
473 pad->n_header_packets = 1;
476 if (header.interlaced_coding != 0) {
477 GST_DEBUG ("non-progressive Dirac coding not implemented");
481 pad->caps = gst_caps_new_simple ("video/x-dirac",
482 "width", G_TYPE_INT, header.width,
483 "height", G_TYPE_INT, header.height,
484 "interlaced", G_TYPE_BOOLEAN, header.interlaced,
485 "pixel-aspect-ratio", GST_TYPE_FRACTION,
486 header.aspect_ratio_numerator, header.aspect_ratio_denominator,
487 "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
488 header.frame_rate_denominator, NULL);
493 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
495 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
501 if (granulepos == -1)
504 dist_h = (granulepos >> 22) & 0xff;
505 dist_l = granulepos & 0xff;
506 dist = (dist_h << 8) | dist_l;
512 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
518 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
519 delay = (gp >> 9) & 0x1fff;
522 GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
528 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
529 gint64 keyframe_granule)
531 /* This conversion requires knowing more details about the Dirac
537 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
546 if (gp == -1 || gp == 0)
549 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
550 dist_h = (gp >> 22) & 0xff;
552 dist = (dist_h << 8) | dist_l;
553 delay = (gp >> 9) & 0x1fff;
556 return dt - 2 * dist + 4;
562 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
564 gint width, height, par_n, par_d, fps_n, fps_d;
566 if (packet->bytes < 26) {
567 GST_DEBUG ("Failed to parse VP8 BOS page");
571 width = GST_READ_UINT16_BE (packet->packet + 8);
572 height = GST_READ_UINT16_BE (packet->packet + 10);
573 par_n = GST_READ_UINT24_BE (packet->packet + 12);
574 par_d = GST_READ_UINT24_BE (packet->packet + 15);
575 fps_n = GST_READ_UINT32_BE (packet->packet + 18);
576 fps_d = GST_READ_UINT32_BE (packet->packet + 22);
578 pad->is_video = TRUE;
580 pad->granulerate_n = fps_n;
581 pad->granulerate_d = fps_d;
582 pad->n_header_packets = 2;
585 pad->caps = gst_caps_new_simple ("video/x-vp8",
586 "width", G_TYPE_INT, width,
587 "height", G_TYPE_INT, height,
588 "pixel-aspect-ratio", GST_TYPE_FRACTION,
589 par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
595 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
597 guint64 gpos = granulepos;
599 if (granulepos == -1)
602 /* Get rid of flags */
605 return ((gpos & 0x07ffffff) == 0);
609 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
611 guint64 gp = (guint64) gpos;
616 dist = (gp >> 3) & 0x07ffffff;
618 GST_DEBUG ("pt %u, dist %u", pt, dist);
624 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
625 gint64 keyframe_granule)
627 /* FIXME: This requires to look into the content of the packets
628 * because the simple granule counter doesn't know about invisible
634 /* Check if this packet contains an invisible frame or not */
636 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
640 if (packet->bytes < 3)
643 hdr = GST_READ_UINT24_LE (packet->packet);
645 return (((hdr >> 4) & 1) != 0) ? 1 : 0;
649 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
651 guint64 gp = granulepos;
652 guint64 pts = (gp >> 32);
653 guint32 dist = (gp >> 3) & 0x07ffffff;
655 if (granulepos == -1 || granulepos == 0)
665 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
667 if (packet->bytes >= 5 && packet->packet[0] == 0x4F &&
668 packet->packet[1] == 0x56 && packet->packet[2] == 0x50 &&
669 packet->packet[3] == 0x38 && packet->packet[4] == 0x30)
675 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
677 if (packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) {
678 tag_list_from_vorbiscomment_packet (packet,
679 (const guint8 *) "OVP80\2 ", 7, &pad->taglist);
686 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
688 guint8 *data = packet->packet;
692 pad->version = GST_READ_UINT32_LE (data);
694 chans = GST_READ_UINT8 (data);
696 pad->granulerate_n = GST_READ_UINT32_LE (data);
697 pad->granulerate_d = 1;
698 pad->granuleshift = 0;
700 GST_LOG ("sample rate: %d", pad->granulerate_n);
703 pad->bitrate_upper = GST_READ_UINT32_LE (data);
705 pad->bitrate_nominal = GST_READ_UINT32_LE (data);
707 pad->bitrate_lower = GST_READ_UINT32_LE (data);
709 if (pad->bitrate_nominal > 0)
710 pad->bitrate = pad->bitrate_nominal;
712 if (pad->bitrate_upper > 0 && !pad->bitrate)
713 pad->bitrate = pad->bitrate_upper;
715 if (pad->bitrate_lower > 0 && !pad->bitrate)
716 pad->bitrate = pad->bitrate_lower;
718 GST_LOG ("bit rate: %d", pad->bitrate);
720 pad->n_header_packets = 3;
722 if (pad->granulerate_n == 0)
725 parse_vorbis_header_packet (pad, packet);
727 pad->caps = gst_caps_new_simple ("audio/x-vorbis",
728 "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
735 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
737 if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
740 if (packet->packet[0] == 5) {
741 parse_vorbis_setup_packet (pad, packet);
748 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
750 if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
753 if (((guint8 *) (packet->packet))[0] == 0x03) {
754 tag_list_from_vorbiscomment_packet (packet,
755 (const guint8 *) "\003vorbis", 7, &pad->taglist);
758 pad->taglist = gst_tag_list_new ();
760 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
761 GST_TAG_ENCODER_VERSION, pad->version, NULL);
763 if (pad->bitrate_nominal > 0)
764 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
765 GST_TAG_NOMINAL_BITRATE, (guint) pad->bitrate_nominal, NULL);
767 if (pad->bitrate_upper > 0)
768 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
769 GST_TAG_MAXIMUM_BITRATE, (guint) pad->bitrate_upper, NULL);
771 if (pad->bitrate_lower > 0)
772 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
773 GST_TAG_MINIMUM_BITRATE, (guint) pad->bitrate_lower, NULL);
776 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
777 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
782 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
788 if (packet->bytes == 0 || packet->packet[0] & 1)
791 mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
792 size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
794 if (pad->last_size == 0) {
797 duration = pad->last_size / 4 + size / 4;
799 pad->last_size = size;
801 GST_DEBUG ("duration %d", (int) duration);
810 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
812 guint8 *data = packet->packet;
815 data += 8 + 20 + 4 + 4;
816 pad->granulerate_n = GST_READ_UINT32_LE (data);
817 pad->granulerate_d = 1;
818 pad->granuleshift = 0;
821 chans = GST_READ_UINT32_LE (data);
823 pad->bitrate = GST_READ_UINT32_LE (data);
825 GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
826 GST_LOG ("bit rate: %d", pad->bitrate);
828 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
829 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
830 GST_READ_UINT32_LE (packet->packet + 56);
832 if (pad->granulerate_n == 0)
835 pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
836 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
842 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
844 /* packet 2 must be comment packet */
845 if (packet->bytes > 0 && pad->n_header_packets_seen == 1) {
846 tag_list_from_vorbiscomment_packet (packet, NULL, 0, &pad->taglist);
849 pad->taglist = gst_tag_list_new ();
852 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
853 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
861 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
863 pad->granulerate_n = 0;
864 pad->granulerate_d = 1;
865 pad->granuleshift = 0;
867 pad->n_header_packets = 3;
869 pad->caps = gst_caps_new_simple ("audio/x-flac", NULL);
875 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
877 if (pad->n_header_packets_seen == 1) {
878 pad->granulerate_n = (packet->packet[14] << 12) |
879 (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
882 if (pad->n_header_packets_seen < pad->n_header_packets) {
890 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
892 guint8 *data = packet->packet;
895 /* see http://flac.sourceforge.net/ogg_mapping.html */
897 pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
898 pad->granulerate_d = 1;
899 pad->granuleshift = 0;
900 chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
902 GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
904 pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
906 if (pad->granulerate_n == 0)
909 pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
910 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
916 is_header_flac (GstOggStream * pad, ogg_packet * packet)
918 return (packet->bytes > 0 && (packet->packet[0] != 0xff));
922 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
924 int block_size_index;
926 if (packet->bytes < 4)
929 block_size_index = packet->packet[2] >> 4;
930 if (block_size_index == 1)
932 if (block_size_index >= 2 && block_size_index <= 5) {
933 return 576 << (block_size_index - 2);
935 if (block_size_index >= 8) {
936 return 256 << (block_size_index - 8);
938 if (block_size_index == 6 || block_size_index == 7) {
939 guint len, bytes = (block_size_index - 6) + 1;
942 if (packet->bytes < 4 + 1 + bytes)
944 tmp = packet->packet[4];
955 if (packet->bytes < 4 + len + bytes)
958 return packet->packet[4 + len] + 1;
960 return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
967 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
969 if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
970 tag_list_from_vorbiscomment_packet (packet,
971 packet->packet, 4, &pad->taglist);
978 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
981 gint64 prestime_n, prestime_d;
982 gint64 basetime_n, basetime_d;
984 data = packet->packet;
986 data += 8; /* header */
988 pad->skeleton_major = GST_READ_UINT16_LE (data);
990 pad->skeleton_minor = GST_READ_UINT16_LE (data);
993 prestime_n = (gint64) GST_READ_UINT64_LE (data);
995 prestime_d = (gint64) GST_READ_UINT64_LE (data);
997 basetime_n = (gint64) GST_READ_UINT64_LE (data);
999 basetime_d = (gint64) GST_READ_UINT64_LE (data);
1002 /* FIXME: we don't use basetime anywhere in the demuxer! */
1003 if (basetime_d != 0)
1004 pad->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
1008 if (prestime_d != 0)
1009 pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1013 /* Ogg Skeleton 3.3+ streams provide additional information in the header */
1014 if (packet->bytes >= SKELETON_FISHEAD_3_3_MIN_SIZE && pad->skeleton_major == 3
1015 && pad->skeleton_minor > 0) {
1016 gint64 firstsampletime_n, firstsampletime_d;
1017 gint64 lastsampletime_n, lastsampletime_d;
1018 gint64 firstsampletime, lastsampletime;
1019 guint64 segment_length, content_offset;
1021 firstsampletime_n = GST_READ_UINT64_LE (data + 64);
1022 firstsampletime_d = GST_READ_UINT64_LE (data + 72);
1023 lastsampletime_n = GST_READ_UINT64_LE (data + 80);
1024 lastsampletime_d = GST_READ_UINT64_LE (data + 88);
1025 segment_length = GST_READ_UINT64_LE (data + 96);
1026 content_offset = GST_READ_UINT64_LE (data + 104);
1028 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1029 firstsampletime_n, firstsampletime_d);
1030 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1031 lastsampletime_n, lastsampletime_d);
1032 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1033 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1035 if (firstsampletime_d > 0)
1036 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1037 firstsampletime_n, firstsampletime_d);
1039 firstsampletime = 0;
1041 if (lastsampletime_d > 0)
1042 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1043 lastsampletime_n, lastsampletime_d);
1047 if (lastsampletime > firstsampletime)
1048 pad->total_time = lastsampletime - firstsampletime;
1050 pad->total_time = -1;
1052 GST_INFO ("skeleton fishead parsed total: %" GST_TIME_FORMAT,
1053 GST_TIME_ARGS (pad->total_time));
1054 } else if (packet->bytes >= SKELETON_FISHEAD_4_0_MIN_SIZE
1055 && pad->skeleton_major == 4) {
1056 guint64 segment_length, content_offset;
1058 segment_length = GST_READ_UINT64_LE (data + 64);
1059 content_offset = GST_READ_UINT64_LE (data + 72);
1061 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1062 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1064 pad->total_time = -1;
1067 GST_INFO ("skeleton fishead %u.%u parsed (basetime: %" GST_TIME_FORMAT
1068 ", prestime: %" GST_TIME_FORMAT ")", pad->skeleton_major,
1069 pad->skeleton_minor, GST_TIME_ARGS (pad->basetime),
1070 GST_TIME_ARGS (pad->prestime));
1072 pad->is_skeleton = TRUE;
1073 pad->is_sparse = TRUE;
1075 pad->caps = gst_caps_new_simple ("none/none", NULL);
1081 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1082 guint32 * serialno, GstOggSkeleton * type)
1084 GstOggSkeleton stype;
1085 guint serial_offset;
1087 if (size < SKELETON_FISBONE_MIN_SIZE) {
1088 GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1092 if (memcmp (data, "fisbone\0", 8) == 0) {
1093 GST_INFO ("got fisbone packet");
1094 stype = GST_OGG_SKELETON_FISBONE;
1096 } else if (memcmp (data, "index\0", 6) == 0) {
1097 GST_INFO ("got index packet");
1098 stype = GST_OGG_SKELETON_INDEX;
1100 } else if (memcmp (data, "fishead\0", 8) == 0) {
1103 GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1108 *serialno = GST_READ_UINT32_LE (data + serial_offset);
1117 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1118 const guint8 * data, guint size, GstClockTime * p_start_time)
1120 GstClockTime start_time;
1121 gint64 start_granule;
1123 if (pad->have_fisbone) {
1124 GST_DEBUG ("already have fisbone, ignoring second one");
1128 /* skip "fisbone\0" + headers offset + serialno + num headers */
1129 data += 8 + 4 + 4 + 4;
1131 pad->have_fisbone = TRUE;
1133 /* we just overwrite whatever was set before by the format-specific setup */
1134 pad->granulerate_n = GST_READ_UINT64_LE (data);
1135 pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
1137 start_granule = GST_READ_UINT64_LE (data + 16);
1138 pad->preroll = GST_READ_UINT32_LE (data + 24);
1139 pad->granuleshift = GST_READ_UINT8 (data + 28);
1141 start_time = granulepos_to_granule_default (pad, start_granule);
1143 GST_INFO ("skeleton fisbone parsed "
1144 "(start time: %" GST_TIME_FORMAT
1145 " granulerate_n: %d granulerate_d: %d "
1146 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
1147 GST_TIME_ARGS (start_time),
1148 pad->granulerate_n, pad->granulerate_d, pad->preroll, pad->granuleshift);
1151 *p_start_time = start_time;
1157 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1165 if (G_UNLIKELY (*size < 1))
1169 *result |= ((byte & 0x7f) << shift);
1174 } while ((byte & 0x80) != 0x80);
1180 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1181 const guint8 * data, guint size)
1183 guint64 i, n_keypoints, isize;
1184 guint64 offset, timestamp;
1185 guint64 offset_d, timestamp_d;
1188 GST_DEBUG ("already have index, ignoring second one");
1192 if ((skel_pad->skeleton_major == 3 && size < 26) ||
1193 (skel_pad->skeleton_major == 4 && size < 62)) {
1194 GST_WARNING ("small index packet of size %u, ignoring", size);
1198 /* skip "index\0" + serialno */
1202 n_keypoints = GST_READ_UINT64_LE (data);
1207 pad->kp_denom = GST_READ_UINT64_LE (data);
1208 if (pad->kp_denom == 0)
1214 if (skel_pad->skeleton_major == 4) {
1215 gint64 firstsampletime_n;
1216 gint64 lastsampletime_n;
1217 gint64 firstsampletime, lastsampletime;
1219 firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1220 lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1222 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1223 firstsampletime_n, pad->kp_denom);
1224 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1225 lastsampletime_n, pad->kp_denom);
1227 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1228 firstsampletime_n, pad->kp_denom);
1229 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1230 lastsampletime_n, pad->kp_denom);
1232 if (lastsampletime > firstsampletime)
1233 pad->total_time = lastsampletime - firstsampletime;
1235 pad->total_time = -1;
1237 GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1238 GST_TIME_ARGS (pad->total_time));
1244 GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1245 G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1247 pad->index = g_try_new (GstOggIndex, n_keypoints);
1255 for (i = 0; i < n_keypoints; i++) {
1257 if (!read_vlc (&data, &size, &offset_d))
1259 if (!read_vlc (&data, &size, ×tamp_d))
1263 timestamp += timestamp_d;
1265 pad->index[i].offset = offset;
1266 pad->index[i].timestamp = timestamp;
1269 GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1272 if (isize != n_keypoints) {
1273 GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1274 G_GUINT64_FORMAT, n_keypoints, isize);
1276 pad->n_index = isize;
1277 /* try to use the index to estimate the bitrate */
1279 guint64 so, eo, st, et, b, t;
1281 /* get start and end offset and timestamps */
1282 so = pad->index[0].offset;
1283 st = pad->index[0].timestamp;
1284 eo = pad->index[isize - 1].offset;
1285 et = pad->index[isize - 1].timestamp;
1290 GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1292 /* this is the total stream bitrate according to this index */
1293 pad->idx_bitrate = gst_util_uint64_scale (8 * b, pad->kp_denom, t);
1295 GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1302 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1305 if (index->timestamp < *ts)
1307 else if (index->timestamp > *ts)
1314 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1315 guint64 * timestamp, guint64 * offset)
1321 n_index = pad->n_index;
1322 if (n_index == 0 || pad->index == NULL)
1325 ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1326 GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1329 gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1330 (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1336 GST_INFO ("found at index %u", (guint) (best - pad->index));
1339 *offset = best->offset;
1342 gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1347 /* Do we need these for something?
1348 * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
1349 * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
1350 * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
1351 * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
1352 * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
1353 * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
1357 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1359 if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1366 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1368 if (!(packet->packet[0] & 1) && (packet->packet[0] & 3 && pad->is_ogm_text)) {
1369 tag_list_from_vorbiscomment_packet (packet,
1370 (const guint8 *) "\003vorbis", 7, &pad->taglist);
1375 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1382 data = packet->packet;
1383 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1385 if (offset > packet->bytes) {
1386 GST_ERROR ("buffer too small");
1391 for (n = offset - 1; n > 0; n--) {
1392 samples = (samples << 8) | data[n];
1399 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1401 guint8 *data = packet->packet;
1404 pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1405 pad->granulerate_d = 1;
1407 fourcc = GST_READ_UINT32_LE (data + 9);
1408 GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1410 pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1412 GST_LOG ("sample rate: %d", pad->granulerate_n);
1413 if (pad->granulerate_n == 0)
1417 gst_caps_set_simple (pad->caps,
1418 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1420 pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
1421 "fourcc", GST_TYPE_FOURCC, fourcc,
1422 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1425 pad->n_header_packets = 1;
1432 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1434 guint8 *data = packet->packet;
1439 GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
1440 GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
1442 pad->is_video = TRUE;
1443 pad->granulerate_n = 10000000;
1444 time_unit = GST_READ_UINT64_LE (data + 17);
1445 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1446 GST_WARNING ("timeunit is out of range");
1448 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1450 GST_LOG ("fps = %d/%d = %.3f",
1451 pad->granulerate_n, pad->granulerate_d,
1452 (double) pad->granulerate_n / pad->granulerate_d);
1454 fourcc = GST_READ_UINT32_LE (data + 9);
1455 width = GST_READ_UINT32_LE (data + 45);
1456 height = GST_READ_UINT32_LE (data + 49);
1457 GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1459 pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1461 if (pad->caps == NULL) {
1462 pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
1463 "fourcc", GST_TYPE_FOURCC, fourcc,
1464 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1465 pad->granulerate_d, NULL);
1467 gst_caps_set_simple (pad->caps,
1468 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1470 "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
1472 GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
1474 pad->n_header_packets = 1;
1475 pad->frame_size = 1;
1482 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1484 guint8 *data = packet->packet;
1487 pad->granulerate_n = 10000000;
1488 time_unit = GST_READ_UINT64_LE (data + 17);
1489 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1490 GST_WARNING ("timeunit is out of range");
1492 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1494 GST_LOG ("fps = %d/%d = %.3f",
1495 pad->granulerate_n, pad->granulerate_d,
1496 (double) pad->granulerate_n / pad->granulerate_d);
1498 if (pad->granulerate_d <= 0)
1501 pad->caps = gst_caps_new_simple ("text/plain", NULL);
1503 pad->n_header_packets = 1;
1505 pad->is_ogm_text = TRUE;
1506 pad->is_sparse = TRUE;
1513 #define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */
1514 #define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */
1515 #define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */
1516 #define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */
1517 #define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */
1518 #define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */
1519 #define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */
1520 #define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */
1522 #define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */
1523 #define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */
1525 #define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */
1526 #define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */
1527 #define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */
1528 #define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */
1532 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1534 guint8 *data = packet->packet;
1539 pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1540 pad->granulerate_d = 1;
1541 GST_LOG ("sample rate: %d", pad->granulerate_n);
1543 format = GST_READ_UINT32_LE (data + 12);
1544 channels = GST_READ_UINT8 (data + 21);
1546 pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1548 if (pad->granulerate_n == 0)
1553 caps = gst_caps_new_simple ("audio/x-raw-int",
1554 "depth", G_TYPE_INT, 8,
1555 "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1558 caps = gst_caps_new_simple ("audio/x-raw-int",
1559 "depth", G_TYPE_INT, 8,
1560 "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
1562 case OGGPCM_FMT_S16_LE:
1563 caps = gst_caps_new_simple ("audio/x-raw-int",
1564 "depth", G_TYPE_INT, 16,
1565 "width", G_TYPE_INT, 16,
1566 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1567 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1569 case OGGPCM_FMT_S16_BE:
1570 caps = gst_caps_new_simple ("audio/x-raw-int",
1571 "depth", G_TYPE_INT, 16,
1572 "width", G_TYPE_INT, 16,
1573 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1574 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1576 case OGGPCM_FMT_S24_LE:
1577 caps = gst_caps_new_simple ("audio/x-raw-int",
1578 "depth", G_TYPE_INT, 24,
1579 "width", G_TYPE_INT, 24,
1580 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1581 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1583 case OGGPCM_FMT_S24_BE:
1584 caps = gst_caps_new_simple ("audio/x-raw-int",
1585 "depth", G_TYPE_INT, 24,
1586 "width", G_TYPE_INT, 24,
1587 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1588 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1590 case OGGPCM_FMT_S32_LE:
1591 caps = gst_caps_new_simple ("audio/x-raw-int",
1592 "depth", G_TYPE_INT, 32,
1593 "width", G_TYPE_INT, 32,
1594 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1595 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1597 case OGGPCM_FMT_S32_BE:
1598 caps = gst_caps_new_simple ("audio/x-raw-int",
1599 "depth", G_TYPE_INT, 32,
1600 "width", G_TYPE_INT, 32,
1601 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1602 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1604 case OGGPCM_FMT_ULAW:
1605 caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
1607 case OGGPCM_FMT_ALAW:
1608 caps = gst_caps_new_simple ("audio/x-alaw", NULL);
1610 case OGGPCM_FMT_FLT32_LE:
1611 caps = gst_caps_new_simple ("audio/x-raw-float",
1612 "width", G_TYPE_INT, 32,
1613 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1615 case OGGPCM_FMT_FLT32_BE:
1616 caps = gst_caps_new_simple ("audio/x-raw-float",
1617 "width", G_TYPE_INT, 32,
1618 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1620 case OGGPCM_FMT_FLT64_LE:
1621 caps = gst_caps_new_simple ("audio/x-raw-float",
1622 "width", G_TYPE_INT, 64,
1623 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1625 case OGGPCM_FMT_FLT64_BE:
1626 caps = gst_caps_new_simple ("audio/x-raw-float",
1627 "width", G_TYPE_INT, 64,
1628 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1634 gst_caps_set_simple (caps, "audio/x-raw-int",
1635 "rate", G_TYPE_INT, pad->granulerate_n,
1636 "channels", G_TYPE_INT, channels, NULL);
1645 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1647 guint8 *data = packet->packet;
1649 pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1650 pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1651 pad->granuleshift = data[28];
1652 GST_LOG ("sample rate: %d", pad->granulerate_n);
1654 pad->n_header_packets = 3;
1656 if (pad->granulerate_n == 0)
1659 data += 4 + (4 + 4 + 4);
1660 GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1661 GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1663 pad->caps = gst_caps_new_simple ("text/x-cmml", NULL);
1664 pad->is_sparse = TRUE;
1672 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1674 guint8 *data = packet->packet;
1676 pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1677 pad->granulerate_d = 1;
1678 pad->granuleshift = 0;
1679 GST_LOG ("sample rate: %d", pad->granulerate_n);
1681 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1682 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1684 if (pad->granulerate_n == 0)
1687 pad->caps = gst_caps_new_simple ("audio/x-celt",
1688 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1696 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1698 guint8 *data = packet->packet;
1699 const char *category;
1701 if (packet->bytes < 64)
1704 pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1705 pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1706 pad->granuleshift = GST_READ_UINT8 (data + 15);
1707 GST_LOG ("sample rate: %d", pad->granulerate_n);
1709 pad->n_header_packets = GST_READ_UINT8 (data + 11);
1710 GST_LOG ("kate header packets: %d", pad->n_header_packets);
1712 if (pad->granulerate_n == 0)
1715 category = (const char *) data + 48;
1716 if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1717 strcmp (category, "spu-subtitles") == 0 ||
1718 strcmp (category, "K-SPU") == 0) {
1719 pad->caps = gst_caps_new_simple ("subtitle/x-kate", NULL);
1721 pad->caps = gst_caps_new_simple ("application/x-kate", NULL);
1724 pad->is_sparse = TRUE;
1730 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1734 if (packet->bytes < 1)
1737 switch (packet->packet[0]) {
1738 case 0x00: /* text data */
1739 if (packet->bytes < 1 + 8 * 2) {
1742 duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
1748 duration = GST_CLOCK_TIME_NONE;
1756 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
1758 GstTagList *list = NULL;
1760 if (packet->bytes <= 0)
1763 switch (packet->packet[0]) {
1765 const gchar *canonical;
1768 if (packet->bytes < 64) {
1769 GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
1773 /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
1774 memcpy (language, packet->packet + 32, 16);
1777 /* language is an ISO 639-1 code or RFC 3066 language code, we
1778 * truncate to ISO 639-1 */
1779 g_strdelimit (language, NULL, '\0');
1780 canonical = gst_tag_get_language_code_iso_639_1 (language);
1782 list = gst_tag_list_new_full (GST_TAG_LANGUAGE_CODE, canonical, NULL);
1784 GST_WARNING ("Unknown or invalid language code %s, ignored", language);
1789 tag_list_from_vorbiscomment_packet (packet,
1790 (const guint8 *) "\201kate\0\0\0\0", 9, &list);
1798 /* ensure the comment packet cannot override the category/language
1799 from the identification header */
1800 gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL);
1802 pad->taglist = list;
1808 /* indent hates our freedoms */
1809 const GstOggMap mappers[] = {
1811 "\200theora", 7, 42,
1813 setup_theora_mapper,
1814 granulepos_to_granule_theora,
1815 granule_to_granulepos_default,
1818 packet_duration_constant,
1823 "\001vorbis", 7, 22,
1825 setup_vorbis_mapper,
1826 granulepos_to_granule_default,
1827 granule_to_granulepos_default,
1830 packet_duration_vorbis,
1838 granulepos_to_granule_default,
1839 granule_to_granulepos_default,
1842 packet_duration_constant,
1859 "CMML\0\0\0\0", 8, 0,
1872 "application/x-annodex",
1873 setup_fishead_mapper,
1874 granulepos_to_granule_default,
1875 granule_to_granulepos_default,
1884 "application/octet-stream",
1885 setup_fishead_mapper,
1898 granulepos_to_granule_default,
1899 granule_to_granulepos_default,
1902 packet_duration_flac,
1910 granulepos_to_granule_default,
1911 granule_to_granulepos_default,
1914 packet_duration_flac,
1920 "application/octet-stream",
1933 granulepos_to_granule_default,
1934 granule_to_granulepos_default,
1937 packet_duration_constant,
1942 "\200kate\0\0\0", 8, 0,
1945 granulepos_to_granule_default,
1946 granule_to_granulepos_default,
1949 packet_duration_kate,
1957 granulepos_to_granule_dirac,
1958 granule_to_granulepos_dirac,
1961 packet_duration_constant,
1962 granulepos_to_key_granule_dirac,
1969 granulepos_to_granule_vp8,
1970 granule_to_granulepos_vp8,
1973 packet_duration_vp8,
1974 granulepos_to_key_granule_vp8,
1978 "\001audio\0\0\0", 9, 53,
1979 "application/x-ogm-audio",
1980 setup_ogmaudio_mapper,
1981 granulepos_to_granule_default,
1982 granule_to_granulepos_default,
1985 packet_duration_ogm,
1990 "\001video\0\0\0", 9, 53,
1991 "application/x-ogm-video",
1992 setup_ogmvideo_mapper,
1993 granulepos_to_granule_default,
1994 granule_to_granulepos_default,
1997 packet_duration_constant,
2002 "\001text\0\0\0", 9, 9,
2003 "application/x-ogm-text",
2004 setup_ogmtext_mapper,
2005 granulepos_to_granule_default,
2006 granule_to_granulepos_default,
2009 packet_duration_ogm,
2017 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2022 for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2023 if (packet->bytes >= mappers[i].min_packet_size &&
2024 packet->bytes >= mappers[i].id_length &&
2025 memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
2027 GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2029 if (mappers[i].setup_func)
2030 ret = mappers[i].setup_func (pad, packet);
2035 GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2039 GST_WARNING ("mapper '%s' did not accept setup header",
2040 mappers[i].media_type);
2049 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2050 const GstCaps * caps)
2053 const GstStructure *structure;
2054 const GValue *streamheader;
2055 const GValue *first_element;
2061 GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2066 structure = gst_caps_get_structure (caps, 0);
2067 streamheader = gst_structure_get_value (structure, "streamheader");
2069 if (streamheader == NULL) {
2070 GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2074 if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2075 GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2079 if (gst_value_array_get_size (streamheader) == 0) {
2080 GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2084 first_element = gst_value_array_get_value (streamheader, 0);
2086 if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2087 GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2091 buf = gst_value_get_buffer (first_element);
2093 GST_ERROR ("no first streamheader buffer");
2097 data = gst_buffer_map (buf, &size, 0, GST_MAP_READ);
2098 if (data == NULL || size == 0) {
2099 GST_ERROR ("invalid first streamheader buffer");
2103 GST_MEMDUMP ("streamheader", data, size);
2105 packet.packet = data;
2106 packet.bytes = size;
2108 GST_INFO ("Found headers on caps, using those to determine type");
2109 ret = gst_ogg_stream_setup_map (pad, &packet);
2111 gst_buffer_unmap (buf, data, size);