gio: Remove unused function
[platform/upstream/gstreamer.git] / ext / ogg / gstoggstream.c
1 /* GStreamer Ogg Granulepos Mapping Utility Functions
2  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
3  * Copyright (C) 2009 David Schleef <ds@schleef.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstoggstream.h"
26 #include "dirac_parse.h"
27
28 #include <gst/riff/riff-media.h>
29
30 #include <string.h>
31
32 GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug);
33 GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_setup_debug);
34 #define GST_CAT_DEFAULT gst_ogg_demux_debug
35
36 typedef struct _GstOggMap GstOggMap;
37
38 typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad,
39     ogg_packet * packet);
40 typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad,
41     gint64 granulepos);
42 typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad,
43     gint64 granulepos);
44 typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad,
45     gint64 granule, gint64 keyframe_granule);
46
47 /* returns TRUE if the granulepos denotes a key frame */
48 typedef gboolean (*GstOggMapIsKeyFrameFunc) (GstOggStream * pad,
49     gint64 granulepos);
50
51 /* returns TRUE if the given packet is a stream header packet */
52 typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
53     ogg_packet * packet);
54 typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
55     ogg_packet * packet);
56
57
58
59 #define SKELETON_FISBONE_MIN_SIZE  52
60
61
62 struct _GstOggMap
63 {
64   const gchar *id;
65   int id_length;
66   int min_packet_size;
67   const gchar *media_type;
68   GstOggMapSetupFunc setup_func;
69   GstOggMapToGranuleFunc granulepos_to_granule_func;
70   GstOggMapToGranuleposFunc granule_to_granulepos_func;
71   GstOggMapIsKeyFrameFunc is_key_frame_func;
72   GstOggMapIsHeaderPacketFunc is_header_func;
73   GstOggMapPacketDurationFunc packet_duration_func;
74 };
75
76 static const GstOggMap mappers[];
77
78 GstClockTime
79 gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet)
80 {
81   int duration;
82
83   if (packet->granulepos == -1) {
84     return GST_CLOCK_TIME_NONE;
85   }
86
87   duration = gst_ogg_stream_get_packet_duration (pad, packet);
88   if (duration == -1) {
89     return GST_CLOCK_TIME_NONE;
90   }
91
92   return gst_ogg_stream_granule_to_time (pad,
93       gst_ogg_stream_granulepos_to_granule (pad,
94           packet->granulepos) - duration);
95 }
96
97 GstClockTime
98 gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad,
99     gint64 granulepos)
100 {
101   if (pad->frame_size == 0)
102     return GST_CLOCK_TIME_NONE;
103
104   return gst_ogg_stream_granule_to_time (pad,
105       gst_ogg_stream_granulepos_to_granule (pad, granulepos));
106 }
107
108 GstClockTime
109 gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad,
110     gint64 granulepos)
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_granule_to_time (GstOggStream * pad, gint64 granule)
118 {
119   if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
120     return 0;
121
122   return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
123       pad->granulerate_n);
124 }
125
126 gint64
127 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
128 {
129   if (granulepos == -1 || granulepos == 0) {
130     return granulepos;
131   }
132
133   if (mappers[pad->map].granulepos_to_granule_func == NULL) {
134     GST_WARNING ("Failed to convert granulepos to granule");
135     return -1;
136   }
137
138   return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
139 }
140
141 gint64
142 gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos)
143 {
144   if (granulepos == -1 || granulepos == 0) {
145     return granulepos;
146   }
147
148   return granulepos >> pad->granuleshift;
149 }
150
151 gint64
152 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
153     gint64 keyframe_granule)
154 {
155   if (granule == -1 || granule == 0) {
156     return granule;
157   }
158
159   if (mappers[pad->map].granule_to_granulepos_func == NULL) {
160     GST_WARNING ("Failed to convert granule to granulepos");
161     return -1;
162   }
163
164   return mappers[pad->map].granule_to_granulepos_func (pad, granule,
165       keyframe_granule);
166 }
167
168 gboolean
169 gst_ogg_stream_packet_granulepos_is_key_frame (GstOggStream * pad,
170     gint64 granulepos)
171 {
172   if (granulepos == -1) {
173     return FALSE;
174   }
175
176   if (mappers[pad->map].is_key_frame_func == NULL) {
177     GST_WARNING ("Failed to determine key frame");
178     return FALSE;
179   }
180
181   return mappers[pad->map].is_key_frame_func (pad, granulepos);
182 }
183
184 gboolean
185 gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
186 {
187   if (mappers[pad->map].is_header_func == NULL) {
188     GST_WARNING ("Failed to determine header");
189     return FALSE;
190   }
191
192   return mappers[pad->map].is_header_func (pad, packet);
193 }
194
195 gint64
196 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
197 {
198   if (mappers[pad->map].packet_duration_func == NULL) {
199     GST_WARNING ("Failed to determine packet duration");
200     return -1;
201   }
202
203   return mappers[pad->map].packet_duration_func (pad, packet);
204 }
205
206
207
208
209 /* some generic functions */
210
211 static gboolean
212 is_keyframe_true (GstOggStream * pad, gint64 granulepos)
213 {
214   return TRUE;
215 }
216
217 static gint64
218 granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos)
219 {
220   gint64 keyindex, keyoffset;
221
222   if (pad->granuleshift != 0) {
223     keyindex = granulepos >> pad->granuleshift;
224     keyoffset = granulepos - (keyindex << pad->granuleshift);
225     return keyindex + keyoffset;
226   } else {
227     return granulepos;
228   }
229 }
230
231
232 static gint64
233 granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
234     gint64 keyframe_granule)
235 {
236   gint64 keyoffset;
237
238   if (pad->granuleshift != 0) {
239     keyoffset = granule - keyframe_granule;
240     return (keyframe_granule << pad->granuleshift) | keyoffset;
241   } else {
242     return granule;
243   }
244 }
245
246 #ifdef unused
247 static gboolean
248 is_header_unknown (GstOggStream * pad, ogg_packet * packet)
249 {
250   GST_WARNING ("don't know how to detect header");
251   return FALSE;
252 }
253 #endif
254
255 static gboolean
256 is_header_true (GstOggStream * pad, ogg_packet * packet)
257 {
258   return TRUE;
259 }
260
261 static gboolean
262 is_header_count (GstOggStream * pad, ogg_packet * packet)
263 {
264   if (pad->n_header_packets_seen < pad->n_header_packets) {
265     return TRUE;
266   }
267   return FALSE;
268 }
269
270 static gint64
271 packet_duration_constant (GstOggStream * pad, ogg_packet * packet)
272 {
273   return pad->frame_size;
274 }
275
276 /* theora */
277
278 static gboolean
279 setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
280 {
281   guint8 *data = packet->packet;
282   guint w, h, par_d, par_n;
283
284   w = GST_READ_UINT24_BE (data + 14) & 0xFFFFF0;
285   h = GST_READ_UINT24_BE (data + 17) & 0xFFFFF0;
286
287   pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
288   pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
289
290   par_n = GST_READ_UINT24_BE (data + 30);
291   par_d = GST_READ_UINT24_BE (data + 33);
292
293   GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
294       pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
295
296   /* 2 bits + 3 bits = 5 bits KFGSHIFT */
297   pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
298       (GST_READ_UINT8 (data + 41) >> 5);
299
300   pad->n_header_packets = 3;
301   pad->frame_size = 1;
302
303   if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
304     GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
305     return FALSE;
306   }
307
308   pad->caps = gst_caps_new_simple ("video/x-theora", NULL);
309
310   if (w > 0 && h > 0) {
311     gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
312         G_TYPE_INT, h, NULL);
313   }
314
315   /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */
316   if (par_n == 0 || par_d == 0)
317     par_n = par_d = 1;
318
319   /* only add framerate now so caps look prettier, with width/height first */
320   gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
321       pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio",
322       GST_TYPE_FRACTION, par_n, par_d, NULL);
323
324   return TRUE;
325 }
326
327 static gint64
328 granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
329 {
330   gint64 keyindex, keyoffset;
331
332   if (pad->granuleshift != 0) {
333     keyindex = granulepos >> pad->granuleshift;
334     keyoffset = granulepos - (keyindex << pad->granuleshift);
335     if (keyoffset == 0) {
336       pad->theora_has_zero_keyoffset = TRUE;
337     }
338     if (pad->theora_has_zero_keyoffset) {
339       keyoffset++;
340     }
341     return keyindex + keyoffset;
342   } else {
343     return granulepos;
344   }
345 }
346
347 static gboolean
348 is_keyframe_theora (GstOggStream * pad, gint64 granulepos)
349 {
350   gint64 frame_mask;
351
352   if (granulepos == (gint64) - 1)
353     return FALSE;
354
355   frame_mask = (1 << (pad->granuleshift + 1)) - 1;
356
357   return ((granulepos & frame_mask) == 0);
358 }
359
360 static gboolean
361 is_header_theora (GstOggStream * pad, ogg_packet * packet)
362 {
363   return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
364 }
365
366 /* dirac */
367
368 static gboolean
369 setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
370 {
371   int ret;
372   DiracSequenceHeader header;
373
374   ret = dirac_sequence_header_parse (&header, packet->packet + 13,
375       packet->bytes - 13);
376   if (ret == 0) {
377     GST_DEBUG ("Failed to parse Dirac sequence header");
378     return FALSE;
379   }
380
381   pad->granulerate_n = header.frame_rate_numerator * 2;
382   pad->granulerate_d = header.frame_rate_denominator;
383   pad->granuleshift = 22;
384   pad->n_header_packets = 1;
385   pad->frame_size = 2;
386
387   if (header.interlaced_coding != 0) {
388     GST_DEBUG ("non-progressive Dirac coding not implemented");
389     return FALSE;
390   }
391
392   pad->caps = gst_caps_new_simple ("video/x-dirac",
393       "width", G_TYPE_INT, header.width,
394       "height", G_TYPE_INT, header.height,
395       "interlaced", G_TYPE_BOOLEAN, header.interlaced,
396       "pixel-aspect-ratio", GST_TYPE_FRACTION,
397       header.aspect_ratio_numerator, header.aspect_ratio_denominator,
398       "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
399       header.frame_rate_denominator, NULL);
400
401   return TRUE;
402 }
403
404 #define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
405 static gboolean
406 is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
407 {
408   gint64 pt;
409   int dist_h;
410   int dist_l;
411   int dist;
412   int delay;
413   gint64 dt;
414
415   if (granulepos == -1)
416     return -1;
417
418   pt = ((granulepos >> 22) + (granulepos & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
419   dist_h = (granulepos >> 22) & 0xff;
420   dist_l = granulepos & 0xff;
421   dist = (dist_h << 8) | dist_l;
422   delay = (granulepos >> 9) & 0x1fff;
423   dt = pt - delay;
424
425   return (dist == 0);
426 }
427
428 static gint64
429 granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
430 {
431   gint64 pt;
432   int dist_h;
433   int dist_l;
434   int dist;
435   int delay;
436   gint64 dt;
437
438   pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
439   dist_h = (gp >> 22) & 0xff;
440   dist_l = gp & 0xff;
441   dist = (dist_h << 8) | dist_l;
442   delay = (gp >> 9) & 0x1fff;
443   dt = pt - delay;
444
445   GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
446
447   return dt + 4;
448 }
449
450 static gint64
451 granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
452     gint64 keyframe_granule)
453 {
454   /* This conversion requires knowing more details about the Dirac
455    * stream. */
456   return -1;
457 }
458
459
460 /* vorbis */
461
462 void parse_vorbis_header_packet (GstOggStream * pad, ogg_packet * op);
463 void parse_vorbis_setup_packet (GstOggStream * pad, ogg_packet * op);
464
465
466 static gboolean
467 setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
468 {
469   guint8 *data = packet->packet;
470   guint chans;
471
472   data += 1 + 6 + 4;
473   chans = GST_READ_UINT8 (data);
474   data += 1;
475   pad->granulerate_n = GST_READ_UINT32_LE (data);
476   pad->granulerate_d = 1;
477   pad->granuleshift = 0;
478   pad->last_size = 0;
479   GST_LOG ("sample rate: %d", pad->granulerate_n);
480
481   pad->n_header_packets = 3;
482
483   if (pad->granulerate_n == 0)
484     return FALSE;
485
486   parse_vorbis_header_packet (pad, packet);
487
488   pad->caps = gst_caps_new_simple ("audio/x-vorbis",
489       "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
490       NULL);
491
492   return TRUE;
493 }
494
495 static gboolean
496 is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
497 {
498   if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
499     return FALSE;
500
501   if (packet->packet[0] == 5) {
502     parse_vorbis_setup_packet (pad, packet);
503   }
504
505   return TRUE;
506 }
507
508 static gint64
509 packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
510 {
511   int mode;
512   int size;
513   int duration;
514
515   if (packet->packet[0] & 1)
516     return 0;
517
518   mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
519   size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
520
521   if (pad->last_size == 0) {
522     duration = 0;
523   } else {
524     duration = pad->last_size / 4 + size / 4;
525   }
526   pad->last_size = size;
527
528   GST_DEBUG ("duration %d", (int) duration);
529
530   return duration;
531 }
532
533 /* speex */
534
535
536 static gboolean
537 setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
538 {
539   guint8 *data = packet->packet;
540   guint chans;
541
542   data += 8 + 20 + 4 + 4;
543   pad->granulerate_n = GST_READ_UINT32_LE (data);
544   pad->granulerate_d = 1;
545   pad->granuleshift = 0;
546
547   data += 4 + 4 + 4;
548   chans = GST_READ_UINT32_LE (data);
549
550   GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
551
552   pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
553   pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
554       GST_READ_UINT32_LE (packet->packet + 56);
555
556   if (pad->granulerate_n == 0)
557     return FALSE;
558
559   pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
560       pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
561
562   return TRUE;
563 }
564
565
566 /* flac */
567
568 static gboolean
569 setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
570 {
571   pad->granulerate_n = 0;
572   pad->granulerate_d = 1;
573   pad->granuleshift = 0;
574
575   pad->n_header_packets = 3;
576
577   pad->caps = gst_caps_new_simple ("audio/x-flac", NULL);
578
579   return TRUE;
580 }
581
582 static gboolean
583 is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
584 {
585   if (pad->n_header_packets_seen == 1) {
586     pad->granulerate_n = (packet->packet[14] << 12) |
587         (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
588   }
589
590   if (pad->n_header_packets_seen < pad->n_header_packets) {
591     return TRUE;
592   }
593
594   return FALSE;
595 }
596
597 static gboolean
598 setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
599 {
600   guint8 *data = packet->packet;
601   guint chans;
602
603   /* see http://flac.sourceforge.net/ogg_mapping.html */
604
605   pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
606   pad->granulerate_d = 1;
607   pad->granuleshift = 0;
608   chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
609
610   GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
611
612   pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
613
614   if (pad->granulerate_n == 0)
615     return FALSE;
616
617   pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
618       pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
619
620   return TRUE;
621 }
622
623 static gboolean
624 is_header_flac (GstOggStream * pad, ogg_packet * packet)
625 {
626   return (packet->bytes > 0 && (packet->packet[0] != 0xff));
627 }
628
629 static gint64
630 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
631 {
632   int block_size_index;
633
634   if (packet->bytes < 4)
635     return -1;
636
637   block_size_index = packet->packet[2] >> 4;
638   if (block_size_index == 1)
639     return 192;
640   if (block_size_index >= 2 && block_size_index <= 5) {
641     return 576 << (block_size_index - 2);
642   }
643   if (block_size_index >= 8) {
644     return 256 << (block_size_index - 8);
645   }
646   if (block_size_index == 6 || block_size_index == 7) {
647     guint len, bytes = (block_size_index - 6) + 1;
648     guint8 tmp;
649
650     if (packet->bytes < 4 + 1 + bytes)
651       return -1;
652     tmp = packet->packet[4];
653     /* utf-8 prefix */
654     len = 0;
655     while (tmp & 0x80) {
656       len++;
657       tmp <<= 1;
658     }
659     if (len == 2)
660       return -1;
661     if (len == 0)
662       len++;
663     if (packet->bytes < 4 + len + bytes)
664       return -1;
665     if (bytes == 1) {
666       return packet->packet[4 + len] + 1;
667     } else {
668       return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
669     }
670   }
671   return -1;
672 }
673
674 /* fishead */
675
676 static gboolean
677 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
678 {
679   guint8 *data;
680   gint64 prestime_n, prestime_d;
681   gint64 basetime_n, basetime_d;
682   gint64 basetime;
683
684   data = packet->packet;
685
686   data += 8 + 2 + 2;            /* header + major/minor version */
687
688   prestime_n = (gint64) GST_READ_UINT64_LE (data);
689   data += 8;
690   prestime_d = (gint64) GST_READ_UINT64_LE (data);
691   data += 8;
692   basetime_n = (gint64) GST_READ_UINT64_LE (data);
693   data += 8;
694   basetime_d = (gint64) GST_READ_UINT64_LE (data);
695   data += 8;
696
697   /* FIXME: we don't use basetime anywhere in the demuxer! */
698   if (basetime_d != 0)
699     basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
700   else
701     basetime = -1;
702
703   GST_INFO ("skeleton fishead parsed (basetime: %" GST_TIME_FORMAT ")",
704       GST_TIME_ARGS (basetime));
705
706   pad->is_skeleton = TRUE;
707
708   return TRUE;
709 }
710
711 gboolean
712 gst_ogg_map_add_fisbone (GstOggStream * pad,
713     const guint8 * data, guint size, GstClockTime * p_start_time,
714     guint32 * p_preroll)
715 {
716   GstClockTime start_time;
717   gint64 start_granule;
718   guint32 preroll;
719
720   if (size < SKELETON_FISBONE_MIN_SIZE || memcmp (data, "fisbone\0", 8) != 0) {
721     GST_WARNING ("invalid fisbone packet, ignoring");
722     return FALSE;
723   }
724
725   if (pad->have_fisbone) {
726     GST_DEBUG ("already have fisbone, ignoring second one");
727     return FALSE;
728   }
729
730   /* skip "fisbone\0" + headers offset + serialno + num headers */
731   data += 8 + 4 + 4 + 4;
732
733   pad->have_fisbone = TRUE;
734
735   /* we just overwrite whatever was set before by the format-specific setup */
736   pad->granulerate_n = GST_READ_UINT64_LE (data);
737   pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
738
739   start_granule = GST_READ_UINT64_LE (data + 16);
740   preroll = GST_READ_UINT32_LE (data + 24);
741   pad->granuleshift = GST_READ_UINT8 (data + 28);
742
743   start_time = granulepos_to_granule_default (pad, start_granule);
744
745   if (p_start_time)
746     *p_start_time = start_time;
747
748   if (p_preroll)
749     *p_preroll = preroll;
750
751   return TRUE;
752 }
753
754 /* Do we need these for something?
755  * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
756  * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
757  * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
758  * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
759  * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
760  * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
761  */
762
763 static gboolean
764 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
765 {
766   if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
767     return TRUE;
768
769   return FALSE;
770 }
771
772 static gint64
773 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
774 {
775   const guint8 *data;
776   int samples;
777   int offset;
778   int n;
779
780   data = packet->packet;
781   offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
782
783   if (offset > packet->bytes) {
784     GST_ERROR ("buffer too small");
785     return -1;
786   }
787
788   samples = 0;
789   for (n = offset - 1; n > 0; n--) {
790     samples = (samples << 8) | data[n];
791   }
792
793   return samples;
794 }
795
796 static gboolean
797 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
798 {
799   guint8 *data = packet->packet;
800   guint32 fourcc;
801
802   pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
803   pad->granulerate_d = 1;
804
805   fourcc = GST_READ_UINT32_LE (data + 9);
806   GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
807
808   pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
809
810   GST_LOG ("sample rate: %d", pad->granulerate_n);
811   if (pad->granulerate_n == 0)
812     return FALSE;
813
814   if (pad->caps) {
815     gst_caps_set_simple (pad->caps,
816         "rate", G_TYPE_INT, pad->granulerate_n, NULL);
817   } else {
818     pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
819         "fourcc", GST_TYPE_FOURCC, fourcc,
820         "rate", G_TYPE_INT, pad->granulerate_n, NULL);
821   }
822
823   pad->n_header_packets = 1;
824   pad->is_ogm = TRUE;
825
826   return TRUE;
827 }
828
829 static gboolean
830 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
831 {
832   guint8 *data = packet->packet;
833   guint32 fourcc;
834   int width, height;
835   gint64 time_unit;
836
837   GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
838   GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
839
840   pad->granulerate_n = 10000000;
841   time_unit = GST_READ_UINT64_LE (data + 17);
842   if (time_unit > G_MAXINT || time_unit < G_MININT) {
843     GST_WARNING ("timeunit is out of range");
844   }
845   pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
846
847   GST_LOG ("fps = %d/%d = %.3f",
848       pad->granulerate_n, pad->granulerate_d,
849       (double) pad->granulerate_n / pad->granulerate_d);
850
851   fourcc = GST_READ_UINT32_LE (data + 9);
852   width = GST_READ_UINT32_LE (data + 45);
853   height = GST_READ_UINT32_LE (data + 49);
854   GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
855
856   pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
857
858   if (pad->caps == NULL) {
859     pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
860         "fourcc", GST_TYPE_FOURCC, fourcc,
861         "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
862         pad->granulerate_d, NULL);
863   } else {
864     gst_caps_set_simple (pad->caps,
865         "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
866         pad->granulerate_d,
867         "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
868   }
869   GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
870
871   pad->n_header_packets = 1;
872   pad->frame_size = 1;
873   pad->is_ogm = TRUE;
874
875   return TRUE;
876 }
877
878 static gboolean
879 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
880 {
881   guint8 *data = packet->packet;
882   gint64 time_unit;
883
884   pad->granulerate_n = 10000000;
885   time_unit = GST_READ_UINT64_LE (data + 17);
886   if (time_unit > G_MAXINT || time_unit < G_MININT) {
887     GST_WARNING ("timeunit is out of range");
888   }
889   pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
890
891   GST_LOG ("fps = %d/%d = %.3f",
892       pad->granulerate_n, pad->granulerate_d,
893       (double) pad->granulerate_n / pad->granulerate_d);
894
895   if (pad->granulerate_d <= 0)
896     return FALSE;
897
898   pad->caps = gst_caps_new_simple ("text/plain", NULL);
899
900   pad->n_header_packets = 1;
901   pad->is_ogm = TRUE;
902   pad->is_ogm_text = TRUE;
903
904   return TRUE;
905 }
906
907 /* PCM */
908
909 #define OGGPCM_FMT_S8 0x00000000        /* Signed integer 8 bit */
910 #define OGGPCM_FMT_U8 0x00000001        /* Unsigned integer 8 bit */
911 #define OGGPCM_FMT_S16_LE 0x00000002    /* Signed integer 16 bit little endian */
912 #define OGGPCM_FMT_S16_BE 0x00000003    /* Signed integer 16 bit big endian */
913 #define OGGPCM_FMT_S24_LE 0x00000004    /* Signed integer 24 bit little endian */
914 #define OGGPCM_FMT_S24_BE 0x00000005    /* Signed integer 24 bit big endian */
915 #define OGGPCM_FMT_S32_LE 0x00000006    /* Signed integer 32 bit little endian */
916 #define OGGPCM_FMT_S32_BE 0x00000007    /* Signed integer 32 bit big endian */
917
918 #define OGGPCM_FMT_ULAW 0x00000010      /* G.711 u-law encoding (8 bit) */
919 #define OGGPCM_FMT_ALAW 0x00000011      /* G.711 A-law encoding (8 bit) */
920
921 #define OGGPCM_FMT_FLT32_LE 0x00000020  /* IEEE Float [-1,1] 32 bit little endian */
922 #define OGGPCM_FMT_FLT32_BE 0x00000021  /* IEEE Float [-1,1] 32 bit big endian */
923 #define OGGPCM_FMT_FLT64_LE 0x00000022  /* IEEE Float [-1,1] 64 bit little endian */
924 #define OGGPCM_FMT_FLT64_BE 0x00000023  /* IEEE Float [-1,1] 64 bit big endian */
925
926
927 static gboolean
928 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
929 {
930   guint8 *data = packet->packet;
931   int format;
932   int channels;
933   GstCaps *caps;
934
935   pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
936   pad->granulerate_d = 1;
937   GST_LOG ("sample rate: %d", pad->granulerate_n);
938
939   format = GST_READ_UINT32_LE (data + 12);
940   channels = GST_READ_UINT8 (data + 21);
941
942   pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
943
944   if (pad->granulerate_n == 0)
945     return FALSE;
946
947   switch (format) {
948     case OGGPCM_FMT_S8:
949       caps = gst_caps_new_simple ("audio/x-raw-int",
950           "depth", G_TYPE_INT, 8,
951           "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
952       break;
953     case OGGPCM_FMT_U8:
954       caps = gst_caps_new_simple ("audio/x-raw-int",
955           "depth", G_TYPE_INT, 8,
956           "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
957       break;
958     case OGGPCM_FMT_S16_LE:
959       caps = gst_caps_new_simple ("audio/x-raw-int",
960           "depth", G_TYPE_INT, 16,
961           "width", G_TYPE_INT, 16,
962           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
963           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
964       break;
965     case OGGPCM_FMT_S16_BE:
966       caps = gst_caps_new_simple ("audio/x-raw-int",
967           "depth", G_TYPE_INT, 16,
968           "width", G_TYPE_INT, 16,
969           "endianness", G_TYPE_INT, G_BIG_ENDIAN,
970           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
971       break;
972     case OGGPCM_FMT_S24_LE:
973       caps = gst_caps_new_simple ("audio/x-raw-int",
974           "depth", G_TYPE_INT, 24,
975           "width", G_TYPE_INT, 24,
976           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
977           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
978       break;
979     case OGGPCM_FMT_S24_BE:
980       caps = gst_caps_new_simple ("audio/x-raw-int",
981           "depth", G_TYPE_INT, 24,
982           "width", G_TYPE_INT, 24,
983           "endianness", G_TYPE_INT, G_BIG_ENDIAN,
984           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
985       break;
986     case OGGPCM_FMT_S32_LE:
987       caps = gst_caps_new_simple ("audio/x-raw-int",
988           "depth", G_TYPE_INT, 32,
989           "width", G_TYPE_INT, 32,
990           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
991           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
992       break;
993     case OGGPCM_FMT_S32_BE:
994       caps = gst_caps_new_simple ("audio/x-raw-int",
995           "depth", G_TYPE_INT, 32,
996           "width", G_TYPE_INT, 32,
997           "endianness", G_TYPE_INT, G_BIG_ENDIAN,
998           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
999       break;
1000     case OGGPCM_FMT_ULAW:
1001       caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
1002       break;
1003     case OGGPCM_FMT_ALAW:
1004       caps = gst_caps_new_simple ("audio/x-alaw", NULL);
1005       break;
1006     case OGGPCM_FMT_FLT32_LE:
1007       caps = gst_caps_new_simple ("audio/x-raw-float",
1008           "width", G_TYPE_INT, 32,
1009           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1010       break;
1011     case OGGPCM_FMT_FLT32_BE:
1012       caps = gst_caps_new_simple ("audio/x-raw-float",
1013           "width", G_TYPE_INT, 32,
1014           "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1015       break;
1016     case OGGPCM_FMT_FLT64_LE:
1017       caps = gst_caps_new_simple ("audio/x-raw-float",
1018           "width", G_TYPE_INT, 64,
1019           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1020       break;
1021     case OGGPCM_FMT_FLT64_BE:
1022       caps = gst_caps_new_simple ("audio/x-raw-float",
1023           "width", G_TYPE_INT, 64,
1024           "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1025       break;
1026     default:
1027       return FALSE;
1028   }
1029
1030   gst_caps_set_simple (caps, "audio/x-raw-int",
1031       "rate", G_TYPE_INT, pad->granulerate_n,
1032       "channels", G_TYPE_INT, channels, NULL);
1033   pad->caps = caps;
1034
1035   return TRUE;
1036 }
1037
1038 /* cmml */
1039
1040 static gboolean
1041 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1042 {
1043   guint8 *data = packet->packet;
1044
1045   pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1046   pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1047   pad->granuleshift = data[28];
1048   GST_LOG ("sample rate: %d", pad->granulerate_n);
1049
1050   pad->n_header_packets = 3;
1051
1052   if (pad->granulerate_n == 0)
1053     return FALSE;
1054
1055   data += 4 + (4 + 4 + 4);
1056   GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1057   GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1058
1059   pad->caps = gst_caps_new_simple ("text/x-cmml", NULL);
1060
1061   return TRUE;
1062 }
1063
1064 /* celt */
1065
1066 static gboolean
1067 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1068 {
1069   guint8 *data = packet->packet;
1070
1071   pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1072   pad->granulerate_d = 1;
1073   pad->granuleshift = 0;
1074   GST_LOG ("sample rate: %d", pad->granulerate_n);
1075
1076   pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1077   pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1078
1079   if (pad->granulerate_n == 0)
1080     return FALSE;
1081
1082   pad->caps = gst_caps_new_simple ("audio/x-celt",
1083       "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1084
1085   return TRUE;
1086 }
1087
1088 /* kate */
1089
1090 static gboolean
1091 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1092 {
1093   guint8 *data = packet->packet;
1094   const char *category;
1095
1096   if (packet->bytes < 64)
1097     return FALSE;
1098
1099   pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1100   pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1101   pad->granuleshift = GST_READ_UINT8 (data + 15);
1102   GST_LOG ("sample rate: %d", pad->granulerate_n);
1103
1104   pad->n_header_packets = GST_READ_UINT8 (data + 11);
1105
1106   if (pad->granulerate_n == 0)
1107     return FALSE;
1108
1109   category = (const char *) data + 48;
1110   if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 ||
1111       strcmp (category, "spu-subtitles") == 0 ||
1112       strcmp (category, "K-SPU") == 0) {
1113     pad->caps = gst_caps_new_simple ("subtitle/x-kate", NULL);
1114   } else {
1115     pad->caps = gst_caps_new_simple ("application/x-kate", NULL);
1116   }
1117
1118   return TRUE;
1119 }
1120
1121
1122 /* *INDENT-OFF* */
1123 /* indent hates our freedoms */
1124 static const GstOggMap mappers[] = {
1125   {
1126     "\200theora", 7, 42,
1127     "video/x-theora",
1128     setup_theora_mapper,
1129     granulepos_to_granule_theora,
1130     granule_to_granulepos_default,
1131     is_keyframe_theora,
1132     is_header_theora,
1133     packet_duration_constant
1134   },
1135   {
1136     "\001vorbis", 7, 22,
1137     "audio/x-vorbis",
1138     setup_vorbis_mapper,
1139     granulepos_to_granule_default,
1140     granule_to_granulepos_default,
1141     is_keyframe_true,
1142     is_header_vorbis,
1143     packet_duration_vorbis
1144   },
1145   {
1146     "Speex", 5, 80,
1147     "audio/x-speex",
1148     setup_speex_mapper,
1149     granulepos_to_granule_default,
1150     granule_to_granulepos_default,
1151     is_keyframe_true,
1152     is_header_count,
1153     packet_duration_constant
1154   },
1155   {
1156     "PCM     ", 8, 0,
1157     "audio/x-raw-int",
1158     setup_pcm_mapper,
1159     NULL,
1160     NULL,
1161     NULL,
1162     is_header_count,
1163     NULL
1164   },
1165   {
1166     "CMML\0\0\0\0", 8, 0,
1167     "text/x-cmml",
1168     setup_cmml_mapper,
1169     NULL,
1170     NULL,
1171     NULL,
1172     is_header_count,
1173     NULL
1174   },
1175   {
1176     "Annodex", 7, 0,
1177     "application/x-annodex",
1178     setup_fishead_mapper,
1179     granulepos_to_granule_default,
1180     granule_to_granulepos_default,
1181     NULL,
1182     is_header_count,
1183     NULL
1184   },
1185   {
1186     "fishead", 7, 64,
1187     "application/octet-stream",
1188     setup_fishead_mapper,
1189     NULL,
1190     NULL,
1191     NULL,
1192     is_header_true,
1193     NULL
1194   },
1195   {
1196     "fLaC", 4, 0,
1197     "audio/x-flac",
1198     setup_fLaC_mapper,
1199     granulepos_to_granule_default,
1200     granule_to_granulepos_default,
1201     is_keyframe_true,
1202     is_header_fLaC,
1203     NULL
1204   },
1205   {
1206     "\177FLAC", 5, 36,
1207     "audio/x-flac",
1208     setup_flac_mapper,
1209     granulepos_to_granule_default,
1210     granule_to_granulepos_default,
1211     is_keyframe_true,
1212     is_header_flac,
1213     packet_duration_flac
1214   },
1215   {
1216     "AnxData", 7, 0,
1217     "application/octet-stream",
1218     NULL,
1219     NULL,
1220     NULL,
1221     NULL,
1222     NULL,
1223   },
1224   {
1225     "CELT    ", 8, 0,
1226     "audio/x-celt",
1227     setup_celt_mapper,
1228     granulepos_to_granule_default,
1229     granule_to_granulepos_default,
1230     NULL,
1231     is_header_count,
1232     packet_duration_constant
1233   },
1234   {
1235     "\200kate\0\0\0", 8, 0,
1236     "text/x-kate",
1237     setup_kate_mapper,
1238     granulepos_to_granule_default,
1239     granule_to_granulepos_default,
1240     NULL,
1241     is_header_count,
1242     NULL
1243   },
1244   {
1245     "BBCD\0", 5, 13,
1246     "video/x-dirac",
1247     setup_dirac_mapper,
1248     granulepos_to_granule_dirac,
1249     granule_to_granulepos_dirac,
1250     is_keyframe_dirac,
1251     is_header_count,
1252     packet_duration_constant
1253   },
1254   {
1255     "\001audio\0\0\0", 9, 53,
1256     "application/x-ogm-audio",
1257     setup_ogmaudio_mapper,
1258     granulepos_to_granule_default,
1259     granule_to_granulepos_default,
1260     is_keyframe_true,
1261     is_header_ogm,
1262     packet_duration_ogm
1263   },
1264   {
1265     "\001video\0\0\0", 9, 53,
1266     "application/x-ogm-video",
1267     setup_ogmvideo_mapper,
1268     granulepos_to_granule_default,
1269     granule_to_granulepos_default,
1270     NULL,
1271     is_header_ogm,
1272     packet_duration_constant
1273   },
1274   {
1275     "\001text\0\0\0", 9, 9,
1276     "application/x-ogm-text",
1277     setup_ogmtext_mapper,
1278     granulepos_to_granule_default,
1279     granule_to_granulepos_default,
1280     is_keyframe_true,
1281     is_header_ogm,
1282     packet_duration_ogm
1283   }
1284 };
1285 /* *INDENT-ON* */
1286
1287 gboolean
1288 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
1289 {
1290   int i;
1291   gboolean ret;
1292
1293   for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
1294     if (packet->bytes >= mappers[i].min_packet_size &&
1295         packet->bytes >= mappers[i].id_length &&
1296         memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
1297
1298       GST_DEBUG ("found mapper for '%s'", mappers[i].id);
1299
1300       if (mappers[i].setup_func)
1301         ret = mappers[i].setup_func (pad, packet);
1302       else
1303         continue;
1304
1305       if (ret) {
1306         GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
1307         pad->map = i;
1308         return TRUE;
1309       } else {
1310         GST_WARNING ("mapper '%s' did not accept setup header",
1311             mappers[i].media_type);
1312       }
1313     }
1314   }
1315
1316   return FALSE;
1317 }