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 gchar *encoder = NULL;
355 g_return_val_if_fail (tags != NULL, FALSE);
357 list = gst_tag_list_from_vorbiscomment (packet->packet, packet->bytes,
358 id_data, id_data_length, &encoder);
361 GST_WARNING ("failed to decode vorbis comments");
368 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
375 gst_tag_list_free (*tags);
384 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
386 guint8 *data = packet->packet;
387 guint w, h, par_d, par_n;
388 guint8 vmaj, vmin, vrev;
394 w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
395 h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
397 pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
398 pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
400 par_n = GST_READ_UINT24_BE (data + 30);
401 par_d = GST_READ_UINT24_BE (data + 33);
403 GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
404 pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
406 /* 2 bits + 3 bits = 5 bits KFGSHIFT */
407 pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
408 (GST_READ_UINT8 (data + 41) >> 5);
409 GST_LOG ("granshift: %d", pad->granuleshift);
411 pad->is_video = TRUE;
412 pad->n_header_packets = 3;
415 pad->bitrate = GST_READ_UINT24_BE (data + 37);
416 GST_LOG ("bit rate: %d", pad->bitrate);
418 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
419 GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
423 /* The interpretation of the granule position has changed with 3.2.1.
424 The granule is now made from the number of frames encoded, rather than
425 the index of the frame being encoded - so there is a difference of 1. */
426 pad->theora_has_zero_keyoffset =
427 ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201;
429 pad->caps = gst_caps_new_empty_simple ("video/x-theora");
431 if (w > 0 && h > 0) {
432 gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
433 G_TYPE_INT, h, NULL);
436 /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
437 if (par_n == 0 || par_d == 0)
440 /* only add framerate now so caps look prettier, with width/height first */
441 gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
442 pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
443 GST_TYPE_FRACTION, par_n, par_d, NULL);
449 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
451 gint64 keyindex, keyoffset;
453 if (pad->granuleshift != 0) {
454 keyindex = granulepos >> pad->granuleshift;
455 keyoffset = granulepos - (keyindex << pad->granuleshift);
456 if (pad->theora_has_zero_keyoffset) {
459 return keyindex + keyoffset;
466 is_granulepos_keyframe_theora (GstOggStream * pad, gint64 granulepos)
470 if (granulepos == (gint64) - 1)
473 frame_mask = (1 << pad->granuleshift) - 1;
475 return ((granulepos & frame_mask) == 0);
479 is_packet_keyframe_theora (GstOggStream * pad, ogg_packet * packet)
481 if (packet->bytes == 0)
483 return (packet->packet[0] & 0xc0) == 0x00;
487 is_header_theora (GstOggStream * pad, ogg_packet * packet)
489 return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
493 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
495 if (packet->bytes > 0 && packet->packet[0] == 0x81) {
496 tag_list_from_vorbiscomment_packet (packet,
497 (const guint8 *) "\201theora", 7, &pad->taglist);
500 pad->taglist = gst_tag_list_new_empty ();
503 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
504 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
511 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
514 DiracSequenceHeader header;
516 ret = dirac_sequence_header_parse (&header, packet->packet + 13,
519 GST_DEBUG ("Failed to parse Dirac sequence header");
523 pad->is_video = TRUE;
524 pad->always_flush_page = TRUE;
525 pad->granulerate_n = header.frame_rate_numerator * 2;
526 pad->granulerate_d = header.frame_rate_denominator;
527 pad->granuleshift = 22;
528 pad->n_header_packets = 1;
531 if (header.interlaced_coding != 0) {
532 GST_DEBUG ("non-progressive Dirac coding not implemented");
536 pad->caps = gst_caps_new_simple ("video/x-dirac",
537 "width", G_TYPE_INT, header.width,
538 "height", G_TYPE_INT, header.height,
539 "interlaced", G_TYPE_BOOLEAN, header.interlaced,
540 "pixel-aspect-ratio", GST_TYPE_FRACTION,
541 header.aspect_ratio_numerator, header.aspect_ratio_denominator,
542 "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
543 header.frame_rate_denominator, NULL);
548 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
550 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
556 if (granulepos == -1)
559 dist_h = (granulepos >> 22) & 0xff;
560 dist_l = granulepos & 0xff;
561 dist = (dist_h << 8) | dist_l;
567 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
573 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
574 delay = (gp >> 9) & 0x1fff;
577 GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
583 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
584 gint64 keyframe_granule)
586 /* This conversion requires knowing more details about the Dirac
592 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
601 if (gp == -1 || gp == 0)
604 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
605 dist_h = (gp >> 22) & 0xff;
607 dist = (dist_h << 8) | dist_l;
608 delay = (gp >> 9) & 0x1fff;
611 return dt - 2 * dist + 4;
617 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
619 gint width, height, par_n, par_d, fps_n, fps_d;
621 if (packet->bytes < 26) {
622 GST_DEBUG ("Failed to parse VP8 BOS page");
626 width = GST_READ_UINT16_BE (packet->packet + 8);
627 height = GST_READ_UINT16_BE (packet->packet + 10);
628 par_n = GST_READ_UINT24_BE (packet->packet + 12);
629 par_d = GST_READ_UINT24_BE (packet->packet + 15);
630 fps_n = GST_READ_UINT32_BE (packet->packet + 18);
631 fps_d = GST_READ_UINT32_BE (packet->packet + 22);
633 pad->is_video = TRUE;
635 pad->granulerate_n = fps_n;
636 pad->granulerate_d = fps_d;
637 pad->n_header_packets = 2;
640 pad->caps = gst_caps_new_simple ("video/x-vp8",
641 "width", G_TYPE_INT, width,
642 "height", G_TYPE_INT, height,
643 "pixel-aspect-ratio", GST_TYPE_FRACTION,
644 par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
650 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
652 guint64 gpos = granulepos;
654 if (granulepos == -1)
657 /* Get rid of flags */
660 return ((gpos & 0x07ffffff) == 0);
664 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
666 guint64 gp = (guint64) gpos;
671 dist = (gp >> 3) & 0x07ffffff;
673 GST_DEBUG ("pt %u, dist %u", pt, dist);
679 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
680 gint64 keyframe_granule)
682 /* FIXME: This requires to look into the content of the packets
683 * because the simple granule counter doesn't know about invisible
689 /* Check if this packet contains an invisible frame or not */
691 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
695 if (packet->bytes < 3)
698 hdr = GST_READ_UINT24_LE (packet->packet);
700 return (((hdr >> 4) & 1) != 0) ? 1 : 0;
704 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
706 guint64 gp = granulepos;
707 guint64 pts = (gp >> 32);
708 guint32 dist = (gp >> 3) & 0x07ffffff;
710 if (granulepos == -1 || granulepos == 0)
720 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
722 if (packet->bytes >= 5 && packet->packet[0] == 0x4F &&
723 packet->packet[1] == 0x56 && packet->packet[2] == 0x50 &&
724 packet->packet[3] == 0x38 && packet->packet[4] == 0x30)
730 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
732 if (packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) {
733 tag_list_from_vorbiscomment_packet (packet,
734 (const guint8 *) "OVP80\2 ", 7, &pad->taglist);
741 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
743 guint8 *data = packet->packet;
747 pad->version = GST_READ_UINT32_LE (data);
749 chans = GST_READ_UINT8 (data);
751 pad->granulerate_n = GST_READ_UINT32_LE (data);
752 pad->granulerate_d = 1;
753 pad->granuleshift = 0;
756 GST_LOG ("sample rate: %d", pad->granulerate_n);
759 pad->bitrate_upper = GST_READ_UINT32_LE (data);
761 pad->bitrate_nominal = GST_READ_UINT32_LE (data);
763 pad->bitrate_lower = GST_READ_UINT32_LE (data);
765 if (pad->bitrate_nominal > 0)
766 pad->bitrate = pad->bitrate_nominal;
768 if (pad->bitrate_upper > 0 && !pad->bitrate)
769 pad->bitrate = pad->bitrate_upper;
771 if (pad->bitrate_lower > 0 && !pad->bitrate)
772 pad->bitrate = pad->bitrate_lower;
774 GST_LOG ("bit rate: %d", pad->bitrate);
776 pad->n_header_packets = 3;
778 if (pad->granulerate_n == 0)
781 parse_vorbis_header_packet (pad, packet);
783 pad->caps = gst_caps_new_simple ("audio/x-vorbis",
784 "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
791 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
793 if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
796 if (packet->packet[0] == 5) {
797 parse_vorbis_setup_packet (pad, packet);
804 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
806 if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
809 if (((guint8 *) (packet->packet))[0] == 0x03) {
810 tag_list_from_vorbiscomment_packet (packet,
811 (const guint8 *) "\003vorbis", 7, &pad->taglist);
814 pad->taglist = gst_tag_list_new_empty ();
816 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
817 GST_TAG_ENCODER_VERSION, pad->version, NULL);
819 if (pad->bitrate_nominal > 0)
820 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
821 GST_TAG_NOMINAL_BITRATE, (guint) pad->bitrate_nominal, NULL);
823 if (pad->bitrate_upper > 0)
824 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
825 GST_TAG_MAXIMUM_BITRATE, (guint) pad->bitrate_upper, NULL);
827 if (pad->bitrate_lower > 0)
828 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
829 GST_TAG_MINIMUM_BITRATE, (guint) pad->bitrate_lower, NULL);
832 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
833 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
838 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
844 if (packet->bytes == 0 || packet->packet[0] & 1)
847 mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
848 size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
850 if (pad->last_size == 0) {
853 duration = pad->last_size / 4 + size / 4;
855 pad->last_size = size;
857 GST_DEBUG ("duration %d", (int) duration);
866 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
868 guint8 *data = packet->packet;
871 data += 8 + 20 + 4 + 4;
872 pad->granulerate_n = GST_READ_UINT32_LE (data);
873 pad->granulerate_d = 1;
874 pad->granuleshift = 0;
877 chans = GST_READ_UINT32_LE (data);
879 pad->bitrate = GST_READ_UINT32_LE (data);
881 GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
882 GST_LOG ("bit rate: %d", pad->bitrate);
884 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
885 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
886 GST_READ_UINT32_LE (packet->packet + 56);
888 if (pad->granulerate_n == 0)
891 pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
892 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
898 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
900 /* packet 2 must be comment packet */
901 if (packet->bytes > 0 && pad->n_header_packets_seen == 1) {
902 tag_list_from_vorbiscomment_packet (packet, NULL, 0, &pad->taglist);
905 pad->taglist = gst_tag_list_new_empty ();
908 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
909 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
917 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
919 pad->granulerate_n = 0;
920 pad->granulerate_d = 1;
921 pad->granuleshift = 0;
923 pad->n_header_packets = 3;
925 pad->caps = gst_caps_new_empty_simple ("audio/x-flac");
931 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
933 if (pad->n_header_packets_seen == 1) {
934 pad->granulerate_n = (packet->packet[14] << 12) |
935 (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
938 if (pad->n_header_packets_seen < pad->n_header_packets) {
946 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
948 guint8 *data = packet->packet;
951 /* see http://flac.sourceforge.net/ogg_mapping.html */
953 pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
954 pad->granulerate_d = 1;
955 pad->granuleshift = 0;
956 chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
958 GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
960 pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
962 if (pad->granulerate_n == 0)
965 pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
966 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
972 is_header_flac (GstOggStream * pad, ogg_packet * packet)
974 return (packet->bytes > 0 && (packet->packet[0] != 0xff));
978 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
980 int block_size_index;
982 if (packet->bytes < 4)
985 block_size_index = packet->packet[2] >> 4;
986 if (block_size_index == 1)
988 if (block_size_index >= 2 && block_size_index <= 5) {
989 return 576 << (block_size_index - 2);
991 if (block_size_index >= 8) {
992 return 256 << (block_size_index - 8);
994 if (block_size_index == 6 || block_size_index == 7) {
995 guint len, bytes = (block_size_index - 6) + 1;
998 if (packet->bytes < 4 + 1 + bytes)
1000 tmp = packet->packet[4];
1003 while (tmp & 0x80) {
1011 if (packet->bytes < 4 + len + bytes)
1014 return packet->packet[4 + len] + 1;
1016 return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
1023 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
1025 if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
1026 tag_list_from_vorbiscomment_packet (packet,
1027 packet->packet, 4, &pad->taglist);
1034 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
1037 gint64 prestime_n, prestime_d;
1038 gint64 basetime_n, basetime_d;
1040 data = packet->packet;
1042 data += 8; /* header */
1044 pad->skeleton_major = GST_READ_UINT16_LE (data);
1046 pad->skeleton_minor = GST_READ_UINT16_LE (data);
1049 prestime_n = (gint64) GST_READ_UINT64_LE (data);
1051 prestime_d = (gint64) GST_READ_UINT64_LE (data);
1053 basetime_n = (gint64) GST_READ_UINT64_LE (data);
1055 basetime_d = (gint64) GST_READ_UINT64_LE (data);
1058 /* FIXME: we don't use basetime anywhere in the demuxer! */
1059 if (basetime_d != 0)
1060 pad->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
1064 if (prestime_d != 0)
1065 pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1069 /* Ogg Skeleton 3.3+ streams provide additional information in the header */
1070 if (packet->bytes >= SKELETON_FISHEAD_3_3_MIN_SIZE && pad->skeleton_major == 3
1071 && pad->skeleton_minor > 0) {
1072 gint64 firstsampletime_n, firstsampletime_d;
1073 gint64 lastsampletime_n, lastsampletime_d;
1074 gint64 firstsampletime, lastsampletime;
1075 guint64 segment_length, content_offset;
1077 firstsampletime_n = GST_READ_UINT64_LE (data + 64);
1078 firstsampletime_d = GST_READ_UINT64_LE (data + 72);
1079 lastsampletime_n = GST_READ_UINT64_LE (data + 80);
1080 lastsampletime_d = GST_READ_UINT64_LE (data + 88);
1081 segment_length = GST_READ_UINT64_LE (data + 96);
1082 content_offset = GST_READ_UINT64_LE (data + 104);
1084 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1085 firstsampletime_n, firstsampletime_d);
1086 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1087 lastsampletime_n, lastsampletime_d);
1088 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1089 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1091 if (firstsampletime_d > 0)
1092 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1093 firstsampletime_n, firstsampletime_d);
1095 firstsampletime = 0;
1097 if (lastsampletime_d > 0)
1098 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1099 lastsampletime_n, lastsampletime_d);
1103 if (lastsampletime > firstsampletime)
1104 pad->total_time = lastsampletime - firstsampletime;
1106 pad->total_time = -1;
1108 GST_INFO ("skeleton fishead parsed total: %" GST_TIME_FORMAT,
1109 GST_TIME_ARGS (pad->total_time));
1110 } else if (packet->bytes >= SKELETON_FISHEAD_4_0_MIN_SIZE
1111 && pad->skeleton_major == 4) {
1112 guint64 segment_length, content_offset;
1114 segment_length = GST_READ_UINT64_LE (data + 64);
1115 content_offset = GST_READ_UINT64_LE (data + 72);
1117 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1118 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1120 pad->total_time = -1;
1123 GST_INFO ("skeleton fishead %u.%u parsed (basetime: %" GST_TIME_FORMAT
1124 ", prestime: %" GST_TIME_FORMAT ")", pad->skeleton_major,
1125 pad->skeleton_minor, GST_TIME_ARGS (pad->basetime),
1126 GST_TIME_ARGS (pad->prestime));
1128 pad->is_skeleton = TRUE;
1129 pad->is_sparse = TRUE;
1131 pad->caps = gst_caps_new_empty_simple ("application/x-ogg-skeleton");
1137 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1138 guint32 * serialno, GstOggSkeleton * type)
1140 GstOggSkeleton stype;
1141 guint serial_offset;
1143 if (size != 0 && size < SKELETON_FISBONE_MIN_SIZE) {
1144 GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1149 /* Skeleton EOS packet is zero bytes */
1151 } else if (memcmp (data, "fisbone\0", 8) == 0) {
1152 GST_INFO ("got fisbone packet");
1153 stype = GST_OGG_SKELETON_FISBONE;
1155 } else if (memcmp (data, "index\0", 6) == 0) {
1156 GST_INFO ("got index packet");
1157 stype = GST_OGG_SKELETON_INDEX;
1159 } else if (memcmp (data, "fishead\0", 8) == 0) {
1162 GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1167 *serialno = GST_READ_UINT32_LE (data + serial_offset);
1176 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1177 const guint8 * data, guint size, GstClockTime * p_start_time)
1179 GstClockTime start_time;
1180 gint64 start_granule;
1182 if (pad->have_fisbone) {
1183 GST_DEBUG ("already have fisbone, ignoring second one");
1187 /* skip "fisbone\0" + headers offset + serialno + num headers */
1188 data += 8 + 4 + 4 + 4;
1190 pad->have_fisbone = TRUE;
1192 /* We don't overwrite whatever was set before by the format-specific
1193 setup: skeleton contains wrong information sometimes, and the codec
1194 headers are authoritative.
1195 So we only gather information that was not already filled out by
1196 the mapper setup. This should hopefully allow handling unknown
1197 streams a bit better, while not trashing correct setup from bad
1199 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
1200 pad->granulerate_n = GST_READ_UINT64_LE (data);
1201 pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
1203 if (pad->granuleshift < 0) {
1204 pad->granuleshift = GST_READ_UINT8 (data + 28);
1207 start_granule = GST_READ_UINT64_LE (data + 16);
1208 pad->preroll = GST_READ_UINT32_LE (data + 24);
1210 start_time = granulepos_to_granule_default (pad, start_granule);
1212 GST_INFO ("skeleton fisbone parsed "
1213 "(start time: %" GST_TIME_FORMAT
1214 " granulerate_n: %d granulerate_d: %d "
1215 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
1216 GST_TIME_ARGS (start_time),
1217 pad->granulerate_n, pad->granulerate_d, pad->preroll, pad->granuleshift);
1220 *p_start_time = start_time;
1226 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1234 if (G_UNLIKELY (*size < 1))
1238 *result |= ((byte & 0x7f) << shift);
1243 } while ((byte & 0x80) != 0x80);
1249 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1250 const guint8 * data, guint size)
1252 guint64 i, n_keypoints, isize;
1253 guint64 offset, timestamp;
1254 guint64 offset_d, timestamp_d;
1257 GST_DEBUG ("already have index, ignoring second one");
1261 if ((skel_pad->skeleton_major == 3 && size < 26) ||
1262 (skel_pad->skeleton_major == 4 && size < 62)) {
1263 GST_WARNING ("small index packet of size %u, ignoring", size);
1267 /* skip "index\0" + serialno */
1271 n_keypoints = GST_READ_UINT64_LE (data);
1276 pad->kp_denom = GST_READ_UINT64_LE (data);
1277 if (pad->kp_denom == 0)
1283 if (skel_pad->skeleton_major == 4) {
1284 gint64 firstsampletime_n;
1285 gint64 lastsampletime_n;
1286 gint64 firstsampletime, lastsampletime;
1288 firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1289 lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1291 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1292 firstsampletime_n, pad->kp_denom);
1293 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1294 lastsampletime_n, pad->kp_denom);
1296 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1297 firstsampletime_n, pad->kp_denom);
1298 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1299 lastsampletime_n, pad->kp_denom);
1301 if (lastsampletime > firstsampletime)
1302 pad->total_time = lastsampletime - firstsampletime;
1304 pad->total_time = -1;
1306 GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1307 GST_TIME_ARGS (pad->total_time));
1313 GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1314 G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1316 pad->index = g_try_new (GstOggIndex, n_keypoints);
1324 for (i = 0; i < n_keypoints; i++) {
1326 if (!read_vlc (&data, &size, &offset_d))
1328 if (!read_vlc (&data, &size, ×tamp_d))
1332 timestamp += timestamp_d;
1334 pad->index[i].offset = offset;
1335 pad->index[i].timestamp = timestamp;
1338 GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1341 if (isize != n_keypoints) {
1342 GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1343 G_GUINT64_FORMAT, n_keypoints, isize);
1345 pad->n_index = isize;
1346 /* try to use the index to estimate the bitrate */
1348 guint64 so, eo, st, et, b, t;
1350 /* get start and end offset and timestamps */
1351 so = pad->index[0].offset;
1352 st = pad->index[0].timestamp;
1353 eo = pad->index[isize - 1].offset;
1354 et = pad->index[isize - 1].timestamp;
1359 GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1361 /* this is the total stream bitrate according to this index */
1362 pad->idx_bitrate = gst_util_uint64_scale (8 * b, pad->kp_denom, t);
1364 GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1371 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1374 if (index->timestamp < *ts)
1376 else if (index->timestamp > *ts)
1383 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1384 guint64 * timestamp, guint64 * offset)
1390 n_index = pad->n_index;
1391 if (n_index == 0 || pad->index == NULL)
1394 ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1395 GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1398 gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1399 (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1405 GST_INFO ("found at index %u", (guint) (best - pad->index));
1408 *offset = best->offset;
1411 gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1416 /* Do we need these for something?
1417 * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
1418 * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
1419 * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
1420 * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
1421 * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
1422 * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
1426 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1428 if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1435 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1437 if (!(packet->packet[0] & 1) && (packet->packet[0] & 3 && pad->is_ogm_text)) {
1438 tag_list_from_vorbiscomment_packet (packet,
1439 (const guint8 *) "\003vorbis", 7, &pad->taglist);
1444 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1451 data = packet->packet;
1452 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1454 if (offset > packet->bytes) {
1455 GST_ERROR ("buffer too small");
1460 for (n = offset - 1; n > 0; n--) {
1461 samples = (samples << 8) | data[n];
1468 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1470 guint8 *data = packet->packet;
1474 pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1475 pad->granulerate_d = 1;
1477 fourcc = GST_READ_UINT32_LE (data + 9);
1478 fstr = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1479 GST_DEBUG ("fourcc: %s", fstr);
1481 /* FIXME: Need to do something with the reorder map */
1483 gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL, NULL);
1485 GST_LOG ("sample rate: %d", pad->granulerate_n);
1486 if (pad->granulerate_n == 0)
1490 gst_caps_set_simple (pad->caps,
1491 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1493 pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
1494 "fourcc", G_TYPE_STRING, fstr,
1495 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1499 pad->n_header_packets = 1;
1506 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1508 guint8 *data = packet->packet;
1514 GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
1515 GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
1517 pad->is_video = TRUE;
1518 pad->granulerate_n = 10000000;
1519 time_unit = GST_READ_UINT64_LE (data + 17);
1520 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1521 GST_WARNING ("timeunit is out of range");
1523 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1525 GST_LOG ("fps = %d/%d = %.3f",
1526 pad->granulerate_n, pad->granulerate_d,
1527 (double) pad->granulerate_n / pad->granulerate_d);
1529 fourcc = GST_READ_UINT32_LE (data + 9);
1530 width = GST_READ_UINT32_LE (data + 45);
1531 height = GST_READ_UINT32_LE (data + 49);
1532 fstr = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1533 GST_DEBUG ("fourcc: %s", fstr);
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", G_TYPE_STRING, fstr,
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);
1551 pad->n_header_packets = 1;
1552 pad->frame_size = 1;
1559 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1561 guint8 *data = packet->packet;
1564 pad->granulerate_n = 10000000;
1565 time_unit = GST_READ_UINT64_LE (data + 17);
1566 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1567 GST_WARNING ("timeunit is out of range");
1569 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1571 GST_LOG ("fps = %d/%d = %.3f",
1572 pad->granulerate_n, pad->granulerate_d,
1573 (double) pad->granulerate_n / pad->granulerate_d);
1575 if (pad->granulerate_d <= 0)
1578 pad->caps = gst_caps_new_empty_simple ("text/plain");
1580 pad->n_header_packets = 1;
1582 pad->is_ogm_text = TRUE;
1583 pad->is_sparse = TRUE;
1590 #define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */
1591 #define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */
1592 #define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */
1593 #define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */
1594 #define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */
1595 #define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */
1596 #define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */
1597 #define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */
1599 #define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */
1600 #define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */
1602 #define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */
1603 #define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */
1604 #define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */
1605 #define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */
1609 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1611 guint8 *data = packet->packet;
1616 pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1617 pad->granulerate_d = 1;
1618 GST_LOG ("sample rate: %d", pad->granulerate_n);
1620 format = GST_READ_UINT32_LE (data + 12);
1621 channels = GST_READ_UINT8 (data + 21);
1623 pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1625 if (pad->granulerate_n == 0)
1630 caps = gst_caps_new_simple ("audio/x-raw",
1631 "format", G_TYPE_STRING, "S8", NULL);
1634 caps = gst_caps_new_simple ("audio/x-raw",
1635 "format", G_TYPE_STRING, "U8", NULL);
1637 case OGGPCM_FMT_S16_LE:
1638 caps = gst_caps_new_simple ("audio/x-raw",
1639 "format", G_TYPE_STRING, "S16LE", NULL);
1641 case OGGPCM_FMT_S16_BE:
1642 caps = gst_caps_new_simple ("audio/x-raw",
1643 "format", G_TYPE_STRING, "S16BE", NULL);
1645 case OGGPCM_FMT_S24_LE:
1646 caps = gst_caps_new_simple ("audio/x-raw",
1647 "format", G_TYPE_STRING, "S24LE", NULL);
1649 case OGGPCM_FMT_S24_BE:
1650 caps = gst_caps_new_simple ("audio/x-raw",
1651 "format", G_TYPE_STRING, "S24BE", NULL);
1653 case OGGPCM_FMT_S32_LE:
1654 caps = gst_caps_new_simple ("audio/x-raw",
1655 "format", G_TYPE_STRING, "S32LE", NULL);
1657 case OGGPCM_FMT_S32_BE:
1658 caps = gst_caps_new_simple ("audio/x-raw",
1659 "format", G_TYPE_STRING, "S32BE", NULL);
1661 case OGGPCM_FMT_ULAW:
1662 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
1664 case OGGPCM_FMT_ALAW:
1665 caps = gst_caps_new_empty_simple ("audio/x-alaw");
1667 case OGGPCM_FMT_FLT32_LE:
1668 caps = gst_caps_new_simple ("audio/x-raw",
1669 "format", G_TYPE_STRING, "F32LE", NULL);
1671 case OGGPCM_FMT_FLT32_BE:
1672 caps = gst_caps_new_simple ("audio/x-raw",
1673 "format", G_TYPE_STRING, "F32BE", NULL);
1675 case OGGPCM_FMT_FLT64_LE:
1676 caps = gst_caps_new_simple ("audio/x-raw",
1677 "format", G_TYPE_STRING, "F64LE", NULL);
1679 case OGGPCM_FMT_FLT64_BE:
1680 caps = gst_caps_new_simple ("audio/x-raw",
1681 "format", G_TYPE_STRING, "F64BE", NULL);
1687 gst_caps_set_simple (caps,
1688 "layout", G_TYPE_STRING, "interleaved",
1689 "rate", G_TYPE_INT, pad->granulerate_n,
1690 "channels", G_TYPE_INT, channels, NULL);
1699 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1701 guint8 *data = packet->packet;
1703 pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1704 pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1705 pad->granuleshift = data[28];
1706 GST_LOG ("sample rate: %d", pad->granulerate_n);
1708 pad->n_header_packets = 3;
1710 if (pad->granulerate_n == 0)
1713 data += 4 + (4 + 4 + 4);
1714 GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1715 GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1717 pad->caps = gst_caps_new_empty_simple ("text/x-cmml");
1718 pad->always_flush_page = TRUE;
1719 pad->is_sparse = TRUE;
1727 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1729 guint8 *data = packet->packet;
1731 pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1732 pad->granulerate_d = 1;
1733 pad->granuleshift = 0;
1734 GST_LOG ("sample rate: %d", pad->granulerate_n);
1736 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1737 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1739 if (pad->granulerate_n == 0)
1742 pad->caps = gst_caps_new_simple ("audio/x-celt",
1743 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1751 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1753 guint8 *data = packet->packet;
1754 const char *category;
1756 if (packet->bytes < 64)
1759 pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1760 pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1761 pad->granuleshift = GST_READ_UINT8 (data + 15);
1762 GST_LOG ("sample rate: %d", pad->granulerate_n);
1764 pad->n_header_packets = GST_READ_UINT8 (data + 11);
1765 GST_LOG ("kate header packets: %d", pad->n_header_packets);
1767 if (pad->granulerate_n == 0)
1770 category = (const char *) data + 48;
1771 if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1772 strcmp (category, "spu-subtitles") == 0 ||
1773 strcmp (category, "K-SPU") == 0) {
1774 pad->caps = gst_caps_new_empty_simple ("subtitle/x-kate");
1776 pad->caps = gst_caps_new_empty_simple ("application/x-kate");
1779 pad->is_sparse = TRUE;
1780 pad->always_flush_page = TRUE;
1786 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1790 if (packet->bytes < 1)
1793 switch (packet->packet[0]) {
1794 case 0x00: /* text data */
1795 if (packet->bytes < 1 + 8 * 2) {
1798 duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
1804 duration = GST_CLOCK_TIME_NONE;
1812 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
1814 GstTagList *list = NULL;
1816 if (packet->bytes <= 0)
1819 switch (packet->packet[0]) {
1821 const gchar *canonical;
1824 if (packet->bytes < 64) {
1825 GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
1829 /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
1830 memcpy (language, packet->packet + 32, 16);
1833 /* language is an ISO 639-1 code or RFC 3066 language code, we
1834 * truncate to ISO 639-1 */
1835 g_strdelimit (language, NULL, '\0');
1836 canonical = gst_tag_get_language_code_iso_639_1 (language);
1838 list = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, canonical, NULL);
1840 GST_WARNING ("Unknown or invalid language code %s, ignored", language);
1845 tag_list_from_vorbiscomment_packet (packet,
1846 (const guint8 *) "\201kate\0\0\0\0", 9, &list);
1854 /* ensure the comment packet cannot override the category/language
1855 from the identification header */
1856 gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL);
1857 gst_tag_list_free (list);
1859 pad->taglist = list;
1866 setup_opus_mapper (GstOggStream * pad, ogg_packet * packet)
1868 if (packet->bytes < 19)
1871 pad->granulerate_n = 48000;
1872 pad->granulerate_d = 1;
1873 pad->granuleshift = 0;
1874 pad->n_header_packets = 2;
1876 /* pre-skip is in samples at 48000 Hz, which matches granule one for one */
1877 pad->granule_offset = -GST_READ_UINT16_LE (packet->packet + 10);
1878 GST_INFO ("Opus has a pre-skip of %" G_GINT64_FORMAT " samples",
1879 -pad->granule_offset);
1881 pad->caps = gst_caps_new_empty_simple ("audio/x-opus");
1887 is_header_opus (GstOggStream * pad, ogg_packet * packet)
1889 return packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4);
1893 packet_duration_opus (GstOggStream * pad, ogg_packet * packet)
1895 static const guint64 durations[32] = {
1896 480, 960, 1920, 2880, /* Silk NB */
1897 480, 960, 1920, 2880, /* Silk MB */
1898 480, 960, 1920, 2880, /* Silk WB */
1899 480, 960, /* Hybrid SWB */
1900 480, 960, /* Hybrid FB */
1901 120, 240, 480, 960, /* CELT NB */
1902 120, 240, 480, 960, /* CELT NB */
1903 120, 240, 480, 960, /* CELT NB */
1904 120, 240, 480, 960, /* CELT NB */
1908 gint64 frame_duration;
1912 if (packet->bytes < 1)
1916 if (is_header_opus (pad, packet))
1919 toc = packet->packet[0];
1921 frame_duration = durations[toc >> 3];
1933 if (packet->bytes < 2) {
1934 GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
1937 nframes = packet->packet[1] & 63;
1941 duration = nframes * frame_duration;
1942 if (duration > 5760) {
1943 GST_WARNING ("Opus packet duration > 120 ms, invalid");
1946 GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
1947 frame_duration / 48.f, nframes, duration / 48.f);
1952 extract_tags_opus (GstOggStream * pad, ogg_packet * packet)
1954 if (packet->bytes >= 8 && memcmp (packet->packet, "OpusTags", 8) == 0) {
1955 tag_list_from_vorbiscomment_packet (packet,
1956 (const guint8 *) "OpusTags", 8, &pad->taglist);
1962 /* indent hates our freedoms */
1963 const GstOggMap mappers[] = {
1965 "\200theora", 7, 42,
1967 setup_theora_mapper,
1968 granulepos_to_granule_theora,
1969 granule_to_granulepos_default,
1970 is_granulepos_keyframe_theora,
1971 is_packet_keyframe_theora,
1973 packet_duration_constant,
1978 "\001vorbis", 7, 22,
1980 setup_vorbis_mapper,
1981 granulepos_to_granule_default,
1982 granule_to_granulepos_default,
1983 is_granulepos_keyframe_true,
1984 is_packet_keyframe_true,
1986 packet_duration_vorbis,
1994 granulepos_to_granule_default,
1995 granule_to_granulepos_default,
1996 is_granulepos_keyframe_true,
1997 is_packet_keyframe_true,
1999 packet_duration_constant,
2017 "CMML\0\0\0\0", 8, 0,
2031 "application/x-annodex",
2032 setup_fishead_mapper,
2033 granulepos_to_granule_default,
2034 granule_to_granulepos_default,
2044 "application/octet-stream",
2045 setup_fishead_mapper,
2059 granulepos_to_granule_default,
2060 granule_to_granulepos_default,
2061 is_granulepos_keyframe_true,
2062 is_packet_keyframe_true,
2064 packet_duration_flac,
2072 granulepos_to_granule_default,
2073 granule_to_granulepos_default,
2074 is_granulepos_keyframe_true,
2075 is_packet_keyframe_true,
2077 packet_duration_flac,
2083 "application/octet-stream",
2097 granulepos_to_granule_default,
2098 granule_to_granulepos_default,
2102 packet_duration_constant,
2107 "\200kate\0\0\0", 8, 0,
2110 granulepos_to_granule_default,
2111 granule_to_granulepos_default,
2115 packet_duration_kate,
2123 granulepos_to_granule_dirac,
2124 granule_to_granulepos_dirac,
2128 packet_duration_constant,
2129 granulepos_to_key_granule_dirac,
2136 granulepos_to_granule_vp8,
2137 granule_to_granulepos_vp8,
2141 packet_duration_vp8,
2142 granulepos_to_key_granule_vp8,
2149 granulepos_to_granule_default,
2150 granule_to_granulepos_default,
2154 packet_duration_opus,
2159 "\001audio\0\0\0", 9, 53,
2160 "application/x-ogm-audio",
2161 setup_ogmaudio_mapper,
2162 granulepos_to_granule_default,
2163 granule_to_granulepos_default,
2164 is_granulepos_keyframe_true,
2165 is_packet_keyframe_true,
2167 packet_duration_ogm,
2172 "\001video\0\0\0", 9, 53,
2173 "application/x-ogm-video",
2174 setup_ogmvideo_mapper,
2175 granulepos_to_granule_default,
2176 granule_to_granulepos_default,
2180 packet_duration_constant,
2185 "\001text\0\0\0", 9, 9,
2186 "application/x-ogm-text",
2187 setup_ogmtext_mapper,
2188 granulepos_to_granule_default,
2189 granule_to_granulepos_default,
2190 is_granulepos_keyframe_true,
2191 is_packet_keyframe_true,
2193 packet_duration_ogm,
2201 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2206 for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2207 if (packet->bytes >= mappers[i].min_packet_size &&
2208 packet->bytes >= mappers[i].id_length &&
2209 memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
2211 GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2213 if (mappers[i].setup_func)
2214 ret = mappers[i].setup_func (pad, packet);
2219 GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2223 GST_WARNING ("mapper '%s' did not accept setup header",
2224 mappers[i].media_type);
2233 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2234 const GstCaps * caps)
2237 const GstStructure *structure;
2238 const GValue *streamheader;
2239 const GValue *first_element;
2244 GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2249 structure = gst_caps_get_structure (caps, 0);
2250 streamheader = gst_structure_get_value (structure, "streamheader");
2252 if (streamheader == NULL) {
2253 GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2257 if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2258 GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2262 if (gst_value_array_get_size (streamheader) == 0) {
2263 GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2267 first_element = gst_value_array_get_value (streamheader, 0);
2269 if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2270 GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2274 buf = gst_value_get_buffer (first_element);
2276 GST_ERROR ("no first streamheader buffer");
2280 if (!gst_buffer_map (buf, &map, GST_MAP_READ) || map.size == 0) {
2281 GST_ERROR ("invalid first streamheader buffer");
2285 GST_MEMDUMP ("streamheader", map.data, map.size);
2287 packet.packet = map.data;
2288 packet.bytes = map.size;
2290 GST_INFO ("Found headers on caps, using those to determine type");
2291 ret = gst_ogg_stream_setup_map (pad, &packet);
2293 gst_buffer_unmap (buf, &map);