1 /* GStreamer Ogg Granulepos Mapping Utility Functions
2 * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
3 * Copyright (C) 2009 David Schleef <ds@schleef.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 #include "gstoggstream.h"
26 #include "dirac_parse.h"
27 #include "vorbis_parse.h"
29 #include <gst/riff/riff-media.h>
34 GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug);
35 GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_setup_debug);
36 #define GST_CAT_DEFAULT gst_ogg_demux_debug
38 typedef struct _GstOggMap GstOggMap;
40 typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad,
42 typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad,
44 typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad,
46 typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad,
47 gint64 granule, gint64 keyframe_granule);
49 /* returns TRUE if the granulepos denotes a key frame */
50 typedef gboolean (*GstOggMapIsKeyFrameFunc) (GstOggStream * pad,
53 /* returns TRUE if the given packet is a stream header packet */
54 typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
56 typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
58 typedef void (*GstOggMapExtractTagsFunc) (GstOggStream * pad,
61 typedef gint64 (*GstOggMapGranuleposToKeyGranuleFunc) (GstOggStream * pad,
64 #define SKELETON_FISBONE_MIN_SIZE 52
65 #define SKELETON_FISHEAD_3_3_MIN_SIZE 112
66 #define SKELETON_FISHEAD_4_0_MIN_SIZE 80
73 const gchar *media_type;
74 GstOggMapSetupFunc setup_func;
75 GstOggMapToGranuleFunc granulepos_to_granule_func;
76 GstOggMapToGranuleposFunc granule_to_granulepos_func;
77 GstOggMapIsKeyFrameFunc is_key_frame_func;
78 GstOggMapIsHeaderPacketFunc is_header_func;
79 GstOggMapPacketDurationFunc packet_duration_func;
80 GstOggMapGranuleposToKeyGranuleFunc granulepos_to_key_granule_func;
81 GstOggMapExtractTagsFunc extract_tags_func;
84 extern const GstOggMap mappers[];
87 gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet)
91 if (packet->granulepos == -1) {
92 return GST_CLOCK_TIME_NONE;
95 duration = gst_ogg_stream_get_packet_duration (pad, packet);
97 return GST_CLOCK_TIME_NONE;
100 return gst_ogg_stream_granule_to_time (pad,
101 gst_ogg_stream_granulepos_to_granule (pad,
102 packet->granulepos) - duration);
106 gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad,
109 if (pad->frame_size == 0)
110 return GST_CLOCK_TIME_NONE;
112 return gst_ogg_stream_granule_to_time (pad,
113 gst_ogg_stream_granulepos_to_granule (pad, granulepos));
117 gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad,
120 return gst_ogg_stream_granule_to_time (pad,
121 gst_ogg_stream_granulepos_to_granule (pad, granulepos));
125 gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule)
127 if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
130 granule += pad->granule_offset;
134 return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
139 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
141 if (granulepos == -1 || granulepos == 0) {
145 if (mappers[pad->map].granulepos_to_granule_func == NULL) {
146 GST_WARNING ("Failed to convert %s granulepos to granule",
147 gst_ogg_stream_get_media_type (pad));
151 return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
155 gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos)
157 if (mappers[pad->map].granulepos_to_key_granule_func)
158 return mappers[pad->map].granulepos_to_key_granule_func (pad, granulepos);
160 if (granulepos == -1 || granulepos == 0) {
164 return granulepos >> pad->granuleshift;
168 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
169 gint64 keyframe_granule)
171 if (granule == -1 || granule == 0) {
175 if (mappers[pad->map].granule_to_granulepos_func == NULL) {
176 GST_WARNING ("Failed to convert %s granule to granulepos",
177 gst_ogg_stream_get_media_type (pad));
181 return mappers[pad->map].granule_to_granulepos_func (pad, granule,
186 gst_ogg_stream_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos)
188 if (granulepos == -1) {
192 if (mappers[pad->map].is_key_frame_func == NULL) {
193 GST_WARNING ("Failed to determine keyframeness for %s granulepos",
194 gst_ogg_stream_get_media_type (pad));
198 return mappers[pad->map].is_key_frame_func (pad, granulepos);
202 gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
204 if (mappers[pad->map].is_header_func == NULL) {
205 GST_WARNING ("Failed to determine headerness of %s packet",
206 gst_ogg_stream_get_media_type (pad));
210 return mappers[pad->map].is_header_func (pad, packet);
214 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
216 if (mappers[pad->map].packet_duration_func == NULL) {
217 GST_WARNING ("Failed to determine %s packet duration",
218 gst_ogg_stream_get_media_type (pad));
222 return mappers[pad->map].packet_duration_func (pad, packet);
227 gst_ogg_stream_extract_tags (GstOggStream * pad, ogg_packet * packet)
229 if (mappers[pad->map].extract_tags_func == NULL) {
230 GST_DEBUG ("No tag extraction");
234 mappers[pad->map].extract_tags_func (pad, packet);
238 gst_ogg_stream_get_media_type (GstOggStream * pad)
240 const GstCaps *caps = pad->caps;
241 const GstStructure *structure;
244 structure = gst_caps_get_structure (caps, 0);
247 return gst_structure_get_name (structure);
250 /* some generic functions */
253 is_keyframe_true (GstOggStream * pad, gint64 granulepos)
259 granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos)
261 gint64 keyindex, keyoffset;
263 if (pad->granuleshift != 0) {
264 keyindex = granulepos >> pad->granuleshift;
265 keyoffset = granulepos - (keyindex << pad->granuleshift);
266 return keyindex + keyoffset;
274 granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
275 gint64 keyframe_granule)
279 if (pad->granuleshift != 0) {
280 /* If we don't know where the previous keyframe is yet, assume it is
281 at 0 or 1, depending on bitstream version. If nothing else, this
282 avoids getting negative granpos back. */
283 if (keyframe_granule < 0)
284 keyframe_granule = pad->theora_has_zero_keyoffset ? 0 : 1;
285 keyoffset = granule - keyframe_granule;
286 return (keyframe_granule << pad->granuleshift) | keyoffset;
294 is_header_unknown (GstOggStream * pad, ogg_packet * packet)
296 GST_WARNING ("don't know how to detect header");
302 is_header_true (GstOggStream * pad, ogg_packet * packet)
308 is_header_count (GstOggStream * pad, ogg_packet * packet)
310 if (pad->n_header_packets_seen < pad->n_header_packets) {
317 packet_duration_constant (GstOggStream * pad, ogg_packet * packet)
319 return pad->frame_size;
322 /* helper: extracts tags from vorbis comment ogg packet.
323 * Returns result in *tags after free'ing existing *tags (if any) */
325 tag_list_from_vorbiscomment_packet (ogg_packet * packet,
326 const guint8 * id_data, const guint id_data_length, GstTagList ** tags)
328 gchar *encoder = NULL;
332 g_return_val_if_fail (tags != NULL, FALSE);
334 list = gst_tag_list_from_vorbiscomment (packet->packet, packet->bytes,
335 id_data, id_data_length, &encoder);
338 GST_WARNING ("failed to decode vorbis comments");
345 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
352 gst_tag_list_free (*tags);
361 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
363 guint8 *data = packet->packet;
364 guint w, h, par_d, par_n;
365 guint8 vmaj, vmin, vrev;
371 w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
372 h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
374 pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
375 pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
377 par_n = GST_READ_UINT24_BE (data + 30);
378 par_d = GST_READ_UINT24_BE (data + 33);
380 GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
381 pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
383 /* 2 bits + 3 bits = 5 bits KFGSHIFT */
384 pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
385 (GST_READ_UINT8 (data + 41) >> 5);
387 pad->is_video = TRUE;
388 pad->n_header_packets = 3;
391 pad->bitrate = GST_READ_UINT24_BE (data + 37);
392 GST_LOG ("bit rate: %d", pad->bitrate);
394 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
395 GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
399 /* The interpretation of the granule position has changed with 3.2.1.
400 The granule is now made from the number of frames encoded, rather than
401 the index of the frame being encoded - so there is a difference of 1. */
402 pad->theora_has_zero_keyoffset =
403 ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201;
405 pad->caps = gst_caps_new_empty_simple ("video/x-theora");
407 if (w > 0 && h > 0) {
408 gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
409 G_TYPE_INT, h, NULL);
412 /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
413 if (par_n == 0 || par_d == 0)
416 /* only add framerate now so caps look prettier, with width/height first */
417 gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
418 pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
419 GST_TYPE_FRACTION, par_n, par_d, NULL);
425 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
427 gint64 keyindex, keyoffset;
429 if (pad->granuleshift != 0) {
430 keyindex = granulepos >> pad->granuleshift;
431 keyoffset = granulepos - (keyindex << pad->granuleshift);
432 if (pad->theora_has_zero_keyoffset) {
435 return keyindex + keyoffset;
442 is_keyframe_theora (GstOggStream * pad, gint64 granulepos)
446 if (granulepos == (gint64) - 1)
449 frame_mask = (1 << pad->granuleshift) - 1;
451 return ((granulepos & frame_mask) == 0);
455 is_header_theora (GstOggStream * pad, ogg_packet * packet)
457 return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
461 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
463 if (packet->bytes > 0 && packet->packet[0] == 0x81) {
464 tag_list_from_vorbiscomment_packet (packet,
465 (const guint8 *) "\201theora", 7, &pad->taglist);
468 pad->taglist = gst_tag_list_new_empty ();
471 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
472 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
479 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
482 DiracSequenceHeader header;
484 ret = dirac_sequence_header_parse (&header, packet->packet + 13,
487 GST_DEBUG ("Failed to parse Dirac sequence header");
491 pad->is_video = TRUE;
492 pad->always_flush_page = TRUE;
493 pad->granulerate_n = header.frame_rate_numerator * 2;
494 pad->granulerate_d = header.frame_rate_denominator;
495 pad->granuleshift = 22;
496 pad->n_header_packets = 1;
499 if (header.interlaced_coding != 0) {
500 GST_DEBUG ("non-progressive Dirac coding not implemented");
504 pad->caps = gst_caps_new_simple ("video/x-dirac",
505 "width", G_TYPE_INT, header.width,
506 "height", G_TYPE_INT, header.height,
507 "interlaced", G_TYPE_BOOLEAN, header.interlaced,
508 "pixel-aspect-ratio", GST_TYPE_FRACTION,
509 header.aspect_ratio_numerator, header.aspect_ratio_denominator,
510 "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
511 header.frame_rate_denominator, NULL);
516 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
518 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
524 if (granulepos == -1)
527 dist_h = (granulepos >> 22) & 0xff;
528 dist_l = granulepos & 0xff;
529 dist = (dist_h << 8) | dist_l;
535 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
541 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
542 delay = (gp >> 9) & 0x1fff;
545 GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
551 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
552 gint64 keyframe_granule)
554 /* This conversion requires knowing more details about the Dirac
560 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
569 if (gp == -1 || gp == 0)
572 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
573 dist_h = (gp >> 22) & 0xff;
575 dist = (dist_h << 8) | dist_l;
576 delay = (gp >> 9) & 0x1fff;
579 return dt - 2 * dist + 4;
585 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
587 gint width, height, par_n, par_d, fps_n, fps_d;
589 if (packet->bytes < 26) {
590 GST_DEBUG ("Failed to parse VP8 BOS page");
594 width = GST_READ_UINT16_BE (packet->packet + 8);
595 height = GST_READ_UINT16_BE (packet->packet + 10);
596 par_n = GST_READ_UINT24_BE (packet->packet + 12);
597 par_d = GST_READ_UINT24_BE (packet->packet + 15);
598 fps_n = GST_READ_UINT32_BE (packet->packet + 18);
599 fps_d = GST_READ_UINT32_BE (packet->packet + 22);
601 pad->is_video = TRUE;
603 pad->granulerate_n = fps_n;
604 pad->granulerate_d = fps_d;
605 pad->n_header_packets = 2;
608 pad->caps = gst_caps_new_simple ("video/x-vp8",
609 "width", G_TYPE_INT, width,
610 "height", G_TYPE_INT, height,
611 "pixel-aspect-ratio", GST_TYPE_FRACTION,
612 par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
618 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
620 guint64 gpos = granulepos;
622 if (granulepos == -1)
625 /* Get rid of flags */
628 return ((gpos & 0x07ffffff) == 0);
632 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
634 guint64 gp = (guint64) gpos;
639 dist = (gp >> 3) & 0x07ffffff;
641 GST_DEBUG ("pt %u, dist %u", pt, dist);
647 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
648 gint64 keyframe_granule)
650 /* FIXME: This requires to look into the content of the packets
651 * because the simple granule counter doesn't know about invisible
657 /* Check if this packet contains an invisible frame or not */
659 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
663 if (packet->bytes < 3)
666 hdr = GST_READ_UINT24_LE (packet->packet);
668 return (((hdr >> 4) & 1) != 0) ? 1 : 0;
672 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
674 guint64 gp = granulepos;
675 guint64 pts = (gp >> 32);
676 guint32 dist = (gp >> 3) & 0x07ffffff;
678 if (granulepos == -1 || granulepos == 0)
688 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
690 if (packet->bytes >= 5 && packet->packet[0] == 0x4F &&
691 packet->packet[1] == 0x56 && packet->packet[2] == 0x50 &&
692 packet->packet[3] == 0x38 && packet->packet[4] == 0x30)
698 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
700 if (packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) {
701 tag_list_from_vorbiscomment_packet (packet,
702 (const guint8 *) "OVP80\2 ", 7, &pad->taglist);
709 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
711 guint8 *data = packet->packet;
715 pad->version = GST_READ_UINT32_LE (data);
717 chans = GST_READ_UINT8 (data);
719 pad->granulerate_n = GST_READ_UINT32_LE (data);
720 pad->granulerate_d = 1;
721 pad->granuleshift = 0;
724 GST_LOG ("sample rate: %d", pad->granulerate_n);
727 pad->bitrate_upper = GST_READ_UINT32_LE (data);
729 pad->bitrate_nominal = GST_READ_UINT32_LE (data);
731 pad->bitrate_lower = GST_READ_UINT32_LE (data);
733 if (pad->bitrate_nominal > 0)
734 pad->bitrate = pad->bitrate_nominal;
736 if (pad->bitrate_upper > 0 && !pad->bitrate)
737 pad->bitrate = pad->bitrate_upper;
739 if (pad->bitrate_lower > 0 && !pad->bitrate)
740 pad->bitrate = pad->bitrate_lower;
742 GST_LOG ("bit rate: %d", pad->bitrate);
744 pad->n_header_packets = 3;
746 if (pad->granulerate_n == 0)
749 parse_vorbis_header_packet (pad, packet);
751 pad->caps = gst_caps_new_simple ("audio/x-vorbis",
752 "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
759 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
761 if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
764 if (packet->packet[0] == 5) {
765 parse_vorbis_setup_packet (pad, packet);
772 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
774 if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
777 if (((guint8 *) (packet->packet))[0] == 0x03) {
778 tag_list_from_vorbiscomment_packet (packet,
779 (const guint8 *) "\003vorbis", 7, &pad->taglist);
782 pad->taglist = gst_tag_list_new_empty ();
784 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
785 GST_TAG_ENCODER_VERSION, pad->version, NULL);
787 if (pad->bitrate_nominal > 0)
788 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
789 GST_TAG_NOMINAL_BITRATE, (guint) pad->bitrate_nominal, NULL);
791 if (pad->bitrate_upper > 0)
792 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
793 GST_TAG_MAXIMUM_BITRATE, (guint) pad->bitrate_upper, NULL);
795 if (pad->bitrate_lower > 0)
796 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
797 GST_TAG_MINIMUM_BITRATE, (guint) pad->bitrate_lower, NULL);
800 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
801 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
806 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
812 if (packet->bytes == 0 || packet->packet[0] & 1)
815 mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
816 size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
818 if (pad->last_size == 0) {
821 duration = pad->last_size / 4 + size / 4;
823 pad->last_size = size;
825 GST_DEBUG ("duration %d", (int) duration);
834 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
836 guint8 *data = packet->packet;
839 data += 8 + 20 + 4 + 4;
840 pad->granulerate_n = GST_READ_UINT32_LE (data);
841 pad->granulerate_d = 1;
842 pad->granuleshift = 0;
845 chans = GST_READ_UINT32_LE (data);
847 pad->bitrate = GST_READ_UINT32_LE (data);
849 GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
850 GST_LOG ("bit rate: %d", pad->bitrate);
852 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
853 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
854 GST_READ_UINT32_LE (packet->packet + 56);
856 if (pad->granulerate_n == 0)
859 pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
860 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
866 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
868 /* packet 2 must be comment packet */
869 if (packet->bytes > 0 && pad->n_header_packets_seen == 1) {
870 tag_list_from_vorbiscomment_packet (packet, NULL, 0, &pad->taglist);
873 pad->taglist = gst_tag_list_new_empty ();
876 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
877 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
885 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
887 pad->granulerate_n = 0;
888 pad->granulerate_d = 1;
889 pad->granuleshift = 0;
891 pad->n_header_packets = 3;
893 pad->caps = gst_caps_new_empty_simple ("audio/x-flac");
899 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
901 if (pad->n_header_packets_seen == 1) {
902 pad->granulerate_n = (packet->packet[14] << 12) |
903 (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
906 if (pad->n_header_packets_seen < pad->n_header_packets) {
914 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
916 guint8 *data = packet->packet;
919 /* see http://flac.sourceforge.net/ogg_mapping.html */
921 pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
922 pad->granulerate_d = 1;
923 pad->granuleshift = 0;
924 chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
926 GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
928 pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
930 if (pad->granulerate_n == 0)
933 pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
934 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
940 is_header_flac (GstOggStream * pad, ogg_packet * packet)
942 return (packet->bytes > 0 && (packet->packet[0] != 0xff));
946 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
948 int block_size_index;
950 if (packet->bytes < 4)
953 block_size_index = packet->packet[2] >> 4;
954 if (block_size_index == 1)
956 if (block_size_index >= 2 && block_size_index <= 5) {
957 return 576 << (block_size_index - 2);
959 if (block_size_index >= 8) {
960 return 256 << (block_size_index - 8);
962 if (block_size_index == 6 || block_size_index == 7) {
963 guint len, bytes = (block_size_index - 6) + 1;
966 if (packet->bytes < 4 + 1 + bytes)
968 tmp = packet->packet[4];
979 if (packet->bytes < 4 + len + bytes)
982 return packet->packet[4 + len] + 1;
984 return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
991 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
993 if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
994 tag_list_from_vorbiscomment_packet (packet,
995 packet->packet, 4, &pad->taglist);
1002 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
1005 gint64 prestime_n, prestime_d;
1006 gint64 basetime_n, basetime_d;
1008 data = packet->packet;
1010 data += 8; /* header */
1012 pad->skeleton_major = GST_READ_UINT16_LE (data);
1014 pad->skeleton_minor = GST_READ_UINT16_LE (data);
1017 prestime_n = (gint64) GST_READ_UINT64_LE (data);
1019 prestime_d = (gint64) GST_READ_UINT64_LE (data);
1021 basetime_n = (gint64) GST_READ_UINT64_LE (data);
1023 basetime_d = (gint64) GST_READ_UINT64_LE (data);
1026 /* FIXME: we don't use basetime anywhere in the demuxer! */
1027 if (basetime_d != 0)
1028 pad->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
1032 if (prestime_d != 0)
1033 pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1037 /* Ogg Skeleton 3.3+ streams provide additional information in the header */
1038 if (packet->bytes >= SKELETON_FISHEAD_3_3_MIN_SIZE && pad->skeleton_major == 3
1039 && pad->skeleton_minor > 0) {
1040 gint64 firstsampletime_n, firstsampletime_d;
1041 gint64 lastsampletime_n, lastsampletime_d;
1042 gint64 firstsampletime, lastsampletime;
1043 guint64 segment_length, content_offset;
1045 firstsampletime_n = GST_READ_UINT64_LE (data + 64);
1046 firstsampletime_d = GST_READ_UINT64_LE (data + 72);
1047 lastsampletime_n = GST_READ_UINT64_LE (data + 80);
1048 lastsampletime_d = GST_READ_UINT64_LE (data + 88);
1049 segment_length = GST_READ_UINT64_LE (data + 96);
1050 content_offset = GST_READ_UINT64_LE (data + 104);
1052 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1053 firstsampletime_n, firstsampletime_d);
1054 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1055 lastsampletime_n, lastsampletime_d);
1056 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1057 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1059 if (firstsampletime_d > 0)
1060 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1061 firstsampletime_n, firstsampletime_d);
1063 firstsampletime = 0;
1065 if (lastsampletime_d > 0)
1066 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1067 lastsampletime_n, lastsampletime_d);
1071 if (lastsampletime > firstsampletime)
1072 pad->total_time = lastsampletime - firstsampletime;
1074 pad->total_time = -1;
1076 GST_INFO ("skeleton fishead parsed total: %" GST_TIME_FORMAT,
1077 GST_TIME_ARGS (pad->total_time));
1078 } else if (packet->bytes >= SKELETON_FISHEAD_4_0_MIN_SIZE
1079 && pad->skeleton_major == 4) {
1080 guint64 segment_length, content_offset;
1082 segment_length = GST_READ_UINT64_LE (data + 64);
1083 content_offset = GST_READ_UINT64_LE (data + 72);
1085 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1086 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1088 pad->total_time = -1;
1091 GST_INFO ("skeleton fishead %u.%u parsed (basetime: %" GST_TIME_FORMAT
1092 ", prestime: %" GST_TIME_FORMAT ")", pad->skeleton_major,
1093 pad->skeleton_minor, GST_TIME_ARGS (pad->basetime),
1094 GST_TIME_ARGS (pad->prestime));
1096 pad->is_skeleton = TRUE;
1097 pad->is_sparse = TRUE;
1099 pad->caps = gst_caps_new_empty_simple ("application/x-ogg-skeleton");
1105 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1106 guint32 * serialno, GstOggSkeleton * type)
1108 GstOggSkeleton stype;
1109 guint serial_offset;
1111 if (size != 0 && size < SKELETON_FISBONE_MIN_SIZE) {
1112 GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1117 /* Skeleton EOS packet is zero bytes */
1119 } else if (memcmp (data, "fisbone\0", 8) == 0) {
1120 GST_INFO ("got fisbone packet");
1121 stype = GST_OGG_SKELETON_FISBONE;
1123 } else if (memcmp (data, "index\0", 6) == 0) {
1124 GST_INFO ("got index packet");
1125 stype = GST_OGG_SKELETON_INDEX;
1127 } else if (memcmp (data, "fishead\0", 8) == 0) {
1130 GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1135 *serialno = GST_READ_UINT32_LE (data + serial_offset);
1144 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1145 const guint8 * data, guint size, GstClockTime * p_start_time)
1147 GstClockTime start_time;
1148 gint64 start_granule;
1150 if (pad->have_fisbone) {
1151 GST_DEBUG ("already have fisbone, ignoring second one");
1155 /* skip "fisbone\0" + headers offset + serialno + num headers */
1156 data += 8 + 4 + 4 + 4;
1158 pad->have_fisbone = TRUE;
1160 /* We don't overwrite whatever was set before by the format-specific
1161 setup: skeleton contains wrong information sometimes, and the codec
1162 headers are authoritative.
1163 So we only gather information that was not already filled out by
1164 the mapper setup. This should hopefully allow handling unknown
1165 streams a bit better, while not trashing correct setup from bad
1167 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
1168 pad->granulerate_n = GST_READ_UINT64_LE (data);
1169 pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
1171 if (pad->granuleshift < 0) {
1172 pad->granuleshift = GST_READ_UINT8 (data + 28);
1175 start_granule = GST_READ_UINT64_LE (data + 16);
1176 pad->preroll = GST_READ_UINT32_LE (data + 24);
1178 start_time = granulepos_to_granule_default (pad, start_granule);
1180 GST_INFO ("skeleton fisbone parsed "
1181 "(start time: %" GST_TIME_FORMAT
1182 " granulerate_n: %d granulerate_d: %d "
1183 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
1184 GST_TIME_ARGS (start_time),
1185 pad->granulerate_n, pad->granulerate_d, pad->preroll, pad->granuleshift);
1188 *p_start_time = start_time;
1194 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1202 if (G_UNLIKELY (*size < 1))
1206 *result |= ((byte & 0x7f) << shift);
1211 } while ((byte & 0x80) != 0x80);
1217 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1218 const guint8 * data, guint size)
1220 guint64 i, n_keypoints, isize;
1221 guint64 offset, timestamp;
1222 guint64 offset_d, timestamp_d;
1225 GST_DEBUG ("already have index, ignoring second one");
1229 if ((skel_pad->skeleton_major == 3 && size < 26) ||
1230 (skel_pad->skeleton_major == 4 && size < 62)) {
1231 GST_WARNING ("small index packet of size %u, ignoring", size);
1235 /* skip "index\0" + serialno */
1239 n_keypoints = GST_READ_UINT64_LE (data);
1244 pad->kp_denom = GST_READ_UINT64_LE (data);
1245 if (pad->kp_denom == 0)
1251 if (skel_pad->skeleton_major == 4) {
1252 gint64 firstsampletime_n;
1253 gint64 lastsampletime_n;
1254 gint64 firstsampletime, lastsampletime;
1256 firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1257 lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1259 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1260 firstsampletime_n, pad->kp_denom);
1261 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1262 lastsampletime_n, pad->kp_denom);
1264 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1265 firstsampletime_n, pad->kp_denom);
1266 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1267 lastsampletime_n, pad->kp_denom);
1269 if (lastsampletime > firstsampletime)
1270 pad->total_time = lastsampletime - firstsampletime;
1272 pad->total_time = -1;
1274 GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1275 GST_TIME_ARGS (pad->total_time));
1281 GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1282 G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1284 pad->index = g_try_new (GstOggIndex, n_keypoints);
1292 for (i = 0; i < n_keypoints; i++) {
1294 if (!read_vlc (&data, &size, &offset_d))
1296 if (!read_vlc (&data, &size, ×tamp_d))
1300 timestamp += timestamp_d;
1302 pad->index[i].offset = offset;
1303 pad->index[i].timestamp = timestamp;
1306 GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1309 if (isize != n_keypoints) {
1310 GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1311 G_GUINT64_FORMAT, n_keypoints, isize);
1313 pad->n_index = isize;
1314 /* try to use the index to estimate the bitrate */
1316 guint64 so, eo, st, et, b, t;
1318 /* get start and end offset and timestamps */
1319 so = pad->index[0].offset;
1320 st = pad->index[0].timestamp;
1321 eo = pad->index[isize - 1].offset;
1322 et = pad->index[isize - 1].timestamp;
1327 GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1329 /* this is the total stream bitrate according to this index */
1330 pad->idx_bitrate = gst_util_uint64_scale (8 * b, pad->kp_denom, t);
1332 GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1339 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1342 if (index->timestamp < *ts)
1344 else if (index->timestamp > *ts)
1351 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1352 guint64 * timestamp, guint64 * offset)
1358 n_index = pad->n_index;
1359 if (n_index == 0 || pad->index == NULL)
1362 ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1363 GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1366 gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1367 (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1373 GST_INFO ("found at index %u", (guint) (best - pad->index));
1376 *offset = best->offset;
1379 gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1384 /* Do we need these for something?
1385 * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
1386 * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
1387 * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
1388 * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
1389 * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
1390 * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
1394 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1396 if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1403 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1405 if (!(packet->packet[0] & 1) && (packet->packet[0] & 3 && pad->is_ogm_text)) {
1406 tag_list_from_vorbiscomment_packet (packet,
1407 (const guint8 *) "\003vorbis", 7, &pad->taglist);
1412 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1419 data = packet->packet;
1420 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1422 if (offset > packet->bytes) {
1423 GST_ERROR ("buffer too small");
1428 for (n = offset - 1; n > 0; n--) {
1429 samples = (samples << 8) | data[n];
1436 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1438 guint8 *data = packet->packet;
1442 pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1443 pad->granulerate_d = 1;
1445 fourcc = GST_READ_UINT32_LE (data + 9);
1446 fstr = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1447 GST_DEBUG ("fourcc: %s", fstr);
1449 pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1451 GST_LOG ("sample rate: %d", pad->granulerate_n);
1452 if (pad->granulerate_n == 0)
1456 gst_caps_set_simple (pad->caps,
1457 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1459 pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
1460 "fourcc", G_TYPE_STRING, fstr,
1461 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1465 pad->n_header_packets = 1;
1472 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1474 guint8 *data = packet->packet;
1480 GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
1481 GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
1483 pad->is_video = TRUE;
1484 pad->granulerate_n = 10000000;
1485 time_unit = GST_READ_UINT64_LE (data + 17);
1486 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1487 GST_WARNING ("timeunit is out of range");
1489 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1491 GST_LOG ("fps = %d/%d = %.3f",
1492 pad->granulerate_n, pad->granulerate_d,
1493 (double) pad->granulerate_n / pad->granulerate_d);
1495 fourcc = GST_READ_UINT32_LE (data + 9);
1496 width = GST_READ_UINT32_LE (data + 45);
1497 height = GST_READ_UINT32_LE (data + 49);
1498 fstr = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1499 GST_DEBUG ("fourcc: %s", fstr);
1501 pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1503 if (pad->caps == NULL) {
1504 pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
1505 "fourcc", G_TYPE_STRING, fstr,
1506 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1507 pad->granulerate_d, NULL);
1509 gst_caps_set_simple (pad->caps,
1510 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1512 "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
1514 GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
1517 pad->n_header_packets = 1;
1518 pad->frame_size = 1;
1525 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1527 guint8 *data = packet->packet;
1530 pad->granulerate_n = 10000000;
1531 time_unit = GST_READ_UINT64_LE (data + 17);
1532 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1533 GST_WARNING ("timeunit is out of range");
1535 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1537 GST_LOG ("fps = %d/%d = %.3f",
1538 pad->granulerate_n, pad->granulerate_d,
1539 (double) pad->granulerate_n / pad->granulerate_d);
1541 if (pad->granulerate_d <= 0)
1544 pad->caps = gst_caps_new_empty_simple ("text/plain");
1546 pad->n_header_packets = 1;
1548 pad->is_ogm_text = TRUE;
1549 pad->is_sparse = TRUE;
1556 #define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */
1557 #define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */
1558 #define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */
1559 #define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */
1560 #define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */
1561 #define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */
1562 #define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */
1563 #define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */
1565 #define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */
1566 #define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */
1568 #define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */
1569 #define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */
1570 #define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */
1571 #define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */
1575 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1577 guint8 *data = packet->packet;
1582 pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1583 pad->granulerate_d = 1;
1584 GST_LOG ("sample rate: %d", pad->granulerate_n);
1586 format = GST_READ_UINT32_LE (data + 12);
1587 channels = GST_READ_UINT8 (data + 21);
1589 pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1591 if (pad->granulerate_n == 0)
1596 caps = gst_caps_new_simple ("audio/x-raw",
1597 "format", G_TYPE_STRING, "S8", NULL);
1600 caps = gst_caps_new_simple ("audio/x-raw",
1601 "format", G_TYPE_STRING, "U8", NULL);
1603 case OGGPCM_FMT_S16_LE:
1604 caps = gst_caps_new_simple ("audio/x-raw",
1605 "format", G_TYPE_STRING, "S16LE", NULL);
1607 case OGGPCM_FMT_S16_BE:
1608 caps = gst_caps_new_simple ("audio/x-raw",
1609 "format", G_TYPE_STRING, "S16BE", NULL);
1611 case OGGPCM_FMT_S24_LE:
1612 caps = gst_caps_new_simple ("audio/x-raw",
1613 "format", G_TYPE_STRING, "S24LE", NULL);
1615 case OGGPCM_FMT_S24_BE:
1616 caps = gst_caps_new_simple ("audio/x-raw",
1617 "format", G_TYPE_STRING, "S24BE", NULL);
1619 case OGGPCM_FMT_S32_LE:
1620 caps = gst_caps_new_simple ("audio/x-raw",
1621 "format", G_TYPE_STRING, "S32LE", NULL);
1623 case OGGPCM_FMT_S32_BE:
1624 caps = gst_caps_new_simple ("audio/x-raw",
1625 "format", G_TYPE_STRING, "S32BE", NULL);
1627 case OGGPCM_FMT_ULAW:
1628 caps = gst_caps_new_empty_simple ("audio/x-mulaw");
1630 case OGGPCM_FMT_ALAW:
1631 caps = gst_caps_new_empty_simple ("audio/x-alaw");
1633 case OGGPCM_FMT_FLT32_LE:
1634 caps = gst_caps_new_simple ("audio/x-raw",
1635 "format", G_TYPE_STRING, "F32LE", NULL);
1637 case OGGPCM_FMT_FLT32_BE:
1638 caps = gst_caps_new_simple ("audio/x-raw",
1639 "format", G_TYPE_STRING, "F32BE", NULL);
1641 case OGGPCM_FMT_FLT64_LE:
1642 caps = gst_caps_new_simple ("audio/x-raw",
1643 "format", G_TYPE_STRING, "F64LE", NULL);
1645 case OGGPCM_FMT_FLT64_BE:
1646 caps = gst_caps_new_simple ("audio/x-raw",
1647 "format", G_TYPE_STRING, "F64BE", NULL);
1653 gst_caps_set_simple (caps,
1654 "rate", G_TYPE_INT, pad->granulerate_n,
1655 "channels", G_TYPE_INT, channels, NULL);
1664 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1666 guint8 *data = packet->packet;
1668 pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1669 pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1670 pad->granuleshift = data[28];
1671 GST_LOG ("sample rate: %d", pad->granulerate_n);
1673 pad->n_header_packets = 3;
1675 if (pad->granulerate_n == 0)
1678 data += 4 + (4 + 4 + 4);
1679 GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1680 GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1682 pad->caps = gst_caps_new_empty_simple ("text/x-cmml");
1683 pad->always_flush_page = TRUE;
1684 pad->is_sparse = TRUE;
1692 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1694 guint8 *data = packet->packet;
1696 pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1697 pad->granulerate_d = 1;
1698 pad->granuleshift = 0;
1699 GST_LOG ("sample rate: %d", pad->granulerate_n);
1701 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1702 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1704 if (pad->granulerate_n == 0)
1707 pad->caps = gst_caps_new_simple ("audio/x-celt",
1708 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1716 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1718 guint8 *data = packet->packet;
1719 const char *category;
1721 if (packet->bytes < 64)
1724 pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1725 pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1726 pad->granuleshift = GST_READ_UINT8 (data + 15);
1727 GST_LOG ("sample rate: %d", pad->granulerate_n);
1729 pad->n_header_packets = GST_READ_UINT8 (data + 11);
1730 GST_LOG ("kate header packets: %d", pad->n_header_packets);
1732 if (pad->granulerate_n == 0)
1735 category = (const char *) data + 48;
1736 if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1737 strcmp (category, "spu-subtitles") == 0 ||
1738 strcmp (category, "K-SPU") == 0) {
1739 pad->caps = gst_caps_new_empty_simple ("subtitle/x-kate");
1741 pad->caps = gst_caps_new_empty_simple ("application/x-kate");
1744 pad->is_sparse = TRUE;
1745 pad->always_flush_page = TRUE;
1751 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1755 if (packet->bytes < 1)
1758 switch (packet->packet[0]) {
1759 case 0x00: /* text data */
1760 if (packet->bytes < 1 + 8 * 2) {
1763 duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
1769 duration = GST_CLOCK_TIME_NONE;
1777 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
1779 GstTagList *list = NULL;
1781 if (packet->bytes <= 0)
1784 switch (packet->packet[0]) {
1786 const gchar *canonical;
1789 if (packet->bytes < 64) {
1790 GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
1794 /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
1795 memcpy (language, packet->packet + 32, 16);
1798 /* language is an ISO 639-1 code or RFC 3066 language code, we
1799 * truncate to ISO 639-1 */
1800 g_strdelimit (language, NULL, '\0');
1801 canonical = gst_tag_get_language_code_iso_639_1 (language);
1803 list = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, canonical, NULL);
1805 GST_WARNING ("Unknown or invalid language code %s, ignored", language);
1810 tag_list_from_vorbiscomment_packet (packet,
1811 (const guint8 *) "\201kate\0\0\0\0", 9, &list);
1819 /* ensure the comment packet cannot override the category/language
1820 from the identification header */
1821 gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL);
1823 pad->taglist = list;
1830 setup_opus_mapper (GstOggStream * pad, ogg_packet * packet)
1832 if (packet->bytes < 19)
1835 pad->granulerate_n = 48000;
1836 pad->granulerate_d = 1;
1837 pad->granuleshift = 0;
1838 pad->n_header_packets = 2;
1840 /* pre-skip is in samples at 48000 Hz, which matches granule one for one */
1841 pad->granule_offset = -GST_READ_UINT16_LE (packet->packet + 10);
1842 GST_INFO ("Opus has a pre-skip of %" G_GINT64_FORMAT " samples",
1843 -pad->granule_offset);
1845 pad->caps = gst_caps_new_simple ("audio/x-opus", NULL);
1851 is_header_opus (GstOggStream * pad, ogg_packet * packet)
1853 return packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4);
1857 packet_duration_opus (GstOggStream * pad, ogg_packet * packet)
1859 static const guint64 durations[32] = {
1860 480, 960, 1920, 2880, /* Silk NB */
1861 480, 960, 1920, 2880, /* Silk MB */
1862 480, 960, 1920, 2880, /* Silk WB */
1863 480, 960, /* Hybrid SWB */
1864 480, 960, /* Hybrid FB */
1865 120, 240, 480, 960, /* CELT NB */
1866 120, 240, 480, 960, /* CELT NB */
1867 120, 240, 480, 960, /* CELT NB */
1868 120, 240, 480, 960, /* CELT NB */
1872 gint64 frame_duration;
1876 if (packet->bytes < 1)
1880 if (is_header_opus (pad, packet))
1883 toc = packet->packet[0];
1885 frame_duration = durations[toc >> 3];
1897 if (packet->bytes < 2) {
1898 GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
1901 nframes = packet->packet[1] & 63;
1905 duration = nframes * frame_duration;
1906 if (duration > 5760) {
1907 GST_WARNING ("Opus packet duration > 120 ms, invalid");
1910 GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
1911 frame_duration / 48.f, nframes, duration / 48.f);
1916 extract_tags_opus (GstOggStream * pad, ogg_packet * packet)
1918 if (packet->bytes >= 8 && memcmp (packet->packet, "OpusTags", 8) == 0) {
1919 tag_list_from_vorbiscomment_packet (packet,
1920 (const guint8 *) "OpusTags", 8, &pad->taglist);
1926 /* indent hates our freedoms */
1927 const GstOggMap mappers[] = {
1929 "\200theora", 7, 42,
1931 setup_theora_mapper,
1932 granulepos_to_granule_theora,
1933 granule_to_granulepos_default,
1936 packet_duration_constant,
1941 "\001vorbis", 7, 22,
1943 setup_vorbis_mapper,
1944 granulepos_to_granule_default,
1945 granule_to_granulepos_default,
1948 packet_duration_vorbis,
1956 granulepos_to_granule_default,
1957 granule_to_granulepos_default,
1960 packet_duration_constant,
1977 "CMML\0\0\0\0", 8, 0,
1990 "application/x-annodex",
1991 setup_fishead_mapper,
1992 granulepos_to_granule_default,
1993 granule_to_granulepos_default,
2002 "application/octet-stream",
2003 setup_fishead_mapper,
2016 granulepos_to_granule_default,
2017 granule_to_granulepos_default,
2020 packet_duration_flac,
2028 granulepos_to_granule_default,
2029 granule_to_granulepos_default,
2032 packet_duration_flac,
2038 "application/octet-stream",
2051 granulepos_to_granule_default,
2052 granule_to_granulepos_default,
2055 packet_duration_constant,
2060 "\200kate\0\0\0", 8, 0,
2063 granulepos_to_granule_default,
2064 granule_to_granulepos_default,
2067 packet_duration_kate,
2075 granulepos_to_granule_dirac,
2076 granule_to_granulepos_dirac,
2079 packet_duration_constant,
2080 granulepos_to_key_granule_dirac,
2087 granulepos_to_granule_vp8,
2088 granule_to_granulepos_vp8,
2091 packet_duration_vp8,
2092 granulepos_to_key_granule_vp8,
2099 granulepos_to_granule_default,
2100 granule_to_granulepos_default,
2103 packet_duration_opus,
2108 "\001audio\0\0\0", 9, 53,
2109 "application/x-ogm-audio",
2110 setup_ogmaudio_mapper,
2111 granulepos_to_granule_default,
2112 granule_to_granulepos_default,
2115 packet_duration_ogm,
2120 "\001video\0\0\0", 9, 53,
2121 "application/x-ogm-video",
2122 setup_ogmvideo_mapper,
2123 granulepos_to_granule_default,
2124 granule_to_granulepos_default,
2127 packet_duration_constant,
2132 "\001text\0\0\0", 9, 9,
2133 "application/x-ogm-text",
2134 setup_ogmtext_mapper,
2135 granulepos_to_granule_default,
2136 granule_to_granulepos_default,
2139 packet_duration_ogm,
2147 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2152 for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2153 if (packet->bytes >= mappers[i].min_packet_size &&
2154 packet->bytes >= mappers[i].id_length &&
2155 memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
2157 GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2159 if (mappers[i].setup_func)
2160 ret = mappers[i].setup_func (pad, packet);
2165 GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2169 GST_WARNING ("mapper '%s' did not accept setup header",
2170 mappers[i].media_type);
2179 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2180 const GstCaps * caps)
2183 const GstStructure *structure;
2184 const GValue *streamheader;
2185 const GValue *first_element;
2191 GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2196 structure = gst_caps_get_structure (caps, 0);
2197 streamheader = gst_structure_get_value (structure, "streamheader");
2199 if (streamheader == NULL) {
2200 GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2204 if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2205 GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2209 if (gst_value_array_get_size (streamheader) == 0) {
2210 GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2214 first_element = gst_value_array_get_value (streamheader, 0);
2216 if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2217 GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2221 buf = gst_value_get_buffer (first_element);
2223 GST_ERROR ("no first streamheader buffer");
2227 data = gst_buffer_map (buf, &size, 0, GST_MAP_READ);
2228 if (data == NULL || size == 0) {
2229 GST_ERROR ("invalid first streamheader buffer");
2233 GST_MEMDUMP ("streamheader", data, size);
2235 packet.packet = data;
2236 packet.bytes = size;
2238 GST_INFO ("Found headers on caps, using those to determine type");
2239 ret = gst_ogg_stream_setup_map (pad, &packet);
2241 gst_buffer_unmap (buf, data, size);