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