Merge branch 'master' of ssh://git.freedesktop.org/git/gstreamer/gst-plugins-base
[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   /* only add framerate now so caps look prettier, with width/height first */
316   gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
317       pad->granulerate_n, pad->granulerate_d, NULL);
318
319   if (par_n > 0 && par_d > 0) {
320     gst_caps_set_simple (pad->caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
321         par_n, par_d, NULL);
322   }
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 gint64
624 packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
625 {
626   int block_size_index;
627
628   if (packet->bytes < 4)
629     return -1;
630
631   block_size_index = packet->packet[2] >> 4;
632   if (block_size_index == 1)
633     return 192;
634   if (block_size_index >= 2 && block_size_index <= 5) {
635     return 576 << (block_size_index - 2);
636   }
637   if (block_size_index >= 8) {
638     return 256 << (block_size_index - 8);
639   }
640   if (block_size_index == 6 || block_size_index == 7) {
641     guint len, bytes = (block_size_index - 6) + 1;
642     guint8 tmp;
643
644     if (packet->bytes < 4 + 1 + bytes)
645       return -1;
646     tmp = packet->packet[4];
647     /* utf-8 prefix */
648     len = 0;
649     while (tmp & 0x80) {
650       len++;
651       tmp <<= 1;
652     }
653     if (len == 2)
654       return -1;
655     if (len == 0)
656       len++;
657     if (packet->bytes < 4 + len + bytes)
658       return -1;
659     if (bytes == 1) {
660       return packet->packet[4 + len] + 1;
661     } else {
662       return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
663     }
664   }
665   return -1;
666 }
667
668 /* fishead */
669
670 static gboolean
671 setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
672 {
673   guint8 *data;
674   gint64 prestime_n, prestime_d;
675   gint64 basetime_n, basetime_d;
676   gint64 basetime;
677
678   data = packet->packet;
679
680   data += 8 + 2 + 2;            /* header + major/minor version */
681
682   prestime_n = (gint64) GST_READ_UINT64_LE (data);
683   data += 8;
684   prestime_d = (gint64) GST_READ_UINT64_LE (data);
685   data += 8;
686   basetime_n = (gint64) GST_READ_UINT64_LE (data);
687   data += 8;
688   basetime_d = (gint64) GST_READ_UINT64_LE (data);
689   data += 8;
690
691   /* FIXME: we don't use basetime anywhere in the demuxer! */
692   basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
693   GST_INFO ("skeleton fishead parsed (basetime: %" GST_TIME_FORMAT ")",
694       GST_TIME_ARGS (basetime));
695
696   return TRUE;
697 }
698
699 gboolean
700 gst_ogg_map_add_fisbone (GstOggStream * pad,
701     const guint8 * data, guint size, GstClockTime * p_start_time,
702     guint32 * p_preroll)
703 {
704   GstClockTime start_time;
705   gint64 start_granule;
706   guint32 preroll;
707
708   if (size < SKELETON_FISBONE_MIN_SIZE || memcmp (data, "fisbone\0", 8) != 0) {
709     GST_WARNING ("invalid fisbone packet, ignoring");
710     return FALSE;
711   }
712
713   if (pad->have_fisbone) {
714     GST_DEBUG ("already have fisbone, ignoring second one");
715     return FALSE;
716   }
717
718   /* skip "fisbone\0" + headers offset + serialno + num headers */
719   data += 8 + 4 + 4 + 4;
720
721   pad->have_fisbone = TRUE;
722
723   /* we just overwrite whatever was set before by the format-specific setup */
724   pad->granulerate_n = GST_READ_UINT64_LE (data);
725   pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
726
727   start_granule = GST_READ_UINT64_LE (data + 16);
728   preroll = GST_READ_UINT32_LE (data + 24);
729   pad->granuleshift = GST_READ_UINT8 (data + 28);
730
731   start_time = granulepos_to_granule_default (pad, start_granule);
732
733   if (p_start_time)
734     *p_start_time = start_time;
735
736   if (p_preroll)
737     *p_preroll = preroll;
738
739   return TRUE;
740 }
741
742 /* Do we need these for something?
743  * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
744  * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
745  * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
746  * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
747  * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
748  * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
749  */
750
751 static gboolean
752 is_header_ogm (GstOggStream * pad, ogg_packet * packet)
753 {
754   if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
755     return TRUE;
756
757   return FALSE;
758 }
759
760 static gint64
761 packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
762 {
763   const guint8 *data;
764   int samples;
765   int offset;
766   int n;
767
768   data = packet->packet;
769   offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
770
771   if (offset > packet->bytes) {
772     GST_ERROR ("buffer too small");
773     return -1;
774   }
775
776   samples = 0;
777   for (n = offset - 1; n > 0; n--) {
778     samples = (samples << 8) | data[n];
779   }
780
781   return samples;
782 }
783
784 static gboolean
785 setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
786 {
787   guint8 *data = packet->packet;
788   guint32 fourcc;
789
790   pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
791   pad->granulerate_d = 1;
792
793   fourcc = GST_READ_UINT32_LE (data + 9);
794   GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
795
796   pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
797
798   GST_LOG ("sample rate: %d", pad->granulerate_n);
799   if (pad->granulerate_n == 0)
800     return FALSE;
801
802   if (pad->caps) {
803     gst_caps_set_simple (pad->caps,
804         "rate", G_TYPE_INT, pad->granulerate_n, NULL);
805   } else {
806     pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
807         "fourcc", GST_TYPE_FOURCC, fourcc,
808         "rate", G_TYPE_INT, pad->granulerate_n, NULL);
809   }
810
811   pad->n_header_packets = 1;
812   pad->is_ogm = TRUE;
813
814   return TRUE;
815 }
816
817 static gboolean
818 setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
819 {
820   guint8 *data = packet->packet;
821   guint32 fourcc;
822   int width, height;
823   gint64 time_unit;
824
825   GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
826   GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
827
828   pad->granulerate_n = 10000000;
829   time_unit = GST_READ_UINT64_LE (data + 17);
830   if (time_unit > G_MAXINT || time_unit < G_MININT) {
831     GST_WARNING ("timeunit is out of range");
832   }
833   pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
834
835   GST_LOG ("fps = %d/%d = %.3f",
836       pad->granulerate_n, pad->granulerate_d,
837       (double) pad->granulerate_n / pad->granulerate_d);
838
839   fourcc = GST_READ_UINT32_LE (data + 9);
840   width = GST_READ_UINT32_LE (data + 45);
841   height = GST_READ_UINT32_LE (data + 49);
842   GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
843
844   pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
845
846   if (pad->caps == NULL) {
847     pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
848         "fourcc", GST_TYPE_FOURCC, fourcc,
849         "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
850         pad->granulerate_d, NULL);
851   } else {
852     gst_caps_set_simple (pad->caps,
853         "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
854         pad->granulerate_d,
855         "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
856   }
857   GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
858
859   pad->n_header_packets = 1;
860   pad->frame_size = 1;
861   pad->is_ogm = TRUE;
862
863   return TRUE;
864 }
865
866 static gboolean
867 setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
868 {
869   guint8 *data = packet->packet;
870   gint64 time_unit;
871
872   pad->granulerate_n = 10000000;
873   time_unit = GST_READ_UINT64_LE (data + 17);
874   if (time_unit > G_MAXINT || time_unit < G_MININT) {
875     GST_WARNING ("timeunit is out of range");
876   }
877   pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
878
879   GST_LOG ("fps = %d/%d = %.3f",
880       pad->granulerate_n, pad->granulerate_d,
881       (double) pad->granulerate_n / pad->granulerate_d);
882
883   if (pad->granulerate_d <= 0)
884     return FALSE;
885
886   pad->caps = gst_caps_new_simple ("text/plain", NULL);
887
888   pad->n_header_packets = 1;
889   pad->is_ogm = TRUE;
890   pad->is_ogm_text = TRUE;
891
892   return TRUE;
893 }
894
895 /* PCM */
896
897 #define OGGPCM_FMT_S8 0x00000000        /* Signed integer 8 bit */
898 #define OGGPCM_FMT_U8 0x00000001        /* Unsigned integer 8 bit */
899 #define OGGPCM_FMT_S16_LE 0x00000002    /* Signed integer 16 bit little endian */
900 #define OGGPCM_FMT_S16_BE 0x00000003    /* Signed integer 16 bit big endian */
901 #define OGGPCM_FMT_S24_LE 0x00000004    /* Signed integer 24 bit little endian */
902 #define OGGPCM_FMT_S24_BE 0x00000005    /* Signed integer 24 bit big endian */
903 #define OGGPCM_FMT_S32_LE 0x00000006    /* Signed integer 32 bit little endian */
904 #define OGGPCM_FMT_S32_BE 0x00000007    /* Signed integer 32 bit big endian */
905
906 #define OGGPCM_FMT_ULAW 0x00000010      /* G.711 u-law encoding (8 bit) */
907 #define OGGPCM_FMT_ALAW 0x00000011      /* G.711 A-law encoding (8 bit) */
908
909 #define OGGPCM_FMT_FLT32_LE 0x00000020  /* IEEE Float [-1,1] 32 bit little endian */
910 #define OGGPCM_FMT_FLT32_BE 0x00000021  /* IEEE Float [-1,1] 32 bit big endian */
911 #define OGGPCM_FMT_FLT64_LE 0x00000022  /* IEEE Float [-1,1] 64 bit little endian */
912 #define OGGPCM_FMT_FLT64_BE 0x00000023  /* IEEE Float [-1,1] 64 bit big endian */
913
914
915 static gboolean
916 setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
917 {
918   guint8 *data = packet->packet;
919   int format;
920   int channels;
921   GstCaps *caps;
922
923   pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
924   pad->granulerate_d = 1;
925   GST_LOG ("sample rate: %d", pad->granulerate_n);
926
927   format = GST_READ_UINT32_LE (data + 12);
928   channels = GST_READ_UINT8 (data + 21);
929
930   pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
931
932   if (pad->granulerate_n == 0)
933     return FALSE;
934
935   switch (format) {
936     case OGGPCM_FMT_S8:
937       caps = gst_caps_new_simple ("audio/x-raw-int",
938           "depth", G_TYPE_INT, 8,
939           "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
940       break;
941     case OGGPCM_FMT_U8:
942       caps = gst_caps_new_simple ("audio/x-raw-int",
943           "depth", G_TYPE_INT, 8,
944           "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
945       break;
946     case OGGPCM_FMT_S16_LE:
947       caps = gst_caps_new_simple ("audio/x-raw-int",
948           "depth", G_TYPE_INT, 16,
949           "width", G_TYPE_INT, 16,
950           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
951           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
952       break;
953     case OGGPCM_FMT_S16_BE:
954       caps = gst_caps_new_simple ("audio/x-raw-int",
955           "depth", G_TYPE_INT, 16,
956           "width", G_TYPE_INT, 16,
957           "endianness", G_TYPE_INT, G_BIG_ENDIAN,
958           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
959       break;
960     case OGGPCM_FMT_S24_LE:
961       caps = gst_caps_new_simple ("audio/x-raw-int",
962           "depth", G_TYPE_INT, 24,
963           "width", G_TYPE_INT, 24,
964           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
965           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
966       break;
967     case OGGPCM_FMT_S24_BE:
968       caps = gst_caps_new_simple ("audio/x-raw-int",
969           "depth", G_TYPE_INT, 24,
970           "width", G_TYPE_INT, 24,
971           "endianness", G_TYPE_INT, G_BIG_ENDIAN,
972           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
973       break;
974     case OGGPCM_FMT_S32_LE:
975       caps = gst_caps_new_simple ("audio/x-raw-int",
976           "depth", G_TYPE_INT, 32,
977           "width", G_TYPE_INT, 32,
978           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
979           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
980       break;
981     case OGGPCM_FMT_S32_BE:
982       caps = gst_caps_new_simple ("audio/x-raw-int",
983           "depth", G_TYPE_INT, 32,
984           "width", G_TYPE_INT, 32,
985           "endianness", G_TYPE_INT, G_BIG_ENDIAN,
986           "signed", G_TYPE_BOOLEAN, TRUE, NULL);
987       break;
988     case OGGPCM_FMT_ULAW:
989       caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
990       break;
991     case OGGPCM_FMT_ALAW:
992       caps = gst_caps_new_simple ("audio/x-alaw", NULL);
993       break;
994     case OGGPCM_FMT_FLT32_LE:
995       caps = gst_caps_new_simple ("audio/x-raw-float",
996           "width", G_TYPE_INT, 32,
997           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
998       break;
999     case OGGPCM_FMT_FLT32_BE:
1000       caps = gst_caps_new_simple ("audio/x-raw-float",
1001           "width", G_TYPE_INT, 32,
1002           "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1003       break;
1004     case OGGPCM_FMT_FLT64_LE:
1005       caps = gst_caps_new_simple ("audio/x-raw-float",
1006           "width", G_TYPE_INT, 64,
1007           "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
1008       break;
1009     case OGGPCM_FMT_FLT64_BE:
1010       caps = gst_caps_new_simple ("audio/x-raw-float",
1011           "width", G_TYPE_INT, 64,
1012           "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
1013       break;
1014     default:
1015       return FALSE;
1016   }
1017
1018   gst_caps_set_simple (caps, "audio/x-raw-int",
1019       "rate", G_TYPE_INT, pad->granulerate_n,
1020       "channels", G_TYPE_INT, channels, NULL);
1021   pad->caps = caps;
1022
1023   return TRUE;
1024 }
1025
1026 /* cmml */
1027
1028 static gboolean
1029 setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
1030 {
1031   guint8 *data = packet->packet;
1032
1033   pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
1034   pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
1035   pad->granuleshift = data[28];
1036   GST_LOG ("sample rate: %d", pad->granulerate_n);
1037
1038   pad->n_header_packets = 3;
1039
1040   if (pad->granulerate_n == 0)
1041     return FALSE;
1042
1043   data += 4 + (4 + 4 + 4);
1044   GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
1045   GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
1046
1047   pad->caps = gst_caps_new_simple ("text/x-cmml", NULL);
1048
1049   return TRUE;
1050 }
1051
1052 /* celt */
1053
1054 static gboolean
1055 setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
1056 {
1057   guint8 *data = packet->packet;
1058
1059   pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
1060   pad->granulerate_d = 1;
1061   pad->granuleshift = 0;
1062   GST_LOG ("sample rate: %d", pad->granulerate_n);
1063
1064   pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
1065   pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
1066
1067   if (pad->granulerate_n == 0)
1068     return FALSE;
1069
1070   pad->caps = gst_caps_new_simple ("audio/x-celt",
1071       "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1072
1073   return TRUE;
1074 }
1075
1076 /* kate */
1077
1078 static gboolean
1079 setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
1080 {
1081   guint8 *data = packet->packet;
1082
1083   pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
1084   pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
1085   pad->granuleshift = GST_READ_UINT8 (data + 15);
1086   GST_LOG ("sample rate: %d", pad->granulerate_n);
1087
1088   pad->n_header_packets = GST_READ_UINT8 (data + 11);
1089
1090   if (pad->granulerate_n == 0)
1091     return FALSE;
1092
1093   pad->caps = gst_caps_new_simple ("audio/x-kate",
1094       "rate", G_TYPE_INT, pad->granulerate_n, NULL);
1095
1096   return TRUE;
1097 }
1098
1099
1100 /* *INDENT-OFF* */
1101 /* indent hates our freedoms */
1102 static const GstOggMap mappers[] = {
1103   {
1104     "\200theora", 7, 42,
1105     "video/x-theora",
1106     setup_theora_mapper,
1107     granulepos_to_granule_theora,
1108     granule_to_granulepos_default,
1109     is_keyframe_theora,
1110     is_header_theora,
1111     packet_duration_constant
1112   },
1113   {
1114     "\001vorbis", 7, 22,
1115     "audio/x-vorbis",
1116     setup_vorbis_mapper,
1117     granulepos_to_granule_default,
1118     granule_to_granulepos_default,
1119     is_keyframe_true,
1120     is_header_vorbis,
1121     packet_duration_vorbis
1122   },
1123   {
1124     "Speex", 5, 80,
1125     "audio/x-speex",
1126     setup_speex_mapper,
1127     granulepos_to_granule_default,
1128     granule_to_granulepos_default,
1129     is_keyframe_true,
1130     is_header_count,
1131     packet_duration_constant
1132   },
1133   {
1134     "PCM     ", 8, 0,
1135     "audio/x-raw-int",
1136     setup_pcm_mapper,
1137     NULL,
1138     NULL,
1139     NULL,
1140     is_header_count,
1141     NULL
1142   },
1143   {
1144     "CMML\0\0\0\0", 8, 0,
1145     "text/x-cmml",
1146     setup_cmml_mapper,
1147     NULL,
1148     NULL,
1149     NULL,
1150     is_header_count,
1151     NULL
1152   },
1153   {
1154     "Annodex", 7, 0,
1155     "application/x-annodex",
1156     setup_fishead_mapper,
1157     granulepos_to_granule_default,
1158     granule_to_granulepos_default,
1159     NULL,
1160     is_header_count,
1161     NULL
1162   },
1163   {
1164     "fishead", 7, 64,
1165     "application/octet-stream",
1166     setup_fishead_mapper,
1167     NULL,
1168     NULL,
1169     NULL,
1170     is_header_true,
1171     NULL
1172   },
1173   {
1174     "fLaC", 4, 0,
1175     "audio/x-flac",
1176     setup_fLaC_mapper,
1177     granulepos_to_granule_default,
1178     granule_to_granulepos_default,
1179     is_keyframe_true,
1180     is_header_fLaC,
1181     NULL
1182   },
1183   {
1184     "\177FLAC", 5, 36,
1185     "audio/x-flac",
1186     setup_flac_mapper,
1187     granulepos_to_granule_default,
1188     granule_to_granulepos_default,
1189     is_keyframe_true,
1190     is_header_count,
1191     packet_duration_flac
1192   },
1193   {
1194     "AnxData", 7, 0,
1195     "application/octet-stream",
1196     NULL,
1197     NULL,
1198     NULL,
1199     NULL,
1200     NULL,
1201   },
1202   {
1203     "CELT    ", 8, 0,
1204     "audio/x-celt",
1205     setup_celt_mapper,
1206     granulepos_to_granule_default,
1207     granule_to_granulepos_default,
1208     NULL,
1209     is_header_count,
1210     packet_duration_constant
1211   },
1212   {
1213     "\200kate\0\0\0", 8, 0,
1214     "text/x-kate",
1215     setup_kate_mapper,
1216     NULL,
1217     NULL,
1218     NULL,
1219     is_header_count,
1220     NULL
1221   },
1222   {
1223     "BBCD\0", 5, 13,
1224     "video/x-dirac",
1225     setup_dirac_mapper,
1226     granulepos_to_granule_dirac,
1227     granule_to_granulepos_dirac,
1228     is_keyframe_dirac,
1229     is_header_count,
1230     packet_duration_constant
1231   },
1232   {
1233     "\001audio\0\0\0", 9, 53,
1234     "application/x-ogm-audio",
1235     setup_ogmaudio_mapper,
1236     granulepos_to_granule_default,
1237     granule_to_granulepos_default,
1238     is_keyframe_true,
1239     is_header_ogm,
1240     packet_duration_ogm
1241   },
1242   {
1243     "\001video\0\0\0", 9, 53,
1244     "application/x-ogm-video",
1245     setup_ogmvideo_mapper,
1246     granulepos_to_granule_default,
1247     granule_to_granulepos_default,
1248     NULL,
1249     is_header_ogm,
1250     packet_duration_constant
1251   },
1252   {
1253     "\001text\0\0\0", 9, 9,
1254     "application/x-ogm-text",
1255     setup_ogmtext_mapper,
1256     granulepos_to_granule_default,
1257     granule_to_granulepos_default,
1258     is_keyframe_true,
1259     is_header_ogm,
1260     packet_duration_ogm
1261   }
1262 };
1263 /* *INDENT-ON* */
1264
1265 gboolean
1266 gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
1267 {
1268   int i;
1269   gboolean ret;
1270
1271   for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
1272     if (packet->bytes >= mappers[i].min_packet_size &&
1273         packet->bytes >= mappers[i].id_length &&
1274         memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
1275       ret = mappers[i].setup_func (pad, packet);
1276       if (ret) {
1277         GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
1278         pad->map = i;
1279         return TRUE;
1280       } else {
1281         GST_WARNING ("mapper '%s' did not accept setup header",
1282             mappers[i].media_type);
1283       }
1284     }
1285   }
1286
1287   return FALSE;
1288 }