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 (*GstOggMapIsGranuleposKeyFrameFunc) (GstOggStream * pad,
53 /* returns TRUE if the packet is a key frame */
54 typedef gboolean (*GstOggMapIsPacketKeyFrameFunc) (GstOggStream * pad,
57 /* returns TRUE if the given packet is a stream header packet */
58 typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
60 typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
62 typedef void (*GstOggMapExtractTagsFunc) (GstOggStream * pad,
65 typedef gint64 (*GstOggMapGranuleposToKeyGranuleFunc) (GstOggStream * pad,
68 #define SKELETON_FISBONE_MIN_SIZE 52
69 #define SKELETON_FISHEAD_3_3_MIN_SIZE 112
70 #define SKELETON_FISHEAD_4_0_MIN_SIZE 80
77 const gchar *media_type;
78 GstOggMapSetupFunc setup_func;
79 GstOggMapToGranuleFunc granulepos_to_granule_func;
80 GstOggMapToGranuleposFunc granule_to_granulepos_func;
81 GstOggMapIsGranuleposKeyFrameFunc is_granulepos_key_frame_func;
82 GstOggMapIsPacketKeyFrameFunc is_packet_key_frame_func;
83 GstOggMapIsHeaderPacketFunc is_header_func;
84 GstOggMapPacketDurationFunc packet_duration_func;
85 GstOggMapGranuleposToKeyGranuleFunc granulepos_to_key_granule_func;
86 GstOggMapExtractTagsFunc extract_tags_func;
89 extern const GstOggMap mappers[];
92 gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet)
96 if (packet->granulepos == -1) {
97 return GST_CLOCK_TIME_NONE;
100 duration = gst_ogg_stream_get_packet_duration (pad, packet);
101 if (duration == -1) {
102 return GST_CLOCK_TIME_NONE;
105 return gst_ogg_stream_granule_to_time (pad,
106 gst_ogg_stream_granulepos_to_granule (pad,
107 packet->granulepos) - duration);
111 gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad,
114 if (pad->frame_size == 0)
115 return GST_CLOCK_TIME_NONE;
117 return gst_ogg_stream_granule_to_time (pad,
118 gst_ogg_stream_granulepos_to_granule (pad, granulepos));
122 gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad,
125 return gst_ogg_stream_granule_to_time (pad,
126 gst_ogg_stream_granulepos_to_granule (pad, granulepos));
130 gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule)
132 if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
135 granule += pad->granule_offset;
139 return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
144 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
146 if (granulepos == -1 || granulepos == 0) {
150 if (mappers[pad->map].granulepos_to_granule_func == NULL) {
151 GST_WARNING ("Failed to convert %s granulepos to granule",
152 gst_ogg_stream_get_media_type (pad));
156 return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
160 gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos)
162 if (mappers[pad->map].granulepos_to_key_granule_func)
163 return mappers[pad->map].granulepos_to_key_granule_func (pad, granulepos);
165 if (granulepos == -1 || granulepos == 0) {
169 return granulepos >> pad->granuleshift;
173 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
174 gint64 keyframe_granule)
176 if (granule == -1 || granule == 0) {
180 if (mappers[pad->map].granule_to_granulepos_func == NULL) {
181 GST_WARNING ("Failed to convert %s granule to granulepos",
182 gst_ogg_stream_get_media_type (pad));
186 return mappers[pad->map].granule_to_granulepos_func (pad, granule,
191 gst_ogg_stream_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos)
193 if (granulepos == -1) {
197 if (mappers[pad->map].is_granulepos_key_frame_func == NULL) {
198 GST_WARNING ("Failed to determine keyframeness for %s granulepos",
199 gst_ogg_stream_get_media_type (pad));
203 return mappers[pad->map].is_granulepos_key_frame_func (pad, granulepos);
207 gst_ogg_stream_packet_is_key_frame (GstOggStream * pad, ogg_packet * packet)
209 if (mappers[pad->map].is_packet_key_frame_func == NULL) {
210 GST_WARNING ("Failed to determine keyframeness of %s packet",
211 gst_ogg_stream_get_media_type (pad));
215 return mappers[pad->map].is_packet_key_frame_func (pad, packet);
219 gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
221 if (mappers[pad->map].is_header_func == NULL) {
222 GST_WARNING ("Failed to determine headerness of %s packet",
223 gst_ogg_stream_get_media_type (pad));
227 return mappers[pad->map].is_header_func (pad, packet);
231 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
233 if (mappers[pad->map].packet_duration_func == NULL) {
234 GST_WARNING ("Failed to determine %s packet duration",
235 gst_ogg_stream_get_media_type (pad));
239 return mappers[pad->map].packet_duration_func (pad, packet);
244 gst_ogg_stream_extract_tags (GstOggStream * pad, ogg_packet * packet)
246 if (mappers[pad->map].extract_tags_func == NULL) {
247 GST_DEBUG ("No tag extraction");
251 mappers[pad->map].extract_tags_func (pad, packet);
255 gst_ogg_stream_get_media_type (GstOggStream * pad)
257 const GstCaps *caps = pad->caps;
258 const GstStructure *structure;
261 structure = gst_caps_get_structure (caps, 0);
264 return gst_structure_get_name (structure);
267 /* some generic functions */
270 is_granulepos_keyframe_true (GstOggStream * pad, gint64 granulepos)
276 is_packet_keyframe_true (GstOggStream * pad, ogg_packet * packet)
282 granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos)
284 gint64 keyindex, keyoffset;
286 if (pad->granuleshift != 0) {
287 keyindex = granulepos >> pad->granuleshift;
288 keyoffset = granulepos - (keyindex << pad->granuleshift);
289 return keyindex + keyoffset;
297 granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
298 gint64 keyframe_granule)
302 if (pad->granuleshift != 0) {
303 /* If we don't know where the previous keyframe is yet, assume it is
304 at 0 or 1, depending on bitstream version. If nothing else, this
305 avoids getting negative granpos back. */
306 if (keyframe_granule < 0)
307 keyframe_granule = pad->theora_has_zero_keyoffset ? 0 : 1;
308 keyoffset = granule - keyframe_granule;
309 return (keyframe_granule << pad->granuleshift) | keyoffset;
317 is_header_unknown (GstOggStream * pad, ogg_packet * packet)
319 GST_WARNING ("don't know how to detect header");
325 is_header_true (GstOggStream * pad, ogg_packet * packet)
331 is_header_count (GstOggStream * pad, ogg_packet * packet)
333 if (pad->n_header_packets_seen < pad->n_header_packets) {
340 packet_duration_constant (GstOggStream * pad, ogg_packet * packet)
342 return pad->frame_size;
345 /* helper: extracts tags from vorbis comment ogg packet.
346 * Returns result in *tags after free'ing existing *tags (if any) */
348 tag_list_from_vorbiscomment_packet (ogg_packet * packet,
349 const guint8 * id_data, const guint id_data_length, GstTagList ** tags)
351 GstBuffer *buf = NULL;
352 gchar *encoder = NULL;
356 g_return_val_if_fail (tags != NULL, FALSE);
358 buf = gst_buffer_new ();
359 GST_BUFFER_DATA (buf) = (guint8 *) packet->packet;
360 GST_BUFFER_SIZE (buf) = packet->bytes;
362 list = gst_tag_list_from_vorbiscomment_buffer (buf, id_data, id_data_length,
366 GST_WARNING ("failed to decode vorbis comments");
373 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
380 gst_tag_list_free (*tags);
383 gst_buffer_unref (buf);
391 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
393 guint8 *data = packet->packet;
394 guint w, h, par_d, par_n;
395 guint8 vmaj, vmin, vrev;
401 w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
402 h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
404 pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
405 pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
407 par_n = GST_READ_UINT24_BE (data + 30);
408 par_d = GST_READ_UINT24_BE (data + 33);
410 GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
411 pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
413 /* 2 bits + 3 bits = 5 bits KFGSHIFT */
414 pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
415 (GST_READ_UINT8 (data + 41) >> 5);
416 GST_LOG ("granshift: %d", pad->granuleshift);
418 pad->is_video = TRUE;
419 pad->n_header_packets = 3;
422 pad->bitrate = GST_READ_UINT24_BE (data + 37);
423 GST_LOG ("bit rate: %d", pad->bitrate);
425 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
426 GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
430 /* The interpretation of the granule position has changed with 3.2.1.
431 The granule is now made from the number of frames encoded, rather than
432 the index of the frame being encoded - so there is a difference of 1. */
433 pad->theora_has_zero_keyoffset =
434 ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201;
436 pad->caps = gst_caps_new_simple ("video/x-theora", NULL);
438 if (w > 0 && h > 0) {
439 gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
440 G_TYPE_INT, h, NULL);
443 /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
444 if (par_n == 0 || par_d == 0)
447 /* only add framerate now so caps look prettier, with width/height first */
448 gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
449 pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
450 GST_TYPE_FRACTION, par_n, par_d, NULL);
456 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
458 gint64 keyindex, keyoffset;
460 if (pad->granuleshift != 0) {
461 keyindex = granulepos >> pad->granuleshift;
462 keyoffset = granulepos - (keyindex << pad->granuleshift);
463 if (pad->theora_has_zero_keyoffset) {
466 return keyindex + keyoffset;
473 is_granulepos_keyframe_theora (GstOggStream * pad, gint64 granulepos)
477 if (granulepos == (gint64) - 1)
480 frame_mask = (1 << pad->granuleshift) - 1;
482 return ((granulepos & frame_mask) == 0);
486 is_packet_keyframe_theora (GstOggStream * pad, ogg_packet * packet)
488 if (packet->bytes == 0)
490 return (packet->packet[0] & 0xc0) == 0x00;
494 is_header_theora (GstOggStream * pad, ogg_packet * packet)
496 return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
500 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
502 if (packet->bytes > 0 && packet->packet[0] == 0x81) {
503 tag_list_from_vorbiscomment_packet (packet,
504 (const guint8 *) "\201theora", 7, &pad->taglist);
507 pad->taglist = gst_tag_list_new ();
510 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
511 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
518 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
521 DiracSequenceHeader header;
523 ret = dirac_sequence_header_parse (&header, packet->packet + 13,
526 GST_DEBUG ("Failed to parse Dirac sequence header");
530 pad->is_video = TRUE;
531 pad->always_flush_page = TRUE;
532 pad->granulerate_n = header.frame_rate_numerator * 2;
533 pad->granulerate_d = header.frame_rate_denominator;
534 pad->granuleshift = 22;
535 pad->n_header_packets = 1;
538 if (header.interlaced_coding != 0) {
539 GST_DEBUG ("non-progressive Dirac coding not implemented");
543 pad->caps = gst_caps_new_simple ("video/x-dirac",
544 "width", G_TYPE_INT, header.width,
545 "height", G_TYPE_INT, header.height,
546 "interlaced", G_TYPE_BOOLEAN, header.interlaced,
547 "pixel-aspect-ratio", GST_TYPE_FRACTION,
548 header.aspect_ratio_numerator, header.aspect_ratio_denominator,
549 "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
550 header.frame_rate_denominator, NULL);
555 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
557 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
563 if (granulepos == -1)
566 dist_h = (granulepos >> 22) & 0xff;
567 dist_l = granulepos & 0xff;
568 dist = (dist_h << 8) | dist_l;
574 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
580 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
581 delay = (gp >> 9) & 0x1fff;
584 GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
590 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
591 gint64 keyframe_granule)
593 /* This conversion requires knowing more details about the Dirac
599 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
608 if (gp == -1 || gp == 0)
611 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
612 dist_h = (gp >> 22) & 0xff;
614 dist = (dist_h << 8) | dist_l;
615 delay = (gp >> 9) & 0x1fff;
618 return dt - 2 * dist + 4;
624 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
626 gint width, height, par_n, par_d, fps_n, fps_d;
628 if (packet->bytes < 26) {
629 GST_DEBUG ("Failed to parse VP8 BOS page");
633 width = GST_READ_UINT16_BE (packet->packet + 8);
634 height = GST_READ_UINT16_BE (packet->packet + 10);
635 par_n = GST_READ_UINT24_BE (packet->packet + 12);
636 par_d = GST_READ_UINT24_BE (packet->packet + 15);
637 fps_n = GST_READ_UINT32_BE (packet->packet + 18);
638 fps_d = GST_READ_UINT32_BE (packet->packet + 22);
640 pad->is_video = TRUE;
642 pad->granulerate_n = fps_n;
643 pad->granulerate_d = fps_d;
644 pad->n_header_packets = 2;
647 pad->caps = gst_caps_new_simple ("video/x-vp8",
648 "width", G_TYPE_INT, width,
649 "height", G_TYPE_INT, height,
650 "pixel-aspect-ratio", GST_TYPE_FRACTION,
651 par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
657 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
659 guint64 gpos = granulepos;
661 if (granulepos == -1)
664 /* Get rid of flags */
667 return ((gpos & 0x07ffffff) == 0);
671 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
673 guint64 gp = (guint64) gpos;
678 dist = (gp >> 3) & 0x07ffffff;
680 GST_DEBUG ("pt %u, dist %u", pt, dist);
686 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
687 gint64 keyframe_granule)
689 /* FIXME: This requires to look into the content of the packets
690 * because the simple granule counter doesn't know about invisible
696 /* Check if this packet contains an invisible frame or not */
698 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
702 if (packet->bytes < 3)
705 hdr = GST_READ_UINT24_LE (packet->packet);
707 return (((hdr >> 4) & 1) != 0) ? 1 : 0;
711 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
713 guint64 gp = granulepos;
714 guint64 pts = (gp >> 32);
715 guint32 dist = (gp >> 3) & 0x07ffffff;
717 if (granulepos == -1 || granulepos == 0)
727 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
729 if (packet->bytes >= 5 && packet->packet[0] == 0x4F &&
730 packet->packet[1] == 0x56 && packet->packet[2] == 0x50 &&
731 packet->packet[3] == 0x38 && packet->packet[4] == 0x30)
737 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
739 if (packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) {
740 tag_list_from_vorbiscomment_packet (packet,
741 (const guint8 *) "OVP80\2 ", 7, &pad->taglist);
748 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
750 guint8 *data = packet->packet;
754 pad->version = GST_READ_UINT32_LE (data);
756 chans = GST_READ_UINT8 (data);
758 pad->granulerate_n = GST_READ_UINT32_LE (data);
759 pad->granulerate_d = 1;
760 pad->granuleshift = 0;
763 GST_LOG ("sample rate: %d", pad->granulerate_n);
766 pad->bitrate_upper = GST_READ_UINT32_LE (data);
768 pad->bitrate_nominal = GST_READ_UINT32_LE (data);
770 pad->bitrate_lower = GST_READ_UINT32_LE (data);
772 if (pad->bitrate_nominal > 0)
773 pad->bitrate = pad->bitrate_nominal;
775 if (pad->bitrate_upper > 0 && !pad->bitrate)
776 pad->bitrate = pad->bitrate_upper;
778 if (pad->bitrate_lower > 0 && !pad->bitrate)
779 pad->bitrate = pad->bitrate_lower;
781 GST_LOG ("bit rate: %d", pad->bitrate);
783 pad->n_header_packets = 3;
785 if (pad->granulerate_n == 0)
788 parse_vorbis_header_packet (pad, packet);
790 pad->caps = gst_caps_new_simple ("audio/x-vorbis",
791 "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
798 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
800 if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
803 if (packet->packet[0] == 5) {
804 parse_vorbis_setup_packet (pad, packet);
811 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
813 if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
816 if (((guint8 *) (packet->packet))[0] == 0x03) {
817 tag_list_from_vorbiscomment_packet (packet,
818 (const guint8 *) "\003vorbis", 7, &pad->taglist);
821 pad->taglist = gst_tag_list_new ();
823 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
824 GST_TAG_ENCODER_VERSION, pad->version, NULL);
826 if (pad->bitrate_nominal > 0)
827 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
828 GST_TAG_NOMINAL_BITRATE, (guint) pad->bitrate_nominal, NULL);
830 if (pad->bitrate_upper > 0)
831 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
832 GST_TAG_MAXIMUM_BITRATE, (guint) pad->bitrate_upper, NULL);
834 if (pad->bitrate_lower > 0)
835 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
836 GST_TAG_MINIMUM_BITRATE, (guint) pad->bitrate_lower, NULL);
839 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
840 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
845 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
851 if (packet->bytes == 0 || packet->packet[0] & 1)
854 mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
855 size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
857 if (pad->last_size == 0) {
860 duration = pad->last_size / 4 + size / 4;
862 pad->last_size = size;
864 GST_DEBUG ("duration %d", (int) duration);
873 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
875 guint8 *data = packet->packet;
878 data += 8 + 20 + 4 + 4;
879 pad->granulerate_n = GST_READ_UINT32_LE (data);
880 pad->granulerate_d = 1;
881 pad->granuleshift = 0;
884 chans = GST_READ_UINT32_LE (data);
886 pad->bitrate = GST_READ_UINT32_LE (data);
888 GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
889 GST_LOG ("bit rate: %d", pad->bitrate);
891 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
892 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
893 GST_READ_UINT32_LE (packet->packet + 56);
895 if (pad->granulerate_n == 0)
898 pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
899 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
905 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
907 /* packet 2 must be comment packet */
908 if (packet->bytes > 0 && pad->n_header_packets_seen == 1) {
909 tag_list_from_vorbiscomment_packet (packet, NULL, 0, &pad->taglist);
912 pad->taglist = gst_tag_list_new ();
915 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
916 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
924 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
926 pad->granulerate_n = 0;
927 pad->granulerate_d = 1;
928 pad->granuleshift = 0;
930 pad->n_header_packets = 3;
932 pad->caps = gst_caps_new_simple ("audio/x-flac", NULL);
938 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
940 if (pad->n_header_packets_seen == 1) {
941 pad->granulerate_n = (packet->packet[14] << 12) |
942 (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
945 if (pad->n_header_packets_seen < pad->n_header_packets) {
953 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
955 guint8 *data = packet->packet;
958 /* see http://flac.sourceforge.net/ogg_mapping.html */
960 pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
961 pad->granulerate_d = 1;
962 pad->granuleshift = 0;
963 chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
965 GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
967 pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
969 if (pad->granulerate_n == 0)
972 pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
973 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
979 is_header_flac (GstOggStream * pad, ogg_packet * packet)
981 return (packet->bytes > 0 && (packet->packet[0] != 0xff));
985 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
987 int block_size_index;
989 if (packet->bytes < 4)
992 block_size_index = packet->packet[2] >> 4;
993 if (block_size_index == 1)
995 if (block_size_index >= 2 && block_size_index <= 5) {
996 return 576 << (block_size_index - 2);
998 if (block_size_index >= 8) {
999 return 256 << (block_size_index - 8);
1001 if (block_size_index == 6 || block_size_index == 7) {
1002 guint len, bytes = (block_size_index - 6) + 1;
1005 if (packet->bytes < 4 + 1 + bytes)
1007 tmp = packet->packet[4];
1010 while (tmp & 0x80) {
1018 if (packet->bytes < 4 + len + bytes)
1021 return packet->packet[4 + len] + 1;
1023 return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
1030 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
1032 if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
1033 tag_list_from_vorbiscomment_packet (packet,
1034 packet->packet, 4, &pad->taglist);
1041 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
1044 gint64 prestime_n, prestime_d;
1045 gint64 basetime_n, basetime_d;
1047 data = packet->packet;
1049 data += 8; /* header */
1051 pad->skeleton_major = GST_READ_UINT16_LE (data);
1053 pad->skeleton_minor = GST_READ_UINT16_LE (data);
1056 prestime_n = (gint64) GST_READ_UINT64_LE (data);
1058 prestime_d = (gint64) GST_READ_UINT64_LE (data);
1060 basetime_n = (gint64) GST_READ_UINT64_LE (data);
1062 basetime_d = (gint64) GST_READ_UINT64_LE (data);
1065 /* FIXME: we don't use basetime anywhere in the demuxer! */
1066 if (basetime_d != 0)
1067 pad->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
1071 if (prestime_d != 0)
1072 pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1076 /* Ogg Skeleton 3.3+ streams provide additional information in the header */
1077 if (packet->bytes >= SKELETON_FISHEAD_3_3_MIN_SIZE && pad->skeleton_major == 3
1078 && pad->skeleton_minor > 0) {
1079 gint64 firstsampletime_n, firstsampletime_d;
1080 gint64 lastsampletime_n, lastsampletime_d;
1081 gint64 firstsampletime, lastsampletime;
1082 guint64 segment_length, content_offset;
1084 firstsampletime_n = GST_READ_UINT64_LE (data + 64);
1085 firstsampletime_d = GST_READ_UINT64_LE (data + 72);
1086 lastsampletime_n = GST_READ_UINT64_LE (data + 80);
1087 lastsampletime_d = GST_READ_UINT64_LE (data + 88);
1088 segment_length = GST_READ_UINT64_LE (data + 96);
1089 content_offset = GST_READ_UINT64_LE (data + 104);
1091 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1092 firstsampletime_n, firstsampletime_d);
1093 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1094 lastsampletime_n, lastsampletime_d);
1095 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1096 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1098 if (firstsampletime_d > 0)
1099 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1100 firstsampletime_n, firstsampletime_d);
1102 firstsampletime = 0;
1104 if (lastsampletime_d > 0)
1105 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1106 lastsampletime_n, lastsampletime_d);
1110 if (lastsampletime > firstsampletime)
1111 pad->total_time = lastsampletime - firstsampletime;
1113 pad->total_time = -1;
1115 GST_INFO ("skeleton fishead parsed total: %" GST_TIME_FORMAT,
1116 GST_TIME_ARGS (pad->total_time));
1117 } else if (packet->bytes >= SKELETON_FISHEAD_4_0_MIN_SIZE
1118 && pad->skeleton_major == 4) {
1119 guint64 segment_length, content_offset;
1121 segment_length = GST_READ_UINT64_LE (data + 64);
1122 content_offset = GST_READ_UINT64_LE (data + 72);
1124 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1125 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1127 pad->total_time = -1;
1130 GST_INFO ("skeleton fishead %u.%u parsed (basetime: %" GST_TIME_FORMAT
1131 ", prestime: %" GST_TIME_FORMAT ")", pad->skeleton_major,
1132 pad->skeleton_minor, GST_TIME_ARGS (pad->basetime),
1133 GST_TIME_ARGS (pad->prestime));
1135 pad->is_skeleton = TRUE;
1136 pad->is_sparse = TRUE;
1138 pad->caps = gst_caps_new_simple ("application/x-ogg-skeleton", NULL);
1144 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1145 guint32 * serialno, GstOggSkeleton * type)
1147 GstOggSkeleton stype;
1148 guint serial_offset;
1150 if (size != 0 && size < SKELETON_FISBONE_MIN_SIZE) {
1151 GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1156 /* Skeleton EOS packet is zero bytes */
1158 } else if (memcmp (data, "fisbone\0", 8) == 0) {
1159 GST_INFO ("got fisbone packet");
1160 stype = GST_OGG_SKELETON_FISBONE;
1162 } else if (memcmp (data, "index\0", 6) == 0) {
1163 GST_INFO ("got index packet");
1164 stype = GST_OGG_SKELETON_INDEX;
1166 } else if (memcmp (data, "fishead\0", 8) == 0) {
1169 GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1174 *serialno = GST_READ_UINT32_LE (data + serial_offset);
1183 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1184 const guint8 * data, guint size, GstClockTime * p_start_time)
1186 GstClockTime start_time;
1187 gint64 start_granule;
1189 if (pad->have_fisbone) {
1190 GST_DEBUG ("already have fisbone, ignoring second one");
1194 /* skip "fisbone\0" + headers offset + serialno + num headers */
1195 data += 8 + 4 + 4 + 4;
1197 pad->have_fisbone = TRUE;
1199 /* We don't overwrite whatever was set before by the format-specific
1200 setup: skeleton contains wrong information sometimes, and the codec
1201 headers are authoritative.
1202 So we only gather information that was not already filled out by
1203 the mapper setup. This should hopefully allow handling unknown
1204 streams a bit better, while not trashing correct setup from bad
1206 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
1207 pad->granulerate_n = GST_READ_UINT64_LE (data);
1208 pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
1210 if (pad->granuleshift < 0) {
1211 pad->granuleshift = GST_READ_UINT8 (data + 28);
1214 start_granule = GST_READ_UINT64_LE (data + 16);
1215 pad->preroll = GST_READ_UINT32_LE (data + 24);
1217 start_time = granulepos_to_granule_default (pad, start_granule);
1219 GST_INFO ("skeleton fisbone parsed "
1220 "(start time: %" GST_TIME_FORMAT
1221 " granulerate_n: %d granulerate_d: %d "
1222 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
1223 GST_TIME_ARGS (start_time),
1224 pad->granulerate_n, pad->granulerate_d, pad->preroll, pad->granuleshift);
1227 *p_start_time = start_time;
1233 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1241 if (G_UNLIKELY (*size < 1))
1245 *result |= ((byte & 0x7f) << shift);
1250 } while ((byte & 0x80) != 0x80);
1256 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1257 const guint8 * data, guint size)
1259 guint64 i, n_keypoints, isize;
1260 guint64 offset, timestamp;
1261 guint64 offset_d, timestamp_d;
1264 GST_DEBUG ("already have index, ignoring second one");
1268 if ((skel_pad->skeleton_major == 3 && size < 26) ||
1269 (skel_pad->skeleton_major == 4 && size < 62)) {
1270 GST_WARNING ("small index packet of size %u, ignoring", size);
1274 /* skip "index\0" + serialno */
1278 n_keypoints = GST_READ_UINT64_LE (data);
1283 pad->kp_denom = GST_READ_UINT64_LE (data);
1284 if (pad->kp_denom == 0)
1290 if (skel_pad->skeleton_major == 4) {
1291 gint64 firstsampletime_n;
1292 gint64 lastsampletime_n;
1293 gint64 firstsampletime, lastsampletime;
1295 firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1296 lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1298 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1299 firstsampletime_n, pad->kp_denom);
1300 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1301 lastsampletime_n, pad->kp_denom);
1303 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1304 firstsampletime_n, pad->kp_denom);
1305 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1306 lastsampletime_n, pad->kp_denom);
1308 if (lastsampletime > firstsampletime)
1309 pad->total_time = lastsampletime - firstsampletime;
1311 pad->total_time = -1;
1313 GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1314 GST_TIME_ARGS (pad->total_time));
1320 GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1321 G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1323 pad->index = g_try_new (GstOggIndex, n_keypoints);
1331 for (i = 0; i < n_keypoints; i++) {
1333 if (!read_vlc (&data, &size, &offset_d))
1335 if (!read_vlc (&data, &size, ×tamp_d))
1339 timestamp += timestamp_d;
1341 pad->index[i].offset = offset;
1342 pad->index[i].timestamp = timestamp;
1345 GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1348 if (isize != n_keypoints) {
1349 GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1350 G_GUINT64_FORMAT, n_keypoints, isize);
1352 pad->n_index = isize;
1353 /* try to use the index to estimate the bitrate */
1355 guint64 so, eo, st, et, b, t;
1357 /* get start and end offset and timestamps */
1358 so = pad->index[0].offset;
1359 st = pad->index[0].timestamp;
1360 eo = pad->index[isize - 1].offset;
1361 et = pad->index[isize - 1].timestamp;
1366 GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1368 /* this is the total stream bitrate according to this index */
1369 pad->idx_bitrate = gst_util_uint64_scale (8 * b, pad->kp_denom, t);
1371 GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1378 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1381 if (index->timestamp < *ts)
1383 else if (index->timestamp > *ts)
1390 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1391 guint64 * timestamp, guint64 * offset)
1397 n_index = pad->n_index;
1398 if (n_index == 0 || pad->index == NULL)
1401 ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1402 GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1405 gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1406 (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1412 GST_INFO ("found at index %u", (guint) (best - pad->index));
1415 *offset = best->offset;
1418 gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1423 /* Do we need these for something?
1424 * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
1425 * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
1426 * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
1427 * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
1428 * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
1429 * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
1433 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1435 if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1442 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1444 if (!(packet->packet[0] & 1) && (packet->packet[0] & 3 && pad->is_ogm_text)) {
1445 tag_list_from_vorbiscomment_packet (packet,
1446 (const guint8 *) "\003vorbis", 7, &pad->taglist);
1451 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1458 data = packet->packet;
1459 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1461 if (offset > packet->bytes) {
1462 GST_ERROR ("buffer too small");
1467 for (n = offset - 1; n > 0; n--) {
1468 samples = (samples << 8) | data[n];
1475 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1477 guint8 *data = packet->packet;
1480 pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1481 pad->granulerate_d = 1;
1483 fourcc = GST_READ_UINT32_LE (data + 9);
1484 GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1486 pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1488 GST_LOG ("sample rate: %d", pad->granulerate_n);
1489 if (pad->granulerate_n == 0)
1493 gst_caps_set_simple (pad->caps,
1494 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1496 pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
1497 "fourcc", GST_TYPE_FOURCC, fourcc,
1498 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1501 pad->n_header_packets = 1;
1508 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1510 guint8 *data = packet->packet;
1515 GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
1516 GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
1518 pad->is_video = TRUE;
1519 pad->granulerate_n = 10000000;
1520 time_unit = GST_READ_UINT64_LE (data + 17);
1521 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1522 GST_WARNING ("timeunit is out of range");
1524 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1526 GST_LOG ("fps = %d/%d = %.3f",
1527 pad->granulerate_n, pad->granulerate_d,
1528 (double) pad->granulerate_n / pad->granulerate_d);
1530 fourcc = GST_READ_UINT32_LE (data + 9);
1531 width = GST_READ_UINT32_LE (data + 45);
1532 height = GST_READ_UINT32_LE (data + 49);
1533 GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1535 pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1537 if (pad->caps == NULL) {
1538 pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
1539 "fourcc", GST_TYPE_FOURCC, fourcc,
1540 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1541 pad->granulerate_d, NULL);
1543 gst_caps_set_simple (pad->caps,
1544 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1546 "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
1548 GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
1550 pad->n_header_packets = 1;
1551 pad->frame_size = 1;
1558 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1560 guint8 *data = packet->packet;
1563 pad->granulerate_n = 10000000;
1564 time_unit = GST_READ_UINT64_LE (data + 17);
1565 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1566 GST_WARNING ("timeunit is out of range");
1568 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1570 GST_LOG ("fps = %d/%d = %.3f",
1571 pad->granulerate_n, pad->granulerate_d,
1572 (double) pad->granulerate_n / pad->granulerate_d);
1574 if (pad->granulerate_d <= 0)
1577 pad->caps = gst_caps_new_simple ("text/plain", NULL);
1579 pad->n_header_packets = 1;
1581 pad->is_ogm_text = TRUE;
1582 pad->is_sparse = TRUE;
1589 #define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */
1590 #define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */
1591 #define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */
1592 #define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */
1593 #define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */
1594 #define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */
1595 #define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */
1596 #define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */
1598 #define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */
1599 #define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */
1601 #define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */
1602 #define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */
1603 #define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */
1604 #define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */
1608 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1610 guint8 *data = packet->packet;
1615 pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1616 pad->granulerate_d = 1;
1617 GST_LOG ("sample rate: %d", pad->granulerate_n);
1619 format = GST_READ_UINT32_LE (data + 12);
1620 channels = GST_READ_UINT8 (data + 21);
1622 pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1624 if (pad->granulerate_n == 0)
1629 caps = gst_caps_new_simple ("audio/x-raw-int",
1630 "depth", G_TYPE_INT, 8,
1631 "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1634 caps = gst_caps_new_simple ("audio/x-raw-int",
1635 "depth", G_TYPE_INT, 8,
1636 "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
1638 case OGGPCM_FMT_S16_LE:
1639 caps = gst_caps_new_simple ("audio/x-raw-int",
1640 "depth", G_TYPE_INT, 16,
1641 "width", G_TYPE_INT, 16,
1642 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1643 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1645 case OGGPCM_FMT_S16_BE:
1646 caps = gst_caps_new_simple ("audio/x-raw-int",
1647 "depth", G_TYPE_INT, 16,
1648 "width", G_TYPE_INT, 16,
1649 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1650 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1652 case OGGPCM_FMT_S24_LE:
1653 caps = gst_caps_new_simple ("audio/x-raw-int",
1654 "depth", G_TYPE_INT, 24,
1655 "width", G_TYPE_INT, 24,
1656 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1657 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1659 case OGGPCM_FMT_S24_BE:
1660 caps = gst_caps_new_simple ("audio/x-raw-int",
1661 "depth", G_TYPE_INT, 24,
1662 "width", G_TYPE_INT, 24,
1663 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1664 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1666 case OGGPCM_FMT_S32_LE:
1667 caps = gst_caps_new_simple ("audio/x-raw-int",
1668 "depth", G_TYPE_INT, 32,
1669 "width", G_TYPE_INT, 32,
1670 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1671 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1673 case OGGPCM_FMT_S32_BE:
1674 caps = gst_caps_new_simple ("audio/x-raw-int",
1675 "depth", G_TYPE_INT, 32,
1676 "width", G_TYPE_INT, 32,
1677 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1678 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1680 case OGGPCM_FMT_ULAW:
1681 caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
1683 case OGGPCM_FMT_ALAW:
1684 caps = gst_caps_new_simple ("audio/x-alaw", NULL);
1686 case OGGPCM_FMT_FLT32_LE:
1687 caps = gst_caps_new_simple ("audio/x-raw-float",
1688 "width", G_TYPE_INT, 32,
1689 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1691 case OGGPCM_FMT_FLT32_BE:
1692 caps = gst_caps_new_simple ("audio/x-raw-float",
1693 "width", G_TYPE_INT, 32,
1694 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1696 case OGGPCM_FMT_FLT64_LE:
1697 caps = gst_caps_new_simple ("audio/x-raw-float",
1698 "width", G_TYPE_INT, 64,
1699 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1701 case OGGPCM_FMT_FLT64_BE:
1702 caps = gst_caps_new_simple ("audio/x-raw-float",
1703 "width", G_TYPE_INT, 64,
1704 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1710 gst_caps_set_simple (caps, "audio/x-raw-int",
1711 "rate", G_TYPE_INT, pad->granulerate_n,
1712 "channels", G_TYPE_INT, channels, NULL);
1721 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1723 guint8 *data = packet->packet;
1725 pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1726 pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1727 pad->granuleshift = data[28];
1728 GST_LOG ("sample rate: %d", pad->granulerate_n);
1730 pad->n_header_packets = 3;
1732 if (pad->granulerate_n == 0)
1735 data += 4 + (4 + 4 + 4);
1736 GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1737 GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1739 pad->caps = gst_caps_new_simple ("text/x-cmml", NULL);
1740 pad->always_flush_page = TRUE;
1741 pad->is_sparse = TRUE;
1749 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1751 guint8 *data = packet->packet;
1753 pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1754 pad->granulerate_d = 1;
1755 pad->granuleshift = 0;
1756 GST_LOG ("sample rate: %d", pad->granulerate_n);
1758 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1759 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1761 if (pad->granulerate_n == 0)
1764 pad->caps = gst_caps_new_simple ("audio/x-celt",
1765 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1773 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1775 guint8 *data = packet->packet;
1776 const char *category;
1778 if (packet->bytes < 64)
1781 pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1782 pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1783 pad->granuleshift = GST_READ_UINT8 (data + 15);
1784 GST_LOG ("sample rate: %d", pad->granulerate_n);
1786 pad->n_header_packets = GST_READ_UINT8 (data + 11);
1787 GST_LOG ("kate header packets: %d", pad->n_header_packets);
1789 if (pad->granulerate_n == 0)
1792 category = (const char *) data + 48;
1793 if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1794 strcmp (category, "spu-subtitles") == 0 ||
1795 strcmp (category, "K-SPU") == 0) {
1796 pad->caps = gst_caps_new_simple ("subtitle/x-kate", NULL);
1798 pad->caps = gst_caps_new_simple ("application/x-kate", NULL);
1801 pad->is_sparse = TRUE;
1802 pad->always_flush_page = TRUE;
1808 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1812 if (packet->bytes < 1)
1815 switch (packet->packet[0]) {
1816 case 0x00: /* text data */
1817 if (packet->bytes < 1 + 8 * 2) {
1820 duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
1826 duration = GST_CLOCK_TIME_NONE;
1834 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
1836 GstTagList *list = NULL;
1838 if (packet->bytes <= 0)
1841 switch (packet->packet[0]) {
1843 const gchar *canonical;
1846 if (packet->bytes < 64) {
1847 GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
1851 /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
1852 memcpy (language, packet->packet + 32, 16);
1855 /* language is an ISO 639-1 code or RFC 3066 language code, we
1856 * truncate to ISO 639-1 */
1857 g_strdelimit (language, NULL, '\0');
1858 canonical = gst_tag_get_language_code_iso_639_1 (language);
1860 list = gst_tag_list_new_full (GST_TAG_LANGUAGE_CODE, canonical, NULL);
1862 GST_WARNING ("Unknown or invalid language code %s, ignored", language);
1867 tag_list_from_vorbiscomment_packet (packet,
1868 (const guint8 *) "\201kate\0\0\0\0", 9, &list);
1876 /* ensure the comment packet cannot override the category/language
1877 from the identification header */
1878 gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL);
1879 gst_tag_list_free (list);
1881 pad->taglist = list;
1888 setup_opus_mapper (GstOggStream * pad, ogg_packet * packet)
1890 if (packet->bytes < 19)
1893 pad->granulerate_n = 48000;
1894 pad->granulerate_d = 1;
1895 pad->granuleshift = 0;
1896 pad->n_header_packets = 2;
1898 /* pre-skip is in samples at 48000 Hz, which matches granule one for one */
1899 pad->granule_offset = -GST_READ_UINT16_LE (packet->packet + 10);
1900 GST_INFO ("Opus has a pre-skip of %" G_GINT64_FORMAT " samples",
1901 -pad->granule_offset);
1903 pad->caps = gst_caps_new_simple ("audio/x-opus", NULL);
1909 is_header_opus (GstOggStream * pad, ogg_packet * packet)
1911 return packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4);
1915 packet_duration_opus (GstOggStream * pad, ogg_packet * packet)
1917 static const guint64 durations[32] = {
1918 480, 960, 1920, 2880, /* Silk NB */
1919 480, 960, 1920, 2880, /* Silk MB */
1920 480, 960, 1920, 2880, /* Silk WB */
1921 480, 960, /* Hybrid SWB */
1922 480, 960, /* Hybrid FB */
1923 120, 240, 480, 960, /* CELT NB */
1924 120, 240, 480, 960, /* CELT NB */
1925 120, 240, 480, 960, /* CELT NB */
1926 120, 240, 480, 960, /* CELT NB */
1930 gint64 frame_duration;
1934 if (packet->bytes < 1)
1938 if (is_header_opus (pad, packet))
1941 toc = packet->packet[0];
1943 frame_duration = durations[toc >> 3];
1955 if (packet->bytes < 2) {
1956 GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
1959 nframes = packet->packet[1] & 63;
1963 duration = nframes * frame_duration;
1964 if (duration > 5760) {
1965 GST_WARNING ("Opus packet duration > 120 ms, invalid");
1968 GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
1969 frame_duration / 48.f, nframes, duration / 48.f);
1974 extract_tags_opus (GstOggStream * pad, ogg_packet * packet)
1976 if (packet->bytes >= 8 && memcmp (packet->packet, "OpusTags", 8) == 0) {
1977 tag_list_from_vorbiscomment_packet (packet,
1978 (const guint8 *) "OpusTags", 8, &pad->taglist);
1984 /* indent hates our freedoms */
1985 const GstOggMap mappers[] = {
1987 "\200theora", 7, 42,
1989 setup_theora_mapper,
1990 granulepos_to_granule_theora,
1991 granule_to_granulepos_default,
1992 is_granulepos_keyframe_theora,
1993 is_packet_keyframe_theora,
1995 packet_duration_constant,
2000 "\001vorbis", 7, 22,
2002 setup_vorbis_mapper,
2003 granulepos_to_granule_default,
2004 granule_to_granulepos_default,
2005 is_granulepos_keyframe_true,
2006 is_packet_keyframe_true,
2008 packet_duration_vorbis,
2016 granulepos_to_granule_default,
2017 granule_to_granulepos_default,
2018 is_granulepos_keyframe_true,
2019 is_packet_keyframe_true,
2021 packet_duration_constant,
2039 "CMML\0\0\0\0", 8, 0,
2053 "application/x-annodex",
2054 setup_fishead_mapper,
2055 granulepos_to_granule_default,
2056 granule_to_granulepos_default,
2066 "application/octet-stream",
2067 setup_fishead_mapper,
2081 granulepos_to_granule_default,
2082 granule_to_granulepos_default,
2083 is_granulepos_keyframe_true,
2084 is_packet_keyframe_true,
2086 packet_duration_flac,
2094 granulepos_to_granule_default,
2095 granule_to_granulepos_default,
2096 is_granulepos_keyframe_true,
2097 is_packet_keyframe_true,
2099 packet_duration_flac,
2105 "application/octet-stream",
2119 granulepos_to_granule_default,
2120 granule_to_granulepos_default,
2124 packet_duration_constant,
2129 "\200kate\0\0\0", 8, 0,
2132 granulepos_to_granule_default,
2133 granule_to_granulepos_default,
2137 packet_duration_kate,
2145 granulepos_to_granule_dirac,
2146 granule_to_granulepos_dirac,
2150 packet_duration_constant,
2151 granulepos_to_key_granule_dirac,
2158 granulepos_to_granule_vp8,
2159 granule_to_granulepos_vp8,
2163 packet_duration_vp8,
2164 granulepos_to_key_granule_vp8,
2171 granulepos_to_granule_default,
2172 granule_to_granulepos_default,
2176 packet_duration_opus,
2181 "\001audio\0\0\0", 9, 53,
2182 "application/x-ogm-audio",
2183 setup_ogmaudio_mapper,
2184 granulepos_to_granule_default,
2185 granule_to_granulepos_default,
2186 is_granulepos_keyframe_true,
2187 is_packet_keyframe_true,
2189 packet_duration_ogm,
2194 "\001video\0\0\0", 9, 53,
2195 "application/x-ogm-video",
2196 setup_ogmvideo_mapper,
2197 granulepos_to_granule_default,
2198 granule_to_granulepos_default,
2202 packet_duration_constant,
2207 "\001text\0\0\0", 9, 9,
2208 "application/x-ogm-text",
2209 setup_ogmtext_mapper,
2210 granulepos_to_granule_default,
2211 granule_to_granulepos_default,
2212 is_granulepos_keyframe_true,
2213 is_packet_keyframe_true,
2215 packet_duration_ogm,
2223 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2228 for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2229 if (packet->bytes >= mappers[i].min_packet_size &&
2230 packet->bytes >= mappers[i].id_length &&
2231 memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
2233 GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2235 if (mappers[i].setup_func)
2236 ret = mappers[i].setup_func (pad, packet);
2241 GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2245 GST_WARNING ("mapper '%s' did not accept setup header",
2246 mappers[i].media_type);
2255 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2256 const GstCaps * caps)
2258 const GstStructure *structure;
2259 const GstBuffer *buf;
2260 const GValue *streamheader;
2261 const GValue *first_element;
2264 GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2269 structure = gst_caps_get_structure (caps, 0);
2270 streamheader = gst_structure_get_value (structure, "streamheader");
2272 if (streamheader == NULL) {
2273 GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2277 if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2278 GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2282 if (gst_value_array_get_size (streamheader) == 0) {
2283 GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2287 first_element = gst_value_array_get_value (streamheader, 0);
2289 if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2290 GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2294 buf = gst_value_get_buffer (first_element);
2295 if (buf == NULL || GST_BUFFER_SIZE (buf) == 0) {
2296 GST_ERROR ("invalid first streamheader buffer");
2300 GST_MEMDUMP ("streamheader", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
2302 packet.packet = GST_BUFFER_DATA (buf);
2303 packet.bytes = GST_BUFFER_SIZE (buf);
2305 GST_INFO ("Found headers on caps, using those to determine type");
2306 return gst_ogg_stream_setup_map (pad, &packet);