Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / ext / ogg / gstoggstream.c
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>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstoggstream.h"
26 #include "dirac_parse.h"
27 #include "vorbis_parse.h"
28
29 #include <gst/riff/riff-media.h>
30
31 #include <stdlib.h>
32 #include <string.h>
33
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
37
38 typedef struct _GstOggMap GstOggMap;
39
40 typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad,
41     ogg_packet * packet);
42 typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad,
43     gint64 granulepos);
44 typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad,
45     gint64 granulepos);
46 typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad,
47     gint64 granule, gint64 keyframe_granule);
48
49 /* returns TRUE if the granulepos denotes a key frame */
50 typedef gboolean (*GstOggMapIsGranuleposKeyFrameFunc) (GstOggStream * pad,
51     gint64 granulepos);
52
53 /* returns TRUE if the packet is a key frame */
54 typedef gboolean (*GstOggMapIsPacketKeyFrameFunc) (GstOggStream * pad,
55     ogg_packet * packet);
56
57 /* returns TRUE if the given packet is a stream header packet */
58 typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
59     ogg_packet * packet);
60 typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
61     ogg_packet * packet);
62 typedef void (*GstOggMapExtractTagsFunc) (GstOggStream * pad,
63     ogg_packet * packet);
64
65 typedef gint64 (*GstOggMapGranuleposToKeyGranuleFunc) (GstOggStream * pad,
66     gint64 granulepos);
67
68 #define SKELETON_FISBONE_MIN_SIZE  52
69 #define SKELETON_FISHEAD_3_3_MIN_SIZE 112
70 #define SKELETON_FISHEAD_4_0_MIN_SIZE 80
71
72 struct _GstOggMap
73 {
74   const gchar *id;
75   int id_length;
76   int min_packet_size;
77   const gchar *media_type;
78   GstOggMapSetupFunc setup_func;
79   GstOggMapToGranuleFunc granulepos_to_granule_func;
80   GstOggMapToGranuleposFunc granule_to_granulepos_func;
81   GstOggMapIsGranuleposKeyFrameFunc is_granulepos_key_frame_func;
82   GstOggMapIsPacketKeyFrameFunc is_packet_key_frame_func;
83   GstOggMapIsHeaderPacketFunc is_header_func;
84   GstOggMapPacketDurationFunc packet_duration_func;
85   GstOggMapGranuleposToKeyGranuleFunc granulepos_to_key_granule_func;
86   GstOggMapExtractTagsFunc extract_tags_func;
87 };
88
89 extern const GstOggMap mappers[];
90
91 GstClockTime
92 gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet)
93 {
94   int duration;
95
96   if (packet->granulepos == -1) {
97     return GST_CLOCK_TIME_NONE;
98   }
99
100   duration = gst_ogg_stream_get_packet_duration (pad, packet);
101   if (duration == -1) {
102     return GST_CLOCK_TIME_NONE;
103   }
104
105   return gst_ogg_stream_granule_to_time (pad,
106       gst_ogg_stream_granulepos_to_granule (pad,
107           packet->granulepos) - duration);
108 }
109
110 GstClockTime
111 gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad,
112     gint64 granulepos)
113 {
114   if (pad->frame_size == 0)
115     return GST_CLOCK_TIME_NONE;
116
117   return gst_ogg_stream_granule_to_time (pad,
118       gst_ogg_stream_granulepos_to_granule (pad, granulepos));
119 }
120
121 GstClockTime
122 gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad,
123     gint64 granulepos)
124 {
125   return gst_ogg_stream_granule_to_time (pad,
126       gst_ogg_stream_granulepos_to_granule (pad, granulepos));
127 }
128
129 GstClockTime
130 gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule)
131 {
132   if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
133     return 0;
134
135   granule += pad->granule_offset;
136   if (granule < 0)
137     return 0;
138
139   return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
140       pad->granulerate_n);
141 }
142
143 gint64
144 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
145 {
146   if (granulepos == -1 || granulepos == 0) {
147     return granulepos;
148   }
149
150   if (mappers[pad->map].granulepos_to_granule_func == NULL) {
151     GST_WARNING ("Failed to convert %s granulepos to granule",
152         gst_ogg_stream_get_media_type (pad));
153     return -1;
154   }
155
156   return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
157 }
158
159 gint64
160 gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos)
161 {
162   if (mappers[pad->map].granulepos_to_key_granule_func)
163     return mappers[pad->map].granulepos_to_key_granule_func (pad, granulepos);
164
165   if (granulepos == -1 || granulepos == 0) {
166     return granulepos;
167   }
168
169   return granulepos >> pad->granuleshift;
170 }
171
172 gint64
173 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
174     gint64 keyframe_granule)
175 {
176   if (granule == -1 || granule == 0) {
177     return granule;
178   }
179
180   if (mappers[pad->map].granule_to_granulepos_func == NULL) {
181     GST_WARNING ("Failed to convert %s granule to granulepos",
182         gst_ogg_stream_get_media_type (pad));
183     return -1;
184   }
185
186   return mappers[pad->map].granule_to_granulepos_func (pad, granule,
187       keyframe_granule);
188 }
189
190 gboolean
191 gst_ogg_stream_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos)
192 {
193   if (granulepos == -1) {
194     return FALSE;
195   }
196
197   if (mappers[pad->map].is_granulepos_key_frame_func == NULL) {
198     GST_WARNING ("Failed to determine keyframeness for %s granulepos",
199         gst_ogg_stream_get_media_type (pad));
200     return FALSE;
201   }
202
203   return mappers[pad->map].is_granulepos_key_frame_func (pad, granulepos);
204 }
205
206 gboolean
207 gst_ogg_stream_packet_is_key_frame (GstOggStream * pad, ogg_packet * packet)
208 {
209   if (mappers[pad->map].is_packet_key_frame_func == NULL) {
210     GST_WARNING ("Failed to determine keyframeness of %s packet",
211         gst_ogg_stream_get_media_type (pad));
212     return FALSE;
213   }
214
215   return mappers[pad->map].is_packet_key_frame_func (pad, packet);
216 }
217
218 gboolean
219 gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
220 {
221   if (mappers[pad->map].is_header_func == NULL) {
222     GST_WARNING ("Failed to determine headerness of %s packet",
223         gst_ogg_stream_get_media_type (pad));
224     return FALSE;
225   }
226
227   return mappers[pad->map].is_header_func (pad, packet);
228 }
229
230 gint64
231 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
232 {
233   if (mappers[pad->map].packet_duration_func == NULL) {
234     GST_WARNING ("Failed to determine %s packet duration",
235         gst_ogg_stream_get_media_type (pad));
236     return -1;
237   }
238
239   return mappers[pad->map].packet_duration_func (pad, packet);
240 }
241
242
243 void
244 gst_ogg_stream_extract_tags (GstOggStream * pad, ogg_packet * packet)
245 {
246   if (mappers[pad->map].extract_tags_func == NULL) {
247     GST_DEBUG ("No tag extraction");
248     return;
249   }
250
251   mappers[pad->map].extract_tags_func (pad, packet);
252 }
253
254 const char *
255 gst_ogg_stream_get_media_type (GstOggStream * pad)
256 {
257   const GstCaps *caps = pad->caps;
258   const GstStructure *structure;
259   if (!caps)
260     return NULL;
261   structure = gst_caps_get_structure (caps, 0);
262   if (!structure)
263     return NULL;
264   return gst_structure_get_name (structure);
265 }
266
267 /* some generic functions */
268
269 static gboolean
270 is_granulepos_keyframe_true (GstOggStream * pad, gint64 granulepos)
271 {
272   return TRUE;
273 }
274
275 static gboolean
276 is_packet_keyframe_true (GstOggStream * pad, ogg_packet * packet)
277 {
278   return TRUE;
279 }
280
281 static gint64
282 granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos)
283 {
284   gint64 keyindex, keyoffset;
285
286   if (pad->granuleshift != 0) {
287     keyindex = granulepos >> pad->granuleshift;
288     keyoffset = granulepos - (keyindex << pad->granuleshift);
289     return keyindex + keyoffset;
290   } else {
291     return granulepos;
292   }
293 }
294
295
296 static gint64
297 granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
298     gint64 keyframe_granule)
299 {
300   gint64 keyoffset;
301
302   if (pad->granuleshift != 0) {
303     /* If we don't know where the previous keyframe is yet, assume it is
304        at 0 or 1, depending on bitstream version. If nothing else, this
305        avoids getting negative granpos back. */
306     if (keyframe_granule < 0)
307       keyframe_granule = pad->theora_has_zero_keyoffset ? 0 : 1;
308     keyoffset = granule - keyframe_granule;
309     return (keyframe_granule << pad->granuleshift) | keyoffset;
310   } else {
311     return granule;
312   }
313 }
314
315 #ifdef unused
316 static gboolean
317 is_header_unknown (GstOggStream * pad, ogg_packet * packet)
318 {
319   GST_WARNING ("don't know how to detect header");
320   return FALSE;
321 }
322 #endif
323
324 static gboolean
325 is_header_true (GstOggStream * pad, ogg_packet * packet)
326 {
327   return TRUE;
328 }
329
330 static gboolean
331 is_header_count (GstOggStream * pad, ogg_packet * packet)
332 {
333   if (pad->n_header_packets_seen < pad->n_header_packets) {
334     return TRUE;
335   }
336   return FALSE;
337 }
338
339 static gint64
340 packet_duration_constant (GstOggStream * pad, ogg_packet * packet)
341 {
342   return pad->frame_size;
343 }
344
345 /* helper: extracts tags from vorbis comment ogg packet.
346  * Returns result in *tags after free'ing existing *tags (if any) */
347 static gboolean
348 tag_list_from_vorbiscomment_packet (ogg_packet * packet,
349     const guint8 * id_data, const guint id_data_length, GstTagList ** tags)
350 {
351   gchar *encoder = NULL;
352   GstTagList *list;
353   gboolean ret = TRUE;
354
355   g_return_val_if_fail (tags != NULL, FALSE);
356
357   list = gst_tag_list_from_vorbiscomment (packet->packet, packet->bytes,
358       id_data, id_data_length, &encoder);
359
360   if (!list) {
361     GST_WARNING ("failed to decode vorbis comments");
362     ret = FALSE;
363     goto exit;
364   }
365
366   if (encoder) {
367     if (encoder[0])
368       gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
369           NULL);
370     g_free (encoder);
371   }
372
373 exit:
374   if (*tags)
375     gst_tag_list_free (*tags);
376   *tags = list;
377
378   return ret;
379 }
380
381 /* theora */
382
383 static gboolean
384 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
385 {
386   guint8 *data = packet->packet;
387   guint w, h, par_d, par_n;
388   guint8 vmaj, vmin, vrev;
389
390   vmaj = data[7];
391   vmin = data[8];
392   vrev = data[9];
393
394   w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
395   h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
396
397   pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
398   pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
399
400   par_n = GST_READ_UINT24_BE (data + 30);
401   par_d = GST_READ_UINT24_BE (data + 33);
402
403   GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
404       pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
405
406   /* 2 bits + 3 bits = 5 bits KFGSHIFT */
407   pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
408       (GST_READ_UINT8 (data + 41) >> 5);
409   GST_LOG ("granshift: %d", pad->granuleshift);
410
411   pad->is_video = TRUE;
412   pad->n_header_packets = 3;
413   pad->frame_size = 1;
414
415   pad->bitrate = GST_READ_UINT24_BE (data + 37);
416   GST_LOG ("bit rate: %d", pad->bitrate);
417
418   if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
419     GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
420     return FALSE;
421   }
422
423   /* The interpretation of the granule position has changed with 3.2.1.
424      The granule is now made from the number of frames encoded, rather than
425      the index of the frame being encoded - so there is a difference of 1. */
426   pad->theora_has_zero_keyoffset =
427       ((vmaj << 16) | (vmin << 8) | vrev) < 0x030201;
428
429   pad->caps = gst_caps_new_empty_simple ("video/x-theora");
430
431   if (w > 0 && h > 0) {
432     gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
433         G_TYPE_INT, h, NULL);
434   }
435
436   /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
437   if (par_n == 0 || par_d == 0)
438     par_n = par_d = 1;
439
440   /* only add framerate now so caps look prettier, with width/height first */
441   gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
442       pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
443       GST_TYPE_FRACTION, par_n, par_d, NULL);
444
445   return TRUE;
446 }
447
448 static gint64
449 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
450 {
451   gint64 keyindex, keyoffset;
452
453   if (pad->granuleshift != 0) {
454     keyindex = granulepos >> pad->granuleshift;
455     keyoffset = granulepos - (keyindex << pad->granuleshift);
456     if (pad->theora_has_zero_keyoffset) {
457       keyoffset++;
458     }
459     return keyindex + keyoffset;
460   } else {
461     return granulepos;
462   }
463 }
464
465 static gboolean
466 is_granulepos_keyframe_theora (GstOggStream * pad, gint64 granulepos)
467 {
468   gint64 frame_mask;
469
470   if (granulepos == (gint64) - 1)
471     return FALSE;
472
473   frame_mask = (1 << pad->granuleshift) - 1;
474
475   return ((granulepos & frame_mask) == 0);
476 }
477
478 static gboolean
479 is_packet_keyframe_theora (GstOggStream * pad, ogg_packet * packet)
480 {
481   if (packet->bytes == 0)
482     return FALSE;
483   return (packet->packet[0] & 0xc0) == 0x00;
484 }
485
486 static gboolean
487 is_header_theora (GstOggStream * pad, ogg_packet * packet)
488 {
489   return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
490 }
491
492 static void
493 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
494 {
495   if (packet->bytes > 0 && packet->packet[0] == 0x81) {
496     tag_list_from_vorbiscomment_packet (packet,
497         (const guint8 *) "\201theora", 7, &pad->taglist);
498
499     if (!pad->taglist)
500       pad->taglist = gst_tag_list_new_empty ();
501
502     if (pad->bitrate)
503       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
504           GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
505   }
506 }
507
508 /* dirac */
509
510 static gboolean
511 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
512 {
513   int ret;
514   DiracSequenceHeader header;
515
516   ret = dirac_sequence_header_parse (&header, packet->packet + 13,
517       packet->bytes - 13);
518   if (ret == 0) {
519     GST_DEBUG ("Failed to parse Dirac sequence header");
520     return FALSE;
521   }
522
523   pad->is_video = TRUE;
524   pad->always_flush_page = TRUE;
525   pad->granulerate_n = header.frame_rate_numerator * 2;
526   pad->granulerate_d = header.frame_rate_denominator;
527   pad->granuleshift = 22;
528   pad->n_header_packets = 1;
529   pad->frame_size = 2;
530
531   if (header.interlaced_coding != 0) {
532     GST_DEBUG ("non-progressive Dirac coding not implemented");
533     return FALSE;
534   }
535
536   pad->caps = gst_caps_new_simple ("video/x-dirac",
537       "width", G_TYPE_INT, header.width,
538       "height", G_TYPE_INT, header.height,
539       "interlaced", G_TYPE_BOOLEAN, header.interlaced,
540       "pixel-aspect-ratio", GST_TYPE_FRACTION,
541       header.aspect_ratio_numerator, header.aspect_ratio_denominator,
542       "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
543       header.frame_rate_denominator, NULL);
544
545   return TRUE;
546 }
547
548 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
549 static gboolean
550 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
551 {
552   int dist_h;
553   int dist_l;
554   int dist;
555
556   if (granulepos == -1)
557     return -1;
558
559   dist_h = (granulepos >> 22) & 0xff;
560   dist_l = granulepos & 0xff;
561   dist = (dist_h << 8) | dist_l;
562
563   return (dist == 0);
564 }
565
566 static gint64
567 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
568 {
569   gint64 pt;
570   int delay;
571   gint64 dt;
572
573   pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
574   delay = (gp >> 9) & 0x1fff;
575   dt = pt - delay;
576
577   GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
578
579   return dt + 4;
580 }
581
582 static gint64
583 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
584     gint64 keyframe_granule)
585 {
586   /* This conversion requires knowing more details about the Dirac
587    * stream. */
588   return -1;
589 }
590
591 static gint64
592 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
593 {
594   gint64 pt;
595   int dist_h;
596   int dist_l;
597   int dist;
598   int delay;
599   gint64 dt;
600
601   if (gp == -1 || gp == 0)
602     return gp;
603
604   pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
605   dist_h = (gp >> 22) & 0xff;
606   dist_l = gp & 0xff;
607   dist = (dist_h << 8) | dist_l;
608   delay = (gp >> 9) & 0x1fff;
609   dt = pt - delay;
610
611   return dt - 2 * dist + 4;
612 }
613
614 /* VP8 */
615
616 static gboolean
617 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
618 {
619   gint width, height, par_n, par_d, fps_n, fps_d;
620
621   if (packet->bytes < 26) {
622     GST_DEBUG ("Failed to parse VP8 BOS page");
623     return FALSE;
624   }
625
626   width = GST_READ_UINT16_BE (packet->packet + 8);
627   height = GST_READ_UINT16_BE (packet->packet + 10);
628   par_n = GST_READ_UINT24_BE (packet->packet + 12);
629   par_d = GST_READ_UINT24_BE (packet->packet + 15);
630   fps_n = GST_READ_UINT32_BE (packet->packet + 18);
631   fps_d = GST_READ_UINT32_BE (packet->packet + 22);
632
633   pad->is_video = TRUE;
634   pad->is_vp8 = TRUE;
635   pad->granulerate_n = fps_n;
636   pad->granulerate_d = fps_d;
637   pad->n_header_packets = 2;
638   pad->frame_size = 1;
639
640   pad->caps = gst_caps_new_simple ("video/x-vp8",
641       "width", G_TYPE_INT, width,
642       "height", G_TYPE_INT, height,
643       "pixel-aspect-ratio", GST_TYPE_FRACTION,
644       par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
645
646   return TRUE;
647 }
648
649 static gboolean
650 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
651 {
652   guint64 gpos = granulepos;
653
654   if (granulepos == -1)
655     return FALSE;
656
657   /* Get rid of flags */
658   gpos >>= 3;
659
660   return ((gpos & 0x07ffffff) == 0);
661 }
662
663 static gint64
664 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
665 {
666   guint64 gp = (guint64) gpos;
667   guint32 pt;
668   guint32 dist;
669
670   pt = (gp >> 32);
671   dist = (gp >> 3) & 0x07ffffff;
672
673   GST_DEBUG ("pt %u, dist %u", pt, dist);
674
675   return pt;
676 }
677
678 static gint64
679 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
680     gint64 keyframe_granule)
681 {
682   /* FIXME: This requires to look into the content of the packets
683    * because the simple granule counter doesn't know about invisible
684    * frames...
685    */
686   return -1;
687 }
688
689 /* Check if this packet contains an invisible frame or not */
690 static gint64
691 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
692 {
693   guint32 hdr;
694
695   if (packet->bytes < 3)
696     return 0;
697
698   hdr = GST_READ_UINT24_LE (packet->packet);
699
700   return (((hdr >> 4) & 1) != 0) ? 1 : 0;
701 }
702
703 static gint64
704 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
705 {
706   guint64 gp = granulepos;
707   guint64 pts = (gp >> 32);
708   guint32 dist = (gp >> 3) & 0x07ffffff;
709
710   if (granulepos == -1 || granulepos == 0)
711     return granulepos;
712
713   if (dist > pts)
714     return 0;
715
716   return pts - dist;
717 }
718
719 static gboolean
720 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
721 {
722   if (packet->bytes >= 5 && packet->packet[0] == 0x4F &&
723       packet->packet[1] == 0x56 && packet->packet[2] == 0x50 &&
724       packet->packet[3] == 0x38 && packet->packet[4] == 0x30)
725     return TRUE;
726   return FALSE;
727 }
728
729 static void
730 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
731 {
732   if (packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) {
733     tag_list_from_vorbiscomment_packet (packet,
734         (const guint8 *) "OVP80\2 ", 7, &pad->taglist);
735   }
736 }
737
738 /* vorbis */
739
740 static gboolean
741 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
742 {
743   guint8 *data = packet->packet;
744   guint chans;
745
746   data += 1 + 6;
747   pad->version = GST_READ_UINT32_LE (data);
748   data += 4;
749   chans = GST_READ_UINT8 (data);
750   data += 1;
751   pad->granulerate_n = GST_READ_UINT32_LE (data);
752   pad->granulerate_d = 1;
753   pad->granuleshift = 0;
754   pad->preroll = 2;
755   pad->last_size = 0;
756   GST_LOG ("sample rate: %d", pad->granulerate_n);
757
758   data += 4;
759   pad->bitrate_upper = GST_READ_UINT32_LE (data);
760   data += 4;
761   pad->bitrate_nominal = GST_READ_UINT32_LE (data);
762   data += 4;
763   pad->bitrate_lower = GST_READ_UINT32_LE (data);
764
765   if (pad->bitrate_nominal > 0)
766     pad->bitrate = pad->bitrate_nominal;
767
768   if (pad->bitrate_upper > 0 && !pad->bitrate)
769     pad->bitrate = pad->bitrate_upper;
770
771   if (pad->bitrate_lower > 0 && !pad->bitrate)
772     pad->bitrate = pad->bitrate_lower;
773
774   GST_LOG ("bit rate: %d", pad->bitrate);
775
776   pad->n_header_packets = 3;
777
778   if (pad->granulerate_n == 0)
779     return FALSE;
780
781   parse_vorbis_header_packet (pad, packet);
782
783   pad->caps = gst_caps_new_simple ("audio/x-vorbis",
784       "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
785       NULL);
786
787   return TRUE;
788 }
789
790 static gboolean
791 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
792 {
793   if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
794     return FALSE;
795
796   if (packet->packet[0] == 5) {
797     parse_vorbis_setup_packet (pad, packet);
798   }
799
800   return TRUE;
801 }
802
803 static void
804 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
805 {
806   if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
807     return;
808
809   if (((guint8 *) (packet->packet))[0] == 0x03) {
810     tag_list_from_vorbiscomment_packet (packet,
811         (const guint8 *) "\003vorbis", 7, &pad->taglist);
812
813     if (!pad->taglist)
814       pad->taglist = gst_tag_list_new_empty ();
815
816     gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
817         GST_TAG_ENCODER_VERSION, pad->version, NULL);
818
819     if (pad->bitrate_nominal > 0)
820       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
821           GST_TAG_NOMINAL_BITRATE, (guint) pad->bitrate_nominal, NULL);
822
823     if (pad->bitrate_upper > 0)
824       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
825           GST_TAG_MAXIMUM_BITRATE, (guint) pad->bitrate_upper, NULL);
826
827     if (pad->bitrate_lower > 0)
828       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
829           GST_TAG_MINIMUM_BITRATE, (guint) pad->bitrate_lower, NULL);
830
831     if (pad->bitrate)
832       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
833           GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
834   }
835 }
836
837 static gint64
838 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
839 {
840   int mode;
841   int size;
842   int duration;
843
844   if (packet->bytes == 0 || packet->packet[0] & 1)
845     return 0;
846
847   mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
848   size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
849
850   if (pad->last_size == 0) {
851     duration = 0;
852   } else {
853     duration = pad->last_size / 4 + size / 4;
854   }
855   pad->last_size = size;
856
857   GST_DEBUG ("duration %d", (int) duration);
858
859   return duration;
860 }
861
862 /* speex */
863
864
865 static gboolean
866 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
867 {
868   guint8 *data = packet->packet;
869   guint chans;
870
871   data += 8 + 20 + 4 + 4;
872   pad->granulerate_n = GST_READ_UINT32_LE (data);
873   pad->granulerate_d = 1;
874   pad->granuleshift = 0;
875
876   data += 4 + 4 + 4;
877   chans = GST_READ_UINT32_LE (data);
878   data += 4;
879   pad->bitrate = GST_READ_UINT32_LE (data);
880
881   GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
882   GST_LOG ("bit rate: %d", pad->bitrate);
883
884   pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
885   pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
886       GST_READ_UINT32_LE (packet->packet + 56);
887
888   if (pad->granulerate_n == 0)
889     return FALSE;
890
891   pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
892       pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
893
894   return TRUE;
895 }
896
897 static void
898 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
899 {
900   /* packet 2 must be comment packet */
901   if (packet->bytes > 0 && pad->n_header_packets_seen == 1) {
902     tag_list_from_vorbiscomment_packet (packet, NULL, 0, &pad->taglist);
903
904     if (!pad->taglist)
905       pad->taglist = gst_tag_list_new_empty ();
906
907     if (pad->bitrate)
908       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
909           GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
910   }
911 }
912
913
914 /* flac */
915
916 static gboolean
917 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
918 {
919   pad->granulerate_n = 0;
920   pad->granulerate_d = 1;
921   pad->granuleshift = 0;
922
923   pad->n_header_packets = 3;
924
925   pad->caps = gst_caps_new_empty_simple ("audio/x-flac");
926
927   return TRUE;
928 }
929
930 static gboolean
931 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
932 {
933   if (pad->n_header_packets_seen == 1) {
934     pad->granulerate_n = (packet->packet[14] << 12) |
935         (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
936   }
937
938   if (pad->n_header_packets_seen < pad->n_header_packets) {
939     return TRUE;
940   }
941
942   return FALSE;
943 }
944
945 static gboolean
946 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
947 {
948   guint8 *data = packet->packet;
949   guint chans;
950
951   /* see http://flac.sourceforge.net/ogg_mapping.html */
952
953   pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
954   pad->granulerate_d = 1;
955   pad->granuleshift = 0;
956   chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
957
958   GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
959
960   pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
961
962   if (pad->granulerate_n == 0)
963     return FALSE;
964
965   pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
966       pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
967
968   return TRUE;
969 }
970
971 static gboolean
972 is_header_flac (GstOggStream * pad, ogg_packet * packet)
973 {
974   return (packet->bytes > 0 && (packet->packet[0] != 0xff));
975 }
976
977 static gint64
978 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
979 {
980   int block_size_index;
981
982   if (packet->bytes < 4)
983     return -1;
984
985   block_size_index = packet->packet[2] >> 4;
986   if (block_size_index == 1)
987     return 192;
988   if (block_size_index >= 2 && block_size_index <= 5) {
989     return 576 << (block_size_index - 2);
990   }
991   if (block_size_index >= 8) {
992     return 256 << (block_size_index - 8);
993   }
994   if (block_size_index == 6 || block_size_index == 7) {
995     guint len, bytes = (block_size_index - 6) + 1;
996     guint8 tmp;
997
998     if (packet->bytes < 4 + 1 + bytes)
999       return -1;
1000     tmp = packet->packet[4];
1001     /* utf-8 prefix */
1002     len = 0;
1003     while (tmp & 0x80) {
1004       len++;
1005       tmp <<= 1;
1006     }
1007     if (len == 2)
1008       return -1;
1009     if (len == 0)
1010       len++;
1011     if (packet->bytes < 4 + len + bytes)
1012       return -1;
1013     if (bytes == 1) {
1014       return packet->packet[4 + len] + 1;
1015     } else {
1016       return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
1017     }
1018   }
1019   return -1;
1020 }
1021
1022 static void
1023 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
1024 {
1025   if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
1026     tag_list_from_vorbiscomment_packet (packet,
1027         packet->packet, 4, &pad->taglist);
1028   }
1029 }
1030
1031 /* fishead */
1032
1033 static gboolean
1034 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
1035 {
1036   guint8 *data;
1037   gint64 prestime_n, prestime_d;
1038   gint64 basetime_n, basetime_d;
1039
1040   data = packet->packet;
1041
1042   data += 8;                    /* header */
1043
1044   pad->skeleton_major = GST_READ_UINT16_LE (data);
1045   data += 2;
1046   pad->skeleton_minor = GST_READ_UINT16_LE (data);
1047   data += 2;
1048
1049   prestime_n = (gint64) GST_READ_UINT64_LE (data);
1050   data += 8;
1051   prestime_d = (gint64) GST_READ_UINT64_LE (data);
1052   data += 8;
1053   basetime_n = (gint64) GST_READ_UINT64_LE (data);
1054   data += 8;
1055   basetime_d = (gint64) GST_READ_UINT64_LE (data);
1056   data += 8;
1057
1058   /* FIXME: we don't use basetime anywhere in the demuxer! */
1059   if (basetime_d != 0)
1060     pad->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
1061   else
1062     pad->basetime = -1;
1063
1064   if (prestime_d != 0)
1065     pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1066   else
1067     pad->prestime = -1;
1068
1069   /* Ogg Skeleton 3.3+ streams provide additional information in the header */
1070   if (packet->bytes >= SKELETON_FISHEAD_3_3_MIN_SIZE && pad->skeleton_major == 3
1071       && pad->skeleton_minor > 0) {
1072     gint64 firstsampletime_n, firstsampletime_d;
1073     gint64 lastsampletime_n, lastsampletime_d;
1074     gint64 firstsampletime, lastsampletime;
1075     guint64 segment_length, content_offset;
1076
1077     firstsampletime_n = GST_READ_UINT64_LE (data + 64);
1078     firstsampletime_d = GST_READ_UINT64_LE (data + 72);
1079     lastsampletime_n = GST_READ_UINT64_LE (data + 80);
1080     lastsampletime_d = GST_READ_UINT64_LE (data + 88);
1081     segment_length = GST_READ_UINT64_LE (data + 96);
1082     content_offset = GST_READ_UINT64_LE (data + 104);
1083
1084     GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1085         firstsampletime_n, firstsampletime_d);
1086     GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1087         lastsampletime_n, lastsampletime_d);
1088     GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1089     GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1090
1091     if (firstsampletime_d > 0)
1092       firstsampletime = gst_util_uint64_scale (GST_SECOND,
1093           firstsampletime_n, firstsampletime_d);
1094     else
1095       firstsampletime = 0;
1096
1097     if (lastsampletime_d > 0)
1098       lastsampletime = gst_util_uint64_scale (GST_SECOND,
1099           lastsampletime_n, lastsampletime_d);
1100     else
1101       lastsampletime = 0;
1102
1103     if (lastsampletime > firstsampletime)
1104       pad->total_time = lastsampletime - firstsampletime;
1105     else
1106       pad->total_time = -1;
1107
1108     GST_INFO ("skeleton fishead parsed total: %" GST_TIME_FORMAT,
1109         GST_TIME_ARGS (pad->total_time));
1110   } else if (packet->bytes >= SKELETON_FISHEAD_4_0_MIN_SIZE
1111       && pad->skeleton_major == 4) {
1112     guint64 segment_length, content_offset;
1113
1114     segment_length = GST_READ_UINT64_LE (data + 64);
1115     content_offset = GST_READ_UINT64_LE (data + 72);
1116
1117     GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1118     GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1119   } else {
1120     pad->total_time = -1;
1121   }
1122
1123   GST_INFO ("skeleton fishead %u.%u parsed (basetime: %" GST_TIME_FORMAT
1124       ", prestime: %" GST_TIME_FORMAT ")", pad->skeleton_major,
1125       pad->skeleton_minor, GST_TIME_ARGS (pad->basetime),
1126       GST_TIME_ARGS (pad->prestime));
1127
1128   pad->is_skeleton = TRUE;
1129   pad->is_sparse = TRUE;
1130
1131   pad->caps = gst_caps_new_empty_simple ("application/x-ogg-skeleton");
1132
1133   return TRUE;
1134 }
1135
1136 gboolean
1137 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1138     guint32 * serialno, GstOggSkeleton * type)
1139 {
1140   GstOggSkeleton stype;
1141   guint serial_offset;
1142
1143   if (size != 0 && size < SKELETON_FISBONE_MIN_SIZE) {
1144     GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1145     return FALSE;
1146   }
1147
1148   if (size == 0) {
1149     /* Skeleton EOS packet is zero bytes */
1150     return FALSE;
1151   } else if (memcmp (data, "fisbone\0", 8) == 0) {
1152     GST_INFO ("got fisbone packet");
1153     stype = GST_OGG_SKELETON_FISBONE;
1154     serial_offset = 12;
1155   } else if (memcmp (data, "index\0", 6) == 0) {
1156     GST_INFO ("got index packet");
1157     stype = GST_OGG_SKELETON_INDEX;
1158     serial_offset = 6;
1159   } else if (memcmp (data, "fishead\0", 8) == 0) {
1160     return FALSE;
1161   } else {
1162     GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1163     return FALSE;
1164   }
1165
1166   if (serialno)
1167     *serialno = GST_READ_UINT32_LE (data + serial_offset);
1168
1169   if (type)
1170     *type = stype;
1171
1172   return TRUE;
1173 }
1174
1175 gboolean
1176 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1177     const guint8 * data, guint size, GstClockTime * p_start_time)
1178 {
1179   GstClockTime start_time;
1180   gint64 start_granule;
1181
1182   if (pad->have_fisbone) {
1183     GST_DEBUG ("already have fisbone, ignoring second one");
1184     return FALSE;
1185   }
1186
1187   /* skip "fisbone\0" + headers offset + serialno + num headers */
1188   data += 8 + 4 + 4 + 4;
1189
1190   pad->have_fisbone = TRUE;
1191
1192   /* We don't overwrite whatever was set before by the format-specific
1193      setup: skeleton contains wrong information sometimes, and the codec
1194      headers are authoritative.
1195      So we only gather information that was not already filled out by
1196      the mapper setup. This should hopefully allow handling unknown
1197      streams a bit better, while not trashing correct setup from bad
1198      skeleton data. */
1199   if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
1200     pad->granulerate_n = GST_READ_UINT64_LE (data);
1201     pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
1202   }
1203   if (pad->granuleshift < 0) {
1204     pad->granuleshift = GST_READ_UINT8 (data + 28);
1205   }
1206
1207   start_granule = GST_READ_UINT64_LE (data + 16);
1208   pad->preroll = GST_READ_UINT32_LE (data + 24);
1209
1210   start_time = granulepos_to_granule_default (pad, start_granule);
1211
1212   GST_INFO ("skeleton fisbone parsed "
1213       "(start time: %" GST_TIME_FORMAT
1214       " granulerate_n: %d granulerate_d: %d "
1215       " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
1216       GST_TIME_ARGS (start_time),
1217       pad->granulerate_n, pad->granulerate_d, pad->preroll, pad->granuleshift);
1218
1219   if (p_start_time)
1220     *p_start_time = start_time;
1221
1222   return TRUE;
1223 }
1224
1225 static gboolean
1226 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1227 {
1228   gint shift = 0;
1229   guint8 byte;
1230
1231   *result = 0;
1232
1233   do {
1234     if (G_UNLIKELY (*size < 1))
1235       return FALSE;
1236
1237     byte = **data;
1238     *result |= ((byte & 0x7f) << shift);
1239     shift += 7;
1240
1241     (*data)++;
1242     (*size)--;
1243   } while ((byte & 0x80) != 0x80);
1244
1245   return TRUE;
1246 }
1247
1248 gboolean
1249 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1250     const guint8 * data, guint size)
1251 {
1252   guint64 i, n_keypoints, isize;
1253   guint64 offset, timestamp;
1254   guint64 offset_d, timestamp_d;
1255
1256   if (pad->index) {
1257     GST_DEBUG ("already have index, ignoring second one");
1258     return TRUE;
1259   }
1260
1261   if ((skel_pad->skeleton_major == 3 && size < 26) ||
1262       (skel_pad->skeleton_major == 4 && size < 62)) {
1263     GST_WARNING ("small index packet of size %u, ignoring", size);
1264     return FALSE;
1265   }
1266
1267   /* skip "index\0" + serialno */
1268   data += 6 + 4;
1269   size -= 6 + 4;
1270
1271   n_keypoints = GST_READ_UINT64_LE (data);
1272
1273   data += 8;
1274   size -= 8;
1275
1276   pad->kp_denom = GST_READ_UINT64_LE (data);
1277   if (pad->kp_denom == 0)
1278     pad->kp_denom = 1;
1279
1280   data += 8;
1281   size -= 8;
1282
1283   if (skel_pad->skeleton_major == 4) {
1284     gint64 firstsampletime_n;
1285     gint64 lastsampletime_n;
1286     gint64 firstsampletime, lastsampletime;
1287
1288     firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1289     lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1290
1291     GST_INFO ("firstsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1292         firstsampletime_n, pad->kp_denom);
1293     GST_INFO ("lastsampletime %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
1294         lastsampletime_n, pad->kp_denom);
1295
1296     firstsampletime = gst_util_uint64_scale (GST_SECOND,
1297         firstsampletime_n, pad->kp_denom);
1298     lastsampletime = gst_util_uint64_scale (GST_SECOND,
1299         lastsampletime_n, pad->kp_denom);
1300
1301     if (lastsampletime > firstsampletime)
1302       pad->total_time = lastsampletime - firstsampletime;
1303     else
1304       pad->total_time = -1;
1305
1306     GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1307         GST_TIME_ARGS (pad->total_time));
1308
1309     data += 16;
1310     size -= 16;
1311   }
1312
1313   GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1314       G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1315
1316   pad->index = g_try_new (GstOggIndex, n_keypoints);
1317   if (!pad->index)
1318     return FALSE;
1319
1320   isize = 0;
1321   offset = 0;
1322   timestamp = 0;
1323
1324   for (i = 0; i < n_keypoints; i++) {
1325     /* read deltas */
1326     if (!read_vlc (&data, &size, &offset_d))
1327       break;
1328     if (!read_vlc (&data, &size, &timestamp_d))
1329       break;
1330
1331     offset += offset_d;
1332     timestamp += timestamp_d;
1333
1334     pad->index[i].offset = offset;
1335     pad->index[i].timestamp = timestamp;
1336     isize++;
1337
1338     GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1339         timestamp);
1340   }
1341   if (isize != n_keypoints) {
1342     GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1343         G_GUINT64_FORMAT, n_keypoints, isize);
1344   }
1345   pad->n_index = isize;
1346   /* try to use the index to estimate the bitrate */
1347   if (isize > 2) {
1348     guint64 so, eo, st, et, b, t;
1349
1350     /* get start and end offset and timestamps */
1351     so = pad->index[0].offset;
1352     st = pad->index[0].timestamp;
1353     eo = pad->index[isize - 1].offset;
1354     et = pad->index[isize - 1].timestamp;
1355
1356     b = eo - so;
1357     t = et - st;
1358
1359     GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1360
1361     /* this is the total stream bitrate according to this index */
1362     pad->idx_bitrate = gst_util_uint64_scale (8 * b, pad->kp_denom, t);
1363
1364     GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1365   }
1366
1367   return TRUE;
1368 }
1369
1370 static gint
1371 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1372     gpointer user_data)
1373 {
1374   if (index->timestamp < *ts)
1375     return -1;
1376   else if (index->timestamp > *ts)
1377     return 1;
1378   else
1379     return 0;
1380 }
1381
1382 gboolean
1383 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1384     guint64 * timestamp, guint64 * offset)
1385 {
1386   guint64 n_index;
1387   guint64 ts;
1388   GstOggIndex *best;
1389
1390   n_index = pad->n_index;
1391   if (n_index == 0 || pad->index == NULL)
1392     return FALSE;
1393
1394   ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1395   GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1396
1397   best =
1398       gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1399       (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1400       NULL);
1401
1402   if (best == NULL)
1403     return FALSE;
1404
1405   GST_INFO ("found at index %u", (guint) (best - pad->index));
1406
1407   if (offset)
1408     *offset = best->offset;
1409   if (timestamp)
1410     *timestamp =
1411         gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1412
1413   return TRUE;
1414 }
1415
1416 /* Do we need these for something?
1417  * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
1418  * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
1419  * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
1420  * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
1421  * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
1422  * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
1423  */
1424
1425 static gboolean
1426 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1427 {
1428   if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1429     return TRUE;
1430
1431   return FALSE;
1432 }
1433
1434 static void
1435 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1436 {
1437   if (!(packet->packet[0] & 1) && (packet->packet[0] & 3 && pad->is_ogm_text)) {
1438     tag_list_from_vorbiscomment_packet (packet,
1439         (const guint8 *) "\003vorbis", 7, &pad->taglist);
1440   }
1441 }
1442
1443 static gint64
1444 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1445 {
1446   const guint8 *data;
1447   int samples;
1448   int offset;
1449   int n;
1450
1451   data = packet->packet;
1452   offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1453
1454   if (offset > packet->bytes) {
1455     GST_ERROR ("buffer too small");
1456     return -1;
1457   }
1458
1459   samples = 0;
1460   for (n = offset - 1; n > 0; n--) {
1461     samples = (samples << 8) | data[n];
1462   }
1463
1464   return samples;
1465 }
1466
1467 static gboolean
1468 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1469 {
1470   guint8 *data = packet->packet;
1471   guint32 fourcc;
1472   gchar *fstr;
1473
1474   pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1475   pad->granulerate_d = 1;
1476
1477   fourcc = GST_READ_UINT32_LE (data + 9);
1478   fstr = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1479   GST_DEBUG ("fourcc: %s", fstr);
1480
1481   /* FIXME: Need to do something with the reorder map */
1482   pad->caps =
1483       gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL, NULL);
1484
1485   GST_LOG ("sample rate: %d", pad->granulerate_n);
1486   if (pad->granulerate_n == 0)
1487     return FALSE;
1488
1489   if (pad->caps) {
1490     gst_caps_set_simple (pad->caps,
1491         "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1492   } else {
1493     pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
1494         "fourcc", G_TYPE_STRING, fstr,
1495         "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1496   }
1497   g_free (fstr);
1498
1499   pad->n_header_packets = 1;
1500   pad->is_ogm = TRUE;
1501
1502   return TRUE;
1503 }
1504
1505 static gboolean
1506 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1507 {
1508   guint8 *data = packet->packet;
1509   guint32 fourcc;
1510   int width, height;
1511   gint64 time_unit;
1512   gchar *fstr;
1513
1514   GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
1515   GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
1516
1517   pad->is_video = TRUE;
1518   pad->granulerate_n = 10000000;
1519   time_unit = GST_READ_UINT64_LE (data + 17);
1520   if (time_unit > G_MAXINT || time_unit < G_MININT) {
1521     GST_WARNING ("timeunit is out of range");
1522   }
1523   pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1524
1525   GST_LOG ("fps = %d/%d = %.3f",
1526       pad->granulerate_n, pad->granulerate_d,
1527       (double) pad->granulerate_n / pad->granulerate_d);
1528
1529   fourcc = GST_READ_UINT32_LE (data + 9);
1530   width = GST_READ_UINT32_LE (data + 45);
1531   height = GST_READ_UINT32_LE (data + 49);
1532   fstr = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1533   GST_DEBUG ("fourcc: %s", fstr);
1534
1535   pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1536
1537   if (pad->caps == NULL) {
1538     pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
1539         "fourcc", G_TYPE_STRING, fstr,
1540         "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1541         pad->granulerate_d, NULL);
1542   } else {
1543     gst_caps_set_simple (pad->caps,
1544         "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1545         pad->granulerate_d,
1546         "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
1547   }
1548   GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
1549   g_free (fstr);
1550
1551   pad->n_header_packets = 1;
1552   pad->frame_size = 1;
1553   pad->is_ogm = TRUE;
1554
1555   return TRUE;
1556 }
1557
1558 static gboolean
1559 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1560 {
1561   guint8 *data = packet->packet;
1562   gint64 time_unit;
1563
1564   pad->granulerate_n = 10000000;
1565   time_unit = GST_READ_UINT64_LE (data + 17);
1566   if (time_unit > G_MAXINT || time_unit < G_MININT) {
1567     GST_WARNING ("timeunit is out of range");
1568   }
1569   pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1570
1571   GST_LOG ("fps = %d/%d = %.3f",
1572       pad->granulerate_n, pad->granulerate_d,
1573       (double) pad->granulerate_n / pad->granulerate_d);
1574
1575   if (pad->granulerate_d <= 0)
1576     return FALSE;
1577
1578   pad->caps = gst_caps_new_empty_simple ("text/plain");
1579
1580   pad->n_header_packets = 1;
1581   pad->is_ogm = TRUE;
1582   pad->is_ogm_text = TRUE;
1583   pad->is_sparse = TRUE;
1584
1585   return TRUE;
1586 }
1587
1588 /* PCM */
1589
1590 #define OGGPCM_FMT_S8 0x00000000        /* Signed integer 8 bit */
1591 #define OGGPCM_FMT_U8 0x00000001        /* Unsigned integer 8 bit */
1592 #define OGGPCM_FMT_S16_LE 0x00000002    /* Signed integer 16 bit little endian */
1593 #define OGGPCM_FMT_S16_BE 0x00000003    /* Signed integer 16 bit big endian */
1594 #define OGGPCM_FMT_S24_LE 0x00000004    /* Signed integer 24 bit little endian */
1595 #define OGGPCM_FMT_S24_BE 0x00000005    /* Signed integer 24 bit big endian */
1596 #define OGGPCM_FMT_S32_LE 0x00000006    /* Signed integer 32 bit little endian */
1597 #define OGGPCM_FMT_S32_BE 0x00000007    /* Signed integer 32 bit big endian */
1598
1599 #define OGGPCM_FMT_ULAW 0x00000010      /* G.711 u-law encoding (8 bit) */
1600 #define OGGPCM_FMT_ALAW 0x00000011      /* G.711 A-law encoding (8 bit) */
1601
1602 #define OGGPCM_FMT_FLT32_LE 0x00000020  /* IEEE Float [-1,1] 32 bit little endian */
1603 #define OGGPCM_FMT_FLT32_BE 0x00000021  /* IEEE Float [-1,1] 32 bit big endian */
1604 #define OGGPCM_FMT_FLT64_LE 0x00000022  /* IEEE Float [-1,1] 64 bit little endian */
1605 #define OGGPCM_FMT_FLT64_BE 0x00000023  /* IEEE Float [-1,1] 64 bit big endian */
1606
1607
1608 static gboolean
1609 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1610 {
1611   guint8 *data = packet->packet;
1612   int format;
1613   int channels;
1614   GstCaps *caps;
1615
1616   pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1617   pad->granulerate_d = 1;
1618   GST_LOG ("sample rate: %d", pad->granulerate_n);
1619
1620   format = GST_READ_UINT32_LE (data + 12);
1621   channels = GST_READ_UINT8 (data + 21);
1622
1623   pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1624
1625   if (pad->granulerate_n == 0)
1626     return FALSE;
1627
1628   switch (format) {
1629     case OGGPCM_FMT_S8:
1630       caps = gst_caps_new_simple ("audio/x-raw",
1631           "format", G_TYPE_STRING, "S8", NULL);
1632       break;
1633     case OGGPCM_FMT_U8:
1634       caps = gst_caps_new_simple ("audio/x-raw",
1635           "format", G_TYPE_STRING, "U8", NULL);
1636       break;
1637     case OGGPCM_FMT_S16_LE:
1638       caps = gst_caps_new_simple ("audio/x-raw",
1639           "format", G_TYPE_STRING, "S16LE", NULL);
1640       break;
1641     case OGGPCM_FMT_S16_BE:
1642       caps = gst_caps_new_simple ("audio/x-raw",
1643           "format", G_TYPE_STRING, "S16BE", NULL);
1644       break;
1645     case OGGPCM_FMT_S24_LE:
1646       caps = gst_caps_new_simple ("audio/x-raw",
1647           "format", G_TYPE_STRING, "S24LE", NULL);
1648       break;
1649     case OGGPCM_FMT_S24_BE:
1650       caps = gst_caps_new_simple ("audio/x-raw",
1651           "format", G_TYPE_STRING, "S24BE", NULL);
1652       break;
1653     case OGGPCM_FMT_S32_LE:
1654       caps = gst_caps_new_simple ("audio/x-raw",
1655           "format", G_TYPE_STRING, "S32LE", NULL);
1656       break;
1657     case OGGPCM_FMT_S32_BE:
1658       caps = gst_caps_new_simple ("audio/x-raw",
1659           "format", G_TYPE_STRING, "S32BE", NULL);
1660       break;
1661     case OGGPCM_FMT_ULAW:
1662       caps = gst_caps_new_empty_simple ("audio/x-mulaw");
1663       break;
1664     case OGGPCM_FMT_ALAW:
1665       caps = gst_caps_new_empty_simple ("audio/x-alaw");
1666       break;
1667     case OGGPCM_FMT_FLT32_LE:
1668       caps = gst_caps_new_simple ("audio/x-raw",
1669           "format", G_TYPE_STRING, "F32LE", NULL);
1670       break;
1671     case OGGPCM_FMT_FLT32_BE:
1672       caps = gst_caps_new_simple ("audio/x-raw",
1673           "format", G_TYPE_STRING, "F32BE", NULL);
1674       break;
1675     case OGGPCM_FMT_FLT64_LE:
1676       caps = gst_caps_new_simple ("audio/x-raw",
1677           "format", G_TYPE_STRING, "F64LE", NULL);
1678       break;
1679     case OGGPCM_FMT_FLT64_BE:
1680       caps = gst_caps_new_simple ("audio/x-raw",
1681           "format", G_TYPE_STRING, "F64BE", NULL);
1682       break;
1683     default:
1684       return FALSE;
1685   }
1686
1687   gst_caps_set_simple (caps,
1688       "layout", G_TYPE_STRING, "interleaved",
1689       "rate", G_TYPE_INT, pad->granulerate_n,
1690       "channels", G_TYPE_INT, channels, NULL);
1691   pad->caps = caps;
1692
1693   return TRUE;
1694 }
1695
1696 /* cmml */
1697
1698 static gboolean
1699 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1700 {
1701   guint8 *data = packet->packet;
1702
1703   pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1704   pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1705   pad->granuleshift = data[28];
1706   GST_LOG ("sample rate: %d", pad->granulerate_n);
1707
1708   pad->n_header_packets = 3;
1709
1710   if (pad->granulerate_n == 0)
1711     return FALSE;
1712
1713   data += 4 + (4 + 4 + 4);
1714   GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1715   GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1716
1717   pad->caps = gst_caps_new_empty_simple ("text/x-cmml");
1718   pad->always_flush_page = TRUE;
1719   pad->is_sparse = TRUE;
1720
1721   return TRUE;
1722 }
1723
1724 /* celt */
1725
1726 static gboolean
1727 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1728 {
1729   guint8 *data = packet->packet;
1730
1731   pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1732   pad->granulerate_d = 1;
1733   pad->granuleshift = 0;
1734   GST_LOG ("sample rate: %d", pad->granulerate_n);
1735
1736   pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1737   pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1738
1739   if (pad->granulerate_n == 0)
1740     return FALSE;
1741
1742   pad->caps = gst_caps_new_simple ("audio/x-celt",
1743       "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1744
1745   return TRUE;
1746 }
1747
1748 /* kate */
1749
1750 static gboolean
1751 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1752 {
1753   guint8 *data = packet->packet;
1754   const char *category;
1755
1756   if (packet->bytes < 64)
1757     return FALSE;
1758
1759   pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1760   pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1761   pad->granuleshift = GST_READ_UINT8 (data + 15);
1762   GST_LOG ("sample rate: %d", pad->granulerate_n);
1763
1764   pad->n_header_packets = GST_READ_UINT8 (data + 11);
1765   GST_LOG ("kate header packets: %d", pad->n_header_packets);
1766
1767   if (pad->granulerate_n == 0)
1768     return FALSE;
1769
1770   category = (const char *) data + 48;
1771   if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1772       strcmp (category, "spu-subtitles") == 0 ||
1773       strcmp (category, "K-SPU") == 0) {
1774     pad->caps = gst_caps_new_empty_simple ("subtitle/x-kate");
1775   } else {
1776     pad->caps = gst_caps_new_empty_simple ("application/x-kate");
1777   }
1778
1779   pad->is_sparse = TRUE;
1780   pad->always_flush_page = TRUE;
1781
1782   return TRUE;
1783 }
1784
1785 static gint64
1786 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1787 {
1788   gint64 duration;
1789
1790   if (packet->bytes < 1)
1791     return 0;
1792
1793   switch (packet->packet[0]) {
1794     case 0x00:                 /* text data */
1795       if (packet->bytes < 1 + 8 * 2) {
1796         duration = 0;
1797       } else {
1798         duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
1799         if (duration < 0)
1800           duration = 0;
1801       }
1802       break;
1803     default:
1804       duration = GST_CLOCK_TIME_NONE;
1805       break;
1806   }
1807
1808   return duration;
1809 }
1810
1811 static void
1812 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
1813 {
1814   GstTagList *list = NULL;
1815
1816   if (packet->bytes <= 0)
1817     return;
1818
1819   switch (packet->packet[0]) {
1820     case 0x80:{
1821       const gchar *canonical;
1822       char language[16];
1823
1824       if (packet->bytes < 64) {
1825         GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
1826         break;
1827       }
1828
1829       /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
1830       memcpy (language, packet->packet + 32, 16);
1831       language[15] = 0;
1832
1833       /* language is an ISO 639-1 code or RFC 3066 language code, we
1834        * truncate to ISO 639-1 */
1835       g_strdelimit (language, NULL, '\0');
1836       canonical = gst_tag_get_language_code_iso_639_1 (language);
1837       if (canonical) {
1838         list = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, canonical, NULL);
1839       } else {
1840         GST_WARNING ("Unknown or invalid language code %s, ignored", language);
1841       }
1842       break;
1843     }
1844     case 0x81:
1845       tag_list_from_vorbiscomment_packet (packet,
1846           (const guint8 *) "\201kate\0\0\0\0", 9, &list);
1847       break;
1848     default:
1849       break;
1850   }
1851
1852   if (list) {
1853     if (pad->taglist) {
1854       /* ensure the comment packet cannot override the category/language
1855          from the identification header */
1856       gst_tag_list_insert (pad->taglist, list, GST_TAG_MERGE_KEEP_ALL);
1857       gst_tag_list_free (list);
1858     } else
1859       pad->taglist = list;
1860   }
1861 }
1862
1863 /* opus */
1864
1865 static gboolean
1866 setup_opus_mapper (GstOggStream * pad, ogg_packet * packet)
1867 {
1868   if (packet->bytes < 19)
1869     return FALSE;
1870
1871   pad->granulerate_n = 48000;
1872   pad->granulerate_d = 1;
1873   pad->granuleshift = 0;
1874   pad->n_header_packets = 2;
1875
1876   /* pre-skip is in samples at 48000 Hz, which matches granule one for one */
1877   pad->granule_offset = -GST_READ_UINT16_LE (packet->packet + 10);
1878   GST_INFO ("Opus has a pre-skip of %" G_GINT64_FORMAT " samples",
1879       -pad->granule_offset);
1880
1881   pad->caps = gst_caps_new_empty_simple ("audio/x-opus");
1882
1883   return TRUE;
1884 }
1885
1886 static gboolean
1887 is_header_opus (GstOggStream * pad, ogg_packet * packet)
1888 {
1889   return packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4);
1890 }
1891
1892 static gint64
1893 packet_duration_opus (GstOggStream * pad, ogg_packet * packet)
1894 {
1895   static const guint64 durations[32] = {
1896     480, 960, 1920, 2880,       /* Silk NB */
1897     480, 960, 1920, 2880,       /* Silk MB */
1898     480, 960, 1920, 2880,       /* Silk WB */
1899     480, 960,                   /* Hybrid SWB */
1900     480, 960,                   /* Hybrid FB */
1901     120, 240, 480, 960,         /* CELT NB */
1902     120, 240, 480, 960,         /* CELT NB */
1903     120, 240, 480, 960,         /* CELT NB */
1904     120, 240, 480, 960,         /* CELT NB */
1905   };
1906
1907   gint64 duration;
1908   gint64 frame_duration;
1909   gint nframes = 0;
1910   guint8 toc;
1911
1912   if (packet->bytes < 1)
1913     return 0;
1914
1915   /* headers */
1916   if (is_header_opus (pad, packet))
1917     return 0;
1918
1919   toc = packet->packet[0];
1920
1921   frame_duration = durations[toc >> 3];
1922   switch (toc & 3) {
1923     case 0:
1924       nframes = 1;
1925       break;
1926     case 1:
1927       nframes = 2;
1928       break;
1929     case 2:
1930       nframes = 2;
1931       break;
1932     case 3:
1933       if (packet->bytes < 2) {
1934         GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
1935         return 0;
1936       }
1937       nframes = packet->packet[1] & 63;
1938       break;
1939   }
1940
1941   duration = nframes * frame_duration;
1942   if (duration > 5760) {
1943     GST_WARNING ("Opus packet duration > 120 ms, invalid");
1944     return 0;
1945   }
1946   GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
1947       frame_duration / 48.f, nframes, duration / 48.f);
1948   return duration;
1949 }
1950
1951 static void
1952 extract_tags_opus (GstOggStream * pad, ogg_packet * packet)
1953 {
1954   if (packet->bytes >= 8 && memcmp (packet->packet, "OpusTags", 8) == 0) {
1955     tag_list_from_vorbiscomment_packet (packet,
1956         (const guint8 *) "OpusTags", 8, &pad->taglist);
1957   }
1958 }
1959
1960
1961 /* *INDENT-OFF* */
1962 /* indent hates our freedoms */
1963 const GstOggMap mappers[] = {
1964   {
1965     "\200theora", 7, 42,
1966     "video/x-theora",
1967     setup_theora_mapper,
1968     granulepos_to_granule_theora,
1969     granule_to_granulepos_default,
1970     is_granulepos_keyframe_theora,
1971     is_packet_keyframe_theora,
1972     is_header_theora,
1973     packet_duration_constant,
1974     NULL,
1975     extract_tags_theora
1976   },
1977   {
1978     "\001vorbis", 7, 22,
1979     "audio/x-vorbis",
1980     setup_vorbis_mapper,
1981     granulepos_to_granule_default,
1982     granule_to_granulepos_default,
1983     is_granulepos_keyframe_true,
1984     is_packet_keyframe_true,
1985     is_header_vorbis,
1986     packet_duration_vorbis,
1987     NULL,
1988     extract_tags_vorbis
1989   },
1990   {
1991     "Speex", 5, 80,
1992     "audio/x-speex",
1993     setup_speex_mapper,
1994     granulepos_to_granule_default,
1995     granule_to_granulepos_default,
1996     is_granulepos_keyframe_true,
1997     is_packet_keyframe_true,
1998     is_header_count,
1999     packet_duration_constant,
2000     NULL,
2001     extract_tags_count
2002   },
2003   {
2004     "PCM     ", 8, 0,
2005     "audio/x-raw",
2006     setup_pcm_mapper,
2007     NULL,
2008     NULL,
2009     NULL,
2010     NULL,
2011     is_header_count,
2012     NULL,
2013     NULL,
2014     NULL
2015   },
2016   {
2017     "CMML\0\0\0\0", 8, 0,
2018     "text/x-cmml",
2019     setup_cmml_mapper,
2020     NULL,
2021     NULL,
2022     NULL,
2023     NULL,
2024     is_header_count,
2025     NULL,
2026     NULL,
2027     NULL
2028   },
2029   {
2030     "Annodex", 7, 0,
2031     "application/x-annodex",
2032     setup_fishead_mapper,
2033     granulepos_to_granule_default,
2034     granule_to_granulepos_default,
2035     NULL,
2036     NULL,
2037     is_header_count,
2038     NULL,
2039     NULL,
2040     NULL
2041   },
2042   {
2043     "fishead", 7, 64,
2044     "application/octet-stream",
2045     setup_fishead_mapper,
2046     NULL,
2047     NULL,
2048     NULL,
2049     NULL,
2050     is_header_true,
2051     NULL,
2052     NULL,
2053     NULL
2054   },
2055   {
2056     "fLaC", 4, 0,
2057     "audio/x-flac",
2058     setup_fLaC_mapper,
2059     granulepos_to_granule_default,
2060     granule_to_granulepos_default,
2061     is_granulepos_keyframe_true,
2062     is_packet_keyframe_true,
2063     is_header_fLaC,
2064     packet_duration_flac,
2065     NULL,
2066     NULL
2067   },
2068   {
2069     "\177FLAC", 5, 36,
2070     "audio/x-flac",
2071     setup_flac_mapper,
2072     granulepos_to_granule_default,
2073     granule_to_granulepos_default,
2074     is_granulepos_keyframe_true,
2075     is_packet_keyframe_true,
2076     is_header_flac,
2077     packet_duration_flac,
2078     NULL,
2079     extract_tags_flac
2080   },
2081   {
2082     "AnxData", 7, 0,
2083     "application/octet-stream",
2084     NULL,
2085     NULL,
2086     NULL,
2087     NULL,
2088     NULL,
2089     NULL,
2090     NULL,
2091     NULL
2092   },
2093   {
2094     "CELT    ", 8, 0,
2095     "audio/x-celt",
2096     setup_celt_mapper,
2097     granulepos_to_granule_default,
2098     granule_to_granulepos_default,
2099     NULL,
2100     NULL,
2101     is_header_count,
2102     packet_duration_constant,
2103     NULL,
2104     extract_tags_count
2105   },
2106   {
2107     "\200kate\0\0\0", 8, 0,
2108     "text/x-kate",
2109     setup_kate_mapper,
2110     granulepos_to_granule_default,
2111     granule_to_granulepos_default,
2112     NULL,
2113     NULL,
2114     is_header_count,
2115     packet_duration_kate,
2116     NULL,
2117     extract_tags_kate
2118   },
2119   {
2120     "BBCD\0", 5, 13,
2121     "video/x-dirac",
2122     setup_dirac_mapper,
2123     granulepos_to_granule_dirac,
2124     granule_to_granulepos_dirac,
2125     is_keyframe_dirac,
2126     NULL,
2127     is_header_count,
2128     packet_duration_constant,
2129     granulepos_to_key_granule_dirac,
2130     NULL
2131   },
2132   {
2133     "OVP80\1\1", 7, 4,
2134     "video/x-vp8",
2135     setup_vp8_mapper,
2136     granulepos_to_granule_vp8,
2137     granule_to_granulepos_vp8,
2138     is_keyframe_vp8,
2139     NULL,
2140     is_header_vp8,
2141     packet_duration_vp8,
2142     granulepos_to_key_granule_vp8,
2143     extract_tags_vp8
2144   },
2145   {
2146     "OpusHead", 8, 0,
2147     "audio/x-opus",
2148     setup_opus_mapper,
2149     granulepos_to_granule_default,
2150     granule_to_granulepos_default,
2151     NULL,
2152     NULL,
2153     is_header_opus,
2154     packet_duration_opus,
2155     NULL,
2156     extract_tags_opus
2157   },
2158   {
2159     "\001audio\0\0\0", 9, 53,
2160     "application/x-ogm-audio",
2161     setup_ogmaudio_mapper,
2162     granulepos_to_granule_default,
2163     granule_to_granulepos_default,
2164     is_granulepos_keyframe_true,
2165     is_packet_keyframe_true,
2166     is_header_ogm,
2167     packet_duration_ogm,
2168     NULL,
2169     NULL
2170   },
2171   {
2172     "\001video\0\0\0", 9, 53,
2173     "application/x-ogm-video",
2174     setup_ogmvideo_mapper,
2175     granulepos_to_granule_default,
2176     granule_to_granulepos_default,
2177     NULL,
2178     NULL,
2179     is_header_ogm,
2180     packet_duration_constant,
2181     NULL,
2182     NULL
2183   },
2184   {
2185     "\001text\0\0\0", 9, 9,
2186     "application/x-ogm-text",
2187     setup_ogmtext_mapper,
2188     granulepos_to_granule_default,
2189     granule_to_granulepos_default,
2190     is_granulepos_keyframe_true,
2191     is_packet_keyframe_true,
2192     is_header_ogm,
2193     packet_duration_ogm,
2194     NULL,
2195     extract_tags_ogm
2196   }
2197 };
2198 /* *INDENT-ON* */
2199
2200 gboolean
2201 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2202 {
2203   int i;
2204   gboolean ret;
2205
2206   for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
2207     if (packet->bytes >= mappers[i].min_packet_size &&
2208         packet->bytes >= mappers[i].id_length &&
2209         memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
2210
2211       GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2212
2213       if (mappers[i].setup_func)
2214         ret = mappers[i].setup_func (pad, packet);
2215       else
2216         continue;
2217
2218       if (ret) {
2219         GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2220         pad->map = i;
2221         return TRUE;
2222       } else {
2223         GST_WARNING ("mapper '%s' did not accept setup header",
2224             mappers[i].media_type);
2225       }
2226     }
2227   }
2228
2229   return FALSE;
2230 }
2231
2232 gboolean
2233 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2234     const GstCaps * caps)
2235 {
2236   GstBuffer *buf;
2237   const GstStructure *structure;
2238   const GValue *streamheader;
2239   const GValue *first_element;
2240   ogg_packet packet;
2241   GstMapInfo map;
2242   gboolean ret;
2243
2244   GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2245
2246   if (caps == NULL)
2247     return FALSE;
2248
2249   structure = gst_caps_get_structure (caps, 0);
2250   streamheader = gst_structure_get_value (structure, "streamheader");
2251
2252   if (streamheader == NULL) {
2253     GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2254     return FALSE;
2255   }
2256
2257   if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2258     GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2259     return FALSE;
2260   }
2261
2262   if (gst_value_array_get_size (streamheader) == 0) {
2263     GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2264     return FALSE;
2265   }
2266
2267   first_element = gst_value_array_get_value (streamheader, 0);
2268
2269   if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2270     GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2271     return FALSE;
2272   }
2273
2274   buf = gst_value_get_buffer (first_element);
2275   if (buf == NULL) {
2276     GST_ERROR ("no first streamheader buffer");
2277     return FALSE;
2278   }
2279
2280   if (!gst_buffer_map (buf, &map, GST_MAP_READ) || map.size == 0) {
2281     GST_ERROR ("invalid first streamheader buffer");
2282     return FALSE;
2283   }
2284
2285   GST_MEMDUMP ("streamheader", map.data, map.size);
2286
2287   packet.packet = map.data;
2288   packet.bytes = map.size;
2289
2290   GST_INFO ("Found headers on caps, using those to determine type");
2291   ret = gst_ogg_stream_setup_map (pad, &packet);
2292
2293   gst_buffer_unmap (buf, &map);
2294
2295   return ret;
2296 }