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