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, ×tamp_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