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