|
1 /* GStreamer |
|
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
|
3 * Copyright (C) <2003> David A. Schleef <ds@schleef.org> |
|
4 * Copyright (C) <2006> Wim Taymans <wim@fluendo.com> |
|
5 * Copyright (C) <2007> Julien Moutte <julien@fluendo.com> |
|
6 * |
|
7 * This library is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU Library General Public |
|
9 * License as published by the Free Software Foundation; either |
|
10 * version 2 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This library is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 * Library General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU Library General Public |
|
18 * License along with this library; if not, write to the |
|
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|
20 * Boston, MA 02111-1307, USA. |
|
21 */ |
|
22 |
|
23 /** |
|
24 * SECTION:element-qtdemux |
|
25 * |
|
26 * Demuxes a .mov file into raw or compressed audio and/or video streams. |
|
27 * |
|
28 * This element supports both push and pull-based scheduling, depending on the |
|
29 * capabilities of the upstream elements. |
|
30 * |
|
31 * <refsect2> |
|
32 * <title>Example launch line</title> |
|
33 * |[ |
|
34 * gst-launch filesrc location=test.mov ! qtdemux name=demux demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink |
|
35 * ]| Play (parse and decode) a .mov file and try to output it to |
|
36 * an automatically detected soundcard and videosink. If the MOV file contains |
|
37 * compressed audio or video data, this will only work if you have the |
|
38 * right decoder elements/plugins installed. |
|
39 * </refsect2> |
|
40 * |
|
41 * Last reviewed on 2006-12-29 (0.10.5) |
|
42 */ |
|
43 |
|
44 #ifdef HAVE_CONFIG_H |
|
45 #include "config.h" |
|
46 #endif |
|
47 |
|
48 #include "gst/gst-i18n-plugin.h" |
|
49 |
|
50 #include <gst/tag/tag.h> |
|
51 |
|
52 #include "qtdemux_types.h" |
|
53 #include "qtdemux_dump.h" |
|
54 #include "qtdemux_fourcc.h" |
|
55 #include "qtdemux.h" |
|
56 #include "qtpalette.h" |
|
57 |
|
58 #include <stdlib.h> |
|
59 #include <string.h> |
|
60 |
|
61 #ifdef HAVE_ZLIB |
|
62 # include <zlib.h> |
|
63 #endif |
|
64 |
|
65 /* max. size considered 'sane' for non-mdat atoms */ |
|
66 #define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024) |
|
67 |
|
68 GST_DEBUG_CATEGORY (qtdemux_debug); |
|
69 |
|
70 /*typedef struct _QtNode QtNode; */ |
|
71 typedef struct _QtDemuxSegment QtDemuxSegment; |
|
72 typedef struct _QtDemuxSample QtDemuxSample; |
|
73 |
|
74 /*struct _QtNode |
|
75 { |
|
76 guint32 type; |
|
77 guint8 *data; |
|
78 gint len; |
|
79 };*/ |
|
80 |
|
81 struct _QtDemuxSample |
|
82 { |
|
83 guint32 size; |
|
84 guint64 offset; |
|
85 GstClockTimeDiff pts_offset; /* Add this value to timestamp to get the pts */ |
|
86 guint64 timestamp; /* In GstClockTime */ |
|
87 guint64 duration; /* in GstClockTime */ |
|
88 gboolean keyframe; /* TRUE when this packet is a keyframe */ |
|
89 }; |
|
90 |
|
91 /* |
|
92 * Quicktime has tracks and segments. A track is a continuous piece of |
|
93 * multimedia content. The track is not always played from start to finish but |
|
94 * instead, pieces of the track are 'cut out' and played in sequence. This is |
|
95 * what the segments do. |
|
96 * |
|
97 * Inside the track we have keyframes (K) and delta frames. The track has its |
|
98 * own timing, which starts from 0 and extends to end. The position in the track |
|
99 * is called the media_time. |
|
100 * |
|
101 * The segments now describe the pieces that should be played from this track |
|
102 * and are basically tupples of media_time/duration/rate entries. We can have |
|
103 * multiple segments and they are all played after one another. An example: |
|
104 * |
|
105 * segment 1: media_time: 1 second, duration: 1 second, rate 1 |
|
106 * segment 2: media_time: 3 second, duration: 2 second, rate 2 |
|
107 * |
|
108 * To correctly play back this track, one must play: 1 second of media starting |
|
109 * from media_time 1 followed by 2 seconds of media starting from media_time 3 |
|
110 * at a rate of 2. |
|
111 * |
|
112 * Each of the segments will be played at a specific time, the first segment at |
|
113 * time 0, the second one after the duration of the first one, etc.. Note that |
|
114 * the time in resulting playback is not identical to the media_time of the |
|
115 * track anymore. |
|
116 * |
|
117 * Visually, assuming the track has 4 second of media_time: |
|
118 * |
|
119 * (a) (b) (c) (d) |
|
120 * .-----------------------------------------------------------. |
|
121 * track: | K.....K.........K........K.......K.......K...........K... | |
|
122 * '-----------------------------------------------------------' |
|
123 * 0 1 2 3 4 |
|
124 * .------------^ ^ .----------^ ^ |
|
125 * / .-------------' / .------------------' |
|
126 * / / .-----' / |
|
127 * .--------------. .--------------. |
|
128 * | segment 1 | | segment 2 | |
|
129 * '--------------' '--------------' |
|
130 * |
|
131 * The challenge here is to cut out the right pieces of the track for each of |
|
132 * the playback segments. This fortunatly can easily be done with the SEGMENT |
|
133 * events of gstreamer. |
|
134 * |
|
135 * For playback of segment 1, we need to provide the decoder with the keyframe |
|
136 * (a), in the above figure, but we must instruct it only to output the decoded |
|
137 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time |
|
138 * position set to the time of the segment: 0. |
|
139 * |
|
140 * We then proceed to push data from keyframe (a) to frame (b). The decoder |
|
141 * decodes but clips all before media_time 1. |
|
142 * |
|
143 * After finishing a segment, we push out a new SEGMENT event with the clipping |
|
144 * boundaries of the new data. |
|
145 * |
|
146 * This is a good usecase for the GStreamer accumulated SEGMENT events. |
|
147 */ |
|
148 |
|
149 struct _QtDemuxSegment |
|
150 { |
|
151 /* global time and duration, all gst time */ |
|
152 guint64 time; |
|
153 guint64 stop_time; |
|
154 guint64 duration; |
|
155 /* media time of trak, all gst time */ |
|
156 guint64 media_start; |
|
157 guint64 media_stop; |
|
158 gdouble rate; |
|
159 }; |
|
160 |
|
161 struct _QtDemuxStream |
|
162 { |
|
163 GstPad *pad; |
|
164 |
|
165 /* stream type */ |
|
166 guint32 subtype; |
|
167 GstCaps *caps; |
|
168 guint32 fourcc; |
|
169 |
|
170 /* duration/scale */ |
|
171 guint64 duration; /* in timescale */ |
|
172 guint32 timescale; |
|
173 |
|
174 /* our samples */ |
|
175 guint32 n_samples; |
|
176 QtDemuxSample *samples; |
|
177 gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */ |
|
178 guint32 min_duration; /* duration in timescale of first sample, used for figuring out |
|
179 the framerate, in timescale units */ |
|
180 |
|
181 /* if we use chunks or samples */ |
|
182 gboolean sampled; |
|
183 guint padding; |
|
184 |
|
185 /* video info */ |
|
186 gint width; |
|
187 gint height; |
|
188 /* aspect ratio */ |
|
189 gint display_width; |
|
190 gint display_height; |
|
191 gint par_w; |
|
192 gint par_h; |
|
193 /* Numerator/denominator framerate */ |
|
194 gint fps_n; |
|
195 gint fps_d; |
|
196 guint16 bits_per_sample; |
|
197 guint16 color_table_id; |
|
198 |
|
199 /* audio info */ |
|
200 gdouble rate; |
|
201 gint n_channels; |
|
202 guint samples_per_packet; |
|
203 guint samples_per_frame; |
|
204 guint bytes_per_packet; |
|
205 guint bytes_per_sample; |
|
206 guint bytes_per_frame; |
|
207 guint compression; |
|
208 |
|
209 /* when a discontinuity is pending */ |
|
210 gboolean discont; |
|
211 |
|
212 /* list of buffers to push first */ |
|
213 GSList *buffers; |
|
214 |
|
215 /* if we need to clip this buffer. This is only needed for uncompressed |
|
216 * data */ |
|
217 gboolean need_clip; |
|
218 |
|
219 /* current position */ |
|
220 guint32 segment_index; |
|
221 guint32 sample_index; |
|
222 guint64 time_position; /* in gst time */ |
|
223 |
|
224 /* the Gst segment we are processing out, used for clipping */ |
|
225 GstSegment segment; |
|
226 |
|
227 /* last GstFlowReturn */ |
|
228 GstFlowReturn last_ret; |
|
229 |
|
230 /* quicktime segments */ |
|
231 guint32 n_segments; |
|
232 QtDemuxSegment *segments; |
|
233 guint32 from_sample; |
|
234 guint32 to_sample; |
|
235 |
|
236 gboolean sent_eos; |
|
237 }; |
|
238 |
|
239 enum QtDemuxState |
|
240 { |
|
241 QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */ |
|
242 QTDEMUX_STATE_HEADER, /* Parsing the header */ |
|
243 QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */ |
|
244 QTDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */ |
|
245 }; |
|
246 |
|
247 static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc); |
|
248 static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc); |
|
249 |
|
250 static const GstElementDetails gst_qtdemux_details = |
|
251 GST_ELEMENT_DETAILS ("QuickTime demuxer", |
|
252 "Codec/Demuxer", |
|
253 "Demultiplex a QuickTime file into audio and video streams", |
|
254 "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>"); |
|
255 |
|
256 static GstStaticPadTemplate gst_qtdemux_sink_template = |
|
257 GST_STATIC_PAD_TEMPLATE ("sink", |
|
258 GST_PAD_SINK, |
|
259 GST_PAD_ALWAYS, |
|
260 GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; " |
|
261 "application/x-3gp") |
|
262 ); |
|
263 |
|
264 static GstStaticPadTemplate gst_qtdemux_videosrc_template = |
|
265 GST_STATIC_PAD_TEMPLATE ("video_%02d", |
|
266 GST_PAD_SRC, |
|
267 GST_PAD_SOMETIMES, |
|
268 GST_STATIC_CAPS_ANY); |
|
269 |
|
270 static GstStaticPadTemplate gst_qtdemux_audiosrc_template = |
|
271 GST_STATIC_PAD_TEMPLATE ("audio_%02d", |
|
272 GST_PAD_SRC, |
|
273 GST_PAD_SOMETIMES, |
|
274 GST_STATIC_CAPS_ANY); |
|
275 |
|
276 static GstStaticPadTemplate gst_qtdemux_subpsrc_template = |
|
277 GST_STATIC_PAD_TEMPLATE ("subp_%02d", |
|
278 GST_PAD_SRC, |
|
279 GST_PAD_SOMETIMES, |
|
280 GST_STATIC_CAPS_ANY); |
|
281 |
|
282 static GstElementClass *parent_class = NULL; |
|
283 |
|
284 static void gst_qtdemux_class_init (GstQTDemuxClass * klass); |
|
285 static void gst_qtdemux_base_init (GstQTDemuxClass * klass); |
|
286 static void gst_qtdemux_init (GstQTDemux * quicktime_demux); |
|
287 static void gst_qtdemux_dispose (GObject * object); |
|
288 |
|
289 static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element, |
|
290 GstStateChange transition); |
|
291 static gboolean qtdemux_sink_activate (GstPad * sinkpad); |
|
292 static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active); |
|
293 static gboolean qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active); |
|
294 |
|
295 static void gst_qtdemux_loop (GstPad * pad); |
|
296 static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf); |
|
297 static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstEvent * event); |
|
298 |
|
299 static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, |
|
300 int length); |
|
301 static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, |
|
302 const guint8 * buffer, int length); |
|
303 static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux); |
|
304 |
|
305 static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux, |
|
306 QtDemuxStream * stream, GNode * esds, GstTagList * list); |
|
307 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux, |
|
308 QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data, |
|
309 gchar ** codec_name); |
|
310 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux, |
|
311 QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len, |
|
312 gchar ** codec_name); |
|
313 static GstCaps *qtdemux_subp_caps (GstQTDemux * qtdemux, |
|
314 QtDemuxStream * stream, guint32 fourcc, const guint8 * data, |
|
315 gchar ** codec_name); |
|
316 |
|
317 GType |
|
318 gst_qtdemux_get_type (void) |
|
319 { |
|
320 static GType qtdemux_type = 0; |
|
321 |
|
322 if (G_UNLIKELY (!qtdemux_type)) { |
|
323 static const GTypeInfo qtdemux_info = { |
|
324 sizeof (GstQTDemuxClass), |
|
325 (GBaseInitFunc) gst_qtdemux_base_init, NULL, |
|
326 (GClassInitFunc) gst_qtdemux_class_init, |
|
327 NULL, NULL, sizeof (GstQTDemux), 0, |
|
328 (GInstanceInitFunc) gst_qtdemux_init, |
|
329 }; |
|
330 |
|
331 qtdemux_type = |
|
332 g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info, |
|
333 0); |
|
334 } |
|
335 return qtdemux_type; |
|
336 } |
|
337 |
|
338 static void |
|
339 gst_qtdemux_base_init (GstQTDemuxClass * klass) |
|
340 { |
|
341 GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
|
342 |
|
343 gst_element_class_add_pad_template (element_class, |
|
344 gst_static_pad_template_get (&gst_qtdemux_sink_template)); |
|
345 gst_element_class_add_pad_template (element_class, |
|
346 gst_static_pad_template_get (&gst_qtdemux_videosrc_template)); |
|
347 gst_element_class_add_pad_template (element_class, |
|
348 gst_static_pad_template_get (&gst_qtdemux_audiosrc_template)); |
|
349 gst_element_class_set_details (element_class, &gst_qtdemux_details); |
|
350 |
|
351 GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin"); |
|
352 } |
|
353 |
|
354 static void |
|
355 gst_qtdemux_class_init (GstQTDemuxClass * klass) |
|
356 { |
|
357 GObjectClass *gobject_class; |
|
358 GstElementClass *gstelement_class; |
|
359 |
|
360 gobject_class = (GObjectClass *) klass; |
|
361 gstelement_class = (GstElementClass *) klass; |
|
362 |
|
363 parent_class = g_type_class_peek_parent (klass); |
|
364 |
|
365 gobject_class->dispose = gst_qtdemux_dispose; |
|
366 |
|
367 gstelement_class->change_state = gst_qtdemux_change_state; |
|
368 } |
|
369 |
|
370 static void |
|
371 gst_qtdemux_init (GstQTDemux * qtdemux) |
|
372 { |
|
373 qtdemux->sinkpad = |
|
374 gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink"); |
|
375 gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate); |
|
376 gst_pad_set_activatepull_function (qtdemux->sinkpad, |
|
377 qtdemux_sink_activate_pull); |
|
378 gst_pad_set_activatepush_function (qtdemux->sinkpad, |
|
379 qtdemux_sink_activate_push); |
|
380 gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain); |
|
381 gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event); |
|
382 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad); |
|
383 |
|
384 qtdemux->state = QTDEMUX_STATE_INITIAL; |
|
385 /* FIXME, use segment last_stop for this */ |
|
386 qtdemux->last_ts = GST_CLOCK_TIME_NONE; |
|
387 qtdemux->pullbased = FALSE; |
|
388 qtdemux->neededbytes = 16; |
|
389 qtdemux->todrop = 0; |
|
390 qtdemux->adapter = gst_adapter_new (); |
|
391 qtdemux->offset = 0; |
|
392 qtdemux->mdatoffset = GST_CLOCK_TIME_NONE; |
|
393 qtdemux->mdatbuffer = NULL; |
|
394 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); |
|
395 } |
|
396 |
|
397 static void |
|
398 gst_qtdemux_dispose (GObject * object) |
|
399 { |
|
400 GstQTDemux *qtdemux = GST_QTDEMUX (object); |
|
401 |
|
402 if (qtdemux->adapter) { |
|
403 g_object_unref (G_OBJECT (qtdemux->adapter)); |
|
404 qtdemux->adapter = NULL; |
|
405 } |
|
406 |
|
407 G_OBJECT_CLASS (parent_class)->dispose (object); |
|
408 } |
|
409 |
|
410 static GstFlowReturn |
|
411 gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size, |
|
412 GstBuffer ** buf) |
|
413 { |
|
414 GstFlowReturn flow; |
|
415 |
|
416 /* Sanity check: catch bogus sizes (fuzzed/broken files) */ |
|
417 if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) { |
|
418 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE, |
|
419 (_("This file is invalid and cannot be played.")), |
|
420 ("atom has bogus size %" G_GUINT64_FORMAT, size)); |
|
421 return GST_FLOW_ERROR; |
|
422 } |
|
423 |
|
424 flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf); |
|
425 |
|
426 if (G_UNLIKELY (flow != GST_FLOW_OK)) |
|
427 return flow; |
|
428 |
|
429 /* Catch short reads - we don't want any partial atoms */ |
|
430 if (G_UNLIKELY (GST_BUFFER_SIZE (*buf) < size)) { |
|
431 GST_WARNING_OBJECT (qtdemux, "short read: %u < %" G_GUINT64_FORMAT, |
|
432 GST_BUFFER_SIZE (*buf), size); |
|
433 gst_buffer_unref (*buf); |
|
434 *buf = NULL; |
|
435 return GST_FLOW_UNEXPECTED; |
|
436 } |
|
437 |
|
438 return flow; |
|
439 } |
|
440 |
|
441 #if 0 |
|
442 static gboolean |
|
443 gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value, |
|
444 GstFormat * dest_format, gint64 * dest_value) |
|
445 { |
|
446 gboolean res = TRUE; |
|
447 QtDemuxStream *stream = gst_pad_get_element_private (pad); |
|
448 |
|
449 if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') && |
|
450 (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) |
|
451 return FALSE; |
|
452 |
|
453 switch (src_format) { |
|
454 case GST_FORMAT_TIME: |
|
455 switch (*dest_format) { |
|
456 case GST_FORMAT_BYTES: |
|
457 *dest_value = src_value * 1; /* FIXME */ |
|
458 break; |
|
459 case GST_FORMAT_DEFAULT: |
|
460 *dest_value = src_value * 1; /* FIXME */ |
|
461 break; |
|
462 default: |
|
463 res = FALSE; |
|
464 break; |
|
465 } |
|
466 break; |
|
467 case GST_FORMAT_BYTES: |
|
468 switch (*dest_format) { |
|
469 case GST_FORMAT_TIME: |
|
470 *dest_value = src_value * 1; /* FIXME */ |
|
471 break; |
|
472 default: |
|
473 res = FALSE; |
|
474 break; |
|
475 } |
|
476 break; |
|
477 case GST_FORMAT_DEFAULT: |
|
478 switch (*dest_format) { |
|
479 case GST_FORMAT_TIME: |
|
480 *dest_value = src_value * 1; /* FIXME */ |
|
481 break; |
|
482 default: |
|
483 res = FALSE; |
|
484 break; |
|
485 } |
|
486 break; |
|
487 default: |
|
488 res = FALSE; |
|
489 } |
|
490 |
|
491 return res; |
|
492 } |
|
493 #endif |
|
494 |
|
495 static const GstQueryType * |
|
496 gst_qtdemux_get_src_query_types (GstPad * pad) |
|
497 { |
|
498 static const GstQueryType src_types[] = { |
|
499 GST_QUERY_POSITION, |
|
500 GST_QUERY_DURATION, |
|
501 GST_QUERY_SEEKING, |
|
502 0 |
|
503 }; |
|
504 |
|
505 return src_types; |
|
506 } |
|
507 |
|
508 static gboolean |
|
509 gst_qtdemux_get_duration (GstQTDemux * qtdemux, gint64 * duration) |
|
510 { |
|
511 gboolean res = TRUE; |
|
512 |
|
513 *duration = GST_CLOCK_TIME_NONE; |
|
514 |
|
515 if (qtdemux->duration != 0) { |
|
516 if (qtdemux->duration != G_MAXINT32 && qtdemux->timescale != 0) { |
|
517 *duration = gst_util_uint64_scale (qtdemux->duration, |
|
518 GST_SECOND, qtdemux->timescale); |
|
519 } |
|
520 } |
|
521 return res; |
|
522 } |
|
523 |
|
524 static gboolean |
|
525 gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query) |
|
526 { |
|
527 gboolean res = FALSE; |
|
528 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad)); |
|
529 |
|
530 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query)); |
|
531 |
|
532 switch (GST_QUERY_TYPE (query)) { |
|
533 case GST_QUERY_POSITION: |
|
534 if (GST_CLOCK_TIME_IS_VALID (qtdemux->segment.last_stop)) { |
|
535 gst_query_set_position (query, GST_FORMAT_TIME, |
|
536 qtdemux->segment.last_stop); |
|
537 res = TRUE; |
|
538 } |
|
539 break; |
|
540 case GST_QUERY_DURATION:{ |
|
541 GstFormat fmt; |
|
542 |
|
543 gst_query_parse_duration (query, &fmt, NULL); |
|
544 if (fmt == GST_FORMAT_TIME) { |
|
545 gint64 duration = -1; |
|
546 |
|
547 gst_qtdemux_get_duration (qtdemux, &duration); |
|
548 if (duration > 0) { |
|
549 gst_query_set_duration (query, GST_FORMAT_TIME, duration); |
|
550 res = TRUE; |
|
551 } |
|
552 } |
|
553 break; |
|
554 } |
|
555 case GST_QUERY_SEEKING:{ |
|
556 GstFormat fmt; |
|
557 gboolean seekable; |
|
558 |
|
559 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); |
|
560 if (fmt == GST_FORMAT_TIME) { |
|
561 gint64 duration = -1; |
|
562 |
|
563 gst_qtdemux_get_duration (qtdemux, &duration); |
|
564 seekable = TRUE; |
|
565 if (!qtdemux->pullbased) { |
|
566 GstQuery *q; |
|
567 |
|
568 /* we might be able with help from upstream */ |
|
569 seekable = FALSE; |
|
570 q = gst_query_new_seeking (GST_FORMAT_BYTES); |
|
571 if (gst_pad_peer_query (qtdemux->sinkpad, q)) { |
|
572 gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL); |
|
573 GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable); |
|
574 } |
|
575 gst_query_unref (q); |
|
576 } |
|
577 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration); |
|
578 res = TRUE; |
|
579 } |
|
580 break; |
|
581 } |
|
582 default: |
|
583 res = gst_pad_query_default (pad, query); |
|
584 break; |
|
585 } |
|
586 |
|
587 gst_object_unref (qtdemux); |
|
588 |
|
589 return res; |
|
590 } |
|
591 |
|
592 /* push event on all source pads; takes ownership of the event */ |
|
593 static void |
|
594 gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event) |
|
595 { |
|
596 guint n; |
|
597 |
|
598 GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads", |
|
599 GST_EVENT_TYPE_NAME (event)); |
|
600 |
|
601 for (n = 0; n < qtdemux->n_streams; n++) { |
|
602 GstPad *pad; |
|
603 |
|
604 if ((pad = qtdemux->streams[n]->pad)) |
|
605 gst_pad_push_event (pad, gst_event_ref (event)); |
|
606 } |
|
607 gst_event_unref (event); |
|
608 } |
|
609 |
|
610 /* push a pending newsegment event, if any from the streaming thread */ |
|
611 static void |
|
612 gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux) |
|
613 { |
|
614 if (qtdemux->pending_newsegment) { |
|
615 gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment); |
|
616 qtdemux->pending_newsegment = NULL; |
|
617 } |
|
618 } |
|
619 |
|
620 typedef struct |
|
621 { |
|
622 guint64 media_time; |
|
623 } FindData; |
|
624 |
|
625 static gint |
|
626 find_func (QtDemuxSample * s1, guint64 * media_time, gpointer user_data) |
|
627 { |
|
628 if (s1->timestamp > *media_time) |
|
629 return 1; |
|
630 |
|
631 return -1; |
|
632 } |
|
633 |
|
634 /* find the index of the sample that includes the data for @media_time |
|
635 * |
|
636 * Returns the index of the sample. |
|
637 */ |
|
638 static guint32 |
|
639 gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str, |
|
640 guint64 media_time) |
|
641 { |
|
642 QtDemuxSample *result; |
|
643 guint32 index; |
|
644 |
|
645 result = gst_util_array_binary_search (str->samples, str->n_samples, |
|
646 sizeof (QtDemuxSample), (GCompareDataFunc) find_func, |
|
647 GST_SEARCH_MODE_BEFORE, &media_time, NULL); |
|
648 |
|
649 if (G_LIKELY (result)) |
|
650 index = result - str->samples; |
|
651 else |
|
652 index = 0; |
|
653 |
|
654 return index; |
|
655 } |
|
656 |
|
657 /* find the index of the keyframe needed to decode the sample at @index |
|
658 * of stream @str. |
|
659 * |
|
660 * Returns the index of the keyframe. |
|
661 */ |
|
662 static guint32 |
|
663 gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str, |
|
664 guint32 index) |
|
665 { |
|
666 guint32 new_index = index; |
|
667 |
|
668 if (index >= str->n_samples) { |
|
669 new_index = str->n_samples; |
|
670 goto beach; |
|
671 } |
|
672 |
|
673 /* all keyframes, return index */ |
|
674 if (str->all_keyframe) { |
|
675 new_index = index; |
|
676 goto beach; |
|
677 } |
|
678 |
|
679 /* else go back until we have a keyframe */ |
|
680 while (TRUE) { |
|
681 if (str->samples[new_index].keyframe) |
|
682 break; |
|
683 |
|
684 if (new_index == 0) |
|
685 break; |
|
686 |
|
687 new_index--; |
|
688 } |
|
689 |
|
690 beach: |
|
691 GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u " |
|
692 "gave %u", index, new_index); |
|
693 |
|
694 return new_index; |
|
695 } |
|
696 |
|
697 /* find the segment for @time_position for @stream |
|
698 * |
|
699 * Returns -1 if the segment cannot be found. |
|
700 */ |
|
701 static guint32 |
|
702 gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
703 guint64 time_position) |
|
704 { |
|
705 gint i; |
|
706 guint32 seg_idx; |
|
707 |
|
708 GST_LOG_OBJECT (qtdemux, "finding segment for %" GST_TIME_FORMAT, |
|
709 GST_TIME_ARGS (time_position)); |
|
710 |
|
711 /* find segment corresponding to time_position if we are looking |
|
712 * for a segment. */ |
|
713 seg_idx = -1; |
|
714 for (i = 0; i < stream->n_segments; i++) { |
|
715 QtDemuxSegment *segment = &stream->segments[i]; |
|
716 |
|
717 GST_LOG_OBJECT (qtdemux, |
|
718 "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT, |
|
719 GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time)); |
|
720 |
|
721 /* For the last segment we include stop_time in the last segment */ |
|
722 if (i < stream->n_segments - 1) { |
|
723 if (segment->time <= time_position && time_position < segment->stop_time) { |
|
724 GST_LOG_OBJECT (qtdemux, "segment %d matches", i); |
|
725 seg_idx = i; |
|
726 break; |
|
727 } |
|
728 } else { |
|
729 if (segment->time <= time_position && time_position <= segment->stop_time) { |
|
730 GST_LOG_OBJECT (qtdemux, "segment %d matches", i); |
|
731 seg_idx = i; |
|
732 break; |
|
733 } |
|
734 } |
|
735 } |
|
736 return seg_idx; |
|
737 } |
|
738 |
|
739 /* move the stream @str to the sample position @index. |
|
740 * |
|
741 * Updates @str->sample_index and marks discontinuity if needed. |
|
742 */ |
|
743 static void |
|
744 gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str, |
|
745 guint32 index) |
|
746 { |
|
747 /* no change needed */ |
|
748 if (index == str->sample_index) |
|
749 return; |
|
750 |
|
751 GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index, |
|
752 str->n_samples); |
|
753 |
|
754 /* position changed, we have a discont */ |
|
755 str->sample_index = index; |
|
756 /* Each time we move in the stream we store the position where we are |
|
757 * starting from */ |
|
758 str->from_sample = index; |
|
759 str->discont = TRUE; |
|
760 } |
|
761 |
|
762 static void |
|
763 gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time, |
|
764 gint64 * key_time, gint64 * key_offset) |
|
765 { |
|
766 guint64 min_offset; |
|
767 gint64 min_byte_offset = -1; |
|
768 gint n; |
|
769 |
|
770 min_offset = desired_time; |
|
771 |
|
772 /* for each stream, find the index of the sample in the segment |
|
773 * and move back to the previous keyframe. */ |
|
774 for (n = 0; n < qtdemux->n_streams; n++) { |
|
775 QtDemuxStream *str; |
|
776 guint32 index, kindex; |
|
777 guint32 seg_idx; |
|
778 guint64 media_start; |
|
779 guint64 media_time; |
|
780 guint64 seg_time; |
|
781 QtDemuxSegment *seg; |
|
782 |
|
783 str = qtdemux->streams[n]; |
|
784 |
|
785 seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_time); |
|
786 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx); |
|
787 |
|
788 /* segment not found, continue with normal flow */ |
|
789 if (seg_idx == -1) |
|
790 continue; |
|
791 |
|
792 /* get segment and time in the segment */ |
|
793 seg = &str->segments[seg_idx]; |
|
794 seg_time = desired_time - seg->time; |
|
795 |
|
796 /* get the media time in the segment */ |
|
797 media_start = seg->media_start + seg_time; |
|
798 |
|
799 /* get the index of the sample with media time */ |
|
800 index = gst_qtdemux_find_index (qtdemux, str, media_start); |
|
801 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u", |
|
802 GST_TIME_ARGS (media_start), index); |
|
803 |
|
804 /* find previous keyframe */ |
|
805 kindex = gst_qtdemux_find_keyframe (qtdemux, str, index); |
|
806 |
|
807 /* if the keyframe is at a different position, we need to update the |
|
808 * requested seek time */ |
|
809 if (index != kindex) { |
|
810 index = kindex; |
|
811 |
|
812 /* get timestamp of keyframe */ |
|
813 media_time = str->samples[kindex].timestamp; |
|
814 GST_DEBUG_OBJECT (qtdemux, "keyframe at %u with time %" GST_TIME_FORMAT, |
|
815 kindex, GST_TIME_ARGS (media_time)); |
|
816 |
|
817 /* keyframes in the segment get a chance to change the |
|
818 * desired_offset. keyframes out of the segment are |
|
819 * ignored. */ |
|
820 if (media_time >= seg->media_start) { |
|
821 guint64 seg_time; |
|
822 |
|
823 /* this keyframe is inside the segment, convert back to |
|
824 * segment time */ |
|
825 seg_time = (media_time - seg->media_start) + seg->time; |
|
826 if (seg_time < min_offset) |
|
827 min_offset = seg_time; |
|
828 } |
|
829 } |
|
830 |
|
831 if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset) |
|
832 min_byte_offset = str->samples[index].offset; |
|
833 } |
|
834 |
|
835 if (key_time) |
|
836 *key_time = min_offset; |
|
837 if (key_offset) |
|
838 *key_offset = min_byte_offset; |
|
839 } |
|
840 |
|
841 static gboolean |
|
842 gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format, |
|
843 GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop) |
|
844 { |
|
845 gboolean res; |
|
846 GstFormat fmt; |
|
847 |
|
848 g_return_val_if_fail (format != NULL, FALSE); |
|
849 g_return_val_if_fail (cur != NULL, FALSE); |
|
850 g_return_val_if_fail (stop != NULL, FALSE); |
|
851 |
|
852 if (*format == GST_FORMAT_TIME) |
|
853 return TRUE; |
|
854 |
|
855 fmt = GST_FORMAT_TIME; |
|
856 res = TRUE; |
|
857 if (cur_type != GST_SEEK_TYPE_NONE) |
|
858 res = gst_pad_query_convert (pad, *format, *cur, &fmt, cur); |
|
859 if (res && stop_type != GST_SEEK_TYPE_NONE) |
|
860 res = gst_pad_query_convert (pad, *format, *stop, &fmt, stop); |
|
861 |
|
862 if (res) |
|
863 *format = GST_FORMAT_TIME; |
|
864 |
|
865 return res; |
|
866 } |
|
867 |
|
868 /* perform seek in push based mode: |
|
869 find BYTE position to move to based on time and delegate to upstream |
|
870 */ |
|
871 static gboolean |
|
872 gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) |
|
873 { |
|
874 gdouble rate; |
|
875 GstFormat format; |
|
876 GstSeekFlags flags; |
|
877 GstSeekType cur_type, stop_type; |
|
878 gint64 cur, stop; |
|
879 gboolean res; |
|
880 gint64 byte_cur; |
|
881 |
|
882 GST_DEBUG_OBJECT (qtdemux, "doing push-based seek"); |
|
883 |
|
884 gst_event_parse_seek (event, &rate, &format, &flags, |
|
885 &cur_type, &cur, &stop_type, &stop); |
|
886 |
|
887 if (stop_type != GST_SEEK_TYPE_NONE) |
|
888 goto unsupported_seek; |
|
889 stop = -1; |
|
890 |
|
891 /* only forward streaming and seeking is possible */ |
|
892 if (rate <= 0) |
|
893 goto unsupported_seek; |
|
894 |
|
895 /* convert to TIME if needed and possible */ |
|
896 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur, |
|
897 stop_type, &stop)) |
|
898 goto no_format; |
|
899 |
|
900 /* find reasonable corresponding BYTE position, |
|
901 * also try to mind about keyframes, since we can not go back a bit for them |
|
902 * later on */ |
|
903 gst_qtdemux_adjust_seek (qtdemux, cur, NULL, &byte_cur); |
|
904 |
|
905 if (byte_cur == -1) |
|
906 goto abort_seek; |
|
907 |
|
908 GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, " |
|
909 "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur, |
|
910 stop); |
|
911 /* BYTE seek event */ |
|
912 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur, |
|
913 stop_type, stop); |
|
914 res = gst_pad_push_event (qtdemux->sinkpad, event); |
|
915 |
|
916 return res; |
|
917 |
|
918 /* ERRORS */ |
|
919 abort_seek: |
|
920 { |
|
921 GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, " |
|
922 "seek aborted."); |
|
923 return FALSE; |
|
924 } |
|
925 unsupported_seek: |
|
926 { |
|
927 GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted."); |
|
928 return FALSE; |
|
929 } |
|
930 no_format: |
|
931 { |
|
932 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted."); |
|
933 return FALSE; |
|
934 } |
|
935 } |
|
936 |
|
937 /* perform the seek. |
|
938 * |
|
939 * We set all segment_indexes in the streams to unknown and |
|
940 * adjust the time_position to the desired position. this is enough |
|
941 * to trigger a segment switch in the streaming thread to start |
|
942 * streaming from the desired position. |
|
943 * |
|
944 * Keyframe seeking is a little more complicated when dealing with |
|
945 * segments. Ideally we want to move to the previous keyframe in |
|
946 * the segment but there might not be a keyframe in the segment. In |
|
947 * fact, none of the segments could contain a keyframe. We take a |
|
948 * practical approach: seek to the previous keyframe in the segment, |
|
949 * if there is none, seek to the beginning of the segment. |
|
950 * |
|
951 * Called with STREAM_LOCK |
|
952 */ |
|
953 static gboolean |
|
954 gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment) |
|
955 { |
|
956 gint64 desired_offset; |
|
957 gint n; |
|
958 |
|
959 desired_offset = segment->last_stop; |
|
960 |
|
961 GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT, |
|
962 GST_TIME_ARGS (desired_offset)); |
|
963 |
|
964 if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) { |
|
965 gint64 min_offset; |
|
966 |
|
967 gst_qtdemux_adjust_seek (qtdemux, desired_offset, &min_offset, NULL); |
|
968 GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %" |
|
969 GST_TIME_FORMAT, GST_TIME_ARGS (min_offset)); |
|
970 desired_offset = min_offset; |
|
971 } |
|
972 |
|
973 /* and set all streams to the final position */ |
|
974 for (n = 0; n < qtdemux->n_streams; n++) { |
|
975 QtDemuxStream *stream = qtdemux->streams[n]; |
|
976 |
|
977 stream->time_position = desired_offset; |
|
978 stream->sample_index = -1; |
|
979 stream->segment_index = -1; |
|
980 stream->last_ret = GST_FLOW_OK; |
|
981 stream->sent_eos = FALSE; |
|
982 } |
|
983 segment->last_stop = desired_offset; |
|
984 segment->time = desired_offset; |
|
985 |
|
986 /* we stop at the end */ |
|
987 if (segment->stop == -1) |
|
988 segment->stop = segment->duration; |
|
989 |
|
990 return TRUE; |
|
991 } |
|
992 |
|
993 /* do a seek in pull based mode */ |
|
994 static gboolean |
|
995 gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) |
|
996 { |
|
997 gdouble rate; |
|
998 GstFormat format; |
|
999 GstSeekFlags flags; |
|
1000 GstSeekType cur_type, stop_type; |
|
1001 gint64 cur, stop; |
|
1002 gboolean flush; |
|
1003 gboolean update; |
|
1004 GstSegment seeksegment; |
|
1005 int i; |
|
1006 |
|
1007 if (event) { |
|
1008 GST_DEBUG_OBJECT (qtdemux, "doing seek with event"); |
|
1009 |
|
1010 gst_event_parse_seek (event, &rate, &format, &flags, |
|
1011 &cur_type, &cur, &stop_type, &stop); |
|
1012 |
|
1013 /* we have to have a format as the segment format. Try to convert |
|
1014 * if not. */ |
|
1015 if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur, |
|
1016 stop_type, &stop)) |
|
1017 goto no_format; |
|
1018 |
|
1019 GST_DEBUG_OBJECT (qtdemux, "seek format %s", gst_format_get_name (format)); |
|
1020 } else { |
|
1021 GST_DEBUG_OBJECT (qtdemux, "doing seek without event"); |
|
1022 flags = 0; |
|
1023 } |
|
1024 |
|
1025 flush = flags & GST_SEEK_FLAG_FLUSH; |
|
1026 |
|
1027 /* stop streaming, either by flushing or by pausing the task */ |
|
1028 if (flush) { |
|
1029 /* unlock upstream pull_range */ |
|
1030 gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ()); |
|
1031 /* make sure out loop function exits */ |
|
1032 gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ()); |
|
1033 } else { |
|
1034 /* non flushing seek, pause the task */ |
|
1035 gst_pad_pause_task (qtdemux->sinkpad); |
|
1036 } |
|
1037 |
|
1038 /* wait for streaming to finish */ |
|
1039 GST_PAD_STREAM_LOCK (qtdemux->sinkpad); |
|
1040 |
|
1041 /* copy segment, we need this because we still need the old |
|
1042 * segment when we close the current segment. */ |
|
1043 memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment)); |
|
1044 |
|
1045 if (event) { |
|
1046 /* configure the segment with the seek variables */ |
|
1047 GST_DEBUG_OBJECT (qtdemux, "configuring seek"); |
|
1048 gst_segment_set_seek (&seeksegment, rate, format, flags, |
|
1049 cur_type, cur, stop_type, stop, &update); |
|
1050 } |
|
1051 |
|
1052 /* now do the seek, this actually never returns FALSE */ |
|
1053 gst_qtdemux_perform_seek (qtdemux, &seeksegment); |
|
1054 |
|
1055 /* prepare for streaming again */ |
|
1056 if (flush) { |
|
1057 gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ()); |
|
1058 gst_qtdemux_push_event (qtdemux, gst_event_new_flush_stop ()); |
|
1059 } else if (qtdemux->segment_running) { |
|
1060 /* we are running the current segment and doing a non-flushing seek, |
|
1061 * close the segment first based on the last_stop. */ |
|
1062 GST_DEBUG_OBJECT (qtdemux, "closing running segment %" G_GINT64_FORMAT |
|
1063 " to %" G_GINT64_FORMAT, qtdemux->segment.start, |
|
1064 qtdemux->segment.last_stop); |
|
1065 |
|
1066 if (qtdemux->segment.rate >= 0) { |
|
1067 /* FIXME, rate is the product of the global rate and the (quicktime) |
|
1068 * segment rate. */ |
|
1069 qtdemux->pending_newsegment = gst_event_new_new_segment (TRUE, |
|
1070 qtdemux->segment.rate, qtdemux->segment.format, |
|
1071 qtdemux->segment.start, qtdemux->segment.last_stop, |
|
1072 qtdemux->segment.time); |
|
1073 } else { /* For Reverse Playback */ |
|
1074 guint64 stop; |
|
1075 |
|
1076 if ((stop = qtdemux->segment.stop) == -1) |
|
1077 stop = qtdemux->segment.duration; |
|
1078 /* for reverse playback, we played from stop to last_stop. */ |
|
1079 qtdemux->pending_newsegment = gst_event_new_new_segment (TRUE, |
|
1080 qtdemux->segment.rate, qtdemux->segment.format, |
|
1081 qtdemux->segment.last_stop, stop, qtdemux->segment.last_stop); |
|
1082 } |
|
1083 } |
|
1084 |
|
1085 /* commit the new segment */ |
|
1086 memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment)); |
|
1087 |
|
1088 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) { |
|
1089 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), |
|
1090 gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux), |
|
1091 qtdemux->segment.format, qtdemux->segment.last_stop)); |
|
1092 } |
|
1093 |
|
1094 /* restart streaming, NEWSEGMENT will be sent from the streaming |
|
1095 * thread. */ |
|
1096 qtdemux->segment_running = TRUE; |
|
1097 for (i = 0; i < qtdemux->n_streams; i++) |
|
1098 qtdemux->streams[i]->last_ret = GST_FLOW_OK; |
|
1099 |
|
1100 gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop, |
|
1101 qtdemux->sinkpad); |
|
1102 |
|
1103 GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad); |
|
1104 |
|
1105 return TRUE; |
|
1106 |
|
1107 /* ERRORS */ |
|
1108 no_format: |
|
1109 { |
|
1110 GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted."); |
|
1111 return FALSE; |
|
1112 } |
|
1113 } |
|
1114 |
|
1115 static gboolean |
|
1116 gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event) |
|
1117 { |
|
1118 gboolean res = TRUE; |
|
1119 GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad)); |
|
1120 |
|
1121 switch (GST_EVENT_TYPE (event)) { |
|
1122 case GST_EVENT_SEEK: |
|
1123 if (qtdemux->pullbased) { |
|
1124 res = gst_qtdemux_do_seek (qtdemux, pad, event); |
|
1125 } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams) { |
|
1126 res = gst_qtdemux_do_push_seek (qtdemux, pad, event); |
|
1127 } else { |
|
1128 GST_DEBUG_OBJECT (qtdemux, |
|
1129 "ignoring seek in push mode in current state"); |
|
1130 res = FALSE; |
|
1131 } |
|
1132 gst_event_unref (event); |
|
1133 break; |
|
1134 case GST_EVENT_QOS: |
|
1135 case GST_EVENT_NAVIGATION: |
|
1136 res = FALSE; |
|
1137 gst_event_unref (event); |
|
1138 break; |
|
1139 default: |
|
1140 res = gst_pad_event_default (pad, event); |
|
1141 break; |
|
1142 } |
|
1143 |
|
1144 gst_object_unref (qtdemux); |
|
1145 |
|
1146 return res; |
|
1147 } |
|
1148 |
|
1149 /* stream/index return sample that is min/max w.r.t. byte position, |
|
1150 * time is min/max w.r.t. time of samples, |
|
1151 * the latter need not be time of the former sample */ |
|
1152 static void |
|
1153 gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw, |
|
1154 gboolean set, QtDemuxStream ** _stream, gint * _index, gint64 * _time) |
|
1155 { |
|
1156 gint i, n, index; |
|
1157 gint64 time, min_time; |
|
1158 QtDemuxStream *stream; |
|
1159 |
|
1160 min_time = -1; |
|
1161 stream = NULL; |
|
1162 index = -1; |
|
1163 |
|
1164 for (n = 0; n < qtdemux->n_streams; ++n) { |
|
1165 QtDemuxStream *str; |
|
1166 gint inc; |
|
1167 gboolean set_sample; |
|
1168 |
|
1169 |
|
1170 str = qtdemux->streams[n]; |
|
1171 set_sample = !set; |
|
1172 |
|
1173 if (fw) { |
|
1174 i = 0; |
|
1175 inc = 1; |
|
1176 } else { |
|
1177 i = str->n_samples - 1; |
|
1178 inc = -1; |
|
1179 } |
|
1180 for (; (i >= 0) && (i < str->n_samples); i += inc) { |
|
1181 if (str->samples[i].size && |
|
1182 ((fw && (str->samples[i].offset >= byte_pos)) || |
|
1183 (!fw && |
|
1184 (str->samples[i].offset + str->samples[i].size <= |
|
1185 byte_pos)))) { |
|
1186 /* move stream to first available sample */ |
|
1187 if (set) { |
|
1188 gst_qtdemux_move_stream (qtdemux, str, i); |
|
1189 set_sample = TRUE; |
|
1190 } |
|
1191 /* determine min/max time */ |
|
1192 time = str->samples[i].timestamp + str->samples[i].pts_offset; |
|
1193 if (min_time == -1 || (fw && min_time > time) || |
|
1194 (!fw && min_time < time)) { |
|
1195 min_time = time; |
|
1196 } |
|
1197 /* determine stream with leading sample, to get its position */ |
|
1198 /* only needed in fw case */ |
|
1199 if (fw && (!stream || |
|
1200 str->samples[i].offset < stream->samples[index].offset)) { |
|
1201 stream = str; |
|
1202 index = i; |
|
1203 } |
|
1204 break; |
|
1205 } |
|
1206 } |
|
1207 /* no sample for this stream, mark eos */ |
|
1208 if (!set_sample) |
|
1209 gst_qtdemux_move_stream (qtdemux, str, str->n_samples); |
|
1210 } |
|
1211 |
|
1212 if (_time) |
|
1213 *_time = min_time; |
|
1214 if (_stream) |
|
1215 *_stream = stream; |
|
1216 if (_index) |
|
1217 *_index = index; |
|
1218 } |
|
1219 |
|
1220 static gboolean |
|
1221 gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event) |
|
1222 { |
|
1223 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad)); |
|
1224 gboolean res; |
|
1225 |
|
1226 GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event)); |
|
1227 |
|
1228 switch (GST_EVENT_TYPE (event)) { |
|
1229 case GST_EVENT_NEWSEGMENT: |
|
1230 { |
|
1231 GstFormat format; |
|
1232 gdouble rate, arate; |
|
1233 gint64 start, stop, time, offset = 0; |
|
1234 QtDemuxStream *stream; |
|
1235 gint idx; |
|
1236 gboolean update; |
|
1237 GstSegment segment; |
|
1238 |
|
1239 /* some debug output */ |
|
1240 gst_segment_init (&segment, GST_FORMAT_UNDEFINED); |
|
1241 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, |
|
1242 &start, &stop, &time); |
|
1243 gst_segment_set_newsegment_full (&segment, update, rate, arate, format, |
|
1244 start, stop, time); |
|
1245 GST_DEBUG_OBJECT (demux, |
|
1246 "received format %d newsegment %" GST_SEGMENT_FORMAT, format, |
|
1247 &segment); |
|
1248 |
|
1249 /* chain will send initial newsegment after pads have been added */ |
|
1250 if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) { |
|
1251 GST_DEBUG_OBJECT (demux, "still starting, eating event"); |
|
1252 goto exit; |
|
1253 } |
|
1254 |
|
1255 /* we only expect a BYTE segment, e.g. following a seek */ |
|
1256 if (format == GST_FORMAT_BYTES) { |
|
1257 if (start > 0) { |
|
1258 offset = start; |
|
1259 gst_qtdemux_find_sample (demux, start, TRUE, FALSE, NULL, NULL, |
|
1260 &start); |
|
1261 start = MAX (start, 0); |
|
1262 } |
|
1263 if (stop > 0) { |
|
1264 gst_qtdemux_find_sample (demux, stop, FALSE, FALSE, NULL, NULL, |
|
1265 &stop); |
|
1266 stop = MAX (stop, 0); |
|
1267 } |
|
1268 } else { |
|
1269 GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring"); |
|
1270 goto exit; |
|
1271 } |
|
1272 |
|
1273 /* accept upstream's notion of segment and distribute along */ |
|
1274 gst_segment_set_newsegment_full (&demux->segment, update, rate, arate, |
|
1275 GST_FORMAT_TIME, start, stop, start); |
|
1276 GST_DEBUG_OBJECT (demux, "Pushing newseg update %d, rate %g, " |
|
1277 "applied rate %g, format %d, start %" G_GINT64_FORMAT ", " |
|
1278 "stop %" G_GINT64_FORMAT, update, rate, arate, GST_FORMAT_TIME, |
|
1279 start, stop); |
|
1280 gst_qtdemux_push_event (demux, |
|
1281 gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, |
|
1282 start, stop, start)); |
|
1283 |
|
1284 /* clear leftover in current segment, if any */ |
|
1285 gst_adapter_clear (demux->adapter); |
|
1286 /* set up streaming thread */ |
|
1287 gst_qtdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx, NULL); |
|
1288 demux->offset = offset; |
|
1289 if (stream) { |
|
1290 demux->todrop = stream->samples[idx].offset - offset; |
|
1291 demux->neededbytes = demux->todrop + stream->samples[idx].size; |
|
1292 } else { |
|
1293 /* set up for EOS */ |
|
1294 demux->neededbytes = -1; |
|
1295 demux->todrop = 0; |
|
1296 } |
|
1297 exit: |
|
1298 gst_event_unref (event); |
|
1299 res = TRUE; |
|
1300 goto drop; |
|
1301 break; |
|
1302 } |
|
1303 case GST_EVENT_FLUSH_STOP: |
|
1304 { |
|
1305 gint i; |
|
1306 |
|
1307 /* clean up, force EOS if no more info follows */ |
|
1308 gst_adapter_clear (demux->adapter); |
|
1309 demux->offset = 0; |
|
1310 demux->neededbytes = -1; |
|
1311 /* reset flow return, e.g. following seek */ |
|
1312 for (i = 0; i < demux->n_streams; i++) { |
|
1313 demux->streams[i]->last_ret = GST_FLOW_OK; |
|
1314 demux->streams[i]->sent_eos = FALSE; |
|
1315 } |
|
1316 break; |
|
1317 } |
|
1318 case GST_EVENT_EOS: |
|
1319 /* If we are in push mode, and get an EOS before we've seen any streams, |
|
1320 * then error out - we have nowhere to send the EOS */ |
|
1321 if (!demux->pullbased && demux->n_streams == 0) { |
|
1322 GST_ELEMENT_ERROR (demux, STREAM, DECODE, |
|
1323 (_("This file contains no playable streams.")), |
|
1324 ("no known streams found")); |
|
1325 } |
|
1326 break; |
|
1327 default: |
|
1328 break; |
|
1329 } |
|
1330 |
|
1331 res = gst_pad_event_default (demux->sinkpad, event); |
|
1332 |
|
1333 drop: |
|
1334 return res; |
|
1335 } |
|
1336 |
|
1337 static GstStateChangeReturn |
|
1338 gst_qtdemux_change_state (GstElement * element, GstStateChange transition) |
|
1339 { |
|
1340 GstQTDemux *qtdemux = GST_QTDEMUX (element); |
|
1341 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE; |
|
1342 |
|
1343 switch (transition) { |
|
1344 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
1345 break; |
|
1346 default: |
|
1347 break; |
|
1348 } |
|
1349 |
|
1350 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
|
1351 |
|
1352 switch (transition) { |
|
1353 case GST_STATE_CHANGE_PAUSED_TO_READY:{ |
|
1354 gint n; |
|
1355 |
|
1356 qtdemux->state = QTDEMUX_STATE_INITIAL; |
|
1357 qtdemux->last_ts = GST_CLOCK_TIME_NONE; |
|
1358 qtdemux->neededbytes = 16; |
|
1359 qtdemux->todrop = 0; |
|
1360 qtdemux->pullbased = FALSE; |
|
1361 qtdemux->offset = 0; |
|
1362 qtdemux->mdatoffset = GST_CLOCK_TIME_NONE; |
|
1363 if (qtdemux->mdatbuffer) |
|
1364 gst_buffer_unref (qtdemux->mdatbuffer); |
|
1365 qtdemux->mdatbuffer = NULL; |
|
1366 gst_adapter_clear (qtdemux->adapter); |
|
1367 for (n = 0; n < qtdemux->n_streams; n++) { |
|
1368 QtDemuxStream *stream = qtdemux->streams[n]; |
|
1369 |
|
1370 while (stream->buffers) { |
|
1371 gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data)); |
|
1372 stream->buffers = |
|
1373 g_slist_delete_link (stream->buffers, stream->buffers); |
|
1374 } |
|
1375 if (stream->pad) |
|
1376 gst_element_remove_pad (element, stream->pad); |
|
1377 if (stream->samples) |
|
1378 g_free (stream->samples); |
|
1379 if (stream->caps) |
|
1380 gst_caps_unref (stream->caps); |
|
1381 if (stream->segments) |
|
1382 g_free (stream->segments); |
|
1383 g_free (stream); |
|
1384 } |
|
1385 qtdemux->major_brand = 0; |
|
1386 qtdemux->n_streams = 0; |
|
1387 qtdemux->n_video_streams = 0; |
|
1388 qtdemux->n_audio_streams = 0; |
|
1389 qtdemux->n_subp_streams = 0; |
|
1390 gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); |
|
1391 break; |
|
1392 } |
|
1393 default: |
|
1394 break; |
|
1395 } |
|
1396 |
|
1397 return result; |
|
1398 } |
|
1399 |
|
1400 static void |
|
1401 extract_initial_length_and_fourcc (const guint8 * data, guint64 * plength, |
|
1402 guint32 * pfourcc) |
|
1403 { |
|
1404 guint64 length; |
|
1405 guint32 fourcc; |
|
1406 |
|
1407 length = QT_UINT32 (data); |
|
1408 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length); |
|
1409 fourcc = QT_FOURCC (data + 4); |
|
1410 GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); |
|
1411 |
|
1412 if (length == 0) { |
|
1413 length = G_MAXUINT32; |
|
1414 } else if (length == 1) { |
|
1415 /* this means we have an extended size, which is the 64 bit value of |
|
1416 * the next 8 bytes */ |
|
1417 length = QT_UINT64 (data + 8); |
|
1418 GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length); |
|
1419 } |
|
1420 |
|
1421 if (plength) |
|
1422 *plength = length; |
|
1423 if (pfourcc) |
|
1424 *pfourcc = fourcc; |
|
1425 } |
|
1426 |
|
1427 static GstFlowReturn |
|
1428 gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) |
|
1429 { |
|
1430 guint64 length = 0; |
|
1431 guint32 fourcc; |
|
1432 GstBuffer *buf = NULL; |
|
1433 GstFlowReturn ret = GST_FLOW_OK; |
|
1434 guint64 cur_offset = qtdemux->offset; |
|
1435 |
|
1436 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf); |
|
1437 if (G_UNLIKELY (ret != GST_FLOW_OK)) |
|
1438 goto beach; |
|
1439 if (G_LIKELY (GST_BUFFER_SIZE (buf) == 16)) |
|
1440 extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), &length, &fourcc); |
|
1441 gst_buffer_unref (buf); |
|
1442 |
|
1443 if (G_UNLIKELY (length == 0)) { |
|
1444 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE, |
|
1445 (_("This file is invalid and cannot be played.")), |
|
1446 ("Header atom '%" GST_FOURCC_FORMAT "' has empty length", |
|
1447 GST_FOURCC_ARGS (fourcc))); |
|
1448 ret = GST_FLOW_ERROR; |
|
1449 goto beach; |
|
1450 } |
|
1451 |
|
1452 switch (fourcc) { |
|
1453 case FOURCC_mdat: |
|
1454 case FOURCC_free: |
|
1455 case FOURCC_wide: |
|
1456 case FOURCC_PICT: |
|
1457 case FOURCC_pnot: |
|
1458 { |
|
1459 GST_LOG_OBJECT (qtdemux, |
|
1460 "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT, |
|
1461 GST_FOURCC_ARGS (fourcc), cur_offset); |
|
1462 qtdemux->offset += length; |
|
1463 break; |
|
1464 } |
|
1465 case FOURCC_moov: |
|
1466 { |
|
1467 GstBuffer *moov; |
|
1468 |
|
1469 ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov); |
|
1470 if (ret != GST_FLOW_OK) |
|
1471 goto beach; |
|
1472 if (length != GST_BUFFER_SIZE (moov)) { |
|
1473 /* Some files have a 'moov' atom at the end of the file which contains |
|
1474 * a terminal 'free' atom where the body of the atom is missing. |
|
1475 * Check for, and permit, this special case. |
|
1476 */ |
|
1477 if (GST_BUFFER_SIZE (moov) >= 8) { |
|
1478 guint8 *final_data = GST_BUFFER_DATA (moov) + |
|
1479 (GST_BUFFER_SIZE (moov) - 8); |
|
1480 guint32 final_length = QT_UINT32 (final_data); |
|
1481 guint32 final_fourcc = QT_FOURCC (final_data + 4); |
|
1482 if (final_fourcc == FOURCC_free && |
|
1483 GST_BUFFER_SIZE (moov) + final_length - 8 == length) { |
|
1484 /* Ok, we've found that special case. Allocate a new buffer with |
|
1485 * that free atom actually present. */ |
|
1486 GstBuffer *newmoov = gst_buffer_new_and_alloc (length); |
|
1487 gst_buffer_copy_metadata (newmoov, moov, |
|
1488 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | |
|
1489 GST_BUFFER_COPY_CAPS); |
|
1490 memcpy (GST_BUFFER_DATA (newmoov), GST_BUFFER_DATA (moov), |
|
1491 GST_BUFFER_SIZE (moov)); |
|
1492 memset (GST_BUFFER_DATA (newmoov) + GST_BUFFER_SIZE (moov), 0, |
|
1493 final_length - 8); |
|
1494 gst_buffer_unref (moov); |
|
1495 moov = newmoov; |
|
1496 } |
|
1497 } |
|
1498 } |
|
1499 |
|
1500 if (length != GST_BUFFER_SIZE (moov)) { |
|
1501 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE, |
|
1502 (_("This file is incomplete and cannot be played.")), |
|
1503 ("We got less than expected (received %u, wanted %u, offset %" |
|
1504 G_GUINT64_FORMAT ")", |
|
1505 GST_BUFFER_SIZE (moov), (guint) length, cur_offset)); |
|
1506 ret = GST_FLOW_ERROR; |
|
1507 goto beach; |
|
1508 } |
|
1509 qtdemux->offset += length; |
|
1510 |
|
1511 qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length); |
|
1512 qtdemux_node_dump (qtdemux, qtdemux->moov_node); |
|
1513 |
|
1514 qtdemux_parse_tree (qtdemux); |
|
1515 g_node_destroy (qtdemux->moov_node); |
|
1516 gst_buffer_unref (moov); |
|
1517 qtdemux->moov_node = NULL; |
|
1518 qtdemux->state = QTDEMUX_STATE_MOVIE; |
|
1519 GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)", |
|
1520 qtdemux->state); |
|
1521 break; |
|
1522 } |
|
1523 case FOURCC_ftyp: |
|
1524 { |
|
1525 GstBuffer *ftyp; |
|
1526 |
|
1527 /* extract major brand; might come in handy for ISO vs QT issues */ |
|
1528 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &ftyp); |
|
1529 if (ret != GST_FLOW_OK) |
|
1530 goto beach; |
|
1531 qtdemux->offset += length; |
|
1532 /* only consider at least a sufficiently complete ftyp atom */ |
|
1533 if (length >= 20) { |
|
1534 qtdemux->major_brand = QT_FOURCC (GST_BUFFER_DATA (ftyp) + 8); |
|
1535 GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT, |
|
1536 GST_FOURCC_ARGS (qtdemux->major_brand)); |
|
1537 } |
|
1538 gst_buffer_unref (ftyp); |
|
1539 break; |
|
1540 } |
|
1541 default: |
|
1542 { |
|
1543 GstBuffer *unknown; |
|
1544 |
|
1545 GST_LOG_OBJECT (qtdemux, |
|
1546 "unknown %08x '%" GST_FOURCC_FORMAT "' of size %" G_GUINT64_FORMAT |
|
1547 " at %" G_GUINT64_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc), length, |
|
1548 cur_offset); |
|
1549 ret = gst_qtdemux_pull_atom (qtdemux, cur_offset, length, &unknown); |
|
1550 if (ret != GST_FLOW_OK) |
|
1551 goto beach; |
|
1552 GST_MEMDUMP ("Unknown tag", GST_BUFFER_DATA (unknown), |
|
1553 GST_BUFFER_SIZE (unknown)); |
|
1554 gst_buffer_unref (unknown); |
|
1555 qtdemux->offset += length; |
|
1556 break; |
|
1557 } |
|
1558 } |
|
1559 |
|
1560 beach: |
|
1561 return ret; |
|
1562 } |
|
1563 |
|
1564 /* Seeks to the previous keyframe of the indexed stream and |
|
1565 * aligns other streams with respect to the keyframe timestamp |
|
1566 * of indexed stream. Only called in case of Reverse Playback |
|
1567 */ |
|
1568 static GstFlowReturn |
|
1569 gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux) |
|
1570 { |
|
1571 guint8 n = 0; |
|
1572 guint32 seg_idx = 0, k_index = 0; |
|
1573 guint64 k_pos = 0, last_stop = 0; |
|
1574 QtDemuxSegment *seg = NULL; |
|
1575 QtDemuxStream *ref_str = NULL; |
|
1576 |
|
1577 /* Now we choose an arbitrary stream, get the previous keyframe timestamp |
|
1578 * and finally align all the other streams on that timestamp with their |
|
1579 * respective keyframes */ |
|
1580 for (n = 0; n < qtdemux->n_streams; n++) { |
|
1581 QtDemuxStream *str = qtdemux->streams[n]; |
|
1582 |
|
1583 seg_idx = gst_qtdemux_find_segment (qtdemux, str, |
|
1584 qtdemux->segment.last_stop); |
|
1585 |
|
1586 /* segment not found, continue with normal flow */ |
|
1587 if (seg_idx == -1) |
|
1588 continue; |
|
1589 |
|
1590 /* No candidate yet, take that one */ |
|
1591 if (!ref_str) { |
|
1592 ref_str = str; |
|
1593 continue; |
|
1594 } |
|
1595 |
|
1596 /* So that stream has a segment, we prefer video streams */ |
|
1597 if (str->subtype == FOURCC_vide) { |
|
1598 ref_str = str; |
|
1599 break; |
|
1600 } |
|
1601 } |
|
1602 |
|
1603 if (G_UNLIKELY (!ref_str)) { |
|
1604 GST_DEBUG_OBJECT (qtdemux, "couldn't find any stream"); |
|
1605 goto eos; |
|
1606 } |
|
1607 |
|
1608 if (G_UNLIKELY (!ref_str->from_sample)) { |
|
1609 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of the file"); |
|
1610 goto eos; |
|
1611 } |
|
1612 |
|
1613 /* So that stream has been playing from from_sample to to_sample. We will |
|
1614 * get the timestamp of the previous sample and search for a keyframe before |
|
1615 * that. For audio streams we do an arbitrary jump in the past (10 samples) */ |
|
1616 if (ref_str->subtype == FOURCC_vide) { |
|
1617 k_index = gst_qtdemux_find_keyframe (qtdemux, ref_str, |
|
1618 ref_str->from_sample - 1); |
|
1619 } else { |
|
1620 k_index = ref_str->from_sample - 10; |
|
1621 } |
|
1622 |
|
1623 /* get current segment for that stream */ |
|
1624 seg = &ref_str->segments[ref_str->segment_index]; |
|
1625 /* Crawl back through segments to find the one containing this I frame */ |
|
1626 while (ref_str->samples[k_index].timestamp < seg->media_start) { |
|
1627 GST_DEBUG_OBJECT (qtdemux, "keyframe position is out of segment %u", |
|
1628 ref_str->segment_index); |
|
1629 if (G_UNLIKELY (!ref_str->segment_index)) { |
|
1630 /* Reached first segment, let's consider it's EOS */ |
|
1631 goto eos; |
|
1632 } |
|
1633 ref_str->segment_index--; |
|
1634 seg = &ref_str->segments[ref_str->segment_index]; |
|
1635 } |
|
1636 /* Calculate time position of the keyframe and where we should stop */ |
|
1637 k_pos = (ref_str->samples[k_index].timestamp - seg->media_start) + seg->time; |
|
1638 last_stop = ref_str->samples[ref_str->from_sample].timestamp; |
|
1639 last_stop = (last_stop - seg->media_start) + seg->time; |
|
1640 |
|
1641 GST_DEBUG_OBJECT (qtdemux, "preferred stream played from sample %u, " |
|
1642 "now going to sample %u (pts %" GST_TIME_FORMAT ")", ref_str->from_sample, |
|
1643 k_index, GST_TIME_ARGS (k_pos)); |
|
1644 |
|
1645 /* Set last_stop with the keyframe timestamp we pushed of that stream */ |
|
1646 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, last_stop); |
|
1647 GST_DEBUG_OBJECT (qtdemux, "last_stop now is %" GST_TIME_FORMAT, |
|
1648 GST_TIME_ARGS (last_stop)); |
|
1649 |
|
1650 if (G_UNLIKELY (last_stop < qtdemux->segment.start)) { |
|
1651 GST_DEBUG_OBJECT (qtdemux, "reached the beginning of segment"); |
|
1652 goto eos; |
|
1653 } |
|
1654 |
|
1655 /* Align them all on this */ |
|
1656 for (n = 0; n < qtdemux->n_streams; n++) { |
|
1657 guint32 index = 0; |
|
1658 guint64 media_start = 0, seg_time = 0; |
|
1659 QtDemuxStream *str = qtdemux->streams[n]; |
|
1660 |
|
1661 seg_idx = gst_qtdemux_find_segment (qtdemux, str, k_pos); |
|
1662 GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx); |
|
1663 |
|
1664 /* segment not found, continue with normal flow */ |
|
1665 if (seg_idx == -1) |
|
1666 continue; |
|
1667 |
|
1668 /* get segment and time in the segment */ |
|
1669 seg = &str->segments[seg_idx]; |
|
1670 seg_time = k_pos - seg->time; |
|
1671 |
|
1672 /* get the media time in the segment */ |
|
1673 media_start = seg->media_start + seg_time; |
|
1674 |
|
1675 /* get the index of the sample with media time */ |
|
1676 index = gst_qtdemux_find_index (qtdemux, str, media_start); |
|
1677 GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u", |
|
1678 GST_TIME_ARGS (media_start), index); |
|
1679 |
|
1680 /* find previous keyframe */ |
|
1681 k_index = gst_qtdemux_find_keyframe (qtdemux, str, index); |
|
1682 |
|
1683 /* Remember until where we want to go */ |
|
1684 str->to_sample = str->from_sample - 1; |
|
1685 /* Define our time position */ |
|
1686 str->time_position = |
|
1687 (str->samples[k_index].timestamp - seg->media_start) + seg->time; |
|
1688 /* Now seek back in time */ |
|
1689 gst_qtdemux_move_stream (qtdemux, str, k_index); |
|
1690 GST_DEBUG_OBJECT (qtdemux, "keyframe at %u, time position %" |
|
1691 GST_TIME_FORMAT " playing from sample %u to %u", k_index, |
|
1692 GST_TIME_ARGS (str->time_position), str->from_sample, str->to_sample); |
|
1693 } |
|
1694 |
|
1695 return GST_FLOW_OK; |
|
1696 |
|
1697 eos: |
|
1698 return GST_FLOW_UNEXPECTED; |
|
1699 } |
|
1700 |
|
1701 /* activate the given segment number @seg_idx of @stream at time @offset. |
|
1702 * @offset is an absolute global position over all the segments. |
|
1703 * |
|
1704 * This will push out a NEWSEGMENT event with the right values and |
|
1705 * position the stream index to the first decodable sample before |
|
1706 * @offset. |
|
1707 */ |
|
1708 static gboolean |
|
1709 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
1710 guint32 seg_idx, guint64 offset) |
|
1711 { |
|
1712 GstEvent *event; |
|
1713 QtDemuxSegment *segment; |
|
1714 guint32 index, kf_index; |
|
1715 guint64 seg_time; |
|
1716 guint64 start, stop, time; |
|
1717 gdouble rate; |
|
1718 |
|
1719 GST_LOG_OBJECT (qtdemux, "activate segment %d, offset %" G_GUINT64_FORMAT, |
|
1720 seg_idx, offset); |
|
1721 |
|
1722 /* update the current segment */ |
|
1723 stream->segment_index = seg_idx; |
|
1724 |
|
1725 /* get the segment */ |
|
1726 segment = &stream->segments[seg_idx]; |
|
1727 |
|
1728 if (G_UNLIKELY (offset < segment->time)) { |
|
1729 GST_WARNING_OBJECT (qtdemux, "offset < segment->time %" G_GUINT64_FORMAT, |
|
1730 segment->time); |
|
1731 return FALSE; |
|
1732 } |
|
1733 |
|
1734 /* get time in this segment */ |
|
1735 seg_time = offset - segment->time; |
|
1736 |
|
1737 GST_LOG_OBJECT (qtdemux, "seg_time %" GST_TIME_FORMAT, |
|
1738 GST_TIME_ARGS (seg_time)); |
|
1739 |
|
1740 if (G_UNLIKELY (seg_time > segment->duration)) { |
|
1741 GST_LOG_OBJECT (qtdemux, "seg_time > segment->duration %" GST_TIME_FORMAT, |
|
1742 GST_TIME_ARGS (segment->duration)); |
|
1743 return FALSE; |
|
1744 } |
|
1745 |
|
1746 /* qtdemux->segment.stop is in outside-time-realm, whereas |
|
1747 * segment->media_stop is in track-time-realm. |
|
1748 * |
|
1749 * In order to compare the two, we need to bring segment.stop |
|
1750 * into the track-time-realm */ |
|
1751 |
|
1752 if (qtdemux->segment.stop == -1) |
|
1753 stop = segment->media_stop; |
|
1754 else |
|
1755 stop = |
|
1756 MIN (segment->media_stop, |
|
1757 qtdemux->segment.stop - segment->time + segment->media_start); |
|
1758 |
|
1759 if (qtdemux->segment.rate >= 0) { |
|
1760 start = MIN (segment->media_start + seg_time, stop); |
|
1761 time = offset; |
|
1762 } else { |
|
1763 start = segment->media_start; |
|
1764 stop = MIN (segment->media_start + seg_time, stop); |
|
1765 time = segment->time; |
|
1766 } |
|
1767 |
|
1768 GST_DEBUG_OBJECT (qtdemux, "newsegment %d from %" GST_TIME_FORMAT |
|
1769 " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx, |
|
1770 GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time)); |
|
1771 |
|
1772 /* combine global rate with that of the segment */ |
|
1773 rate = segment->rate * qtdemux->segment.rate; |
|
1774 |
|
1775 /* update the segment values used for clipping */ |
|
1776 gst_segment_init (&stream->segment, GST_FORMAT_TIME); |
|
1777 gst_segment_set_newsegment (&stream->segment, FALSE, rate, GST_FORMAT_TIME, |
|
1778 start, stop, time); |
|
1779 |
|
1780 /* now prepare and send the segment */ |
|
1781 if (stream->pad) { |
|
1782 event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME, |
|
1783 start, stop, time); |
|
1784 gst_pad_push_event (stream->pad, event); |
|
1785 /* assume we can send more data now */ |
|
1786 stream->last_ret = GST_FLOW_OK; |
|
1787 } |
|
1788 |
|
1789 /* and move to the keyframe before the indicated media time of the |
|
1790 * segment */ |
|
1791 if (qtdemux->segment.rate >= 0) { |
|
1792 index = gst_qtdemux_find_index (qtdemux, stream, start); |
|
1793 stream->to_sample = stream->n_samples; |
|
1794 GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT |
|
1795 ", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (start), index, |
|
1796 GST_TIME_ARGS (stream->samples[index].timestamp)); |
|
1797 } else { |
|
1798 index = gst_qtdemux_find_index (qtdemux, stream, stop); |
|
1799 stream->to_sample = index; |
|
1800 GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT |
|
1801 ", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (stop), index, |
|
1802 GST_TIME_ARGS (stream->samples[index].timestamp)); |
|
1803 } |
|
1804 |
|
1805 /* we're at the right spot */ |
|
1806 if (index == stream->sample_index) { |
|
1807 GST_DEBUG_OBJECT (qtdemux, "we are at the right index"); |
|
1808 return TRUE; |
|
1809 } |
|
1810 |
|
1811 /* find keyframe of the target index */ |
|
1812 kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index); |
|
1813 |
|
1814 /* if we move forwards, we don't have to go back to the previous |
|
1815 * keyframe since we already sent that. We can also just jump to |
|
1816 * the keyframe right before the target index if there is one. */ |
|
1817 if (index > stream->sample_index) { |
|
1818 /* moving forwards check if we move past a keyframe */ |
|
1819 if (kf_index > stream->sample_index) { |
|
1820 GST_DEBUG_OBJECT (qtdemux, "moving forwards to keyframe at %u (pts %" |
|
1821 GST_TIME_FORMAT, kf_index, |
|
1822 GST_TIME_ARGS (stream->samples[kf_index].timestamp)); |
|
1823 gst_qtdemux_move_stream (qtdemux, stream, kf_index); |
|
1824 } else { |
|
1825 GST_DEBUG_OBJECT (qtdemux, "moving forwards, keyframe at %u (pts %" |
|
1826 GST_TIME_FORMAT " already sent", kf_index, |
|
1827 GST_TIME_ARGS (stream->samples[kf_index].timestamp)); |
|
1828 } |
|
1829 } else { |
|
1830 GST_DEBUG_OBJECT (qtdemux, "moving backwards to keyframe at %u (pts %" |
|
1831 GST_TIME_FORMAT, kf_index, |
|
1832 GST_TIME_ARGS (stream->samples[kf_index].timestamp)); |
|
1833 gst_qtdemux_move_stream (qtdemux, stream, kf_index); |
|
1834 } |
|
1835 |
|
1836 return TRUE; |
|
1837 } |
|
1838 |
|
1839 /* prepare to get the current sample of @stream, getting essential values. |
|
1840 * |
|
1841 * This function will also prepare and send the segment when needed. |
|
1842 * |
|
1843 * Return FALSE if the stream is EOS. |
|
1844 */ |
|
1845 static gboolean |
|
1846 gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux, |
|
1847 QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp, |
|
1848 guint64 * duration, gboolean * keyframe) |
|
1849 { |
|
1850 QtDemuxSample *sample; |
|
1851 guint64 time_position; |
|
1852 guint32 seg_idx; |
|
1853 |
|
1854 g_return_val_if_fail (stream != NULL, FALSE); |
|
1855 |
|
1856 time_position = stream->time_position; |
|
1857 if (G_UNLIKELY (time_position == -1)) |
|
1858 goto eos; |
|
1859 |
|
1860 seg_idx = stream->segment_index; |
|
1861 if (G_UNLIKELY (seg_idx == -1)) { |
|
1862 /* find segment corresponding to time_position if we are looking |
|
1863 * for a segment. */ |
|
1864 seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position); |
|
1865 |
|
1866 /* nothing found, we're really eos */ |
|
1867 if (seg_idx == -1) |
|
1868 goto eos; |
|
1869 } |
|
1870 |
|
1871 /* different segment, activate it, sample_index will be set. */ |
|
1872 if (G_UNLIKELY (stream->segment_index != seg_idx)) |
|
1873 gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position); |
|
1874 |
|
1875 GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u", |
|
1876 stream->sample_index, stream->n_samples); |
|
1877 |
|
1878 /* send out pending buffers */ |
|
1879 while (stream->buffers) { |
|
1880 GstBuffer *buffer = (GstBuffer *) stream->buffers->data; |
|
1881 |
|
1882 if (G_UNLIKELY (stream->discont)) { |
|
1883 GST_LOG_OBJECT (qtdemux, "marking discont buffer"); |
|
1884 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); |
|
1885 stream->discont = FALSE; |
|
1886 } |
|
1887 gst_buffer_set_caps (buffer, stream->caps); |
|
1888 |
|
1889 gst_pad_push (stream->pad, buffer); |
|
1890 |
|
1891 stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers); |
|
1892 } |
|
1893 |
|
1894 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) |
|
1895 goto eos; |
|
1896 |
|
1897 /* now get the info for the sample we're at */ |
|
1898 sample = &stream->samples[stream->sample_index]; |
|
1899 |
|
1900 *timestamp = sample->timestamp + sample->pts_offset; |
|
1901 *offset = sample->offset; |
|
1902 *size = sample->size; |
|
1903 *duration = sample->duration; |
|
1904 *keyframe = stream->all_keyframe || sample->keyframe; |
|
1905 |
|
1906 /* add padding */ |
|
1907 if (stream->padding) { |
|
1908 *offset += stream->padding; |
|
1909 *size -= stream->padding; |
|
1910 } |
|
1911 |
|
1912 return TRUE; |
|
1913 |
|
1914 /* special cases */ |
|
1915 eos: |
|
1916 { |
|
1917 stream->time_position = -1; |
|
1918 return FALSE; |
|
1919 } |
|
1920 } |
|
1921 |
|
1922 /* move to the next sample in @stream. |
|
1923 * |
|
1924 * Moves to the next segment when needed. |
|
1925 */ |
|
1926 static void |
|
1927 gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream) |
|
1928 { |
|
1929 QtDemuxSample *sample; |
|
1930 QtDemuxSegment *segment; |
|
1931 |
|
1932 if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) { |
|
1933 /* Mark the stream as EOS */ |
|
1934 GST_DEBUG_OBJECT (qtdemux, "reached max allowed sample %u, mark EOS", |
|
1935 stream->to_sample); |
|
1936 stream->time_position = -1; |
|
1937 return; |
|
1938 } |
|
1939 |
|
1940 /* move to next sample */ |
|
1941 stream->sample_index++; |
|
1942 |
|
1943 /* get current segment */ |
|
1944 segment = &stream->segments[stream->segment_index]; |
|
1945 |
|
1946 /* reached the last sample, we need the next segment */ |
|
1947 if (G_UNLIKELY (stream->sample_index >= stream->n_samples)) |
|
1948 goto next_segment; |
|
1949 |
|
1950 /* get next sample */ |
|
1951 sample = &stream->samples[stream->sample_index]; |
|
1952 |
|
1953 /* see if we are past the segment */ |
|
1954 if (G_UNLIKELY (sample->timestamp >= segment->media_stop)) |
|
1955 goto next_segment; |
|
1956 |
|
1957 if (sample->timestamp >= segment->media_start) { |
|
1958 /* inside the segment, update time_position, looks very familiar to |
|
1959 * GStreamer segments, doesn't it? */ |
|
1960 stream->time_position = |
|
1961 (sample->timestamp - segment->media_start) + segment->time; |
|
1962 } else { |
|
1963 /* not yet in segment, time does not yet increment. This means |
|
1964 * that we are still prerolling keyframes to the decoder so it can |
|
1965 * decode the first sample of the segment. */ |
|
1966 stream->time_position = segment->time; |
|
1967 } |
|
1968 return; |
|
1969 |
|
1970 /* move to the next segment */ |
|
1971 next_segment: |
|
1972 { |
|
1973 GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index); |
|
1974 |
|
1975 if (stream->segment_index == stream->n_segments - 1) { |
|
1976 /* are we at the end of the last segment, we're EOS */ |
|
1977 stream->time_position = -1; |
|
1978 } else { |
|
1979 /* else we're only at the end of the current segment */ |
|
1980 stream->time_position = segment->stop_time; |
|
1981 } |
|
1982 /* make sure we select a new segment */ |
|
1983 stream->segment_index = -1; |
|
1984 } |
|
1985 } |
|
1986 |
|
1987 static void |
|
1988 gst_qtdemux_sync_streams (GstQTDemux * demux) |
|
1989 { |
|
1990 gint i; |
|
1991 |
|
1992 if (demux->n_streams <= 1) |
|
1993 return; |
|
1994 |
|
1995 for (i = 0; i < demux->n_streams; i++) { |
|
1996 QtDemuxStream *stream; |
|
1997 GstClockTime end_time; |
|
1998 |
|
1999 stream = demux->streams[i]; |
|
2000 |
|
2001 if (!stream->pad) |
|
2002 continue; |
|
2003 |
|
2004 /* TODO advance time on subtitle streams here, if any some day */ |
|
2005 |
|
2006 /* some clips/trailers may have unbalanced streams at the end, |
|
2007 * so send EOS on shorter stream to prevent stalling others */ |
|
2008 |
|
2009 /* do not mess with EOS if SEGMENT seeking */ |
|
2010 if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) |
|
2011 continue; |
|
2012 |
|
2013 if (demux->pullbased) { |
|
2014 /* loop mode is sample time based */ |
|
2015 if (stream->time_position != -1) |
|
2016 continue; |
|
2017 } else { |
|
2018 /* push mode is byte position based */ |
|
2019 if (stream->samples[stream->n_samples - 1].offset >= demux->offset) |
|
2020 continue; |
|
2021 } |
|
2022 |
|
2023 if (stream->sent_eos) |
|
2024 continue; |
|
2025 |
|
2026 /* only act if some gap */ |
|
2027 end_time = stream->segments[stream->n_segments - 1].stop_time; |
|
2028 GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT |
|
2029 ", stream end: %" GST_TIME_FORMAT, GST_TIME_ARGS (end_time), |
|
2030 GST_TIME_ARGS (demux->segment.last_stop)); |
|
2031 if (end_time + 2 * GST_SECOND < demux->segment.last_stop) { |
|
2032 GST_DEBUG_OBJECT (demux, "sending EOS for stream %s", |
|
2033 GST_PAD_NAME (stream->pad)); |
|
2034 stream->sent_eos = TRUE; |
|
2035 gst_pad_push_event (stream->pad, gst_event_new_eos ()); |
|
2036 } |
|
2037 } |
|
2038 } |
|
2039 |
|
2040 /* UNEXPECTED and NOT_LINKED need to be combined. This means that we return: |
|
2041 * |
|
2042 * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED. |
|
2043 * GST_FLOW_UNEXPECTED: when all pads UNEXPECTED or NOT_LINKED. |
|
2044 */ |
|
2045 static GstFlowReturn |
|
2046 gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream, |
|
2047 GstFlowReturn ret) |
|
2048 { |
|
2049 gint i; |
|
2050 gboolean unexpected = FALSE, not_linked = TRUE; |
|
2051 |
|
2052 GST_LOG_OBJECT (demux, "flow return: %s", gst_flow_get_name (ret)); |
|
2053 |
|
2054 /* store the value */ |
|
2055 stream->last_ret = ret; |
|
2056 |
|
2057 for (i = 0; i < demux->n_streams; i++) { |
|
2058 QtDemuxStream *ostream = demux->streams[i]; |
|
2059 |
|
2060 ret = ostream->last_ret; |
|
2061 |
|
2062 /* no unexpected or unlinked, return */ |
|
2063 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED)) |
|
2064 goto done; |
|
2065 |
|
2066 /* we check to see if we have at least 1 unexpected or all unlinked */ |
|
2067 unexpected |= (ret == GST_FLOW_UNEXPECTED); |
|
2068 not_linked &= (ret == GST_FLOW_NOT_LINKED); |
|
2069 } |
|
2070 |
|
2071 /* when we get here, we all have unlinked or unexpected */ |
|
2072 if (not_linked) |
|
2073 ret = GST_FLOW_NOT_LINKED; |
|
2074 else if (unexpected) |
|
2075 ret = GST_FLOW_UNEXPECTED; |
|
2076 done: |
|
2077 GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret)); |
|
2078 return ret; |
|
2079 } |
|
2080 |
|
2081 /* the input buffer metadata must be writable. Returns NULL when the buffer is |
|
2082 * completely cliped */ |
|
2083 static GstBuffer * |
|
2084 gst_qtdemux_clip_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
2085 GstBuffer * buf) |
|
2086 { |
|
2087 gint64 start, stop, cstart, cstop, diff; |
|
2088 GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE; |
|
2089 guint8 *data; |
|
2090 guint size; |
|
2091 gint num_rate, denom_rate; |
|
2092 gint frame_size; |
|
2093 gboolean clip_data; |
|
2094 |
|
2095 data = GST_BUFFER_DATA (buf); |
|
2096 size = GST_BUFFER_SIZE (buf); |
|
2097 |
|
2098 /* depending on the type, setup the clip parameters */ |
|
2099 if (stream->subtype == FOURCC_soun) { |
|
2100 frame_size = stream->bytes_per_frame; |
|
2101 num_rate = GST_SECOND; |
|
2102 denom_rate = (gint) stream->rate; |
|
2103 clip_data = TRUE; |
|
2104 } else if (stream->subtype == FOURCC_vide) { |
|
2105 frame_size = size; |
|
2106 num_rate = stream->fps_n; |
|
2107 denom_rate = stream->fps_d; |
|
2108 clip_data = FALSE; |
|
2109 } else |
|
2110 goto wrong_type; |
|
2111 |
|
2112 /* we can only clip if we have a valid timestamp */ |
|
2113 timestamp = GST_BUFFER_TIMESTAMP (buf); |
|
2114 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) |
|
2115 goto no_timestamp; |
|
2116 |
|
2117 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buf))) { |
|
2118 duration = GST_BUFFER_DURATION (buf); |
|
2119 } else { |
|
2120 duration = |
|
2121 gst_util_uint64_scale_int (size / frame_size, num_rate, denom_rate); |
|
2122 } |
|
2123 |
|
2124 start = timestamp; |
|
2125 stop = start + duration; |
|
2126 |
|
2127 if (G_UNLIKELY (!gst_segment_clip (&stream->segment, GST_FORMAT_TIME, |
|
2128 start, stop, &cstart, &cstop))) |
|
2129 goto clipped; |
|
2130 |
|
2131 /* see if some clipping happened */ |
|
2132 diff = cstart - start; |
|
2133 if (diff > 0) { |
|
2134 timestamp = cstart; |
|
2135 duration -= diff; |
|
2136 |
|
2137 if (clip_data) { |
|
2138 /* bring clipped time to samples and to bytes */ |
|
2139 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate); |
|
2140 diff *= frame_size; |
|
2141 |
|
2142 GST_DEBUG_OBJECT (qtdemux, "clipping start to %" GST_TIME_FORMAT " %" |
|
2143 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff); |
|
2144 |
|
2145 data += diff; |
|
2146 size -= diff; |
|
2147 } |
|
2148 } |
|
2149 diff = stop - cstop; |
|
2150 if (diff > 0) { |
|
2151 duration -= diff; |
|
2152 |
|
2153 if (clip_data) { |
|
2154 /* bring clipped time to samples and then to bytes */ |
|
2155 diff = gst_util_uint64_scale_int (diff, denom_rate, num_rate); |
|
2156 diff *= frame_size; |
|
2157 |
|
2158 GST_DEBUG_OBJECT (qtdemux, "clipping stop to %" GST_TIME_FORMAT " %" |
|
2159 G_GUINT64_FORMAT " bytes", GST_TIME_ARGS (cstop), diff); |
|
2160 |
|
2161 size -= diff; |
|
2162 } |
|
2163 } |
|
2164 |
|
2165 GST_BUFFER_TIMESTAMP (buf) = timestamp; |
|
2166 GST_BUFFER_DURATION (buf) = duration; |
|
2167 GST_BUFFER_SIZE (buf) = size; |
|
2168 GST_BUFFER_DATA (buf) = data; |
|
2169 |
|
2170 return buf; |
|
2171 |
|
2172 /* dropped buffer */ |
|
2173 wrong_type: |
|
2174 { |
|
2175 GST_DEBUG_OBJECT (qtdemux, "unknown stream type"); |
|
2176 return buf; |
|
2177 } |
|
2178 no_timestamp: |
|
2179 { |
|
2180 GST_DEBUG_OBJECT (qtdemux, "no timestamp on buffer"); |
|
2181 return buf; |
|
2182 } |
|
2183 clipped: |
|
2184 { |
|
2185 GST_DEBUG_OBJECT (qtdemux, "clipped buffer"); |
|
2186 gst_buffer_unref (buf); |
|
2187 return NULL; |
|
2188 } |
|
2189 } |
|
2190 |
|
2191 static GstFlowReturn |
|
2192 gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) |
|
2193 { |
|
2194 GstFlowReturn ret = GST_FLOW_OK; |
|
2195 GstBuffer *buf = NULL; |
|
2196 QtDemuxStream *stream; |
|
2197 guint64 min_time; |
|
2198 guint64 offset = 0; |
|
2199 guint64 timestamp = GST_CLOCK_TIME_NONE; |
|
2200 guint64 duration = 0; |
|
2201 gboolean keyframe = FALSE; |
|
2202 guint size = 0; |
|
2203 gint index; |
|
2204 gint i; |
|
2205 |
|
2206 gst_qtdemux_push_pending_newsegment (qtdemux); |
|
2207 |
|
2208 /* Figure out the next stream sample to output, min_time is expressed in |
|
2209 * global time and runs over the edit list segments. */ |
|
2210 min_time = G_MAXUINT64; |
|
2211 index = -1; |
|
2212 for (i = 0; i < qtdemux->n_streams; i++) { |
|
2213 guint64 position; |
|
2214 |
|
2215 stream = qtdemux->streams[i]; |
|
2216 position = stream->time_position; |
|
2217 |
|
2218 /* position of -1 is EOS */ |
|
2219 if (position != -1 && position < min_time) { |
|
2220 min_time = position; |
|
2221 index = i; |
|
2222 } |
|
2223 } |
|
2224 /* all are EOS */ |
|
2225 if (G_UNLIKELY (index == -1)) { |
|
2226 GST_DEBUG_OBJECT (qtdemux, "all streams are EOS"); |
|
2227 goto eos; |
|
2228 } |
|
2229 |
|
2230 /* check for segment end */ |
|
2231 if (G_UNLIKELY (qtdemux->segment.stop != -1 |
|
2232 && qtdemux->segment.stop < min_time)) { |
|
2233 GST_DEBUG_OBJECT (qtdemux, "we reached the end of our segment."); |
|
2234 goto eos; |
|
2235 } |
|
2236 |
|
2237 stream = qtdemux->streams[index]; |
|
2238 |
|
2239 /* fetch info for the current sample of this stream */ |
|
2240 if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset, |
|
2241 &size, ×tamp, &duration, &keyframe))) |
|
2242 goto eos_stream; |
|
2243 |
|
2244 GST_LOG_OBJECT (qtdemux, |
|
2245 "pushing from stream %d, offset %" G_GUINT64_FORMAT |
|
2246 ", size %d, timestamp=%" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, |
|
2247 index, offset, size, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration)); |
|
2248 |
|
2249 /* hmm, empty sample, skip and move to next sample */ |
|
2250 if (G_UNLIKELY (size <= 0)) |
|
2251 goto next; |
|
2252 |
|
2253 /* last pushed sample was out of boundary, goto next sample */ |
|
2254 if (G_UNLIKELY (stream->last_ret == GST_FLOW_UNEXPECTED)) |
|
2255 goto next; |
|
2256 |
|
2257 GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size, |
|
2258 offset); |
|
2259 |
|
2260 ret = gst_qtdemux_pull_atom (qtdemux, offset, size, &buf); |
|
2261 if (G_UNLIKELY (ret != GST_FLOW_OK)) |
|
2262 goto beach; |
|
2263 |
|
2264 if (G_UNLIKELY (stream->fourcc == FOURCC_rtsp)) { |
|
2265 GstMessage *m; |
|
2266 gchar *url; |
|
2267 |
|
2268 url = g_strndup ((gchar *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); |
|
2269 |
|
2270 /* we have RTSP redirect now */ |
|
2271 m = gst_message_new_element (GST_OBJECT_CAST (qtdemux), |
|
2272 gst_structure_new ("redirect", |
|
2273 "new-location", G_TYPE_STRING, url, NULL)); |
|
2274 g_free (url); |
|
2275 |
|
2276 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m); |
|
2277 } |
|
2278 |
|
2279 qtdemux->last_ts = min_time; |
|
2280 if (qtdemux->segment.rate >= 0) { |
|
2281 gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, min_time); |
|
2282 gst_qtdemux_sync_streams (qtdemux); |
|
2283 } |
|
2284 if (G_LIKELY (stream->pad)) { |
|
2285 /* we're going to modify the metadata */ |
|
2286 buf = gst_buffer_make_metadata_writable (buf); |
|
2287 |
|
2288 GST_BUFFER_TIMESTAMP (buf) = timestamp; |
|
2289 GST_BUFFER_DURATION (buf) = duration; |
|
2290 GST_BUFFER_OFFSET (buf) = -1; |
|
2291 GST_BUFFER_OFFSET_END (buf) = -1; |
|
2292 |
|
2293 if (stream->need_clip) |
|
2294 buf = gst_qtdemux_clip_buffer (qtdemux, stream, buf); |
|
2295 |
|
2296 if (buf == NULL) |
|
2297 goto next; |
|
2298 |
|
2299 if (stream->discont) { |
|
2300 GST_LOG_OBJECT (qtdemux, "marking discont buffer"); |
|
2301 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); |
|
2302 stream->discont = FALSE; |
|
2303 } |
|
2304 |
|
2305 if (!keyframe) |
|
2306 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); |
|
2307 |
|
2308 gst_buffer_set_caps (buf, stream->caps); |
|
2309 |
|
2310 GST_LOG_OBJECT (qtdemux, |
|
2311 "Pushing buffer with time %" GST_TIME_FORMAT ", duration %" |
|
2312 GST_TIME_FORMAT " on pad %s", |
|
2313 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), |
|
2314 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_PAD_NAME (stream->pad)); |
|
2315 |
|
2316 ret = gst_pad_push (stream->pad, buf); |
|
2317 } else { |
|
2318 GST_DEBUG_OBJECT (qtdemux, "No output pad for stream, ignoring"); |
|
2319 gst_buffer_unref (buf); |
|
2320 ret = GST_FLOW_OK; |
|
2321 } |
|
2322 |
|
2323 /* combine flows */ |
|
2324 ret = gst_qtdemux_combine_flows (qtdemux, stream, ret); |
|
2325 /* ignore unlinked, we will not push on the pad anymore and we will EOS when |
|
2326 * we have no more data for the pad to push */ |
|
2327 if (ret == GST_FLOW_UNEXPECTED) |
|
2328 ret = GST_FLOW_OK; |
|
2329 |
|
2330 next: |
|
2331 gst_qtdemux_advance_sample (qtdemux, stream); |
|
2332 |
|
2333 beach: |
|
2334 return ret; |
|
2335 |
|
2336 /* special cases */ |
|
2337 eos: |
|
2338 { |
|
2339 GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS"); |
|
2340 ret = GST_FLOW_UNEXPECTED; |
|
2341 goto beach; |
|
2342 } |
|
2343 eos_stream: |
|
2344 { |
|
2345 GST_DEBUG_OBJECT (qtdemux, "No samples left for stream"); |
|
2346 /* EOS will be raised if all are EOS */ |
|
2347 ret = GST_FLOW_OK; |
|
2348 goto beach; |
|
2349 } |
|
2350 } |
|
2351 |
|
2352 static void |
|
2353 gst_qtdemux_loop (GstPad * pad) |
|
2354 { |
|
2355 GstQTDemux *qtdemux; |
|
2356 guint64 cur_offset; |
|
2357 GstFlowReturn ret; |
|
2358 |
|
2359 qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad)); |
|
2360 |
|
2361 cur_offset = qtdemux->offset; |
|
2362 GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d", |
|
2363 cur_offset, qtdemux->state); |
|
2364 |
|
2365 switch (qtdemux->state) { |
|
2366 case QTDEMUX_STATE_INITIAL: |
|
2367 case QTDEMUX_STATE_HEADER: |
|
2368 ret = gst_qtdemux_loop_state_header (qtdemux); |
|
2369 break; |
|
2370 case QTDEMUX_STATE_MOVIE: |
|
2371 ret = gst_qtdemux_loop_state_movie (qtdemux); |
|
2372 if (qtdemux->segment.rate < 0 && ret == GST_FLOW_UNEXPECTED) { |
|
2373 ret = gst_qtdemux_seek_to_previous_keyframe (qtdemux); |
|
2374 } |
|
2375 break; |
|
2376 default: |
|
2377 /* ouch */ |
|
2378 goto invalid_state; |
|
2379 } |
|
2380 |
|
2381 /* if something went wrong, pause */ |
|
2382 if (ret != GST_FLOW_OK) |
|
2383 goto pause; |
|
2384 |
|
2385 done: |
|
2386 gst_object_unref (qtdemux); |
|
2387 return; |
|
2388 |
|
2389 /* ERRORS */ |
|
2390 invalid_state: |
|
2391 { |
|
2392 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED, |
|
2393 (NULL), ("streaming stopped, invalid state")); |
|
2394 qtdemux->segment_running = FALSE; |
|
2395 gst_pad_pause_task (pad); |
|
2396 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ()); |
|
2397 goto done; |
|
2398 } |
|
2399 pause: |
|
2400 { |
|
2401 const gchar *reason = gst_flow_get_name (ret); |
|
2402 |
|
2403 GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", reason); |
|
2404 |
|
2405 qtdemux->segment_running = FALSE; |
|
2406 gst_pad_pause_task (pad); |
|
2407 |
|
2408 /* fatal errors need special actions */ |
|
2409 if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) { |
|
2410 /* check EOS */ |
|
2411 if (ret == GST_FLOW_UNEXPECTED) { |
|
2412 if (qtdemux->n_streams == 0) { |
|
2413 /* we have no streams, post an error */ |
|
2414 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE, |
|
2415 (_("This file contains no playable streams.")), |
|
2416 ("no known streams found")); |
|
2417 } |
|
2418 if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) { |
|
2419 gint64 stop; |
|
2420 |
|
2421 /* FIXME: I am not sure this is the right fix. If the sinks are |
|
2422 * supposed to detect the segment is complete and accumulate |
|
2423 * automatically, it does not seem to work here. Need more work */ |
|
2424 qtdemux->segment_running = TRUE; |
|
2425 |
|
2426 if ((stop = qtdemux->segment.stop) == -1) |
|
2427 stop = qtdemux->segment.duration; |
|
2428 |
|
2429 if (qtdemux->segment.rate >= 0) { |
|
2430 GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment"); |
|
2431 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), |
|
2432 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux), |
|
2433 GST_FORMAT_TIME, stop)); |
|
2434 } else { |
|
2435 /* For Reverse Playback */ |
|
2436 GST_LOG_OBJECT (qtdemux, |
|
2437 "Sending segment done, at start of segment"); |
|
2438 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), |
|
2439 gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux), |
|
2440 GST_FORMAT_TIME, qtdemux->segment.start)); |
|
2441 } |
|
2442 } else { |
|
2443 GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment"); |
|
2444 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ()); |
|
2445 } |
|
2446 } else { |
|
2447 GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED, |
|
2448 (NULL), ("streaming stopped, reason %s", reason)); |
|
2449 gst_qtdemux_push_event (qtdemux, gst_event_new_eos ()); |
|
2450 } |
|
2451 } |
|
2452 goto done; |
|
2453 } |
|
2454 } |
|
2455 |
|
2456 /* |
|
2457 * next_entry_size |
|
2458 * |
|
2459 * Returns the size of the first entry at the current offset. |
|
2460 * If -1, there are none (which means EOS or empty file). |
|
2461 */ |
|
2462 static guint64 |
|
2463 next_entry_size (GstQTDemux * demux) |
|
2464 { |
|
2465 QtDemuxStream *stream; |
|
2466 int i; |
|
2467 int smallidx = -1; |
|
2468 guint64 smalloffs = (guint64) - 1; |
|
2469 |
|
2470 GST_LOG_OBJECT (demux, "Finding entry at offset %lld", demux->offset); |
|
2471 |
|
2472 for (i = 0; i < demux->n_streams; i++) { |
|
2473 stream = demux->streams[i]; |
|
2474 |
|
2475 if (stream->sample_index == -1) |
|
2476 stream->sample_index = 0; |
|
2477 |
|
2478 if (stream->sample_index >= stream->n_samples) { |
|
2479 GST_LOG_OBJECT (demux, "stream %d samples exhausted", i); |
|
2480 continue; |
|
2481 } |
|
2482 |
|
2483 GST_LOG_OBJECT (demux, |
|
2484 "Checking Stream %d (sample_index:%d / offset:%lld / size:%d)", |
|
2485 i, stream->sample_index, stream->samples[stream->sample_index].offset, |
|
2486 stream->samples[stream->sample_index].size); |
|
2487 |
|
2488 if (((smalloffs == -1) |
|
2489 || (stream->samples[stream->sample_index].offset < smalloffs)) |
|
2490 && (stream->samples[stream->sample_index].size)) { |
|
2491 smallidx = i; |
|
2492 smalloffs = stream->samples[stream->sample_index].offset; |
|
2493 } |
|
2494 } |
|
2495 |
|
2496 GST_LOG_OBJECT (demux, "stream %d offset %lld demux->offset :%lld", |
|
2497 smallidx, smalloffs, demux->offset); |
|
2498 |
|
2499 if (smallidx == -1) |
|
2500 return -1; |
|
2501 stream = demux->streams[smallidx]; |
|
2502 |
|
2503 if (stream->samples[stream->sample_index].offset >= demux->offset) { |
|
2504 demux->todrop = |
|
2505 stream->samples[stream->sample_index].offset - demux->offset; |
|
2506 return stream->samples[stream->sample_index].size + demux->todrop; |
|
2507 } |
|
2508 |
|
2509 GST_DEBUG_OBJECT (demux, "There wasn't any entry at offset %lld", |
|
2510 demux->offset); |
|
2511 return -1; |
|
2512 } |
|
2513 |
|
2514 static void |
|
2515 gst_qtdemux_post_progress (GstQTDemux * demux, gint num, gint denom) |
|
2516 { |
|
2517 gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom); |
|
2518 |
|
2519 gst_element_post_message (GST_ELEMENT_CAST (demux), |
|
2520 gst_message_new_element (GST_OBJECT_CAST (demux), |
|
2521 gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL))); |
|
2522 } |
|
2523 |
|
2524 /* FIXME, unverified after edit list updates */ |
|
2525 static GstFlowReturn |
|
2526 gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) |
|
2527 { |
|
2528 GstQTDemux *demux; |
|
2529 GstFlowReturn ret = GST_FLOW_OK; |
|
2530 |
|
2531 demux = GST_QTDEMUX (gst_pad_get_parent (sinkpad)); |
|
2532 |
|
2533 gst_adapter_push (demux->adapter, inbuf); |
|
2534 |
|
2535 /* we never really mean to buffer that much */ |
|
2536 if (demux->neededbytes == -1) |
|
2537 goto eos; |
|
2538 |
|
2539 GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u", |
|
2540 inbuf, demux->neededbytes, gst_adapter_available (demux->adapter)); |
|
2541 |
|
2542 while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) && |
|
2543 (ret == GST_FLOW_OK)) { |
|
2544 |
|
2545 GST_DEBUG_OBJECT (demux, |
|
2546 "state:%d , demux->neededbytes:%d, demux->offset:%lld", demux->state, |
|
2547 demux->neededbytes, demux->offset); |
|
2548 |
|
2549 switch (demux->state) { |
|
2550 case QTDEMUX_STATE_INITIAL:{ |
|
2551 const guint8 *data; |
|
2552 guint32 fourcc; |
|
2553 guint64 size; |
|
2554 |
|
2555 data = gst_adapter_peek (demux->adapter, demux->neededbytes); |
|
2556 |
|
2557 /* get fourcc/length, set neededbytes */ |
|
2558 extract_initial_length_and_fourcc ((guint8 *) data, &size, &fourcc); |
|
2559 GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] " |
|
2560 "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size); |
|
2561 if (size == 0) { |
|
2562 GST_ELEMENT_ERROR (demux, STREAM, DECODE, |
|
2563 (_("This file is invalid and cannot be played.")), |
|
2564 ("initial atom '%" GST_FOURCC_FORMAT "' has empty length", |
|
2565 GST_FOURCC_ARGS (fourcc))); |
|
2566 ret = GST_FLOW_ERROR; |
|
2567 break; |
|
2568 } |
|
2569 if (fourcc == FOURCC_mdat) { |
|
2570 if (demux->n_streams > 0) { |
|
2571 demux->state = QTDEMUX_STATE_MOVIE; |
|
2572 demux->neededbytes = next_entry_size (demux); |
|
2573 } else { |
|
2574 guint bs; |
|
2575 |
|
2576 buffer_data: |
|
2577 /* there may be multiple mdat (or alike) buffers */ |
|
2578 /* sanity check */ |
|
2579 if (demux->mdatbuffer) |
|
2580 bs = GST_BUFFER_SIZE (demux->mdatbuffer); |
|
2581 else |
|
2582 bs = 0; |
|
2583 if (size + bs > 10 * (1 << 20)) |
|
2584 goto no_moov; |
|
2585 demux->state = QTDEMUX_STATE_BUFFER_MDAT; |
|
2586 demux->neededbytes = size; |
|
2587 if (!demux->mdatbuffer) |
|
2588 demux->mdatoffset = demux->offset; |
|
2589 } |
|
2590 } else if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) { |
|
2591 GST_ELEMENT_ERROR (demux, STREAM, DECODE, |
|
2592 (_("This file is invalid and cannot be played.")), |
|
2593 ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT, |
|
2594 GST_FOURCC_ARGS (fourcc), size)); |
|
2595 ret = GST_FLOW_ERROR; |
|
2596 break; |
|
2597 } else { |
|
2598 /* this means we already started buffering and still no moov header, |
|
2599 * let's continue buffering everything till we get moov */ |
|
2600 if (demux->mdatbuffer && (fourcc != FOURCC_moov)) |
|
2601 goto buffer_data; |
|
2602 demux->neededbytes = size; |
|
2603 demux->state = QTDEMUX_STATE_HEADER; |
|
2604 } |
|
2605 break; |
|
2606 } |
|
2607 case QTDEMUX_STATE_HEADER:{ |
|
2608 const guint8 *data; |
|
2609 guint32 fourcc; |
|
2610 |
|
2611 GST_DEBUG_OBJECT (demux, "In header"); |
|
2612 |
|
2613 data = gst_adapter_peek (demux->adapter, demux->neededbytes); |
|
2614 |
|
2615 /* parse the header */ |
|
2616 extract_initial_length_and_fourcc (data, NULL, &fourcc); |
|
2617 if (fourcc == FOURCC_moov) { |
|
2618 GST_DEBUG_OBJECT (demux, "Parsing [moov]"); |
|
2619 |
|
2620 qtdemux_parse_moov (demux, data, demux->neededbytes); |
|
2621 qtdemux_node_dump (demux, demux->moov_node); |
|
2622 qtdemux_parse_tree (demux); |
|
2623 |
|
2624 g_node_destroy (demux->moov_node); |
|
2625 demux->moov_node = NULL; |
|
2626 GST_DEBUG_OBJECT (demux, "Finished parsing the header"); |
|
2627 } else { |
|
2628 GST_WARNING_OBJECT (demux, |
|
2629 "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT, |
|
2630 GST_FOURCC_ARGS (fourcc)); |
|
2631 /* Let's jump that one and go back to initial state */ |
|
2632 } |
|
2633 |
|
2634 if (demux->mdatbuffer && demux->n_streams) { |
|
2635 GstBuffer *buf; |
|
2636 |
|
2637 /* the mdat was before the header */ |
|
2638 GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p", |
|
2639 demux->n_streams, demux->mdatbuffer); |
|
2640 /* restore our adapter/offset view of things with upstream; |
|
2641 * put preceding buffered data ahead of current moov data. |
|
2642 * This should also handle evil mdat, moov, mdat cases and alike */ |
|
2643 buf = gst_adapter_take_buffer (demux->adapter, |
|
2644 gst_adapter_available (demux->adapter)); |
|
2645 gst_adapter_clear (demux->adapter); |
|
2646 gst_adapter_push (demux->adapter, demux->mdatbuffer); |
|
2647 gst_adapter_push (demux->adapter, buf); |
|
2648 demux->mdatbuffer = NULL; |
|
2649 demux->offset = demux->mdatoffset; |
|
2650 demux->neededbytes = next_entry_size (demux); |
|
2651 demux->state = QTDEMUX_STATE_MOVIE; |
|
2652 } else { |
|
2653 GST_DEBUG_OBJECT (demux, "Carrying on normally"); |
|
2654 gst_adapter_flush (demux->adapter, demux->neededbytes); |
|
2655 demux->offset += demux->neededbytes; |
|
2656 demux->neededbytes = 16; |
|
2657 demux->state = QTDEMUX_STATE_INITIAL; |
|
2658 } |
|
2659 |
|
2660 break; |
|
2661 } |
|
2662 case QTDEMUX_STATE_BUFFER_MDAT:{ |
|
2663 GstBuffer *buf; |
|
2664 |
|
2665 GST_DEBUG_OBJECT (demux, "Got our buffer at offset %lld", |
|
2666 demux->offset); |
|
2667 buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes); |
|
2668 GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT, |
|
2669 GST_FOURCC_ARGS (QT_FOURCC (GST_BUFFER_DATA (buf) + 4))); |
|
2670 if (demux->mdatbuffer) |
|
2671 demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf); |
|
2672 else |
|
2673 demux->mdatbuffer = buf; |
|
2674 demux->offset += demux->neededbytes; |
|
2675 demux->neededbytes = 16; |
|
2676 demux->state = QTDEMUX_STATE_INITIAL; |
|
2677 gst_qtdemux_post_progress (demux, 1, 1); |
|
2678 |
|
2679 break; |
|
2680 } |
|
2681 case QTDEMUX_STATE_MOVIE:{ |
|
2682 GstBuffer *outbuf; |
|
2683 QtDemuxStream *stream = NULL; |
|
2684 int i = -1; |
|
2685 |
|
2686 GST_DEBUG_OBJECT (demux, "BEGIN // in MOVIE for offset %lld", |
|
2687 demux->offset); |
|
2688 |
|
2689 if (demux->todrop) { |
|
2690 GST_LOG_OBJECT (demux, "Dropping %d bytes", demux->todrop); |
|
2691 gst_adapter_flush (demux->adapter, demux->todrop); |
|
2692 demux->neededbytes -= demux->todrop; |
|
2693 demux->offset += demux->todrop; |
|
2694 } |
|
2695 |
|
2696 /* Figure out which stream this is packet belongs to */ |
|
2697 for (i = 0; i < demux->n_streams; i++) { |
|
2698 stream = demux->streams[i]; |
|
2699 if (stream->sample_index >= stream->n_samples) |
|
2700 continue; |
|
2701 GST_LOG_OBJECT (demux, |
|
2702 "Checking stream %d (sample_index:%d / offset:%lld / size:%d)", |
|
2703 i, stream->sample_index, |
|
2704 stream->samples[stream->sample_index].offset, |
|
2705 stream->samples[stream->sample_index].size); |
|
2706 |
|
2707 if (stream->samples[stream->sample_index].offset == demux->offset) |
|
2708 break; |
|
2709 } |
|
2710 |
|
2711 if (G_UNLIKELY (stream == NULL || i == demux->n_streams)) |
|
2712 goto unknown_stream; |
|
2713 |
|
2714 /* first buffer? */ |
|
2715 /* initial newsegment sent here after having added pads, |
|
2716 * possible others in sink_event */ |
|
2717 if (G_UNLIKELY (demux->last_ts == GST_CLOCK_TIME_NONE)) { |
|
2718 gst_qtdemux_push_event (demux, |
|
2719 gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, |
|
2720 0, GST_CLOCK_TIME_NONE, 0)); |
|
2721 } |
|
2722 |
|
2723 /* Put data in a buffer, set timestamps, caps, ... */ |
|
2724 outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes); |
|
2725 GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT, |
|
2726 GST_FOURCC_ARGS (stream->fourcc)); |
|
2727 |
|
2728 g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR); |
|
2729 |
|
2730 if (stream->samples[stream->sample_index].pts_offset) { |
|
2731 demux->last_ts = stream->samples[stream->sample_index].timestamp; |
|
2732 GST_BUFFER_TIMESTAMP (outbuf) = demux->last_ts + |
|
2733 stream->samples[stream->sample_index].pts_offset; |
|
2734 } else { |
|
2735 GST_BUFFER_TIMESTAMP (outbuf) = |
|
2736 stream->samples[stream->sample_index].timestamp; |
|
2737 demux->last_ts = GST_BUFFER_TIMESTAMP (outbuf); |
|
2738 } |
|
2739 GST_BUFFER_DURATION (outbuf) = |
|
2740 stream->samples[stream->sample_index].duration; |
|
2741 if (!stream->all_keyframe && |
|
2742 !stream->samples[stream->sample_index].keyframe) |
|
2743 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); |
|
2744 |
|
2745 /* position reporting */ |
|
2746 gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME, |
|
2747 demux->last_ts); |
|
2748 gst_qtdemux_sync_streams (demux); |
|
2749 |
|
2750 /* send buffer */ |
|
2751 if (stream->pad) { |
|
2752 GST_LOG_OBJECT (demux, |
|
2753 "Pushing buffer with time %" GST_TIME_FORMAT " on pad %p", |
|
2754 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), stream->pad); |
|
2755 gst_buffer_set_caps (outbuf, stream->caps); |
|
2756 ret = gst_pad_push (stream->pad, outbuf); |
|
2757 } else { |
|
2758 gst_buffer_unref (outbuf); |
|
2759 ret = GST_FLOW_OK; |
|
2760 } |
|
2761 |
|
2762 /* combine flows */ |
|
2763 ret = gst_qtdemux_combine_flows (demux, stream, ret); |
|
2764 |
|
2765 stream->sample_index++; |
|
2766 |
|
2767 /* update current offset and figure out size of next buffer */ |
|
2768 GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u", |
|
2769 demux->offset, demux->neededbytes); |
|
2770 demux->offset += demux->neededbytes; |
|
2771 GST_LOG_OBJECT (demux, "offset is now %lld", demux->offset); |
|
2772 |
|
2773 if ((demux->neededbytes = next_entry_size (demux)) == -1) |
|
2774 goto eos; |
|
2775 break; |
|
2776 } |
|
2777 default: |
|
2778 goto invalid_state; |
|
2779 } |
|
2780 } |
|
2781 |
|
2782 /* when buffering movie data, at least show user something is happening */ |
|
2783 if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT && |
|
2784 gst_adapter_available (demux->adapter) <= demux->neededbytes) { |
|
2785 gst_qtdemux_post_progress (demux, gst_adapter_available (demux->adapter), |
|
2786 demux->neededbytes); |
|
2787 } |
|
2788 done: |
|
2789 gst_object_unref (demux); |
|
2790 |
|
2791 return ret; |
|
2792 |
|
2793 /* ERRORS */ |
|
2794 unknown_stream: |
|
2795 { |
|
2796 GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found")); |
|
2797 ret = GST_FLOW_ERROR; |
|
2798 goto done; |
|
2799 } |
|
2800 eos: |
|
2801 { |
|
2802 GST_DEBUG_OBJECT (demux, "no next entry, EOS"); |
|
2803 ret = GST_FLOW_UNEXPECTED; |
|
2804 goto done; |
|
2805 } |
|
2806 invalid_state: |
|
2807 { |
|
2808 GST_ELEMENT_ERROR (demux, STREAM, FAILED, |
|
2809 (NULL), ("qtdemuxer invalid state %d", demux->state)); |
|
2810 ret = GST_FLOW_ERROR; |
|
2811 goto done; |
|
2812 } |
|
2813 no_moov: |
|
2814 { |
|
2815 GST_ELEMENT_ERROR (demux, STREAM, FAILED, |
|
2816 (NULL), ("no 'moov' atom withing first 10 MB")); |
|
2817 ret = GST_FLOW_ERROR; |
|
2818 goto done; |
|
2819 } |
|
2820 } |
|
2821 |
|
2822 static gboolean |
|
2823 qtdemux_sink_activate (GstPad * sinkpad) |
|
2824 { |
|
2825 if (gst_pad_check_pull_range (sinkpad)) |
|
2826 return gst_pad_activate_pull (sinkpad, TRUE); |
|
2827 else |
|
2828 return gst_pad_activate_push (sinkpad, TRUE); |
|
2829 } |
|
2830 |
|
2831 static gboolean |
|
2832 qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active) |
|
2833 { |
|
2834 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad)); |
|
2835 |
|
2836 if (active) { |
|
2837 demux->pullbased = TRUE; |
|
2838 demux->segment_running = TRUE; |
|
2839 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop, |
|
2840 sinkpad); |
|
2841 } else { |
|
2842 demux->segment_running = FALSE; |
|
2843 return gst_pad_stop_task (sinkpad); |
|
2844 } |
|
2845 } |
|
2846 |
|
2847 static gboolean |
|
2848 qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active) |
|
2849 { |
|
2850 GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad)); |
|
2851 |
|
2852 demux->pullbased = FALSE; |
|
2853 |
|
2854 return TRUE; |
|
2855 } |
|
2856 |
|
2857 #ifdef HAVE_ZLIB |
|
2858 static void * |
|
2859 qtdemux_zalloc (void *opaque, unsigned int items, unsigned int size) |
|
2860 { |
|
2861 return g_malloc (items * size); |
|
2862 } |
|
2863 |
|
2864 static void |
|
2865 qtdemux_zfree (void *opaque, void *addr) |
|
2866 { |
|
2867 g_free (addr); |
|
2868 } |
|
2869 |
|
2870 static void * |
|
2871 qtdemux_inflate (void *z_buffer, int z_length, int length) |
|
2872 { |
|
2873 guint8 *buffer; |
|
2874 z_stream *z; |
|
2875 int ret; |
|
2876 |
|
2877 z = g_new0 (z_stream, 1); |
|
2878 z->zalloc = qtdemux_zalloc; |
|
2879 z->zfree = qtdemux_zfree; |
|
2880 z->opaque = NULL; |
|
2881 |
|
2882 z->next_in = z_buffer; |
|
2883 z->avail_in = z_length; |
|
2884 |
|
2885 buffer = (guint8 *) g_malloc (length); |
|
2886 ret = inflateInit (z); |
|
2887 while (z->avail_in > 0) { |
|
2888 if (z->avail_out == 0) { |
|
2889 length += 1024; |
|
2890 buffer = (guint8 *) g_realloc (buffer, length); |
|
2891 z->next_out = buffer + z->total_out; |
|
2892 z->avail_out = 1024; |
|
2893 } |
|
2894 ret = inflate (z, Z_SYNC_FLUSH); |
|
2895 if (ret != Z_OK) |
|
2896 break; |
|
2897 } |
|
2898 if (ret != Z_STREAM_END) { |
|
2899 g_warning ("inflate() returned %d", ret); |
|
2900 } |
|
2901 |
|
2902 g_free (z); |
|
2903 return buffer; |
|
2904 } |
|
2905 #endif /* HAVE_ZLIB */ |
|
2906 |
|
2907 static gboolean |
|
2908 qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, int length) |
|
2909 { |
|
2910 GNode *cmov; |
|
2911 |
|
2912 qtdemux->moov_node = g_node_new ((guint8 *) buffer); |
|
2913 |
|
2914 GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom"); |
|
2915 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length); |
|
2916 |
|
2917 cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov); |
|
2918 if (cmov) { |
|
2919 guint32 method; |
|
2920 GNode *dcom; |
|
2921 GNode *cmvd; |
|
2922 |
|
2923 dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom); |
|
2924 cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd); |
|
2925 if (dcom == NULL || cmvd == NULL) |
|
2926 goto invalid_compression; |
|
2927 |
|
2928 method = QT_FOURCC ((guint8 *) dcom->data + 8); |
|
2929 switch (method) { |
|
2930 #ifdef HAVE_ZLIB |
|
2931 case GST_MAKE_FOURCC ('z', 'l', 'i', 'b'):{ |
|
2932 int uncompressed_length; |
|
2933 int compressed_length; |
|
2934 guint8 *buf; |
|
2935 |
|
2936 uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8); |
|
2937 compressed_length = QT_UINT32 ((guint8 *) cmvd->data + 4) - 12; |
|
2938 GST_LOG ("length = %d", uncompressed_length); |
|
2939 |
|
2940 buf = |
|
2941 (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12, |
|
2942 compressed_length, uncompressed_length); |
|
2943 |
|
2944 qtdemux->moov_node_compressed = qtdemux->moov_node; |
|
2945 qtdemux->moov_node = g_node_new (buf); |
|
2946 |
|
2947 qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf, |
|
2948 uncompressed_length); |
|
2949 break; |
|
2950 } |
|
2951 #endif /* HAVE_ZLIB */ |
|
2952 default: |
|
2953 GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression " |
|
2954 "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method)); |
|
2955 break; |
|
2956 } |
|
2957 } |
|
2958 return TRUE; |
|
2959 |
|
2960 /* ERRORS */ |
|
2961 invalid_compression: |
|
2962 { |
|
2963 GST_ERROR_OBJECT (qtdemux, "invalid compressed header"); |
|
2964 return FALSE; |
|
2965 } |
|
2966 } |
|
2967 |
|
2968 static gboolean |
|
2969 qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf, |
|
2970 const guint8 * end) |
|
2971 { |
|
2972 while (G_UNLIKELY (buf < end)) { |
|
2973 GNode *child; |
|
2974 guint32 len; |
|
2975 |
|
2976 if (G_UNLIKELY (buf + 4 > end)) { |
|
2977 GST_LOG_OBJECT (qtdemux, "buffer overrun"); |
|
2978 break; |
|
2979 } |
|
2980 len = QT_UINT32 (buf); |
|
2981 if (G_UNLIKELY (len == 0)) { |
|
2982 GST_LOG_OBJECT (qtdemux, "empty container"); |
|
2983 break; |
|
2984 } |
|
2985 if (G_UNLIKELY (len < 8)) { |
|
2986 GST_WARNING_OBJECT (qtdemux, "length too short (%d < 8)", len); |
|
2987 break; |
|
2988 } |
|
2989 if (G_UNLIKELY (len > (end - buf))) { |
|
2990 GST_WARNING_OBJECT (qtdemux, "length too long (%d > %d)", len, end - buf); |
|
2991 break; |
|
2992 } |
|
2993 |
|
2994 child = g_node_new ((guint8 *) buf); |
|
2995 g_node_append (node, child); |
|
2996 qtdemux_parse_node (qtdemux, child, buf, len); |
|
2997 |
|
2998 buf += len; |
|
2999 } |
|
3000 return TRUE; |
|
3001 } |
|
3002 |
|
3003 static gboolean |
|
3004 qtdemux_parse_theora_extension (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
3005 GNode * xdxt) |
|
3006 { |
|
3007 int len = QT_UINT32 (xdxt->data); |
|
3008 guint8 *buf = xdxt->data; |
|
3009 guint8 *end = buf + len; |
|
3010 GstBuffer *buffer; |
|
3011 |
|
3012 /* skip size and type */ |
|
3013 buf += 8; |
|
3014 end -= 8; |
|
3015 |
|
3016 while (buf < end) { |
|
3017 gint size; |
|
3018 guint32 type; |
|
3019 |
|
3020 size = QT_UINT32 (buf); |
|
3021 type = QT_FOURCC (buf + 4); |
|
3022 |
|
3023 GST_LOG_OBJECT (qtdemux, "%p %p", buf, end); |
|
3024 |
|
3025 if (buf + size > end || size <= 0) |
|
3026 break; |
|
3027 |
|
3028 buf += 8; |
|
3029 size -= 8; |
|
3030 |
|
3031 GST_WARNING_OBJECT (qtdemux, "have cookie %" GST_FOURCC_FORMAT, |
|
3032 GST_FOURCC_ARGS (type)); |
|
3033 |
|
3034 switch (type) { |
|
3035 case FOURCC_tCtH: |
|
3036 buffer = gst_buffer_new_and_alloc (size); |
|
3037 memcpy (GST_BUFFER_DATA (buffer), buf, size); |
|
3038 stream->buffers = g_slist_append (stream->buffers, buffer); |
|
3039 GST_LOG_OBJECT (qtdemux, "parsing theora header"); |
|
3040 break; |
|
3041 case FOURCC_tCt_: |
|
3042 buffer = gst_buffer_new_and_alloc (size); |
|
3043 memcpy (GST_BUFFER_DATA (buffer), buf, size); |
|
3044 stream->buffers = g_slist_append (stream->buffers, buffer); |
|
3045 GST_LOG_OBJECT (qtdemux, "parsing theora comment"); |
|
3046 break; |
|
3047 case FOURCC_tCtC: |
|
3048 buffer = gst_buffer_new_and_alloc (size); |
|
3049 memcpy (GST_BUFFER_DATA (buffer), buf, size); |
|
3050 stream->buffers = g_slist_append (stream->buffers, buffer); |
|
3051 GST_LOG_OBJECT (qtdemux, "parsing theora codebook"); |
|
3052 break; |
|
3053 default: |
|
3054 GST_WARNING_OBJECT (qtdemux, |
|
3055 "unknown theora cookie %" GST_FOURCC_FORMAT, |
|
3056 GST_FOURCC_ARGS (type)); |
|
3057 break; |
|
3058 } |
|
3059 buf += size; |
|
3060 } |
|
3061 return TRUE; |
|
3062 } |
|
3063 |
|
3064 static gboolean |
|
3065 qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer, |
|
3066 int length) |
|
3067 { |
|
3068 guint32 fourcc; |
|
3069 guint32 node_length; |
|
3070 const QtNodeType *type; |
|
3071 const guint8 *end; |
|
3072 |
|
3073 GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %d", buffer, length); |
|
3074 |
|
3075 node_length = QT_UINT32 (buffer); |
|
3076 fourcc = QT_FOURCC (buffer + 4); |
|
3077 |
|
3078 /* ignore empty nodes */ |
|
3079 if (G_UNLIKELY (fourcc == 0 || node_length == 8)) |
|
3080 return TRUE; |
|
3081 |
|
3082 type = qtdemux_type_get (fourcc); |
|
3083 |
|
3084 end = buffer + length; |
|
3085 |
|
3086 GST_LOG_OBJECT (qtdemux, |
|
3087 "parsing '%" GST_FOURCC_FORMAT "', length=%d, name '%s'", |
|
3088 GST_FOURCC_ARGS (fourcc), node_length, type->name); |
|
3089 |
|
3090 if (type->flags & QT_FLAG_CONTAINER) { |
|
3091 qtdemux_parse_container (qtdemux, node, buffer + 8, end); |
|
3092 } else { |
|
3093 switch (fourcc) { |
|
3094 case FOURCC_stsd: |
|
3095 { |
|
3096 if (node_length < 20) { |
|
3097 GST_LOG_OBJECT (qtdemux, "skipping small stsd box"); |
|
3098 break; |
|
3099 } |
|
3100 GST_DEBUG_OBJECT (qtdemux, |
|
3101 "parsing stsd (sample table, sample description) atom"); |
|
3102 qtdemux_parse_container (qtdemux, node, buffer + 16, end); |
|
3103 break; |
|
3104 } |
|
3105 case FOURCC_mp4a: |
|
3106 { |
|
3107 guint32 version; |
|
3108 guint32 offset; |
|
3109 |
|
3110 if (length < 20) { |
|
3111 /* small boxes are also inside wave inside the mp4a box */ |
|
3112 GST_LOG_OBJECT (qtdemux, "skipping small mp4a box"); |
|
3113 break; |
|
3114 } |
|
3115 version = QT_UINT32 (buffer + 16); |
|
3116 |
|
3117 GST_DEBUG_OBJECT (qtdemux, "mp4a version 0x%08x", version); |
|
3118 |
|
3119 /* parse any esds descriptors */ |
|
3120 switch (version) { |
|
3121 case 0x00000000: |
|
3122 offset = 0x24; |
|
3123 break; |
|
3124 case 0x00010000: |
|
3125 offset = 0x34; |
|
3126 break; |
|
3127 case 0x00020000: |
|
3128 offset = 0x58; |
|
3129 break; |
|
3130 default: |
|
3131 GST_WARNING_OBJECT (qtdemux, "unhandled mp4a version 0x%08x", |
|
3132 version); |
|
3133 offset = 0; |
|
3134 break; |
|
3135 } |
|
3136 if (offset) |
|
3137 qtdemux_parse_container (qtdemux, node, buffer + offset, end); |
|
3138 break; |
|
3139 } |
|
3140 case FOURCC_mp4v: |
|
3141 { |
|
3142 const guint8 *buf; |
|
3143 guint32 version; |
|
3144 int tlen; |
|
3145 |
|
3146 GST_DEBUG_OBJECT (qtdemux, "parsing in mp4v"); |
|
3147 version = QT_UINT32 (buffer + 16); |
|
3148 GST_DEBUG_OBJECT (qtdemux, "version %08x", version); |
|
3149 if (1 || version == 0x00000000) { |
|
3150 buf = buffer + 0x32; |
|
3151 |
|
3152 /* FIXME Quicktime uses PASCAL string while |
|
3153 * the iso format uses C strings. Check the file |
|
3154 * type before attempting to parse the string here. */ |
|
3155 tlen = QT_UINT8 (buf); |
|
3156 GST_DEBUG_OBJECT (qtdemux, "tlen = %d", tlen); |
|
3157 buf++; |
|
3158 GST_DEBUG_OBJECT (qtdemux, "string = %.*s", tlen, (char *) buf); |
|
3159 /* the string has a reserved space of 32 bytes so skip |
|
3160 * the remaining 31 */ |
|
3161 buf += 31; |
|
3162 buf += 4; /* and 4 bytes reserved */ |
|
3163 |
|
3164 GST_MEMDUMP_OBJECT (qtdemux, "mp4v", buf, end - buf); |
|
3165 |
|
3166 qtdemux_parse_container (qtdemux, node, buf, end); |
|
3167 } |
|
3168 break; |
|
3169 } |
|
3170 case FOURCC_mjp2: |
|
3171 { |
|
3172 qtdemux_parse_container (qtdemux, node, buffer + 86, end); |
|
3173 break; |
|
3174 } |
|
3175 case FOURCC_meta: |
|
3176 { |
|
3177 GST_DEBUG_OBJECT (qtdemux, "parsing meta atom"); |
|
3178 qtdemux_parse_container (qtdemux, node, buffer + 12, end); |
|
3179 break; |
|
3180 } |
|
3181 case FOURCC_XiTh: |
|
3182 { |
|
3183 guint32 version; |
|
3184 guint32 offset; |
|
3185 |
|
3186 version = QT_UINT32 (buffer + 12); |
|
3187 GST_DEBUG_OBJECT (qtdemux, "parsing XiTh atom version 0x%08x", version); |
|
3188 |
|
3189 switch (version) { |
|
3190 case 0x00000001: |
|
3191 offset = 0x62; |
|
3192 break; |
|
3193 default: |
|
3194 GST_DEBUG_OBJECT (qtdemux, "unknown version 0x%08x", version); |
|
3195 offset = 0; |
|
3196 break; |
|
3197 } |
|
3198 if (offset) |
|
3199 qtdemux_parse_container (qtdemux, node, buffer + offset, end); |
|
3200 break; |
|
3201 } |
|
3202 case FOURCC_in24: |
|
3203 { |
|
3204 qtdemux_parse_container (qtdemux, node, buffer + 0x34, end); |
|
3205 break; |
|
3206 } |
|
3207 default: |
|
3208 if (!strcmp (type->name, "unknown")) |
|
3209 GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4); |
|
3210 break; |
|
3211 } |
|
3212 } |
|
3213 GST_LOG_OBJECT (qtdemux, "parsed '%" GST_FOURCC_FORMAT, |
|
3214 GST_FOURCC_ARGS (fourcc)); |
|
3215 return TRUE; |
|
3216 } |
|
3217 |
|
3218 static GNode * |
|
3219 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc) |
|
3220 { |
|
3221 GNode *child; |
|
3222 guint8 *buffer; |
|
3223 guint32 child_fourcc; |
|
3224 |
|
3225 for (child = g_node_first_child (node); child; |
|
3226 child = g_node_next_sibling (child)) { |
|
3227 buffer = (guint8 *) child->data; |
|
3228 |
|
3229 child_fourcc = QT_FOURCC (buffer + 4); |
|
3230 |
|
3231 if (G_UNLIKELY (child_fourcc == fourcc)) { |
|
3232 return child; |
|
3233 } |
|
3234 } |
|
3235 return NULL; |
|
3236 } |
|
3237 |
|
3238 static GNode * |
|
3239 qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc) |
|
3240 { |
|
3241 GNode *child; |
|
3242 guint8 *buffer; |
|
3243 guint32 child_fourcc; |
|
3244 |
|
3245 for (child = g_node_next_sibling (node); child; |
|
3246 child = g_node_next_sibling (child)) { |
|
3247 buffer = (guint8 *) child->data; |
|
3248 |
|
3249 child_fourcc = QT_FOURCC (buffer + 4); |
|
3250 |
|
3251 if (child_fourcc == fourcc) { |
|
3252 return child; |
|
3253 } |
|
3254 } |
|
3255 return NULL; |
|
3256 } |
|
3257 |
|
3258 static gboolean |
|
3259 gst_qtdemux_add_stream (GstQTDemux * qtdemux, |
|
3260 QtDemuxStream * stream, GstTagList * list) |
|
3261 { |
|
3262 if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS) |
|
3263 goto too_many_streams; |
|
3264 |
|
3265 if (stream->subtype == FOURCC_vide) { |
|
3266 gchar *name = g_strdup_printf ("video_%02d", qtdemux->n_video_streams); |
|
3267 |
|
3268 stream->pad = |
|
3269 gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name); |
|
3270 g_free (name); |
|
3271 |
|
3272 /* fps is calculated base on the duration of the first frames since |
|
3273 * qt does not have a fixed framerate. */ |
|
3274 if ((stream->n_samples == 1) && (stream->min_duration == 0)) { |
|
3275 /* still frame */ |
|
3276 stream->fps_n = 0; |
|
3277 stream->fps_d = 1; |
|
3278 } else { |
|
3279 stream->fps_n = stream->timescale; |
|
3280 if (stream->min_duration == 0) |
|
3281 stream->fps_d = 1; |
|
3282 else |
|
3283 stream->fps_d = stream->min_duration; |
|
3284 } |
|
3285 |
|
3286 if (stream->caps) { |
|
3287 gboolean gray; |
|
3288 gint depth, palette_count; |
|
3289 const guint32 *palette_data = NULL; |
|
3290 |
|
3291 gst_caps_set_simple (stream->caps, |
|
3292 "width", G_TYPE_INT, stream->width, |
|
3293 "height", G_TYPE_INT, stream->height, |
|
3294 "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL); |
|
3295 |
|
3296 /* iso files: |
|
3297 * calculate pixel-aspect-ratio using display width and height */ |
|
3298 if (qtdemux->major_brand != FOURCC_qt__) { |
|
3299 GST_DEBUG_OBJECT (qtdemux, |
|
3300 "video size %dx%d, target display size %dx%d", stream->width, |
|
3301 stream->height, stream->display_width, stream->display_height); |
|
3302 |
|
3303 if (stream->display_width > 0 && stream->display_height > 0 && |
|
3304 stream->width > 0 && stream->height > 0) { |
|
3305 gint n, d; |
|
3306 |
|
3307 /* calculate the pixel aspect ratio using the display and pixel w/h */ |
|
3308 n = stream->display_width * stream->height; |
|
3309 d = stream->display_height * stream->width; |
|
3310 if (n != d) { |
|
3311 GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d); |
|
3312 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio", |
|
3313 GST_TYPE_FRACTION, n, d, NULL); |
|
3314 } |
|
3315 } |
|
3316 } |
|
3317 |
|
3318 /* qt file might have pasp atom */ |
|
3319 if (stream->par_w > 0 && stream->par_h > 0) { |
|
3320 GST_DEBUG_OBJECT (qtdemux, "par %d:%d", stream->par_w, stream->par_h); |
|
3321 gst_caps_set_simple (stream->caps, "pixel-aspect-ratio", |
|
3322 GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL); |
|
3323 } |
|
3324 |
|
3325 depth = stream->bits_per_sample; |
|
3326 |
|
3327 /* more than 32 bits means grayscale */ |
|
3328 gray = (depth > 32); |
|
3329 /* low 32 bits specify the depth */ |
|
3330 depth &= 0x1F; |
|
3331 |
|
3332 /* different number of palette entries is determined by depth. */ |
|
3333 palette_count = 0; |
|
3334 if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8)) |
|
3335 palette_count = (1 << depth); |
|
3336 |
|
3337 switch (palette_count) { |
|
3338 case 0: |
|
3339 break; |
|
3340 case 2: |
|
3341 palette_data = ff_qt_default_palette_2; |
|
3342 break; |
|
3343 case 4: |
|
3344 palette_data = ff_qt_default_palette_4; |
|
3345 break; |
|
3346 case 16: |
|
3347 if (gray) |
|
3348 palette_data = ff_qt_grayscale_palette_16; |
|
3349 else |
|
3350 palette_data = ff_qt_default_palette_16; |
|
3351 break; |
|
3352 case 256: |
|
3353 if (gray) |
|
3354 palette_data = ff_qt_grayscale_palette_256; |
|
3355 else |
|
3356 palette_data = ff_qt_default_palette_256; |
|
3357 break; |
|
3358 default: |
|
3359 GST_ELEMENT_WARNING (qtdemux, STREAM, DECODE, |
|
3360 (_("The video in this file might not play correctly.")), |
|
3361 ("unsupported palette depth %d", depth)); |
|
3362 break; |
|
3363 } |
|
3364 if (palette_data) { |
|
3365 GstBuffer *palette; |
|
3366 |
|
3367 /* make sure it's not writable. We leave MALLOCDATA to NULL so that we |
|
3368 * don't free any of the buffer data. */ |
|
3369 palette = gst_buffer_new (); |
|
3370 GST_BUFFER_FLAG_SET (palette, GST_BUFFER_FLAG_READONLY); |
|
3371 GST_BUFFER_DATA (palette) = (guint8 *) palette_data; |
|
3372 GST_BUFFER_SIZE (palette) = sizeof (guint32) * palette_count; |
|
3373 |
|
3374 gst_caps_set_simple (stream->caps, "palette_data", |
|
3375 GST_TYPE_BUFFER, palette, NULL); |
|
3376 gst_buffer_unref (palette); |
|
3377 } else if (palette_count != 0) { |
|
3378 GST_ELEMENT_WARNING (qtdemux, STREAM, NOT_IMPLEMENTED, |
|
3379 (NULL), ("Unsupported palette depth %d. Ignoring stream.", depth)); |
|
3380 |
|
3381 gst_object_unref (stream->pad); |
|
3382 stream->pad = NULL; |
|
3383 } |
|
3384 } |
|
3385 qtdemux->n_video_streams++; |
|
3386 } else if (stream->subtype == FOURCC_soun) { |
|
3387 gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams); |
|
3388 |
|
3389 stream->pad = |
|
3390 gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name); |
|
3391 g_free (name); |
|
3392 if (stream->caps) { |
|
3393 gst_caps_set_simple (stream->caps, |
|
3394 "rate", G_TYPE_INT, (int) stream->rate, |
|
3395 "channels", G_TYPE_INT, stream->n_channels, NULL); |
|
3396 } |
|
3397 qtdemux->n_audio_streams++; |
|
3398 } else if (stream->subtype == FOURCC_strm) { |
|
3399 GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad"); |
|
3400 } else if (stream->subtype == FOURCC_subp) { |
|
3401 gchar *name = g_strdup_printf ("subp_%02d", qtdemux->n_subp_streams); |
|
3402 |
|
3403 stream->pad = |
|
3404 gst_pad_new_from_static_template (&gst_qtdemux_subpsrc_template, name); |
|
3405 g_free (name); |
|
3406 qtdemux->n_subp_streams++; |
|
3407 } else { |
|
3408 GST_DEBUG_OBJECT (qtdemux, "unknown stream type"); |
|
3409 goto done; |
|
3410 } |
|
3411 |
|
3412 qtdemux->streams[qtdemux->n_streams] = stream; |
|
3413 qtdemux->n_streams++; |
|
3414 GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams); |
|
3415 |
|
3416 if (stream->pad) { |
|
3417 GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream; |
|
3418 |
|
3419 gst_pad_use_fixed_caps (stream->pad); |
|
3420 gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event); |
|
3421 gst_pad_set_query_type_function (stream->pad, |
|
3422 gst_qtdemux_get_src_query_types); |
|
3423 gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query); |
|
3424 |
|
3425 GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps); |
|
3426 gst_pad_set_caps (stream->pad, stream->caps); |
|
3427 |
|
3428 GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p", |
|
3429 GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux); |
|
3430 gst_pad_set_active (stream->pad, TRUE); |
|
3431 gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad); |
|
3432 if (list) |
|
3433 gst_element_found_tags_for_pad (GST_ELEMENT_CAST (qtdemux), stream->pad, |
|
3434 list); |
|
3435 } |
|
3436 done: |
|
3437 return TRUE; |
|
3438 |
|
3439 too_many_streams: |
|
3440 { |
|
3441 GST_ELEMENT_WARNING (qtdemux, STREAM, DECODE, |
|
3442 (_("This file contains too many streams. Only playing first %d"), |
|
3443 GST_QTDEMUX_MAX_STREAMS), (NULL)); |
|
3444 return TRUE; |
|
3445 } |
|
3446 } |
|
3447 |
|
3448 /* collect all samples for @stream by reading the info from @stbl |
|
3449 */ |
|
3450 static gboolean |
|
3451 qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
3452 GNode * stbl) |
|
3453 { |
|
3454 GNode *stsc; |
|
3455 GNode *stsz; |
|
3456 GNode *stco; |
|
3457 GNode *co64; |
|
3458 GNode *stts; |
|
3459 GNode *stss; |
|
3460 GNode *stps; |
|
3461 GNode *ctts; |
|
3462 const guint8 *stsc_data, *stsz_data, *stco_data, *co64_data, *stts_data; |
|
3463 int sample_size; |
|
3464 int sample_index; |
|
3465 int n_samples; |
|
3466 int n_samples_per_chunk; |
|
3467 int n_sample_times; |
|
3468 QtDemuxSample *samples; |
|
3469 gint i, j, k; |
|
3470 int index; |
|
3471 guint64 timestamp, time; |
|
3472 |
|
3473 /* sample to chunk */ |
|
3474 if (!(stsc = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsc))) |
|
3475 goto corrupt_file; |
|
3476 stsc_data = (const guint8 *) stsc->data; |
|
3477 |
|
3478 /* sample size */ |
|
3479 if (!(stsz = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsz))) |
|
3480 goto corrupt_file; |
|
3481 stsz_data = (const guint8 *) stsz->data; |
|
3482 |
|
3483 /* chunk offsets */ |
|
3484 stco = qtdemux_tree_get_child_by_type (stbl, FOURCC_stco); |
|
3485 co64 = qtdemux_tree_get_child_by_type (stbl, FOURCC_co64); |
|
3486 if (stco) { |
|
3487 stco_data = (const guint8 *) stco->data; |
|
3488 co64_data = NULL; |
|
3489 } else { |
|
3490 stco_data = NULL; |
|
3491 if (co64 == NULL) |
|
3492 goto corrupt_file; |
|
3493 co64_data = (const guint8 *) co64->data; |
|
3494 } |
|
3495 /* sample time */ |
|
3496 if (!(stts = qtdemux_tree_get_child_by_type (stbl, FOURCC_stts))) |
|
3497 goto corrupt_file; |
|
3498 stts_data = (const guint8 *) stts->data; |
|
3499 |
|
3500 sample_size = QT_UINT32 (stsz_data + 12); |
|
3501 if (sample_size == 0 || stream->sampled) { |
|
3502 n_samples = QT_UINT32 (stsz_data + 16); |
|
3503 |
|
3504 if (n_samples == 0) |
|
3505 goto no_samples; |
|
3506 else if (n_samples < 0) |
|
3507 goto corrupt_file; |
|
3508 |
|
3509 GST_DEBUG_OBJECT (qtdemux, "stsz sample_size 0, allocating n_samples %d", |
|
3510 n_samples); |
|
3511 |
|
3512 samples = g_try_new0 (QtDemuxSample, n_samples); |
|
3513 if (samples == NULL) |
|
3514 goto out_of_memory; |
|
3515 |
|
3516 stream->n_samples = n_samples; |
|
3517 stream->samples = samples; |
|
3518 |
|
3519 /* set the sample sizes */ |
|
3520 if (sample_size == 0) { |
|
3521 const guint8 *stsz_p = stsz_data + 20; |
|
3522 /* different sizes for each sample */ |
|
3523 for (i = 0; i < n_samples; i++) { |
|
3524 samples[i].size = QT_UINT32 (stsz_p); |
|
3525 GST_LOG_OBJECT (qtdemux, "sample %d has size %d", i, samples[i].size); |
|
3526 stsz_p += 4; |
|
3527 } |
|
3528 } else { |
|
3529 /* samples have the same size */ |
|
3530 GST_LOG_OBJECT (qtdemux, "all samples have size %d", sample_size); |
|
3531 for (i = 0; i < n_samples; i++) |
|
3532 samples[i].size = sample_size; |
|
3533 } |
|
3534 |
|
3535 /* set the sample offsets in the file */ |
|
3536 n_samples_per_chunk = QT_UINT32 (stsc_data + 12); |
|
3537 index = 0; |
|
3538 for (i = 0; i < n_samples_per_chunk; i++) { |
|
3539 guint32 first_chunk, last_chunk; |
|
3540 guint32 samples_per_chunk; |
|
3541 |
|
3542 first_chunk = QT_UINT32 (stsc_data + 16 + i * 12 + 0) - 1; |
|
3543 if (G_UNLIKELY (i == n_samples_per_chunk - 1)) { |
|
3544 last_chunk = G_MAXUINT32; |
|
3545 } else { |
|
3546 last_chunk = QT_UINT32 (stsc_data + 16 + i * 12 + 12) - 1; |
|
3547 } |
|
3548 samples_per_chunk = QT_UINT32 (stsc_data + 16 + i * 12 + 4); |
|
3549 |
|
3550 for (j = first_chunk; j < last_chunk; j++) { |
|
3551 guint64 chunk_offset; |
|
3552 |
|
3553 if (stco) { |
|
3554 chunk_offset = QT_UINT32 (stco_data + 16 + j * 4); |
|
3555 } else { |
|
3556 chunk_offset = QT_UINT64 (co64_data + 16 + j * 8); |
|
3557 } |
|
3558 for (k = 0; k < samples_per_chunk; k++) { |
|
3559 GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %lld", |
|
3560 index, chunk_offset); |
|
3561 samples[index].offset = chunk_offset; |
|
3562 chunk_offset += samples[index].size; |
|
3563 index++; |
|
3564 if (G_UNLIKELY (index >= n_samples)) |
|
3565 goto done2; |
|
3566 } |
|
3567 } |
|
3568 } |
|
3569 done2: |
|
3570 |
|
3571 n_sample_times = QT_UINT32 (stts_data + 12); |
|
3572 GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", n_sample_times); |
|
3573 timestamp = 0; |
|
3574 stream->min_duration = 0; |
|
3575 time = 0; |
|
3576 index = 0; |
|
3577 stts_data += 16; |
|
3578 for (i = 0; i < n_sample_times; i++) { |
|
3579 guint32 n; |
|
3580 guint32 duration; |
|
3581 |
|
3582 n = QT_UINT32 (stts_data); |
|
3583 stts_data += 4; |
|
3584 duration = QT_UINT32 (stts_data); |
|
3585 stts_data += 4; |
|
3586 GST_LOG_OBJECT (qtdemux, "block %d, %u timestamps, duration %u ", i, n, |
|
3587 duration); |
|
3588 |
|
3589 /* take first duration for fps */ |
|
3590 if (G_UNLIKELY (stream->min_duration == 0)) |
|
3591 stream->min_duration = duration; |
|
3592 |
|
3593 for (j = 0; j < n; j++) { |
|
3594 GST_DEBUG_OBJECT (qtdemux, |
|
3595 "sample %d: index %d, timestamp %" GST_TIME_FORMAT, index, j, |
|
3596 GST_TIME_ARGS (timestamp)); |
|
3597 |
|
3598 samples[index].timestamp = timestamp; |
|
3599 /* add non-scaled values to avoid rounding errors */ |
|
3600 time += duration; |
|
3601 timestamp = gst_util_uint64_scale (time, GST_SECOND, stream->timescale); |
|
3602 samples[index].duration = timestamp - samples[index].timestamp; |
|
3603 |
|
3604 index++; |
|
3605 if (G_UNLIKELY (index >= n_samples)) |
|
3606 goto done3; |
|
3607 } |
|
3608 } |
|
3609 /* fill up empty timestamps with the last timestamp, this can happen when |
|
3610 * the last samples do not decode and so we don't have timestamps for them. |
|
3611 * We however look at the last timestamp to estimate the track length so we |
|
3612 * need something in here. */ |
|
3613 for (; index < n_samples; index++) { |
|
3614 GST_DEBUG_OBJECT (qtdemux, "fill sample %d: timestamp %" GST_TIME_FORMAT, |
|
3615 index, GST_TIME_ARGS (timestamp)); |
|
3616 samples[index].timestamp = timestamp; |
|
3617 samples[index].duration = -1; |
|
3618 } |
|
3619 done3: |
|
3620 |
|
3621 /* sample sync, can be NULL */ |
|
3622 stss = qtdemux_tree_get_child_by_type (stbl, FOURCC_stss); |
|
3623 |
|
3624 if (stss) { |
|
3625 /* mark keyframes */ |
|
3626 guint32 n_sample_syncs; |
|
3627 const guint8 *stss_p = (guint8 *) stss->data; |
|
3628 |
|
3629 stss_p += 12; |
|
3630 n_sample_syncs = QT_UINT32 (stss_p); |
|
3631 if (n_sample_syncs == 0) { |
|
3632 stream->all_keyframe = TRUE; |
|
3633 } else { |
|
3634 for (i = 0; i < n_sample_syncs; i++) { |
|
3635 stss_p += 4; |
|
3636 /* note that the first sample is index 1, not 0 */ |
|
3637 index = QT_UINT32 (stss_p); |
|
3638 if (G_LIKELY (index > 0 && index <= n_samples)) |
|
3639 samples[index - 1].keyframe = TRUE; |
|
3640 } |
|
3641 } |
|
3642 stps = qtdemux_tree_get_child_by_type (stbl, FOURCC_stps); |
|
3643 if (stps) { |
|
3644 /* stps marks partial sync frames like open GOP I-Frames */ |
|
3645 guint32 n_sample_syncs; |
|
3646 const guint8 *stps_p = (guint8 *) stps->data; |
|
3647 |
|
3648 stps_p += 12; |
|
3649 n_sample_syncs = QT_UINT32 (stps_p); |
|
3650 if (n_sample_syncs != 0) { |
|
3651 /* no entries, the stss table contains the real sync |
|
3652 * samples */ |
|
3653 } else { |
|
3654 for (i = 0; i < n_sample_syncs; i++) { |
|
3655 stps_p += 4; |
|
3656 /* note that the first sample is index 1, not 0 */ |
|
3657 index = QT_UINT32 (stps_p); |
|
3658 if (G_LIKELY (index > 0 && index <= n_samples)) |
|
3659 samples[index - 1].keyframe = TRUE; |
|
3660 } |
|
3661 } |
|
3662 } |
|
3663 } else { |
|
3664 /* no stss, all samples are keyframes */ |
|
3665 stream->all_keyframe = TRUE; |
|
3666 } |
|
3667 } else { |
|
3668 GST_DEBUG_OBJECT (qtdemux, |
|
3669 "stsz sample_size %d != 0, treating chunks as samples", sample_size); |
|
3670 /* treat chunks as samples */ |
|
3671 if (stco) { |
|
3672 n_samples = QT_UINT32 (stco_data + 12); |
|
3673 } else { |
|
3674 n_samples = QT_UINT32 (co64_data + 12); |
|
3675 } |
|
3676 |
|
3677 if (n_samples == 0) |
|
3678 goto no_samples; |
|
3679 else if (n_samples < 0) |
|
3680 goto corrupt_file; |
|
3681 |
|
3682 GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %d", n_samples); |
|
3683 |
|
3684 samples = g_try_new0 (QtDemuxSample, n_samples); |
|
3685 if (samples == NULL) |
|
3686 goto out_of_memory; |
|
3687 |
|
3688 stream->n_samples = n_samples; |
|
3689 stream->samples = samples; |
|
3690 |
|
3691 n_samples_per_chunk = QT_UINT32 (stsc_data + 12); |
|
3692 GST_DEBUG_OBJECT (qtdemux, "n_samples_per_chunk %d", n_samples_per_chunk); |
|
3693 sample_index = 0; |
|
3694 timestamp = 0; |
|
3695 for (i = 0; i < n_samples_per_chunk; i++) { |
|
3696 guint32 first_chunk, last_chunk; |
|
3697 guint32 samples_per_chunk; |
|
3698 |
|
3699 first_chunk = QT_UINT32 (stsc_data + 16 + i * 12 + 0) - 1; |
|
3700 /* the last chunk of each entry is calculated by taking the first chunk |
|
3701 * of the next entry; except if there is no next, where we fake it with |
|
3702 * INT_MAX */ |
|
3703 if (i == n_samples_per_chunk - 1) { |
|
3704 last_chunk = G_MAXUINT32; |
|
3705 } else { |
|
3706 last_chunk = QT_UINT32 (stsc_data + 16 + i * 12 + 12) - 1; |
|
3707 } |
|
3708 samples_per_chunk = QT_UINT32 (stsc_data + 16 + i * 12 + 4); |
|
3709 |
|
3710 GST_LOG_OBJECT (qtdemux, |
|
3711 "entry %d has first_chunk %d, last_chunk %d, samples_per_chunk %d", i, |
|
3712 first_chunk, last_chunk, samples_per_chunk); |
|
3713 |
|
3714 for (j = first_chunk; j < last_chunk; j++) { |
|
3715 guint64 chunk_offset; |
|
3716 |
|
3717 if (j >= n_samples) |
|
3718 goto done; |
|
3719 |
|
3720 if (stco) { |
|
3721 chunk_offset = QT_UINT32 (stco_data + 16 + j * 4); |
|
3722 } else { |
|
3723 chunk_offset = QT_UINT64 (co64_data + 16 + j * 8); |
|
3724 } |
|
3725 GST_LOG_OBJECT (qtdemux, |
|
3726 "Creating entry %d with offset %" G_GUINT64_FORMAT, j, |
|
3727 chunk_offset); |
|
3728 |
|
3729 samples[j].offset = chunk_offset; |
|
3730 |
|
3731 if (stream->samples_per_frame * stream->bytes_per_frame) { |
|
3732 samples[j].size = (samples_per_chunk * stream->n_channels) / |
|
3733 stream->samples_per_frame * stream->bytes_per_frame; |
|
3734 } else { |
|
3735 samples[j].size = samples_per_chunk; |
|
3736 } |
|
3737 |
|
3738 GST_DEBUG_OBJECT (qtdemux, "sample %d: timestamp %" GST_TIME_FORMAT |
|
3739 ", size %u", j, GST_TIME_ARGS (timestamp), samples[j].size); |
|
3740 |
|
3741 samples[j].timestamp = timestamp; |
|
3742 sample_index += samples_per_chunk; |
|
3743 |
|
3744 timestamp = gst_util_uint64_scale (sample_index, |
|
3745 GST_SECOND, stream->timescale); |
|
3746 samples[j].duration = timestamp - samples[j].timestamp; |
|
3747 |
|
3748 samples[j].keyframe = TRUE; |
|
3749 } |
|
3750 } |
|
3751 } |
|
3752 |
|
3753 /* composition time to sample */ |
|
3754 if ((ctts = qtdemux_tree_get_child_by_type (stbl, FOURCC_ctts))) { |
|
3755 const guint8 *ctts_data, *ctts_p; |
|
3756 guint32 n_entries; |
|
3757 guint32 count; |
|
3758 gint32 soffset; |
|
3759 |
|
3760 ctts_data = (const guint8 *) ctts->data; |
|
3761 n_entries = QT_UINT32 (ctts_data + 12); |
|
3762 |
|
3763 /* Fill in the pts_offsets */ |
|
3764 index = 0; |
|
3765 ctts_p = ctts_data + 16; |
|
3766 /* FIXME: make sure we don't read beyond the atom size/boundary */ |
|
3767 for (i = 0; i < n_entries; i++) { |
|
3768 count = QT_UINT32 (ctts_p); |
|
3769 ctts_p += 4; |
|
3770 soffset = QT_UINT32 (ctts_p); |
|
3771 ctts_p += 4; |
|
3772 for (j = 0; j < count; j++) { |
|
3773 /* we operate with very small soffset values here, it shouldn't overflow */ |
|
3774 samples[index].pts_offset = soffset * GST_SECOND / stream->timescale; |
|
3775 index++; |
|
3776 if (G_UNLIKELY (index >= n_samples)) |
|
3777 goto done; |
|
3778 } |
|
3779 } |
|
3780 } |
|
3781 done: |
|
3782 return TRUE; |
|
3783 |
|
3784 /* ERRORS */ |
|
3785 corrupt_file: |
|
3786 { |
|
3787 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE, |
|
3788 (_("This file is corrupt and cannot be played.")), (NULL)); |
|
3789 return FALSE; |
|
3790 } |
|
3791 no_samples: |
|
3792 { |
|
3793 GST_WARNING_OBJECT (qtdemux, "stream has no samples"); |
|
3794 return FALSE; |
|
3795 } |
|
3796 out_of_memory: |
|
3797 { |
|
3798 GST_WARNING_OBJECT (qtdemux, "failed to allocate %d samples", n_samples); |
|
3799 return FALSE; |
|
3800 } |
|
3801 } |
|
3802 |
|
3803 /* collect all segment info for @stream. |
|
3804 */ |
|
3805 static gboolean |
|
3806 qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
3807 GNode * trak) |
|
3808 { |
|
3809 GNode *edts; |
|
3810 |
|
3811 /* parse and prepare segment info from the edit list */ |
|
3812 GST_DEBUG_OBJECT (qtdemux, "looking for edit list container"); |
|
3813 stream->n_segments = 0; |
|
3814 stream->segments = NULL; |
|
3815 if ((edts = qtdemux_tree_get_child_by_type (trak, FOURCC_edts))) { |
|
3816 GNode *elst; |
|
3817 gint n_segments; |
|
3818 gint i, count; |
|
3819 guint64 time, stime; |
|
3820 guint8 *buffer; |
|
3821 |
|
3822 GST_DEBUG_OBJECT (qtdemux, "looking for edit list"); |
|
3823 if (!(elst = qtdemux_tree_get_child_by_type (edts, FOURCC_elst))) |
|
3824 goto done; |
|
3825 |
|
3826 buffer = elst->data; |
|
3827 |
|
3828 n_segments = QT_UINT32 (buffer + 12); |
|
3829 |
|
3830 /* we might allocate a bit too much, at least allocate 1 segment */ |
|
3831 stream->segments = g_new (QtDemuxSegment, MAX (n_segments, 1)); |
|
3832 |
|
3833 /* segments always start from 0 */ |
|
3834 time = 0; |
|
3835 stime = 0; |
|
3836 count = 0; |
|
3837 for (i = 0; i < n_segments; i++) { |
|
3838 guint64 duration; |
|
3839 guint64 media_time; |
|
3840 QtDemuxSegment *segment; |
|
3841 guint32 rate_int; |
|
3842 |
|
3843 media_time = QT_UINT32 (buffer + 20 + i * 12); |
|
3844 |
|
3845 /* -1 media time is an empty segment, just ignore it */ |
|
3846 if (media_time == G_MAXUINT32) |
|
3847 continue; |
|
3848 |
|
3849 duration = QT_UINT32 (buffer + 16 + i * 12); |
|
3850 |
|
3851 segment = &stream->segments[count++]; |
|
3852 |
|
3853 /* time and duration expressed in global timescale */ |
|
3854 segment->time = stime; |
|
3855 /* add non scaled values so we don't cause roundoff errors */ |
|
3856 time += duration; |
|
3857 stime = gst_util_uint64_scale (time, GST_SECOND, qtdemux->timescale); |
|
3858 segment->stop_time = stime; |
|
3859 segment->duration = stime - segment->time; |
|
3860 /* media_time expressed in stream timescale */ |
|
3861 segment->media_start = |
|
3862 gst_util_uint64_scale (media_time, GST_SECOND, stream->timescale); |
|
3863 segment->media_stop = segment->media_start + segment->duration; |
|
3864 rate_int = GST_READ_UINT32_BE (buffer + 24 + i * 12); |
|
3865 |
|
3866 if (rate_int <= 1) { |
|
3867 /* 0 is not allowed, some programs write 1 instead of the floating point |
|
3868 * value */ |
|
3869 GST_WARNING_OBJECT (qtdemux, "found suspicious rate %" G_GUINT32_FORMAT, |
|
3870 rate_int); |
|
3871 segment->rate = 1; |
|
3872 } else { |
|
3873 segment->rate = rate_int / 65536.0; |
|
3874 } |
|
3875 |
|
3876 GST_DEBUG_OBJECT (qtdemux, "created segment %d time %" GST_TIME_FORMAT |
|
3877 ", duration %" GST_TIME_FORMAT ", media_time %" GST_TIME_FORMAT |
|
3878 ", rate %g, (%d)", i, GST_TIME_ARGS (segment->time), |
|
3879 GST_TIME_ARGS (segment->duration), |
|
3880 GST_TIME_ARGS (segment->media_start), segment->rate, rate_int); |
|
3881 } |
|
3882 GST_DEBUG_OBJECT (qtdemux, "found %d non-empty segments", count); |
|
3883 stream->n_segments = count; |
|
3884 } |
|
3885 done: |
|
3886 |
|
3887 /* push based does not handle segments, so act accordingly here, |
|
3888 * and warn if applicable */ |
|
3889 if (!qtdemux->pullbased) { |
|
3890 GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments"); |
|
3891 /* remove and use default one below, we stream like it anyway */ |
|
3892 g_free (stream->segments); |
|
3893 stream->segments = NULL; |
|
3894 stream->n_segments = 0; |
|
3895 } |
|
3896 |
|
3897 /* no segments, create one to play the complete trak */ |
|
3898 if (stream->n_segments == 0) { |
|
3899 GstClockTime stream_duration = 0; |
|
3900 |
|
3901 if (stream->segments == NULL) |
|
3902 stream->segments = g_new (QtDemuxSegment, 1); |
|
3903 |
|
3904 /* samples know best */ |
|
3905 if (stream->n_samples > 0) { |
|
3906 stream_duration = |
|
3907 stream->samples[stream->n_samples - 1].timestamp + |
|
3908 stream->samples[stream->n_samples - 1].pts_offset + |
|
3909 stream->samples[stream->n_samples - 1].duration; |
|
3910 } |
|
3911 |
|
3912 stream->segments[0].time = 0; |
|
3913 stream->segments[0].stop_time = stream_duration; |
|
3914 stream->segments[0].duration = stream_duration; |
|
3915 stream->segments[0].media_start = 0; |
|
3916 stream->segments[0].media_stop = stream_duration; |
|
3917 stream->segments[0].rate = 1.0; |
|
3918 |
|
3919 GST_DEBUG_OBJECT (qtdemux, "created dummy segment %" GST_TIME_FORMAT, |
|
3920 GST_TIME_ARGS (stream_duration)); |
|
3921 stream->n_segments = 1; |
|
3922 } |
|
3923 GST_DEBUG_OBJECT (qtdemux, "using %d segments", stream->n_segments); |
|
3924 |
|
3925 return TRUE; |
|
3926 } |
|
3927 |
|
3928 /* parse the traks. |
|
3929 * With each track we associate a new QtDemuxStream that contains all the info |
|
3930 * about the trak. |
|
3931 * traks that do not decode to something (like strm traks) will not have a pad. |
|
3932 */ |
|
3933 static gboolean |
|
3934 qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) |
|
3935 { |
|
3936 int offset; |
|
3937 GNode *tkhd; |
|
3938 GNode *mdia; |
|
3939 GNode *mdhd; |
|
3940 GNode *hdlr; |
|
3941 GNode *minf; |
|
3942 GNode *stbl; |
|
3943 GNode *stsd; |
|
3944 GNode *mp4a; |
|
3945 GNode *mp4v; |
|
3946 GNode *wave; |
|
3947 GNode *esds; |
|
3948 GNode *pasp; |
|
3949 QtDemuxStream *stream; |
|
3950 GstTagList *list = NULL; |
|
3951 gchar *codec = NULL; |
|
3952 const guint8 *stsd_data; |
|
3953 guint32 version; |
|
3954 |
|
3955 stream = g_new0 (QtDemuxStream, 1); |
|
3956 /* new streams always need a discont */ |
|
3957 stream->discont = TRUE; |
|
3958 /* we enable clipping for raw audio/video streams */ |
|
3959 stream->need_clip = FALSE; |
|
3960 stream->segment_index = -1; |
|
3961 stream->time_position = 0; |
|
3962 stream->sample_index = -1; |
|
3963 stream->last_ret = GST_FLOW_OK; |
|
3964 |
|
3965 if (!(tkhd = qtdemux_tree_get_child_by_type (trak, FOURCC_tkhd))) |
|
3966 goto corrupt_file; |
|
3967 |
|
3968 GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags: 0x%08x", |
|
3969 QT_UINT32 ((guint8 *) tkhd->data + 8)); |
|
3970 |
|
3971 if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia))) |
|
3972 goto corrupt_file; |
|
3973 |
|
3974 if (!(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd))) { |
|
3975 /* be nice for some crooked mjp2 files that use mhdr for mdhd */ |
|
3976 if (qtdemux->major_brand != FOURCC_mjp2 || |
|
3977 !(mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mhdr))) |
|
3978 goto corrupt_file; |
|
3979 } |
|
3980 |
|
3981 version = QT_UINT32 ((guint8 *) mdhd->data + 8); |
|
3982 GST_LOG_OBJECT (qtdemux, "track version/flags: %08x", version); |
|
3983 if (version == 0x01000000) { |
|
3984 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 28); |
|
3985 stream->duration = QT_UINT64 ((guint8 *) mdhd->data + 32); |
|
3986 } else { |
|
3987 stream->timescale = QT_UINT32 ((guint8 *) mdhd->data + 20); |
|
3988 stream->duration = QT_UINT32 ((guint8 *) mdhd->data + 24); |
|
3989 } |
|
3990 |
|
3991 GST_LOG_OBJECT (qtdemux, "track timescale: %" G_GUINT32_FORMAT, |
|
3992 stream->timescale); |
|
3993 GST_LOG_OBJECT (qtdemux, "track duration: %" G_GUINT64_FORMAT, |
|
3994 stream->duration); |
|
3995 |
|
3996 if (G_UNLIKELY (stream->timescale == 0 || qtdemux->timescale == 0)) |
|
3997 goto corrupt_file; |
|
3998 |
|
3999 if (qtdemux->duration != G_MAXINT32 && stream->duration != G_MAXINT32) { |
|
4000 guint64 tdur1, tdur2; |
|
4001 |
|
4002 /* don't overflow */ |
|
4003 tdur1 = stream->timescale * (guint64) qtdemux->duration; |
|
4004 tdur2 = qtdemux->timescale * (guint64) stream->duration; |
|
4005 |
|
4006 /* HACK: |
|
4007 * some of those trailers, nowadays, have prologue images that are |
|
4008 * themselves vide tracks as well. I haven't really found a way to |
|
4009 * identify those yet, except for just looking at their duration. */ |
|
4010 if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) { |
|
4011 GST_WARNING_OBJECT (qtdemux, |
|
4012 "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT |
|
4013 " vs. %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT ") of the stream " |
|
4014 "found, assuming preview image or something; skipping track", |
|
4015 stream->duration, stream->timescale, qtdemux->duration, |
|
4016 qtdemux->timescale); |
|
4017 g_free (stream); |
|
4018 return TRUE; |
|
4019 } |
|
4020 } |
|
4021 |
|
4022 if (!(hdlr = qtdemux_tree_get_child_by_type (mdia, FOURCC_hdlr))) |
|
4023 goto corrupt_file; |
|
4024 |
|
4025 GST_LOG_OBJECT (qtdemux, "track type: %" GST_FOURCC_FORMAT, |
|
4026 GST_FOURCC_ARGS (QT_FOURCC ((guint8 *) hdlr->data + 12))); |
|
4027 |
|
4028 stream->subtype = QT_FOURCC ((guint8 *) hdlr->data + 16); |
|
4029 GST_LOG_OBJECT (qtdemux, "track subtype: %" GST_FOURCC_FORMAT, |
|
4030 GST_FOURCC_ARGS (stream->subtype)); |
|
4031 |
|
4032 if (!(minf = qtdemux_tree_get_child_by_type (mdia, FOURCC_minf))) |
|
4033 goto corrupt_file; |
|
4034 |
|
4035 if (!(stbl = qtdemux_tree_get_child_by_type (minf, FOURCC_stbl))) |
|
4036 goto corrupt_file; |
|
4037 |
|
4038 /* parse stsd */ |
|
4039 if (!(stsd = qtdemux_tree_get_child_by_type (stbl, FOURCC_stsd))) |
|
4040 goto corrupt_file; |
|
4041 stsd_data = (const guint8 *) stsd->data; |
|
4042 |
|
4043 if (stream->subtype == FOURCC_vide) { |
|
4044 guint32 fourcc; |
|
4045 const guint8 *tkhd_data = (const guint8 *) tkhd->data; |
|
4046 |
|
4047 stream->sampled = TRUE; |
|
4048 |
|
4049 /* version 1 uses some 64-bit ints */ |
|
4050 offset = (QT_UINT8 (tkhd_data + 8) == 1) ? 96 : 84; |
|
4051 stream->display_width = (guint) QT_FP32 (tkhd_data + offset); |
|
4052 stream->display_height = (guint) QT_FP32 (tkhd_data + offset + 4); |
|
4053 |
|
4054 offset = 16; |
|
4055 stream->fourcc = fourcc = QT_FOURCC (stsd_data + offset + 4); |
|
4056 GST_LOG_OBJECT (qtdemux, "st type: %" GST_FOURCC_FORMAT, |
|
4057 GST_FOURCC_ARGS (fourcc)); |
|
4058 |
|
4059 stream->width = QT_UINT16 (stsd_data + offset + 32); |
|
4060 stream->height = QT_UINT16 (stsd_data + offset + 34); |
|
4061 stream->fps_n = 0; /* this is filled in later */ |
|
4062 stream->fps_d = 0; /* this is filled in later */ |
|
4063 stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82); |
|
4064 stream->color_table_id = QT_UINT16 (stsd_data + offset + 84); |
|
4065 |
|
4066 GST_LOG_OBJECT (qtdemux, "frame count: %u", |
|
4067 QT_UINT16 (stsd_data + offset + 48)); |
|
4068 |
|
4069 if (fourcc == FOURCC_drms) |
|
4070 goto error_encrypted; |
|
4071 |
|
4072 stream->caps = |
|
4073 qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec); |
|
4074 if (codec) { |
|
4075 list = gst_tag_list_new (); |
|
4076 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, |
|
4077 GST_TAG_VIDEO_CODEC, codec, NULL); |
|
4078 g_free (codec); |
|
4079 codec = NULL; |
|
4080 } |
|
4081 |
|
4082 esds = NULL; |
|
4083 pasp = NULL; |
|
4084 mp4v = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4v); |
|
4085 if (mp4v) { |
|
4086 esds = qtdemux_tree_get_child_by_type (mp4v, FOURCC_esds); |
|
4087 pasp = qtdemux_tree_get_child_by_type (mp4v, FOURCC_pasp); |
|
4088 } |
|
4089 |
|
4090 if (pasp) { |
|
4091 const guint8 *pasp_data = (const guint8 *) pasp->data; |
|
4092 |
|
4093 stream->par_w = QT_UINT32 (pasp_data + 8); |
|
4094 stream->par_h = QT_UINT32 (pasp_data + 12); |
|
4095 } else { |
|
4096 stream->par_w = 0; |
|
4097 stream->par_h = 0; |
|
4098 } |
|
4099 |
|
4100 if (esds) { |
|
4101 gst_qtdemux_handle_esds (qtdemux, stream, esds, list); |
|
4102 } else { |
|
4103 switch (fourcc) { |
|
4104 case FOURCC_avc1: |
|
4105 { |
|
4106 gint len = QT_UINT32 (stsd_data) - 0x66; |
|
4107 const guint8 *avc_data = stsd_data + 0x66; |
|
4108 |
|
4109 /* find avcC */ |
|
4110 while (len >= 0x8 && |
|
4111 QT_FOURCC (avc_data + 0x4) != FOURCC_avcC && |
|
4112 QT_UINT32 (avc_data) < len) { |
|
4113 len -= QT_UINT32 (avc_data); |
|
4114 avc_data += QT_UINT32 (avc_data); |
|
4115 } |
|
4116 |
|
4117 /* parse, if found */ |
|
4118 if (len > 0x8 && QT_FOURCC (avc_data + 0x4) == FOURCC_avcC) { |
|
4119 GstBuffer *buf; |
|
4120 gint size; |
|
4121 |
|
4122 if (QT_UINT32 (avc_data) < len) |
|
4123 size = QT_UINT32 (avc_data) - 0x8; |
|
4124 else |
|
4125 size = len - 0x8; |
|
4126 |
|
4127 GST_DEBUG_OBJECT (qtdemux, "found avcC codec_data in stsd"); |
|
4128 |
|
4129 buf = gst_buffer_new_and_alloc (size); |
|
4130 memcpy (GST_BUFFER_DATA (buf), avc_data + 0x8, size); |
|
4131 gst_caps_set_simple (stream->caps, |
|
4132 "codec_data", GST_TYPE_BUFFER, buf, NULL); |
|
4133 gst_buffer_unref (buf); |
|
4134 } |
|
4135 break; |
|
4136 } |
|
4137 case FOURCC_mjp2: |
|
4138 { |
|
4139 GNode *jp2h, *colr, *mjp2, *field, *prefix; |
|
4140 const guint8 *data; |
|
4141 guint32 fourcc = 0; |
|
4142 |
|
4143 GST_DEBUG_OBJECT (qtdemux, "found mjp2"); |
|
4144 /* some required atoms */ |
|
4145 mjp2 = qtdemux_tree_get_child_by_type (stsd, FOURCC_mjp2); |
|
4146 if (!mjp2) |
|
4147 break; |
|
4148 jp2h = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2h); |
|
4149 if (!jp2h) |
|
4150 break; |
|
4151 colr = qtdemux_tree_get_child_by_type (jp2h, FOURCC_colr); |
|
4152 if (!colr) |
|
4153 break; |
|
4154 GST_DEBUG_OBJECT (qtdemux, "found colr"); |
|
4155 /* try to extract colour space info */ |
|
4156 if (QT_UINT8 ((guint8 *) colr->data + 8) == 1) { |
|
4157 switch (QT_UINT32 ((guint8 *) colr->data + 11)) { |
|
4158 case 16: |
|
4159 fourcc = GST_MAKE_FOURCC ('s', 'R', 'G', 'B'); |
|
4160 break; |
|
4161 case 17: |
|
4162 fourcc = GST_MAKE_FOURCC ('G', 'R', 'A', 'Y'); |
|
4163 break; |
|
4164 case 18: |
|
4165 fourcc = GST_MAKE_FOURCC ('s', 'Y', 'U', 'V'); |
|
4166 break; |
|
4167 default: |
|
4168 break; |
|
4169 } |
|
4170 } |
|
4171 |
|
4172 if (fourcc) |
|
4173 gst_caps_set_simple (stream->caps, |
|
4174 "fourcc", GST_TYPE_FOURCC, fourcc, NULL); |
|
4175 |
|
4176 /* some optional atoms */ |
|
4177 field = qtdemux_tree_get_child_by_type (mjp2, FOURCC_fiel); |
|
4178 prefix = qtdemux_tree_get_child_by_type (mjp2, FOURCC_jp2x); |
|
4179 |
|
4180 /* indicate possible fields in caps */ |
|
4181 if (field) { |
|
4182 data = (guint8 *) field->data + 8; |
|
4183 if (*data != 1) |
|
4184 gst_caps_set_simple (stream->caps, "fields", G_TYPE_INT, |
|
4185 (gint) * data, NULL); |
|
4186 } |
|
4187 /* add codec_data if provided */ |
|
4188 if (prefix) { |
|
4189 GstBuffer *buf; |
|
4190 gint len; |
|
4191 |
|
4192 GST_DEBUG_OBJECT (qtdemux, "found prefix data in stsd"); |
|
4193 data = prefix->data; |
|
4194 len = QT_UINT32 (data); |
|
4195 if (len > 0x8) { |
|
4196 len -= 0x8; |
|
4197 buf = gst_buffer_new_and_alloc (len); |
|
4198 memcpy (GST_BUFFER_DATA (buf), data + 8, len); |
|
4199 gst_caps_set_simple (stream->caps, |
|
4200 "codec_data", GST_TYPE_BUFFER, buf, NULL); |
|
4201 gst_buffer_unref (buf); |
|
4202 } |
|
4203 } |
|
4204 break; |
|
4205 } |
|
4206 case FOURCC_SVQ3: |
|
4207 case FOURCC_VP31: |
|
4208 { |
|
4209 GstBuffer *buf; |
|
4210 gint len = QT_UINT32 (stsd_data); |
|
4211 |
|
4212 GST_DEBUG_OBJECT (qtdemux, "found codec_data in stsd"); |
|
4213 |
|
4214 buf = gst_buffer_new_and_alloc (len); |
|
4215 memcpy (GST_BUFFER_DATA (buf), stsd_data, len); |
|
4216 gst_caps_set_simple (stream->caps, |
|
4217 "codec_data", GST_TYPE_BUFFER, buf, NULL); |
|
4218 gst_buffer_unref (buf); |
|
4219 break; |
|
4220 } |
|
4221 case FOURCC_rle_: |
|
4222 { |
|
4223 gst_caps_set_simple (stream->caps, |
|
4224 "depth", G_TYPE_INT, QT_UINT16 (stsd_data + offset + 82), NULL); |
|
4225 break; |
|
4226 } |
|
4227 case FOURCC_XiTh: |
|
4228 { |
|
4229 GNode *xith, *xdxt; |
|
4230 |
|
4231 GST_DEBUG_OBJECT (qtdemux, "found XiTh"); |
|
4232 xith = qtdemux_tree_get_child_by_type (stsd, FOURCC_XiTh); |
|
4233 if (!xith) |
|
4234 break; |
|
4235 |
|
4236 xdxt = qtdemux_tree_get_child_by_type (xith, FOURCC_XdxT); |
|
4237 if (!xdxt) |
|
4238 break; |
|
4239 |
|
4240 GST_DEBUG_OBJECT (qtdemux, "found XdxT node"); |
|
4241 /* collect the headers and store them in a stream list so that we can |
|
4242 * send them out first */ |
|
4243 qtdemux_parse_theora_extension (qtdemux, stream, xdxt); |
|
4244 break; |
|
4245 } |
|
4246 default: |
|
4247 break; |
|
4248 } |
|
4249 } |
|
4250 |
|
4251 GST_INFO_OBJECT (qtdemux, |
|
4252 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, |
|
4253 GST_FOURCC_ARGS (fourcc), stream->caps); |
|
4254 |
|
4255 } else if (stream->subtype == FOURCC_soun) { |
|
4256 int version, samplesize; |
|
4257 guint32 fourcc; |
|
4258 int len; |
|
4259 guint16 compression_id; |
|
4260 |
|
4261 len = QT_UINT32 (stsd_data + 16); |
|
4262 GST_LOG_OBJECT (qtdemux, "stsd len: %d", len); |
|
4263 |
|
4264 stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4); |
|
4265 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT, |
|
4266 GST_FOURCC_ARGS (stream->fourcc)); |
|
4267 |
|
4268 offset = 32; |
|
4269 |
|
4270 version = QT_UINT32 (stsd_data + offset); |
|
4271 stream->n_channels = QT_UINT16 (stsd_data + offset + 8); |
|
4272 samplesize = QT_UINT16 (stsd_data + offset + 10); |
|
4273 compression_id = QT_UINT16 (stsd_data + offset + 12); |
|
4274 stream->rate = QT_FP32 (stsd_data + offset + 16); |
|
4275 |
|
4276 GST_LOG_OBJECT (qtdemux, "version/rev: %08x", version); |
|
4277 GST_LOG_OBJECT (qtdemux, "vendor: %08x", |
|
4278 QT_UINT32 (stsd_data + offset + 4)); |
|
4279 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels); |
|
4280 GST_LOG_OBJECT (qtdemux, "sample_size: %d", samplesize); |
|
4281 GST_LOG_OBJECT (qtdemux, "compression_id: %d", compression_id); |
|
4282 GST_LOG_OBJECT (qtdemux, "packet size: %d", |
|
4283 QT_UINT16 (stsd_data + offset + 14)); |
|
4284 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate); |
|
4285 |
|
4286 if (compression_id == 0xfffe) |
|
4287 stream->sampled = TRUE; |
|
4288 |
|
4289 /* first assume uncompressed audio */ |
|
4290 stream->bytes_per_sample = samplesize / 8; |
|
4291 stream->samples_per_frame = stream->n_channels; |
|
4292 stream->bytes_per_frame = stream->n_channels * stream->bytes_per_sample; |
|
4293 stream->samples_per_packet = stream->samples_per_frame; |
|
4294 stream->bytes_per_packet = stream->bytes_per_sample; |
|
4295 |
|
4296 offset = 52; |
|
4297 switch (fourcc) { |
|
4298 /* Yes, these have to be hard-coded */ |
|
4299 case FOURCC_MAC6: |
|
4300 { |
|
4301 stream->samples_per_packet = 6; |
|
4302 stream->bytes_per_packet = 1; |
|
4303 stream->bytes_per_frame = 1 * stream->n_channels; |
|
4304 stream->bytes_per_sample = 1; |
|
4305 stream->samples_per_frame = 6 * stream->n_channels; |
|
4306 break; |
|
4307 } |
|
4308 case FOURCC_MAC3: |
|
4309 { |
|
4310 stream->samples_per_packet = 3; |
|
4311 stream->bytes_per_packet = 1; |
|
4312 stream->bytes_per_frame = 1 * stream->n_channels; |
|
4313 stream->bytes_per_sample = 1; |
|
4314 stream->samples_per_frame = 3 * stream->n_channels; |
|
4315 break; |
|
4316 } |
|
4317 case FOURCC_ima4: |
|
4318 { |
|
4319 stream->samples_per_packet = 64; |
|
4320 stream->bytes_per_packet = 34; |
|
4321 stream->bytes_per_frame = 34 * stream->n_channels; |
|
4322 stream->bytes_per_sample = 2; |
|
4323 stream->samples_per_frame = 64 * stream->n_channels; |
|
4324 break; |
|
4325 } |
|
4326 case FOURCC_ulaw: |
|
4327 case FOURCC_alaw: |
|
4328 { |
|
4329 stream->samples_per_packet = 1; |
|
4330 stream->bytes_per_packet = 1; |
|
4331 stream->bytes_per_frame = 1 * stream->n_channels; |
|
4332 stream->bytes_per_sample = 1; |
|
4333 stream->samples_per_frame = 1 * stream->n_channels; |
|
4334 break; |
|
4335 } |
|
4336 case FOURCC_agsm: |
|
4337 { |
|
4338 stream->samples_per_packet = 160; |
|
4339 stream->bytes_per_packet = 33; |
|
4340 stream->bytes_per_frame = 33 * stream->n_channels; |
|
4341 stream->bytes_per_sample = 2; |
|
4342 stream->samples_per_frame = 160 * stream->n_channels; |
|
4343 break; |
|
4344 } |
|
4345 default: |
|
4346 break; |
|
4347 } |
|
4348 |
|
4349 if (version == 0x00010000) { |
|
4350 switch (fourcc) { |
|
4351 case FOURCC_twos: |
|
4352 case FOURCC_sowt: |
|
4353 case FOURCC_raw_: |
|
4354 break; |
|
4355 default: |
|
4356 { |
|
4357 /* only parse extra decoding config for non-pcm audio */ |
|
4358 stream->samples_per_packet = QT_UINT32 (stsd_data + offset); |
|
4359 stream->bytes_per_packet = QT_UINT32 (stsd_data + offset + 4); |
|
4360 stream->bytes_per_frame = QT_UINT32 (stsd_data + offset + 8); |
|
4361 stream->bytes_per_sample = QT_UINT32 (stsd_data + offset + 12); |
|
4362 |
|
4363 GST_LOG_OBJECT (qtdemux, "samples/packet: %d", |
|
4364 stream->samples_per_packet); |
|
4365 GST_LOG_OBJECT (qtdemux, "bytes/packet: %d", |
|
4366 stream->bytes_per_packet); |
|
4367 GST_LOG_OBJECT (qtdemux, "bytes/frame: %d", |
|
4368 stream->bytes_per_frame); |
|
4369 GST_LOG_OBJECT (qtdemux, "bytes/sample: %d", |
|
4370 stream->bytes_per_sample); |
|
4371 |
|
4372 if (!stream->sampled && stream->bytes_per_packet) { |
|
4373 stream->samples_per_frame = (stream->bytes_per_frame / |
|
4374 stream->bytes_per_packet) * stream->samples_per_packet; |
|
4375 GST_LOG_OBJECT (qtdemux, "samples/frame: %d", |
|
4376 stream->samples_per_frame); |
|
4377 } |
|
4378 break; |
|
4379 } |
|
4380 } |
|
4381 } else if (version == 0x00020000) { |
|
4382 union |
|
4383 { |
|
4384 gdouble fp; |
|
4385 guint64 val; |
|
4386 } qtfp; |
|
4387 |
|
4388 stream->samples_per_packet = QT_UINT32 (stsd_data + offset); |
|
4389 qtfp.val = QT_UINT64 (stsd_data + offset + 4); |
|
4390 stream->rate = qtfp.fp; |
|
4391 stream->n_channels = QT_UINT32 (stsd_data + offset + 12); |
|
4392 |
|
4393 GST_LOG_OBJECT (qtdemux, "samples/packet: %d", |
|
4394 stream->samples_per_packet); |
|
4395 GST_LOG_OBJECT (qtdemux, "sample rate: %g", stream->rate); |
|
4396 GST_LOG_OBJECT (qtdemux, "n_channels: %d", stream->n_channels); |
|
4397 |
|
4398 } else { |
|
4399 GST_WARNING_OBJECT (qtdemux, "unknown version %08x", version); |
|
4400 } |
|
4401 |
|
4402 if (fourcc == FOURCC_drms) |
|
4403 goto error_encrypted; |
|
4404 |
|
4405 stream->caps = qtdemux_audio_caps (qtdemux, stream, fourcc, NULL, 0, |
|
4406 &codec); |
|
4407 |
|
4408 switch (fourcc) { |
|
4409 case FOURCC_in24: |
|
4410 { |
|
4411 GNode *enda; |
|
4412 GNode *in24; |
|
4413 |
|
4414 in24 = qtdemux_tree_get_child_by_type (stsd, FOURCC_in24); |
|
4415 |
|
4416 enda = qtdemux_tree_get_child_by_type (in24, FOURCC_enda); |
|
4417 if (!enda) { |
|
4418 wave = qtdemux_tree_get_child_by_type (in24, FOURCC_wave); |
|
4419 if (wave) |
|
4420 enda = qtdemux_tree_get_child_by_type (wave, FOURCC_enda); |
|
4421 } |
|
4422 if (enda) { |
|
4423 gst_caps_set_simple (stream->caps, |
|
4424 "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL); |
|
4425 } |
|
4426 break; |
|
4427 } |
|
4428 default: |
|
4429 break; |
|
4430 } |
|
4431 |
|
4432 if (codec) { |
|
4433 list = gst_tag_list_new (); |
|
4434 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, |
|
4435 GST_TAG_AUDIO_CODEC, codec, NULL); |
|
4436 g_free (codec); |
|
4437 codec = NULL; |
|
4438 } |
|
4439 |
|
4440 mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a); |
|
4441 wave = NULL; |
|
4442 esds = NULL; |
|
4443 if (mp4a) { |
|
4444 wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave); |
|
4445 if (wave) |
|
4446 esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds); |
|
4447 if (!esds) |
|
4448 esds = qtdemux_tree_get_child_by_type (mp4a, FOURCC_esds); |
|
4449 } |
|
4450 |
|
4451 if (esds) { |
|
4452 gst_qtdemux_handle_esds (qtdemux, stream, esds, list); |
|
4453 } else { |
|
4454 switch (fourcc) { |
|
4455 #if 0 |
|
4456 /* FIXME: what is in the chunk? */ |
|
4457 case FOURCC_QDMC: |
|
4458 { |
|
4459 gint len = QT_UINT32 (stsd_data); |
|
4460 |
|
4461 /* seems to be always = 116 = 0x74 */ |
|
4462 break; |
|
4463 } |
|
4464 #endif |
|
4465 case FOURCC_QDM2: |
|
4466 { |
|
4467 gint len = QT_UINT32 (stsd_data); |
|
4468 |
|
4469 if (len > 0x4C) { |
|
4470 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x4C); |
|
4471 |
|
4472 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x4C, len - 0x4C); |
|
4473 gst_caps_set_simple (stream->caps, |
|
4474 "codec_data", GST_TYPE_BUFFER, buf, NULL); |
|
4475 gst_buffer_unref (buf); |
|
4476 } |
|
4477 gst_caps_set_simple (stream->caps, |
|
4478 "samplesize", G_TYPE_INT, samplesize, NULL); |
|
4479 break; |
|
4480 } |
|
4481 case FOURCC_alac: |
|
4482 { |
|
4483 gint len = QT_UINT32 (stsd_data); |
|
4484 |
|
4485 if (len > 0x34) { |
|
4486 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34); |
|
4487 |
|
4488 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x34, len - 0x34); |
|
4489 gst_caps_set_simple (stream->caps, |
|
4490 "codec_data", GST_TYPE_BUFFER, buf, NULL); |
|
4491 gst_buffer_unref (buf); |
|
4492 } |
|
4493 gst_caps_set_simple (stream->caps, |
|
4494 "samplesize", G_TYPE_INT, samplesize, NULL); |
|
4495 break; |
|
4496 } |
|
4497 case FOURCC_samr: |
|
4498 { |
|
4499 gint len = QT_UINT32 (stsd_data); |
|
4500 |
|
4501 if (len > 0x34) { |
|
4502 GstBuffer *buf = gst_buffer_new_and_alloc (len - 0x34); |
|
4503 |
|
4504 memcpy (GST_BUFFER_DATA (buf), stsd_data + 0x34, len - 0x34); |
|
4505 |
|
4506 gst_caps_set_simple (stream->caps, |
|
4507 "codec_data", GST_TYPE_BUFFER, buf, NULL); |
|
4508 gst_buffer_unref (buf); |
|
4509 } |
|
4510 break; |
|
4511 } |
|
4512 default: |
|
4513 break; |
|
4514 } |
|
4515 } |
|
4516 GST_INFO_OBJECT (qtdemux, |
|
4517 "type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, |
|
4518 GST_FOURCC_ARGS (fourcc), stream->caps); |
|
4519 |
|
4520 } else if (stream->subtype == FOURCC_strm) { |
|
4521 guint32 fourcc; |
|
4522 |
|
4523 stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4); |
|
4524 GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT, |
|
4525 GST_FOURCC_ARGS (fourcc)); |
|
4526 |
|
4527 if (fourcc != FOURCC_rtsp) { |
|
4528 GST_INFO_OBJECT (qtdemux, "unhandled stream type %" GST_FOURCC_FORMAT, |
|
4529 GST_FOURCC_ARGS (fourcc)); |
|
4530 goto unknown_stream; |
|
4531 } |
|
4532 stream->sampled = TRUE; |
|
4533 } else if (stream->subtype == FOURCC_subp) { |
|
4534 guint32 fourcc; |
|
4535 |
|
4536 stream->sampled = TRUE; |
|
4537 |
|
4538 offset = 16; |
|
4539 stream->fourcc = fourcc = QT_FOURCC (stsd_data + offset + 4); |
|
4540 GST_LOG_OBJECT (qtdemux, "st type: %" GST_FOURCC_FORMAT, |
|
4541 GST_FOURCC_ARGS (fourcc)); |
|
4542 |
|
4543 stream->caps = |
|
4544 qtdemux_subp_caps (qtdemux, stream, fourcc, stsd_data, &codec); |
|
4545 } else { |
|
4546 goto unknown_stream; |
|
4547 } |
|
4548 |
|
4549 /* promote to sampled format */ |
|
4550 if (stream->fourcc == FOURCC_samr) { |
|
4551 /* force mono 8000 Hz for AMR */ |
|
4552 stream->sampled = TRUE; |
|
4553 stream->n_channels = 1; |
|
4554 stream->rate = 8000; |
|
4555 } else if (stream->fourcc == FOURCC_sawb) { |
|
4556 /* force mono 16000 Hz for AMR-WB */ |
|
4557 stream->sampled = TRUE; |
|
4558 stream->n_channels = 1; |
|
4559 stream->rate = 16000; |
|
4560 } else if (stream->fourcc == FOURCC_mp4a) { |
|
4561 stream->sampled = TRUE; |
|
4562 } |
|
4563 |
|
4564 /* collect sample information */ |
|
4565 if (!qtdemux_parse_samples (qtdemux, stream, stbl)) |
|
4566 goto samples_failed; |
|
4567 |
|
4568 /* configure segments */ |
|
4569 if (!qtdemux_parse_segments (qtdemux, stream, trak)) |
|
4570 goto segments_failed; |
|
4571 |
|
4572 /* now we are ready to add the stream */ |
|
4573 gst_qtdemux_add_stream (qtdemux, stream, list); |
|
4574 |
|
4575 return TRUE; |
|
4576 |
|
4577 /* ERRORS */ |
|
4578 corrupt_file: |
|
4579 { |
|
4580 GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE, |
|
4581 (_("This file is corrupt and cannot be played.")), (NULL)); |
|
4582 g_free (stream); |
|
4583 return FALSE; |
|
4584 } |
|
4585 error_encrypted: |
|
4586 { |
|
4587 GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), (NULL)); |
|
4588 g_free (stream); |
|
4589 return FALSE; |
|
4590 } |
|
4591 samples_failed: |
|
4592 { |
|
4593 /* we posted an error already */ |
|
4594 g_free (stream); |
|
4595 return FALSE; |
|
4596 } |
|
4597 segments_failed: |
|
4598 { |
|
4599 /* we posted an error already */ |
|
4600 g_free (stream); |
|
4601 return FALSE; |
|
4602 } |
|
4603 unknown_stream: |
|
4604 { |
|
4605 GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT, |
|
4606 GST_FOURCC_ARGS (stream->subtype)); |
|
4607 g_free (stream); |
|
4608 return TRUE; |
|
4609 } |
|
4610 } |
|
4611 |
|
4612 static inline gboolean |
|
4613 qtdemux_is_string_3gp (GstQTDemux * qtdemux, guint32 fourcc) |
|
4614 { |
|
4615 /* Detect if the tag must be handled as 3gpp - i18n metadata. The first |
|
4616 * check is for catching all the possible brands, e.g. 3gp4,3gp5,3gg6,.. and |
|
4617 * handling properly the tags present in more than one brand.*/ |
|
4618 return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) == |
|
4619 GST_MAKE_FOURCC ('3', 'g', 0, 0) |
|
4620 && (fourcc == FOURCC_cprt || fourcc == FOURCC_gnre |
|
4621 || fourcc == FOURCC_kywd)) || fourcc == FOURCC_titl |
|
4622 || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth |
|
4623 || fourcc == FOURCC_albm; |
|
4624 } |
|
4625 |
|
4626 static void |
|
4627 qtdemux_tag_add_location (GstQTDemux * qtdemux, const char *tag, |
|
4628 const char *dummy, GNode * node) |
|
4629 { |
|
4630 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL }; |
|
4631 int offset; |
|
4632 char *name; |
|
4633 gchar *data; |
|
4634 gdouble longitude, latitude, altitude; |
|
4635 |
|
4636 data = node->data; |
|
4637 offset = 14; |
|
4638 |
|
4639 /* TODO: language code skipped */ |
|
4640 |
|
4641 name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars); |
|
4642 |
|
4643 if (!name) { |
|
4644 /* do not alarm in trivial case, but bail out otherwise */ |
|
4645 if (*(data + offset) != 0) { |
|
4646 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, " |
|
4647 "giving up", tag); |
|
4648 } |
|
4649 } else { |
|
4650 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, |
|
4651 GST_TAG_GEO_LOCATION_NAME, name, NULL); |
|
4652 offset += strlen (name); |
|
4653 g_free (name); |
|
4654 } |
|
4655 |
|
4656 /* +1 +1 = skip null-terminator and location role byte */ |
|
4657 offset += 1 + 1; |
|
4658 longitude = QT_FP32 (data + offset); |
|
4659 |
|
4660 offset += 4; |
|
4661 latitude = QT_FP32 (data + offset); |
|
4662 |
|
4663 offset += 4; |
|
4664 altitude = QT_FP32 (data + offset); |
|
4665 |
|
4666 /* one invalid means all are invalid */ |
|
4667 if (longitude >= -180.0 && longitude <= 180.0 && |
|
4668 latitude >= -90.0 && latitude <= 90.0) { |
|
4669 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, |
|
4670 GST_TAG_GEO_LOCATION_LATITUDE, latitude, |
|
4671 GST_TAG_GEO_LOCATION_LONGITUDE, longitude, |
|
4672 GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL); |
|
4673 } |
|
4674 |
|
4675 /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */ |
|
4676 } |
|
4677 |
|
4678 |
|
4679 static void |
|
4680 qtdemux_tag_add_year (GstQTDemux * qtdemux, const char *tag, const char *dummy, |
|
4681 GNode * node) |
|
4682 { |
|
4683 guint16 y; |
|
4684 GDate *date; |
|
4685 |
|
4686 y = QT_UINT16 ((guint8 *) node->data + 12); |
|
4687 GST_DEBUG_OBJECT (qtdemux, "year: %u", y); |
|
4688 |
|
4689 date = g_date_new_dmy (1, 1, y); |
|
4690 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, date, NULL); |
|
4691 g_date_free (date); |
|
4692 } |
|
4693 |
|
4694 static void |
|
4695 qtdemux_tag_add_classification (GstQTDemux * qtdemux, const char *tag, |
|
4696 const char *dummy, GNode * node) |
|
4697 { |
|
4698 int offset; |
|
4699 char *tag_str = NULL; |
|
4700 guint8 *entity; |
|
4701 guint16 table; |
|
4702 |
|
4703 |
|
4704 offset = 12; |
|
4705 entity = (guint8 *) node->data + offset; |
|
4706 |
|
4707 offset += 4; |
|
4708 table = QT_UINT16 ((guint8 *) node->data + offset); |
|
4709 |
|
4710 /* Language code skipped */ |
|
4711 |
|
4712 offset += 4; |
|
4713 |
|
4714 /* Tag format: "XXXX://Y[YYYY]/classification info string" |
|
4715 * XXXX: classification entity, fixed length 4 chars. |
|
4716 * Y[YYYY]: classification table, max 5 chars. |
|
4717 */ |
|
4718 tag_str = g_strdup_printf ("----://%u/%s", |
|
4719 table, (char *) node->data + offset); |
|
4720 |
|
4721 /* memcpy To be sure we're preserving byte order */ |
|
4722 memcpy (tag_str, entity, 4); |
|
4723 GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str); |
|
4724 |
|
4725 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_APPEND, tag, |
|
4726 tag_str, NULL); |
|
4727 |
|
4728 g_free (tag_str); |
|
4729 } |
|
4730 |
|
4731 static void |
|
4732 qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag, const char *dummy, |
|
4733 GNode * node) |
|
4734 { |
|
4735 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL }; |
|
4736 GNode *data; |
|
4737 char *s; |
|
4738 int len; |
|
4739 guint32 type; |
|
4740 int offset; |
|
4741 |
|
4742 data = qtdemux_tree_get_child_by_type (node, FOURCC_data); |
|
4743 if (data) { |
|
4744 len = QT_UINT32 (data->data); |
|
4745 type = QT_UINT32 ((guint8 *) data->data + 8); |
|
4746 if (type == 0x00000001) { |
|
4747 s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16, |
|
4748 env_vars); |
|
4749 if (s) { |
|
4750 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s)); |
|
4751 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s, |
|
4752 NULL); |
|
4753 g_free (s); |
|
4754 } else { |
|
4755 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag); |
|
4756 } |
|
4757 } |
|
4758 } else { |
|
4759 len = QT_UINT32 (node->data); |
|
4760 type = QT_UINT32 ((guint8 *) node->data + 4); |
|
4761 if ((type >> 24) == 0xa9) { |
|
4762 /* Type starts with the (C) symbol, so the next 32 bits are |
|
4763 * the language code, which we ignore */ |
|
4764 offset = 12; |
|
4765 GST_DEBUG_OBJECT (qtdemux, "found international text tag"); |
|
4766 } else if (qtdemux_is_string_3gp (qtdemux, |
|
4767 QT_FOURCC ((guint8 *) node->data + 4))) { |
|
4768 offset = 14; |
|
4769 /* 16-bit Language code is ignored here as well */ |
|
4770 GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag"); |
|
4771 } else { |
|
4772 offset = 8; |
|
4773 GST_DEBUG_OBJECT (qtdemux, "found normal text tag"); |
|
4774 } |
|
4775 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset, |
|
4776 len - offset, env_vars); |
|
4777 if (s) { |
|
4778 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s)); |
|
4779 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, s, NULL); |
|
4780 g_free (s); |
|
4781 } else { |
|
4782 GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag); |
|
4783 } |
|
4784 } |
|
4785 } |
|
4786 |
|
4787 static void |
|
4788 qtdemux_tag_add_keywords (GstQTDemux * qtdemux, const char *tag, |
|
4789 const char *dummy, GNode * node) |
|
4790 { |
|
4791 const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL }; |
|
4792 guint8 *data; |
|
4793 char *s, *t, *k = NULL; |
|
4794 int len; |
|
4795 int offset; |
|
4796 int count; |
|
4797 |
|
4798 /* re-route to normal string tag if not 3GP */ |
|
4799 if (!qtdemux_is_string_3gp (qtdemux, FOURCC_kywd)) |
|
4800 return qtdemux_tag_add_str (qtdemux, tag, dummy, node); |
|
4801 |
|
4802 GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag"); |
|
4803 |
|
4804 data = node->data; |
|
4805 |
|
4806 len = QT_UINT32 (data); |
|
4807 if (len < 15) |
|
4808 goto short_read; |
|
4809 |
|
4810 count = QT_UINT8 (data + 14); |
|
4811 offset = 15; |
|
4812 for (; count; count--) { |
|
4813 gint slen; |
|
4814 |
|
4815 if (offset + 1 > len) |
|
4816 goto short_read; |
|
4817 slen = QT_UINT8 (data + offset); |
|
4818 offset += 1; |
|
4819 if (offset + slen > len) |
|
4820 goto short_read; |
|
4821 s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset, |
|
4822 slen, env_vars); |
|
4823 if (s) { |
|
4824 GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s)); |
|
4825 if (k) { |
|
4826 t = g_strjoin (",", k, s, NULL); |
|
4827 g_free (s); |
|
4828 g_free (k); |
|
4829 k = t; |
|
4830 } else { |
|
4831 k = s; |
|
4832 } |
|
4833 } else { |
|
4834 GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8"); |
|
4835 } |
|
4836 offset += slen; |
|
4837 } |
|
4838 |
|
4839 done: |
|
4840 if (k) { |
|
4841 GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k)); |
|
4842 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, k, NULL); |
|
4843 } |
|
4844 g_free (k); |
|
4845 |
|
4846 return; |
|
4847 |
|
4848 /* ERRORS */ |
|
4849 short_read: |
|
4850 { |
|
4851 GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords"); |
|
4852 goto done; |
|
4853 } |
|
4854 } |
|
4855 |
|
4856 static void |
|
4857 qtdemux_tag_add_num (GstQTDemux * qtdemux, const char *tag1, |
|
4858 const char *tag2, GNode * node) |
|
4859 { |
|
4860 GNode *data; |
|
4861 int len; |
|
4862 int type; |
|
4863 int n1, n2; |
|
4864 |
|
4865 data = qtdemux_tree_get_child_by_type (node, FOURCC_data); |
|
4866 if (data) { |
|
4867 len = QT_UINT32 (data->data); |
|
4868 type = QT_UINT32 ((guint8 *) data->data + 8); |
|
4869 if (type == 0x00000000 && len >= 22) { |
|
4870 n1 = QT_UINT16 ((guint8 *) data->data + 18); |
|
4871 n2 = QT_UINT16 ((guint8 *) data->data + 20); |
|
4872 if (n1 > 0) { |
|
4873 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1); |
|
4874 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, |
|
4875 tag1, n1, NULL); |
|
4876 } |
|
4877 if (n2 > 0) { |
|
4878 GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2); |
|
4879 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, |
|
4880 tag2, n2, NULL); |
|
4881 } |
|
4882 } |
|
4883 } |
|
4884 } |
|
4885 |
|
4886 static void |
|
4887 qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, const char *tag1, const char *dummy, |
|
4888 GNode * node) |
|
4889 { |
|
4890 GNode *data; |
|
4891 int len; |
|
4892 int type; |
|
4893 int n1; |
|
4894 |
|
4895 data = qtdemux_tree_get_child_by_type (node, FOURCC_data); |
|
4896 if (data) { |
|
4897 len = QT_UINT32 (data->data); |
|
4898 type = QT_UINT32 ((guint8 *) data->data + 8); |
|
4899 GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len); |
|
4900 /* some files wrongly have a type 0x0f=15, but it should be 0x15 */ |
|
4901 if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) { |
|
4902 n1 = QT_UINT16 ((guint8 *) data->data + 16); |
|
4903 if (n1) { |
|
4904 /* do not add bpm=0 */ |
|
4905 GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1); |
|
4906 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, |
|
4907 tag1, (gdouble) n1, NULL); |
|
4908 } |
|
4909 } |
|
4910 } |
|
4911 } |
|
4912 |
|
4913 static void |
|
4914 qtdemux_tag_add_covr (GstQTDemux * qtdemux, const char *tag1, const char *dummy, |
|
4915 GNode * node) |
|
4916 { |
|
4917 GNode *data; |
|
4918 int len; |
|
4919 int type; |
|
4920 GstBuffer *buf; |
|
4921 |
|
4922 data = qtdemux_tree_get_child_by_type (node, FOURCC_data); |
|
4923 if (data) { |
|
4924 len = QT_UINT32 (data->data); |
|
4925 type = QT_UINT32 ((guint8 *) data->data + 8); |
|
4926 GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len); |
|
4927 if ((type == 0x0000000d || type == 0x0000000e) && len > 16) { |
|
4928 if ((buf = gst_tag_image_data_to_image_buffer ((guint8 *) data->data + 16, |
|
4929 len - 16, GST_TAG_IMAGE_TYPE_NONE))) { |
|
4930 GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16); |
|
4931 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, |
|
4932 tag1, buf, NULL); |
|
4933 gst_buffer_unref (buf); |
|
4934 } |
|
4935 } |
|
4936 } |
|
4937 } |
|
4938 |
|
4939 static void |
|
4940 qtdemux_tag_add_date (GstQTDemux * qtdemux, const char *tag, const char *dummy, |
|
4941 GNode * node) |
|
4942 { |
|
4943 GNode *data; |
|
4944 char *s; |
|
4945 int len; |
|
4946 int type; |
|
4947 |
|
4948 data = qtdemux_tree_get_child_by_type (node, FOURCC_data); |
|
4949 if (data) { |
|
4950 len = QT_UINT32 (data->data); |
|
4951 type = QT_UINT32 ((guint8 *) data->data + 8); |
|
4952 if (type == 0x00000001) { |
|
4953 guint y, m = 1, d = 1; |
|
4954 gint ret; |
|
4955 |
|
4956 s = g_strndup ((char *) data->data + 16, len - 16); |
|
4957 GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s); |
|
4958 ret = sscanf (s, "%u-%u-%u", &y, &m, &d); |
|
4959 if (ret >= 1 && y > 1500 && y < 3000) { |
|
4960 GDate *date; |
|
4961 |
|
4962 date = g_date_new_dmy (d, m, y); |
|
4963 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, |
|
4964 date, NULL); |
|
4965 g_date_free (date); |
|
4966 } else { |
|
4967 GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s); |
|
4968 } |
|
4969 g_free (s); |
|
4970 } |
|
4971 } |
|
4972 } |
|
4973 |
|
4974 static void |
|
4975 qtdemux_tag_add_gnre (GstQTDemux * qtdemux, const char *tag, const char *dummy, |
|
4976 GNode * node) |
|
4977 { |
|
4978 static const gchar *genres[] = { |
|
4979 "N/A", "Blues", "Classic Rock", "Country", "Dance", "Disco", |
|
4980 "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", |
|
4981 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", |
|
4982 "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", |
|
4983 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", |
|
4984 "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", |
|
4985 "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", |
|
4986 "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", |
|
4987 "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", |
|
4988 "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", |
|
4989 "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", |
|
4990 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", |
|
4991 "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", |
|
4992 "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", |
|
4993 "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", |
|
4994 "Folk/Rock", "National Folk", "Swing", "Fast-Fusion", "Bebob", |
|
4995 "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", |
|
4996 "Gothic Rock", "Progressive Rock", "Psychedelic Rock", |
|
4997 "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", |
|
4998 "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", |
|
4999 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", |
|
5000 "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", |
|
5001 "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", |
|
5002 "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella", |
|
5003 "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club House", |
|
5004 "Hardcore", "Terror", "Indie", "BritPop", "NegerPunk", |
|
5005 "Polsk Punk", "Beat", "Christian Gangsta", "Heavy Metal", |
|
5006 "Black Metal", "Crossover", "Contemporary C", "Christian Rock", |
|
5007 "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "SynthPop" |
|
5008 }; |
|
5009 GNode *data; |
|
5010 int len; |
|
5011 int type; |
|
5012 int n; |
|
5013 |
|
5014 /* re-route to normal string tag if 3GP */ |
|
5015 if (qtdemux_is_string_3gp (qtdemux, FOURCC_gnre)) |
|
5016 return qtdemux_tag_add_str (qtdemux, tag, dummy, node); |
|
5017 |
|
5018 data = qtdemux_tree_get_child_by_type (node, FOURCC_data); |
|
5019 if (data) { |
|
5020 len = QT_UINT32 (data->data); |
|
5021 type = QT_UINT32 ((guint8 *) data->data + 8); |
|
5022 if (type == 0x00000000 && len >= 18) { |
|
5023 n = QT_UINT16 ((guint8 *) data->data + 16); |
|
5024 if (n > 0 && n < sizeof (genres) / sizeof (char *)) { |
|
5025 GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genres[n]); |
|
5026 gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, |
|
5027 tag, genres[n], NULL); |
|
5028 } |
|
5029 } |
|
5030 } |
|
5031 } |
|
5032 |
|
5033 typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, |
|
5034 const char *tag, const char *tag_bis, GNode * node); |
|
5035 |
|
5036 static const struct |
|
5037 { |
|
5038 guint32 fourcc; |
|
5039 const gchar *gst_tag; |
|
5040 const gchar *gst_tag_bis; |
|
5041 const GstQTDemuxAddTagFunc func; |
|
5042 } add_funcs[] = { |
|
5043 { |
|
5044 FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, { |
|
5045 FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, { |
|
5046 FOURCC__grp, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, { |
|
5047 FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, { |
|
5048 FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, { |
|
5049 FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, { |
|
5050 FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, { |
|
5051 FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, { |
|
5052 FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, { |
|
5053 FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, { |
|
5054 FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, { |
|
5055 FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, { |
|
5056 FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, { |
|
5057 FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, { |
|
5058 FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, { |
|
5059 FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, { |
|
5060 FOURCC__too, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, { |
|
5061 FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, { |
|
5062 FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, { |
|
5063 FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT, |
|
5064 qtdemux_tag_add_num}, { |
|
5065 FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT, |
|
5066 qtdemux_tag_add_num}, { |
|
5067 FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, { |
|
5068 FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, { |
|
5069 FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, { |
|
5070 FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, qtdemux_tag_add_covr}, { |
|
5071 FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, { |
|
5072 FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, { |
|
5073 FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, { |
|
5074 FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, { |
|
5075 FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL, |
|
5076 qtdemux_tag_add_classification} |
|
5077 }; |
|
5078 |
|
5079 static void |
|
5080 qtdemux_tag_add_blob (GNode * node, GstQTDemux * demux) |
|
5081 { |
|
5082 gint len; |
|
5083 guint8 *data; |
|
5084 GstBuffer *buf; |
|
5085 gchar *media_type, *style; |
|
5086 GstCaps *caps; |
|
5087 guint i; |
|
5088 guint8 ndata[4]; |
|
5089 |
|
5090 data = node->data; |
|
5091 len = QT_UINT32 (data); |
|
5092 buf = gst_buffer_new_and_alloc (len); |
|
5093 memcpy (GST_BUFFER_DATA (buf), data, len); |
|
5094 |
|
5095 /* heuristic to determine style of tag */ |
|
5096 if (QT_FOURCC (data + 4) == FOURCC_____ || |
|
5097 (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data)) |
|
5098 style = "itunes"; |
|
5099 else if (demux->major_brand == FOURCC_qt__) |
|
5100 style = "quicktime"; |
|
5101 /* fall back to assuming iso/3gp tag style */ |
|
5102 else |
|
5103 style = "iso"; |
|
5104 |
|
5105 /* santize the name for the caps. */ |
|
5106 for (i = 0; i < 4; i++) { |
|
5107 guint8 d = data[4 + i]; |
|
5108 if (g_ascii_isalnum (d)) |
|
5109 ndata[i] = g_ascii_tolower (d); |
|
5110 else |
|
5111 ndata[i] = '_'; |
|
5112 } |
|
5113 |
|
5114 media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag", |
|
5115 ndata[0], ndata[1], ndata[2], ndata[3]); |
|
5116 GST_DEBUG_OBJECT (demux, "media type %s", media_type); |
|
5117 |
|
5118 caps = gst_caps_new_simple (media_type, "style", G_TYPE_STRING, style, NULL); |
|
5119 gst_buffer_set_caps (buf, caps); |
|
5120 gst_caps_unref (caps); |
|
5121 g_free (media_type); |
|
5122 |
|
5123 GST_DEBUG_OBJECT (demux, "adding private tag; size %d, caps %" GST_PTR_FORMAT, |
|
5124 GST_BUFFER_SIZE (buf), caps); |
|
5125 |
|
5126 gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_APPEND, |
|
5127 GST_QT_DEMUX_PRIVATE_TAG, buf, NULL); |
|
5128 gst_buffer_unref (buf); |
|
5129 } |
|
5130 |
|
5131 static void |
|
5132 qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta) |
|
5133 { |
|
5134 GNode *meta; |
|
5135 GNode *ilst; |
|
5136 GNode *node; |
|
5137 gint i; |
|
5138 |
|
5139 meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta); |
|
5140 if (meta != NULL) { |
|
5141 ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst); |
|
5142 if (ilst == NULL) { |
|
5143 GST_LOG_OBJECT (qtdemux, "no ilst"); |
|
5144 return; |
|
5145 } |
|
5146 } else { |
|
5147 ilst = udta; |
|
5148 GST_LOG_OBJECT (qtdemux, "no meta so using udta itself"); |
|
5149 } |
|
5150 |
|
5151 GST_DEBUG_OBJECT (qtdemux, "new tag list"); |
|
5152 qtdemux->tag_list = gst_tag_list_new (); |
|
5153 |
|
5154 for (i = 0; i < G_N_ELEMENTS (add_funcs); ++i) { |
|
5155 node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc); |
|
5156 if (node) { |
|
5157 add_funcs[i].func (qtdemux, add_funcs[i].gst_tag, |
|
5158 add_funcs[i].gst_tag_bis, node); |
|
5159 g_node_destroy (node); |
|
5160 } |
|
5161 } |
|
5162 |
|
5163 /* parsed nodes have been removed, pass along remainder as blob */ |
|
5164 g_node_children_foreach (ilst, G_TRAVERSE_ALL, |
|
5165 (GNodeForeachFunc) qtdemux_tag_add_blob, qtdemux); |
|
5166 |
|
5167 } |
|
5168 |
|
5169 typedef struct |
|
5170 { |
|
5171 GstStructure *structure; /* helper for sort function */ |
|
5172 gchar *location; |
|
5173 guint min_req_bitrate; |
|
5174 guint min_req_qt_version; |
|
5175 } GstQtReference; |
|
5176 |
|
5177 static gint |
|
5178 qtdemux_redirects_sort_func (gconstpointer a, gconstpointer b) |
|
5179 { |
|
5180 GstQtReference *ref_a = (GstQtReference *) a; |
|
5181 GstQtReference *ref_b = (GstQtReference *) b; |
|
5182 |
|
5183 if (ref_b->min_req_qt_version != ref_a->min_req_qt_version) |
|
5184 return ref_b->min_req_qt_version - ref_a->min_req_qt_version; |
|
5185 |
|
5186 /* known bitrates go before unknown; higher bitrates go first */ |
|
5187 return ref_b->min_req_bitrate - ref_a->min_req_bitrate; |
|
5188 } |
|
5189 |
|
5190 /* sort the redirects and post a message for the application. |
|
5191 */ |
|
5192 static void |
|
5193 qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references) |
|
5194 { |
|
5195 GstQtReference *best; |
|
5196 GstStructure *s; |
|
5197 GstMessage *msg; |
|
5198 GValue list_val = { 0, }; |
|
5199 GList *l; |
|
5200 |
|
5201 g_assert (references != NULL); |
|
5202 |
|
5203 references = g_list_sort (references, qtdemux_redirects_sort_func); |
|
5204 |
|
5205 best = (GstQtReference *) references->data; |
|
5206 |
|
5207 g_value_init (&list_val, GST_TYPE_LIST); |
|
5208 |
|
5209 for (l = references; l != NULL; l = l->next) { |
|
5210 GstQtReference *ref = (GstQtReference *) l->data; |
|
5211 GValue struct_val = { 0, }; |
|
5212 |
|
5213 ref->structure = gst_structure_new ("redirect", |
|
5214 "new-location", G_TYPE_STRING, ref->location, NULL); |
|
5215 |
|
5216 if (ref->min_req_bitrate > 0) { |
|
5217 gst_structure_set (ref->structure, "minimum-bitrate", G_TYPE_INT, |
|
5218 ref->min_req_bitrate, NULL); |
|
5219 } |
|
5220 |
|
5221 g_value_init (&struct_val, GST_TYPE_STRUCTURE); |
|
5222 g_value_set_boxed (&struct_val, ref->structure); |
|
5223 gst_value_list_append_value (&list_val, &struct_val); |
|
5224 g_value_unset (&struct_val); |
|
5225 /* don't free anything here yet, since we need best->structure below */ |
|
5226 } |
|
5227 |
|
5228 g_assert (best != NULL); |
|
5229 s = gst_structure_copy (best->structure); |
|
5230 |
|
5231 if (g_list_length (references) > 1) { |
|
5232 gst_structure_set_value (s, "locations", &list_val); |
|
5233 } |
|
5234 |
|
5235 g_value_unset (&list_val); |
|
5236 |
|
5237 for (l = references; l != NULL; l = l->next) { |
|
5238 GstQtReference *ref = (GstQtReference *) l->data; |
|
5239 |
|
5240 gst_structure_free (ref->structure); |
|
5241 g_free (ref->location); |
|
5242 g_free (ref); |
|
5243 } |
|
5244 g_list_free (references); |
|
5245 |
|
5246 GST_INFO_OBJECT (qtdemux, "posting redirect message: %" GST_PTR_FORMAT, s); |
|
5247 msg = gst_message_new_element (GST_OBJECT_CAST (qtdemux), s); |
|
5248 gst_element_post_message (GST_ELEMENT_CAST (qtdemux), msg); |
|
5249 } |
|
5250 |
|
5251 /* look for redirect nodes, collect all redirect information and |
|
5252 * process it. |
|
5253 */ |
|
5254 static gboolean |
|
5255 qtdemux_parse_redirects (GstQTDemux * qtdemux) |
|
5256 { |
|
5257 GNode *rmra, *rmda, *rdrf; |
|
5258 |
|
5259 rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra); |
|
5260 if (rmra) { |
|
5261 GList *redirects = NULL; |
|
5262 |
|
5263 rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda); |
|
5264 while (rmda) { |
|
5265 GstQtReference ref = { NULL, NULL, 0, 0 }; |
|
5266 GNode *rmdr, *rmvc; |
|
5267 |
|
5268 if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) { |
|
5269 ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12); |
|
5270 GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u", |
|
5271 ref.min_req_bitrate); |
|
5272 } |
|
5273 |
|
5274 if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) { |
|
5275 guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12); |
|
5276 guint version = QT_UINT32 ((guint8 *) rmvc->data + 16); |
|
5277 |
|
5278 #ifndef GST_DISABLE_GST_DEBUG |
|
5279 guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20); |
|
5280 #endif |
|
5281 guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24); |
|
5282 |
|
5283 GST_LOG_OBJECT (qtdemux, |
|
5284 "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x" |
|
5285 ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version, |
|
5286 bitmask, check_type); |
|
5287 if (package == FOURCC_qtim && check_type == 0) { |
|
5288 ref.min_req_qt_version = version; |
|
5289 } |
|
5290 } |
|
5291 |
|
5292 rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf); |
|
5293 if (rdrf) { |
|
5294 guint32 ref_type; |
|
5295 guint8 *ref_data; |
|
5296 |
|
5297 ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12); |
|
5298 ref_data = (guint8 *) rdrf->data + 20; |
|
5299 if (ref_type == FOURCC_alis) { |
|
5300 guint record_len, record_version, fn_len; |
|
5301 |
|
5302 /* MacOSX alias record, google for alias-layout.txt */ |
|
5303 record_len = QT_UINT16 (ref_data + 4); |
|
5304 record_version = QT_UINT16 (ref_data + 4 + 2); |
|
5305 fn_len = QT_UINT8 (ref_data + 50); |
|
5306 if (record_len > 50 && record_version == 2 && fn_len > 0) { |
|
5307 ref.location = g_strndup ((gchar *) ref_data + 51, fn_len); |
|
5308 } |
|
5309 } else if (ref_type == FOURCC_url_) { |
|
5310 ref.location = g_strdup ((gchar *) ref_data); |
|
5311 } else { |
|
5312 GST_DEBUG_OBJECT (qtdemux, |
|
5313 "unknown rdrf reference type %" GST_FOURCC_FORMAT, |
|
5314 GST_FOURCC_ARGS (ref_type)); |
|
5315 } |
|
5316 if (ref.location != NULL) { |
|
5317 GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location); |
|
5318 redirects = g_list_prepend (redirects, g_memdup (&ref, sizeof (ref))); |
|
5319 } else { |
|
5320 GST_WARNING_OBJECT (qtdemux, |
|
5321 "Failed to extract redirect location from rdrf atom"); |
|
5322 } |
|
5323 } |
|
5324 |
|
5325 /* look for others */ |
|
5326 rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda); |
|
5327 } |
|
5328 |
|
5329 if (redirects != NULL) { |
|
5330 qtdemux_process_redirects (qtdemux, redirects); |
|
5331 } |
|
5332 } |
|
5333 return TRUE; |
|
5334 } |
|
5335 |
|
5336 static GstTagList * |
|
5337 qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags) |
|
5338 { |
|
5339 const gchar *fmt; |
|
5340 |
|
5341 if (tags == NULL) |
|
5342 tags = gst_tag_list_new (); |
|
5343 |
|
5344 if (qtdemux->major_brand == FOURCC_mjp2) |
|
5345 fmt = "Motion JPEG 2000"; |
|
5346 else if ((qtdemux->major_brand & 0xffff) == GST_MAKE_FOURCC ('3', 'g', 0, 0)) |
|
5347 fmt = "3GP"; |
|
5348 else if (qtdemux->major_brand == FOURCC_qt__) |
|
5349 fmt = "Quicktime"; |
|
5350 else |
|
5351 fmt = "ISO MP4/M4A"; |
|
5352 |
|
5353 GST_LOG_OBJECT (qtdemux, "mapped %" GST_FOURCC_FORMAT " to '%s'", |
|
5354 GST_FOURCC_ARGS (qtdemux->major_brand), fmt); |
|
5355 |
|
5356 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT, |
|
5357 fmt, NULL); |
|
5358 |
|
5359 return tags; |
|
5360 } |
|
5361 |
|
5362 /* we have read th complete moov node now. |
|
5363 * This function parses all of the relevant info, creates the traks and |
|
5364 * prepares all data structures for playback |
|
5365 */ |
|
5366 static gboolean |
|
5367 qtdemux_parse_tree (GstQTDemux * qtdemux) |
|
5368 { |
|
5369 GNode *mvhd; |
|
5370 GNode *trak; |
|
5371 GNode *udta; |
|
5372 gint64 duration; |
|
5373 |
|
5374 mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd); |
|
5375 if (mvhd == NULL) { |
|
5376 GST_LOG_OBJECT (qtdemux, "No mvhd node found, looking for redirects."); |
|
5377 return qtdemux_parse_redirects (qtdemux); |
|
5378 } |
|
5379 |
|
5380 qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20); |
|
5381 qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24); |
|
5382 |
|
5383 GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale); |
|
5384 GST_INFO_OBJECT (qtdemux, "duration: %u", qtdemux->duration); |
|
5385 |
|
5386 /* set duration in the segment info */ |
|
5387 gst_qtdemux_get_duration (qtdemux, &duration); |
|
5388 gst_segment_set_duration (&qtdemux->segment, GST_FORMAT_TIME, duration); |
|
5389 |
|
5390 /* parse all traks */ |
|
5391 trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak); |
|
5392 while (trak) { |
|
5393 qtdemux_parse_trak (qtdemux, trak); |
|
5394 /* iterate all siblings */ |
|
5395 trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak); |
|
5396 } |
|
5397 gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux)); |
|
5398 |
|
5399 /* find and push tags, we do this after adding the pads so we can push the |
|
5400 * tags downstream as well. */ |
|
5401 udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta); |
|
5402 if (udta) { |
|
5403 qtdemux_parse_udta (qtdemux, udta); |
|
5404 } else { |
|
5405 GST_LOG_OBJECT (qtdemux, "No udta node found."); |
|
5406 } |
|
5407 |
|
5408 /* FIXME: tags must be pushed after the initial newsegment event */ |
|
5409 qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list); |
|
5410 GST_INFO_OBJECT (qtdemux, "global tags: %" GST_PTR_FORMAT, qtdemux->tag_list); |
|
5411 gst_element_found_tags (GST_ELEMENT_CAST (qtdemux), qtdemux->tag_list); |
|
5412 qtdemux->tag_list = NULL; |
|
5413 |
|
5414 return TRUE; |
|
5415 } |
|
5416 |
|
5417 /* taken from ffmpeg */ |
|
5418 static unsigned int |
|
5419 get_size (guint8 * ptr, guint8 ** end) |
|
5420 { |
|
5421 int count = 4; |
|
5422 int len = 0; |
|
5423 |
|
5424 while (count--) { |
|
5425 int c = *ptr; |
|
5426 |
|
5427 ptr++; |
|
5428 len = (len << 7) | (c & 0x7f); |
|
5429 if (!(c & 0x80)) |
|
5430 break; |
|
5431 } |
|
5432 if (end) |
|
5433 *end = ptr; |
|
5434 return len; |
|
5435 } |
|
5436 |
|
5437 /* this can change the codec originally present in @list */ |
|
5438 static void |
|
5439 gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
5440 GNode * esds, GstTagList * list) |
|
5441 { |
|
5442 int len = QT_UINT32 (esds->data); |
|
5443 guint8 *ptr = esds->data; |
|
5444 guint8 *end = ptr + len; |
|
5445 int tag; |
|
5446 guint8 *data_ptr = NULL; |
|
5447 int data_len = 0; |
|
5448 guint8 object_type_id = 0; |
|
5449 char *codec_name = NULL; |
|
5450 GstCaps *caps = NULL; |
|
5451 |
|
5452 GST_MEMDUMP_OBJECT (qtdemux, "esds", ptr, len); |
|
5453 ptr += 8; |
|
5454 GST_DEBUG_OBJECT (qtdemux, "version/flags = %08x", QT_UINT32 (ptr)); |
|
5455 ptr += 4; |
|
5456 while (ptr < end) { |
|
5457 tag = QT_UINT8 (ptr); |
|
5458 GST_DEBUG_OBJECT (qtdemux, "tag = %02x", tag); |
|
5459 ptr++; |
|
5460 len = get_size (ptr, &ptr); |
|
5461 GST_DEBUG_OBJECT (qtdemux, "len = %d", len); |
|
5462 |
|
5463 switch (tag) { |
|
5464 case 0x03: |
|
5465 GST_DEBUG_OBJECT (qtdemux, "ID %04x", QT_UINT16 (ptr)); |
|
5466 GST_DEBUG_OBJECT (qtdemux, "priority %04x", QT_UINT8 (ptr + 2)); |
|
5467 ptr += 3; |
|
5468 break; |
|
5469 case 0x04: |
|
5470 object_type_id = QT_UINT8 (ptr); |
|
5471 GST_DEBUG_OBJECT (qtdemux, "object_type_id %02x", object_type_id); |
|
5472 GST_DEBUG_OBJECT (qtdemux, "stream_type %02x", QT_UINT8 (ptr + 1)); |
|
5473 GST_DEBUG_OBJECT (qtdemux, "buffer_size_db %02x", QT_UINT24 (ptr + 2)); |
|
5474 GST_DEBUG_OBJECT (qtdemux, "max bitrate %d", QT_UINT32 (ptr + 5)); |
|
5475 GST_DEBUG_OBJECT (qtdemux, "avg bitrate %d", QT_UINT32 (ptr + 9)); |
|
5476 ptr += 13; |
|
5477 break; |
|
5478 case 0x05: |
|
5479 GST_MEMDUMP_OBJECT (qtdemux, "data", ptr, len); |
|
5480 data_ptr = ptr; |
|
5481 data_len = len; |
|
5482 ptr += len; |
|
5483 break; |
|
5484 case 0x06: |
|
5485 GST_DEBUG_OBJECT (qtdemux, "data %02x", QT_UINT8 (ptr)); |
|
5486 ptr += 1; |
|
5487 break; |
|
5488 default: |
|
5489 GST_ERROR_OBJECT (qtdemux, "parse error"); |
|
5490 break; |
|
5491 } |
|
5492 } |
|
5493 |
|
5494 /* object_type_id in the esds atom in mp4a and mp4v tells us which codec is |
|
5495 * in use, and should also be used to override some other parameters for some |
|
5496 * codecs. */ |
|
5497 switch (object_type_id) { |
|
5498 case 0x20: /* MPEG-4 */ |
|
5499 break; /* Nothing special needed here */ |
|
5500 case 0x21: /* H.264 */ |
|
5501 codec_name = "H.264 / AVC"; |
|
5502 caps = gst_caps_new_simple ("video/x-h264", NULL); |
|
5503 break; |
|
5504 case 0x40: /* AAC (any) */ |
|
5505 case 0x66: /* AAC Main */ |
|
5506 case 0x67: /* AAC LC */ |
|
5507 case 0x68: /* AAC SSR */ |
|
5508 /* Override channels and rate based on the codec_data, as it's often |
|
5509 * wrong. */ |
|
5510 if (data_ptr && data_len >= 2) { |
|
5511 guint channels, rateindex; |
|
5512 int rates[] = { 96000, 88200, 64000, 48000, 44100, 32000, |
|
5513 24000, 22050, 16000, 12000, 11025, 8000 |
|
5514 }; |
|
5515 |
|
5516 channels = (data_ptr[1] & 0x7f) >> 3; |
|
5517 if (channels <= 7) { |
|
5518 stream->n_channels = channels; |
|
5519 } |
|
5520 |
|
5521 rateindex = ((data_ptr[0] & 0x7) << 1) | ((data_ptr[1] & 0x80) >> 7); |
|
5522 if (rateindex < sizeof (rates) / sizeof (*rates)) { |
|
5523 stream->rate = rates[rateindex]; |
|
5524 } |
|
5525 } |
|
5526 break; |
|
5527 case 0x60: /* MPEG-2, various profiles */ |
|
5528 case 0x61: |
|
5529 case 0x62: |
|
5530 case 0x63: |
|
5531 case 0x64: |
|
5532 case 0x65: |
|
5533 codec_name = "MPEG-2 video"; |
|
5534 |
|
5535 gst_caps_unref (stream->caps); |
|
5536 stream->caps = gst_caps_new_simple ("video/mpeg", |
|
5537 "mpegversion", G_TYPE_INT, 2, |
|
5538 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5539 break; |
|
5540 case 0x69: /* MP3 has two different values, accept either */ |
|
5541 case 0x6B: |
|
5542 /* change to mpeg1 layer 3 audio */ |
|
5543 gst_caps_set_simple (stream->caps, "layer", G_TYPE_INT, 3, |
|
5544 "mpegversion", G_TYPE_INT, 1, NULL); |
|
5545 codec_name = "MPEG-1 layer 3"; |
|
5546 break; |
|
5547 case 0x6A: /* MPEG-1 */ |
|
5548 codec_name = "MPEG-1 video"; |
|
5549 |
|
5550 gst_caps_unref (stream->caps); |
|
5551 stream->caps = gst_caps_new_simple ("video/mpeg", |
|
5552 "mpegversion", G_TYPE_INT, 1, |
|
5553 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5554 break; |
|
5555 case 0x6C: /* MJPEG */ |
|
5556 caps = gst_caps_new_simple ("image/jpeg", NULL); |
|
5557 codec_name = "Motion-JPEG"; |
|
5558 break; |
|
5559 case 0x6D: /* PNG */ |
|
5560 caps = gst_caps_new_simple ("image/png", NULL); |
|
5561 codec_name = "PNG still images"; |
|
5562 break; |
|
5563 case 0x6E: /* JPEG2000 */ |
|
5564 codec_name = "JPEG-2000"; |
|
5565 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL); |
|
5566 break; |
|
5567 case 0xA4: /* Dirac */ |
|
5568 codec_name = "Dirac"; |
|
5569 caps = gst_caps_new_simple ("video/x-dirac", NULL); |
|
5570 break; |
|
5571 case 0xA5: /* AC3 */ |
|
5572 codec_name = "AC-3 audio"; |
|
5573 caps = gst_caps_new_simple ("audio/x-ac3", NULL); |
|
5574 break; |
|
5575 case 0xE1: /* QCELP */ |
|
5576 /* QCELP, the codec_data is a riff tag (little endian) with |
|
5577 * more info (http://ftp.3gpp2.org/TSGC/Working/2003/2003-05-SanDiego/TSG-C-2003-05-San%20Diego/WG1/SWG12/C12-20030512-006%20=%20C12-20030217-015_Draft_Baseline%20Text%20of%20FFMS_R2.doc). */ |
|
5578 caps = gst_caps_new_simple ("audio/qcelp", NULL); |
|
5579 codec_name = "QCELP"; |
|
5580 break; |
|
5581 default: |
|
5582 break; |
|
5583 } |
|
5584 |
|
5585 /* If we have a replacement caps, then change our caps for this stream */ |
|
5586 if (caps) { |
|
5587 gst_caps_unref (stream->caps); |
|
5588 stream->caps = caps; |
|
5589 } |
|
5590 |
|
5591 if (codec_name && list) |
|
5592 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, |
|
5593 GST_TAG_AUDIO_CODEC, codec_name, NULL); |
|
5594 |
|
5595 /* Add the codec_data attribute to caps, if we have it */ |
|
5596 if (data_ptr) { |
|
5597 GstBuffer *buffer; |
|
5598 |
|
5599 buffer = gst_buffer_new_and_alloc (data_len); |
|
5600 memcpy (GST_BUFFER_DATA (buffer), data_ptr, data_len); |
|
5601 |
|
5602 GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds"); |
|
5603 GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len); |
|
5604 |
|
5605 gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER, |
|
5606 buffer, NULL); |
|
5607 gst_buffer_unref (buffer); |
|
5608 } |
|
5609 |
|
5610 } |
|
5611 |
|
5612 #define _codec(name) \ |
|
5613 do { \ |
|
5614 if (codec_name) { \ |
|
5615 *codec_name = g_strdup (name); \ |
|
5616 } \ |
|
5617 } while (0) |
|
5618 |
|
5619 static GstCaps * |
|
5620 qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
5621 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name) |
|
5622 { |
|
5623 GstCaps *caps; |
|
5624 const GstStructure *s; |
|
5625 const gchar *name; |
|
5626 |
|
5627 switch (fourcc) { |
|
5628 case GST_MAKE_FOURCC ('p', 'n', 'g', ' '): |
|
5629 _codec ("PNG still images"); |
|
5630 caps = gst_caps_new_simple ("image/png", NULL); |
|
5631 break; |
|
5632 case GST_MAKE_FOURCC ('j', 'p', 'e', 'g'): |
|
5633 _codec ("JPEG still images"); |
|
5634 caps = gst_caps_new_simple ("image/jpeg", NULL); |
|
5635 break; |
|
5636 case GST_MAKE_FOURCC ('m', 'j', 'p', 'a'): |
|
5637 case GST_MAKE_FOURCC ('A', 'V', 'D', 'J'): |
|
5638 case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'): |
|
5639 _codec ("Motion-JPEG"); |
|
5640 caps = gst_caps_new_simple ("image/jpeg", NULL); |
|
5641 break; |
|
5642 case GST_MAKE_FOURCC ('m', 'j', 'p', 'b'): |
|
5643 _codec ("Motion-JPEG format B"); |
|
5644 caps = gst_caps_new_simple ("video/x-mjpeg-b", NULL); |
|
5645 break; |
|
5646 case GST_MAKE_FOURCC ('m', 'j', 'p', '2'): |
|
5647 _codec ("JPEG-2000"); |
|
5648 /* override to what it should be according to spec, avoid palette_data */ |
|
5649 stream->bits_per_sample = 24; |
|
5650 caps = gst_caps_new_simple ("image/x-j2c", "fields", G_TYPE_INT, 1, NULL); |
|
5651 break; |
|
5652 case GST_MAKE_FOURCC ('S', 'V', 'Q', '3'): |
|
5653 _codec ("Sorensen video v.3"); |
|
5654 caps = gst_caps_new_simple ("video/x-svq", |
|
5655 "svqversion", G_TYPE_INT, 3, NULL); |
|
5656 break; |
|
5657 case GST_MAKE_FOURCC ('s', 'v', 'q', 'i'): |
|
5658 case GST_MAKE_FOURCC ('S', 'V', 'Q', '1'): |
|
5659 _codec ("Sorensen video v.1"); |
|
5660 caps = gst_caps_new_simple ("video/x-svq", |
|
5661 "svqversion", G_TYPE_INT, 1, NULL); |
|
5662 break; |
|
5663 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '): |
|
5664 { |
|
5665 guint16 bps; |
|
5666 |
|
5667 _codec ("Raw RGB video"); |
|
5668 bps = QT_UINT16 (stsd_data + 98); |
|
5669 /* set common stuff */ |
|
5670 caps = gst_caps_new_simple ("video/x-raw-rgb", |
|
5671 "endianness", G_TYPE_INT, G_BYTE_ORDER, "depth", G_TYPE_INT, bps, |
|
5672 NULL); |
|
5673 |
|
5674 switch (bps) { |
|
5675 case 15: |
|
5676 gst_caps_set_simple (caps, |
|
5677 "bpp", G_TYPE_INT, 16, |
|
5678 "endianness", G_TYPE_INT, G_BIG_ENDIAN, |
|
5679 "red_mask", G_TYPE_INT, 0x7c00, |
|
5680 "green_mask", G_TYPE_INT, 0x03e0, |
|
5681 "blue_mask", G_TYPE_INT, 0x001f, NULL); |
|
5682 break; |
|
5683 case 16: |
|
5684 gst_caps_set_simple (caps, |
|
5685 "bpp", G_TYPE_INT, 16, |
|
5686 "endianness", G_TYPE_INT, G_BIG_ENDIAN, |
|
5687 "red_mask", G_TYPE_INT, 0xf800, |
|
5688 "green_mask", G_TYPE_INT, 0x07e0, |
|
5689 "blue_mask", G_TYPE_INT, 0x001f, NULL); |
|
5690 break; |
|
5691 case 24: |
|
5692 gst_caps_set_simple (caps, |
|
5693 "bpp", G_TYPE_INT, 24, |
|
5694 "endianness", G_TYPE_INT, G_BIG_ENDIAN, |
|
5695 "red_mask", G_TYPE_INT, 0xff0000, |
|
5696 "green_mask", G_TYPE_INT, 0x00ff00, |
|
5697 "blue_mask", G_TYPE_INT, 0x0000ff, NULL); |
|
5698 break; |
|
5699 case 32: |
|
5700 gst_caps_set_simple (caps, |
|
5701 "bpp", G_TYPE_INT, 32, |
|
5702 "endianness", G_TYPE_INT, G_BIG_ENDIAN, |
|
5703 "alpha_mask", G_TYPE_INT, 0xff000000, |
|
5704 "red_mask", G_TYPE_INT, 0x00ff0000, |
|
5705 "green_mask", G_TYPE_INT, 0x0000ff00, |
|
5706 "blue_mask", G_TYPE_INT, 0x000000ff, NULL); |
|
5707 break; |
|
5708 default: |
|
5709 /* unknown */ |
|
5710 break; |
|
5711 } |
|
5712 break; |
|
5713 } |
|
5714 case GST_MAKE_FOURCC ('y', 'v', '1', '2'): |
|
5715 _codec ("Raw planar YUV 4:2:0"); |
|
5716 caps = gst_caps_new_simple ("video/x-raw-yuv", |
|
5717 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'), |
|
5718 NULL); |
|
5719 break; |
|
5720 case GST_MAKE_FOURCC ('y', 'u', 'v', '2'): |
|
5721 case GST_MAKE_FOURCC ('Y', 'u', 'v', '2'): |
|
5722 _codec ("Raw packed YUV 4:2:2"); |
|
5723 caps = gst_caps_new_simple ("video/x-raw-yuv", |
|
5724 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'), |
|
5725 NULL); |
|
5726 break; |
|
5727 case GST_MAKE_FOURCC ('2', 'v', 'u', 'y'): |
|
5728 _codec ("Raw packed YUV 4:2:0"); |
|
5729 caps = gst_caps_new_simple ("video/x-raw-yuv", |
|
5730 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'), |
|
5731 NULL); |
|
5732 break; |
|
5733 case GST_MAKE_FOURCC ('v', '2', '1', '0'): |
|
5734 _codec ("Raw packed YUV 10-bit 4:2:2"); |
|
5735 caps = gst_caps_new_simple ("video/x-raw-yuv", |
|
5736 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('v', '2', '1', '0'), |
|
5737 NULL); |
|
5738 break; |
|
5739 case GST_MAKE_FOURCC ('m', 'p', 'e', 'g'): |
|
5740 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'): |
|
5741 _codec ("MPEG-1 video"); |
|
5742 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1, |
|
5743 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5744 break; |
|
5745 case GST_MAKE_FOURCC ('h', 'd', 'v', '1'): // HDV 720p30 |
|
5746 case GST_MAKE_FOURCC ('h', 'd', 'v', '2'): // HDV 1080i60 |
|
5747 case GST_MAKE_FOURCC ('h', 'd', 'v', '3'): // HDV 1080i50 |
|
5748 case GST_MAKE_FOURCC ('h', 'd', 'v', '5'): // HDV 720p25 |
|
5749 case GST_MAKE_FOURCC ('h', 'd', 'v', '6'): // HDV 1080i60 |
|
5750 case GST_MAKE_FOURCC ('m', 'x', '5', 'n'): // MPEG2 IMX NTSC 525/60 50mb/s produced by FCP |
|
5751 case GST_MAKE_FOURCC ('m', 'x', '5', 'p'): // MPEG2 IMX PAL 625/60 50mb/s produced by FCP |
|
5752 case GST_MAKE_FOURCC ('m', 'x', '4', 'n'): // MPEG2 IMX NTSC 525/60 40mb/s produced by FCP |
|
5753 case GST_MAKE_FOURCC ('m', 'x', '4', 'p'): // MPEG2 IMX PAL 625/60 40mb/s produced by FCP |
|
5754 case GST_MAKE_FOURCC ('m', 'x', '3', 'n'): // MPEG2 IMX NTSC 525/60 30mb/s produced by FCP |
|
5755 case GST_MAKE_FOURCC ('m', 'x', '3', 'p'): // MPEG2 IMX PAL 625/50 30mb/s produced by FCP |
|
5756 case GST_MAKE_FOURCC ('x', 'd', 'v', '2'): // XDCAM HD 1080i60 |
|
5757 case GST_MAKE_FOURCC ('A', 'V', 'm', 'p'): // AVID IMX PAL |
|
5758 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'): // AVID IMX PAL |
|
5759 _codec ("MPEG-2 video"); |
|
5760 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 2, |
|
5761 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5762 break; |
|
5763 case GST_MAKE_FOURCC ('g', 'i', 'f', ' '): |
|
5764 _codec ("GIF still images"); |
|
5765 caps = gst_caps_new_simple ("image/gif", NULL); |
|
5766 break; |
|
5767 case GST_MAKE_FOURCC ('h', '2', '6', '3'): |
|
5768 case GST_MAKE_FOURCC ('H', '2', '6', '3'): |
|
5769 case GST_MAKE_FOURCC ('s', '2', '6', '3'): |
|
5770 case GST_MAKE_FOURCC ('U', '2', '6', '3'): |
|
5771 _codec ("H.263"); |
|
5772 /* ffmpeg uses the height/width props, don't know why */ |
|
5773 caps = gst_caps_new_simple ("video/x-h263", NULL); |
|
5774 break; |
|
5775 case GST_MAKE_FOURCC ('m', 'p', '4', 'v'): |
|
5776 _codec ("MPEG-4 video"); |
|
5777 caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4, |
|
5778 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5779 break; |
|
5780 case GST_MAKE_FOURCC ('3', 'i', 'v', 'd'): |
|
5781 case GST_MAKE_FOURCC ('3', 'I', 'V', 'D'): |
|
5782 _codec ("Microsoft MPEG-4 4.3"); /* FIXME? */ |
|
5783 caps = gst_caps_new_simple ("video/x-msmpeg", |
|
5784 "msmpegversion", G_TYPE_INT, 43, NULL); |
|
5785 break; |
|
5786 case GST_MAKE_FOURCC ('3', 'I', 'V', '1'): |
|
5787 case GST_MAKE_FOURCC ('3', 'I', 'V', '2'): |
|
5788 _codec ("3ivX video"); |
|
5789 caps = gst_caps_new_simple ("video/x-3ivx", NULL); |
|
5790 break; |
|
5791 case GST_MAKE_FOURCC ('D', 'I', 'V', '3'): |
|
5792 _codec ("DivX 3"); |
|
5793 caps = gst_caps_new_simple ("video/x-divx", |
|
5794 "divxversion", G_TYPE_INT, 3, NULL); |
|
5795 break; |
|
5796 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'): |
|
5797 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'): |
|
5798 _codec ("DivX 4"); |
|
5799 caps = gst_caps_new_simple ("video/x-divx", |
|
5800 "divxversion", G_TYPE_INT, 4, NULL); |
|
5801 break; |
|
5802 case GST_MAKE_FOURCC ('D', 'X', '5', '0'): |
|
5803 _codec ("DivX 5"); |
|
5804 caps = gst_caps_new_simple ("video/x-divx", |
|
5805 "divxversion", G_TYPE_INT, 5, NULL); |
|
5806 break; |
|
5807 case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'): |
|
5808 case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'): |
|
5809 _codec ("XVID MPEG-4"); |
|
5810 caps = gst_caps_new_simple ("video/x-xvid", NULL); |
|
5811 break; |
|
5812 |
|
5813 case GST_MAKE_FOURCC ('F', 'M', 'P', '4'): |
|
5814 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'): |
|
5815 caps = gst_caps_new_simple ("video/mpeg", |
|
5816 "mpegversion", G_TYPE_INT, 4, NULL); |
|
5817 if (codec_name) |
|
5818 *codec_name = g_strdup ("FFmpeg MPEG-4"); |
|
5819 break; |
|
5820 |
|
5821 case GST_MAKE_FOURCC ('c', 'v', 'i', 'd'): |
|
5822 _codec ("Cinepak"); |
|
5823 caps = gst_caps_new_simple ("video/x-cinepak", NULL); |
|
5824 break; |
|
5825 case GST_MAKE_FOURCC ('q', 'd', 'r', 'w'): |
|
5826 _codec ("Apple QuickDraw"); |
|
5827 caps = gst_caps_new_simple ("video/x-qdrw", NULL); |
|
5828 break; |
|
5829 case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'): |
|
5830 _codec ("Apple video"); |
|
5831 caps = gst_caps_new_simple ("video/x-apple-video", NULL); |
|
5832 break; |
|
5833 case GST_MAKE_FOURCC ('a', 'v', 'c', '1'): |
|
5834 _codec ("H.264 / AVC"); |
|
5835 caps = gst_caps_new_simple ("video/x-h264", NULL); |
|
5836 break; |
|
5837 case GST_MAKE_FOURCC ('r', 'l', 'e', ' '): |
|
5838 _codec ("Run-length encoding"); |
|
5839 caps = gst_caps_new_simple ("video/x-rle", |
|
5840 "layout", G_TYPE_STRING, "quicktime", NULL); |
|
5841 break; |
|
5842 case GST_MAKE_FOURCC ('i', 'v', '3', '2'): |
|
5843 _codec ("Indeo Video 3"); |
|
5844 caps = gst_caps_new_simple ("video/x-indeo", |
|
5845 "indeoversion", G_TYPE_INT, 3, NULL); |
|
5846 break; |
|
5847 case GST_MAKE_FOURCC ('I', 'V', '4', '1'): |
|
5848 case GST_MAKE_FOURCC ('i', 'v', '4', '1'): |
|
5849 _codec ("Intel Video 4"); |
|
5850 caps = gst_caps_new_simple ("video/x-indeo", |
|
5851 "indeoversion", G_TYPE_INT, 4, NULL); |
|
5852 break; |
|
5853 case GST_MAKE_FOURCC ('d', 'v', 'c', 'p'): |
|
5854 case GST_MAKE_FOURCC ('d', 'v', 'c', ' '): |
|
5855 case GST_MAKE_FOURCC ('d', 'v', 's', 'd'): |
|
5856 case GST_MAKE_FOURCC ('D', 'V', 'S', 'D'): |
|
5857 case GST_MAKE_FOURCC ('d', 'v', 'c', 's'): |
|
5858 case GST_MAKE_FOURCC ('D', 'V', 'C', 'S'): |
|
5859 case GST_MAKE_FOURCC ('d', 'v', '2', '5'): |
|
5860 case GST_MAKE_FOURCC ('d', 'v', 'p', 'p'): |
|
5861 _codec ("DV Video"); |
|
5862 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 25, |
|
5863 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5864 break; |
|
5865 case GST_MAKE_FOURCC ('d', 'v', '5', 'n'): //DVCPRO50 NTSC |
|
5866 case GST_MAKE_FOURCC ('d', 'v', '5', 'p'): //DVCPRO50 PAL |
|
5867 _codec ("DVCPro50 Video"); |
|
5868 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 50, |
|
5869 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5870 break; |
|
5871 case GST_MAKE_FOURCC ('d', 'v', 'h', '5'): //DVCPRO HD 50i produced by FCP |
|
5872 case GST_MAKE_FOURCC ('d', 'v', 'h', '6'): //DVCPRO HD 60i produced by FCP |
|
5873 _codec ("DVCProHD Video"); |
|
5874 caps = gst_caps_new_simple ("video/x-dv", "dvversion", G_TYPE_INT, 100, |
|
5875 "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5876 break; |
|
5877 case GST_MAKE_FOURCC ('s', 'm', 'c', ' '): |
|
5878 _codec ("Apple Graphics (SMC)"); |
|
5879 caps = gst_caps_new_simple ("video/x-smc", NULL); |
|
5880 break; |
|
5881 case GST_MAKE_FOURCC ('V', 'P', '3', '1'): |
|
5882 _codec ("VP3"); |
|
5883 caps = gst_caps_new_simple ("video/x-vp3", NULL); |
|
5884 break; |
|
5885 case GST_MAKE_FOURCC ('X', 'i', 'T', 'h'): |
|
5886 _codec ("Theora"); |
|
5887 caps = gst_caps_new_simple ("video/x-theora", NULL); |
|
5888 /* theora uses one byte of padding in the data stream because it does not |
|
5889 * allow 0 sized packets while theora does */ |
|
5890 stream->padding = 1; |
|
5891 break; |
|
5892 case GST_MAKE_FOURCC ('d', 'r', 'a', 'c'): |
|
5893 _codec ("Dirac"); |
|
5894 caps = gst_caps_new_simple ("video/x-dirac", NULL); |
|
5895 break; |
|
5896 case GST_MAKE_FOURCC ('t', 'i', 'f', 'f'): |
|
5897 _codec ("TIFF still images"); |
|
5898 caps = gst_caps_new_simple ("image/tiff", NULL); |
|
5899 break; |
|
5900 case GST_MAKE_FOURCC ('i', 'c', 'o', 'd'): |
|
5901 _codec ("Apple Intermediate Codec"); |
|
5902 caps = gst_caps_from_string ("video/x-apple-intermediate-codec"); |
|
5903 break; |
|
5904 case GST_MAKE_FOURCC ('A', 'V', 'd', 'n'): |
|
5905 _codec ("AVID DNxHD"); |
|
5906 caps = gst_caps_from_string ("video/x-dnxhd"); |
|
5907 break; |
|
5908 case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'): |
|
5909 default: |
|
5910 { |
|
5911 char *s; |
|
5912 |
|
5913 s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT, |
|
5914 GST_FOURCC_ARGS (fourcc)); |
|
5915 caps = gst_caps_new_simple (s, NULL); |
|
5916 break; |
|
5917 } |
|
5918 } |
|
5919 |
|
5920 /* enable clipping for raw video streams */ |
|
5921 s = gst_caps_get_structure (caps, 0); |
|
5922 name = gst_structure_get_name (s); |
|
5923 if (g_str_has_prefix (name, "video/x-raw-")) { |
|
5924 stream->need_clip = TRUE; |
|
5925 } |
|
5926 return caps; |
|
5927 } |
|
5928 |
|
5929 static GstCaps * |
|
5930 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
5931 guint32 fourcc, const guint8 * data, int len, gchar ** codec_name) |
|
5932 { |
|
5933 GstCaps *caps; |
|
5934 const GstStructure *s; |
|
5935 const gchar *name; |
|
5936 gint endian = 0; |
|
5937 |
|
5938 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc %08x", fourcc); |
|
5939 |
|
5940 switch (fourcc) { |
|
5941 case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'): |
|
5942 case GST_MAKE_FOURCC ('r', 'a', 'w', ' '): |
|
5943 _codec ("Raw 8-bit PCM audio"); |
|
5944 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 8, |
|
5945 "depth", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL); |
|
5946 break; |
|
5947 case GST_MAKE_FOURCC ('t', 'w', 'o', 's'): |
|
5948 endian = G_BIG_ENDIAN; |
|
5949 /* fall-through */ |
|
5950 case GST_MAKE_FOURCC ('s', 'o', 'w', 't'): |
|
5951 { |
|
5952 gchar *str; |
|
5953 gint depth; |
|
5954 |
|
5955 if (!endian) |
|
5956 endian = G_LITTLE_ENDIAN; |
|
5957 |
|
5958 depth = stream->bytes_per_packet * 8; |
|
5959 str = g_strdup_printf ("Raw %d-bit PCM audio", depth); |
|
5960 _codec (str); |
|
5961 g_free (str); |
|
5962 caps = gst_caps_new_simple ("audio/x-raw-int", |
|
5963 "width", G_TYPE_INT, depth, "depth", G_TYPE_INT, depth, |
|
5964 "endianness", G_TYPE_INT, endian, |
|
5965 "signed", G_TYPE_BOOLEAN, TRUE, NULL); |
|
5966 break; |
|
5967 } |
|
5968 case GST_MAKE_FOURCC ('f', 'l', '6', '4'): |
|
5969 _codec ("Raw 64-bit floating-point audio"); |
|
5970 caps = gst_caps_new_simple ("audio/x-raw-float", "width", G_TYPE_INT, 64, |
|
5971 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL); |
|
5972 break; |
|
5973 case GST_MAKE_FOURCC ('f', 'l', '3', '2'): |
|
5974 _codec ("Raw 32-bit floating-point audio"); |
|
5975 caps = gst_caps_new_simple ("audio/x-raw-float", "width", G_TYPE_INT, 32, |
|
5976 "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL); |
|
5977 break; |
|
5978 case FOURCC_in24: |
|
5979 _codec ("Raw 24-bit PCM audio"); |
|
5980 /* we assume BIG ENDIAN, an enda box will tell us to change this to little |
|
5981 * endian later */ |
|
5982 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 24, |
|
5983 "depth", G_TYPE_INT, 24, |
|
5984 "endianness", G_TYPE_INT, G_BIG_ENDIAN, |
|
5985 "signed", G_TYPE_BOOLEAN, TRUE, NULL); |
|
5986 break; |
|
5987 case GST_MAKE_FOURCC ('i', 'n', '3', '2'): |
|
5988 _codec ("Raw 32-bit PCM audio"); |
|
5989 caps = gst_caps_new_simple ("audio/x-raw-int", "width", G_TYPE_INT, 32, |
|
5990 "depth", G_TYPE_INT, 32, |
|
5991 "endianness", G_TYPE_INT, G_BIG_ENDIAN, |
|
5992 "signed", G_TYPE_BOOLEAN, TRUE, NULL); |
|
5993 break; |
|
5994 case GST_MAKE_FOURCC ('u', 'l', 'a', 'w'): |
|
5995 _codec ("Mu-law audio"); |
|
5996 caps = gst_caps_new_simple ("audio/x-mulaw", NULL); |
|
5997 break; |
|
5998 case GST_MAKE_FOURCC ('a', 'l', 'a', 'w'): |
|
5999 _codec ("A-law audio"); |
|
6000 caps = gst_caps_new_simple ("audio/x-alaw", NULL); |
|
6001 break; |
|
6002 case 0x0200736d: |
|
6003 case 0x6d730002: |
|
6004 _codec ("Microsoft ADPCM"); |
|
6005 /* Microsoft ADPCM-ACM code 2 */ |
|
6006 caps = gst_caps_new_simple ("audio/x-adpcm", |
|
6007 "layout", G_TYPE_STRING, "microsoft", NULL); |
|
6008 break; |
|
6009 case 0x1100736d: |
|
6010 case 0x6d730011: |
|
6011 _codec ("IMA Loki SDL MJPEG ADPCM"); |
|
6012 /* Loki ADPCM, See #550288 for a file that only decodes |
|
6013 * with the smjpeg variant of the ADPCM decoder. */ |
|
6014 caps = gst_caps_new_simple ("audio/x-adpcm", |
|
6015 "layout", G_TYPE_STRING, "smjpeg", NULL); |
|
6016 break; |
|
6017 case 0x1700736d: |
|
6018 case 0x6d730017: |
|
6019 _codec ("DVI/Intel IMA ADPCM"); |
|
6020 /* FIXME DVI/Intel IMA ADPCM/ACM code 17 */ |
|
6021 caps = gst_caps_new_simple ("audio/x-adpcm", |
|
6022 "layout", G_TYPE_STRING, "quicktime", NULL); |
|
6023 break; |
|
6024 case 0x5500736d: |
|
6025 case 0x6d730055: |
|
6026 /* MPEG layer 3, CBR only (pre QT4.1) */ |
|
6027 case GST_MAKE_FOURCC ('.', 'm', 'p', '3'): |
|
6028 _codec ("MPEG-1 layer 3"); |
|
6029 /* MPEG layer 3, CBR & VBR (QT4.1 and later) */ |
|
6030 caps = gst_caps_new_simple ("audio/mpeg", "layer", G_TYPE_INT, 3, |
|
6031 "mpegversion", G_TYPE_INT, 1, NULL); |
|
6032 break; |
|
6033 case 0x20736d: |
|
6034 case GST_MAKE_FOURCC ('a', 'c', '-', '3'): |
|
6035 _codec ("AC-3 audio"); |
|
6036 caps = gst_caps_new_simple ("audio/x-ac3", NULL); |
|
6037 stream->sampled = TRUE; |
|
6038 break; |
|
6039 case GST_MAKE_FOURCC ('M', 'A', 'C', '3'): |
|
6040 _codec ("MACE-3"); |
|
6041 caps = gst_caps_new_simple ("audio/x-mace", |
|
6042 "maceversion", G_TYPE_INT, 3, NULL); |
|
6043 break; |
|
6044 case GST_MAKE_FOURCC ('M', 'A', 'C', '6'): |
|
6045 _codec ("MACE-6"); |
|
6046 caps = gst_caps_new_simple ("audio/x-mace", |
|
6047 "maceversion", G_TYPE_INT, 6, NULL); |
|
6048 break; |
|
6049 case GST_MAKE_FOURCC ('O', 'g', 'g', 'V'): |
|
6050 /* ogg/vorbis */ |
|
6051 caps = gst_caps_new_simple ("application/ogg", NULL); |
|
6052 break; |
|
6053 case GST_MAKE_FOURCC ('d', 'v', 'c', 'a'): |
|
6054 _codec ("DV audio"); |
|
6055 caps = gst_caps_new_simple ("audio/x-dv", NULL); |
|
6056 break; |
|
6057 case GST_MAKE_FOURCC ('m', 'p', '4', 'a'): |
|
6058 _codec ("MPEG-4 AAC audio"); |
|
6059 caps = gst_caps_new_simple ("audio/mpeg", |
|
6060 "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE, NULL); |
|
6061 break; |
|
6062 case GST_MAKE_FOURCC ('Q', 'D', 'M', 'C'): |
|
6063 _codec ("QDesign Music"); |
|
6064 caps = gst_caps_new_simple ("audio/x-qdm", NULL); |
|
6065 break; |
|
6066 case GST_MAKE_FOURCC ('Q', 'D', 'M', '2'): |
|
6067 _codec ("QDesign Music v.2"); |
|
6068 /* FIXME: QDesign music version 2 (no constant) */ |
|
6069 if (data) { |
|
6070 caps = gst_caps_new_simple ("audio/x-qdm2", |
|
6071 "framesize", G_TYPE_INT, QT_UINT32 (data + 52), |
|
6072 "bitrate", G_TYPE_INT, QT_UINT32 (data + 40), |
|
6073 "blocksize", G_TYPE_INT, QT_UINT32 (data + 44), NULL); |
|
6074 } else { |
|
6075 caps = gst_caps_new_simple ("audio/x-qdm2", NULL); |
|
6076 } |
|
6077 break; |
|
6078 case GST_MAKE_FOURCC ('a', 'g', 's', 'm'): |
|
6079 _codec ("GSM audio"); |
|
6080 caps = gst_caps_new_simple ("audio/x-gsm", NULL); |
|
6081 break; |
|
6082 case GST_MAKE_FOURCC ('s', 'a', 'm', 'r'): |
|
6083 _codec ("AMR audio"); |
|
6084 caps = gst_caps_new_simple ("audio/AMR", NULL); |
|
6085 break; |
|
6086 case GST_MAKE_FOURCC ('s', 'a', 'w', 'b'): |
|
6087 _codec ("AMR-WB audio"); |
|
6088 caps = gst_caps_new_simple ("audio/AMR-WB", NULL); |
|
6089 break; |
|
6090 case GST_MAKE_FOURCC ('i', 'm', 'a', '4'): |
|
6091 _codec ("Quicktime IMA ADPCM"); |
|
6092 caps = gst_caps_new_simple ("audio/x-adpcm", |
|
6093 "layout", G_TYPE_STRING, "quicktime", NULL); |
|
6094 break; |
|
6095 case GST_MAKE_FOURCC ('a', 'l', 'a', 'c'): |
|
6096 _codec ("Apple lossless audio"); |
|
6097 caps = gst_caps_new_simple ("audio/x-alac", NULL); |
|
6098 break; |
|
6099 case GST_MAKE_FOURCC ('q', 't', 'v', 'r'): |
|
6100 /* ? */ |
|
6101 case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'): |
|
6102 /* QUALCOMM PureVoice */ |
|
6103 default: |
|
6104 { |
|
6105 char *s; |
|
6106 |
|
6107 s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT, |
|
6108 GST_FOURCC_ARGS (fourcc)); |
|
6109 caps = gst_caps_new_simple (s, NULL); |
|
6110 break; |
|
6111 } |
|
6112 } |
|
6113 |
|
6114 /* enable clipping for raw audio streams */ |
|
6115 s = gst_caps_get_structure (caps, 0); |
|
6116 name = gst_structure_get_name (s); |
|
6117 if (g_str_has_prefix (name, "audio/x-raw-")) { |
|
6118 stream->need_clip = TRUE; |
|
6119 } |
|
6120 return caps; |
|
6121 } |
|
6122 |
|
6123 static GstCaps * |
|
6124 qtdemux_subp_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, |
|
6125 guint32 fourcc, const guint8 * stsd_data, gchar ** codec_name) |
|
6126 { |
|
6127 GstCaps *caps; |
|
6128 |
|
6129 GST_DEBUG_OBJECT (qtdemux, "resolve fourcc %08x", fourcc); |
|
6130 |
|
6131 switch (fourcc) { |
|
6132 case GST_MAKE_FOURCC ('m', 'p', '4', 's'): |
|
6133 _codec ("DVD subtitle"); |
|
6134 caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL); |
|
6135 break; |
|
6136 default: |
|
6137 { |
|
6138 char *s; |
|
6139 |
|
6140 s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT, |
|
6141 GST_FOURCC_ARGS (fourcc)); |
|
6142 caps = gst_caps_new_simple (s, NULL); |
|
6143 break; |
|
6144 } |
|
6145 } |
|
6146 return caps; |
|
6147 } |