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