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