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 GstBuffer *buf = NULL;
307 gchar *encoder = NULL;
311 g_return_val_if_fail (tags != NULL, FALSE);
313 buf = gst_buffer_new ();
314 GST_BUFFER_DATA (buf) = (guint8 *) packet->packet;
315 GST_BUFFER_SIZE (buf) = packet->bytes;
317 list = gst_tag_list_from_vorbiscomment_buffer (buf, id_data, id_data_length,
321 GST_WARNING ("failed to decode vorbis comments");
328 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
335 gst_tag_list_free (*tags);
338 gst_buffer_unref (buf);
346 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
348 guint8 *data = packet->packet;
349 guint w, h, par_d, par_n;
350 guint8 vmaj, vmin, vrev;
356 w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
357 h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
359 pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
360 pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
362 par_n = GST_READ_UINT24_BE (data + 30);
363 par_d = GST_READ_UINT24_BE (data + 33);
365 GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
366 pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
368 /* 2 bits + 3 bits = 5 bits KFGSHIFT */
369 pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
370 (GST_READ_UINT8 (data + 41) >> 5);
372 pad->is_video = TRUE;
373 pad->n_header_packets = 3;
376 pad->bitrate = GST_READ_UINT24_BE (data + 37);
377 GST_LOG ("bit rate: %d", pad->bitrate);
379 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
380 GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
384 /* The interpretation of the granule position has changed with 3.2.1.
385 The granule is now made from the number of frames encoded, rather than
386 the index of the frame being encoded - so there is a difference of 1. */
387 pad->theora_has_zero_keyoffset =
388 ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201;
390 pad->caps = gst_caps_new_simple ("video/x-theora", NULL);
392 if (w > 0 && h > 0) {
393 gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
394 G_TYPE_INT, h, NULL);
397 /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
398 if (par_n == 0 || par_d == 0)
401 /* only add framerate now so caps look prettier, with width/height first */
402 gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
403 pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
404 GST_TYPE_FRACTION, par_n, par_d, NULL);
410 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
412 gint64 keyindex, keyoffset;
414 if (pad->granuleshift != 0) {
415 keyindex = granulepos >> pad->granuleshift;
416 keyoffset = granulepos - (keyindex << pad->granuleshift);
417 if (pad->theora_has_zero_keyoffset) {
420 return keyindex + keyoffset;
427 is_keyframe_theora (GstOggStream * pad, gint64 granulepos)
431 if (granulepos == (gint64) - 1)
434 frame_mask = (1 << pad->granuleshift) - 1;
436 return ((granulepos & frame_mask) == 0);
440 is_header_theora (GstOggStream * pad, ogg_packet * packet)
442 return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
446 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
448 if (packet->bytes > 0 && packet->packet[0] == 0x81) {
449 tag_list_from_vorbiscomment_packet (packet,
450 (const guint8 *) "\201theora", 7, &pad->taglist);
453 pad->taglist = gst_tag_list_new ();
456 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
457 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
464 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
467 DiracSequenceHeader header;
469 ret = dirac_sequence_header_parse (&header, packet->packet + 13,
472 GST_DEBUG ("Failed to parse Dirac sequence header");
476 pad->is_video = TRUE;
477 pad->granulerate_n = header.frame_rate_numerator * 2;
478 pad->granulerate_d = header.frame_rate_denominator;
479 pad->granuleshift = 22;
480 pad->n_header_packets = 1;
483 if (header.interlaced_coding != 0) {
484 GST_DEBUG ("non-progressive Dirac coding not implemented");
488 pad->caps = gst_caps_new_simple ("video/x-dirac",
489 "width", G_TYPE_INT, header.width,
490 "height", G_TYPE_INT, header.height,
491 "interlaced", G_TYPE_BOOLEAN, header.interlaced,
492 "pixel-aspect-ratio", GST_TYPE_FRACTION,
493 header.aspect_ratio_numerator, header.aspect_ratio_denominator,
494 "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
495 header.frame_rate_denominator, NULL);
500 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
502 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
508 if (granulepos == -1)
511 dist_h = (granulepos >> 22) & 0xff;
512 dist_l = granulepos & 0xff;
513 dist = (dist_h << 8) | dist_l;
519 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
525 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
526 delay = (gp >> 9) & 0x1fff;
529 GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
535 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
536 gint64 keyframe_granule)
538 /* This conversion requires knowing more details about the Dirac
544 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
553 if (gp == -1 || gp == 0)
556 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
557 dist_h = (gp >> 22) & 0xff;
559 dist = (dist_h << 8) | dist_l;
560 delay = (gp >> 9) & 0x1fff;
563 return dt - 2 * dist + 4;
569 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
571 gint width, height, par_n, par_d, fps_n, fps_d;
573 if (packet->bytes < 26) {
574 GST_DEBUG ("Failed to parse VP8 BOS page");
578 width = GST_READ_UINT16_BE (packet->packet + 8);
579 height = GST_READ_UINT16_BE (packet->packet + 10);
580 par_n = GST_READ_UINT24_BE (packet->packet + 12);
581 par_d = GST_READ_UINT24_BE (packet->packet + 15);
582 fps_n = GST_READ_UINT32_BE (packet->packet + 18);
583 fps_d = GST_READ_UINT32_BE (packet->packet + 22);
585 pad->is_video = TRUE;
587 pad->granulerate_n = fps_n;
588 pad->granulerate_d = fps_d;
589 pad->n_header_packets = 2;
592 pad->caps = gst_caps_new_simple ("video/x-vp8",
593 "width", G_TYPE_INT, width,
594 "height", G_TYPE_INT, height,
595 "pixel-aspect-ratio", GST_TYPE_FRACTION,
596 par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
602 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
604 guint64 gpos = granulepos;
606 if (granulepos == -1)
609 /* Get rid of flags */
612 return ((gpos & 0x07ffffff) == 0);
616 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
618 guint64 gp = (guint64) gpos;
623 dist = (gp >> 3) & 0x07ffffff;
625 GST_DEBUG ("pt %u, dist %u", pt, dist);
631 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
632 gint64 keyframe_granule)
634 /* FIXME: This requires to look into the content of the packets
635 * because the simple granule counter doesn't know about invisible
641 /* Check if this packet contains an invisible frame or not */
643 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
647 if (packet->bytes < 3)
650 hdr = GST_READ_UINT24_LE (packet->packet);
652 return (((hdr >> 4) & 1) != 0) ? 1 : 0;
656 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
658 guint64 gp = granulepos;
659 guint64 pts = (gp >> 32);
660 guint32 dist = (gp >> 3) & 0x07ffffff;
662 if (granulepos == -1 || granulepos == 0)
672 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
674 if (packet->bytes >= 5 && packet->packet[0] == 0x4F &&
675 packet->packet[1] == 0x56 && packet->packet[2] == 0x50 &&
676 packet->packet[3] == 0x38 && packet->packet[4] == 0x30)
682 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
684 if (packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) {
685 tag_list_from_vorbiscomment_packet (packet,
686 (const guint8 *) "OVP80\2 ", 7, &pad->taglist);
693 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
695 guint8 *data = packet->packet;
699 pad->version = GST_READ_UINT32_LE (data);
701 chans = GST_READ_UINT8 (data);
703 pad->granulerate_n = GST_READ_UINT32_LE (data);
704 pad->granulerate_d = 1;
705 pad->granuleshift = 0;
707 GST_LOG ("sample rate: %d", pad->granulerate_n);
710 pad->bitrate_upper = GST_READ_UINT32_LE (data);
712 pad->bitrate_nominal = GST_READ_UINT32_LE (data);
714 pad->bitrate_lower = GST_READ_UINT32_LE (data);
716 if (pad->bitrate_nominal > 0)
717 pad->bitrate = pad->bitrate_nominal;
719 if (pad->bitrate_upper > 0 && !pad->bitrate)
720 pad->bitrate = pad->bitrate_upper;
722 if (pad->bitrate_lower > 0 && !pad->bitrate)
723 pad->bitrate = pad->bitrate_lower;
725 GST_LOG ("bit rate: %d", pad->bitrate);
727 pad->n_header_packets = 3;
729 if (pad->granulerate_n == 0)
732 parse_vorbis_header_packet (pad, packet);
734 pad->caps = gst_caps_new_simple ("audio/x-vorbis",
735 "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
742 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
744 if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
747 if (packet->packet[0] == 5) {
748 parse_vorbis_setup_packet (pad, packet);
755 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
757 if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
760 if (((guint8 *) (packet->packet))[0] == 0x03) {
761 tag_list_from_vorbiscomment_packet (packet,
762 (const guint8 *) "\003vorbis", 7, &pad->taglist);
765 pad->taglist = gst_tag_list_new ();
767 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
768 GST_TAG_ENCODER_VERSION, pad->version, NULL);
770 if (pad->bitrate_nominal > 0)
771 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
772 GST_TAG_NOMINAL_BITRATE, (guint) pad->bitrate_nominal, NULL);
774 if (pad->bitrate_upper > 0)
775 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
776 GST_TAG_MAXIMUM_BITRATE, (guint) pad->bitrate_upper, NULL);
778 if (pad->bitrate_lower > 0)
779 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
780 GST_TAG_MINIMUM_BITRATE, (guint) pad->bitrate_lower, NULL);
783 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
784 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
789 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
795 if (packet->packet[0] & 1)
798 mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
799 size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
801 if (pad->last_size == 0) {
804 duration = pad->last_size / 4 + size / 4;
806 pad->last_size = size;
808 GST_DEBUG ("duration %d", (int) duration);
817 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
819 guint8 *data = packet->packet;
822 data += 8 + 20 + 4 + 4;
823 pad->granulerate_n = GST_READ_UINT32_LE (data);
824 pad->granulerate_d = 1;
825 pad->granuleshift = 0;
828 chans = GST_READ_UINT32_LE (data);
830 pad->bitrate = GST_READ_UINT32_LE (data);
832 GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
833 GST_LOG ("bit rate: %d", pad->bitrate);
835 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
836 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
837 GST_READ_UINT32_LE (packet->packet + 56);
839 if (pad->granulerate_n == 0)
842 pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
843 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
849 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
851 /* packet 2 must be comment packet */
852 if (packet->bytes > 0 && pad->n_header_packets_seen == 1) {
853 tag_list_from_vorbiscomment_packet (packet, NULL, 0, &pad->taglist);
856 pad->taglist = gst_tag_list_new ();
859 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
860 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
868 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
870 pad->granulerate_n = 0;
871 pad->granulerate_d = 1;
872 pad->granuleshift = 0;
874 pad->n_header_packets = 3;
876 pad->caps = gst_caps_new_simple ("audio/x-flac", NULL);
882 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
884 if (pad->n_header_packets_seen == 1) {
885 pad->granulerate_n = (packet->packet[14] << 12) |
886 (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
889 if (pad->n_header_packets_seen < pad->n_header_packets) {
897 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
899 guint8 *data = packet->packet;
902 /* see http://flac.sourceforge.net/ogg_mapping.html */
904 pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
905 pad->granulerate_d = 1;
906 pad->granuleshift = 0;
907 chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
909 GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
911 pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
913 if (pad->granulerate_n == 0)
916 pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
917 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
923 is_header_flac (GstOggStream * pad, ogg_packet * packet)
925 return (packet->bytes > 0 && (packet->packet[0] != 0xff));
929 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
931 int block_size_index;
933 if (packet->bytes < 4)
936 block_size_index = packet->packet[2] >> 4;
937 if (block_size_index == 1)
939 if (block_size_index >= 2 && block_size_index <= 5) {
940 return 576 << (block_size_index - 2);
942 if (block_size_index >= 8) {
943 return 256 << (block_size_index - 8);
945 if (block_size_index == 6 || block_size_index == 7) {
946 guint len, bytes = (block_size_index - 6) + 1;
949 if (packet->bytes < 4 + 1 + bytes)
951 tmp = packet->packet[4];
962 if (packet->bytes < 4 + len + bytes)
965 return packet->packet[4 + len] + 1;
967 return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
974 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
976 if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
977 tag_list_from_vorbiscomment_packet (packet,
978 packet->packet, 4, &pad->taglist);
985 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
988 gint64 prestime_n, prestime_d;
989 gint64 basetime_n, basetime_d;
991 data = packet->packet;
993 data += 8; /* header */
995 pad->skeleton_major = GST_READ_UINT16_LE (data);
997 pad->skeleton_minor = GST_READ_UINT16_LE (data);
1000 prestime_n = (gint64) GST_READ_UINT64_LE (data);
1002 prestime_d = (gint64) GST_READ_UINT64_LE (data);
1004 basetime_n = (gint64) GST_READ_UINT64_LE (data);
1006 basetime_d = (gint64) GST_READ_UINT64_LE (data);
1009 /* FIXME: we don't use basetime anywhere in the demuxer! */
1010 if (basetime_d != 0)
1011 pad->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
1015 if (prestime_d != 0)
1016 pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1020 /* Ogg Skeleton 3.3+ streams provide additional information in the header */
1021 if (packet->bytes >= SKELETON_FISHEAD_3_3_MIN_SIZE && pad->skeleton_major == 3
1022 && pad->skeleton_minor > 0) {
1023 gint64 firstsampletime_n, firstsampletime_d;
1024 gint64 lastsampletime_n, lastsampletime_d;
1025 gint64 firstsampletime, lastsampletime;
1026 guint64 segment_length, content_offset;
1028 firstsampletime_n = GST_READ_UINT64_LE (data + 64);
1029 firstsampletime_d = GST_READ_UINT64_LE (data + 72);
1030 lastsampletime_n = GST_READ_UINT64_LE (data + 80);
1031 lastsampletime_d = GST_READ_UINT64_LE (data + 88);
1032 segment_length = GST_READ_UINT64_LE (data + 96);
1033 content_offset = GST_READ_UINT64_LE (data + 104);
1035 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1036 firstsampletime_n, firstsampletime_d);
1037 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1038 lastsampletime_n, lastsampletime_d);
1039 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1040 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1042 if (firstsampletime_d > 0)
1043 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1044 firstsampletime_n, firstsampletime_d);
1046 firstsampletime = 0;
1048 if (lastsampletime_d > 0)
1049 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1050 lastsampletime_n, lastsampletime_d);
1054 if (lastsampletime > firstsampletime)
1055 pad->total_time = lastsampletime - firstsampletime;
1057 pad->total_time = -1;
1059 GST_INFO ("skeleton fishead parsed total: %" GST_TIME_FORMAT,
1060 GST_TIME_ARGS (pad->total_time));
1061 } else if (packet->bytes >= SKELETON_FISHEAD_4_0_MIN_SIZE
1062 && pad->skeleton_major == 4) {
1063 guint64 segment_length, content_offset;
1065 segment_length = GST_READ_UINT64_LE (data + 64);
1066 content_offset = GST_READ_UINT64_LE (data + 72);
1068 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1069 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1071 pad->total_time = -1;
1074 GST_INFO ("skeleton fishead %u.%u parsed (basetime: %" GST_TIME_FORMAT
1075 ", prestime: %" GST_TIME_FORMAT ")", pad->skeleton_major,
1076 pad->skeleton_minor, GST_TIME_ARGS (pad->basetime),
1077 GST_TIME_ARGS (pad->prestime));
1079 pad->is_skeleton = TRUE;
1080 pad->is_sparse = TRUE;
1082 pad->caps = gst_caps_new_simple ("none/none", NULL);
1088 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1089 guint32 * serialno, GstOggSkeleton * type)
1091 GstOggSkeleton stype;
1092 guint serial_offset;
1094 if (size < SKELETON_FISBONE_MIN_SIZE) {
1095 GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1099 if (memcmp (data, "fisbone\0", 8) == 0) {
1100 GST_INFO ("got fisbone packet");
1101 stype = GST_OGG_SKELETON_FISBONE;
1103 } else if (memcmp (data, "index\0", 6) == 0) {
1104 GST_INFO ("got index packet");
1105 stype = GST_OGG_SKELETON_INDEX;
1107 } else if (memcmp (data, "fishead\0", 8) == 0) {
1110 GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1115 *serialno = GST_READ_UINT32_LE (data + serial_offset);
1124 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1125 const guint8 * data, guint size, GstClockTime * p_start_time)
1127 GstClockTime start_time;
1128 gint64 start_granule;
1130 if (pad->have_fisbone) {
1131 GST_DEBUG ("already have fisbone, ignoring second one");
1135 /* skip "fisbone\0" + headers offset + serialno + num headers */
1136 data += 8 + 4 + 4 + 4;
1138 pad->have_fisbone = TRUE;
1140 /* we just overwrite whatever was set before by the format-specific setup */
1141 pad->granulerate_n = GST_READ_UINT64_LE (data);
1142 pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
1144 start_granule = GST_READ_UINT64_LE (data + 16);
1145 pad->preroll = GST_READ_UINT32_LE (data + 24);
1146 pad->granuleshift = GST_READ_UINT8 (data + 28);
1148 start_time = granulepos_to_granule_default (pad, start_granule);
1150 GST_INFO ("skeleton fisbone parsed "
1151 "(start time: %" GST_TIME_FORMAT
1152 " granulerate_n: %d granulerate_d: %d "
1153 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
1154 GST_TIME_ARGS (start_time),
1155 pad->granulerate_n, pad->granulerate_d, pad->preroll, pad->granuleshift);
1158 *p_start_time = start_time;
1164 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1172 if (G_UNLIKELY (*size < 1))
1176 *result |= ((byte & 0x7f) << shift);
1181 } while ((byte & 0x80) != 0x80);
1187 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1188 const guint8 * data, guint size)
1190 guint64 i, n_keypoints, isize;
1191 guint64 offset, timestamp;
1192 guint64 offset_d, timestamp_d;
1195 GST_DEBUG ("already have index, ignoring second one");
1199 if ((skel_pad->skeleton_major == 3 && size < 26) ||
1200 (skel_pad->skeleton_major == 4 && size < 62)) {
1201 GST_WARNING ("small index packet of size %u, ignoring", size);
1205 /* skip "index\0" + serialno */
1209 n_keypoints = GST_READ_UINT64_LE (data);
1214 pad->kp_denom = GST_READ_UINT64_LE (data);
1215 if (pad->kp_denom == 0)
1221 if (skel_pad->skeleton_major == 4) {
1222 gint64 firstsampletime_n;
1223 gint64 lastsampletime_n;
1224 gint64 firstsampletime, lastsampletime;
1226 firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1227 lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1229 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1230 firstsampletime_n, pad->kp_denom);
1231 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1232 lastsampletime_n, pad->kp_denom);
1234 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1235 firstsampletime_n, pad->kp_denom);
1236 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1237 lastsampletime_n, pad->kp_denom);
1239 if (lastsampletime > firstsampletime)
1240 pad->total_time = lastsampletime - firstsampletime;
1242 pad->total_time = -1;
1244 GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1245 GST_TIME_ARGS (pad->total_time));
1251 GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1252 G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1254 pad->index = g_try_new (GstOggIndex, n_keypoints);
1262 for (i = 0; i < n_keypoints; i++) {
1264 if (!read_vlc (&data, &size, &offset_d))
1266 if (!read_vlc (&data, &size, ×tamp_d))
1270 timestamp += timestamp_d;
1272 pad->index[i].offset = offset;
1273 pad->index[i].timestamp = timestamp;
1276 GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1279 if (isize != n_keypoints) {
1280 GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1281 G_GUINT64_FORMAT, n_keypoints, isize);
1283 pad->n_index = isize;
1284 /* try to use the index to estimate the bitrate */
1286 guint64 so, eo, st, et, b, t;
1288 /* get start and end offset and timestamps */
1289 so = pad->index[0].offset;
1290 st = pad->index[0].timestamp;
1291 eo = pad->index[isize - 1].offset;
1292 et = pad->index[isize - 1].timestamp;
1297 GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1299 /* this is the total stream bitrate according to this index */
1300 pad->idx_bitrate = gst_util_uint64_scale (8 * b, pad->kp_denom, t);
1302 GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1309 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1312 if (index->timestamp < *ts)
1314 else if (index->timestamp > *ts)
1321 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1322 guint64 * timestamp, guint64 * offset)
1328 n_index = pad->n_index;
1329 if (n_index == 0 || pad->index == NULL)
1332 ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1333 GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1336 gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1337 (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1343 GST_INFO ("found at index %u", (guint) (best - pad->index));
1346 *offset = best->offset;
1349 gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1354 /* Do we need these for something?
1355 * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
1356 * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
1357 * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
1358 * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
1359 * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
1360 * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
1364 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1366 if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1373 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1375 if (!(packet->packet[0] & 1) && (packet->packet[0] & 3 && pad->is_ogm_text)) {
1376 tag_list_from_vorbiscomment_packet (packet,
1377 (const guint8 *) "\003vorbis", 7, &pad->taglist);
1382 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1389 data = packet->packet;
1390 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1392 if (offset > packet->bytes) {
1393 GST_ERROR ("buffer too small");
1398 for (n = offset - 1; n > 0; n--) {
1399 samples = (samples << 8) | data[n];
1406 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1408 guint8 *data = packet->packet;
1411 pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1412 pad->granulerate_d = 1;
1414 fourcc = GST_READ_UINT32_LE (data + 9);
1415 GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1417 pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1419 GST_LOG ("sample rate: %d", pad->granulerate_n);
1420 if (pad->granulerate_n == 0)
1424 gst_caps_set_simple (pad->caps,
1425 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1427 pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
1428 "fourcc", GST_TYPE_FOURCC, fourcc,
1429 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1432 pad->n_header_packets = 1;
1439 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1441 guint8 *data = packet->packet;
1446 GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
1447 GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
1449 pad->is_video = TRUE;
1450 pad->granulerate_n = 10000000;
1451 time_unit = GST_READ_UINT64_LE (data + 17);
1452 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1453 GST_WARNING ("timeunit is out of range");
1455 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1457 GST_LOG ("fps = %d/%d = %.3f",
1458 pad->granulerate_n, pad->granulerate_d,
1459 (double) pad->granulerate_n / pad->granulerate_d);
1461 fourcc = GST_READ_UINT32_LE (data + 9);
1462 width = GST_READ_UINT32_LE (data + 45);
1463 height = GST_READ_UINT32_LE (data + 49);
1464 GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1466 pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1468 if (pad->caps == NULL) {
1469 pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
1470 "fourcc", GST_TYPE_FOURCC, fourcc,
1471 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1472 pad->granulerate_d, NULL);
1474 gst_caps_set_simple (pad->caps,
1475 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1477 "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
1479 GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
1481 pad->n_header_packets = 1;
1482 pad->frame_size = 1;
1489 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1491 guint8 *data = packet->packet;
1494 pad->granulerate_n = 10000000;
1495 time_unit = GST_READ_UINT64_LE (data + 17);
1496 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1497 GST_WARNING ("timeunit is out of range");
1499 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1501 GST_LOG ("fps = %d/%d = %.3f",
1502 pad->granulerate_n, pad->granulerate_d,
1503 (double) pad->granulerate_n / pad->granulerate_d);
1505 if (pad->granulerate_d <= 0)
1508 pad->caps = gst_caps_new_simple ("text/plain", NULL);
1510 pad->n_header_packets = 1;
1512 pad->is_ogm_text = TRUE;
1513 pad->is_sparse = TRUE;
1520 #define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */
1521 #define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */
1522 #define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */
1523 #define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */
1524 #define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */
1525 #define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */
1526 #define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */
1527 #define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */
1529 #define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */
1530 #define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */
1532 #define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */
1533 #define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */
1534 #define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */
1535 #define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */
1539 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1541 guint8 *data = packet->packet;
1546 pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1547 pad->granulerate_d = 1;
1548 GST_LOG ("sample rate: %d", pad->granulerate_n);
1550 format = GST_READ_UINT32_LE (data + 12);
1551 channels = GST_READ_UINT8 (data + 21);
1553 pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1555 if (pad->granulerate_n == 0)
1560 caps = gst_caps_new_simple ("audio/x-raw-int",
1561 "depth", G_TYPE_INT, 8,
1562 "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1565 caps = gst_caps_new_simple ("audio/x-raw-int",
1566 "depth", G_TYPE_INT, 8,
1567 "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
1569 case OGGPCM_FMT_S16_LE:
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_LITTLE_ENDIAN,
1574 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1576 case OGGPCM_FMT_S16_BE:
1577 caps = gst_caps_new_simple ("audio/x-raw-int",
1578 "depth", G_TYPE_INT, 16,
1579 "width", G_TYPE_INT, 16,
1580 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1581 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1583 case OGGPCM_FMT_S24_LE:
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_LITTLE_ENDIAN,
1588 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1590 case OGGPCM_FMT_S24_BE:
1591 caps = gst_caps_new_simple ("audio/x-raw-int",
1592 "depth", G_TYPE_INT, 24,
1593 "width", G_TYPE_INT, 24,
1594 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1595 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1597 case OGGPCM_FMT_S32_LE:
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_LITTLE_ENDIAN,
1602 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1604 case OGGPCM_FMT_S32_BE:
1605 caps = gst_caps_new_simple ("audio/x-raw-int",
1606 "depth", G_TYPE_INT, 32,
1607 "width", G_TYPE_INT, 32,
1608 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1609 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1611 case OGGPCM_FMT_ULAW:
1612 caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
1614 case OGGPCM_FMT_ALAW:
1615 caps = gst_caps_new_simple ("audio/x-alaw", NULL);
1617 case OGGPCM_FMT_FLT32_LE:
1618 caps = gst_caps_new_simple ("audio/x-raw-float",
1619 "width", G_TYPE_INT, 32,
1620 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1622 case OGGPCM_FMT_FLT32_BE:
1623 caps = gst_caps_new_simple ("audio/x-raw-float",
1624 "width", G_TYPE_INT, 32,
1625 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1627 case OGGPCM_FMT_FLT64_LE:
1628 caps = gst_caps_new_simple ("audio/x-raw-float",
1629 "width", G_TYPE_INT, 64,
1630 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1632 case OGGPCM_FMT_FLT64_BE:
1633 caps = gst_caps_new_simple ("audio/x-raw-float",
1634 "width", G_TYPE_INT, 64,
1635 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1641 gst_caps_set_simple (caps, "audio/x-raw-int",
1642 "rate", G_TYPE_INT, pad->granulerate_n,
1643 "channels", G_TYPE_INT, channels, NULL);
1652 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1654 guint8 *data = packet->packet;
1656 pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1657 pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1658 pad->granuleshift = data[28];
1659 GST_LOG ("sample rate: %d", pad->granulerate_n);
1661 pad->n_header_packets = 3;
1663 if (pad->granulerate_n == 0)
1666 data += 4 + (4 + 4 + 4);
1667 GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1668 GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1670 pad->caps = gst_caps_new_simple ("text/x-cmml", NULL);
1671 pad->is_sparse = TRUE;
1679 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1681 guint8 *data = packet->packet;
1683 pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1684 pad->granulerate_d = 1;
1685 pad->granuleshift = 0;
1686 GST_LOG ("sample rate: %d", pad->granulerate_n);
1688 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1689 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1691 if (pad->granulerate_n == 0)
1694 pad->caps = gst_caps_new_simple ("audio/x-celt",
1695 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1703 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1705 guint8 *data = packet->packet;
1706 const char *category;
1708 if (packet->bytes < 64)
1711 pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1712 pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1713 pad->granuleshift = GST_READ_UINT8 (data + 15);
1714 GST_LOG ("sample rate: %d", pad->granulerate_n);
1716 pad->n_header_packets = GST_READ_UINT8 (data + 11);
1717 GST_LOG ("kate header packets: %d", pad->n_header_packets);
1719 if (pad->granulerate_n == 0)
1722 category = (const char *) data + 48;
1723 if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1724 strcmp (category, "spu-subtitles") == 0 ||
1725 strcmp (category, "K-SPU") == 0) {
1726 pad->caps = gst_caps_new_simple ("subtitle/x-kate", NULL);
1728 pad->caps = gst_caps_new_simple ("application/x-kate", NULL);
1731 pad->is_sparse = TRUE;
1737 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1741 if (packet->bytes < 1)
1744 switch (packet->packet[0]) {
1745 case 0x00: /* text data */
1746 if (packet->bytes < 1 + 8 * 2) {
1749 duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
1755 duration = GST_CLOCK_TIME_NONE;
1763 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
1765 GstTagList *list = NULL;
1767 if (packet->bytes <= 0)
1770 switch (packet->packet[0]) {
1772 const gchar *canonical;
1775 if (packet->bytes < 64) {
1776 GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
1780 /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
1781 memcpy (language, packet->packet + 32, 16);
1784 /* language is an ISO 639-1 code or RFC 3066 language code, we
1785 * truncate to ISO 639-1 */
1786 g_strdelimit (language, NULL, '\0');
1787 canonical = gst_tag_get_language_code_iso_639_1 (language);
1789 list = gst_tag_list_new_full (GST_TAG_LANGUAGE_CODE, canonical, NULL);
1791 GST_WARNING ("Unknown or invalid language code %s, ignored", language);
1796 tag_list_from_vorbiscomment_packet (packet,
1797 (const guint8 *) "\201kate\0\0\0\0", 9, &list);
1805 /* ensure the comment packet cannot override the category/language
1806 from the identification header */
1807 gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL);
1809 pad->taglist = list;
1815 /* indent hates our freedoms */
1816 const GstOggMap mappers[] = {
1818 "\200theora", 7, 42,
1820 setup_theora_mapper,
1821 granulepos_to_granule_theora,
1822 granule_to_granulepos_default,
1825 packet_duration_constant,
1830 "\001vorbis", 7, 22,
1832 setup_vorbis_mapper,
1833 granulepos_to_granule_default,
1834 granule_to_granulepos_default,
1837 packet_duration_vorbis,
1845 granulepos_to_granule_default,
1846 granule_to_granulepos_default,
1849 packet_duration_constant,
1866 "CMML\0\0\0\0", 8, 0,
1879 "application/x-annodex",
1880 setup_fishead_mapper,
1881 granulepos_to_granule_default,
1882 granule_to_granulepos_default,
1891 "application/octet-stream",
1892 setup_fishead_mapper,
1905 granulepos_to_granule_default,
1906 granule_to_granulepos_default,
1909 packet_duration_flac,
1917 granulepos_to_granule_default,
1918 granule_to_granulepos_default,
1921 packet_duration_flac,
1927 "application/octet-stream",
1940 granulepos_to_granule_default,
1941 granule_to_granulepos_default,
1944 packet_duration_constant,
1949 "\200kate\0\0\0", 8, 0,
1952 granulepos_to_granule_default,
1953 granule_to_granulepos_default,
1956 packet_duration_kate,
1964 granulepos_to_granule_dirac,
1965 granule_to_granulepos_dirac,
1968 packet_duration_constant,
1969 granulepos_to_key_granule_dirac,
1976 granulepos_to_granule_vp8,
1977 granule_to_granulepos_vp8,
1980 packet_duration_vp8,
1981 granulepos_to_key_granule_vp8,
1985 "\001audio\0\0\0", 9, 53,
1986 "application/x-ogm-audio",
1987 setup_ogmaudio_mapper,
1988 granulepos_to_granule_default,
1989 granule_to_granulepos_default,
1992 packet_duration_ogm,
1997 "\001video\0\0\0", 9, 53,
1998 "application/x-ogm-video",
1999 setup_ogmvideo_mapper,
2000 granulepos_to_granule_default,
2001 granule_to_granulepos_default,
2004 packet_duration_constant,
2009 "\001text\0\0\0", 9, 9,
2010 "application/x-ogm-text",
2011 setup_ogmtext_mapper,
2012 granulepos_to_granule_default,
2013 granule_to_granulepos_default,
2016 packet_duration_ogm,
2024 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2029 for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2030 if (packet->bytes >= mappers[i].min_packet_size &&
2031 packet->bytes >= mappers[i].id_length &&
2032 memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
2034 GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2036 if (mappers[i].setup_func)
2037 ret = mappers[i].setup_func (pad, packet);
2042 GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2046 GST_WARNING ("mapper '%s' did not accept setup header",
2047 mappers[i].media_type);
2056 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2057 const GstCaps * caps)
2059 const GstStructure *structure;
2060 const GstBuffer *buf;
2061 const GValue *streamheader;
2062 const GValue *first_element;
2065 GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2070 structure = gst_caps_get_structure (caps, 0);
2071 streamheader = gst_structure_get_value (structure, "streamheader");
2073 if (streamheader == NULL) {
2074 GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2078 if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2079 GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2083 if (gst_value_array_get_size (streamheader) == 0) {
2084 GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2088 first_element = gst_value_array_get_value (streamheader, 0);
2090 if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2091 GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2095 buf = gst_value_get_buffer (first_element);
2096 if (buf == NULL || GST_BUFFER_SIZE (buf) == 0) {
2097 GST_ERROR ("invalid first streamheader buffer");
2101 GST_MEMDUMP ("streamheader", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
2103 packet.packet = GST_BUFFER_DATA (buf);
2104 packet.bytes = GST_BUFFER_SIZE (buf);
2106 GST_INFO ("Found headers on caps, using those to determine type");
2107 return gst_ogg_stream_setup_map (pad, &packet);