oggstream: initialize variable
[platform/upstream/gst-plugins-base.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 (*GstOggMapIsKeyFrameFunc) (GstOggStream * pad,
51     gint64 granulepos);
52
53 /* returns TRUE if the given packet is a stream header packet */
54 typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
55     ogg_packet * packet);
56 typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
57     ogg_packet * packet);
58 typedef void (*GstOggMapExtractTagsFunc) (GstOggStream * pad,
59     ogg_packet * packet);
60
61 typedef gint64 (*GstOggMapGranuleposToKeyGranuleFunc) (GstOggStream * pad,
62     gint64 granulepos);
63
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
67
68 struct _GstOggMap
69 {
70   const gchar *id;
71   int id_length;
72   int min_packet_size;
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;
82 };
83
84 extern const GstOggMap mappers[];
85
86 GstClockTime
87 gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet)
88 {
89   int duration;
90
91   if (packet->granulepos == -1) {
92     return GST_CLOCK_TIME_NONE;
93   }
94
95   duration = gst_ogg_stream_get_packet_duration (pad, packet);
96   if (duration == -1) {
97     return GST_CLOCK_TIME_NONE;
98   }
99
100   return gst_ogg_stream_granule_to_time (pad,
101       gst_ogg_stream_granulepos_to_granule (pad,
102           packet->granulepos) - duration);
103 }
104
105 GstClockTime
106 gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad,
107     gint64 granulepos)
108 {
109   if (pad->frame_size == 0)
110     return GST_CLOCK_TIME_NONE;
111
112   return gst_ogg_stream_granule_to_time (pad,
113       gst_ogg_stream_granulepos_to_granule (pad, granulepos));
114 }
115
116 GstClockTime
117 gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad,
118     gint64 granulepos)
119 {
120   return gst_ogg_stream_granule_to_time (pad,
121       gst_ogg_stream_granulepos_to_granule (pad, granulepos));
122 }
123
124 GstClockTime
125 gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule)
126 {
127   if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
128     return 0;
129
130   granule += pad->granule_offset;
131   if (granule < 0)
132     return 0;
133
134   return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
135       pad->granulerate_n);
136 }
137
138 gint64
139 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
140 {
141   if (granulepos == -1 || granulepos == 0) {
142     return granulepos;
143   }
144
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));
148     return -1;
149   }
150
151   return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
152 }
153
154 gint64
155 gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos)
156 {
157   if (mappers[pad->map].granulepos_to_key_granule_func)
158     return mappers[pad->map].granulepos_to_key_granule_func (pad, granulepos);
159
160   if (granulepos == -1 || granulepos == 0) {
161     return granulepos;
162   }
163
164   return granulepos >> pad->granuleshift;
165 }
166
167 gint64
168 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
169     gint64 keyframe_granule)
170 {
171   if (granule == -1 || granule == 0) {
172     return granule;
173   }
174
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));
178     return -1;
179   }
180
181   return mappers[pad->map].granule_to_granulepos_func (pad, granule,
182       keyframe_granule);
183 }
184
185 gboolean
186 gst_ogg_stream_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos)
187 {
188   if (granulepos == -1) {
189     return FALSE;
190   }
191
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));
195     return FALSE;
196   }
197
198   return mappers[pad->map].is_key_frame_func (pad, granulepos);
199 }
200
201 gboolean
202 gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
203 {
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));
207     return FALSE;
208   }
209
210   return mappers[pad->map].is_header_func (pad, packet);
211 }
212
213 gint64
214 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
215 {
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));
219     return -1;
220   }
221
222   return mappers[pad->map].packet_duration_func (pad, packet);
223 }
224
225
226 void
227 gst_ogg_stream_extract_tags (GstOggStream * pad, ogg_packet * packet)
228 {
229   if (mappers[pad->map].extract_tags_func == NULL) {
230     GST_DEBUG ("No tag extraction");
231     return;
232   }
233
234   mappers[pad->map].extract_tags_func (pad, packet);
235 }
236
237 const char *
238 gst_ogg_stream_get_media_type (GstOggStream * pad)
239 {
240   const GstCaps *caps = pad->caps;
241   const GstStructure *structure;
242   if (!caps)
243     return NULL;
244   structure = gst_caps_get_structure (caps, 0);
245   if (!structure)
246     return NULL;
247   return gst_structure_get_name (structure);
248 }
249
250 /* some generic functions */
251
252 static gboolean
253 is_keyframe_true (GstOggStream * pad, gint64 granulepos)
254 {
255   return TRUE;
256 }
257
258 static gint64
259 granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos)
260 {
261   gint64 keyindex, keyoffset;
262
263   if (pad->granuleshift != 0) {
264     keyindex = granulepos >> pad->granuleshift;
265     keyoffset = granulepos - (keyindex << pad->granuleshift);
266     return keyindex + keyoffset;
267   } else {
268     return granulepos;
269   }
270 }
271
272
273 static gint64
274 granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
275     gint64 keyframe_granule)
276 {
277   gint64 keyoffset;
278
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;
287   } else {
288     return granule;
289   }
290 }
291
292 #ifdef unused
293 static gboolean
294 is_header_unknown (GstOggStream * pad, ogg_packet * packet)
295 {
296   GST_WARNING ("don't know how to detect header");
297   return FALSE;
298 }
299 #endif
300
301 static gboolean
302 is_header_true (GstOggStream * pad, ogg_packet * packet)
303 {
304   return TRUE;
305 }
306
307 static gboolean
308 is_header_count (GstOggStream * pad, ogg_packet * packet)
309 {
310   if (pad->n_header_packets_seen < pad->n_header_packets) {
311     return TRUE;
312   }
313   return FALSE;
314 }
315
316 static gint64
317 packet_duration_constant (GstOggStream * pad, ogg_packet * packet)
318 {
319   return pad->frame_size;
320 }
321
322 /* helper: extracts tags from vorbis comment ogg packet.
323  * Returns result in *tags after free'ing existing *tags (if any) */
324 static gboolean
325 tag_list_from_vorbiscomment_packet (ogg_packet * packet,
326     const guint8 * id_data, const guint id_data_length, GstTagList ** tags)
327 {
328   GstBuffer *buf = NULL;
329   gchar *encoder = NULL;
330   GstTagList *list;
331   gboolean ret = TRUE;
332
333   g_return_val_if_fail (tags != NULL, FALSE);
334
335   buf = gst_buffer_new ();
336   GST_BUFFER_DATA (buf) = (guint8 *) packet->packet;
337   GST_BUFFER_SIZE (buf) = packet->bytes;
338
339   list = gst_tag_list_from_vorbiscomment_buffer (buf, id_data, id_data_length,
340       &encoder);
341
342   if (!list) {
343     GST_WARNING ("failed to decode vorbis comments");
344     ret = FALSE;
345     goto exit;
346   }
347
348   if (encoder) {
349     if (encoder[0])
350       gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
351           NULL);
352     g_free (encoder);
353   }
354
355 exit:
356   if (*tags)
357     gst_tag_list_free (*tags);
358   *tags = list;
359
360   gst_buffer_unref (buf);
361
362   return ret;
363 }
364
365 /* theora */
366
367 static gboolean
368 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
369 {
370   guint8 *data = packet->packet;
371   guint w, h, par_d, par_n;
372   guint8 vmaj, vmin, vrev;
373
374   vmaj = data[7];
375   vmin = data[8];
376   vrev = data[9];
377
378   w = GST_READ_UINT24_BE (data + 14) & 0xFFFFFF;
379   h = GST_READ_UINT24_BE (data + 17) & 0xFFFFFF;
380
381   pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
382   pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
383
384   par_n = GST_READ_UINT24_BE (data + 30);
385   par_d = GST_READ_UINT24_BE (data + 33);
386
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);
389
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);
393
394   pad->is_video = TRUE;
395   pad->n_header_packets = 3;
396   pad->frame_size = 1;
397
398   pad->bitrate = GST_READ_UINT24_BE (data + 37);
399   GST_LOG ("bit rate: %d", pad->bitrate);
400
401   if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
402     GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
403     return FALSE;
404   }
405
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;
411
412   pad->caps = gst_caps_new_simple ("video/x-theora", NULL);
413
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);
417   }
418
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)
421     par_n = par_d = 1;
422
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);
427
428   return TRUE;
429 }
430
431 static gint64
432 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
433 {
434   gint64 keyindex, keyoffset;
435
436   if (pad->granuleshift != 0) {
437     keyindex = granulepos >> pad->granuleshift;
438     keyoffset = granulepos - (keyindex << pad->granuleshift);
439     if (pad->theora_has_zero_keyoffset) {
440       keyoffset++;
441     }
442     return keyindex + keyoffset;
443   } else {
444     return granulepos;
445   }
446 }
447
448 static gboolean
449 is_keyframe_theora (GstOggStream * pad, gint64 granulepos)
450 {
451   gint64 frame_mask;
452
453   if (granulepos == (gint64) - 1)
454     return FALSE;
455
456   frame_mask = (1 << pad->granuleshift) - 1;
457
458   return ((granulepos & frame_mask) == 0);
459 }
460
461 static gboolean
462 is_header_theora (GstOggStream * pad, ogg_packet * packet)
463 {
464   return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
465 }
466
467 static void
468 extract_tags_theora (GstOggStream * pad, ogg_packet * packet)
469 {
470   if (packet->bytes > 0 && packet->packet[0] == 0x81) {
471     tag_list_from_vorbiscomment_packet (packet,
472         (const guint8 *) "\201theora", 7, &pad->taglist);
473
474     if (!pad->taglist)
475       pad->taglist = gst_tag_list_new ();
476
477     if (pad->bitrate)
478       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
479           GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
480   }
481 }
482
483 /* dirac */
484
485 static gboolean
486 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
487 {
488   int ret;
489   DiracSequenceHeader header;
490
491   ret = dirac_sequence_header_parse (&header, packet->packet + 13,
492       packet->bytes - 13);
493   if (ret == 0) {
494     GST_DEBUG ("Failed to parse Dirac sequence header");
495     return FALSE;
496   }
497
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;
504   pad->frame_size = 2;
505
506   if (header.interlaced_coding != 0) {
507     GST_DEBUG ("non-progressive Dirac coding not implemented");
508     return FALSE;
509   }
510
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);
519
520   return TRUE;
521 }
522
523 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
524 static gboolean
525 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
526 {
527   int dist_h;
528   int dist_l;
529   int dist;
530
531   if (granulepos == -1)
532     return -1;
533
534   dist_h = (granulepos >> 22) & 0xff;
535   dist_l = granulepos & 0xff;
536   dist = (dist_h << 8) | dist_l;
537
538   return (dist == 0);
539 }
540
541 static gint64
542 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
543 {
544   gint64 pt;
545   int delay;
546   gint64 dt;
547
548   pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
549   delay = (gp >> 9) & 0x1fff;
550   dt = pt - delay;
551
552   GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
553
554   return dt + 4;
555 }
556
557 static gint64
558 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
559     gint64 keyframe_granule)
560 {
561   /* This conversion requires knowing more details about the Dirac
562    * stream. */
563   return -1;
564 }
565
566 static gint64
567 granulepos_to_key_granule_dirac (GstOggStream * pad, gint64 gp)
568 {
569   gint64 pt;
570   int dist_h;
571   int dist_l;
572   int dist;
573   int delay;
574   gint64 dt;
575
576   if (gp == -1 || gp == 0)
577     return gp;
578
579   pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
580   dist_h = (gp >> 22) & 0xff;
581   dist_l = gp & 0xff;
582   dist = (dist_h << 8) | dist_l;
583   delay = (gp >> 9) & 0x1fff;
584   dt = pt - delay;
585
586   return dt - 2 * dist + 4;
587 }
588
589 /* VP8 */
590
591 static gboolean
592 setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet)
593 {
594   gint width, height, par_n, par_d, fps_n, fps_d;
595
596   if (packet->bytes < 26) {
597     GST_DEBUG ("Failed to parse VP8 BOS page");
598     return FALSE;
599   }
600
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);
607
608   pad->is_video = TRUE;
609   pad->is_vp8 = TRUE;
610   pad->granulerate_n = fps_n;
611   pad->granulerate_d = fps_d;
612   pad->n_header_packets = 2;
613   pad->frame_size = 1;
614
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);
620
621   return TRUE;
622 }
623
624 static gboolean
625 is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos)
626 {
627   guint64 gpos = granulepos;
628
629   if (granulepos == -1)
630     return FALSE;
631
632   /* Get rid of flags */
633   gpos >>= 3;
634
635   return ((gpos & 0x07ffffff) == 0);
636 }
637
638 static gint64
639 granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos)
640 {
641   guint64 gp = (guint64) gpos;
642   guint32 pt;
643   guint32 dist;
644
645   pt = (gp >> 32);
646   dist = (gp >> 3) & 0x07ffffff;
647
648   GST_DEBUG ("pt %u, dist %u", pt, dist);
649
650   return pt;
651 }
652
653 static gint64
654 granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule,
655     gint64 keyframe_granule)
656 {
657   /* FIXME: This requires to look into the content of the packets
658    * because the simple granule counter doesn't know about invisible
659    * frames...
660    */
661   return -1;
662 }
663
664 /* Check if this packet contains an invisible frame or not */
665 static gint64
666 packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet)
667 {
668   guint32 hdr;
669
670   if (packet->bytes < 3)
671     return 0;
672
673   hdr = GST_READ_UINT24_LE (packet->packet);
674
675   return (((hdr >> 4) & 1) != 0) ? 1 : 0;
676 }
677
678 static gint64
679 granulepos_to_key_granule_vp8 (GstOggStream * pad, gint64 granulepos)
680 {
681   guint64 gp = granulepos;
682   guint64 pts = (gp >> 32);
683   guint32 dist = (gp >> 3) & 0x07ffffff;
684
685   if (granulepos == -1 || granulepos == 0)
686     return granulepos;
687
688   if (dist > pts)
689     return 0;
690
691   return pts - dist;
692 }
693
694 static gboolean
695 is_header_vp8 (GstOggStream * pad, ogg_packet * packet)
696 {
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)
700     return TRUE;
701   return FALSE;
702 }
703
704 static void
705 extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet)
706 {
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);
710   }
711 }
712
713 /* vorbis */
714
715 static gboolean
716 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
717 {
718   guint8 *data = packet->packet;
719   guint chans;
720
721   data += 1 + 6;
722   pad->version = GST_READ_UINT32_LE (data);
723   data += 4;
724   chans = GST_READ_UINT8 (data);
725   data += 1;
726   pad->granulerate_n = GST_READ_UINT32_LE (data);
727   pad->granulerate_d = 1;
728   pad->granuleshift = 0;
729   pad->preroll = 2;
730   pad->last_size = 0;
731   GST_LOG ("sample rate: %d", pad->granulerate_n);
732
733   data += 4;
734   pad->bitrate_upper = GST_READ_UINT32_LE (data);
735   data += 4;
736   pad->bitrate_nominal = GST_READ_UINT32_LE (data);
737   data += 4;
738   pad->bitrate_lower = GST_READ_UINT32_LE (data);
739
740   if (pad->bitrate_nominal > 0)
741     pad->bitrate = pad->bitrate_nominal;
742
743   if (pad->bitrate_upper > 0 && !pad->bitrate)
744     pad->bitrate = pad->bitrate_upper;
745
746   if (pad->bitrate_lower > 0 && !pad->bitrate)
747     pad->bitrate = pad->bitrate_lower;
748
749   GST_LOG ("bit rate: %d", pad->bitrate);
750
751   pad->n_header_packets = 3;
752
753   if (pad->granulerate_n == 0)
754     return FALSE;
755
756   parse_vorbis_header_packet (pad, packet);
757
758   pad->caps = gst_caps_new_simple ("audio/x-vorbis",
759       "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
760       NULL);
761
762   return TRUE;
763 }
764
765 static gboolean
766 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
767 {
768   if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
769     return FALSE;
770
771   if (packet->packet[0] == 5) {
772     parse_vorbis_setup_packet (pad, packet);
773   }
774
775   return TRUE;
776 }
777
778 static void
779 extract_tags_vorbis (GstOggStream * pad, ogg_packet * packet)
780 {
781   if (packet->bytes == 0 || (packet->packet[0] & 0x01) == 0)
782     return;
783
784   if (((guint8 *) (packet->packet))[0] == 0x03) {
785     tag_list_from_vorbiscomment_packet (packet,
786         (const guint8 *) "\003vorbis", 7, &pad->taglist);
787
788     if (!pad->taglist)
789       pad->taglist = gst_tag_list_new ();
790
791     gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
792         GST_TAG_ENCODER_VERSION, pad->version, NULL);
793
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);
797
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);
801
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);
805
806     if (pad->bitrate)
807       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
808           GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
809   }
810 }
811
812 static gint64
813 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
814 {
815   int mode;
816   int size;
817   int duration;
818
819   if (packet->bytes == 0 || packet->packet[0] & 1)
820     return 0;
821
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;
824
825   if (pad->last_size == 0) {
826     duration = 0;
827   } else {
828     duration = pad->last_size / 4 + size / 4;
829   }
830   pad->last_size = size;
831
832   GST_DEBUG ("duration %d", (int) duration);
833
834   return duration;
835 }
836
837 /* speex */
838
839
840 static gboolean
841 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
842 {
843   guint8 *data = packet->packet;
844   guint chans;
845
846   data += 8 + 20 + 4 + 4;
847   pad->granulerate_n = GST_READ_UINT32_LE (data);
848   pad->granulerate_d = 1;
849   pad->granuleshift = 0;
850
851   data += 4 + 4 + 4;
852   chans = GST_READ_UINT32_LE (data);
853   data += 4;
854   pad->bitrate = GST_READ_UINT32_LE (data);
855
856   GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
857   GST_LOG ("bit rate: %d", pad->bitrate);
858
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);
862
863   if (pad->granulerate_n == 0)
864     return FALSE;
865
866   pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
867       pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
868
869   return TRUE;
870 }
871
872 static void
873 extract_tags_count (GstOggStream * pad, ogg_packet * packet)
874 {
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);
878
879     if (!pad->taglist)
880       pad->taglist = gst_tag_list_new ();
881
882     if (pad->bitrate)
883       gst_tag_list_add (pad->taglist, GST_TAG_MERGE_REPLACE,
884           GST_TAG_BITRATE, (guint) pad->bitrate, NULL);
885   }
886 }
887
888
889 /* flac */
890
891 static gboolean
892 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
893 {
894   pad->granulerate_n = 0;
895   pad->granulerate_d = 1;
896   pad->granuleshift = 0;
897
898   pad->n_header_packets = 3;
899
900   pad->caps = gst_caps_new_simple ("audio/x-flac", NULL);
901
902   return TRUE;
903 }
904
905 static gboolean
906 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
907 {
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);
911   }
912
913   if (pad->n_header_packets_seen < pad->n_header_packets) {
914     return TRUE;
915   }
916
917   return FALSE;
918 }
919
920 static gboolean
921 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
922 {
923   guint8 *data = packet->packet;
924   guint chans;
925
926   /* see http://flac.sourceforge.net/ogg_mapping.html */
927
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;
932
933   GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
934
935   pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
936
937   if (pad->granulerate_n == 0)
938     return FALSE;
939
940   pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
941       pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
942
943   return TRUE;
944 }
945
946 static gboolean
947 is_header_flac (GstOggStream * pad, ogg_packet * packet)
948 {
949   return (packet->bytes > 0 && (packet->packet[0] != 0xff));
950 }
951
952 static gint64
953 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
954 {
955   int block_size_index;
956
957   if (packet->bytes < 4)
958     return -1;
959
960   block_size_index = packet->packet[2] >> 4;
961   if (block_size_index == 1)
962     return 192;
963   if (block_size_index >= 2 && block_size_index <= 5) {
964     return 576 << (block_size_index - 2);
965   }
966   if (block_size_index >= 8) {
967     return 256 << (block_size_index - 8);
968   }
969   if (block_size_index == 6 || block_size_index == 7) {
970     guint len, bytes = (block_size_index - 6) + 1;
971     guint8 tmp;
972
973     if (packet->bytes < 4 + 1 + bytes)
974       return -1;
975     tmp = packet->packet[4];
976     /* utf-8 prefix */
977     len = 0;
978     while (tmp & 0x80) {
979       len++;
980       tmp <<= 1;
981     }
982     if (len == 2)
983       return -1;
984     if (len == 0)
985       len++;
986     if (packet->bytes < 4 + len + bytes)
987       return -1;
988     if (bytes == 1) {
989       return packet->packet[4 + len] + 1;
990     } else {
991       return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
992     }
993   }
994   return -1;
995 }
996
997 static void
998 extract_tags_flac (GstOggStream * pad, ogg_packet * packet)
999 {
1000   if (packet->bytes > 4 && ((packet->packet[0] & 0x7F) == 0x4)) {
1001     tag_list_from_vorbiscomment_packet (packet,
1002         packet->packet, 4, &pad->taglist);
1003   }
1004 }
1005
1006 /* fishead */
1007
1008 static gboolean
1009 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
1010 {
1011   guint8 *data;
1012   gint64 prestime_n, prestime_d;
1013   gint64 basetime_n, basetime_d;
1014
1015   data = packet->packet;
1016
1017   data += 8;                    /* header */
1018
1019   pad->skeleton_major = GST_READ_UINT16_LE (data);
1020   data += 2;
1021   pad->skeleton_minor = GST_READ_UINT16_LE (data);
1022   data += 2;
1023
1024   prestime_n = (gint64) GST_READ_UINT64_LE (data);
1025   data += 8;
1026   prestime_d = (gint64) GST_READ_UINT64_LE (data);
1027   data += 8;
1028   basetime_n = (gint64) GST_READ_UINT64_LE (data);
1029   data += 8;
1030   basetime_d = (gint64) GST_READ_UINT64_LE (data);
1031   data += 8;
1032
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);
1036   else
1037     pad->basetime = -1;
1038
1039   if (prestime_d != 0)
1040     pad->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
1041   else
1042     pad->prestime = -1;
1043
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;
1051
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);
1058
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);
1065
1066     if (firstsampletime_d > 0)
1067       firstsampletime = gst_util_uint64_scale (GST_SECOND,
1068           firstsampletime_n, firstsampletime_d);
1069     else
1070       firstsampletime = 0;
1071
1072     if (lastsampletime_d > 0)
1073       lastsampletime = gst_util_uint64_scale (GST_SECOND,
1074           lastsampletime_n, lastsampletime_d);
1075     else
1076       lastsampletime = 0;
1077
1078     if (lastsampletime > firstsampletime)
1079       pad->total_time = lastsampletime - firstsampletime;
1080     else
1081       pad->total_time = -1;
1082
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;
1088
1089     segment_length = GST_READ_UINT64_LE (data + 64);
1090     content_offset = GST_READ_UINT64_LE (data + 72);
1091
1092     GST_INFO ("segment length %" G_GUINT64_FORMAT, segment_length);
1093     GST_INFO ("content offset %" G_GUINT64_FORMAT, content_offset);
1094   } else {
1095     pad->total_time = -1;
1096   }
1097
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));
1102
1103   pad->is_skeleton = TRUE;
1104   pad->is_sparse = TRUE;
1105
1106   pad->caps = gst_caps_new_simple ("application/x-ogg-skeleton", NULL);
1107
1108   return TRUE;
1109 }
1110
1111 gboolean
1112 gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
1113     guint32 * serialno, GstOggSkeleton * type)
1114 {
1115   GstOggSkeleton stype;
1116   guint serial_offset;
1117
1118   if (size != 0 && size < SKELETON_FISBONE_MIN_SIZE) {
1119     GST_WARNING ("small fisbone packet of size %d, ignoring", size);
1120     return FALSE;
1121   }
1122
1123   if (size == 0) {
1124     /* Skeleton EOS packet is zero bytes */
1125     return FALSE;
1126   } else if (memcmp (data, "fisbone\0", 8) == 0) {
1127     GST_INFO ("got fisbone packet");
1128     stype = GST_OGG_SKELETON_FISBONE;
1129     serial_offset = 12;
1130   } else if (memcmp (data, "index\0", 6) == 0) {
1131     GST_INFO ("got index packet");
1132     stype = GST_OGG_SKELETON_INDEX;
1133     serial_offset = 6;
1134   } else if (memcmp (data, "fishead\0", 8) == 0) {
1135     return FALSE;
1136   } else {
1137     GST_WARNING ("unknown skeleton packet \"%10.10s\"", data);
1138     return FALSE;
1139   }
1140
1141   if (serialno)
1142     *serialno = GST_READ_UINT32_LE (data + serial_offset);
1143
1144   if (type)
1145     *type = stype;
1146
1147   return TRUE;
1148 }
1149
1150 gboolean
1151 gst_ogg_map_add_fisbone (GstOggStream * pad, GstOggStream * skel_pad,
1152     const guint8 * data, guint size, GstClockTime * p_start_time)
1153 {
1154   GstClockTime start_time;
1155   gint64 start_granule;
1156
1157   if (pad->have_fisbone) {
1158     GST_DEBUG ("already have fisbone, ignoring second one");
1159     return FALSE;
1160   }
1161
1162   /* skip "fisbone\0" + headers offset + serialno + num headers */
1163   data += 8 + 4 + 4 + 4;
1164
1165   pad->have_fisbone = TRUE;
1166
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
1173      skeleton data. */
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);
1177   }
1178   if (pad->granuleshift < 0) {
1179     pad->granuleshift = GST_READ_UINT8 (data + 28);
1180   }
1181
1182   start_granule = GST_READ_UINT64_LE (data + 16);
1183   pad->preroll = GST_READ_UINT32_LE (data + 24);
1184
1185   start_time = granulepos_to_granule_default (pad, start_granule);
1186
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);
1193
1194   if (p_start_time)
1195     *p_start_time = start_time;
1196
1197   return TRUE;
1198 }
1199
1200 static gboolean
1201 read_vlc (const guint8 ** data, guint * size, guint64 * result)
1202 {
1203   gint shift = 0;
1204   guint8 byte;
1205
1206   *result = 0;
1207
1208   do {
1209     if (G_UNLIKELY (*size < 1))
1210       return FALSE;
1211
1212     byte = **data;
1213     *result |= ((byte & 0x7f) << shift);
1214     shift += 7;
1215
1216     (*data)++;
1217     (*size)--;
1218   } while ((byte & 0x80) != 0x80);
1219
1220   return TRUE;
1221 }
1222
1223 gboolean
1224 gst_ogg_map_add_index (GstOggStream * pad, GstOggStream * skel_pad,
1225     const guint8 * data, guint size)
1226 {
1227   guint64 i, n_keypoints, isize;
1228   guint64 offset, timestamp;
1229   guint64 offset_d, timestamp_d;
1230
1231   if (pad->index) {
1232     GST_DEBUG ("already have index, ignoring second one");
1233     return TRUE;
1234   }
1235
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);
1239     return FALSE;
1240   }
1241
1242   /* skip "index\0" + serialno */
1243   data += 6 + 4;
1244   size -= 6 + 4;
1245
1246   n_keypoints = GST_READ_UINT64_LE (data);
1247
1248   data += 8;
1249   size -= 8;
1250
1251   pad->kp_denom = GST_READ_UINT64_LE (data);
1252   if (pad->kp_denom == 0)
1253     pad->kp_denom = 1;
1254
1255   data += 8;
1256   size -= 8;
1257
1258   if (skel_pad->skeleton_major == 4) {
1259     gint64 firstsampletime_n;
1260     gint64 lastsampletime_n;
1261     gint64 firstsampletime, lastsampletime;
1262
1263     firstsampletime_n = GST_READ_UINT64_LE (data + 0);
1264     lastsampletime_n = GST_READ_UINT64_LE (data + 8);
1265
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);
1270
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);
1275
1276     if (lastsampletime > firstsampletime)
1277       pad->total_time = lastsampletime - firstsampletime;
1278     else
1279       pad->total_time = -1;
1280
1281     GST_INFO ("skeleton index parsed total: %" GST_TIME_FORMAT,
1282         GST_TIME_ARGS (pad->total_time));
1283
1284     data += 16;
1285     size -= 16;
1286   }
1287
1288   GST_INFO ("skeleton index has %" G_GUINT64_FORMAT " keypoints, denom: %"
1289       G_GINT64_FORMAT, n_keypoints, pad->kp_denom);
1290
1291   pad->index = g_try_new (GstOggIndex, n_keypoints);
1292   if (!pad->index)
1293     return FALSE;
1294
1295   isize = 0;
1296   offset = 0;
1297   timestamp = 0;
1298
1299   for (i = 0; i < n_keypoints; i++) {
1300     /* read deltas */
1301     if (!read_vlc (&data, &size, &offset_d))
1302       break;
1303     if (!read_vlc (&data, &size, &timestamp_d))
1304       break;
1305
1306     offset += offset_d;
1307     timestamp += timestamp_d;
1308
1309     pad->index[i].offset = offset;
1310     pad->index[i].timestamp = timestamp;
1311     isize++;
1312
1313     GST_INFO ("offset %" G_GUINT64_FORMAT " time %" G_GUINT64_FORMAT, offset,
1314         timestamp);
1315   }
1316   if (isize != n_keypoints) {
1317     GST_WARNING ("truncated index, expected %" G_GUINT64_FORMAT ", found %"
1318         G_GUINT64_FORMAT, n_keypoints, isize);
1319   }
1320   pad->n_index = isize;
1321   /* try to use the index to estimate the bitrate */
1322   if (isize > 2) {
1323     guint64 so, eo, st, et, b, t;
1324
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;
1330
1331     b = eo - so;
1332     t = et - st;
1333
1334     GST_DEBUG ("bytes/time %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, b, t);
1335
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);
1338
1339     GST_DEBUG ("bitrate %" G_GUINT64_FORMAT, pad->idx_bitrate);
1340   }
1341
1342   return TRUE;
1343 }
1344
1345 static gint
1346 gst_ogg_index_compare (const GstOggIndex * index, const guint64 * ts,
1347     gpointer user_data)
1348 {
1349   if (index->timestamp < *ts)
1350     return -1;
1351   else if (index->timestamp > *ts)
1352     return 1;
1353   else
1354     return 0;
1355 }
1356
1357 gboolean
1358 gst_ogg_map_search_index (GstOggStream * pad, gboolean before,
1359     guint64 * timestamp, guint64 * offset)
1360 {
1361   guint64 n_index;
1362   guint64 ts;
1363   GstOggIndex *best;
1364
1365   n_index = pad->n_index;
1366   if (n_index == 0 || pad->index == NULL)
1367     return FALSE;
1368
1369   ts = gst_util_uint64_scale (*timestamp, pad->kp_denom, GST_SECOND);
1370   GST_INFO ("timestamp %" G_GUINT64_FORMAT, ts);
1371
1372   best =
1373       gst_util_array_binary_search (pad->index, n_index, sizeof (GstOggIndex),
1374       (GCompareDataFunc) gst_ogg_index_compare, GST_SEARCH_MODE_BEFORE, &ts,
1375       NULL);
1376
1377   if (best == NULL)
1378     return FALSE;
1379
1380   GST_INFO ("found at index %u", (guint) (best - pad->index));
1381
1382   if (offset)
1383     *offset = best->offset;
1384   if (timestamp)
1385     *timestamp =
1386         gst_util_uint64_scale (best->timestamp, GST_SECOND, pad->kp_denom);
1387
1388   return TRUE;
1389 }
1390
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]);
1398  */
1399
1400 static gboolean
1401 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
1402 {
1403   if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
1404     return TRUE;
1405
1406   return FALSE;
1407 }
1408
1409 static void
1410 extract_tags_ogm (GstOggStream * pad, ogg_packet * packet)
1411 {
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);
1415   }
1416 }
1417
1418 static gint64
1419 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
1420 {
1421   const guint8 *data;
1422   int samples;
1423   int offset;
1424   int n;
1425
1426   data = packet->packet;
1427   offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
1428
1429   if (offset > packet->bytes) {
1430     GST_ERROR ("buffer too small");
1431     return -1;
1432   }
1433
1434   samples = 0;
1435   for (n = offset - 1; n > 0; n--) {
1436     samples = (samples << 8) | data[n];
1437   }
1438
1439   return samples;
1440 }
1441
1442 static gboolean
1443 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
1444 {
1445   guint8 *data = packet->packet;
1446   guint32 fourcc;
1447
1448   pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
1449   pad->granulerate_d = 1;
1450
1451   fourcc = GST_READ_UINT32_LE (data + 9);
1452   GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
1453
1454   pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1455
1456   GST_LOG ("sample rate: %d", pad->granulerate_n);
1457   if (pad->granulerate_n == 0)
1458     return FALSE;
1459
1460   if (pad->caps) {
1461     gst_caps_set_simple (pad->caps,
1462         "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1463   } else {
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);
1467   }
1468
1469   pad->n_header_packets = 1;
1470   pad->is_ogm = TRUE;
1471
1472   return TRUE;
1473 }
1474
1475 static gboolean
1476 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
1477 {
1478   guint8 *data = packet->packet;
1479   guint32 fourcc;
1480   int width, height;
1481   gint64 time_unit;
1482
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));
1485
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");
1491   }
1492   pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1493
1494   GST_LOG ("fps = %d/%d = %.3f",
1495       pad->granulerate_n, pad->granulerate_d,
1496       (double) pad->granulerate_n / pad->granulerate_d);
1497
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));
1502
1503   pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
1504
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);
1510   } else {
1511     gst_caps_set_simple (pad->caps,
1512         "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
1513         pad->granulerate_d,
1514         "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
1515   }
1516   GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
1517
1518   pad->n_header_packets = 1;
1519   pad->frame_size = 1;
1520   pad->is_ogm = TRUE;
1521
1522   return TRUE;
1523 }
1524
1525 static gboolean
1526 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
1527 {
1528   guint8 *data = packet->packet;
1529   gint64 time_unit;
1530
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");
1535   }
1536   pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
1537
1538   GST_LOG ("fps = %d/%d = %.3f",
1539       pad->granulerate_n, pad->granulerate_d,
1540       (double) pad->granulerate_n / pad->granulerate_d);
1541
1542   if (pad->granulerate_d <= 0)
1543     return FALSE;
1544
1545   pad->caps = gst_caps_new_simple ("text/plain", NULL);
1546
1547   pad->n_header_packets = 1;
1548   pad->is_ogm = TRUE;
1549   pad->is_ogm_text = TRUE;
1550   pad->is_sparse = TRUE;
1551
1552   return TRUE;
1553 }
1554
1555 /* PCM */
1556
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 */
1565
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) */
1568
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 */
1573
1574
1575 static gboolean
1576 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
1577 {
1578   guint8 *data = packet->packet;
1579   int format;
1580   int channels;
1581   GstCaps *caps;
1582
1583   pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
1584   pad->granulerate_d = 1;
1585   GST_LOG ("sample rate: %d", pad->granulerate_n);
1586
1587   format = GST_READ_UINT32_LE (data + 12);
1588   channels = GST_READ_UINT8 (data + 21);
1589
1590   pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
1591
1592   if (pad->granulerate_n == 0)
1593     return FALSE;
1594
1595   switch (format) {
1596     case OGGPCM_FMT_S8:
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);
1600       break;
1601     case OGGPCM_FMT_U8:
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);
1605       break;
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);
1612       break;
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);
1619       break;
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);
1626       break;
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);
1633       break;
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);
1640       break;
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);
1647       break;
1648     case OGGPCM_FMT_ULAW:
1649       caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
1650       break;
1651     case OGGPCM_FMT_ALAW:
1652       caps = gst_caps_new_simple ("audio/x-alaw", NULL);
1653       break;
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);
1658       break;
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);
1663       break;
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);
1668       break;
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);
1673       break;
1674     default:
1675       return FALSE;
1676   }
1677
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);
1681   pad->caps = caps;
1682
1683   return TRUE;
1684 }
1685
1686 /* cmml */
1687
1688 static gboolean
1689 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1690 {
1691   guint8 *data = packet->packet;
1692
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);
1697
1698   pad->n_header_packets = 3;
1699
1700   if (pad->granulerate_n == 0)
1701     return FALSE;
1702
1703   data += 4 + (4 + 4 + 4);
1704   GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1705   GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1706
1707   pad->caps = gst_caps_new_simple ("text/x-cmml", NULL);
1708   pad->always_flush_page = TRUE;
1709   pad->is_sparse = TRUE;
1710
1711   return TRUE;
1712 }
1713
1714 /* celt */
1715
1716 static gboolean
1717 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1718 {
1719   guint8 *data = packet->packet;
1720
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);
1725
1726   pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1727   pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1728
1729   if (pad->granulerate_n == 0)
1730     return FALSE;
1731
1732   pad->caps = gst_caps_new_simple ("audio/x-celt",
1733       "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1734
1735   return TRUE;
1736 }
1737
1738 /* kate */
1739
1740 static gboolean
1741 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1742 {
1743   guint8 *data = packet->packet;
1744   const char *category;
1745
1746   if (packet->bytes < 64)
1747     return FALSE;
1748
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);
1753
1754   pad->n_header_packets = GST_READ_UINT8 (data + 11);
1755   GST_LOG ("kate header packets: %d", pad->n_header_packets);
1756
1757   if (pad->granulerate_n == 0)
1758     return FALSE;
1759
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);
1765   } else {
1766     pad->caps = gst_caps_new_simple ("application/x-kate", NULL);
1767   }
1768
1769   pad->is_sparse = TRUE;
1770   pad->always_flush_page = TRUE;
1771
1772   return TRUE;
1773 }
1774
1775 static gint64
1776 packet_duration_kate (GstOggStream * pad, ogg_packet * packet)
1777 {
1778   gint64 duration;
1779
1780   if (packet->bytes < 1)
1781     return 0;
1782
1783   switch (packet->packet[0]) {
1784     case 0x00:                 /* text data */
1785       if (packet->bytes < 1 + 8 * 2) {
1786         duration = 0;
1787       } else {
1788         duration = GST_READ_UINT64_LE (packet->packet + 1 + 8);
1789         if (duration < 0)
1790           duration = 0;
1791       }
1792       break;
1793     default:
1794       duration = GST_CLOCK_TIME_NONE;
1795       break;
1796   }
1797
1798   return duration;
1799 }
1800
1801 static void
1802 extract_tags_kate (GstOggStream * pad, ogg_packet * packet)
1803 {
1804   GstTagList *list = NULL;
1805
1806   if (packet->bytes <= 0)
1807     return;
1808
1809   switch (packet->packet[0]) {
1810     case 0x80:{
1811       const gchar *canonical;
1812       char language[16];
1813
1814       if (packet->bytes < 64) {
1815         GST_WARNING ("Kate ID header packet is less than 64 bytes, ignored");
1816         break;
1817       }
1818
1819       /* the language tag is 16 bytes at offset 32, ensure NUL terminator */
1820       memcpy (language, packet->packet + 32, 16);
1821       language[15] = 0;
1822
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);
1827       if (canonical) {
1828         list = gst_tag_list_new_full (GST_TAG_LANGUAGE_CODE, canonical, NULL);
1829       } else {
1830         GST_WARNING ("Unknown or invalid language code %s, ignored", language);
1831       }
1832       break;
1833     }
1834     case 0x81:
1835       tag_list_from_vorbiscomment_packet (packet,
1836           (const guint8 *) "\201kate\0\0\0\0", 9, &list);
1837       break;
1838     default:
1839       break;
1840   }
1841
1842   if (list) {
1843     if (pad->taglist) {
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);
1848     } else
1849       pad->taglist = list;
1850   }
1851 }
1852
1853 /* opus */
1854
1855 static gboolean
1856 setup_opus_mapper (GstOggStream * pad, ogg_packet * packet)
1857 {
1858   if (packet->bytes < 19)
1859     return FALSE;
1860
1861   pad->granulerate_n = 48000;
1862   pad->granulerate_d = 1;
1863   pad->granuleshift = 0;
1864   pad->n_header_packets = 2;
1865
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);
1870
1871   pad->caps = gst_caps_new_simple ("audio/x-opus", NULL);
1872
1873   return TRUE;
1874 }
1875
1876 static gboolean
1877 is_header_opus (GstOggStream * pad, ogg_packet * packet)
1878 {
1879   return packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4);
1880 }
1881
1882 static gint64
1883 packet_duration_opus (GstOggStream * pad, ogg_packet * packet)
1884 {
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 */
1895   };
1896
1897   gint64 duration;
1898   gint64 frame_duration;
1899   gint nframes = 0;
1900   guint8 toc;
1901
1902   if (packet->bytes < 1)
1903     return 0;
1904
1905   /* headers */
1906   if (is_header_opus (pad, packet))
1907     return 0;
1908
1909   toc = packet->packet[0];
1910
1911   frame_duration = durations[toc >> 3];
1912   switch (toc & 3) {
1913     case 0:
1914       nframes = 1;
1915       break;
1916     case 1:
1917       nframes = 2;
1918       break;
1919     case 2:
1920       nframes = 2;
1921       break;
1922     case 3:
1923       if (packet->bytes < 2) {
1924         GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
1925         return 0;
1926       }
1927       nframes = packet->packet[1] & 63;
1928       break;
1929   }
1930
1931   duration = nframes * frame_duration;
1932   if (duration > 5760) {
1933     GST_WARNING ("Opus packet duration > 120 ms, invalid");
1934     return 0;
1935   }
1936   GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
1937       frame_duration / 48.f, nframes, duration / 48.f);
1938   return duration;
1939 }
1940
1941 static void
1942 extract_tags_opus (GstOggStream * pad, ogg_packet * packet)
1943 {
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);
1947   }
1948 }
1949
1950
1951 /* *INDENT-OFF* */
1952 /* indent hates our freedoms */
1953 const GstOggMap mappers[] = {
1954   {
1955     "\200theora", 7, 42,
1956     "video/x-theora",
1957     setup_theora_mapper,
1958     granulepos_to_granule_theora,
1959     granule_to_granulepos_default,
1960     is_keyframe_theora,
1961     is_header_theora,
1962     packet_duration_constant,
1963     NULL,
1964     extract_tags_theora
1965   },
1966   {
1967     "\001vorbis", 7, 22,
1968     "audio/x-vorbis",
1969     setup_vorbis_mapper,
1970     granulepos_to_granule_default,
1971     granule_to_granulepos_default,
1972     is_keyframe_true,
1973     is_header_vorbis,
1974     packet_duration_vorbis,
1975     NULL,
1976     extract_tags_vorbis
1977   },
1978   {
1979     "Speex", 5, 80,
1980     "audio/x-speex",
1981     setup_speex_mapper,
1982     granulepos_to_granule_default,
1983     granule_to_granulepos_default,
1984     is_keyframe_true,
1985     is_header_count,
1986     packet_duration_constant,
1987     NULL,
1988     extract_tags_count
1989   },
1990   {
1991     "PCM     ", 8, 0,
1992     "audio/x-raw-int",
1993     setup_pcm_mapper,
1994     NULL,
1995     NULL,
1996     NULL,
1997     is_header_count,
1998     NULL,
1999     NULL,
2000     NULL
2001   },
2002   {
2003     "CMML\0\0\0\0", 8, 0,
2004     "text/x-cmml",
2005     setup_cmml_mapper,
2006     NULL,
2007     NULL,
2008     NULL,
2009     is_header_count,
2010     NULL,
2011     NULL,
2012     NULL
2013   },
2014   {
2015     "Annodex", 7, 0,
2016     "application/x-annodex",
2017     setup_fishead_mapper,
2018     granulepos_to_granule_default,
2019     granule_to_granulepos_default,
2020     NULL,
2021     is_header_count,
2022     NULL,
2023     NULL,
2024     NULL
2025   },
2026   {
2027     "fishead", 7, 64,
2028     "application/octet-stream",
2029     setup_fishead_mapper,
2030     NULL,
2031     NULL,
2032     NULL,
2033     is_header_true,
2034     NULL,
2035     NULL,
2036     NULL
2037   },
2038   {
2039     "fLaC", 4, 0,
2040     "audio/x-flac",
2041     setup_fLaC_mapper,
2042     granulepos_to_granule_default,
2043     granule_to_granulepos_default,
2044     is_keyframe_true,
2045     is_header_fLaC,
2046     packet_duration_flac,
2047     NULL,
2048     NULL
2049   },
2050   {
2051     "\177FLAC", 5, 36,
2052     "audio/x-flac",
2053     setup_flac_mapper,
2054     granulepos_to_granule_default,
2055     granule_to_granulepos_default,
2056     is_keyframe_true,
2057     is_header_flac,
2058     packet_duration_flac,
2059     NULL,
2060     extract_tags_flac
2061   },
2062   {
2063     "AnxData", 7, 0,
2064     "application/octet-stream",
2065     NULL,
2066     NULL,
2067     NULL,
2068     NULL,
2069     NULL,
2070     NULL,
2071     NULL
2072   },
2073   {
2074     "CELT    ", 8, 0,
2075     "audio/x-celt",
2076     setup_celt_mapper,
2077     granulepos_to_granule_default,
2078     granule_to_granulepos_default,
2079     NULL,
2080     is_header_count,
2081     packet_duration_constant,
2082     NULL,
2083     extract_tags_count
2084   },
2085   {
2086     "\200kate\0\0\0", 8, 0,
2087     "text/x-kate",
2088     setup_kate_mapper,
2089     granulepos_to_granule_default,
2090     granule_to_granulepos_default,
2091     NULL,
2092     is_header_count,
2093     packet_duration_kate,
2094     NULL,
2095     extract_tags_kate
2096   },
2097   {
2098     "BBCD\0", 5, 13,
2099     "video/x-dirac",
2100     setup_dirac_mapper,
2101     granulepos_to_granule_dirac,
2102     granule_to_granulepos_dirac,
2103     is_keyframe_dirac,
2104     is_header_count,
2105     packet_duration_constant,
2106     granulepos_to_key_granule_dirac,
2107     NULL
2108   },
2109   {
2110     "OVP80\1\1", 7, 4,
2111     "video/x-vp8",
2112     setup_vp8_mapper,
2113     granulepos_to_granule_vp8,
2114     granule_to_granulepos_vp8,
2115     is_keyframe_vp8,
2116     is_header_vp8,
2117     packet_duration_vp8,
2118     granulepos_to_key_granule_vp8,
2119     extract_tags_vp8
2120   },
2121   {
2122     "OpusHead", 8, 0,
2123     "audio/x-opus",
2124     setup_opus_mapper,
2125     granulepos_to_granule_default,
2126     granule_to_granulepos_default,
2127     NULL,
2128     is_header_opus,
2129     packet_duration_opus,
2130     NULL,
2131     extract_tags_opus
2132   },
2133   {
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,
2139     is_keyframe_true,
2140     is_header_ogm,
2141     packet_duration_ogm,
2142     NULL,
2143     NULL
2144   },
2145   {
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,
2151     NULL,
2152     is_header_ogm,
2153     packet_duration_constant,
2154     NULL,
2155     NULL
2156   },
2157   {
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,
2163     is_keyframe_true,
2164     is_header_ogm,
2165     packet_duration_ogm,
2166     NULL,
2167     extract_tags_ogm
2168   }
2169 };
2170 /* *INDENT-ON* */
2171
2172 gboolean
2173 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
2174 {
2175   int i;
2176   gboolean ret;
2177
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) {
2182
2183       GST_DEBUG ("found mapper for '%s'", mappers[i].id);
2184
2185       if (mappers[i].setup_func)
2186         ret = mappers[i].setup_func (pad, packet);
2187       else
2188         continue;
2189
2190       if (ret) {
2191         GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
2192         pad->map = i;
2193         return TRUE;
2194       } else {
2195         GST_WARNING ("mapper '%s' did not accept setup header",
2196             mappers[i].media_type);
2197       }
2198     }
2199   }
2200
2201   return FALSE;
2202 }
2203
2204 gboolean
2205 gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad,
2206     const GstCaps * caps)
2207 {
2208   const GstStructure *structure;
2209   const GstBuffer *buf;
2210   const GValue *streamheader;
2211   const GValue *first_element;
2212   ogg_packet packet;
2213
2214   GST_INFO ("Checking streamheader on caps %" GST_PTR_FORMAT, caps);
2215
2216   if (caps == NULL)
2217     return FALSE;
2218
2219   structure = gst_caps_get_structure (caps, 0);
2220   streamheader = gst_structure_get_value (structure, "streamheader");
2221
2222   if (streamheader == NULL) {
2223     GST_LOG ("no streamheader field in caps %" GST_PTR_FORMAT, caps);
2224     return FALSE;
2225   }
2226
2227   if (!GST_VALUE_HOLDS_ARRAY (streamheader)) {
2228     GST_ERROR ("streamheader field not an array, caps: %" GST_PTR_FORMAT, caps);
2229     return FALSE;
2230   }
2231
2232   if (gst_value_array_get_size (streamheader) == 0) {
2233     GST_ERROR ("empty streamheader field in caps %" GST_PTR_FORMAT, caps);
2234     return FALSE;
2235   }
2236
2237   first_element = gst_value_array_get_value (streamheader, 0);
2238
2239   if (!GST_VALUE_HOLDS_BUFFER (first_element)) {
2240     GST_ERROR ("first streamheader not a buffer, caps: %" GST_PTR_FORMAT, caps);
2241     return FALSE;
2242   }
2243
2244   buf = gst_value_get_buffer (first_element);
2245   if (buf == NULL || GST_BUFFER_SIZE (buf) == 0) {
2246     GST_ERROR ("invalid first streamheader buffer");
2247     return FALSE;
2248   }
2249
2250   GST_MEMDUMP ("streamheader", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
2251
2252   packet.packet = GST_BUFFER_DATA (buf);
2253   packet.bytes = GST_BUFFER_SIZE (buf);
2254
2255   GST_INFO ("Found headers on caps, using those to determine type");
2256   return gst_ogg_stream_setup_map (pad, &packet);
2257 }