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