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