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 GstBuffer *buf = NULL;
329 gchar *encoder = NULL;
333 g_return_val_if_fail (tags != NULL, FALSE);
335 buf = gst_buffer_new ();
336 GST_BUFFER_DATA (buf) = (guint8 *) packet->packet;
337 GST_BUFFER_SIZE (buf) = packet->bytes;
339 list = gst_tag_list_from_vorbiscomment_buffer (buf, id_data, id_data_length,
343 GST_WARNING ("failed to decode vorbis comments");
350 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
357 gst_tag_list_free (*tags);
360 gst_buffer_unref (buf);
368 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
370 guint8 *data = packet->packet;
371 guint w, h, par_d, par_n;
372 guint8 vmaj, vmin, vrev;
378 w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
379 h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
381 pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
382 pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
384 par_n = GST_READ_UINT24_BE (data + 30);
385 par_d = GST_READ_UINT24_BE (data + 33);
387 GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
388 pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
390 /* 2 bits + 3 bits = 5 bits KFGSHIFT */
391 pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
392 (GST_READ_UINT8 (data + 41) >> 5);
394 pad->is_video = TRUE;
395 pad->n_header_packets = 3;
398 pad->bitrate = GST_READ_UINT24_BE (data + 37);
399 GST_LOG ("bit rate: %d", pad->bitrate);
401 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
402 GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
406 /* The interpretation of the granule position has changed with 3.2.1.
407 The granule is now made from the number of frames encoded, rather than
408 the index of the frame being encoded - so there is a difference of 1. */
409 pad->theora_has_zero_keyoffset =
410 ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201;
412 pad->caps = gst_caps_new_simple ("video/x-theora", NULL);
414 if (w > 0 && h > 0) {
415 gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
416 G_TYPE_INT, h, NULL);
419 /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
420 if (par_n == 0 || par_d == 0)
423 /* only add framerate now so caps look prettier, with width/height first */
424 gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
425 pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
426 GST_TYPE_FRACTION, par_n, par_d, NULL);
432 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
434 gint64 keyindex, keyoffset;
436 if (pad->granuleshift != 0) {
437 keyindex = granulepos >> pad->granuleshift;
438 keyoffset = granulepos - (keyindex << pad->granuleshift);
439 if (pad->theora_has_zero_keyoffset) {
442 return keyindex + keyoffset;
449 is_keyframe_theora (GstOggStream * pad, gint64 granulepos)
453 if (granulepos == (gint64) - 1)
456 frame_mask = (1 << pad->granuleshift) - 1;
458 return ((granulepos & frame_mask) == 0);
462 is_header_theora (GstOggStream * pad, ogg_packet * packet)
464 return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
468 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
470 if (packet->bytes > 0 && packet->packet[0] == 0x81) {
471 tag_list_from_vorbiscomment_packet (packet,
472 (const guint8 *) "\201theora", 7, &pad->taglist);
475 pad->taglist = gst_tag_list_new ();
478 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
479 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
486 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
489 DiracSequenceHeader header;
491 ret = dirac_sequence_header_parse (&header, packet->packet + 13,
494 GST_DEBUG ("Failed to parse Dirac sequence header");
498 pad->is_video = TRUE;
499 pad->always_flush_page = TRUE;
500 pad->granulerate_n = header.frame_rate_numerator * 2;
501 pad->granulerate_d = header.frame_rate_denominator;
502 pad->granuleshift = 22;
503 pad->n_header_packets = 1;
506 if (header.interlaced_coding != 0) {
507 GST_DEBUG ("non-progressive Dirac coding not implemented");
511 pad->caps = gst_caps_new_simple ("video/x-dirac",
512 "width", G_TYPE_INT, header.width,
513 "height", G_TYPE_INT, header.height,
514 "interlaced", G_TYPE_BOOLEAN, header.interlaced,
515 "pixel-aspect-ratio", GST_TYPE_FRACTION,
516 header.aspect_ratio_numerator, header.aspect_ratio_denominator,
517 "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
518 header.frame_rate_denominator, NULL);
523 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
525 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
531 if (granulepos == -1)
534 dist_h = (granulepos >> 22) & 0xff;
535 dist_l = granulepos & 0xff;
536 dist = (dist_h << 8) | dist_l;
542 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
548 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
549 delay = (gp >> 9) & 0x1fff;
552 GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
558 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
559 gint64 keyframe_granule)
561 /* This conversion requires knowing more details about the Dirac
567 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
576 if (gp == -1 || gp == 0)
579 pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
580 dist_h = (gp >> 22) & 0xff;
582 dist = (dist_h << 8) | dist_l;
583 delay = (gp >> 9) & 0x1fff;
586 return dt - 2 * dist + 4;
592 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
594 gint width, height, par_n, par_d, fps_n, fps_d;
596 if (packet->bytes < 26) {
597 GST_DEBUG ("Failed to parse VP8 BOS page");
601 width = GST_READ_UINT16_BE (packet->packet + 8);
602 height = GST_READ_UINT16_BE (packet->packet + 10);
603 par_n = GST_READ_UINT24_BE (packet->packet + 12);
604 par_d = GST_READ_UINT24_BE (packet->packet + 15);
605 fps_n = GST_READ_UINT32_BE (packet->packet + 18);
606 fps_d = GST_READ_UINT32_BE (packet->packet + 22);
608 pad->is_video = TRUE;
610 pad->granulerate_n = fps_n;
611 pad->granulerate_d = fps_d;
612 pad->n_header_packets = 2;
615 pad->caps = gst_caps_new_simple ("video/x-vp8",
616 "width", G_TYPE_INT, width,
617 "height", G_TYPE_INT, height,
618 "pixel-aspect-ratio", GST_TYPE_FRACTION,
619 par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
625 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
627 guint64 gpos = granulepos;
629 if (granulepos == -1)
632 /* Get rid of flags */
635 return ((gpos & 0x07ffffff) == 0);
639 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
641 guint64 gp = (guint64) gpos;
646 dist = (gp >> 3) & 0x07ffffff;
648 GST_DEBUG ("pt %u, dist %u", pt, dist);
654 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
655 gint64 keyframe_granule)
657 /* FIXME: This requires to look into the content of the packets
658 * because the simple granule counter doesn't know about invisible
664 /* Check if this packet contains an invisible frame or not */
666 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
670 if (packet->bytes < 3)
673 hdr = GST_READ_UINT24_LE (packet->packet);
675 return (((hdr >> 4) & 1) != 0) ? 1 : 0;
679 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
681 guint64 gp = granulepos;
682 guint64 pts = (gp >> 32);
683 guint32 dist = (gp >> 3) & 0x07ffffff;
685 if (granulepos == -1 || granulepos == 0)
695 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
697 if (packet->bytes >= 5 && packet->packet[0] == 0x4F &&
698 packet->packet[1] == 0x56 && packet->packet[2] == 0x50 &&
699 packet->packet[3] == 0x38 && packet->packet[4] == 0x30)
705 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
707 if (packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) {
708 tag_list_from_vorbiscomment_packet (packet,
709 (const guint8 *) "OVP80\2 ", 7, &pad->taglist);
716 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
718 guint8 *data = packet->packet;
722 pad->version = GST_READ_UINT32_LE (data);
724 chans = GST_READ_UINT8 (data);
726 pad->granulerate_n = GST_READ_UINT32_LE (data);
727 pad->granulerate_d = 1;
728 pad->granuleshift = 0;
731 GST_LOG ("sample rate: %d", pad->granulerate_n);
734 pad->bitrate_upper = GST_READ_UINT32_LE (data);
736 pad->bitrate_nominal = GST_READ_UINT32_LE (data);
738 pad->bitrate_lower = GST_READ_UINT32_LE (data);
740 if (pad->bitrate_nominal > 0)
741 pad->bitrate = pad->bitrate_nominal;
743 if (pad->bitrate_upper > 0 && !pad->bitrate)
744 pad->bitrate = pad->bitrate_upper;
746 if (pad->bitrate_lower > 0 && !pad->bitrate)
747 pad->bitrate = pad->bitrate_lower;
749 GST_LOG ("bit rate: %d", pad->bitrate);
751 pad->n_header_packets = 3;
753 if (pad->granulerate_n == 0)
756 parse_vorbis_header_packet (pad, packet);
758 pad->caps = gst_caps_new_simple ("audio/x-vorbis",
759 "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
766 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
768 if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
771 if (packet->packet[0] == 5) {
772 parse_vorbis_setup_packet (pad, packet);
779 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
781 if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
784 if (((guint8 *) (packet->packet))[0] == 0x03) {
785 tag_list_from_vorbiscomment_packet (packet,
786 (const guint8 *) "\003vorbis", 7, &pad->taglist);
789 pad->taglist = gst_tag_list_new ();
791 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
792 GST_TAG_ENCODER_VERSION, pad->version, NULL);
794 if (pad->bitrate_nominal > 0)
795 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
796 GST_TAG_NOMINAL_BITRATE, (guint) pad->bitrate_nominal, NULL);
798 if (pad->bitrate_upper > 0)
799 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
800 GST_TAG_MAXIMUM_BITRATE, (guint) pad->bitrate_upper, NULL);
802 if (pad->bitrate_lower > 0)
803 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
804 GST_TAG_MINIMUM_BITRATE, (guint) pad->bitrate_lower, NULL);
807 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
808 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
813 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
819 if (packet->bytes == 0 || packet->packet[0] & 1)
822 mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
823 size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
825 if (pad->last_size == 0) {
828 duration = pad->last_size / 4 + size / 4;
830 pad->last_size = size;
832 GST_DEBUG ("duration %d", (int) duration);
841 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
843 guint8 *data = packet->packet;
846 data += 8 + 20 + 4 + 4;
847 pad->granulerate_n = GST_READ_UINT32_LE (data);
848 pad->granulerate_d = 1;
849 pad->granuleshift = 0;
852 chans = GST_READ_UINT32_LE (data);
854 pad->bitrate = GST_READ_UINT32_LE (data);
856 GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
857 GST_LOG ("bit rate: %d", pad->bitrate);
859 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
860 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
861 GST_READ_UINT32_LE (packet->packet + 56);
863 if (pad->granulerate_n == 0)
866 pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
867 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
873 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
875 /* packet 2 must be comment packet */
876 if (packet->bytes > 0 && pad->n_header_packets_seen == 1) {
877 tag_list_from_vorbiscomment_packet (packet, NULL, 0, &pad->taglist);
880 pad->taglist = gst_tag_list_new ();
883 gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
884 GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
892 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
894 pad->granulerate_n = 0;
895 pad->granulerate_d = 1;
896 pad->granuleshift = 0;
898 pad->n_header_packets = 3;
900 pad->caps = gst_caps_new_simple ("audio/x-flac", NULL);
906 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
908 if (pad->n_header_packets_seen == 1) {
909 pad->granulerate_n = (packet->packet[14] << 12) |
910 (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
913 if (pad->n_header_packets_seen < pad->n_header_packets) {
921 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
923 guint8 *data = packet->packet;
926 /* see http://flac.sourceforge.net/ogg_mapping.html */
928 pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
929 pad->granulerate_d = 1;
930 pad->granuleshift = 0;
931 chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
933 GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
935 pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
937 if (pad->granulerate_n == 0)
940 pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
941 pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
947 is_header_flac (GstOggStream * pad, ogg_packet * packet)
949 return (packet->bytes > 0 && (packet->packet[0] != 0xff));
953 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
955 int block_size_index;
957 if (packet->bytes < 4)
960 block_size_index = packet->packet[2] >> 4;
961 if (block_size_index == 1)
963 if (block_size_index >= 2 && block_size_index <= 5) {
964 return 576 << (block_size_index - 2);
966 if (block_size_index >= 8) {
967 return 256 << (block_size_index - 8);
969 if (block_size_index == 6 || block_size_index == 7) {
970 guint len, bytes = (block_size_index - 6) + 1;
973 if (packet->bytes < 4 + 1 + bytes)
975 tmp = packet->packet[4];
986 if (packet->bytes < 4 + len + bytes)
989 return packet->packet[4 + len] + 1;
991 return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
998 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
1000 if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
1001 tag_list_from_vorbiscomment_packet (packet,
1002 packet->packet, 4, &pad->taglist);
1009 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
1012 gint64 prestime_n, prestime_d;
1013 gint64 basetime_n, basetime_d;
1015 data = packet->packet;
1017 data += 8; /* header */
1019 pad->skeleton_major = GST_READ_UINT16_LE (data);
1021 pad->skeleton_minor = GST_READ_UINT16_LE (data);
1024 prestime_n = (gint64) GST_READ_UINT64_LE (data);
1026 prestime_d = (gint64) GST_READ_UINT64_LE (data);
1028 basetime_n = (gint64) GST_READ_UINT64_LE (data);
1030 basetime_d = (gint64) GST_READ_UINT64_LE (data);
1033 /* FIXME: we don't use basetime anywhere in the demuxer! */
1034 if (basetime_d != 0)
1035 pad->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
1039 if (prestime_d != 0)
1040 pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1044 /* Ogg Skeleton 3.3+ streams provide additional information in the header */
1045 if (packet->bytes >= SKELETON_FISHEAD_3_3_MIN_SIZE && pad->skeleton_major == 3
1046 && pad->skeleton_minor > 0) {
1047 gint64 firstsampletime_n, firstsampletime_d;
1048 gint64 lastsampletime_n, lastsampletime_d;
1049 gint64 firstsampletime, lastsampletime;
1050 guint64 segment_length, content_offset;
1052 firstsampletime_n = GST_READ_UINT64_LE (data + 64);
1053 firstsampletime_d = GST_READ_UINT64_LE (data + 72);
1054 lastsampletime_n = GST_READ_UINT64_LE (data + 80);
1055 lastsampletime_d = GST_READ_UINT64_LE (data + 88);
1056 segment_length = GST_READ_UINT64_LE (data + 96);
1057 content_offset = GST_READ_UINT64_LE (data + 104);
1059 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1060 firstsampletime_n, firstsampletime_d);
1061 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1062 lastsampletime_n, lastsampletime_d);
1063 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1064 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1066 if (firstsampletime_d > 0)
1067 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1068 firstsampletime_n, firstsampletime_d);
1070 firstsampletime = 0;
1072 if (lastsampletime_d > 0)
1073 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1074 lastsampletime_n, lastsampletime_d);
1078 if (lastsampletime > firstsampletime)
1079 pad->total_time = lastsampletime - firstsampletime;
1081 pad->total_time = -1;
1083 GST_INFO ("skeleton fishead parsed total: %" GST_TIME_FORMAT,
1084 GST_TIME_ARGS (pad->total_time));
1085 } else if (packet->bytes >= SKELETON_FISHEAD_4_0_MIN_SIZE
1086 && pad->skeleton_major == 4) {
1087 guint64 segment_length, content_offset;
1089 segment_length = GST_READ_UINT64_LE (data + 64);
1090 content_offset = GST_READ_UINT64_LE (data + 72);
1092 GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1093 GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1095 pad->total_time = -1;
1098 GST_INFO ("skeleton fishead %u.%u parsed (basetime: %" GST_TIME_FORMAT
1099 ", prestime: %" GST_TIME_FORMAT ")", pad->skeleton_major,
1100 pad->skeleton_minor, GST_TIME_ARGS (pad->basetime),
1101 GST_TIME_ARGS (pad->prestime));
1103 pad->is_skeleton = TRUE;
1104 pad->is_sparse = TRUE;
1106 pad->caps = gst_caps_new_simple ("application/x-ogg-skeleton", NULL);
1112 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1113 guint32 * serialno, GstOggSkeleton * type)
1115 GstOggSkeleton stype;
1116 guint serial_offset;
1118 if (size != 0 && size < SKELETON_FISBONE_MIN_SIZE) {
1119 GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1124 /* Skeleton EOS packet is zero bytes */
1126 } else if (memcmp (data, "fisbone\0", 8) == 0) {
1127 GST_INFO ("got fisbone packet");
1128 stype = GST_OGG_SKELETON_FISBONE;
1130 } else if (memcmp (data, "index\0", 6) == 0) {
1131 GST_INFO ("got index packet");
1132 stype = GST_OGG_SKELETON_INDEX;
1134 } else if (memcmp (data, "fishead\0", 8) == 0) {
1137 GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1142 *serialno = GST_READ_UINT32_LE (data + serial_offset);
1151 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1152 const guint8 * data, guint size, GstClockTime * p_start_time)
1154 GstClockTime start_time;
1155 gint64 start_granule;
1157 if (pad->have_fisbone) {
1158 GST_DEBUG ("already have fisbone, ignoring second one");
1162 /* skip "fisbone\0" + headers offset + serialno + num headers */
1163 data += 8 + 4 + 4 + 4;
1165 pad->have_fisbone = TRUE;
1167 /* We don't overwrite whatever was set before by the format-specific
1168 setup: skeleton contains wrong information sometimes, and the codec
1169 headers are authoritative.
1170 So we only gather information that was not already filled out by
1171 the mapper setup. This should hopefully allow handling unknown
1172 streams a bit better, while not trashing correct setup from bad
1174 if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
1175 pad->granulerate_n = GST_READ_UINT64_LE (data);
1176 pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
1178 if (pad->granuleshift < 0) {
1179 pad->granuleshift = GST_READ_UINT8 (data + 28);
1182 start_granule = GST_READ_UINT64_LE (data + 16);
1183 pad->preroll = GST_READ_UINT32_LE (data + 24);
1185 start_time = granulepos_to_granule_default (pad, start_granule);
1187 GST_INFO ("skeleton fisbone parsed "
1188 "(start time: %" GST_TIME_FORMAT
1189 " granulerate_n: %d granulerate_d: %d "
1190 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
1191 GST_TIME_ARGS (start_time),
1192 pad->granulerate_n, pad->granulerate_d, pad->preroll, pad->granuleshift);
1195 *p_start_time = start_time;
1201 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1209 if (G_UNLIKELY (*size < 1))
1213 *result |= ((byte & 0x7f) << shift);
1218 } while ((byte & 0x80) != 0x80);
1224 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1225 const guint8 * data, guint size)
1227 guint64 i, n_keypoints, isize;
1228 guint64 offset, timestamp;
1229 guint64 offset_d, timestamp_d;
1232 GST_DEBUG ("already have index, ignoring second one");
1236 if ((skel_pad->skeleton_major == 3 && size < 26) ||
1237 (skel_pad->skeleton_major == 4 && size < 62)) {
1238 GST_WARNING ("small index packet of size %u, ignoring", size);
1242 /* skip "index\0" + serialno */
1246 n_keypoints = GST_READ_UINT64_LE (data);
1251 pad->kp_denom = GST_READ_UINT64_LE (data);
1252 if (pad->kp_denom == 0)
1258 if (skel_pad->skeleton_major == 4) {
1259 gint64 firstsampletime_n;
1260 gint64 lastsampletime_n;
1261 gint64 firstsampletime, lastsampletime;
1263 firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1264 lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1266 GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1267 firstsampletime_n, pad->kp_denom);
1268 GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1269 lastsampletime_n, pad->kp_denom);
1271 firstsampletime = gst_util_uint64_scale (GST_SECOND,
1272 firstsampletime_n, pad->kp_denom);
1273 lastsampletime = gst_util_uint64_scale (GST_SECOND,
1274 lastsampletime_n, pad->kp_denom);
1276 if (lastsampletime > firstsampletime)
1277 pad->total_time = lastsampletime - firstsampletime;
1279 pad->total_time = -1;
1281 GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1282 GST_TIME_ARGS (pad->total_time));
1288 GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1289 G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1291 pad->index = g_try_new (GstOggIndex, n_keypoints);
1299 for (i = 0; i < n_keypoints; i++) {
1301 if (!read_vlc (&data, &size, &offset_d))
1303 if (!read_vlc (&data, &size, ×tamp_d))
1307 timestamp += timestamp_d;
1309 pad->index[i].offset = offset;
1310 pad->index[i].timestamp = timestamp;
1313 GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1316 if (isize != n_keypoints) {
1317 GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1318 G_GUINT64_FORMAT, n_keypoints, isize);
1320 pad->n_index = isize;
1321 /* try to use the index to estimate the bitrate */
1323 guint64 so, eo, st, et, b, t;
1325 /* get start and end offset and timestamps */
1326 so = pad->index[0].offset;
1327 st = pad->index[0].timestamp;
1328 eo = pad->index[isize - 1].offset;
1329 et = pad->index[isize - 1].timestamp;
1334 GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1336 /* this is the total stream bitrate according to this index */
1337 pad->idx_bitrate = gst_util_uint64_scale (8 * b, pad->kp_denom, t);
1339 GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1346 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1349 if (index->timestamp < *ts)
1351 else if (index->timestamp > *ts)
1358 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1359 guint64 * timestamp, guint64 * offset)
1365 n_index = pad->n_index;
1366 if (n_index == 0 || pad->index == NULL)
1369 ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1370 GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1373 gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1374 (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1380 GST_INFO ("found at index %u", (guint) (best - pad->index));
1383 *offset = best->offset;
1386 gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1391 /* Do we need these for something?
1392 * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
1393 * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
1394 * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
1395 * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
1396 * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
1397 * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
1401 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1403 if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1410 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1412 if (!(packet->packet[0] & 1) && (packet->packet[0] & 3 && pad->is_ogm_text)) {
1413 tag_list_from_vorbiscomment_packet (packet,
1414 (const guint8 *) "\003vorbis", 7, &pad->taglist);
1419 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1426 data = packet->packet;
1427 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1429 if (offset > packet->bytes) {
1430 GST_ERROR ("buffer too small");
1435 for (n = offset - 1; n > 0; n--) {
1436 samples = (samples << 8) | data[n];
1443 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1445 guint8 *data = packet->packet;
1448 pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1449 pad->granulerate_d = 1;
1451 fourcc = GST_READ_UINT32_LE (data + 9);
1452 GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1454 pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1456 GST_LOG ("sample rate: %d", pad->granulerate_n);
1457 if (pad->granulerate_n == 0)
1461 gst_caps_set_simple (pad->caps,
1462 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1464 pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
1465 "fourcc", GST_TYPE_FOURCC, fourcc,
1466 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1469 pad->n_header_packets = 1;
1476 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1478 guint8 *data = packet->packet;
1483 GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
1484 GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
1486 pad->is_video = TRUE;
1487 pad->granulerate_n = 10000000;
1488 time_unit = GST_READ_UINT64_LE (data + 17);
1489 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1490 GST_WARNING ("timeunit is out of range");
1492 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1494 GST_LOG ("fps = %d/%d = %.3f",
1495 pad->granulerate_n, pad->granulerate_d,
1496 (double) pad->granulerate_n / pad->granulerate_d);
1498 fourcc = GST_READ_UINT32_LE (data + 9);
1499 width = GST_READ_UINT32_LE (data + 45);
1500 height = GST_READ_UINT32_LE (data + 49);
1501 GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1503 pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1505 if (pad->caps == NULL) {
1506 pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
1507 "fourcc", GST_TYPE_FOURCC, fourcc,
1508 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1509 pad->granulerate_d, NULL);
1511 gst_caps_set_simple (pad->caps,
1512 "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1514 "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
1516 GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
1518 pad->n_header_packets = 1;
1519 pad->frame_size = 1;
1526 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1528 guint8 *data = packet->packet;
1531 pad->granulerate_n = 10000000;
1532 time_unit = GST_READ_UINT64_LE (data + 17);
1533 if (time_unit > G_MAXINT || time_unit < G_MININT) {
1534 GST_WARNING ("timeunit is out of range");
1536 pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1538 GST_LOG ("fps = %d/%d = %.3f",
1539 pad->granulerate_n, pad->granulerate_d,
1540 (double) pad->granulerate_n / pad->granulerate_d);
1542 if (pad->granulerate_d <= 0)
1545 pad->caps = gst_caps_new_simple ("text/plain", NULL);
1547 pad->n_header_packets = 1;
1549 pad->is_ogm_text = TRUE;
1550 pad->is_sparse = TRUE;
1557 #define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */
1558 #define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */
1559 #define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */
1560 #define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */
1561 #define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */
1562 #define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */
1563 #define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */
1564 #define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */
1566 #define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */
1567 #define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */
1569 #define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */
1570 #define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */
1571 #define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */
1572 #define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */
1576 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1578 guint8 *data = packet->packet;
1583 pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1584 pad->granulerate_d = 1;
1585 GST_LOG ("sample rate: %d", pad->granulerate_n);
1587 format = GST_READ_UINT32_LE (data + 12);
1588 channels = GST_READ_UINT8 (data + 21);
1590 pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1592 if (pad->granulerate_n == 0)
1597 caps = gst_caps_new_simple ("audio/x-raw-int",
1598 "depth", G_TYPE_INT, 8,
1599 "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1602 caps = gst_caps_new_simple ("audio/x-raw-int",
1603 "depth", G_TYPE_INT, 8,
1604 "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
1606 case OGGPCM_FMT_S16_LE:
1607 caps = gst_caps_new_simple ("audio/x-raw-int",
1608 "depth", G_TYPE_INT, 16,
1609 "width", G_TYPE_INT, 16,
1610 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1611 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1613 case OGGPCM_FMT_S16_BE:
1614 caps = gst_caps_new_simple ("audio/x-raw-int",
1615 "depth", G_TYPE_INT, 16,
1616 "width", G_TYPE_INT, 16,
1617 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1618 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1620 case OGGPCM_FMT_S24_LE:
1621 caps = gst_caps_new_simple ("audio/x-raw-int",
1622 "depth", G_TYPE_INT, 24,
1623 "width", G_TYPE_INT, 24,
1624 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1625 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1627 case OGGPCM_FMT_S24_BE:
1628 caps = gst_caps_new_simple ("audio/x-raw-int",
1629 "depth", G_TYPE_INT, 24,
1630 "width", G_TYPE_INT, 24,
1631 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1632 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1634 case OGGPCM_FMT_S32_LE:
1635 caps = gst_caps_new_simple ("audio/x-raw-int",
1636 "depth", G_TYPE_INT, 32,
1637 "width", G_TYPE_INT, 32,
1638 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
1639 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1641 case OGGPCM_FMT_S32_BE:
1642 caps = gst_caps_new_simple ("audio/x-raw-int",
1643 "depth", G_TYPE_INT, 32,
1644 "width", G_TYPE_INT, 32,
1645 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1646 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
1648 case OGGPCM_FMT_ULAW:
1649 caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
1651 case OGGPCM_FMT_ALAW:
1652 caps = gst_caps_new_simple ("audio/x-alaw", NULL);
1654 case OGGPCM_FMT_FLT32_LE:
1655 caps = gst_caps_new_simple ("audio/x-raw-float",
1656 "width", G_TYPE_INT, 32,
1657 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1659 case OGGPCM_FMT_FLT32_BE:
1660 caps = gst_caps_new_simple ("audio/x-raw-float",
1661 "width", G_TYPE_INT, 32,
1662 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1664 case OGGPCM_FMT_FLT64_LE:
1665 caps = gst_caps_new_simple ("audio/x-raw-float",
1666 "width", G_TYPE_INT, 64,
1667 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1669 case OGGPCM_FMT_FLT64_BE:
1670 caps = gst_caps_new_simple ("audio/x-raw-float",
1671 "width", G_TYPE_INT, 64,
1672 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1678 gst_caps_set_simple (caps, "audio/x-raw-int",
1679 "rate", G_TYPE_INT, pad->granulerate_n,
1680 "channels", G_TYPE_INT, channels, NULL);
1689 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1691 guint8 *data = packet->packet;
1693 pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1694 pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1695 pad->granuleshift = data[28];
1696 GST_LOG ("sample rate: %d", pad->granulerate_n);
1698 pad->n_header_packets = 3;
1700 if (pad->granulerate_n == 0)
1703 data += 4 + (4 + 4 + 4);
1704 GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1705 GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1707 pad->caps = gst_caps_new_simple ("text/x-cmml", NULL);
1708 pad->always_flush_page = TRUE;
1709 pad->is_sparse = TRUE;
1717 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1719 guint8 *data = packet->packet;
1721 pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1722 pad->granulerate_d = 1;
1723 pad->granuleshift = 0;
1724 GST_LOG ("sample rate: %d", pad->granulerate_n);
1726 pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1727 pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1729 if (pad->granulerate_n == 0)
1732 pad->caps = gst_caps_new_simple ("audio/x-celt",
1733 "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1741 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1743 guint8 *data = packet->packet;
1744 const char *category;
1746 if (packet->bytes < 64)
1749 pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1750 pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1751 pad->granuleshift = GST_READ_UINT8 (data + 15);
1752 GST_LOG ("sample rate: %d", pad->granulerate_n);
1754 pad->n_header_packets = GST_READ_UINT8 (data + 11);
1755 GST_LOG ("kate header packets: %d", pad->n_header_packets);
1757 if (pad->granulerate_n == 0)
1760 category = (const char *) data + 48;
1761 if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1762 strcmp (category, "spu-subtitles") == 0 ||
1763 strcmp (category, "K-SPU") == 0) {
1764 pad->caps = gst_caps_new_simple ("subtitle/x-kate", NULL);
1766 pad->caps = gst_caps_new_simple ("application/x-kate", NULL);
1769 pad->is_sparse = TRUE;
1770 pad->always_flush_page = TRUE;
1776 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1780 if (packet->bytes < 1)
1783 switch (packet->packet[0]) {
1784 case 0x00: /* text data */
1785 if (packet->bytes < 1 + 8 * 2) {
1788 duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
1794 duration = GST_CLOCK_TIME_NONE;
1802 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
1804 GstTagList *list = NULL;
1806 if (packet->bytes <= 0)
1809 switch (packet->packet[0]) {
1811 const gchar *canonical;
1814 if (packet->bytes < 64) {
1815 GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
1819 /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
1820 memcpy (language, packet->packet + 32, 16);
1823 /* language is an ISO 639-1 code or RFC 3066 language code, we
1824 * truncate to ISO 639-1 */
1825 g_strdelimit (language, NULL, '\0');
1826 canonical = gst_tag_get_language_code_iso_639_1 (language);
1828 list = gst_tag_list_new_full (GST_TAG_LANGUAGE_CODE, canonical, NULL);
1830 GST_WARNING ("Unknown or invalid language code %s, ignored", language);
1835 tag_list_from_vorbiscomment_packet (packet,
1836 (const guint8 *) "\201kate\0\0\0\0", 9, &list);
1844 /* ensure the comment packet cannot override the category/language
1845 from the identification header */
1846 gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL);
1847 gst_tag_list_free (list);
1849 pad->taglist = list;
1856 setup_opus_mapper (GstOggStream * pad, ogg_packet * packet)
1858 if (packet->bytes < 19)
1861 pad->granulerate_n = 48000;
1862 pad->granulerate_d = 1;
1863 pad->granuleshift = 0;
1864 pad->n_header_packets = 2;
1866 /* pre-skip is in samples at 48000 Hz, which matches granule one for one */
1867 pad->granule_offset = -GST_READ_UINT16_LE (packet->packet + 10);
1868 GST_INFO ("Opus has a pre-skip of %" G_GINT64_FORMAT " samples",
1869 -pad->granule_offset);
1871 pad->caps = gst_caps_new_simple ("audio/x-opus", NULL);
1877 is_header_opus (GstOggStream * pad, ogg_packet * packet)
1879 return packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4);
1883 packet_duration_opus (GstOggStream * pad, ogg_packet * packet)
1885 static const guint64 durations[32] = {
1886 480, 960, 1920, 2880, /* Silk NB */
1887 480, 960, 1920, 2880, /* Silk MB */
1888 480, 960, 1920, 2880, /* Silk WB */
1889 480, 960, /* Hybrid SWB */
1890 480, 960, /* Hybrid FB */
1891 120, 240, 480, 960, /* CELT NB */
1892 120, 240, 480, 960, /* CELT NB */
1893 120, 240, 480, 960, /* CELT NB */
1894 120, 240, 480, 960, /* CELT NB */
1898 gint64 frame_duration;
1902 if (packet->bytes < 1)
1906 if (is_header_opus (pad, packet))
1909 toc = packet->packet[0];
1911 frame_duration = durations[toc >> 3];
1923 if (packet->bytes < 2) {
1924 GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
1927 nframes = packet->packet[1] & 63;
1931 duration = nframes * frame_duration;
1932 if (duration > 5760) {
1933 GST_WARNING ("Opus packet duration > 120 ms, invalid");
1936 GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
1937 frame_duration / 48.f, nframes, duration / 48.f);
1942 extract_tags_opus (GstOggStream * pad, ogg_packet * packet)
1944 if (packet->bytes >= 8 && memcmp (packet->packet, "OpusTags", 8) == 0) {
1945 tag_list_from_vorbiscomment_packet (packet,
1946 (const guint8 *) "OpusTags", 8, &pad->taglist);
1952 /* indent hates our freedoms */
1953 const GstOggMap mappers[] = {
1955 "\200theora", 7, 42,
1957 setup_theora_mapper,
1958 granulepos_to_granule_theora,
1959 granule_to_granulepos_default,
1962 packet_duration_constant,
1967 "\001vorbis", 7, 22,
1969 setup_vorbis_mapper,
1970 granulepos_to_granule_default,
1971 granule_to_granulepos_default,
1974 packet_duration_vorbis,
1982 granulepos_to_granule_default,
1983 granule_to_granulepos_default,
1986 packet_duration_constant,
2003 "CMML\0\0\0\0", 8, 0,
2016 "application/x-annodex",
2017 setup_fishead_mapper,
2018 granulepos_to_granule_default,
2019 granule_to_granulepos_default,
2028 "application/octet-stream",
2029 setup_fishead_mapper,
2042 granulepos_to_granule_default,
2043 granule_to_granulepos_default,
2046 packet_duration_flac,
2054 granulepos_to_granule_default,
2055 granule_to_granulepos_default,
2058 packet_duration_flac,
2064 "application/octet-stream",
2077 granulepos_to_granule_default,
2078 granule_to_granulepos_default,
2081 packet_duration_constant,
2086 "\200kate\0\0\0", 8, 0,
2089 granulepos_to_granule_default,
2090 granule_to_granulepos_default,
2093 packet_duration_kate,
2101 granulepos_to_granule_dirac,
2102 granule_to_granulepos_dirac,
2105 packet_duration_constant,
2106 granulepos_to_key_granule_dirac,
2113 granulepos_to_granule_vp8,
2114 granule_to_granulepos_vp8,
2117 packet_duration_vp8,
2118 granulepos_to_key_granule_vp8,
2125 granulepos_to_granule_default,
2126 granule_to_granulepos_default,
2129 packet_duration_opus,
2134 "\001audio\0\0\0", 9, 53,
2135 "application/x-ogm-audio",
2136 setup_ogmaudio_mapper,
2137 granulepos_to_granule_default,
2138 granule_to_granulepos_default,
2141 packet_duration_ogm,
2146 "\001video\0\0\0", 9, 53,
2147 "application/x-ogm-video",
2148 setup_ogmvideo_mapper,
2149 granulepos_to_granule_default,
2150 granule_to_granulepos_default,
2153 packet_duration_constant,
2158 "\001text\0\0\0", 9, 9,
2159 "application/x-ogm-text",
2160 setup_ogmtext_mapper,
2161 granulepos_to_granule_default,
2162 granule_to_granulepos_default,
2165 packet_duration_ogm,
2173 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2178 for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2179 if (packet->bytes >= mappers[i].min_packet_size &&
2180 packet->bytes >= mappers[i].id_length &&
2181 memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
2183 GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2185 if (mappers[i].setup_func)
2186 ret = mappers[i].setup_func (pad, packet);
2191 GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2195 GST_WARNING ("mapper '%s' did not accept setup header",
2196 mappers[i].media_type);
2205 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2206 const GstCaps * caps)
2208 const GstStructure *structure;
2209 const GstBuffer *buf;
2210 const GValue *streamheader;
2211 const GValue *first_element;
2214 GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2219 structure = gst_caps_get_structure (caps, 0);
2220 streamheader = gst_structure_get_value (structure, "streamheader");
2222 if (streamheader == NULL) {
2223 GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2227 if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2228 GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2232 if (gst_value_array_get_size (streamheader) == 0) {
2233 GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2237 first_element = gst_value_array_get_value (streamheader, 0);
2239 if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2240 GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2244 buf = gst_value_get_buffer (first_element);
2245 if (buf == NULL || GST_BUFFER_SIZE (buf) == 0) {
2246 GST_ERROR ("invalid first streamheader buffer");
2250 GST_MEMDUMP ("streamheader", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
2252 packet.packet = GST_BUFFER_DATA (buf);
2253 packet.bytes = GST_BUFFER_SIZE (buf);
2255 GST_INFO ("Found headers on caps, using those to determine type");
2256 return gst_ogg_stream_setup_map (pad, &packet);