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