1 /* GStreamer
2 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
3 * Copyright (C) <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
4 * Copyright (C) <2009-2010> STEricsson <benjamin.gaignard@stericsson.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21 /* Element-Checklist-Version: 5 */
22
23 /**
24 * SECTION:element-avidemux
25 * @title: avidemux
26 *
27 * Demuxes an .avi file into raw or compressed audio and/or video streams.
28 *
29 * This element supports both push and pull-based scheduling, depending on the
30 * capabilities of the upstream elements.
31 *
32 * ## Example launch line
33 * |[
34 * gst-launch-1.0 filesrc location=test.avi ! avidemux name=demux demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_00 ! queue ! decodebin ! videoconvert ! videoscale ! autovideosink
35 * ]| Play (parse and decode) an .avi file and try to output it to
36 * an automatically detected soundcard and videosink. If the AVI file contains
37 * compressed audio or video data, this will only work if you have the
38 * right decoder elements/plugins installed.
39 *
40 */
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include <string.h>
47 #include <stdio.h>
48
49 #include "gst/riff/riff-media.h"
50 #include "gstavielements.h"
51 #include "gstavidemux.h"
52 #include "avi-ids.h"
53 #include <gst/gst-i18n-plugin.h>
54 #include <gst/base/gstadapter.h>
55 #include <gst/tag/tag.h>
56
57 #define DIV_ROUND_UP(s,v) (((s) + ((v)-1)) / (v))
58
59 #define GST_AVI_KEYFRAME (1 << 0)
60 #define ENTRY_IS_KEYFRAME(e) ((e)->flags == GST_AVI_KEYFRAME)
61 #define ENTRY_SET_KEYFRAME(e) ((e)->flags = GST_AVI_KEYFRAME)
62 #define ENTRY_UNSET_KEYFRAME(e) ((e)->flags = 0)
63
64
65 GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
66 #define GST_CAT_DEFAULT avidemux_debug
67
68 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
69 GST_PAD_SINK,
70 GST_PAD_ALWAYS,
71 GST_STATIC_CAPS ("video/x-msvideo")
72 );
73
74 #ifndef GST_DISABLE_GST_DEBUG
75 static const char *const snap_types[2][2] = {
76 {"any", "after"},
77 {"before", "nearest"},
78 };
79 #endif
80
81 static void gst_avi_demux_finalize (GObject * object);
82
83 static void gst_avi_demux_reset (GstAviDemux * avi);
84
85 #if 0
86 static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
87 #endif
88 static gboolean gst_avi_demux_handle_src_event (GstPad * pad,
89 GstObject * parent, GstEvent * event);
90 static gboolean gst_avi_demux_handle_sink_event (GstPad * pad,
91 GstObject * parent, GstEvent * event);
92 static gboolean gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event);
93
94 #if 0
95 static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
96 #endif
97 static gboolean gst_avi_demux_handle_src_query (GstPad * pad,
98 GstObject * parent, GstQuery * query);
99 static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format,
100 gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
101
102 static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment,
103 GstSeekFlags flags);
104 static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
105 GstEvent * event);
106 static gboolean gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
107 GstEvent * event);
108 static void gst_avi_demux_loop (GstPad * pad);
109 static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad,
110 GstObject * parent);
111 static gboolean gst_avi_demux_sink_activate_mode (GstPad * sinkpad,
112 GstObject * parent, GstPadMode mode, gboolean active);
113 static GstFlowReturn gst_avi_demux_chain (GstPad * pad, GstObject * parent,
114 GstBuffer * buf);
115 #if 0
116 static void gst_avi_demux_set_index (GstElement * element, GstIndex * index);
117 static GstIndex *gst_avi_demux_get_index (GstElement * element);
118 #endif
119 static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
120 GstStateChange transition);
121 static void gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi);
122 static void gst_avi_demux_get_buffer_info (GstAviDemux * avi,
123 GstAviStream * stream, guint entry_n, GstClockTime * timestamp,
124 GstClockTime * ts_end, guint64 * offset, guint64 * offset_end);
125
126 static void gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf);
127 static void gst_avi_demux_parse_strd (GstAviDemux * avi, GstBuffer * buf);
128
129 static void parse_tag_value (GstAviDemux * avi, GstTagList * taglist,
130 const gchar * type, guint8 * ptr, guint tsize);
131
132 /* GObject methods */
133
134 #define gst_avi_demux_parent_class parent_class
135 G_DEFINE_TYPE (GstAviDemux, gst_avi_demux, GST_TYPE_ELEMENT);
136 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (avidemux, "avidemux", GST_RANK_PRIMARY,
137 GST_TYPE_AVI_DEMUX, avi_element_init (plugin));
138
139 static void
gst_avi_demux_class_init(GstAviDemuxClass * klass)140 gst_avi_demux_class_init (GstAviDemuxClass * klass)
141 {
142 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
143 GObjectClass *gobject_class = (GObjectClass *) klass;
144 GstPadTemplate *videosrctempl, *audiosrctempl, *subsrctempl, *subpicsrctempl;
145 GstCaps *audcaps, *vidcaps, *subcaps, *subpiccaps;
146
147 GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
148 0, "Demuxer for AVI streams");
149
150 gobject_class->finalize = gst_avi_demux_finalize;
151
152 gstelement_class->change_state =
153 GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
154 #if 0
155 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_avi_demux_set_index);
156 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_avi_demux_get_index);
157 #endif
158
159 audcaps = gst_riff_create_audio_template_caps ();
160 gst_caps_append (audcaps, gst_caps_new_empty_simple ("audio/x-avi-unknown"));
161 audiosrctempl = gst_pad_template_new ("audio_%u",
162 GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
163
164 vidcaps = gst_riff_create_video_template_caps ();
165 gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
166 gst_caps_append (vidcaps, gst_caps_new_empty_simple ("video/x-avi-unknown"));
167 videosrctempl = gst_pad_template_new ("video_%u",
168 GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
169
170 subcaps = gst_caps_new_empty_simple ("application/x-subtitle-avi");
171 subsrctempl = gst_pad_template_new ("subtitle_%u",
172 GST_PAD_SRC, GST_PAD_SOMETIMES, subcaps);
173 subpiccaps = gst_caps_new_empty_simple ("subpicture/x-xsub");
174 subpicsrctempl = gst_pad_template_new ("subpicture_%u",
175 GST_PAD_SRC, GST_PAD_SOMETIMES, subpiccaps);
176 gst_element_class_add_pad_template (gstelement_class, audiosrctempl);
177 gst_element_class_add_pad_template (gstelement_class, videosrctempl);
178 gst_element_class_add_pad_template (gstelement_class, subsrctempl);
179 gst_element_class_add_pad_template (gstelement_class, subpicsrctempl);
180 gst_element_class_add_static_pad_template (gstelement_class, &sink_templ);
181
182 gst_caps_unref (audcaps);
183 gst_caps_unref (vidcaps);
184 gst_caps_unref (subcaps);
185 gst_caps_unref (subpiccaps);
186
187 gst_element_class_set_static_metadata (gstelement_class, "Avi demuxer",
188 "Codec/Demuxer",
189 "Demultiplex an avi file into audio and video",
190 "Erik Walthinsen <omega@cse.ogi.edu>, "
191 "Wim Taymans <wim.taymans@chello.be>, "
192 "Thijs Vermeir <thijsvermeir@gmail.com>");
193 }
194
195 static void
gst_avi_demux_init(GstAviDemux * avi)196 gst_avi_demux_init (GstAviDemux * avi)
197 {
198 avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
199 gst_pad_set_activate_function (avi->sinkpad,
200 GST_DEBUG_FUNCPTR (gst_avi_demux_sink_activate));
201 gst_pad_set_activatemode_function (avi->sinkpad,
202 GST_DEBUG_FUNCPTR (gst_avi_demux_sink_activate_mode));
203 gst_pad_set_chain_function (avi->sinkpad,
204 GST_DEBUG_FUNCPTR (gst_avi_demux_chain));
205 gst_pad_set_event_function (avi->sinkpad,
206 GST_DEBUG_FUNCPTR (gst_avi_demux_handle_sink_event));
207 gst_element_add_pad (GST_ELEMENT_CAST (avi), avi->sinkpad);
208
209 avi->adapter = gst_adapter_new ();
210 avi->flowcombiner = gst_flow_combiner_new ();
211
212 gst_avi_demux_reset (avi);
213
214 GST_OBJECT_FLAG_SET (avi, GST_ELEMENT_FLAG_INDEXABLE);
215 }
216
217 static void
gst_avi_demux_finalize(GObject * object)218 gst_avi_demux_finalize (GObject * object)
219 {
220 GstAviDemux *avi = GST_AVI_DEMUX (object);
221
222 GST_DEBUG ("AVI: finalize");
223
224 g_object_unref (avi->adapter);
225 gst_flow_combiner_free (avi->flowcombiner);
226
227 G_OBJECT_CLASS (parent_class)->finalize (object);
228 }
229
230 static void
gst_avi_demux_reset_stream(GstAviDemux * avi,GstAviStream * stream)231 gst_avi_demux_reset_stream (GstAviDemux * avi, GstAviStream * stream)
232 {
233 g_free (stream->strh);
234 g_free (stream->strf.data);
235 g_free (stream->name);
236 g_free (stream->index);
237 g_free (stream->indexes);
238 if (stream->initdata)
239 gst_buffer_unref (stream->initdata);
240 if (stream->extradata)
241 gst_buffer_unref (stream->extradata);
242 if (stream->rgb8_palette)
243 gst_buffer_unref (stream->rgb8_palette);
244 if (stream->pad) {
245 if (stream->exposed) {
246 gst_pad_set_active (stream->pad, FALSE);
247 gst_element_remove_pad (GST_ELEMENT_CAST (avi), stream->pad);
248 gst_flow_combiner_remove_pad (avi->flowcombiner, stream->pad);
249 } else
250 gst_object_unref (stream->pad);
251 }
252 if (stream->taglist) {
253 gst_tag_list_unref (stream->taglist);
254 stream->taglist = NULL;
255 }
256 memset (stream, 0, sizeof (GstAviStream));
257 }
258
259 static void
gst_avi_demux_reset(GstAviDemux * avi)260 gst_avi_demux_reset (GstAviDemux * avi)
261 {
262 gint i;
263
264 GST_DEBUG ("AVI: reset");
265
266 for (i = 0; i < avi->num_streams; i++)
267 gst_avi_demux_reset_stream (avi, &avi->stream[i]);
268
269 avi->header_state = GST_AVI_DEMUX_HEADER_TAG_LIST;
270 avi->num_streams = 0;
271 avi->num_v_streams = 0;
272 avi->num_a_streams = 0;
273 avi->num_t_streams = 0;
274 avi->num_sp_streams = 0;
275 avi->main_stream = -1;
276
277 avi->have_group_id = FALSE;
278 avi->group_id = G_MAXUINT;
279
280 avi->state = GST_AVI_DEMUX_START;
281 avi->offset = 0;
282 avi->building_index = FALSE;
283
284 avi->index_offset = 0;
285 g_free (avi->avih);
286 avi->avih = NULL;
287
288 #if 0
289 if (avi->element_index)
290 gst_object_unref (avi->element_index);
291 avi->element_index = NULL;
292 #endif
293
294 if (avi->seg_event) {
295 gst_event_unref (avi->seg_event);
296 avi->seg_event = NULL;
297 }
298 if (avi->seek_event) {
299 gst_event_unref (avi->seek_event);
300 avi->seek_event = NULL;
301 }
302
303 if (avi->globaltags)
304 gst_tag_list_unref (avi->globaltags);
305 avi->globaltags = NULL;
306
307 avi->got_tags = TRUE; /* we always want to push global tags */
308 avi->have_eos = FALSE;
309 avi->seekable = TRUE;
310
311 gst_adapter_clear (avi->adapter);
312
313 gst_segment_init (&avi->segment, GST_FORMAT_TIME);
314 avi->segment_seqnum = 0;
315 }
316
317
318 /* GstElement methods */
319
320 #if 0
321 static const GstFormat *
322 gst_avi_demux_get_src_formats (GstPad * pad)
323 {
324 GstAviStream *stream = gst_pad_get_element_private (pad);
325
326 static const GstFormat src_a_formats[] = {
327 GST_FORMAT_TIME,
328 GST_FORMAT_BYTES,
329 GST_FORMAT_DEFAULT,
330 0
331 };
332 static const GstFormat src_v_formats[] = {
333 GST_FORMAT_TIME,
334 GST_FORMAT_DEFAULT,
335 0
336 };
337
338 return (stream->strh->type == GST_RIFF_FCC_auds ?
339 src_a_formats : src_v_formats);
340 }
341 #endif
342
343 /* assumes stream->strf.auds->av_bps != 0 */
344 static inline GstClockTime
avi_stream_convert_bytes_to_time_unchecked(GstAviStream * stream,guint64 bytes)345 avi_stream_convert_bytes_to_time_unchecked (GstAviStream * stream,
346 guint64 bytes)
347 {
348 return gst_util_uint64_scale_int (bytes, GST_SECOND,
349 stream->strf.auds->av_bps);
350 }
351
352 static inline guint64
avi_stream_convert_time_to_bytes_unchecked(GstAviStream * stream,GstClockTime time)353 avi_stream_convert_time_to_bytes_unchecked (GstAviStream * stream,
354 GstClockTime time)
355 {
356 return gst_util_uint64_scale_int (time, stream->strf.auds->av_bps,
357 GST_SECOND);
358 }
359
360 /* assumes stream->strh->rate != 0 */
361 static inline GstClockTime
avi_stream_convert_frames_to_time_unchecked(GstAviStream * stream,guint64 frames)362 avi_stream_convert_frames_to_time_unchecked (GstAviStream * stream,
363 guint64 frames)
364 {
365 return gst_util_uint64_scale (frames, stream->strh->scale * GST_SECOND,
366 stream->strh->rate);
367 }
368
369 static inline guint64
avi_stream_convert_time_to_frames_unchecked(GstAviStream * stream,GstClockTime time)370 avi_stream_convert_time_to_frames_unchecked (GstAviStream * stream,
371 GstClockTime time)
372 {
373 return gst_util_uint64_scale (time, stream->strh->rate,
374 stream->strh->scale * GST_SECOND);
375 }
376
377 static gboolean
gst_avi_demux_src_convert(GstPad * pad,GstFormat src_format,gint64 src_value,GstFormat * dest_format,gint64 * dest_value)378 gst_avi_demux_src_convert (GstPad * pad,
379 GstFormat src_format,
380 gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
381 {
382 GstAviStream *stream = gst_pad_get_element_private (pad);
383 gboolean res = TRUE;
384
385 GST_LOG_OBJECT (pad,
386 "Received src_format:%s, src_value:%" G_GUINT64_FORMAT
387 ", dest_format:%s", gst_format_get_name (src_format), src_value,
388 gst_format_get_name (*dest_format));
389
390 if (G_UNLIKELY (src_format == *dest_format)) {
391 *dest_value = src_value;
392 goto done;
393 }
394 if (G_UNLIKELY (!stream->strh || !stream->strf.data)) {
395 res = FALSE;
396 goto done;
397 }
398 if (G_UNLIKELY (stream->strh->type == GST_RIFF_FCC_vids &&
399 (src_format == GST_FORMAT_BYTES
400 || *dest_format == GST_FORMAT_BYTES))) {
401 res = FALSE;
402 goto done;
403 }
404
405 switch (src_format) {
406 case GST_FORMAT_TIME:
407 switch (*dest_format) {
408 case GST_FORMAT_BYTES:
409 *dest_value = gst_util_uint64_scale_int (src_value,
410 stream->strf.auds->av_bps, GST_SECOND);
411 break;
412 case GST_FORMAT_DEFAULT:
413 *dest_value =
414 gst_util_uint64_scale_round (src_value, stream->strh->rate,
415 stream->strh->scale * GST_SECOND);
416 break;
417 default:
418 res = FALSE;
419 break;
420 }
421 break;
422 case GST_FORMAT_BYTES:
423 switch (*dest_format) {
424 case GST_FORMAT_TIME:
425 if (stream->strf.auds->av_bps != 0) {
426 *dest_value = avi_stream_convert_bytes_to_time_unchecked (stream,
427 src_value);
428 } else
429 res = FALSE;
430 break;
431 default:
432 res = FALSE;
433 break;
434 }
435 break;
436 case GST_FORMAT_DEFAULT:
437 switch (*dest_format) {
438 case GST_FORMAT_TIME:
439 *dest_value =
440 avi_stream_convert_frames_to_time_unchecked (stream, src_value);
441 break;
442 default:
443 res = FALSE;
444 break;
445 }
446 break;
447 default:
448 res = FALSE;
449 }
450
451 done:
452 GST_LOG_OBJECT (pad,
453 "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
454 gst_format_get_name (*dest_format), *dest_value);
455 return res;
456 }
457
458 static gboolean
gst_avi_demux_handle_src_query(GstPad * pad,GstObject * parent,GstQuery * query)459 gst_avi_demux_handle_src_query (GstPad * pad, GstObject * parent,
460 GstQuery * query)
461 {
462 gboolean res = TRUE;
463 GstAviDemux *avi = GST_AVI_DEMUX (parent);
464
465 GstAviStream *stream = gst_pad_get_element_private (pad);
466
467 if (!stream->strh || !stream->strf.data)
468 return gst_pad_query_default (pad, parent, query);
469
470 switch (GST_QUERY_TYPE (query)) {
471 case GST_QUERY_POSITION:{
472 gint64 pos = 0;
473
474 GST_DEBUG ("pos query for stream %u: frames %u, bytes %u",
475 stream->num, stream->current_entry, stream->current_total);
476
477 /* FIXME, this looks clumsy */
478 if (stream->strh->type == GST_RIFF_FCC_auds) {
479 if (stream->is_vbr) {
480 /* VBR */
481 pos = avi_stream_convert_frames_to_time_unchecked (stream,
482 stream->current_entry);
483 GST_DEBUG_OBJECT (avi, "VBR convert frame %u, time %"
484 GST_TIME_FORMAT, stream->current_entry, GST_TIME_ARGS (pos));
485 } else if (stream->strf.auds->av_bps != 0) {
486 /* CBR */
487 pos = avi_stream_convert_bytes_to_time_unchecked (stream,
488 stream->current_total);
489 GST_DEBUG_OBJECT (avi,
490 "CBR convert bytes %u, time %" GST_TIME_FORMAT,
491 stream->current_total, GST_TIME_ARGS (pos));
492 } else if (stream->idx_n != 0 && stream->total_bytes != 0) {
493 /* calculate timestamps based on percentage of length */
494 guint64 xlen = avi->avih->us_frame *
495 avi->avih->tot_frames * GST_USECOND;
496
497 pos = gst_util_uint64_scale (xlen, stream->current_total,
498 stream->total_bytes);
499 GST_DEBUG_OBJECT (avi,
500 "CBR perc convert bytes %u, time %" GST_TIME_FORMAT,
501 stream->current_total, GST_TIME_ARGS (pos));
502 } else {
503 /* we don't know */
504 res = FALSE;
505 }
506 } else {
507 if (stream->strh->rate != 0) {
508 pos = gst_util_uint64_scale ((guint64) stream->current_entry *
509 stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
510 } else {
511 pos = stream->current_entry * avi->avih->us_frame * GST_USECOND;
512 }
513 }
514 if (res) {
515 GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
516 gst_query_set_position (query, GST_FORMAT_TIME, pos);
517 } else
518 GST_WARNING ("pos query failed");
519 break;
520 }
521 case GST_QUERY_DURATION:
522 {
523 GstFormat fmt;
524 GstClockTime duration;
525
526 /* only act on audio or video streams */
527 if (stream->strh->type != GST_RIFF_FCC_auds &&
528 stream->strh->type != GST_RIFF_FCC_vids &&
529 stream->strh->type != GST_RIFF_FCC_iavs) {
530 res = FALSE;
531 break;
532 }
533
534 /* take stream duration, fall back to avih duration */
535 if ((duration = stream->duration) == -1)
536 if ((duration = stream->hdr_duration) == -1)
537 duration = avi->duration;
538
539 gst_query_parse_duration (query, &fmt, NULL);
540
541 switch (fmt) {
542 case GST_FORMAT_TIME:
543 gst_query_set_duration (query, fmt, duration);
544 break;
545 case GST_FORMAT_DEFAULT:
546 {
547 gint64 dur;
548 GST_DEBUG_OBJECT (query, "total frames is %" G_GUINT32_FORMAT,
549 stream->idx_n);
550
551 if (stream->idx_n > 0)
552 gst_query_set_duration (query, fmt, stream->idx_n);
553 else if (gst_pad_query_convert (pad, GST_FORMAT_TIME,
554 duration, fmt, &dur))
555 gst_query_set_duration (query, fmt, dur);
556 break;
557 }
558 default:
559 res = FALSE;
560 break;
561 }
562 break;
563 }
564 case GST_QUERY_SEEKING:{
565 GstFormat fmt;
566
567 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
568 if (fmt == GST_FORMAT_TIME) {
569 gboolean seekable = TRUE;
570
571 if (avi->streaming) {
572 seekable = avi->seekable;
573 }
574
575 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
576 0, stream->duration);
577 res = TRUE;
578 }
579 break;
580 }
581 case GST_QUERY_CONVERT:{
582 GstFormat src_fmt, dest_fmt;
583 gint64 src_val, dest_val;
584
585 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
586 if ((res = gst_avi_demux_src_convert (pad, src_fmt, src_val, &dest_fmt,
587 &dest_val)))
588 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
589 else
590 res = gst_pad_query_default (pad, parent, query);
591 break;
592 }
593 case GST_QUERY_SEGMENT:
594 {
595 GstFormat format;
596 gint64 start, stop;
597
598 format = avi->segment.format;
599
600 start =
601 gst_segment_to_stream_time (&avi->segment, format,
602 avi->segment.start);
603 if ((stop = avi->segment.stop) == -1)
604 stop = avi->segment.duration;
605 else
606 stop = gst_segment_to_stream_time (&avi->segment, format, stop);
607
608 gst_query_set_segment (query, avi->segment.rate, format, start, stop);
609 res = TRUE;
610 break;
611 }
612 default:
613 res = gst_pad_query_default (pad, parent, query);
614 break;
615 }
616
617 return res;
618 }
619
620 #if 0
621 static const GstEventMask *
622 gst_avi_demux_get_event_mask (GstPad * pad)
623 {
624 static const GstEventMask masks[] = {
625 {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
626 {0,}
627 };
628
629 return masks;
630 }
631 #endif
632
633 #if 0
634 static guint64
635 gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before)
636 {
637 GstAviStream *stream;
638 GstIndexEntry *entry;
639 gint i;
640 gint64 val, min = offset;
641
642 for (i = 0; i < avi->num_streams; i++) {
643 stream = &avi->stream[i];
644
645 entry = gst_index_get_assoc_entry (avi->element_index, stream->index_id,
646 before ? GST_INDEX_LOOKUP_BEFORE : GST_INDEX_LOOKUP_AFTER,
647 GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, offset);
648
649 if (before) {
650 if (entry) {
651 gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
652 GST_DEBUG_OBJECT (avi, "stream %d, previous entry at %"
653 G_GUINT64_FORMAT, i, val);
654 if (val < min)
655 min = val;
656 }
657 continue;
658 }
659
660 if (!entry) {
661 GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
662 stream->current_entry = 0;
663 stream->current_total = 0;
664 continue;
665 }
666
667 gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
668 GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT,
669 i, val);
670
671 gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &val);
672 stream->current_total = val;
673 gst_index_entry_assoc_map (entry, GST_FORMAT_DEFAULT, &val);
674 stream->current_entry = val;
675 }
676
677 return min;
678 }
679 #endif
680
681 static gint
gst_avi_demux_index_entry_offset_search(GstAviIndexEntry * entry,guint64 * offset)682 gst_avi_demux_index_entry_offset_search (GstAviIndexEntry * entry,
683 guint64 * offset)
684 {
685 if (entry->offset < *offset)
686 return -1;
687 else if (entry->offset > *offset)
688 return 1;
689 return 0;
690 }
691
692 static guint64
gst_avi_demux_seek_streams_index(GstAviDemux * avi,guint64 offset,gboolean before)693 gst_avi_demux_seek_streams_index (GstAviDemux * avi, guint64 offset,
694 gboolean before)
695 {
696 GstAviStream *stream;
697 GstAviIndexEntry *entry;
698 gint i;
699 gint64 val, min = offset;
700 guint index = 0;
701
702 for (i = 0; i < avi->num_streams; i++) {
703 stream = &avi->stream[i];
704
705 /* compensate for chunk header */
706 offset += 8;
707 entry =
708 gst_util_array_binary_search (stream->index, stream->idx_n,
709 sizeof (GstAviIndexEntry),
710 (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
711 before ? GST_SEARCH_MODE_BEFORE : GST_SEARCH_MODE_AFTER, &offset, NULL);
712 offset -= 8;
713
714 if (entry)
715 index = entry - stream->index;
716
717 if (before) {
718 if (entry) {
719 val = stream->index[index].offset;
720 GST_DEBUG_OBJECT (avi,
721 "stream %d, previous entry at %" G_GUINT64_FORMAT, i, val);
722 if (val < min)
723 min = val;
724 }
725 continue;
726 }
727
728 if (!entry) {
729 GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
730 stream->current_entry = 0;
731 stream->current_total = 0;
732 continue;
733 }
734
735 val = stream->index[index].offset - 8;
736 GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT, i,
737 val);
738
739 stream->current_total = stream->index[index].total;
740 stream->current_entry = index;
741 }
742
743 return min;
744 }
745
746 #define GST_AVI_SEEK_PUSH_DISPLACE (4 * GST_SECOND)
747
748 static gboolean
gst_avi_demux_handle_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)749 gst_avi_demux_handle_sink_event (GstPad * pad, GstObject * parent,
750 GstEvent * event)
751 {
752 gboolean res = TRUE;
753 GstAviDemux *avi = GST_AVI_DEMUX (parent);
754
755 GST_DEBUG_OBJECT (avi,
756 "have event type %s: %p on sink pad", GST_EVENT_TYPE_NAME (event), event);
757
758 switch (GST_EVENT_TYPE (event)) {
759 case GST_EVENT_SEGMENT:
760 {
761 gint64 boffset, offset = 0;
762 GstSegment segment;
763 GstEvent *segment_event;
764
765 /* some debug output */
766 gst_event_copy_segment (event, &segment);
767 GST_DEBUG_OBJECT (avi, "received newsegment %" GST_SEGMENT_FORMAT,
768 &segment);
769
770 /* chain will send initial newsegment after pads have been added */
771 if (avi->state != GST_AVI_DEMUX_MOVI) {
772 GST_DEBUG_OBJECT (avi, "still starting, eating event");
773 goto exit;
774 }
775
776 /* we only expect a BYTE segment, e.g. following a seek */
777 if (segment.format != GST_FORMAT_BYTES) {
778 GST_DEBUG_OBJECT (avi, "unsupported segment format, ignoring");
779 goto exit;
780 }
781
782 if (avi->have_index) {
783 GstAviIndexEntry *entry;
784 guint i = 0, index = 0, k = 0;
785 GstAviStream *stream;
786
787 /* compensate chunk header, stored index offset points after header */
788 boffset = segment.start + 8;
789 /* find which stream we're on */
790 do {
791 stream = &avi->stream[i];
792
793 /* find the index for start bytes offset */
794 entry = gst_util_array_binary_search (stream->index,
795 stream->idx_n, sizeof (GstAviIndexEntry),
796 (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
797 GST_SEARCH_MODE_AFTER, &boffset, NULL);
798
799 if (entry == NULL)
800 continue;
801 index = entry - stream->index;
802
803 /* we are on the stream with a chunk start offset closest to start */
804 if (!offset || stream->index[index].offset < offset) {
805 offset = stream->index[index].offset;
806 k = i;
807 }
808 /* exact match needs no further searching */
809 if (stream->index[index].offset == boffset)
810 break;
811 } while (++i < avi->num_streams);
812 boffset -= 8;
813 offset -= 8;
814 stream = &avi->stream[k];
815
816 /* so we have no idea what is to come, or where we are */
817 if (!offset) {
818 GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
819 goto eos;
820 }
821
822 /* get the ts corresponding to start offset bytes for the stream */
823 gst_avi_demux_get_buffer_info (avi, stream, index,
824 (GstClockTime *) & segment.time, NULL, NULL, NULL);
825 #if 0
826 } else if (avi->element_index) {
827 GstIndexEntry *entry;
828
829 /* Let's check if we have an index entry for this position */
830 entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id,
831 GST_INDEX_LOOKUP_AFTER, GST_ASSOCIATION_FLAG_NONE,
832 GST_FORMAT_BYTES, segment.start);
833
834 /* we can not go where we have not yet been before ... */
835 if (!entry) {
836 GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
837 goto eos;
838 }
839
840 gst_index_entry_assoc_map (entry, GST_FORMAT_TIME,
841 (gint64 *) & segment.time);
842 gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &offset);
843 #endif
844 } else {
845 GST_WARNING_OBJECT (avi, "no index data, forcing EOS");
846 goto eos;
847 }
848
849 segment.format = GST_FORMAT_TIME;
850 segment.start = segment.time;
851 segment.stop = GST_CLOCK_TIME_NONE;
852 segment.position = segment.start;
853
854 /* rescue duration */
855 segment.duration = avi->segment.duration;
856
857 /* set up segment and send downstream */
858 gst_segment_copy_into (&segment, &avi->segment);
859
860 GST_DEBUG_OBJECT (avi, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment);
861 avi->segment_seqnum = gst_event_get_seqnum (event);
862 segment_event = gst_event_new_segment (&segment);
863 gst_event_set_seqnum (segment_event, gst_event_get_seqnum (event));
864 gst_avi_demux_push_event (avi, segment_event);
865
866 GST_DEBUG_OBJECT (avi, "next chunk expected at %" G_GINT64_FORMAT,
867 boffset);
868
869 /* adjust state for streaming thread accordingly */
870 if (avi->have_index)
871 gst_avi_demux_seek_streams_index (avi, offset, FALSE);
872 #if 0
873 else
874 gst_avi_demux_seek_streams (avi, offset, FALSE);
875 #endif
876
877 /* set up streaming thread */
878 g_assert (offset >= boffset);
879 avi->offset = boffset;
880 avi->todrop = offset - boffset;
881
882 exit:
883 gst_event_unref (event);
884 res = TRUE;
885 break;
886 eos:
887 /* set up for EOS */
888 avi->have_eos = TRUE;
889 goto exit;
890 }
891 case GST_EVENT_EOS:
892 {
893 if (avi->state != GST_AVI_DEMUX_MOVI) {
894 gst_event_unref (event);
895 GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
896 (NULL), ("got eos and didn't receive a complete header object"));
897 } else if (!gst_avi_demux_push_event (avi, event)) {
898 GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
899 (NULL), ("got eos but no streams (yet)"));
900 }
901 break;
902 }
903 case GST_EVENT_FLUSH_STOP:
904 {
905 gint i;
906
907 gst_adapter_clear (avi->adapter);
908 avi->have_eos = FALSE;
909 for (i = 0; i < avi->num_streams; i++) {
910 avi->stream[i].discont = TRUE;
911 }
912 /* fall through to default case so that the event gets passed downstream */
913 }
914 default:
915 res = gst_pad_event_default (pad, parent, event);
916 break;
917 }
918
919 return res;
920 }
921
922 static gboolean
gst_avi_demux_handle_src_event(GstPad * pad,GstObject * parent,GstEvent * event)923 gst_avi_demux_handle_src_event (GstPad * pad, GstObject * parent,
924 GstEvent * event)
925 {
926 gboolean res = TRUE;
927 GstAviDemux *avi = GST_AVI_DEMUX (parent);
928
929 GST_DEBUG_OBJECT (avi,
930 "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
931
932 switch (GST_EVENT_TYPE (event)) {
933 case GST_EVENT_SEEK:
934 if (!avi->streaming) {
935 res = gst_avi_demux_handle_seek (avi, pad, event);
936 } else {
937 res = gst_avi_demux_handle_seek_push (avi, pad, event);
938 }
939 gst_event_unref (event);
940 break;
941 default:
942 res = gst_pad_event_default (pad, parent, event);
943 break;
944 }
945
946 return res;
947 }
948
949 /* streaming helper (push) */
950
951 /*
952 * gst_avi_demux_peek_chunk_info:
953 * @avi: Avi object
954 * @tag: holder for tag
955 * @size: holder for tag size
956 *
957 * Peek next chunk info (tag and size)
958 *
959 * Returns: TRUE when one chunk info has been got
960 */
961 static gboolean
gst_avi_demux_peek_chunk_info(GstAviDemux * avi,guint32 * tag,guint32 * size)962 gst_avi_demux_peek_chunk_info (GstAviDemux * avi, guint32 * tag, guint32 * size)
963 {
964 const guint8 *data = NULL;
965
966 if (gst_adapter_available (avi->adapter) < 8)
967 return FALSE;
968
969 data = gst_adapter_map (avi->adapter, 8);
970 *tag = GST_READ_UINT32_LE (data);
971 *size = GST_READ_UINT32_LE (data + 4);
972 gst_adapter_unmap (avi->adapter);
973
974 return TRUE;
975 }
976
977 /*
978 * gst_avi_demux_peek_chunk:
979 * @avi: Avi object
980 * @tag: holder for tag
981 * @size: holder for tag size
982 *
983 * Peek enough data for one full chunk
984 *
985 * Returns: %TRUE when one chunk has been got
986 */
987 static gboolean
gst_avi_demux_peek_chunk(GstAviDemux * avi,guint32 * tag,guint32 * size)988 gst_avi_demux_peek_chunk (GstAviDemux * avi, guint32 * tag, guint32 * size)
989 {
990 guint32 peek_size = 0;
991 gint available;
992
993 if (!gst_avi_demux_peek_chunk_info (avi, tag, size))
994 goto peek_failed;
995
996 /* size 0 -> empty data buffer would surprise most callers,
997 * large size -> do not bother trying to squeeze that into adapter,
998 * so we throw poor man's exception, which can be caught if caller really
999 * wants to handle 0 size chunk */
1000 if (!(*size) || (*size) >= (1 << 30))
1001 goto strange_size;
1002
1003 peek_size = (*size + 1) & ~1;
1004 available = gst_adapter_available (avi->adapter);
1005
1006 GST_DEBUG_OBJECT (avi,
1007 "Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT
1008 ", %d bytes available", *size, GST_FOURCC_ARGS (*tag), available);
1009
1010 if (available < (8 + peek_size))
1011 goto need_more;
1012
1013 return TRUE;
1014
1015 /* ERRORS */
1016 peek_failed:
1017 {
1018 GST_INFO_OBJECT (avi, "Failed to peek");
1019 return FALSE;
1020 }
1021 strange_size:
1022 {
1023 GST_INFO_OBJECT (avi,
1024 "Invalid/unexpected chunk size %d for tag %" GST_FOURCC_FORMAT, *size,
1025 GST_FOURCC_ARGS (*tag));
1026 /* chain should give up */
1027 avi->abort_buffering = TRUE;
1028 return FALSE;
1029 }
1030 need_more:
1031 {
1032 GST_INFO_OBJECT (avi, "need more %d < %" G_GUINT32_FORMAT,
1033 available, 8 + peek_size);
1034 return FALSE;
1035 }
1036 }
1037
1038 /* AVI init */
1039
1040 /*
1041 * gst_avi_demux_parse_file_header:
1042 * @element: caller element (used for errors/debug).
1043 * @buf: input data to be used for parsing.
1044 *
1045 * "Open" a RIFF/AVI file. The buffer should be at least 12
1046 * bytes long. Takes ownership of @buf.
1047 *
1048 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
1049 * Throws an error, caller should error out (fatal).
1050 */
1051 static gboolean
gst_avi_demux_parse_file_header(GstElement * element,GstBuffer * buf)1052 gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
1053 {
1054 guint32 doctype;
1055 GstClockTime stamp;
1056
1057 stamp = gst_util_get_timestamp ();
1058
1059 /* riff_parse posts an error */
1060 if (!gst_riff_parse_file_header (element, buf, &doctype))
1061 return FALSE;
1062
1063 if (doctype != GST_RIFF_RIFF_AVI)
1064 goto not_avi;
1065
1066 stamp = gst_util_get_timestamp () - stamp;
1067 GST_DEBUG_OBJECT (element, "header parsing took %" GST_TIME_FORMAT,
1068 GST_TIME_ARGS (stamp));
1069
1070 return TRUE;
1071
1072 /* ERRORS */
1073 not_avi:
1074 {
1075 GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
1076 ("File is not an AVI file: 0x%" G_GINT32_MODIFIER "x", doctype));
1077 return FALSE;
1078 }
1079 }
1080
1081 /*
1082 * Read AVI file tag when streaming
1083 */
1084 static GstFlowReturn
gst_avi_demux_stream_init_push(GstAviDemux * avi)1085 gst_avi_demux_stream_init_push (GstAviDemux * avi)
1086 {
1087 if (gst_adapter_available (avi->adapter) >= 12) {
1088 GstBuffer *tmp;
1089
1090 tmp = gst_adapter_take_buffer (avi->adapter, 12);
1091
1092 GST_DEBUG ("Parsing avi header");
1093 if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), tmp)) {
1094 return GST_FLOW_ERROR;
1095 }
1096 GST_DEBUG ("header ok");
1097 avi->offset += 12;
1098
1099 avi->state = GST_AVI_DEMUX_HEADER;
1100 }
1101 return GST_FLOW_OK;
1102 }
1103
1104 /*
1105 * Read AVI file tag
1106 */
1107 static GstFlowReturn
gst_avi_demux_stream_init_pull(GstAviDemux * avi)1108 gst_avi_demux_stream_init_pull (GstAviDemux * avi)
1109 {
1110 GstFlowReturn res;
1111 GstBuffer *buf = NULL;
1112
1113 res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
1114 if (res != GST_FLOW_OK)
1115 return res;
1116 else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
1117 goto wrong_header;
1118
1119 avi->offset += 12;
1120
1121 return GST_FLOW_OK;
1122
1123 /* ERRORS */
1124 wrong_header:
1125 {
1126 GST_DEBUG_OBJECT (avi, "error parsing file header");
1127 return GST_FLOW_ERROR;
1128 }
1129 }
1130
1131 /* AVI header handling */
1132 /*
1133 * gst_avi_demux_parse_avih:
1134 * @avi: caller element (used for errors/debug).
1135 * @buf: input data to be used for parsing.
1136 * @avih: pointer to structure (filled in by function) containing
1137 * stream information (such as flags, number of streams, etc.).
1138 *
1139 * Read 'avih' header. Discards buffer after use.
1140 *
1141 * Returns: TRUE on success, FALSE otherwise. Throws an error if
1142 * the header is invalid. The caller should error out
1143 * (fatal).
1144 */
1145 static gboolean
gst_avi_demux_parse_avih(GstAviDemux * avi,GstBuffer * buf,gst_riff_avih ** _avih)1146 gst_avi_demux_parse_avih (GstAviDemux * avi,
1147 GstBuffer * buf, gst_riff_avih ** _avih)
1148 {
1149 gst_riff_avih *avih;
1150 gsize size;
1151
1152 if (buf == NULL)
1153 goto no_buffer;
1154
1155 size = gst_buffer_get_size (buf);
1156 if (size < sizeof (gst_riff_avih))
1157 goto avih_too_small;
1158
1159 avih = g_malloc (size);
1160 gst_buffer_extract (buf, 0, avih, size);
1161
1162 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1163 avih->us_frame = GUINT32_FROM_LE (avih->us_frame);
1164 avih->max_bps = GUINT32_FROM_LE (avih->max_bps);
1165 avih->pad_gran = GUINT32_FROM_LE (avih->pad_gran);
1166 avih->flags = GUINT32_FROM_LE (avih->flags);
1167 avih->tot_frames = GUINT32_FROM_LE (avih->tot_frames);
1168 avih->init_frames = GUINT32_FROM_LE (avih->init_frames);
1169 avih->streams = GUINT32_FROM_LE (avih->streams);
1170 avih->bufsize = GUINT32_FROM_LE (avih->bufsize);
1171 avih->width = GUINT32_FROM_LE (avih->width);
1172 avih->height = GUINT32_FROM_LE (avih->height);
1173 avih->scale = GUINT32_FROM_LE (avih->scale);
1174 avih->rate = GUINT32_FROM_LE (avih->rate);
1175 avih->start = GUINT32_FROM_LE (avih->start);
1176 avih->length = GUINT32_FROM_LE (avih->length);
1177 #endif
1178
1179 /* debug stuff */
1180 GST_INFO_OBJECT (avi, "avih tag found:");
1181 GST_INFO_OBJECT (avi, " us_frame %u", avih->us_frame);
1182 GST_INFO_OBJECT (avi, " max_bps %u", avih->max_bps);
1183 GST_INFO_OBJECT (avi, " pad_gran %u", avih->pad_gran);
1184 GST_INFO_OBJECT (avi, " flags 0x%08x", avih->flags);
1185 GST_INFO_OBJECT (avi, " tot_frames %u", avih->tot_frames);
1186 GST_INFO_OBJECT (avi, " init_frames %u", avih->init_frames);
1187 GST_INFO_OBJECT (avi, " streams %u", avih->streams);
1188 GST_INFO_OBJECT (avi, " bufsize %u", avih->bufsize);
1189 GST_INFO_OBJECT (avi, " width %u", avih->width);
1190 GST_INFO_OBJECT (avi, " height %u", avih->height);
1191 GST_INFO_OBJECT (avi, " scale %u", avih->scale);
1192 GST_INFO_OBJECT (avi, " rate %u", avih->rate);
1193 GST_INFO_OBJECT (avi, " start %u", avih->start);
1194 GST_INFO_OBJECT (avi, " length %u", avih->length);
1195
1196 *_avih = avih;
1197 gst_buffer_unref (buf);
1198
1199 if (avih->us_frame != 0 && avih->tot_frames != 0)
1200 avi->duration =
1201 (guint64) avih->us_frame * (guint64) avih->tot_frames * 1000;
1202 else
1203 avi->duration = GST_CLOCK_TIME_NONE;
1204
1205 GST_INFO_OBJECT (avi, " header duration %" GST_TIME_FORMAT,
1206 GST_TIME_ARGS (avi->duration));
1207
1208 return TRUE;
1209
1210 /* ERRORS */
1211 no_buffer:
1212 {
1213 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No buffer"));
1214 return FALSE;
1215 }
1216 avih_too_small:
1217 {
1218 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
1219 ("Too small avih (%" G_GSIZE_FORMAT " available, %d needed)",
1220 size, (int) sizeof (gst_riff_avih)));
1221 gst_buffer_unref (buf);
1222 return FALSE;
1223 }
1224 }
1225
1226 /*
1227 * gst_avi_demux_parse_superindex:
1228 * @avi: caller element (used for debugging/errors).
1229 * @buf: input data to use for parsing.
1230 * @locations: locations in the file (byte-offsets) that contain
1231 * the actual indexes (see get_avi_demux_parse_subindex()).
1232 * The array ends with GST_BUFFER_OFFSET_NONE.
1233 *
1234 * Reads superindex (openDML-2 spec stuff) from the provided data.
1235 *
1236 * Returns: TRUE on success, FALSE otherwise. Indexes should be skipped
1237 * on error, but they are not fatal.
1238 */
1239 static gboolean
gst_avi_demux_parse_superindex(GstAviDemux * avi,GstBuffer * buf,guint64 ** _indexes)1240 gst_avi_demux_parse_superindex (GstAviDemux * avi,
1241 GstBuffer * buf, guint64 ** _indexes)
1242 {
1243 GstMapInfo map;
1244 guint8 *data;
1245 guint16 bpe = 16;
1246 guint32 num, i;
1247 guint64 *indexes;
1248 gsize size;
1249
1250 *_indexes = NULL;
1251
1252 if (buf) {
1253 gst_buffer_map (buf, &map, GST_MAP_READ);
1254 data = map.data;
1255 size = map.size;
1256 } else {
1257 data = NULL;
1258 size = 0;
1259 }
1260
1261 if (size < 24)
1262 goto too_small;
1263
1264 /* check type of index. The opendml2 specs state that
1265 * there should be 4 dwords per array entry. Type can be
1266 * either frame or field (and we don't care). */
1267 if (GST_READ_UINT16_LE (data) != 4 ||
1268 (data[2] & 0xfe) != 0x0 || data[3] != 0x0) {
1269 GST_WARNING_OBJECT (avi,
1270 "Superindex for stream has unexpected "
1271 "size_entry %d (bytes) or flags 0x%02x/0x%02x",
1272 GST_READ_UINT16_LE (data), data[2], data[3]);
1273 bpe = GST_READ_UINT16_LE (data) * 4;
1274 }
1275 num = GST_READ_UINT32_LE (&data[4]);
1276
1277 GST_DEBUG_OBJECT (avi, "got %d indexes", num);
1278
1279 /* this can't work out well ... */
1280 if (num > G_MAXUINT32 >> 1 || bpe < 8) {
1281 goto invalid_params;
1282 }
1283
1284 indexes = g_new (guint64, num + 1);
1285 for (i = 0; i < num; i++) {
1286 if (size < 24 + bpe * (i + 1))
1287 break;
1288 indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
1289 GST_DEBUG_OBJECT (avi, "index %d at %" G_GUINT64_FORMAT, i, indexes[i]);
1290 }
1291 indexes[i] = GST_BUFFER_OFFSET_NONE;
1292 *_indexes = indexes;
1293
1294 gst_buffer_unmap (buf, &map);
1295 gst_buffer_unref (buf);
1296
1297 return TRUE;
1298
1299 /* ERRORS */
1300 too_small:
1301 {
1302 GST_ERROR_OBJECT (avi,
1303 "Not enough data to parse superindex (%" G_GSIZE_FORMAT
1304 " available, 24 needed)", size);
1305 if (buf) {
1306 gst_buffer_unmap (buf, &map);
1307 gst_buffer_unref (buf);
1308 }
1309 return FALSE;
1310 }
1311 invalid_params:
1312 {
1313 GST_ERROR_OBJECT (avi, "invalid index parameters (num = %d, bpe = %d)",
1314 num, bpe);
1315 gst_buffer_unmap (buf, &map);
1316 gst_buffer_unref (buf);
1317 return FALSE;
1318 }
1319 }
1320
1321 /* add an entry to the index of a stream. @num should be an estimate of the
1322 * total amount of index entries for all streams and is used to dynamically
1323 * allocate memory for the index entries. */
1324 static inline gboolean
gst_avi_demux_add_index(GstAviDemux * avi,GstAviStream * stream,guint num,GstAviIndexEntry * entry)1325 gst_avi_demux_add_index (GstAviDemux * avi, GstAviStream * stream,
1326 guint num, GstAviIndexEntry * entry)
1327 {
1328 /* ensure index memory */
1329 if (G_UNLIKELY (stream->idx_n >= stream->idx_max)) {
1330 guint idx_max = stream->idx_max;
1331 GstAviIndexEntry *new_idx;
1332
1333 /* we need to make some more room */
1334 if (idx_max == 0) {
1335 /* initial size guess, assume each stream has an equal amount of entries,
1336 * overshoot with at least 8K */
1337 idx_max = (num / avi->num_streams) + (8192 / sizeof (GstAviIndexEntry));
1338 } else {
1339 idx_max += 8192 / sizeof (GstAviIndexEntry);
1340 GST_DEBUG_OBJECT (avi, "expanded index from %u to %u",
1341 stream->idx_max, idx_max);
1342 }
1343 new_idx = g_try_renew (GstAviIndexEntry, stream->index, idx_max);
1344 /* out of memory, if this fails stream->index is untouched. */
1345 if (G_UNLIKELY (!new_idx))
1346 return FALSE;
1347 /* use new index */
1348 stream->index = new_idx;
1349 stream->idx_max = idx_max;
1350 }
1351
1352 /* update entry total and stream stats. The entry total can be converted to
1353 * the timestamp of the entry easily. */
1354 if (stream->strh->type == GST_RIFF_FCC_auds) {
1355 gint blockalign;
1356
1357 if (stream->is_vbr) {
1358 entry->total = stream->total_blocks;
1359 } else {
1360 entry->total = stream->total_bytes;
1361 }
1362 blockalign = stream->strf.auds->blockalign;
1363 if (blockalign > 0)
1364 stream->total_blocks += DIV_ROUND_UP (entry->size, blockalign);
1365 else
1366 stream->total_blocks++;
1367 } else {
1368 if (stream->is_vbr) {
1369 entry->total = stream->idx_n;
1370 } else {
1371 entry->total = stream->total_bytes;
1372 }
1373 }
1374 stream->total_bytes += entry->size;
1375 if (ENTRY_IS_KEYFRAME (entry))
1376 stream->n_keyframes++;
1377
1378 /* and add */
1379 GST_LOG_OBJECT (avi,
1380 "Adding stream %u, index entry %d, kf %d, size %u "
1381 ", offset %" G_GUINT64_FORMAT ", total %" G_GUINT64_FORMAT, stream->num,
1382 stream->idx_n, ENTRY_IS_KEYFRAME (entry), entry->size, entry->offset,
1383 entry->total);
1384 stream->index[stream->idx_n++] = *entry;
1385
1386 return TRUE;
1387 }
1388
1389 /* given @entry_n in @stream, calculate info such as timestamps and
1390 * offsets for the entry. */
1391 static void
gst_avi_demux_get_buffer_info(GstAviDemux * avi,GstAviStream * stream,guint entry_n,GstClockTime * timestamp,GstClockTime * ts_end,guint64 * offset,guint64 * offset_end)1392 gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
1393 guint entry_n, GstClockTime * timestamp, GstClockTime * ts_end,
1394 guint64 * offset, guint64 * offset_end)
1395 {
1396 GstAviIndexEntry *entry;
1397
1398 entry = &stream->index[entry_n];
1399
1400 if (stream->is_vbr) {
1401 /* VBR stream next timestamp */
1402 if (stream->strh->type == GST_RIFF_FCC_auds) {
1403 if (timestamp)
1404 *timestamp =
1405 avi_stream_convert_frames_to_time_unchecked (stream, entry->total);
1406 if (ts_end) {
1407 gint size = 1;
1408 if (G_LIKELY (entry_n + 1 < stream->idx_n))
1409 size = stream->index[entry_n + 1].total - entry->total;
1410 *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
1411 entry->total + size);
1412 }
1413 } else {
1414 if (timestamp)
1415 *timestamp =
1416 avi_stream_convert_frames_to_time_unchecked (stream, entry_n);
1417 if (ts_end)
1418 *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
1419 entry_n + 1);
1420 }
1421 } else if (stream->strh->type == GST_RIFF_FCC_auds) {
1422 /* constant rate stream */
1423 if (timestamp)
1424 *timestamp =
1425 avi_stream_convert_bytes_to_time_unchecked (stream, entry->total);
1426 if (ts_end)
1427 *ts_end = avi_stream_convert_bytes_to_time_unchecked (stream,
1428 entry->total + entry->size);
1429 }
1430 if (stream->strh->type == GST_RIFF_FCC_vids) {
1431 /* video offsets are the frame number */
1432 if (offset)
1433 *offset = entry_n;
1434 if (offset_end)
1435 *offset_end = entry_n + 1;
1436 } else {
1437 /* no offsets for audio */
1438 if (offset)
1439 *offset = -1;
1440 if (offset_end)
1441 *offset_end = -1;
1442 }
1443 }
1444
1445 /* collect and debug stats about the indexes for all streams.
1446 * This method is also responsible for filling in the stream duration
1447 * as measured by the amount of index entries.
1448 *
1449 * Returns TRUE if the index is not empty, else FALSE */
1450 static gboolean
gst_avi_demux_do_index_stats(GstAviDemux * avi)1451 gst_avi_demux_do_index_stats (GstAviDemux * avi)
1452 {
1453 guint total_idx = 0;
1454 guint i;
1455 #ifndef GST_DISABLE_GST_DEBUG
1456 guint total_max = 0;
1457 #endif
1458
1459 /* get stream stats now */
1460 for (i = 0; i < avi->num_streams; i++) {
1461 GstAviStream *stream;
1462
1463 if (G_UNLIKELY (!(stream = &avi->stream[i])))
1464 continue;
1465 if (G_UNLIKELY (!stream->strh))
1466 continue;
1467 if (G_UNLIKELY (!stream->index || stream->idx_n == 0))
1468 continue;
1469
1470 /* we interested in the end_ts of the last entry, which is the total
1471 * duration of this stream */
1472 gst_avi_demux_get_buffer_info (avi, stream, stream->idx_n - 1,
1473 NULL, &stream->idx_duration, NULL, NULL);
1474
1475 total_idx += stream->idx_n;
1476 #ifndef GST_DISABLE_GST_DEBUG
1477 total_max += stream->idx_max;
1478 #endif
1479 GST_INFO_OBJECT (avi, "Stream %d, dur %" GST_TIME_FORMAT ", %6u entries, "
1480 "%5u keyframes, entry size = %2u, total size = %10u, allocated %10u",
1481 i, GST_TIME_ARGS (stream->idx_duration), stream->idx_n,
1482 stream->n_keyframes, (guint) sizeof (GstAviIndexEntry),
1483 (guint) (stream->idx_n * sizeof (GstAviIndexEntry)),
1484 (guint) (stream->idx_max * sizeof (GstAviIndexEntry)));
1485
1486 /* knowing all that we do, that also includes avg bitrate */
1487 if (!stream->taglist) {
1488 stream->taglist = gst_tag_list_new_empty ();
1489 }
1490 if (stream->total_bytes && stream->idx_duration)
1491 gst_tag_list_add (stream->taglist, GST_TAG_MERGE_REPLACE,
1492 GST_TAG_BITRATE,
1493 (guint) gst_util_uint64_scale (stream->total_bytes * 8,
1494 GST_SECOND, stream->idx_duration), NULL);
1495 }
1496 total_idx *= sizeof (GstAviIndexEntry);
1497 #ifndef GST_DISABLE_GST_DEBUG
1498 total_max *= sizeof (GstAviIndexEntry);
1499 #endif
1500 GST_INFO_OBJECT (avi, "%u bytes for index vs %u ideally, %u wasted",
1501 total_max, total_idx, total_max - total_idx);
1502
1503 if (total_idx == 0) {
1504 GST_WARNING_OBJECT (avi, "Index is empty !");
1505 return FALSE;
1506 }
1507 return TRUE;
1508 }
1509
1510 /*
1511 * gst_avi_demux_parse_subindex:
1512 * @avi: Avi object
1513 * @buf: input data to use for parsing.
1514 * @stream: stream context.
1515 * @entries_list: a list (returned by the function) containing all the
1516 * indexes parsed in this specific subindex. The first
1517 * entry is also a pointer to allocated memory that needs
1518 * to be free´ed. May be NULL if no supported indexes were
1519 * found.
1520 *
1521 * Reads superindex (openDML-2 spec stuff) from the provided data.
1522 * The buffer should contain a GST_RIFF_TAG_ix?? chunk.
1523 *
1524 * Returns: TRUE on success, FALSE otherwise. Errors are fatal, we
1525 * throw an error, caller should bail out asap.
1526 */
1527 static gboolean
gst_avi_demux_parse_subindex(GstAviDemux * avi,GstAviStream * stream,GstBuffer * buf)1528 gst_avi_demux_parse_subindex (GstAviDemux * avi, GstAviStream * stream,
1529 GstBuffer * buf)
1530 {
1531 GstMapInfo map;
1532 guint8 *data;
1533 guint16 bpe;
1534 guint32 num, i;
1535 guint64 baseoff;
1536
1537 if (buf == NULL)
1538 return TRUE;
1539
1540 gst_buffer_map (buf, &map, GST_MAP_READ);
1541 data = map.data;
1542
1543 /* check size */
1544 if (map.size < 24)
1545 goto too_small;
1546
1547 /* We don't support index-data yet */
1548 if (data[3] & 0x80)
1549 goto not_implemented;
1550
1551 /* check type of index. The opendml2 specs state that
1552 * there should be 4 dwords per array entry. Type can be
1553 * either frame or field (and we don't care). */
1554 bpe = (data[2] & 0x01) ? 12 : 8;
1555 if (GST_READ_UINT16_LE (data) != bpe / 4 ||
1556 (data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
1557 GST_WARNING_OBJECT (avi,
1558 "Superindex for stream %d has unexpected "
1559 "size_entry %d (bytes) or flags 0x%02x/0x%02x",
1560 stream->num, GST_READ_UINT16_LE (data), data[2], data[3]);
1561 bpe = GST_READ_UINT16_LE (data) * 4;
1562 }
1563 num = GST_READ_UINT32_LE (&data[4]);
1564 baseoff = GST_READ_UINT64_LE (&data[12]);
1565
1566 /* If there's nothing, just return ! */
1567 if (num == 0)
1568 goto empty_index;
1569
1570 GST_INFO_OBJECT (avi, "Parsing subindex, nr_entries = %6d", num);
1571
1572 for (i = 0; i < num; i++) {
1573 GstAviIndexEntry entry;
1574
1575 if (map.size < 24 + bpe * (i + 1))
1576 break;
1577
1578 /* fill in offset and size. offset contains the keyframe flag in the
1579 * upper bit*/
1580 entry.offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * i]);
1581 entry.size = GST_READ_UINT32_LE (&data[24 + bpe * i + 4]);
1582 /* handle flags */
1583 if (stream->strh->type == GST_RIFF_FCC_auds) {
1584 /* all audio frames are keyframes */
1585 ENTRY_SET_KEYFRAME (&entry);
1586 } else {
1587 /* else read flags */
1588 entry.flags = (entry.size & 0x80000000) ? 0 : GST_AVI_KEYFRAME;
1589 }
1590 entry.size &= ~0x80000000;
1591
1592 /* and add */
1593 if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
1594 goto out_of_mem;
1595 }
1596 done:
1597 gst_buffer_unmap (buf, &map);
1598 gst_buffer_unref (buf);
1599
1600 return TRUE;
1601
1602 /* ERRORS */
1603 too_small:
1604 {
1605 GST_ERROR_OBJECT (avi,
1606 "Not enough data to parse subindex (%" G_GSIZE_FORMAT
1607 " available, 24 needed)", map.size);
1608 goto done; /* continue */
1609 }
1610 not_implemented:
1611 {
1612 GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL),
1613 ("Subindex-is-data is not implemented"));
1614 gst_buffer_unmap (buf, &map);
1615 gst_buffer_unref (buf);
1616 return FALSE;
1617 }
1618 empty_index:
1619 {
1620 GST_DEBUG_OBJECT (avi, "the index is empty");
1621 goto done; /* continue */
1622 }
1623 out_of_mem:
1624 {
1625 GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
1626 ("Cannot allocate memory for %u*%u=%u bytes",
1627 (guint) sizeof (GstAviIndexEntry), num,
1628 (guint) sizeof (GstAviIndexEntry) * num));
1629 gst_buffer_unmap (buf, &map);
1630 gst_buffer_unref (buf);
1631 return FALSE;
1632 }
1633 }
1634
1635 /*
1636 * Create and push a flushing seek event upstream
1637 */
1638 static gboolean
perform_seek_to_offset(GstAviDemux * demux,guint64 offset,guint32 seqnum)1639 perform_seek_to_offset (GstAviDemux * demux, guint64 offset, guint32 seqnum)
1640 {
1641 GstEvent *event;
1642 gboolean res = 0;
1643
1644 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
1645
1646 event =
1647 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1648 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
1649 GST_SEEK_TYPE_NONE, -1);
1650 gst_event_set_seqnum (event, seqnum);
1651 res = gst_pad_push_event (demux->sinkpad, event);
1652
1653 if (res)
1654 demux->offset = offset;
1655 return res;
1656 }
1657
1658 /*
1659 * Read AVI index when streaming
1660 */
1661 static gboolean
gst_avi_demux_read_subindexes_push(GstAviDemux * avi)1662 gst_avi_demux_read_subindexes_push (GstAviDemux * avi)
1663 {
1664 guint32 tag = 0, size;
1665 GstBuffer *buf = NULL;
1666 guint odml_stream;
1667
1668 GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
1669
1670 if (avi->odml_subidxs[avi->odml_subidx] != avi->offset)
1671 return FALSE;
1672
1673 if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
1674 return TRUE;
1675
1676 /* this is the ODML chunk we expect */
1677 odml_stream = avi->odml_stream;
1678
1679 if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + odml_stream / 10,
1680 '0' + odml_stream % 10)) &&
1681 (tag != GST_MAKE_FOURCC ('0' + odml_stream / 10,
1682 '0' + odml_stream % 10, 'i', 'x'))) {
1683 GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
1684 GST_FOURCC_ARGS (tag));
1685 return FALSE;
1686 }
1687
1688 avi->offset += 8 + GST_ROUND_UP_2 (size);
1689 /* flush chunk header so we get just the 'size' payload data */
1690 gst_adapter_flush (avi->adapter, 8);
1691 buf = gst_adapter_take_buffer (avi->adapter, size);
1692
1693 if (!gst_avi_demux_parse_subindex (avi, &avi->stream[odml_stream], buf))
1694 return FALSE;
1695
1696 /* we parsed the index, go to next subindex */
1697 avi->odml_subidx++;
1698
1699 if (avi->odml_subidxs[avi->odml_subidx] == GST_BUFFER_OFFSET_NONE) {
1700 /* we reached the end of the indexes for this stream, move to the next
1701 * stream to handle the first index */
1702 avi->odml_stream++;
1703 avi->odml_subidx = 0;
1704
1705 if (avi->odml_stream < avi->num_streams) {
1706 /* there are more indexes */
1707 avi->odml_subidxs = avi->stream[avi->odml_stream].indexes;
1708 } else {
1709 /* we're done, get stream stats now */
1710 avi->have_index = gst_avi_demux_do_index_stats (avi);
1711
1712 return TRUE;
1713 }
1714 }
1715
1716 /* seek to next index */
1717 return perform_seek_to_offset (avi, avi->odml_subidxs[avi->odml_subidx],
1718 avi->segment_seqnum);
1719 }
1720
1721 /*
1722 * Read AVI index
1723 */
1724 static void
gst_avi_demux_read_subindexes_pull(GstAviDemux * avi)1725 gst_avi_demux_read_subindexes_pull (GstAviDemux * avi)
1726 {
1727 guint32 tag;
1728 GstBuffer *buf;
1729 gint i, n;
1730
1731 GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
1732
1733 for (n = 0; n < avi->num_streams; n++) {
1734 GstAviStream *stream = &avi->stream[n];
1735
1736 if (stream->indexes == NULL)
1737 continue;
1738
1739 for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
1740 if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi), avi->sinkpad,
1741 &stream->indexes[i], &tag, &buf) != GST_FLOW_OK)
1742 continue;
1743 else if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
1744 '0' + stream->num % 10)) &&
1745 (tag != GST_MAKE_FOURCC ('0' + stream->num / 10,
1746 '0' + stream->num % 10, 'i', 'x'))) {
1747 /* Some ODML files (created by god knows what muxer) have a ##ix format
1748 * instead of the 'official' ix##. They are still valid though. */
1749 GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
1750 GST_FOURCC_ARGS (tag));
1751 gst_buffer_unref (buf);
1752 continue;
1753 }
1754
1755 if (!gst_avi_demux_parse_subindex (avi, stream, buf))
1756 continue;
1757 }
1758
1759 g_free (stream->indexes);
1760 stream->indexes = NULL;
1761 }
1762 /* get stream stats now */
1763 avi->have_index = gst_avi_demux_do_index_stats (avi);
1764 }
1765
1766 /*
1767 * gst_avi_demux_riff_parse_vprp:
1768 * @element: caller element (used for debugging/error).
1769 * @buf: input data to be used for parsing, stripped from header.
1770 * @vprp: a pointer (returned by this function) to a filled-in vprp
1771 * structure. Caller should free it.
1772 *
1773 * Parses a video stream´s vprp. This function takes ownership of @buf.
1774 *
1775 * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
1776 * should be skipped on error, but it is not fatal.
1777 */
1778 static gboolean
gst_avi_demux_riff_parse_vprp(GstElement * element,GstBuffer * buf,gst_riff_vprp ** _vprp)1779 gst_avi_demux_riff_parse_vprp (GstElement * element,
1780 GstBuffer * buf, gst_riff_vprp ** _vprp)
1781 {
1782 gst_riff_vprp *vprp;
1783 gint k;
1784 gsize size;
1785
1786 g_return_val_if_fail (buf != NULL, FALSE);
1787 g_return_val_if_fail (_vprp != NULL, FALSE);
1788
1789 size = gst_buffer_get_size (buf);
1790
1791 if (size < G_STRUCT_OFFSET (gst_riff_vprp, field_info))
1792 goto too_small;
1793
1794 vprp = g_malloc (size);
1795 gst_buffer_extract (buf, 0, vprp, size);
1796
1797 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1798 vprp->format_token = GUINT32_FROM_LE (vprp->format_token);
1799 vprp->standard = GUINT32_FROM_LE (vprp->standard);
1800 vprp->vert_rate = GUINT32_FROM_LE (vprp->vert_rate);
1801 vprp->hor_t_total = GUINT32_FROM_LE (vprp->hor_t_total);
1802 vprp->vert_lines = GUINT32_FROM_LE (vprp->vert_lines);
1803 vprp->aspect = GUINT32_FROM_LE (vprp->aspect);
1804 vprp->width = GUINT32_FROM_LE (vprp->width);
1805 vprp->height = GUINT32_FROM_LE (vprp->height);
1806 vprp->fields = GUINT32_FROM_LE (vprp->fields);
1807 #endif
1808
1809 /* size checking */
1810 /* calculate fields based on size */
1811 k = (size - G_STRUCT_OFFSET (gst_riff_vprp, field_info)) / vprp->fields;
1812 if (vprp->fields > k) {
1813 GST_WARNING_OBJECT (element,
1814 "vprp header indicated %d fields, only %d available", vprp->fields, k);
1815 vprp->fields = k;
1816 }
1817 if (vprp->fields > GST_RIFF_VPRP_VIDEO_FIELDS) {
1818 GST_WARNING_OBJECT (element,
1819 "vprp header indicated %d fields, at most %d supported", vprp->fields,
1820 GST_RIFF_VPRP_VIDEO_FIELDS);
1821 vprp->fields = GST_RIFF_VPRP_VIDEO_FIELDS;
1822 }
1823 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1824 for (k = 0; k < vprp->fields; k++) {
1825 gst_riff_vprp_video_field_desc *fd;
1826
1827 fd = &vprp->field_info[k];
1828 fd->compressed_bm_height = GUINT32_FROM_LE (fd->compressed_bm_height);
1829 fd->compressed_bm_width = GUINT32_FROM_LE (fd->compressed_bm_width);
1830 fd->valid_bm_height = GUINT32_FROM_LE (fd->valid_bm_height);
1831 fd->valid_bm_width = GUINT16_FROM_LE (fd->valid_bm_width);
1832 fd->valid_bm_x_offset = GUINT16_FROM_LE (fd->valid_bm_x_offset);
1833 fd->valid_bm_y_offset = GUINT32_FROM_LE (fd->valid_bm_y_offset);
1834 fd->video_x_t_offset = GUINT32_FROM_LE (fd->video_x_t_offset);
1835 fd->video_y_start = GUINT32_FROM_LE (fd->video_y_start);
1836 }
1837 #endif
1838
1839 /* debug */
1840 GST_INFO_OBJECT (element, "vprp tag found in context vids:");
1841 GST_INFO_OBJECT (element, " format_token %d", vprp->format_token);
1842 GST_INFO_OBJECT (element, " standard %d", vprp->standard);
1843 GST_INFO_OBJECT (element, " vert_rate %d", vprp->vert_rate);
1844 GST_INFO_OBJECT (element, " hor_t_total %d", vprp->hor_t_total);
1845 GST_INFO_OBJECT (element, " vert_lines %d", vprp->vert_lines);
1846 GST_INFO_OBJECT (element, " aspect %d:%d", vprp->aspect >> 16,
1847 vprp->aspect & 0xffff);
1848 GST_INFO_OBJECT (element, " width %d", vprp->width);
1849 GST_INFO_OBJECT (element, " height %d", vprp->height);
1850 GST_INFO_OBJECT (element, " fields %d", vprp->fields);
1851 for (k = 0; k < vprp->fields; k++) {
1852 gst_riff_vprp_video_field_desc *fd;
1853
1854 fd = &(vprp->field_info[k]);
1855 GST_INFO_OBJECT (element, " field %u description:", k);
1856 GST_INFO_OBJECT (element, " compressed_bm_height %d",
1857 fd->compressed_bm_height);
1858 GST_INFO_OBJECT (element, " compressed_bm_width %d",
1859 fd->compressed_bm_width);
1860 GST_INFO_OBJECT (element, " valid_bm_height %d",
1861 fd->valid_bm_height);
1862 GST_INFO_OBJECT (element, " valid_bm_width %d", fd->valid_bm_width);
1863 GST_INFO_OBJECT (element, " valid_bm_x_offset %d",
1864 fd->valid_bm_x_offset);
1865 GST_INFO_OBJECT (element, " valid_bm_y_offset %d",
1866 fd->valid_bm_y_offset);
1867 GST_INFO_OBJECT (element, " video_x_t_offset %d",
1868 fd->video_x_t_offset);
1869 GST_INFO_OBJECT (element, " video_y_start %d", fd->video_y_start);
1870 }
1871
1872 gst_buffer_unref (buf);
1873
1874 *_vprp = vprp;
1875
1876 return TRUE;
1877
1878 /* ERRORS */
1879 too_small:
1880 {
1881 GST_ERROR_OBJECT (element,
1882 "Too small vprp (%" G_GSIZE_FORMAT " available, at least %d needed)",
1883 size, (int) G_STRUCT_OFFSET (gst_riff_vprp, field_info));
1884 gst_buffer_unref (buf);
1885 return FALSE;
1886 }
1887 }
1888
1889 static void
gst_avi_demux_expose_streams(GstAviDemux * avi,gboolean force)1890 gst_avi_demux_expose_streams (GstAviDemux * avi, gboolean force)
1891 {
1892 guint i;
1893
1894 GST_DEBUG_OBJECT (avi, "force : %d", force);
1895
1896 for (i = 0; i < avi->num_streams; i++) {
1897 GstAviStream *stream = &avi->stream[i];
1898
1899 if (force || stream->idx_n != 0) {
1900 GST_LOG_OBJECT (avi, "Adding pad %s", GST_PAD_NAME (stream->pad));
1901 gst_element_add_pad ((GstElement *) avi, stream->pad);
1902 gst_flow_combiner_add_pad (avi->flowcombiner, stream->pad);
1903
1904 #if 0
1905 if (avi->element_index)
1906 gst_index_get_writer_id (avi->element_index,
1907 GST_OBJECT_CAST (stream->pad), &stream->index_id);
1908 #endif
1909
1910 stream->exposed = TRUE;
1911 if (avi->main_stream == -1)
1912 avi->main_stream = i;
1913 } else {
1914 GST_WARNING_OBJECT (avi, "Stream #%d doesn't have any entry, removing it",
1915 i);
1916 gst_avi_demux_reset_stream (avi, stream);
1917 }
1918 }
1919 }
1920
1921 /* buf contains LIST chunk data, and will be padded to even size,
1922 * since some buggy files do not account for the padding of chunks
1923 * within a LIST in the size of the LIST */
1924 static inline void
gst_avi_demux_roundup_list(GstAviDemux * avi,GstBuffer ** buf)1925 gst_avi_demux_roundup_list (GstAviDemux * avi, GstBuffer ** buf)
1926 {
1927 gsize size;
1928
1929 size = gst_buffer_get_size (*buf);
1930
1931 if (G_UNLIKELY (size & 1)) {
1932 GstBuffer *obuf;
1933 GstMapInfo map;
1934
1935 GST_DEBUG_OBJECT (avi, "rounding up dubious list size %" G_GSIZE_FORMAT,
1936 size);
1937 obuf = gst_buffer_new_and_alloc (size + 1);
1938
1939 gst_buffer_map (obuf, &map, GST_MAP_WRITE);
1940 gst_buffer_extract (*buf, 0, map.data, size);
1941 /* assume 0 padding, at least makes outcome deterministic */
1942 map.data[size] = 0;
1943 gst_buffer_unmap (obuf, &map);
1944 gst_buffer_replace (buf, obuf);
1945 }
1946 }
1947
1948 static GstCaps *
gst_avi_demux_check_caps(GstAviDemux * avi,GstAviStream * stream,GstCaps * caps)1949 gst_avi_demux_check_caps (GstAviDemux * avi, GstAviStream * stream,
1950 GstCaps * caps)
1951 {
1952 GstStructure *s;
1953 const GValue *val;
1954 GstBuffer *buf;
1955
1956 caps = gst_caps_make_writable (caps);
1957
1958 s = gst_caps_get_structure (caps, 0);
1959 if (gst_structure_has_name (s, "video/x-raw")) {
1960 stream->is_raw = TRUE;
1961 stream->alignment = 32;
1962 if (!gst_structure_has_field (s, "pixel-aspect-ratio"))
1963 gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1964 1, 1, NULL);
1965 if (gst_structure_has_field_typed (s, "palette_data", GST_TYPE_BUFFER)) {
1966 gst_structure_get (s, "palette_data", GST_TYPE_BUFFER,
1967 &stream->rgb8_palette, NULL);
1968 gst_structure_remove_field (s, "palette_data");
1969 return caps;
1970 }
1971 } else if (gst_structure_has_name (s, "video/x-h264")) {
1972 GST_DEBUG_OBJECT (avi, "checking caps %" GST_PTR_FORMAT, caps);
1973
1974 /* some muxers put invalid bytestream stuff in h264 extra data */
1975 val = gst_structure_get_value (s, "codec_data");
1976 if (val && (buf = gst_value_get_buffer (val))) {
1977 guint8 *data;
1978 gint size;
1979 GstMapInfo map;
1980
1981 gst_buffer_map (buf, &map, GST_MAP_READ);
1982 data = map.data;
1983 size = map.size;
1984 if (size >= 4) {
1985 guint32 h = GST_READ_UINT32_BE (data);
1986
1987 gst_buffer_unmap (buf, &map);
1988 if (h == 0x01 || (h >> 8) == 0x01) {
1989 /* can hardly be valid AVC codec data */
1990 GST_DEBUG_OBJECT (avi,
1991 "discarding invalid codec_data containing byte-stream");
1992 /* so do not pretend to downstream that it is packetized avc */
1993 gst_structure_remove_field (s, "codec_data");
1994 /* ... but rather properly parsed bytestream */
1995 gst_structure_set (s, "stream-format", G_TYPE_STRING, "byte-stream",
1996 "alignment", G_TYPE_STRING, "au", NULL);
1997 }
1998 } else {
1999 gst_buffer_unmap (buf, &map);
2000 }
2001 }
2002 }
2003
2004 return caps;
2005 }
2006
2007 /*
2008 * gst_avi_demux_parse_stream:
2009 * @avi: calling element (used for debugging/errors).
2010 * @buf: input buffer used to parse the stream.
2011 *
2012 * Parses all subchunks in a strl chunk (which defines a single
2013 * stream). Discards the buffer after use. This function will
2014 * increment the stream counter internally.
2015 *
2016 * Returns: whether the stream was identified successfully.
2017 * Errors are not fatal. It does indicate the stream
2018 * was skipped.
2019 */
2020 static gboolean
gst_avi_demux_parse_stream(GstAviDemux * avi,GstBuffer * buf)2021 gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
2022 {
2023 GstAviStream *stream;
2024 GstElementClass *klass;
2025 GstPadTemplate *templ;
2026 GstBuffer *sub = NULL;
2027 guint offset = 4;
2028 guint32 tag = 0;
2029 gchar *codec_name = NULL, *padname = NULL;
2030 const gchar *tag_name;
2031 GstCaps *caps = NULL;
2032 GstPad *pad;
2033 GstElement *element;
2034 gboolean got_strh = FALSE, got_strf = FALSE, got_vprp = FALSE;
2035 gst_riff_vprp *vprp = NULL;
2036 GstEvent *event;
2037 gchar *stream_id;
2038 GstMapInfo map;
2039 gboolean sparse = FALSE;
2040
2041 element = GST_ELEMENT_CAST (avi);
2042
2043 GST_DEBUG_OBJECT (avi, "Parsing stream");
2044
2045 gst_avi_demux_roundup_list (avi, &buf);
2046
2047 if (avi->num_streams >= GST_AVI_DEMUX_MAX_STREAMS) {
2048 GST_WARNING_OBJECT (avi,
2049 "maximum no of streams (%d) exceeded, ignoring stream",
2050 GST_AVI_DEMUX_MAX_STREAMS);
2051 gst_buffer_unref (buf);
2052 /* not a fatal error, let's say */
2053 return TRUE;
2054 }
2055
2056 stream = &avi->stream[avi->num_streams];
2057
2058 /* initial settings */
2059 stream->idx_duration = GST_CLOCK_TIME_NONE;
2060 stream->hdr_duration = GST_CLOCK_TIME_NONE;
2061 stream->duration = GST_CLOCK_TIME_NONE;
2062
2063 while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
2064 /* sub can be NULL if the chunk is empty */
2065 if (sub == NULL) {
2066 GST_DEBUG_OBJECT (avi, "ignoring empty chunk %" GST_FOURCC_FORMAT,
2067 GST_FOURCC_ARGS (tag));
2068 continue;
2069 }
2070 switch (tag) {
2071 case GST_RIFF_TAG_strh:
2072 {
2073 gst_riff_strh *strh;
2074
2075 if (got_strh) {
2076 GST_WARNING_OBJECT (avi, "Ignoring additional strh chunk");
2077 break;
2078 }
2079 if (!gst_riff_parse_strh (element, sub, &stream->strh)) {
2080 /* ownership given away */
2081 sub = NULL;
2082 GST_WARNING_OBJECT (avi, "Failed to parse strh chunk");
2083 goto fail;
2084 }
2085 sub = NULL;
2086 strh = stream->strh;
2087 /* sanity check; stream header frame rate matches global header
2088 * frame duration */
2089 if (stream->strh->type == GST_RIFF_FCC_vids) {
2090 GstClockTime s_dur;
2091 GstClockTime h_dur = avi->avih->us_frame * GST_USECOND;
2092
2093 s_dur = gst_util_uint64_scale (GST_SECOND, strh->scale, strh->rate);
2094 GST_DEBUG_OBJECT (avi, "verifying stream framerate %d/%d, "
2095 "frame duration = %d ms", strh->rate, strh->scale,
2096 (gint) (s_dur / GST_MSECOND));
2097 if (h_dur > (10 * GST_MSECOND) && (s_dur > 10 * h_dur)) {
2098 strh->rate = GST_SECOND / GST_USECOND;
2099 strh->scale = h_dur / GST_USECOND;
2100 GST_DEBUG_OBJECT (avi, "correcting stream framerate to %d/%d",
2101 strh->rate, strh->scale);
2102 }
2103 }
2104 /* determine duration as indicated by header */
2105 stream->hdr_duration = gst_util_uint64_scale ((guint64) strh->length *
2106 strh->scale, GST_SECOND, (guint64) strh->rate);
2107 GST_INFO ("Stream duration according to header: %" GST_TIME_FORMAT,
2108 GST_TIME_ARGS (stream->hdr_duration));
2109 if (stream->hdr_duration == 0)
2110 stream->hdr_duration = GST_CLOCK_TIME_NONE;
2111
2112 got_strh = TRUE;
2113 break;
2114 }
2115 case GST_RIFF_TAG_strf:
2116 {
2117 gboolean res = FALSE;
2118
2119 if (got_strf) {
2120 GST_WARNING_OBJECT (avi, "Ignoring additional strf chunk");
2121 break;
2122 }
2123 if (!got_strh) {
2124 GST_ERROR_OBJECT (avi, "Found strf chunk before strh chunk");
2125 goto fail;
2126 }
2127 switch (stream->strh->type) {
2128 case GST_RIFF_FCC_vids:
2129 stream->is_vbr = TRUE;
2130 res = gst_riff_parse_strf_vids (element, sub,
2131 &stream->strf.vids, &stream->extradata);
2132 sub = NULL;
2133 GST_DEBUG_OBJECT (element, "marking video as VBR, res %d", res);
2134 break;
2135 case GST_RIFF_FCC_auds:
2136 res =
2137 gst_riff_parse_strf_auds (element, sub, &stream->strf.auds,
2138 &stream->extradata);
2139 sub = NULL;
2140 if (!res)
2141 break;
2142 stream->is_vbr = (stream->strh->samplesize == 0)
2143 && stream->strh->scale > 1
2144 && stream->strf.auds->blockalign != 1;
2145 GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d",
2146 stream->is_vbr, res);
2147 /* we need these or we have no way to come up with timestamps */
2148 if ((!stream->is_vbr && !stream->strf.auds->av_bps) ||
2149 (stream->is_vbr && (!stream->strh->scale ||
2150 !stream->strh->rate))) {
2151 GST_WARNING_OBJECT (element,
2152 "invalid audio header, ignoring stream");
2153 goto fail;
2154 }
2155 /* some more sanity checks */
2156 if (stream->is_vbr) {
2157 if (stream->strf.auds->blockalign <= 4) {
2158 /* that would mean (too) many frames per chunk,
2159 * so not likely set as expected */
2160 GST_DEBUG_OBJECT (element,
2161 "suspicious blockalign %d for VBR audio; "
2162 "overriding to 1 frame per chunk",
2163 stream->strf.auds->blockalign);
2164 /* this should top any likely value */
2165 stream->strf.auds->blockalign = (1 << 12);
2166 }
2167 }
2168 break;
2169 case GST_RIFF_FCC_iavs:
2170 stream->is_vbr = TRUE;
2171 res = gst_riff_parse_strf_iavs (element, sub,
2172 &stream->strf.iavs, &stream->extradata);
2173 sub = NULL;
2174 GST_DEBUG_OBJECT (element, "marking iavs as VBR, res %d", res);
2175 break;
2176 case GST_RIFF_FCC_txts:
2177 /* nothing to parse here */
2178 stream->is_vbr = (stream->strh->samplesize == 0)
2179 && (stream->strh->scale > 1);
2180 res = TRUE;
2181 break;
2182 default:
2183 GST_ERROR_OBJECT (avi,
2184 "Don´t know how to handle stream type %" GST_FOURCC_FORMAT,
2185 GST_FOURCC_ARGS (stream->strh->type));
2186 break;
2187 }
2188 if (sub) {
2189 gst_buffer_unref (sub);
2190 sub = NULL;
2191 }
2192 if (!res)
2193 goto fail;
2194 got_strf = TRUE;
2195 break;
2196 }
2197 case GST_RIFF_TAG_vprp:
2198 {
2199 if (got_vprp) {
2200 GST_WARNING_OBJECT (avi, "Ignoring additional vprp chunk");
2201 break;
2202 }
2203 if (!got_strh) {
2204 GST_ERROR_OBJECT (avi, "Found vprp chunk before strh chunk");
2205 goto fail;
2206 }
2207 if (!got_strf) {
2208 GST_ERROR_OBJECT (avi, "Found vprp chunk before strf chunk");
2209 goto fail;
2210 }
2211
2212 if (!gst_avi_demux_riff_parse_vprp (element, sub, &vprp)) {
2213 GST_WARNING_OBJECT (avi, "Failed to parse vprp chunk");
2214 /* not considered fatal */
2215 g_free (vprp);
2216 vprp = NULL;
2217 } else
2218 got_vprp = TRUE;
2219 sub = NULL;
2220 break;
2221 }
2222 case GST_RIFF_TAG_strd:
2223 if (stream->initdata)
2224 gst_buffer_unref (stream->initdata);
2225 stream->initdata = sub;
2226 if (sub != NULL) {
2227 gst_avi_demux_parse_strd (avi, sub);
2228 sub = NULL;
2229 }
2230 break;
2231 case GST_RIFF_TAG_strn:
2232 {
2233 gchar *stream_name = NULL;
2234
2235 gst_buffer_map (sub, &map, GST_MAP_READ);
2236
2237 if (avi->globaltags == NULL)
2238 avi->globaltags = gst_tag_list_new_empty ();
2239 parse_tag_value (avi, avi->globaltags, GST_TAG_TITLE,
2240 map.data, map.size);
2241
2242 if (gst_tag_list_get_string (avi->globaltags, GST_TAG_TITLE,
2243 &stream_name)) {
2244 GST_DEBUG_OBJECT (avi, "stream name: %s", stream_name);
2245 g_free (stream->name);
2246 stream->name = stream_name;
2247 }
2248
2249 gst_buffer_unmap (sub, &map);
2250 gst_buffer_unref (sub);
2251 sub = NULL;
2252 }
2253 break;
2254 case GST_RIFF_IDIT:
2255 gst_avi_demux_parse_idit (avi, sub);
2256 break;
2257 default:
2258 if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
2259 tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
2260 '0' + avi->num_streams % 10)) {
2261 g_free (stream->indexes);
2262 gst_avi_demux_parse_superindex (avi, sub, &stream->indexes);
2263 stream->superindex = TRUE;
2264 sub = NULL;
2265 break;
2266 }
2267 GST_WARNING_OBJECT (avi,
2268 "Unknown stream header tag %" GST_FOURCC_FORMAT ", ignoring",
2269 GST_FOURCC_ARGS (tag));
2270 /* Only get buffer for debugging if the memdump is needed */
2271 if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= 9) {
2272 GstMapInfo map;
2273
2274 gst_buffer_map (sub, &map, GST_MAP_READ);
2275 GST_MEMDUMP_OBJECT (avi, "Unknown stream header tag", map.data,
2276 map.size);
2277 gst_buffer_unmap (sub, &map);
2278 }
2279 /* fall-through */
2280 case GST_RIFF_TAG_JUNQ:
2281 case GST_RIFF_TAG_JUNK:
2282 break;
2283 }
2284 if (sub != NULL) {
2285 gst_buffer_unref (sub);
2286 sub = NULL;
2287 }
2288 }
2289
2290 if (!got_strh) {
2291 GST_WARNING_OBJECT (avi, "Failed to find strh chunk");
2292 goto fail;
2293 }
2294
2295 if (!got_strf) {
2296 GST_WARNING_OBJECT (avi, "Failed to find strf chunk");
2297 goto fail;
2298 }
2299
2300 /* get class to figure out the template */
2301 klass = GST_ELEMENT_GET_CLASS (avi);
2302
2303 /* we now have all info, let´s set up a pad and a caps and be done */
2304 /* create stream name + pad */
2305 switch (stream->strh->type) {
2306 case GST_RIFF_FCC_vids:{
2307 guint32 fourcc;
2308
2309 fourcc = (stream->strf.vids->compression) ?
2310 stream->strf.vids->compression : stream->strh->fcc_handler;
2311 caps = gst_riff_create_video_caps (fourcc, stream->strh,
2312 stream->strf.vids, stream->extradata, stream->initdata, &codec_name);
2313
2314 /* DXSB is XSUB, and it is placed inside a vids */
2315 if (!caps || (fourcc != GST_MAKE_FOURCC ('D', 'X', 'S', 'B') &&
2316 fourcc != GST_MAKE_FOURCC ('D', 'X', 'S', 'A'))) {
2317 padname = g_strdup_printf ("video_%u", avi->num_v_streams);
2318 templ = gst_element_class_get_pad_template (klass, "video_%u");
2319 if (!caps) {
2320 caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
2321 G_TYPE_INT, fourcc, NULL);
2322 } else if (got_vprp && vprp) {
2323 guint32 aspect_n, aspect_d;
2324 gint n, d;
2325
2326 aspect_n = vprp->aspect >> 16;
2327 aspect_d = vprp->aspect & 0xffff;
2328 /* calculate the pixel aspect ratio using w/h and aspect ratio */
2329 n = aspect_n * stream->strf.vids->height;
2330 d = aspect_d * stream->strf.vids->width;
2331 if (n && d)
2332 gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2333 n, d, NULL);
2334 }
2335 caps = gst_avi_demux_check_caps (avi, stream, caps);
2336 tag_name = GST_TAG_VIDEO_CODEC;
2337 avi->num_v_streams++;
2338 } else {
2339 padname = g_strdup_printf ("subpicture_%u", avi->num_sp_streams);
2340 templ = gst_element_class_get_pad_template (klass, "subpicture_%u");
2341 tag_name = NULL;
2342 avi->num_sp_streams++;
2343 sparse = TRUE;
2344 }
2345 break;
2346 }
2347 case GST_RIFF_FCC_auds:{
2348 /* FIXME: Do something with the channel reorder map */
2349 padname = g_strdup_printf ("audio_%u", avi->num_a_streams);
2350 templ = gst_element_class_get_pad_template (klass, "audio_%u");
2351 caps = gst_riff_create_audio_caps (stream->strf.auds->format,
2352 stream->strh, stream->strf.auds, stream->extradata,
2353 stream->initdata, &codec_name, NULL);
2354 if (!caps) {
2355 caps = gst_caps_new_simple ("audio/x-avi-unknown", "codec_id",
2356 G_TYPE_INT, stream->strf.auds->format, NULL);
2357 }
2358 tag_name = GST_TAG_AUDIO_CODEC;
2359 avi->num_a_streams++;
2360 break;
2361 }
2362 case GST_RIFF_FCC_iavs:{
2363 guint32 fourcc = stream->strh->fcc_handler;
2364
2365 padname = g_strdup_printf ("video_%u", avi->num_v_streams);
2366 templ = gst_element_class_get_pad_template (klass, "video_%u");
2367 caps = gst_riff_create_iavs_caps (fourcc, stream->strh,
2368 stream->strf.iavs, stream->extradata, stream->initdata, &codec_name);
2369 if (!caps) {
2370 caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
2371 G_TYPE_INT, fourcc, NULL);
2372 }
2373 tag_name = GST_TAG_VIDEO_CODEC;
2374 avi->num_v_streams++;
2375 break;
2376 }
2377 case GST_RIFF_FCC_txts:{
2378 padname = g_strdup_printf ("subtitle_%u", avi->num_t_streams);
2379 templ = gst_element_class_get_pad_template (klass, "subtitle_%u");
2380 caps = gst_caps_new_empty_simple ("application/x-subtitle-avi");
2381 tag_name = NULL;
2382 avi->num_t_streams++;
2383 sparse = TRUE;
2384 break;
2385 }
2386 default:
2387 g_return_val_if_reached (FALSE);
2388 }
2389
2390 /* no caps means no stream */
2391 if (!caps) {
2392 GST_ERROR_OBJECT (element, "Did not find caps for stream %s", padname);
2393 goto fail;
2394 }
2395
2396 GST_DEBUG_OBJECT (element, "codec-name=%s", codec_name ? codec_name : "NULL");
2397 GST_DEBUG_OBJECT (element, "caps=%" GST_PTR_FORMAT, caps);
2398
2399 /* set proper settings and add it */
2400 if (stream->pad)
2401 gst_object_unref (stream->pad);
2402 pad = stream->pad = gst_pad_new_from_template (templ, padname);
2403 g_free (padname);
2404
2405 gst_pad_use_fixed_caps (pad);
2406 #if 0
2407 gst_pad_set_formats_function (pad,
2408 GST_DEBUG_FUNCPTR (gst_avi_demux_get_src_formats));
2409 gst_pad_set_event_mask_function (pad,
2410 GST_DEBUG_FUNCPTR (gst_avi_demux_get_event_mask));
2411 #endif
2412 gst_pad_set_event_function (pad,
2413 GST_DEBUG_FUNCPTR (gst_avi_demux_handle_src_event));
2414 gst_pad_set_query_function (pad,
2415 GST_DEBUG_FUNCPTR (gst_avi_demux_handle_src_query));
2416 #if 0
2417 gst_pad_set_convert_function (pad,
2418 GST_DEBUG_FUNCPTR (gst_avi_demux_src_convert));
2419 #endif
2420
2421 stream->num = avi->num_streams;
2422
2423 stream->start_entry = 0;
2424 stream->step_entry = 0;
2425 stream->stop_entry = 0;
2426
2427 stream->current_entry = -1;
2428 stream->current_total = 0;
2429
2430 stream->discont = TRUE;
2431
2432 stream->total_bytes = 0;
2433 stream->total_blocks = 0;
2434 stream->n_keyframes = 0;
2435
2436 stream->idx_n = 0;
2437 stream->idx_max = 0;
2438
2439 gst_pad_set_element_private (pad, stream);
2440 avi->num_streams++;
2441
2442 gst_pad_set_active (pad, TRUE);
2443 stream_id =
2444 gst_pad_create_stream_id_printf (pad, GST_ELEMENT_CAST (avi), "%03u",
2445 avi->num_streams);
2446
2447 event = gst_pad_get_sticky_event (avi->sinkpad, GST_EVENT_STREAM_START, 0);
2448 if (event) {
2449 if (gst_event_parse_group_id (event, &avi->group_id))
2450 avi->have_group_id = TRUE;
2451 else
2452 avi->have_group_id = FALSE;
2453 gst_event_unref (event);
2454 } else if (!avi->have_group_id) {
2455 avi->have_group_id = TRUE;
2456 avi->group_id = gst_util_group_id_next ();
2457 }
2458
2459 event = gst_event_new_stream_start (stream_id);
2460 if (avi->have_group_id)
2461 gst_event_set_group_id (event, avi->group_id);
2462 if (sparse)
2463 gst_event_set_stream_flags (event, GST_STREAM_FLAG_SPARSE);
2464
2465 gst_pad_push_event (pad, event);
2466 g_free (stream_id);
2467 gst_pad_set_caps (pad, caps);
2468 gst_caps_unref (caps);
2469
2470 /* make tags */
2471 if (codec_name && tag_name) {
2472 if (!stream->taglist)
2473 stream->taglist = gst_tag_list_new_empty ();
2474
2475 avi->got_tags = TRUE;
2476
2477 gst_tag_list_add (stream->taglist, GST_TAG_MERGE_APPEND, tag_name,
2478 codec_name, NULL);
2479 }
2480
2481 g_free (vprp);
2482 g_free (codec_name);
2483 gst_buffer_unref (buf);
2484
2485 return TRUE;
2486
2487 /* ERRORS */
2488 fail:
2489 {
2490 /* unref any mem that may be in use */
2491 if (buf)
2492 gst_buffer_unref (buf);
2493 if (sub)
2494 gst_buffer_unref (sub);
2495 g_free (vprp);
2496 g_free (codec_name);
2497 gst_avi_demux_reset_stream (avi, stream);
2498 avi->num_streams++;
2499 return FALSE;
2500 }
2501 }
2502
2503 /*
2504 * gst_avi_demux_parse_odml:
2505 * @avi: calling element (used for debug/error).
2506 * @buf: input buffer to be used for parsing.
2507 *
2508 * Read an openDML-2.0 extension header. Fills in the frame number
2509 * in the avi demuxer object when reading succeeds.
2510 */
2511 static void
gst_avi_demux_parse_odml(GstAviDemux * avi,GstBuffer * buf)2512 gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf)
2513 {
2514 guint32 tag = 0;
2515 guint offset = 4;
2516 GstBuffer *sub = NULL;
2517
2518 while (gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
2519 &sub)) {
2520 switch (tag) {
2521 case GST_RIFF_TAG_dmlh:{
2522 gst_riff_dmlh dmlh, *_dmlh;
2523 GstMapInfo map;
2524
2525 /* sub == NULL is possible and means an empty buffer */
2526 if (sub == NULL)
2527 goto next;
2528
2529 gst_buffer_map (sub, &map, GST_MAP_READ);
2530
2531 /* check size */
2532 if (map.size < sizeof (gst_riff_dmlh)) {
2533 GST_ERROR_OBJECT (avi,
2534 "DMLH entry is too small (%" G_GSIZE_FORMAT " bytes, %d needed)",
2535 map.size, (int) sizeof (gst_riff_dmlh));
2536 gst_buffer_unmap (sub, &map);
2537 goto next;
2538 }
2539 _dmlh = (gst_riff_dmlh *) map.data;
2540 dmlh.totalframes = GST_READ_UINT32_LE (&_dmlh->totalframes);
2541 gst_buffer_unmap (sub, &map);
2542
2543 GST_INFO_OBJECT (avi, "dmlh tag found: totalframes: %u",
2544 dmlh.totalframes);
2545
2546 avi->avih->tot_frames = dmlh.totalframes;
2547 goto next;
2548 }
2549
2550 default:
2551 GST_WARNING_OBJECT (avi,
2552 "Unknown tag %" GST_FOURCC_FORMAT " in ODML header",
2553 GST_FOURCC_ARGS (tag));
2554 /* Only get buffer for debugging if the memdump is needed */
2555 if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= 9) {
2556 GstMapInfo map;
2557
2558 gst_buffer_map (sub, &map, GST_MAP_READ);
2559 GST_MEMDUMP_OBJECT (avi, "Unknown ODML tag", map.data, map.size);
2560 gst_buffer_unmap (sub, &map);
2561 }
2562 /* fall-through */
2563 case GST_RIFF_TAG_JUNQ:
2564 case GST_RIFF_TAG_JUNK:
2565 next:
2566 /* skip and move to next chunk */
2567 if (sub) {
2568 gst_buffer_unref (sub);
2569 sub = NULL;
2570 }
2571 break;
2572 }
2573 }
2574 if (buf)
2575 gst_buffer_unref (buf);
2576 }
2577
2578 /* Index helper */
2579 static guint
gst_avi_demux_index_last(GstAviDemux * avi,GstAviStream * stream)2580 gst_avi_demux_index_last (GstAviDemux * avi, GstAviStream * stream)
2581 {
2582 return stream->idx_n;
2583 }
2584
2585 /* find a previous entry in the index with the given flags */
2586 static guint
gst_avi_demux_index_prev(GstAviDemux * avi,GstAviStream * stream,guint last,gboolean keyframe)2587 gst_avi_demux_index_prev (GstAviDemux * avi, GstAviStream * stream,
2588 guint last, gboolean keyframe)
2589 {
2590 GstAviIndexEntry *entry;
2591 guint i;
2592
2593 for (i = last; i > 0; i--) {
2594 entry = &stream->index[i - 1];
2595 if (!keyframe || ENTRY_IS_KEYFRAME (entry)) {
2596 return i - 1;
2597 }
2598 }
2599 return 0;
2600 }
2601
2602 static guint
gst_avi_demux_index_next(GstAviDemux * avi,GstAviStream * stream,guint last,gboolean keyframe)2603 gst_avi_demux_index_next (GstAviDemux * avi, GstAviStream * stream,
2604 guint last, gboolean keyframe)
2605 {
2606 GstAviIndexEntry *entry;
2607 gint i;
2608
2609 for (i = last + 1; i < stream->idx_n; i++) {
2610 entry = &stream->index[i];
2611 if (!keyframe || ENTRY_IS_KEYFRAME (entry)) {
2612 return i;
2613 }
2614 }
2615 return stream->idx_n - 1;
2616 }
2617
2618 static guint
gst_avi_demux_index_entry_search(GstAviIndexEntry * entry,guint64 * total)2619 gst_avi_demux_index_entry_search (GstAviIndexEntry * entry, guint64 * total)
2620 {
2621 if (entry->total < *total)
2622 return -1;
2623 else if (entry->total > *total)
2624 return 1;
2625 return 0;
2626 }
2627
2628 /*
2629 * gst_avi_demux_index_for_time:
2630 * @avi: Avi object
2631 * @stream: the stream
2632 * @time: a time position
2633 * @next: whether to look for entry before or after @time
2634 *
2635 * Finds the index entry which time is less/more or equal than the requested time.
2636 * Try to avoid binary search when we can convert the time to an index
2637 * position directly (for example for video frames with a fixed duration).
2638 *
2639 * Returns: the found position in the index.
2640 */
2641 static guint
gst_avi_demux_index_for_time(GstAviDemux * avi,GstAviStream * stream,guint64 time,gboolean next)2642 gst_avi_demux_index_for_time (GstAviDemux * avi,
2643 GstAviStream * stream, guint64 time, gboolean next)
2644 {
2645 guint index = -1;
2646 guint64 total;
2647
2648 GST_LOG_OBJECT (avi, "search time:%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
2649
2650 /* easy (and common) cases */
2651 if (time == 0 || stream->idx_n == 0)
2652 return 0;
2653 if (time >= stream->idx_duration)
2654 return stream->idx_n - 1;
2655
2656 /* figure out where we need to go. For that we convert the time to an
2657 * index entry or we convert it to a total and then do a binary search. */
2658 if (stream->is_vbr) {
2659 /* VBR stream next timestamp */
2660 if (stream->strh->type == GST_RIFF_FCC_auds) {
2661 total = avi_stream_convert_time_to_frames_unchecked (stream, time);
2662 } else {
2663 index = avi_stream_convert_time_to_frames_unchecked (stream, time);
2664 /* this entry typically undershoots the target time,
2665 * so check a bit more if next needed */
2666 if (next && index != -1) {
2667 GstClockTime itime =
2668 avi_stream_convert_frames_to_time_unchecked (stream, index);
2669 if (itime < time && index + 1 < stream->idx_n)
2670 index++;
2671 }
2672 }
2673 } else if (stream->strh->type == GST_RIFF_FCC_auds) {
2674 /* constant rate stream */
2675 total = avi_stream_convert_time_to_bytes_unchecked (stream, time);
2676 } else
2677 return -1;
2678
2679 if (index == -1) {
2680 GstAviIndexEntry *entry;
2681
2682 /* no index, find index with binary search on total */
2683 GST_LOG_OBJECT (avi, "binary search for entry with total %"
2684 G_GUINT64_FORMAT, total);
2685
2686 entry = gst_util_array_binary_search (stream->index,
2687 stream->idx_n, sizeof (GstAviIndexEntry),
2688 (GCompareDataFunc) gst_avi_demux_index_entry_search,
2689 next ? GST_SEARCH_MODE_AFTER : GST_SEARCH_MODE_BEFORE, &total, NULL);
2690
2691 if (entry == NULL) {
2692 GST_LOG_OBJECT (avi, "not found, assume index 0");
2693 index = 0;
2694 } else {
2695 index = entry - stream->index;
2696 GST_LOG_OBJECT (avi, "found at %u", index);
2697 }
2698 } else {
2699 GST_LOG_OBJECT (avi, "converted time to index %u", index);
2700 }
2701
2702 return index;
2703 }
2704
2705 static inline GstAviStream *
gst_avi_demux_stream_for_id(GstAviDemux * avi,guint32 id)2706 gst_avi_demux_stream_for_id (GstAviDemux * avi, guint32 id)
2707 {
2708 guint stream_nr;
2709 GstAviStream *stream;
2710
2711 /* get the stream for this entry */
2712 stream_nr = CHUNKID_TO_STREAMNR (id);
2713 if (G_UNLIKELY (stream_nr >= avi->num_streams)) {
2714 GST_WARNING_OBJECT (avi,
2715 "invalid stream nr %d (0x%08x, %" GST_FOURCC_FORMAT ")", stream_nr, id,
2716 GST_FOURCC_ARGS (id));
2717 return NULL;
2718 }
2719 stream = &avi->stream[stream_nr];
2720 if (G_UNLIKELY (!stream->strh)) {
2721 GST_WARNING_OBJECT (avi, "Unhandled stream %d, skipping", stream_nr);
2722 return NULL;
2723 }
2724 return stream;
2725 }
2726
2727 /*
2728 * gst_avi_demux_parse_index:
2729 * @avi: calling element (used for debugging/errors).
2730 * @buf: buffer containing the full index.
2731 *
2732 * Read index entries from the provided buffer.
2733 * The buffer should contain a GST_RIFF_TAG_idx1 chunk.
2734 */
2735 static gboolean
gst_avi_demux_parse_index(GstAviDemux * avi,GstBuffer * buf)2736 gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
2737 {
2738 GstMapInfo map;
2739 guint i, num, n;
2740 gst_riff_index_entry *index;
2741 GstClockTime stamp;
2742 GstAviStream *stream;
2743 GstAviIndexEntry entry;
2744 guint32 id;
2745
2746 if (!buf)
2747 return FALSE;
2748
2749 gst_buffer_map (buf, &map, GST_MAP_READ);
2750
2751 stamp = gst_util_get_timestamp ();
2752
2753 /* see how many items in the index */
2754 num = map.size / sizeof (gst_riff_index_entry);
2755 if (num == 0)
2756 goto empty_list;
2757
2758 GST_INFO_OBJECT (avi, "Parsing index, nr_entries = %6d", num);
2759
2760 index = (gst_riff_index_entry *) map.data;
2761
2762 /* figure out if the index is 0 based or relative to the MOVI start */
2763 entry.offset = GST_READ_UINT32_LE (&index[0].offset);
2764 if (entry.offset < avi->offset) {
2765 avi->index_offset = avi->offset + 8;
2766 GST_DEBUG ("index_offset = %" G_GUINT64_FORMAT, avi->index_offset);
2767 } else {
2768 avi->index_offset = 0;
2769 GST_DEBUG ("index is 0 based");
2770 }
2771
2772 for (i = 0, n = 0; i < num; i++) {
2773 id = GST_READ_UINT32_LE (&index[i].id);
2774 entry.offset = GST_READ_UINT32_LE (&index[i].offset);
2775
2776 /* some sanity checks */
2777 if (G_UNLIKELY (id == GST_RIFF_rec || id == 0 ||
2778 (entry.offset == 0 && n > 0)))
2779 continue;
2780
2781 /* get the stream for this entry */
2782 stream = gst_avi_demux_stream_for_id (avi, id);
2783 if (G_UNLIKELY (!stream))
2784 continue;
2785
2786 /* handle offset and size */
2787 entry.offset += avi->index_offset + 8;
2788 entry.size = GST_READ_UINT32_LE (&index[i].size);
2789
2790 /* handle flags */
2791 if (stream->strh->type == GST_RIFF_FCC_auds) {
2792 /* all audio frames are keyframes */
2793 ENTRY_SET_KEYFRAME (&entry);
2794 } else if (stream->strh->type == GST_RIFF_FCC_vids &&
2795 stream->strf.vids->compression == GST_RIFF_DXSB) {
2796 /* all xsub frames are keyframes */
2797 ENTRY_SET_KEYFRAME (&entry);
2798 } else {
2799 guint32 flags;
2800 /* else read flags */
2801 flags = GST_READ_UINT32_LE (&index[i].flags);
2802 if (flags & GST_RIFF_IF_KEYFRAME) {
2803 ENTRY_SET_KEYFRAME (&entry);
2804 } else {
2805 ENTRY_UNSET_KEYFRAME (&entry);
2806 }
2807 }
2808
2809 /* and add */
2810 if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
2811 goto out_of_mem;
2812
2813 n++;
2814 }
2815 gst_buffer_unmap (buf, &map);
2816 gst_buffer_unref (buf);
2817
2818 /* get stream stats now */
2819 avi->have_index = gst_avi_demux_do_index_stats (avi);
2820
2821 stamp = gst_util_get_timestamp () - stamp;
2822 GST_DEBUG_OBJECT (avi, "index parsing took %" GST_TIME_FORMAT,
2823 GST_TIME_ARGS (stamp));
2824
2825 return TRUE;
2826
2827 /* ERRORS */
2828 empty_list:
2829 {
2830 GST_DEBUG_OBJECT (avi, "empty index");
2831 gst_buffer_unmap (buf, &map);
2832 gst_buffer_unref (buf);
2833 return FALSE;
2834 }
2835 out_of_mem:
2836 {
2837 GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
2838 ("Cannot allocate memory for %u*%u=%u bytes",
2839 (guint) sizeof (GstAviIndexEntry), num,
2840 (guint) sizeof (GstAviIndexEntry) * num));
2841 gst_buffer_unmap (buf, &map);
2842 gst_buffer_unref (buf);
2843 return FALSE;
2844 }
2845 }
2846
2847 /*
2848 * gst_avi_demux_stream_index:
2849 * @avi: avi demuxer object.
2850 *
2851 * Seeks to index and reads it.
2852 */
2853 static void
gst_avi_demux_stream_index(GstAviDemux * avi)2854 gst_avi_demux_stream_index (GstAviDemux * avi)
2855 {
2856 GstFlowReturn res;
2857 guint64 offset = avi->offset;
2858 GstBuffer *buf = NULL;
2859 guint32 tag;
2860 guint32 size;
2861 GstMapInfo map;
2862
2863 GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset);
2864
2865 /* get chunk information */
2866 res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2867 if (res != GST_FLOW_OK)
2868 goto pull_failed;
2869
2870 gst_buffer_map (buf, &map, GST_MAP_READ);
2871 if (map.size < 8)
2872 goto too_small;
2873
2874 /* check tag first before blindly trying to read 'size' bytes */
2875 tag = GST_READ_UINT32_LE (map.data);
2876 size = GST_READ_UINT32_LE (map.data + 4);
2877 if (tag == GST_RIFF_TAG_LIST) {
2878 /* this is the movi tag */
2879 GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
2880 (8 + GST_ROUND_UP_2 (size)));
2881 offset += 8 + GST_ROUND_UP_2 (size);
2882 gst_buffer_unmap (buf, &map);
2883 gst_buffer_unref (buf);
2884
2885 buf = NULL;
2886 res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2887 if (res != GST_FLOW_OK)
2888 goto pull_failed;
2889
2890 gst_buffer_map (buf, &map, GST_MAP_READ);
2891 if (map.size < 8)
2892 goto too_small;
2893
2894 tag = GST_READ_UINT32_LE (map.data);
2895 size = GST_READ_UINT32_LE (map.data + 4);
2896 }
2897 gst_buffer_unmap (buf, &map);
2898 gst_buffer_unref (buf);
2899
2900 if (tag != GST_RIFF_TAG_idx1)
2901 goto no_index;
2902 if (!size)
2903 goto zero_index;
2904
2905 GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset);
2906
2907 /* read chunk, advance offset */
2908 if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi),
2909 avi->sinkpad, &offset, &tag, &buf) != GST_FLOW_OK)
2910 return;
2911
2912 GST_DEBUG ("will parse index chunk size %" G_GSIZE_FORMAT " for tag %"
2913 GST_FOURCC_FORMAT, gst_buffer_get_size (buf), GST_FOURCC_ARGS (tag));
2914
2915 gst_avi_demux_parse_index (avi, buf);
2916
2917 #ifndef GST_DISABLE_GST_DEBUG
2918 /* debug our indexes */
2919 {
2920 gint i;
2921 GstAviStream *stream;
2922
2923 for (i = 0; i < avi->num_streams; i++) {
2924 stream = &avi->stream[i];
2925 GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes",
2926 i, stream->idx_n, stream->total_bytes);
2927 }
2928 }
2929 #endif
2930 return;
2931
2932 /* ERRORS */
2933 pull_failed:
2934 {
2935 GST_DEBUG_OBJECT (avi,
2936 "pull range failed: pos=%" G_GUINT64_FORMAT " size=8", offset);
2937 return;
2938 }
2939 too_small:
2940 {
2941 GST_DEBUG_OBJECT (avi, "Buffer is too small");
2942 gst_buffer_unmap (buf, &map);
2943 gst_buffer_unref (buf);
2944 return;
2945 }
2946 no_index:
2947 {
2948 GST_WARNING_OBJECT (avi,
2949 "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT,
2950 GST_FOURCC_ARGS (tag));
2951 return;
2952 }
2953 zero_index:
2954 {
2955 GST_WARNING_OBJECT (avi, "Empty index data (idx1) after movi chunk");
2956 return;
2957 }
2958 }
2959
2960 /*
2961 * gst_avi_demux_stream_index_push:
2962 * @avi: avi demuxer object.
2963 *
2964 * Read index.
2965 */
2966 static void
gst_avi_demux_stream_index_push(GstAviDemux * avi)2967 gst_avi_demux_stream_index_push (GstAviDemux * avi)
2968 {
2969 guint64 offset = avi->idx1_offset;
2970 GstBuffer *buf;
2971 guint32 tag;
2972 guint32 size;
2973
2974 GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset);
2975
2976 /* get chunk information */
2977 if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
2978 return;
2979
2980 /* check tag first before blindly trying to read 'size' bytes */
2981 if (tag == GST_RIFF_TAG_LIST) {
2982 /* this is the movi tag */
2983 GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
2984 (8 + GST_ROUND_UP_2 (size)));
2985 avi->idx1_offset = offset + 8 + GST_ROUND_UP_2 (size);
2986 /* issue seek to allow chain function to handle it and return! */
2987 perform_seek_to_offset (avi, avi->idx1_offset, avi->segment_seqnum);
2988 return;
2989 }
2990
2991 if (tag != GST_RIFF_TAG_idx1)
2992 goto no_index;
2993
2994 GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset);
2995
2996 /* flush chunk header */
2997 gst_adapter_flush (avi->adapter, 8);
2998 /* read chunk payload */
2999 buf = gst_adapter_take_buffer (avi->adapter, size);
3000 if (!buf)
3001 goto pull_failed;
3002 /* advance offset */
3003 offset += 8 + GST_ROUND_UP_2 (size);
3004
3005 GST_DEBUG ("will parse index chunk size %" G_GSIZE_FORMAT " for tag %"
3006 GST_FOURCC_FORMAT, gst_buffer_get_size (buf), GST_FOURCC_ARGS (tag));
3007
3008 avi->offset = avi->first_movi_offset;
3009 gst_avi_demux_parse_index (avi, buf);
3010
3011 #ifndef GST_DISABLE_GST_DEBUG
3012 /* debug our indexes */
3013 {
3014 gint i;
3015 GstAviStream *stream;
3016
3017 for (i = 0; i < avi->num_streams; i++) {
3018 stream = &avi->stream[i];
3019 GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes",
3020 i, stream->idx_n, stream->total_bytes);
3021 }
3022 }
3023 #endif
3024 return;
3025
3026 /* ERRORS */
3027 pull_failed:
3028 {
3029 GST_DEBUG_OBJECT (avi,
3030 "taking data from adapter failed: pos=%" G_GUINT64_FORMAT " size=%u",
3031 offset, size);
3032 return;
3033 }
3034 no_index:
3035 {
3036 GST_WARNING_OBJECT (avi,
3037 "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT,
3038 GST_FOURCC_ARGS (tag));
3039 return;
3040 }
3041 }
3042
3043 /*
3044 * gst_avi_demux_peek_tag:
3045 *
3046 * Returns the tag and size of the next chunk
3047 */
3048 static GstFlowReturn
gst_avi_demux_peek_tag(GstAviDemux * avi,guint64 offset,guint32 * tag,guint * size)3049 gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
3050 guint * size)
3051 {
3052 GstFlowReturn res;
3053 GstBuffer *buf = NULL;
3054 GstMapInfo map;
3055
3056 res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
3057 if (res != GST_FLOW_OK)
3058 goto pull_failed;
3059
3060 gst_buffer_map (buf, &map, GST_MAP_READ);
3061 if (map.size != 8)
3062 goto wrong_size;
3063
3064 *tag = GST_READ_UINT32_LE (map.data);
3065 *size = GST_READ_UINT32_LE (map.data + 4);
3066
3067 GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %"
3068 G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag),
3069 *size, offset + 8, offset + 8 + (gint64) * size);
3070
3071 done:
3072 gst_buffer_unmap (buf, &map);
3073 gst_buffer_unref (buf);
3074
3075 return res;
3076
3077 /* ERRORS */
3078 pull_failed:
3079 {
3080 GST_DEBUG_OBJECT (avi, "pull_ranged returned %s", gst_flow_get_name (res));
3081 return res;
3082 }
3083 wrong_size:
3084 {
3085 GST_DEBUG_OBJECT (avi, "got %" G_GSIZE_FORMAT " bytes which is <> 8 bytes",
3086 map.size);
3087 res = GST_FLOW_ERROR;
3088 goto done;
3089 }
3090 }
3091
3092 /*
3093 * gst_avi_demux_next_data_buffer:
3094 *
3095 * Returns the offset and size of the next buffer
3096 * Position is the position of the buffer (after tag and size)
3097 */
3098 static GstFlowReturn
gst_avi_demux_next_data_buffer(GstAviDemux * avi,guint64 * offset,guint32 * tag,guint * size)3099 gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
3100 guint32 * tag, guint * size)
3101 {
3102 guint64 off = *offset;
3103 guint _size = 0;
3104 GstFlowReturn res;
3105
3106 do {
3107 res = gst_avi_demux_peek_tag (avi, off, tag, &_size);
3108 if (res != GST_FLOW_OK)
3109 break;
3110 if (*tag == GST_RIFF_TAG_LIST || *tag == GST_RIFF_TAG_RIFF)
3111 off += 8 + 4; /* skip tag + size + subtag */
3112 else {
3113 *offset = off + 8;
3114 *size = _size;
3115 break;
3116 }
3117 } while (TRUE);
3118
3119 return res;
3120 }
3121
3122 /*
3123 * gst_avi_demux_stream_scan:
3124 * @avi: calling element (used for debugging/errors).
3125 *
3126 * Scan the file for all chunks to "create" a new index.
3127 * pull-range based
3128 */
3129 static gboolean
gst_avi_demux_stream_scan(GstAviDemux * avi)3130 gst_avi_demux_stream_scan (GstAviDemux * avi)
3131 {
3132 GstFlowReturn res;
3133 GstAviStream *stream;
3134 guint64 pos = 0;
3135 guint64 length;
3136 gint64 tmplength;
3137 guint32 tag = 0;
3138 guint num;
3139
3140 /* FIXME:
3141 * - implement non-seekable source support.
3142 */
3143 GST_DEBUG_OBJECT (avi, "Creating index");
3144
3145 /* get the size of the file */
3146 if (!gst_pad_peer_query_duration (avi->sinkpad, GST_FORMAT_BYTES, &tmplength))
3147 return FALSE;
3148 length = tmplength;
3149
3150 /* guess the total amount of entries we expect */
3151 num = 16000;
3152
3153 while (TRUE) {
3154 GstAviIndexEntry entry;
3155 guint size = 0;
3156
3157 /* start reading data buffers to find the id and offset */
3158 res = gst_avi_demux_next_data_buffer (avi, &pos, &tag, &size);
3159 if (G_UNLIKELY (res != GST_FLOW_OK))
3160 break;
3161
3162 /* get stream */
3163 stream = gst_avi_demux_stream_for_id (avi, tag);
3164 if (G_UNLIKELY (!stream))
3165 goto next;
3166
3167 /* we can't figure out the keyframes, assume they all are */
3168 entry.flags = GST_AVI_KEYFRAME;
3169 entry.offset = pos;
3170 entry.size = size;
3171
3172 /* and add to the index of this stream */
3173 if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
3174 goto out_of_mem;
3175
3176 next:
3177 /* update position */
3178 pos += GST_ROUND_UP_2 (size);
3179 if (G_UNLIKELY (pos > length)) {
3180 GST_WARNING_OBJECT (avi,
3181 "Stopping index lookup since we are further than EOF");
3182 break;
3183 }
3184 }
3185
3186 /* collect stats */
3187 avi->have_index = gst_avi_demux_do_index_stats (avi);
3188
3189 return TRUE;
3190
3191 /* ERRORS */
3192 out_of_mem:
3193 {
3194 GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
3195 ("Cannot allocate memory for %u*%u=%u bytes",
3196 (guint) sizeof (GstAviIndexEntry), num,
3197 (guint) sizeof (GstAviIndexEntry) * num));
3198 return FALSE;
3199 }
3200 }
3201
3202 static void
gst_avi_demux_calculate_durations_from_index(GstAviDemux * avi)3203 gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
3204 {
3205 guint i;
3206 GstClockTime total;
3207 GstAviStream *stream;
3208
3209 total = GST_CLOCK_TIME_NONE;
3210
3211 /* all streams start at a timestamp 0 */
3212 for (i = 0; i < avi->num_streams; i++) {
3213 GstClockTime duration, hduration;
3214 gst_riff_strh *strh;
3215
3216 stream = &avi->stream[i];
3217 if (G_UNLIKELY (!stream || !stream->idx_n || !(strh = stream->strh)))
3218 continue;
3219
3220 /* get header duration for the stream */
3221 hduration = stream->hdr_duration;
3222 /* index duration calculated during parsing */
3223 duration = stream->idx_duration;
3224
3225 /* now pick a good duration */
3226 if (GST_CLOCK_TIME_IS_VALID (duration)) {
3227 /* index gave valid duration, use that */
3228 GST_INFO ("Stream %p duration according to index: %" GST_TIME_FORMAT,
3229 stream, GST_TIME_ARGS (duration));
3230 } else {
3231 /* fall back to header info to calculate a duration */
3232 duration = hduration;
3233 }
3234 GST_INFO ("Setting duration of stream #%d to %" GST_TIME_FORMAT,
3235 i, GST_TIME_ARGS (duration));
3236 /* set duration for the stream */
3237 stream->duration = duration;
3238
3239 /* find total duration */
3240 if (total == GST_CLOCK_TIME_NONE ||
3241 (GST_CLOCK_TIME_IS_VALID (duration) && duration > total))
3242 total = duration;
3243 }
3244
3245 if (GST_CLOCK_TIME_IS_VALID (total) && (total > 0)) {
3246 /* now update the duration for those streams where we had none */
3247 for (i = 0; i < avi->num_streams; i++) {
3248 stream = &avi->stream[i];
3249
3250 if (!GST_CLOCK_TIME_IS_VALID (stream->duration)
3251 || stream->duration == 0) {
3252 stream->duration = total;
3253
3254 GST_INFO ("Stream %p duration according to total: %" GST_TIME_FORMAT,
3255 stream, GST_TIME_ARGS (total));
3256 }
3257 }
3258 }
3259
3260 /* and set the total duration in the segment. */
3261 GST_INFO ("Setting total duration to: %" GST_TIME_FORMAT,
3262 GST_TIME_ARGS (total));
3263
3264 avi->segment.duration = total;
3265 }
3266
3267 /* returns FALSE if there are no pads to deliver event to,
3268 * otherwise TRUE (whatever the outcome of event sending),
3269 * takes ownership of the event. */
3270 static gboolean
gst_avi_demux_push_event(GstAviDemux * avi,GstEvent * event)3271 gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event)
3272 {
3273 gboolean result = FALSE;
3274 gint i;
3275
3276 GST_DEBUG_OBJECT (avi, "sending %s event to %d streams",
3277 GST_EVENT_TYPE_NAME (event), avi->num_streams);
3278
3279 for (i = 0; i < avi->num_streams; i++) {
3280 GstAviStream *stream = &avi->stream[i];
3281
3282 if (stream->pad) {
3283 result = TRUE;
3284 gst_pad_push_event (stream->pad, gst_event_ref (event));
3285 }
3286 }
3287 gst_event_unref (event);
3288 return result;
3289 }
3290
3291 static void
gst_avi_demux_check_seekability(GstAviDemux * avi)3292 gst_avi_demux_check_seekability (GstAviDemux * avi)
3293 {
3294 GstQuery *query;
3295 gboolean seekable = FALSE;
3296 gint64 start = -1, stop = -1;
3297
3298 query = gst_query_new_seeking (GST_FORMAT_BYTES);
3299 if (!gst_pad_peer_query (avi->sinkpad, query)) {
3300 GST_DEBUG_OBJECT (avi, "seeking query failed");
3301 goto done;
3302 }
3303
3304 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
3305
3306 /* try harder to query upstream size if we didn't get it the first time */
3307 if (seekable && stop == -1) {
3308 GST_DEBUG_OBJECT (avi, "doing duration query to fix up unset stop");
3309 gst_pad_peer_query_duration (avi->sinkpad, GST_FORMAT_BYTES, &stop);
3310 }
3311
3312 /* if upstream doesn't know the size, it's likely that it's not seekable in
3313 * practice even if it technically may be seekable */
3314 if (seekable && (start != 0 || stop <= start)) {
3315 GST_DEBUG_OBJECT (avi, "seekable but unknown start/stop -> disable");
3316 seekable = FALSE;
3317 }
3318
3319 done:
3320 GST_INFO_OBJECT (avi, "seekable: %d (%" G_GUINT64_FORMAT " - %"
3321 G_GUINT64_FORMAT ")", seekable, start, stop);
3322 avi->seekable = seekable;
3323
3324 gst_query_unref (query);
3325 }
3326
3327 /*
3328 * Read AVI headers when streaming
3329 */
3330 static GstFlowReturn
gst_avi_demux_stream_header_push(GstAviDemux * avi)3331 gst_avi_demux_stream_header_push (GstAviDemux * avi)
3332 {
3333 GstFlowReturn ret = GST_FLOW_OK;
3334 guint32 tag = 0;
3335 guint32 ltag = 0;
3336 guint32 size = 0;
3337 const guint8 *data;
3338 GstBuffer *buf = NULL, *sub = NULL;
3339 guint offset = 4;
3340 gint i;
3341 GstTagList *tags = NULL;
3342 guint8 fourcc[4];
3343
3344 GST_DEBUG ("Reading and parsing avi headers: %d", avi->header_state);
3345
3346 switch (avi->header_state) {
3347 case GST_AVI_DEMUX_HEADER_TAG_LIST:
3348 again:
3349 if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3350 avi->offset += 8 + GST_ROUND_UP_2 (size);
3351 if (tag != GST_RIFF_TAG_LIST)
3352 goto header_no_list;
3353
3354 gst_adapter_flush (avi->adapter, 8);
3355 /* Find the 'hdrl' LIST tag */
3356 GST_DEBUG ("Reading %d bytes", size);
3357 buf = gst_adapter_take_buffer (avi->adapter, size);
3358
3359 gst_buffer_extract (buf, 0, fourcc, 4);
3360
3361 if (GST_READ_UINT32_LE (fourcc) != GST_RIFF_LIST_hdrl) {
3362 GST_WARNING_OBJECT (avi, "Invalid AVI header (no hdrl at start): %"
3363 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag));
3364 gst_buffer_unref (buf);
3365 goto again;
3366 }
3367
3368 /* mind padding */
3369 if (size & 1)
3370 gst_adapter_flush (avi->adapter, 1);
3371
3372 GST_DEBUG ("'hdrl' LIST tag found. Parsing next chunk");
3373
3374 gst_avi_demux_roundup_list (avi, &buf);
3375
3376 /* the hdrl starts with a 'avih' header */
3377 if (!gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
3378 &sub))
3379 goto header_no_avih;
3380
3381 if (tag != GST_RIFF_TAG_avih)
3382 goto header_no_avih;
3383
3384 if (!gst_avi_demux_parse_avih (avi, sub, &avi->avih))
3385 goto header_wrong_avih;
3386
3387 GST_DEBUG_OBJECT (avi, "AVI header ok, reading elements from header");
3388
3389 /* now, read the elements from the header until the end */
3390 while (gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
3391 &sub)) {
3392 /* sub can be NULL on empty tags */
3393 if (!sub)
3394 continue;
3395
3396 switch (tag) {
3397 case GST_RIFF_TAG_LIST:
3398 if (gst_buffer_get_size (sub) < 4)
3399 goto next;
3400
3401 gst_buffer_extract (sub, 0, fourcc, 4);
3402
3403 switch (GST_READ_UINT32_LE (fourcc)) {
3404 case GST_RIFF_LIST_strl:
3405 if (!(gst_avi_demux_parse_stream (avi, sub))) {
3406 sub = NULL;
3407 GST_ELEMENT_WARNING (avi, STREAM, DEMUX, (NULL),
3408 ("failed to parse stream, ignoring"));
3409 goto next;
3410 }
3411 sub = NULL;
3412 goto next;
3413 case GST_RIFF_LIST_odml:
3414 gst_avi_demux_parse_odml (avi, sub);
3415 sub = NULL;
3416 break;
3417 default:
3418 GST_WARNING_OBJECT (avi,
3419 "Unknown list %" GST_FOURCC_FORMAT " in AVI header",
3420 GST_FOURCC_ARGS (GST_READ_UINT32_LE (fourcc)));
3421 /* fall-through */
3422 case GST_RIFF_TAG_JUNQ:
3423 case GST_RIFF_TAG_JUNK:
3424 goto next;
3425 }
3426 break;
3427 case GST_RIFF_IDIT:
3428 gst_avi_demux_parse_idit (avi, sub);
3429 goto next;
3430 default:
3431 GST_WARNING_OBJECT (avi,
3432 "Unknown tag %" GST_FOURCC_FORMAT " in AVI header",
3433 GST_FOURCC_ARGS (tag));
3434 /* Only get buffer for debugging if the memdump is needed */
3435 if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= 9) {
3436 GstMapInfo map;
3437
3438 gst_buffer_map (sub, &map, GST_MAP_READ);
3439 GST_MEMDUMP_OBJECT (avi, "Unknown tag", map.data, map.size);
3440 gst_buffer_unmap (sub, &map);
3441 }
3442 /* fall-through */
3443 case GST_RIFF_TAG_JUNQ:
3444 case GST_RIFF_TAG_JUNK:
3445 next:
3446 /* move to next chunk */
3447 if (sub)
3448 gst_buffer_unref (sub);
3449 sub = NULL;
3450 break;
3451 }
3452 }
3453 gst_buffer_unref (buf);
3454 GST_DEBUG ("elements parsed");
3455
3456 /* check parsed streams */
3457 if (avi->num_streams == 0) {
3458 goto no_streams;
3459 } else if (avi->num_streams != avi->avih->streams) {
3460 GST_WARNING_OBJECT (avi,
3461 "Stream header mentioned %d streams, but %d available",
3462 avi->avih->streams, avi->num_streams);
3463 }
3464 GST_DEBUG ("Get junk and info next");
3465 avi->header_state = GST_AVI_DEMUX_HEADER_INFO;
3466 } else {
3467 /* Need more data */
3468 return ret;
3469 }
3470 /* fall-though */
3471 case GST_AVI_DEMUX_HEADER_INFO:
3472 GST_DEBUG_OBJECT (avi, "skipping junk between header and data ...");
3473 while (TRUE) {
3474 if (gst_adapter_available (avi->adapter) < 12)
3475 return GST_FLOW_OK;
3476
3477 data = gst_adapter_map (avi->adapter, 12);
3478 tag = GST_READ_UINT32_LE (data);
3479 size = GST_READ_UINT32_LE (data + 4);
3480 ltag = GST_READ_UINT32_LE (data + 8);
3481 gst_adapter_unmap (avi->adapter);
3482
3483 if (tag == GST_RIFF_TAG_LIST) {
3484 switch (ltag) {
3485 case GST_RIFF_LIST_movi:
3486 gst_adapter_flush (avi->adapter, 12);
3487 if (!avi->first_movi_offset)
3488 avi->first_movi_offset = avi->offset;
3489 avi->offset += 12;
3490 avi->idx1_offset = avi->offset + size - 4;
3491 goto skipping_done;
3492 case GST_RIFF_LIST_INFO:
3493 GST_DEBUG ("Found INFO chunk");
3494 if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3495 GST_DEBUG ("got size %d", size);
3496 avi->offset += 12;
3497 gst_adapter_flush (avi->adapter, 12);
3498 if (size > 4) {
3499 buf = gst_adapter_take_buffer (avi->adapter, size - 4);
3500 /* mind padding */
3501 if (size & 1)
3502 gst_adapter_flush (avi->adapter, 1);
3503 gst_riff_parse_info (GST_ELEMENT_CAST (avi), buf, &tags);
3504 if (tags) {
3505 if (avi->globaltags) {
3506 gst_tag_list_insert (avi->globaltags, tags,
3507 GST_TAG_MERGE_REPLACE);
3508 gst_tag_list_unref (tags);
3509 } else {
3510 avi->globaltags = tags;
3511 }
3512 }
3513 tags = NULL;
3514 gst_buffer_unref (buf);
3515
3516 avi->offset += GST_ROUND_UP_2 (size) - 4;
3517 } else {
3518 GST_DEBUG ("skipping INFO LIST prefix");
3519 }
3520 } else {
3521 /* Need more data */
3522 return GST_FLOW_OK;
3523 }
3524 break;
3525 default:
3526 if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
3527 /* accept 0 size buffer here */
3528 avi->abort_buffering = FALSE;
3529 avi->offset += 8 + GST_ROUND_UP_2 (size);
3530 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
3531 } else {
3532 /* Need more data */
3533 return GST_FLOW_OK;
3534 }
3535 break;
3536 }
3537 } else {
3538 if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
3539 /* accept 0 size buffer here */
3540 avi->abort_buffering = FALSE;
3541 avi->offset += 8 + GST_ROUND_UP_2 (size);
3542 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
3543 } else {
3544 /* Need more data */
3545 return GST_FLOW_OK;
3546 }
3547 }
3548 }
3549 break;
3550 default:
3551 GST_WARNING ("unhandled header state: %d", avi->header_state);
3552 break;
3553 }
3554 skipping_done:
3555
3556 GST_DEBUG_OBJECT (avi, "skipping done ... (streams=%u, stream[0].indexes=%p)",
3557 avi->num_streams, avi->stream[0].indexes);
3558
3559 GST_DEBUG ("Found movi chunk. Starting to stream data");
3560 avi->state = GST_AVI_DEMUX_MOVI;
3561
3562 /* no indexes in push mode, but it still sets some variables */
3563 gst_avi_demux_calculate_durations_from_index (avi);
3564
3565 gst_avi_demux_expose_streams (avi, TRUE);
3566
3567 /* prepare all streams for index 0 */
3568 for (i = 0; i < avi->num_streams; i++)
3569 avi->stream[i].current_entry = 0;
3570
3571 /* create initial NEWSEGMENT event */
3572 if (avi->seg_event)
3573 gst_event_unref (avi->seg_event);
3574 avi->seg_event = gst_event_new_segment (&avi->segment);
3575 if (avi->segment_seqnum)
3576 gst_event_set_seqnum (avi->seg_event, avi->segment_seqnum);
3577
3578 gst_avi_demux_check_seekability (avi);
3579
3580 /* at this point we know all the streams and we can signal the no more
3581 * pads signal */
3582 GST_DEBUG_OBJECT (avi, "signaling no more pads");
3583 gst_element_no_more_pads (GST_ELEMENT_CAST (avi));
3584
3585 return GST_FLOW_OK;
3586
3587 /* ERRORS */
3588 no_streams:
3589 {
3590 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
3591 return GST_FLOW_ERROR;
3592 }
3593 header_no_list:
3594 {
3595 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3596 ("Invalid AVI header (no LIST at start): %"
3597 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3598 return GST_FLOW_ERROR;
3599 }
3600 header_no_avih:
3601 {
3602 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3603 ("Invalid AVI header (no avih at start): %"
3604 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3605 if (sub)
3606 gst_buffer_unref (sub);
3607
3608 gst_buffer_unref (buf);
3609 return GST_FLOW_ERROR;
3610 }
3611 header_wrong_avih:
3612 {
3613 gst_buffer_unref (buf);
3614 return GST_FLOW_ERROR;
3615 }
3616 }
3617
3618 static void
gst_avi_demux_add_date_tag(GstAviDemux * avi,gint y,gint m,gint d,gint h,gint min,gint s)3619 gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d,
3620 gint h, gint min, gint s)
3621 {
3622 GDate *date;
3623 GstDateTime *dt;
3624
3625 date = g_date_new_dmy (d, m, y);
3626 if (!g_date_valid (date)) {
3627 /* bogus date */
3628 GST_WARNING_OBJECT (avi, "Refusing to add invalid date %d-%d-%d", y, m, d);
3629 g_date_free (date);
3630 return;
3631 }
3632
3633 dt = gst_date_time_new_local_time (y, m, d, h, min, s);
3634
3635 if (avi->globaltags == NULL)
3636 avi->globaltags = gst_tag_list_new_empty ();
3637
3638 gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date,
3639 NULL);
3640 g_date_free (date);
3641 if (dt) {
3642 gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
3643 dt, NULL);
3644 gst_date_time_unref (dt);
3645 }
3646 }
3647
3648 static void
gst_avi_demux_parse_idit_nums_only(GstAviDemux * avi,gchar * data)3649 gst_avi_demux_parse_idit_nums_only (GstAviDemux * avi, gchar * data)
3650 {
3651 gint y, m, d;
3652 gint hr = 0, min = 0, sec = 0;
3653 gint ret;
3654
3655 GST_DEBUG ("data : '%s'", data);
3656
3657 ret = sscanf (data, "%d:%d:%d %d:%d:%d", &y, &m, &d, &hr, &min, &sec);
3658 if (ret < 3) {
3659 /* Attempt YYYY/MM/DD/ HH:MM variant (found in CASIO cameras) */
3660 ret = sscanf (data, "%04d/%02d/%02d/ %d:%d", &y, &m, &d, &hr, &min);
3661 if (ret < 3) {
3662 GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
3663 return;
3664 }
3665 }
3666 gst_avi_demux_add_date_tag (avi, y, m, d, hr, min, sec);
3667 }
3668
3669 static gint
get_month_num(gchar * data,guint size)3670 get_month_num (gchar * data, guint size)
3671 {
3672 if (g_ascii_strncasecmp (data, "jan", 3) == 0) {
3673 return 1;
3674 } else if (g_ascii_strncasecmp (data, "feb", 3) == 0) {
3675 return 2;
3676 } else if (g_ascii_strncasecmp (data, "mar", 3) == 0) {
3677 return 3;
3678 } else if (g_ascii_strncasecmp (data, "apr", 3) == 0) {
3679 return 4;
3680 } else if (g_ascii_strncasecmp (data, "may", 3) == 0) {
3681 return 5;
3682 } else if (g_ascii_strncasecmp (data, "jun", 3) == 0) {
3683 return 6;
3684 } else if (g_ascii_strncasecmp (data, "jul", 3) == 0) {
3685 return 7;
3686 } else if (g_ascii_strncasecmp (data, "aug", 3) == 0) {
3687 return 8;
3688 } else if (g_ascii_strncasecmp (data, "sep", 3) == 0) {
3689 return 9;
3690 } else if (g_ascii_strncasecmp (data, "oct", 3) == 0) {
3691 return 10;
3692 } else if (g_ascii_strncasecmp (data, "nov", 3) == 0) {
3693 return 11;
3694 } else if (g_ascii_strncasecmp (data, "dec", 3) == 0) {
3695 return 12;
3696 }
3697
3698 return 0;
3699 }
3700
3701 static void
gst_avi_demux_parse_idit_text(GstAviDemux * avi,gchar * data)3702 gst_avi_demux_parse_idit_text (GstAviDemux * avi, gchar * data)
3703 {
3704 gint year, month, day;
3705 gint hour, min, sec;
3706 gint ret;
3707 gchar weekday[4];
3708 gchar monthstr[4];
3709
3710 ret = sscanf (data, "%3s %3s %d %d:%d:%d %d", weekday, monthstr, &day, &hour,
3711 &min, &sec, &year);
3712 if (ret != 7) {
3713 GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
3714 return;
3715 }
3716 month = get_month_num (monthstr, strlen (monthstr));
3717 gst_avi_demux_add_date_tag (avi, year, month, day, hour, min, sec);
3718 }
3719
3720 static void
gst_avi_demux_parse_idit(GstAviDemux * avi,GstBuffer * buf)3721 gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf)
3722 {
3723 GstMapInfo map;
3724 gchar *ptr;
3725 gsize left;
3726 gchar *safedata = NULL;
3727
3728 gst_buffer_map (buf, &map, GST_MAP_READ);
3729 /*
3730 * According to:
3731 * http://www.eden-foundation.org/products/code/film_date_stamp/index.html
3732 *
3733 * This tag could be in one of the below formats
3734 * 2005:08:17 11:42:43
3735 * THU OCT 26 16:46:04 2006
3736 * Mon Mar 3 09:44:56 2008
3737 *
3738 * FIXME: Our date tag doesn't include hours
3739 */
3740
3741 /* skip eventual initial whitespace */
3742 ptr = (gchar *) map.data;
3743 left = map.size;
3744
3745 while (left > 0 && g_ascii_isspace (ptr[0])) {
3746 ptr++;
3747 left--;
3748 }
3749
3750 if (left == 0) {
3751 goto non_parsable;
3752 }
3753
3754 /* make a safe copy to add a \0 to the end of the string */
3755 safedata = g_strndup (ptr, left);
3756
3757 /* test if the first char is a alpha or a number */
3758 if (g_ascii_isdigit (ptr[0])) {
3759 gst_avi_demux_parse_idit_nums_only (avi, safedata);
3760 g_free (safedata);
3761 gst_buffer_unmap (buf, &map);
3762 return;
3763 } else if (g_ascii_isalpha (ptr[0])) {
3764 gst_avi_demux_parse_idit_text (avi, safedata);
3765 g_free (safedata);
3766 gst_buffer_unmap (buf, &map);
3767 return;
3768 }
3769
3770 g_free (safedata);
3771
3772 non_parsable:
3773 GST_WARNING_OBJECT (avi, "IDIT tag has no parsable info");
3774 gst_buffer_unmap (buf, &map);
3775 }
3776
3777 static void
parse_tag_value(GstAviDemux * avi,GstTagList * taglist,const gchar * type,guint8 * ptr,guint tsize)3778 parse_tag_value (GstAviDemux * avi, GstTagList * taglist, const gchar * type,
3779 guint8 * ptr, guint tsize)
3780 {
3781 static const gchar *env_vars[] = { "GST_AVI_TAG_ENCODING",
3782 "GST_RIFF_TAG_ENCODING", "GST_TAG_ENCODING", NULL
3783 };
3784 GType tag_type;
3785 gchar *val;
3786
3787 tag_type = gst_tag_get_type (type);
3788 val = gst_tag_freeform_string_to_utf8 ((gchar *) ptr, tsize, env_vars);
3789
3790 if (val != NULL) {
3791 if (tag_type == G_TYPE_STRING) {
3792 gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, type, val, NULL);
3793 } else {
3794 GValue tag_val = { 0, };
3795
3796 g_value_init (&tag_val, tag_type);
3797 if (gst_value_deserialize (&tag_val, val)) {
3798 gst_tag_list_add_value (taglist, GST_TAG_MERGE_APPEND, type, &tag_val);
3799 } else {
3800 GST_WARNING_OBJECT (avi, "could not deserialize '%s' into a "
3801 "tag %s of type %s", val, type, g_type_name (tag_type));
3802 }
3803 g_value_unset (&tag_val);
3804 }
3805 g_free (val);
3806 } else {
3807 GST_WARNING_OBJECT (avi, "could not extract %s tag", type);
3808 }
3809 }
3810
3811 static void
gst_avi_demux_parse_strd(GstAviDemux * avi,GstBuffer * buf)3812 gst_avi_demux_parse_strd (GstAviDemux * avi, GstBuffer * buf)
3813 {
3814 GstMapInfo map;
3815 guint32 tag;
3816
3817 gst_buffer_map (buf, &map, GST_MAP_READ);
3818 if (map.size > 4) {
3819 guint8 *ptr = map.data;
3820 gsize left = map.size;
3821
3822 /* parsing based on
3823 * http://www.eden-foundation.org/products/code/film_date_stamp/index.html
3824 */
3825 tag = GST_READ_UINT32_LE (ptr);
3826 if ((tag == GST_MAKE_FOURCC ('A', 'V', 'I', 'F')) && (map.size > 98)) {
3827 gsize sub_size;
3828
3829 ptr += 98;
3830 left -= 98;
3831 if (!memcmp (ptr, "FUJIFILM", 8)) {
3832 GST_MEMDUMP_OBJECT (avi, "fujifim tag", ptr, 48);
3833
3834 ptr += 10;
3835 left -= 10;
3836 sub_size = 0;
3837 while (ptr[sub_size] && sub_size < left)
3838 sub_size++;
3839
3840 if (avi->globaltags == NULL)
3841 avi->globaltags = gst_tag_list_new_empty ();
3842
3843 gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_APPEND,
3844 GST_TAG_DEVICE_MANUFACTURER, "FUJIFILM", NULL);
3845 parse_tag_value (avi, avi->globaltags, GST_TAG_DEVICE_MODEL, ptr,
3846 sub_size);
3847
3848 while (ptr[sub_size] == '\0' && sub_size < left)
3849 sub_size++;
3850
3851 ptr += sub_size;
3852 left -= sub_size;
3853 sub_size = 0;
3854 while (ptr[sub_size] && sub_size < left)
3855 sub_size++;
3856 if (ptr[4] == ':')
3857 ptr[4] = '-';
3858 if (ptr[7] == ':')
3859 ptr[7] = '-';
3860
3861 parse_tag_value (avi, avi->globaltags, GST_TAG_DATE_TIME, ptr,
3862 sub_size);
3863 }
3864 }
3865 }
3866 gst_buffer_unmap (buf, &map);
3867 }
3868
3869 /*
3870 * gst_avi_demux_parse_ncdt:
3871 * @element: caller element (used for debugging/error).
3872 * @buf: input data to be used for parsing, stripped from header.
3873 * @taglist: a pointer to a taglist (returned by this function)
3874 * containing information about this stream. May be
3875 * NULL if no supported tags were found.
3876 *
3877 * Parses Nikon metadata from input data.
3878 */
3879 static void
gst_avi_demux_parse_ncdt(GstAviDemux * avi,GstBuffer * buf,GstTagList ** _taglist)3880 gst_avi_demux_parse_ncdt (GstAviDemux * avi, GstBuffer * buf,
3881 GstTagList ** _taglist)
3882 {
3883 GstMapInfo info;
3884 guint8 *ptr;
3885 gsize left;
3886 guint tsize;
3887 guint32 tag;
3888 const gchar *type;
3889 GstTagList *taglist;
3890
3891 g_return_if_fail (_taglist != NULL);
3892
3893 if (!buf) {
3894 *_taglist = NULL;
3895 return;
3896 }
3897 gst_buffer_map (buf, &info, GST_MAP_READ);
3898
3899 taglist = gst_tag_list_new_empty ();
3900
3901 ptr = info.data;
3902 left = info.size;
3903
3904 while (left > 8) {
3905 tag = GST_READ_UINT32_LE (ptr);
3906 tsize = GST_READ_UINT32_LE (ptr + 4);
3907
3908 GST_MEMDUMP_OBJECT (avi, "tag chunk", ptr, MIN (tsize + 8, left));
3909
3910 left -= 8;
3911 ptr += 8;
3912
3913 GST_DEBUG_OBJECT (avi, "tag %" GST_FOURCC_FORMAT ", size %u",
3914 GST_FOURCC_ARGS (tag), tsize);
3915
3916 if (tsize > left) {
3917 GST_WARNING_OBJECT (avi,
3918 "Tagsize %d is larger than available data %" G_GSIZE_FORMAT,
3919 tsize, left);
3920 tsize = left;
3921 }
3922
3923 /* find out the type of metadata */
3924 switch (tag) {
3925 case GST_RIFF_LIST_nctg:
3926 while (tsize > 4) {
3927 guint16 sub_tag = GST_READ_UINT16_LE (ptr);
3928 guint16 sub_size = GST_READ_UINT16_LE (ptr + 2);
3929
3930 tsize -= 4;
3931 ptr += 4;
3932 left -= 4;
3933
3934 if (sub_size > tsize)
3935 break;
3936
3937 GST_DEBUG_OBJECT (avi, "sub-tag %u, size %u", sub_tag, sub_size);
3938 /* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
3939 * for some reason the sub_tag has a +2 offset
3940 */
3941 switch (sub_tag) {
3942 case 0x03: /* Make */
3943 type = GST_TAG_DEVICE_MANUFACTURER;
3944 break;
3945 case 0x04: /* Model */
3946 type = GST_TAG_DEVICE_MODEL;
3947 break;
3948 /* TODO: 0x05: is software version, like V1.0 */
3949 case 0x06: /* Software */
3950 type = GST_TAG_ENCODER;
3951 break;
3952 case 0x13: /* CreationDate */
3953 type = GST_TAG_DATE_TIME;
3954 if (left > 7) {
3955 if (ptr[4] == ':')
3956 ptr[4] = '-';
3957 if (ptr[7] == ':')
3958 ptr[7] = '-';
3959 }
3960 break;
3961 default:
3962 type = NULL;
3963 break;
3964 }
3965 if (type != NULL && ptr[0] != '\0') {
3966 GST_DEBUG_OBJECT (avi, "mapped tag %u to tag %s", sub_tag, type);
3967
3968 parse_tag_value (avi, taglist, type, ptr, sub_size);
3969 }
3970
3971 ptr += sub_size;
3972 tsize -= sub_size;
3973 left -= sub_size;
3974 }
3975 break;
3976 default:
3977 type = NULL;
3978 GST_WARNING_OBJECT (avi,
3979 "Unknown ncdt (metadata) tag entry %" GST_FOURCC_FORMAT,
3980 GST_FOURCC_ARGS (tag));
3981 GST_MEMDUMP_OBJECT (avi, "Unknown ncdt", ptr, tsize);
3982 break;
3983 }
3984
3985 if (tsize & 1) {
3986 tsize++;
3987 if (tsize > left)
3988 tsize = left;
3989 }
3990
3991 ptr += tsize;
3992 left -= tsize;
3993 }
3994
3995 if (!gst_tag_list_is_empty (taglist)) {
3996 GST_INFO_OBJECT (avi, "extracted tags: %" GST_PTR_FORMAT, taglist);
3997 *_taglist = taglist;
3998 } else {
3999 *_taglist = NULL;
4000 gst_tag_list_unref (taglist);
4001 }
4002 gst_buffer_unmap (buf, &info);
4003
4004 return;
4005 }
4006
4007 /*
4008 * Read full AVI headers.
4009 */
4010 static GstFlowReturn
gst_avi_demux_stream_header_pull(GstAviDemux * avi)4011 gst_avi_demux_stream_header_pull (GstAviDemux * avi)
4012 {
4013 GstFlowReturn res;
4014 GstBuffer *buf, *sub = NULL;
4015 guint32 tag;
4016 guint offset = 4;
4017 GstElement *element = GST_ELEMENT_CAST (avi);
4018 GstClockTime stamp;
4019 GstTagList *tags = NULL;
4020 guint8 fourcc[4];
4021
4022 stamp = gst_util_get_timestamp ();
4023
4024 /* the header consists of a 'hdrl' LIST tag */
4025 res = gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag, &buf);
4026 if (res != GST_FLOW_OK)
4027 goto pull_range_failed;
4028 else if (tag != GST_RIFF_TAG_LIST)
4029 goto no_list;
4030 else if (gst_buffer_get_size (buf) < 4)
4031 goto no_header;
4032
4033 GST_DEBUG_OBJECT (avi, "parsing headers");
4034
4035 /* Find the 'hdrl' LIST tag */
4036 gst_buffer_extract (buf, 0, fourcc, 4);
4037 while (GST_READ_UINT32_LE (fourcc) != GST_RIFF_LIST_hdrl) {
4038 GST_LOG_OBJECT (avi, "buffer contains %" GST_FOURCC_FORMAT,
4039 GST_FOURCC_ARGS (GST_READ_UINT32_LE (fourcc)));
4040
4041 /* Eat up */
4042 gst_buffer_unref (buf);
4043
4044 /* read new chunk */
4045 res = gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag, &buf);
4046 if (res != GST_FLOW_OK)
4047 goto pull_range_failed;
4048 else if (tag != GST_RIFF_TAG_LIST)
4049 goto no_list;
4050 else if (gst_buffer_get_size (buf) < 4)
4051 goto no_header;
4052 gst_buffer_extract (buf, 0, fourcc, 4);
4053 }
4054
4055 GST_DEBUG_OBJECT (avi, "hdrl LIST tag found");
4056
4057 gst_avi_demux_roundup_list (avi, &buf);
4058
4059 /* the hdrl starts with a 'avih' header */
4060 if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub))
4061 goto no_avih;
4062 else if (tag != GST_RIFF_TAG_avih)
4063 goto no_avih;
4064 else if (!gst_avi_demux_parse_avih (avi, sub, &avi->avih))
4065 goto invalid_avih;
4066
4067 GST_DEBUG_OBJECT (avi, "AVI header ok, reading elements from header");
4068
4069 /* now, read the elements from the header until the end */
4070 while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
4071 GstMapInfo map;
4072
4073 /* sub can be NULL on empty tags */
4074 if (!sub)
4075 continue;
4076
4077 gst_buffer_map (sub, &map, GST_MAP_READ);
4078
4079 switch (tag) {
4080 case GST_RIFF_TAG_LIST:
4081 if (map.size < 4)
4082 goto next;
4083
4084 switch (GST_READ_UINT32_LE (map.data)) {
4085 case GST_RIFF_LIST_strl:
4086 gst_buffer_unmap (sub, &map);
4087 if (!(gst_avi_demux_parse_stream (avi, sub))) {
4088 GST_ELEMENT_WARNING (avi, STREAM, DEMUX, (NULL),
4089 ("failed to parse stream, ignoring"));
4090 sub = NULL;
4091 }
4092 sub = NULL;
4093 goto next;
4094 case GST_RIFF_LIST_odml:
4095 gst_buffer_unmap (sub, &map);
4096 gst_avi_demux_parse_odml (avi, sub);
4097 sub = NULL;
4098 break;
4099 case GST_RIFF_LIST_INFO:
4100 gst_buffer_unmap (sub, &map);
4101 gst_buffer_resize (sub, 4, -1);
4102 gst_riff_parse_info (element, sub, &tags);
4103 if (tags) {
4104 if (avi->globaltags) {
4105 gst_tag_list_insert (avi->globaltags, tags,
4106 GST_TAG_MERGE_REPLACE);
4107 gst_tag_list_unref (tags);
4108 } else {
4109 avi->globaltags = tags;
4110 }
4111 }
4112 tags = NULL;
4113 gst_buffer_unref (sub);
4114 sub = NULL;
4115 break;
4116 case GST_RIFF_LIST_ncdt:
4117 gst_buffer_unmap (sub, &map);
4118 gst_buffer_resize (sub, 4, -1);
4119 gst_avi_demux_parse_ncdt (avi, sub, &tags);
4120 if (tags) {
4121 if (avi->globaltags) {
4122 gst_tag_list_insert (avi->globaltags, tags,
4123 GST_TAG_MERGE_REPLACE);
4124 gst_tag_list_unref (tags);
4125 } else {
4126 avi->globaltags = tags;
4127 }
4128 }
4129 tags = NULL;
4130 gst_buffer_unref (sub);
4131 sub = NULL;
4132 break;
4133 default:
4134 GST_WARNING_OBJECT (avi,
4135 "Unknown list %" GST_FOURCC_FORMAT " in AVI header",
4136 GST_FOURCC_ARGS (GST_READ_UINT32_LE (map.data)));
4137 GST_MEMDUMP_OBJECT (avi, "Unknown list", map.data, map.size);
4138 /* fall-through */
4139 case GST_RIFF_TAG_JUNQ:
4140 case GST_RIFF_TAG_JUNK:
4141 goto next;
4142 }
4143 break;
4144 case GST_RIFF_IDIT:
4145 gst_avi_demux_parse_idit (avi, sub);
4146 goto next;
4147 default:
4148 GST_WARNING_OBJECT (avi,
4149 "Unknown tag %" GST_FOURCC_FORMAT " in AVI header",
4150 GST_FOURCC_ARGS (tag));
4151 GST_MEMDUMP_OBJECT (avi, "Unknown tag", map.data, map.size);
4152 /* fall-through */
4153 case GST_RIFF_TAG_JUNQ:
4154 case GST_RIFF_TAG_JUNK:
4155 next:
4156 if (sub) {
4157 gst_buffer_unmap (sub, &map);
4158 gst_buffer_unref (sub);
4159 }
4160 sub = NULL;
4161 break;
4162 }
4163 }
4164 gst_buffer_unref (buf);
4165 GST_DEBUG ("elements parsed");
4166
4167 /* check parsed streams */
4168 if (avi->num_streams == 0)
4169 goto no_streams;
4170 else if (avi->num_streams != avi->avih->streams) {
4171 GST_WARNING_OBJECT (avi,
4172 "Stream header mentioned %d streams, but %d available",
4173 avi->avih->streams, avi->num_streams);
4174 }
4175
4176 GST_DEBUG_OBJECT (avi, "skipping junk between header and data, offset=%"
4177 G_GUINT64_FORMAT, avi->offset);
4178
4179 /* Now, find the data (i.e. skip all junk between header and data) */
4180 do {
4181 GstMapInfo map;
4182 guint size;
4183 guint32 tag, ltag;
4184
4185 buf = NULL;
4186 res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
4187 if (res != GST_FLOW_OK) {
4188 GST_DEBUG_OBJECT (avi, "pull_range failure while looking for tags");
4189 goto pull_range_failed;
4190 } else if (gst_buffer_get_size (buf) < 12) {
4191 GST_DEBUG_OBJECT (avi,
4192 "got %" G_GSIZE_FORMAT " bytes which is less than 12 bytes",
4193 gst_buffer_get_size (buf));
4194 gst_buffer_unref (buf);
4195 return GST_FLOW_ERROR;
4196 }
4197
4198 gst_buffer_map (buf, &map, GST_MAP_READ);
4199 tag = GST_READ_UINT32_LE (map.data);
4200 size = GST_READ_UINT32_LE (map.data + 4);
4201 ltag = GST_READ_UINT32_LE (map.data + 8);
4202
4203 GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u",
4204 GST_FOURCC_ARGS (tag), size);
4205 GST_MEMDUMP ("Tag content", map.data, map.size);
4206 gst_buffer_unmap (buf, &map);
4207 gst_buffer_unref (buf);
4208
4209 switch (tag) {
4210 case GST_RIFF_TAG_LIST:{
4211 switch (ltag) {
4212 case GST_RIFF_LIST_movi:
4213 GST_DEBUG_OBJECT (avi,
4214 "Reached the 'movi' tag, we're done with skipping");
4215 goto skipping_done;
4216 case GST_RIFF_LIST_INFO:
4217 res =
4218 gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag,
4219 &buf);
4220 if (res != GST_FLOW_OK) {
4221 GST_DEBUG_OBJECT (avi, "couldn't read INFO chunk");
4222 goto pull_range_failed;
4223 }
4224 GST_DEBUG ("got size %" G_GSIZE_FORMAT, gst_buffer_get_size (buf));
4225 if (size < 4) {
4226 GST_DEBUG ("skipping INFO LIST prefix");
4227 avi->offset += (4 - GST_ROUND_UP_2 (size));
4228 gst_buffer_unref (buf);
4229 continue;
4230 }
4231
4232 sub = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, 4, -1);
4233 gst_riff_parse_info (element, sub, &tags);
4234 if (tags) {
4235 if (avi->globaltags) {
4236 gst_tag_list_insert (avi->globaltags, tags,
4237 GST_TAG_MERGE_REPLACE);
4238 gst_tag_list_unref (tags);
4239 } else {
4240 avi->globaltags = tags;
4241 }
4242 }
4243 tags = NULL;
4244 if (sub) {
4245 gst_buffer_unref (sub);
4246 sub = NULL;
4247 }
4248 gst_buffer_unref (buf);
4249 /* gst_riff_read_chunk() has already advanced avi->offset */
4250 break;
4251 case GST_RIFF_LIST_ncdt:
4252 res =
4253 gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag,
4254 &buf);
4255 if (res != GST_FLOW_OK) {
4256 GST_DEBUG_OBJECT (avi, "couldn't read ncdt chunk");
4257 goto pull_range_failed;
4258 }
4259 GST_DEBUG ("got size %" G_GSIZE_FORMAT, gst_buffer_get_size (buf));
4260 if (size < 4) {
4261 GST_DEBUG ("skipping ncdt LIST prefix");
4262 avi->offset += (4 - GST_ROUND_UP_2 (size));
4263 gst_buffer_unref (buf);
4264 continue;
4265 }
4266
4267 sub = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, 4, -1);
4268 gst_avi_demux_parse_ncdt (avi, sub, &tags);
4269 if (tags) {
4270 if (avi->globaltags) {
4271 gst_tag_list_insert (avi->globaltags, tags,
4272 GST_TAG_MERGE_REPLACE);
4273 gst_tag_list_unref (tags);
4274 } else {
4275 avi->globaltags = tags;
4276 }
4277 }
4278 tags = NULL;
4279 if (sub) {
4280 gst_buffer_unref (sub);
4281 sub = NULL;
4282 }
4283 gst_buffer_unref (buf);
4284 /* gst_riff_read_chunk() has already advanced avi->offset */
4285 break;
4286 default:
4287 GST_WARNING_OBJECT (avi,
4288 "Skipping unknown list tag %" GST_FOURCC_FORMAT,
4289 GST_FOURCC_ARGS (ltag));
4290 avi->offset += 8 + GST_ROUND_UP_2 (size);
4291 break;
4292 }
4293 }
4294 break;
4295 default:
4296 GST_WARNING_OBJECT (avi, "Skipping unknown tag %" GST_FOURCC_FORMAT,
4297 GST_FOURCC_ARGS (tag));
4298 /* Fall-through */
4299 case GST_MAKE_FOURCC ('J', 'U', 'N', 'Q'):
4300 case GST_MAKE_FOURCC ('J', 'U', 'N', 'K'):
4301 /* Only get buffer for debugging if the memdump is needed */
4302 if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= 9) {
4303 buf = NULL;
4304 res = gst_pad_pull_range (avi->sinkpad, avi->offset, size, &buf);
4305 if (res != GST_FLOW_OK) {
4306 GST_DEBUG_OBJECT (avi, "couldn't read INFO chunk");
4307 goto pull_range_failed;
4308 }
4309 gst_buffer_map (buf, &map, GST_MAP_READ);
4310 GST_MEMDUMP ("Junk", map.data, map.size);
4311 gst_buffer_unmap (buf, &map);
4312 gst_buffer_unref (buf);
4313 }
4314 avi->offset += 8 + GST_ROUND_UP_2 (size);
4315 break;
4316 }
4317 } while (1);
4318 skipping_done:
4319
4320 GST_DEBUG_OBJECT (avi, "skipping done ... (streams=%u, stream[0].indexes=%p)",
4321 avi->num_streams, avi->stream[0].indexes);
4322
4323 /* create or read stream index (for seeking) */
4324 if (avi->stream[0].indexes != NULL) {
4325 /* we read a super index already (gst_avi_demux_parse_superindex() ) */
4326 gst_avi_demux_read_subindexes_pull (avi);
4327 }
4328 if (!avi->have_index) {
4329 if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX)
4330 gst_avi_demux_stream_index (avi);
4331
4332 /* still no index, scan */
4333 if (!avi->have_index) {
4334 gst_avi_demux_stream_scan (avi);
4335
4336 /* still no index.. this is a fatal error for now.
4337 * FIXME, we should switch to plain push mode without seeking
4338 * instead of failing. */
4339 if (!avi->have_index)
4340 goto no_index;
4341 }
4342 }
4343 /* use the indexes now to construct nice durations */
4344 gst_avi_demux_calculate_durations_from_index (avi);
4345
4346 gst_avi_demux_expose_streams (avi, FALSE);
4347
4348 /* do initial seek to the default segment values */
4349 gst_avi_demux_do_seek (avi, &avi->segment, 0);
4350
4351 /* create initial NEWSEGMENT event */
4352 if (avi->seg_event)
4353 gst_event_unref (avi->seg_event);
4354 avi->seg_event = gst_event_new_segment (&avi->segment);
4355 if (avi->segment_seqnum)
4356 gst_event_set_seqnum (avi->seg_event, avi->segment_seqnum);
4357
4358 stamp = gst_util_get_timestamp () - stamp;
4359 GST_DEBUG_OBJECT (avi, "pulling header took %" GST_TIME_FORMAT,
4360 GST_TIME_ARGS (stamp));
4361
4362 /* at this point we know all the streams and we can signal the no more
4363 * pads signal */
4364 GST_DEBUG_OBJECT (avi, "signaling no more pads");
4365 gst_element_no_more_pads (GST_ELEMENT_CAST (avi));
4366
4367 return GST_FLOW_OK;
4368
4369 /* ERRORS */
4370 no_list:
4371 {
4372 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4373 ("Invalid AVI header (no LIST at start): %"
4374 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
4375 gst_buffer_unref (buf);
4376 return GST_FLOW_ERROR;
4377 }
4378 no_header:
4379 {
4380 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4381 ("Invalid AVI header (no hdrl at start): %"
4382 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
4383 gst_buffer_unref (buf);
4384 return GST_FLOW_ERROR;
4385 }
4386 no_avih:
4387 {
4388 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4389 ("Invalid AVI header (no avih at start): %"
4390 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
4391 if (sub)
4392 gst_buffer_unref (sub);
4393 gst_buffer_unref (buf);
4394 return GST_FLOW_ERROR;
4395 }
4396 invalid_avih:
4397 {
4398 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4399 ("Invalid AVI header (cannot parse avih at start)"));
4400 gst_buffer_unref (buf);
4401 return GST_FLOW_ERROR;
4402 }
4403 no_streams:
4404 {
4405 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
4406 return GST_FLOW_ERROR;
4407 }
4408 no_index:
4409 {
4410 GST_WARNING ("file without or too big index");
4411 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4412 ("Could not get/create index"));
4413 return GST_FLOW_ERROR;
4414 }
4415 pull_range_failed:
4416 {
4417 if (res == GST_FLOW_FLUSHING)
4418 return res;
4419 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4420 ("pull_range flow reading header: %s", gst_flow_get_name (res)));
4421 return res;
4422 }
4423 }
4424
4425 /* move a stream to @index */
4426 static void
gst_avi_demux_move_stream(GstAviDemux * avi,GstAviStream * stream,GstSegment * segment,guint index)4427 gst_avi_demux_move_stream (GstAviDemux * avi, GstAviStream * stream,
4428 GstSegment * segment, guint index)
4429 {
4430 GST_DEBUG_OBJECT (avi, "Move stream %d to %u", stream->num, index);
4431
4432 if (segment->rate < 0.0) {
4433 guint next_key;
4434 /* Because we don't know the frame order we need to push from the prev keyframe
4435 * to the next keyframe. If there is a smart decoder downstream he will notice
4436 * that there are too many encoded frames send and return EOS when there
4437 * are enough decoded frames to fill the segment. */
4438 next_key = gst_avi_demux_index_next (avi, stream, index, TRUE);
4439
4440 /* FIXME, we go back to 0, we should look at segment.start. We will however
4441 * stop earlier when the see the timestamp < segment.start */
4442 stream->start_entry = 0;
4443 stream->step_entry = index;
4444 stream->current_entry = index;
4445 stream->stop_entry = next_key;
4446
4447 GST_DEBUG_OBJECT (avi, "reverse seek: start %u, step %u, stop %u",
4448 stream->start_entry, stream->step_entry, stream->stop_entry);
4449 } else {
4450 stream->start_entry = index;
4451 stream->step_entry = index;
4452 stream->stop_entry = gst_avi_demux_index_last (avi, stream);
4453 }
4454 if (stream->current_entry != index) {
4455 GST_DEBUG_OBJECT (avi, "Move DISCONT from %u to %u",
4456 stream->current_entry, index);
4457 stream->current_entry = index;
4458 stream->discont = TRUE;
4459 }
4460
4461 /* update the buffer info */
4462 gst_avi_demux_get_buffer_info (avi, stream, index,
4463 &stream->current_timestamp, &stream->current_ts_end,
4464 &stream->current_offset, &stream->current_offset_end);
4465
4466 GST_DEBUG_OBJECT (avi, "Moved to %u, ts %" GST_TIME_FORMAT
4467 ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
4468 ", off_end %" G_GUINT64_FORMAT, index,
4469 GST_TIME_ARGS (stream->current_timestamp),
4470 GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
4471 stream->current_offset_end);
4472
4473 GST_DEBUG_OBJECT (avi, "Seeking to offset %" G_GUINT64_FORMAT,
4474 stream->index[index].offset);
4475 }
4476
4477 /*
4478 * Do the actual seeking.
4479 */
4480 static gboolean
gst_avi_demux_do_seek(GstAviDemux * avi,GstSegment * segment,GstSeekFlags flags)4481 gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment,
4482 GstSeekFlags flags)
4483 {
4484 GstClockTime seek_time;
4485 gboolean keyframe, before, after;
4486 guint i, index;
4487 GstAviStream *stream;
4488 gboolean next;
4489
4490 seek_time = segment->position;
4491 keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
4492 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
4493 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
4494
4495 GST_DEBUG_OBJECT (avi, "seek to: %" GST_TIME_FORMAT
4496 " keyframe seeking:%d, %s", GST_TIME_ARGS (seek_time), keyframe,
4497 snap_types[before ? 1 : 0][after ? 1 : 0]);
4498
4499 /* FIXME, this code assumes the main stream with keyframes is stream 0,
4500 * which is mostly correct... */
4501 stream = &avi->stream[avi->main_stream];
4502
4503 next = after && !before;
4504 if (segment->rate < 0)
4505 next = !next;
4506
4507 /* get the entry index for the requested position */
4508 index = gst_avi_demux_index_for_time (avi, stream, seek_time, next);
4509 GST_DEBUG_OBJECT (avi, "Got entry %u", index);
4510 if (index == -1)
4511 return FALSE;
4512
4513 /* check if we are already on a keyframe */
4514 if (!ENTRY_IS_KEYFRAME (&stream->index[index])) {
4515 if (next) {
4516 GST_DEBUG_OBJECT (avi, "not keyframe, searching forward");
4517 /* now go to the next keyframe, this is where we should start
4518 * decoding from. */
4519 index = gst_avi_demux_index_next (avi, stream, index, TRUE);
4520 GST_DEBUG_OBJECT (avi, "next keyframe at %u", index);
4521 } else {
4522 GST_DEBUG_OBJECT (avi, "not keyframe, searching back");
4523 /* now go to the previous keyframe, this is where we should start
4524 * decoding from. */
4525 index = gst_avi_demux_index_prev (avi, stream, index, TRUE);
4526 GST_DEBUG_OBJECT (avi, "previous keyframe at %u", index);
4527 }
4528 }
4529
4530 /* move the main stream to this position */
4531 gst_avi_demux_move_stream (avi, stream, segment, index);
4532
4533 if (keyframe) {
4534 /* when seeking to a keyframe, we update the result seek time
4535 * to the time of the keyframe. */
4536 seek_time = stream->current_timestamp;
4537 GST_DEBUG_OBJECT (avi, "keyframe adjusted to %" GST_TIME_FORMAT,
4538 GST_TIME_ARGS (seek_time));
4539 /* the seek time is always the position ... */
4540 segment->position = seek_time;
4541 /* ... and start and stream time when going forwards,
4542 * otherwise only stop time */
4543 if (segment->rate > 0.0)
4544 segment->start = segment->time = seek_time;
4545 else
4546 segment->stop = seek_time;
4547 }
4548
4549 /* now set DISCONT and align the other streams */
4550 for (i = 0; i < avi->num_streams; i++) {
4551 GstAviStream *ostream;
4552
4553 ostream = &avi->stream[i];
4554 if ((ostream == stream) || (ostream->index == NULL))
4555 continue;
4556
4557 /* get the entry index for the requested position */
4558 index = gst_avi_demux_index_for_time (avi, ostream, seek_time, FALSE);
4559 if (index == -1)
4560 continue;
4561
4562 /* move to previous keyframe */
4563 if (!ENTRY_IS_KEYFRAME (&ostream->index[index]))
4564 index = gst_avi_demux_index_prev (avi, ostream, index, TRUE);
4565
4566 gst_avi_demux_move_stream (avi, ostream, segment, index);
4567 }
4568 GST_DEBUG_OBJECT (avi, "done seek to: %" GST_TIME_FORMAT,
4569 GST_TIME_ARGS (seek_time));
4570
4571 return TRUE;
4572 }
4573
4574 /*
4575 * Handle seek event in pull mode.
4576 */
4577 static gboolean
gst_avi_demux_handle_seek(GstAviDemux * avi,GstPad * pad,GstEvent * event)4578 gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
4579 {
4580 gdouble rate;
4581 GstFormat format;
4582 GstSeekFlags flags;
4583 GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
4584 gint64 cur, stop;
4585 gboolean flush;
4586 gboolean update;
4587 GstSegment seeksegment = { 0, };
4588 gint i;
4589 guint32 seqnum = 0;
4590
4591 if (event) {
4592 GST_DEBUG_OBJECT (avi, "doing seek with event");
4593
4594 gst_event_parse_seek (event, &rate, &format, &flags,
4595 &cur_type, &cur, &stop_type, &stop);
4596 seqnum = gst_event_get_seqnum (event);
4597
4598 /* we have to have a format as the segment format. Try to convert
4599 * if not. */
4600 if (format != GST_FORMAT_TIME) {
4601 gboolean res = TRUE;
4602
4603 if (cur_type != GST_SEEK_TYPE_NONE)
4604 res = gst_pad_query_convert (pad, format, cur, GST_FORMAT_TIME, &cur);
4605 if (res && stop_type != GST_SEEK_TYPE_NONE)
4606 res = gst_pad_query_convert (pad, format, stop, GST_FORMAT_TIME, &stop);
4607 if (!res)
4608 goto no_format;
4609
4610 format = GST_FORMAT_TIME;
4611 }
4612 GST_DEBUG_OBJECT (avi,
4613 "seek requested: rate %g cur %" GST_TIME_FORMAT " stop %"
4614 GST_TIME_FORMAT, rate, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
4615 /* FIXME: can we do anything with rate!=1.0 */
4616 } else {
4617 GST_DEBUG_OBJECT (avi, "doing seek without event");
4618 flags = 0;
4619 rate = 1.0;
4620 }
4621
4622 /* save flush flag */
4623 flush = flags & GST_SEEK_FLAG_FLUSH;
4624
4625 if (flush) {
4626 GstEvent *fevent = gst_event_new_flush_start ();
4627
4628 if (seqnum)
4629 gst_event_set_seqnum (fevent, seqnum);
4630 /* for a flushing seek, we send a flush_start on all pads. This will
4631 * eventually stop streaming with a WRONG_STATE. We can thus eventually
4632 * take the STREAM_LOCK. */
4633 GST_DEBUG_OBJECT (avi, "sending flush start");
4634 gst_avi_demux_push_event (avi, gst_event_ref (fevent));
4635 gst_pad_push_event (avi->sinkpad, fevent);
4636 } else {
4637 /* a non-flushing seek, we PAUSE the task so that we can take the
4638 * STREAM_LOCK */
4639 GST_DEBUG_OBJECT (avi, "non flushing seek, pausing task");
4640 gst_pad_pause_task (avi->sinkpad);
4641 }
4642
4643 /* wait for streaming to stop */
4644 GST_DEBUG_OBJECT (avi, "wait for streaming to stop");
4645 GST_PAD_STREAM_LOCK (avi->sinkpad);
4646
4647 /* copy segment, we need this because we still need the old
4648 * segment when we close the current segment. */
4649 memcpy (&seeksegment, &avi->segment, sizeof (GstSegment));
4650
4651 if (event) {
4652 GST_DEBUG_OBJECT (avi, "configuring seek");
4653 gst_segment_do_seek (&seeksegment, rate, format, flags,
4654 cur_type, cur, stop_type, stop, &update);
4655 }
4656 /* do the seek, seeksegment.position contains the new position, this
4657 * actually never fails. */
4658 gst_avi_demux_do_seek (avi, &seeksegment, flags);
4659
4660 if (flush) {
4661 GstEvent *fevent = gst_event_new_flush_stop (TRUE);
4662
4663 if (seqnum)
4664 gst_event_set_seqnum (fevent, seqnum);
4665
4666 GST_DEBUG_OBJECT (avi, "sending flush stop");
4667 gst_avi_demux_push_event (avi, gst_event_ref (fevent));
4668 gst_pad_push_event (avi->sinkpad, fevent);
4669 }
4670
4671 /* now update the real segment info */
4672 memcpy (&avi->segment, &seeksegment, sizeof (GstSegment));
4673
4674 /* post the SEGMENT_START message when we do segmented playback */
4675 if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4676 GstMessage *segment_start_msg =
4677 gst_message_new_segment_start (GST_OBJECT_CAST (avi),
4678 avi->segment.format, avi->segment.position);
4679 if (seqnum)
4680 gst_message_set_seqnum (segment_start_msg, seqnum);
4681 gst_element_post_message (GST_ELEMENT_CAST (avi), segment_start_msg);
4682 }
4683
4684 /* queue the segment event for the streaming thread. */
4685 if (avi->seg_event)
4686 gst_event_unref (avi->seg_event);
4687 avi->seg_event = gst_event_new_segment (&avi->segment);
4688 if (seqnum)
4689 gst_event_set_seqnum (avi->seg_event, seqnum);
4690 avi->segment_seqnum = seqnum;
4691
4692 if (!avi->streaming) {
4693 gst_pad_start_task (avi->sinkpad, (GstTaskFunction) gst_avi_demux_loop,
4694 avi->sinkpad, NULL);
4695 }
4696 /* reset the last flow and mark discont, seek is always DISCONT */
4697 for (i = 0; i < avi->num_streams; i++) {
4698 GST_DEBUG_OBJECT (avi, "marking DISCONT");
4699 avi->stream[i].discont = TRUE;
4700 }
4701 /* likewise for the whole new segment */
4702 gst_flow_combiner_reset (avi->flowcombiner);
4703 GST_PAD_STREAM_UNLOCK (avi->sinkpad);
4704
4705 return TRUE;
4706
4707 /* ERRORS */
4708 no_format:
4709 {
4710 GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted.");
4711 return FALSE;
4712 }
4713 }
4714
4715 /*
4716 * Handle seek event in push mode.
4717 */
4718 static gboolean
avi_demux_handle_seek_push(GstAviDemux * avi,GstPad * pad,GstEvent * event)4719 avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, GstEvent * event)
4720 {
4721 gdouble rate;
4722 GstFormat format;
4723 GstSeekFlags flags;
4724 GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
4725 gint64 cur, stop;
4726 gboolean keyframe, before, after, next;
4727 GstAviStream *stream;
4728 guint index;
4729 guint n, str_num;
4730 guint64 min_offset;
4731 GstSegment seeksegment;
4732 gboolean update;
4733
4734 /* check we have the index */
4735 if (!avi->have_index) {
4736 GST_DEBUG_OBJECT (avi, "no seek index built, seek aborted.");
4737 return FALSE;
4738 } else {
4739 GST_DEBUG_OBJECT (avi, "doing push-based seek with event");
4740 }
4741
4742 gst_event_parse_seek (event, &rate, &format, &flags,
4743 &cur_type, &cur, &stop_type, &stop);
4744
4745 if (format != GST_FORMAT_TIME) {
4746 gboolean res = TRUE;
4747
4748 if (cur_type != GST_SEEK_TYPE_NONE)
4749 res = gst_pad_query_convert (pad, format, cur, GST_FORMAT_TIME, &cur);
4750 if (res && stop_type != GST_SEEK_TYPE_NONE)
4751 res = gst_pad_query_convert (pad, format, stop, GST_FORMAT_TIME, &stop);
4752 if (!res) {
4753 GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted.");
4754 return FALSE;
4755 }
4756
4757 format = GST_FORMAT_TIME;
4758 }
4759
4760 /* let gst_segment handle any tricky stuff */
4761 GST_DEBUG_OBJECT (avi, "configuring seek");
4762 memcpy (&seeksegment, &avi->segment, sizeof (GstSegment));
4763 gst_segment_do_seek (&seeksegment, rate, format, flags,
4764 cur_type, cur, stop_type, stop, &update);
4765
4766 keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
4767 cur = seeksegment.position;
4768 before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
4769 after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);
4770
4771 GST_DEBUG_OBJECT (avi,
4772 "Seek requested: ts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT
4773 ", kf %u, %s, rate %lf", GST_TIME_ARGS (cur), GST_TIME_ARGS (stop),
4774 keyframe, snap_types[before ? 1 : 0][after ? 1 : 0], rate);
4775
4776 if (rate < 0) {
4777 GST_DEBUG_OBJECT (avi, "negative rate seek not supported in push mode");
4778 return FALSE;
4779 }
4780
4781 /* FIXME, this code assumes the main stream with keyframes is stream 0,
4782 * which is mostly correct... */
4783 str_num = avi->main_stream;
4784 stream = &avi->stream[str_num];
4785
4786 next = after && !before;
4787 if (seeksegment.rate < 0)
4788 next = !next;
4789
4790 /* get the entry index for the requested position */
4791 index = gst_avi_demux_index_for_time (avi, stream, cur, next);
4792 GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT,
4793 str_num, index, GST_TIME_ARGS (cur));
4794 if (index == -1)
4795 return -1;
4796
4797 /* check if we are already on a keyframe */
4798 if (!ENTRY_IS_KEYFRAME (&stream->index[index])) {
4799 if (next) {
4800 GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching forward");
4801 /* now go to the next keyframe, this is where we should start
4802 * decoding from. */
4803 index = gst_avi_demux_index_next (avi, stream, index, TRUE);
4804 GST_DEBUG_OBJECT (avi, "Found next keyframe at %u", index);
4805 } else {
4806 GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back");
4807 /* now go to the previous keyframe, this is where we should start
4808 * decoding from. */
4809 index = gst_avi_demux_index_prev (avi, stream, index, TRUE);
4810 GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", index);
4811 }
4812 }
4813
4814 gst_avi_demux_get_buffer_info (avi, stream, index,
4815 &stream->current_timestamp, &stream->current_ts_end,
4816 &stream->current_offset, &stream->current_offset_end);
4817
4818 /* re-use cur to be the timestamp of the seek as it _will_ be */
4819 cur = stream->current_timestamp;
4820
4821 min_offset = stream->index[index].offset;
4822 avi->seek_kf_offset = min_offset - 8;
4823
4824 GST_DEBUG_OBJECT (avi,
4825 "Seek to: ts %" GST_TIME_FORMAT " (on str %u, idx %u, offset %"
4826 G_GUINT64_FORMAT ")", GST_TIME_ARGS (stream->current_timestamp), str_num,
4827 index, min_offset);
4828
4829 for (n = 0; n < avi->num_streams; n++) {
4830 GstAviStream *str = &avi->stream[n];
4831 guint idx;
4832
4833 if (n == avi->main_stream)
4834 continue;
4835
4836 /* get the entry index for the requested position */
4837 idx = gst_avi_demux_index_for_time (avi, str, cur, FALSE);
4838 GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT, n,
4839 idx, GST_TIME_ARGS (cur));
4840 if (idx == -1)
4841 continue;
4842
4843 /* check if we are already on a keyframe */
4844 if (!ENTRY_IS_KEYFRAME (&str->index[idx])) {
4845 if (next) {
4846 GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching forward");
4847 /* now go to the next keyframe, this is where we should start
4848 * decoding from. */
4849 idx = gst_avi_demux_index_next (avi, str, idx, TRUE);
4850 GST_DEBUG_OBJECT (avi, "Found next keyframe at %u", idx);
4851 } else {
4852 GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back");
4853 /* now go to the previous keyframe, this is where we should start
4854 * decoding from. */
4855 idx = gst_avi_demux_index_prev (avi, str, idx, TRUE);
4856 GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", idx);
4857 }
4858 }
4859
4860 gst_avi_demux_get_buffer_info (avi, str, idx,
4861 &str->current_timestamp, &str->current_ts_end,
4862 &str->current_offset, &str->current_offset_end);
4863
4864 if (str->index[idx].offset < min_offset) {
4865 min_offset = str->index[idx].offset;
4866 GST_DEBUG_OBJECT (avi,
4867 "Found an earlier offset at %" G_GUINT64_FORMAT ", str %u",
4868 min_offset, n);
4869 str_num = n;
4870 stream = str;
4871 index = idx;
4872 }
4873 }
4874
4875 GST_DEBUG_OBJECT (avi,
4876 "Seek performed: str %u, offset %" G_GUINT64_FORMAT ", idx %u, ts %"
4877 GST_TIME_FORMAT ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
4878 ", off_end %" G_GUINT64_FORMAT, str_num, min_offset, index,
4879 GST_TIME_ARGS (stream->current_timestamp),
4880 GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
4881 stream->current_offset_end);
4882
4883 /* index data refers to data, not chunk header (for pull mode convenience) */
4884 min_offset -= 8;
4885 GST_DEBUG_OBJECT (avi, "seeking to chunk at offset %" G_GUINT64_FORMAT,
4886 min_offset);
4887
4888 if (!perform_seek_to_offset (avi, min_offset, gst_event_get_seqnum (event))) {
4889 GST_DEBUG_OBJECT (avi, "seek event failed!");
4890 return FALSE;
4891 }
4892
4893 return TRUE;
4894 }
4895
4896 /*
4897 * Handle whether we can perform the seek event or if we have to let the chain
4898 * function handle seeks to build the seek indexes first.
4899 */
4900 static gboolean
gst_avi_demux_handle_seek_push(GstAviDemux * avi,GstPad * pad,GstEvent * event)4901 gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
4902 GstEvent * event)
4903 {
4904 /* check for having parsed index already */
4905 if (!avi->have_index) {
4906 guint64 offset = 0;
4907 gboolean building_index;
4908
4909 GST_OBJECT_LOCK (avi);
4910 /* handle the seek event in the chain function */
4911 avi->state = GST_AVI_DEMUX_SEEK;
4912
4913 /* copy the event */
4914 if (avi->seek_event)
4915 gst_event_unref (avi->seek_event);
4916 avi->seek_event = gst_event_ref (event);
4917
4918 /* set the building_index flag so that only one thread can setup the
4919 * structures for index seeking. */
4920 building_index = avi->building_index;
4921 if (!building_index) {
4922 avi->building_index = TRUE;
4923 if (avi->stream[0].indexes) {
4924 avi->odml_stream = 0;
4925 avi->odml_subidxs = avi->stream[avi->odml_stream].indexes;
4926 offset = avi->odml_subidxs[0];
4927 } else {
4928 offset = avi->idx1_offset;
4929 }
4930 }
4931 GST_OBJECT_UNLOCK (avi);
4932
4933 if (!building_index) {
4934 /* seek to the first subindex or legacy index */
4935 GST_INFO_OBJECT (avi,
4936 "Seeking to legacy index/first subindex at %" G_GUINT64_FORMAT,
4937 offset);
4938 return perform_seek_to_offset (avi, offset, gst_event_get_seqnum (event));
4939 }
4940
4941 /* FIXME: we have to always return true so that we don't block the seek
4942 * thread.
4943 * Note: maybe it is OK to return true if we're still building the index */
4944 return TRUE;
4945 }
4946
4947 return avi_demux_handle_seek_push (avi, pad, event);
4948 }
4949
4950 /*
4951 * Helper for gst_avi_demux_invert()
4952 */
4953 static inline void
swap_line(guint8 * d1,guint8 * d2,guint8 * tmp,gint bytes)4954 swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes)
4955 {
4956 memcpy (tmp, d1, bytes);
4957 memcpy (d1, d2, bytes);
4958 memcpy (d2, tmp, bytes);
4959 }
4960
4961
4962 #define gst_avi_demux_is_uncompressed(fourcc) \
4963 (fourcc == GST_RIFF_DIB || \
4964 fourcc == GST_RIFF_rgb || \
4965 fourcc == GST_RIFF_RGB || fourcc == GST_RIFF_RAW)
4966
4967 /*
4968 * Invert DIB buffers... Takes existing buffer and
4969 * returns either the buffer or a new one (with old
4970 * one dereferenced).
4971 * FIXME: can't we preallocate tmp? and remember stride, bpp?
4972 */
4973 static GstBuffer *
gst_avi_demux_invert(GstAviStream * stream,GstBuffer * buf)4974 gst_avi_demux_invert (GstAviStream * stream, GstBuffer * buf)
4975 {
4976 guint y, w, h;
4977 guint bpp, stride;
4978 guint8 *tmp = NULL;
4979 GstMapInfo map;
4980 guint32 fourcc;
4981
4982 if (stream->strh->type != GST_RIFF_FCC_vids)
4983 return buf;
4984
4985 if (stream->strf.vids == NULL) {
4986 GST_WARNING ("Failed to retrieve vids for stream");
4987 return buf;
4988 }
4989
4990 fourcc = (stream->strf.vids->compression) ?
4991 stream->strf.vids->compression : stream->strh->fcc_handler;
4992 if (!gst_avi_demux_is_uncompressed (fourcc)) {
4993 return buf; /* Ignore non DIB buffers */
4994 }
4995
4996 /* raw rgb data is stored topdown, but instead of inverting the buffer, */
4997 /* some tools just negate the height field in the header (e.g. ffmpeg) */
4998 if (((gint32) stream->strf.vids->height) < 0)
4999 return buf;
5000
5001 h = stream->strf.vids->height;
5002 w = stream->strf.vids->width;
5003 bpp = stream->strf.vids->bit_cnt ? stream->strf.vids->bit_cnt : 8;
5004
5005 if ((guint64) w * ((guint64) bpp / 8) > G_MAXUINT - 4) {
5006 GST_WARNING ("Width x stride overflows");
5007 return buf;
5008 }
5009
5010 if (w == 0 || h == 0) {
5011 GST_WARNING ("Zero width or height");
5012 return buf;
5013 }
5014
5015 stride = GST_ROUND_UP_4 (w * (bpp / 8));
5016
5017 buf = gst_buffer_make_writable (buf);
5018
5019 gst_buffer_map (buf, &map, GST_MAP_READWRITE);
5020 if (map.size < ((guint64) stride * (guint64) h)) {
5021 GST_WARNING ("Buffer is smaller than reported Width x Height x Depth");
5022 gst_buffer_unmap (buf, &map);
5023 return buf;
5024 }
5025
5026 tmp = g_malloc (stride);
5027
5028 for (y = 0; y < h / 2; y++) {
5029 swap_line (map.data + stride * y, map.data + stride * (h - 1 - y), tmp,
5030 stride);
5031 }
5032
5033 g_free (tmp);
5034
5035 gst_buffer_unmap (buf, &map);
5036
5037 /* append palette to paletted RGB8 buffer data */
5038 if (stream->rgb8_palette != NULL)
5039 buf = gst_buffer_append (buf, gst_buffer_ref (stream->rgb8_palette));
5040
5041 return buf;
5042 }
5043
5044 #if 0
5045 static void
5046 gst_avi_demux_add_assoc (GstAviDemux * avi, GstAviStream * stream,
5047 GstClockTime timestamp, guint64 offset, gboolean keyframe)
5048 {
5049 /* do not add indefinitely for open-ended streaming */
5050 if (G_UNLIKELY (avi->element_index && avi->seekable)) {
5051 GST_LOG_OBJECT (avi, "adding association %" GST_TIME_FORMAT "-> %"
5052 G_GUINT64_FORMAT, GST_TIME_ARGS (timestamp), offset);
5053 gst_index_add_association (avi->element_index, avi->index_id,
5054 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
5055 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, timestamp,
5056 GST_FORMAT_BYTES, offset, NULL);
5057 /* current_entry is DEFAULT (frame #) */
5058 gst_index_add_association (avi->element_index, stream->index_id,
5059 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
5060 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, timestamp,
5061 GST_FORMAT_BYTES, offset, GST_FORMAT_DEFAULT, stream->current_entry,
5062 NULL);
5063 }
5064 }
5065 #endif
5066
5067 /*
5068 * Returns the aggregated GstFlowReturn.
5069 */
5070 static GstFlowReturn
gst_avi_demux_combine_flows(GstAviDemux * avi,GstAviStream * stream,GstFlowReturn ret)5071 gst_avi_demux_combine_flows (GstAviDemux * avi, GstAviStream * stream,
5072 GstFlowReturn ret)
5073 {
5074 GST_LOG_OBJECT (avi, "Stream %s:%s flow return: %s",
5075 GST_DEBUG_PAD_NAME (stream->pad), gst_flow_get_name (ret));
5076 ret = gst_flow_combiner_update_pad_flow (avi->flowcombiner, stream->pad, ret);
5077 GST_LOG_OBJECT (avi, "combined to return %s", gst_flow_get_name (ret));
5078
5079 return ret;
5080 }
5081
5082 /* move @stream to the next position in its index */
5083 static GstFlowReturn
gst_avi_demux_advance(GstAviDemux * avi,GstAviStream * stream,GstFlowReturn ret)5084 gst_avi_demux_advance (GstAviDemux * avi, GstAviStream * stream,
5085 GstFlowReturn ret)
5086 {
5087 guint old_entry, new_entry;
5088
5089 old_entry = stream->current_entry;
5090 /* move forwards */
5091 new_entry = old_entry + 1;
5092
5093 /* see if we reached the end */
5094 if (new_entry >= stream->stop_entry) {
5095 if (avi->segment.rate < 0.0) {
5096 if (stream->step_entry == stream->start_entry) {
5097 /* we stepped all the way to the start, eos */
5098 GST_DEBUG_OBJECT (avi, "reverse reached start %u", stream->start_entry);
5099 goto eos;
5100 }
5101 /* backwards, stop becomes step, find a new step */
5102 stream->stop_entry = stream->step_entry;
5103 stream->step_entry = gst_avi_demux_index_prev (avi, stream,
5104 stream->stop_entry, TRUE);
5105
5106 GST_DEBUG_OBJECT (avi,
5107 "reverse playback jump: start %u, step %u, stop %u",
5108 stream->start_entry, stream->step_entry, stream->stop_entry);
5109
5110 /* and start from the previous keyframe now */
5111 new_entry = stream->step_entry;
5112 } else {
5113 /* EOS */
5114 GST_DEBUG_OBJECT (avi, "forward reached stop %u", stream->stop_entry);
5115 goto eos;
5116 }
5117 }
5118
5119 if (new_entry != old_entry) {
5120 stream->current_entry = new_entry;
5121 stream->current_total = stream->index[new_entry].total;
5122
5123 if (new_entry == old_entry + 1) {
5124 GST_DEBUG_OBJECT (avi, "moved forwards from %u to %u",
5125 old_entry, new_entry);
5126 /* we simply moved one step forwards, reuse current info */
5127 stream->current_timestamp = stream->current_ts_end;
5128 stream->current_offset = stream->current_offset_end;
5129 gst_avi_demux_get_buffer_info (avi, stream, new_entry,
5130 NULL, &stream->current_ts_end, NULL, &stream->current_offset_end);
5131 } else {
5132 /* we moved DISCONT, full update */
5133 gst_avi_demux_get_buffer_info (avi, stream, new_entry,
5134 &stream->current_timestamp, &stream->current_ts_end,
5135 &stream->current_offset, &stream->current_offset_end);
5136 /* and MARK discont for this stream */
5137 stream->discont = TRUE;
5138 GST_DEBUG_OBJECT (avi, "Moved from %u to %u, ts %" GST_TIME_FORMAT
5139 ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
5140 ", off_end %" G_GUINT64_FORMAT, old_entry, new_entry,
5141 GST_TIME_ARGS (stream->current_timestamp),
5142 GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
5143 stream->current_offset_end);
5144 }
5145 }
5146 return ret;
5147
5148 /* ERROR */
5149 eos:
5150 {
5151 GST_DEBUG_OBJECT (avi, "we are EOS");
5152 /* setting current_timestamp to -1 marks EOS */
5153 stream->current_timestamp = -1;
5154 return GST_FLOW_EOS;
5155 }
5156 }
5157
5158 /* find the stream with the lowest current position when going forwards or with
5159 * the highest position when going backwards, this is the stream
5160 * we should push from next */
5161 static gint
gst_avi_demux_find_next(GstAviDemux * avi,gfloat rate)5162 gst_avi_demux_find_next (GstAviDemux * avi, gfloat rate)
5163 {
5164 guint64 min_time, max_time;
5165 guint stream_num, i;
5166
5167 max_time = 0;
5168 min_time = G_MAXUINT64;
5169 stream_num = -1;
5170
5171 for (i = 0; i < avi->num_streams; i++) {
5172 guint64 position;
5173 GstAviStream *stream;
5174
5175 stream = &avi->stream[i];
5176
5177 /* ignore streams that finished */
5178 if (stream->pad && GST_PAD_LAST_FLOW_RETURN (stream->pad) == GST_FLOW_EOS)
5179 continue;
5180
5181 position = stream->current_timestamp;
5182
5183 /* position of -1 is EOS */
5184 if (position != -1) {
5185 if (rate > 0.0 && position < min_time) {
5186 min_time = position;
5187 stream_num = i;
5188 } else if (rate < 0.0 && position >= max_time) {
5189 max_time = position;
5190 stream_num = i;
5191 }
5192 }
5193 }
5194 return stream_num;
5195 }
5196
5197 static GstBuffer *
gst_avi_demux_align_buffer(GstAviDemux * demux,GstBuffer * buffer,gsize alignment)5198 gst_avi_demux_align_buffer (GstAviDemux * demux,
5199 GstBuffer * buffer, gsize alignment)
5200 {
5201 GstMapInfo map;
5202
5203 gst_buffer_map (buffer, &map, GST_MAP_READ);
5204
5205 if (map.size < sizeof (guintptr)) {
5206 gst_buffer_unmap (buffer, &map);
5207 return buffer;
5208 }
5209
5210 if (((guintptr) map.data) & (alignment - 1)) {
5211 GstBuffer *new_buffer;
5212 GstAllocationParams params = { 0, alignment - 1, 0, 0, };
5213
5214 new_buffer = gst_buffer_new_allocate (NULL,
5215 gst_buffer_get_size (buffer), ¶ms);
5216
5217 /* Copy data "by hand", so ensure alignment is kept: */
5218 gst_buffer_fill (new_buffer, 0, map.data, map.size);
5219
5220 gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
5221 GST_DEBUG_OBJECT (demux,
5222 "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
5223 alignment);
5224
5225 gst_buffer_unmap (buffer, &map);
5226 gst_buffer_unref (buffer);
5227
5228 return new_buffer;
5229 }
5230
5231 gst_buffer_unmap (buffer, &map);
5232 return buffer;
5233 }
5234
5235 static GstFlowReturn
gst_avi_demux_loop_data(GstAviDemux * avi)5236 gst_avi_demux_loop_data (GstAviDemux * avi)
5237 {
5238 GstFlowReturn ret = GST_FLOW_OK;
5239 guint stream_num;
5240 GstAviStream *stream;
5241 gboolean processed = FALSE;
5242 GstBuffer *buf;
5243 guint64 offset, size;
5244 GstClockTime timestamp, duration;
5245 guint64 out_offset, out_offset_end;
5246 gboolean keyframe;
5247 GstAviIndexEntry *entry;
5248
5249 do {
5250 stream_num = gst_avi_demux_find_next (avi, avi->segment.rate);
5251
5252 /* all are EOS */
5253 if (G_UNLIKELY (stream_num == -1)) {
5254 GST_DEBUG_OBJECT (avi, "all streams are EOS");
5255 goto eos;
5256 }
5257
5258 /* we have the stream now */
5259 stream = &avi->stream[stream_num];
5260
5261 /* skip streams without pads */
5262 if (!stream->pad) {
5263 GST_DEBUG_OBJECT (avi, "skipping entry from stream %d without pad",
5264 stream_num);
5265 goto next;
5266 }
5267
5268 /* get the timing info for the entry */
5269 timestamp = stream->current_timestamp;
5270 duration = stream->current_ts_end - timestamp;
5271 out_offset = stream->current_offset;
5272 out_offset_end = stream->current_offset_end;
5273
5274 /* get the entry data info */
5275 entry = &stream->index[stream->current_entry];
5276 offset = entry->offset;
5277 size = entry->size;
5278 keyframe = ENTRY_IS_KEYFRAME (entry);
5279
5280 /* skip empty entries */
5281 if (size == 0) {
5282 GST_DEBUG_OBJECT (avi, "Skipping entry %u (%" G_GUINT64_FORMAT ", %p)",
5283 stream->current_entry, size, stream->pad);
5284 goto next;
5285 }
5286
5287 if (avi->segment.rate > 0.0) {
5288 /* only check this for forwards playback for now */
5289 if (keyframe && GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
5290 && (timestamp > avi->segment.stop)) {
5291 goto eos_stop;
5292 }
5293 } else {
5294 if (keyframe && GST_CLOCK_TIME_IS_VALID (avi->segment.start)
5295 && (timestamp < avi->segment.start))
5296 goto eos_stop;
5297 }
5298
5299 GST_LOG ("reading buffer (size=%" G_GUINT64_FORMAT "), stream %d, pos %"
5300 G_GUINT64_FORMAT " (0x%" G_GINT64_MODIFIER "x), kf %d", size,
5301 stream_num, offset, offset, keyframe);
5302
5303 /* FIXME, check large chunks and cut them up */
5304
5305 /* pull in the data */
5306 buf = NULL;
5307 ret = gst_pad_pull_range (avi->sinkpad, offset, size, &buf);
5308 if (ret != GST_FLOW_OK)
5309 goto pull_failed;
5310
5311 /* check for short buffers, this is EOS as well */
5312 if (gst_buffer_get_size (buf) < size)
5313 goto short_buffer;
5314
5315 /* invert the picture if needed, and append palette for RGB8P */
5316 buf = gst_avi_demux_invert (stream, buf);
5317
5318 /* mark non-keyframes */
5319 if (keyframe || stream->is_raw) {
5320 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
5321 GST_BUFFER_PTS (buf) = timestamp;
5322 } else {
5323 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
5324 GST_BUFFER_PTS (buf) = GST_CLOCK_TIME_NONE;
5325 }
5326
5327 GST_BUFFER_DTS (buf) = timestamp;
5328
5329 GST_BUFFER_DURATION (buf) = duration;
5330 GST_BUFFER_OFFSET (buf) = out_offset;
5331 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
5332
5333 /* mark discont when pending */
5334 if (stream->discont) {
5335 GST_DEBUG_OBJECT (avi, "setting DISCONT flag");
5336 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5337 stream->discont = FALSE;
5338 } else {
5339 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5340 }
5341 #if 0
5342 gst_avi_demux_add_assoc (avi, stream, timestamp, offset, keyframe);
5343 #endif
5344
5345 /* update current position in the segment */
5346 avi->segment.position = timestamp;
5347
5348 GST_DEBUG_OBJECT (avi, "Pushing buffer of size %" G_GSIZE_FORMAT ", ts %"
5349 GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
5350 ", off_end %" G_GUINT64_FORMAT,
5351 gst_buffer_get_size (buf), GST_TIME_ARGS (timestamp),
5352 GST_TIME_ARGS (duration), out_offset, out_offset_end);
5353
5354 if (stream->alignment > 1)
5355 buf = gst_avi_demux_align_buffer (avi, buf, stream->alignment);
5356 ret = gst_pad_push (stream->pad, buf);
5357
5358 /* mark as processed, we increment the frame and byte counters then
5359 * leave the while loop and return the GstFlowReturn */
5360 processed = TRUE;
5361
5362 if (avi->segment.rate < 0) {
5363 if (timestamp > avi->segment.stop && ret == GST_FLOW_EOS) {
5364 /* In reverse playback we can get a GST_FLOW_EOS when
5365 * we are at the end of the segment, so we just need to jump
5366 * back to the previous section. */
5367 GST_DEBUG_OBJECT (avi, "downstream has reached end of segment");
5368 ret = GST_FLOW_OK;
5369 }
5370 }
5371 next:
5372 /* move to next item */
5373 ret = gst_avi_demux_advance (avi, stream, ret);
5374
5375 /* combine flows */
5376 ret = gst_avi_demux_combine_flows (avi, stream, ret);
5377 } while (!processed);
5378
5379 beach:
5380 return ret;
5381
5382 /* special cases */
5383 eos:
5384 {
5385 GST_DEBUG_OBJECT (avi, "No samples left for any streams - EOS");
5386 ret = GST_FLOW_EOS;
5387 goto beach;
5388 }
5389 eos_stop:
5390 {
5391 GST_LOG_OBJECT (avi, "Found keyframe after segment,"
5392 " setting EOS (%" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ")",
5393 GST_TIME_ARGS (timestamp), GST_TIME_ARGS (avi->segment.stop));
5394 ret = GST_FLOW_EOS;
5395 /* move to next stream */
5396 goto next;
5397 }
5398 pull_failed:
5399 {
5400 GST_DEBUG_OBJECT (avi, "pull range failed: pos=%" G_GUINT64_FORMAT
5401 " size=%" G_GUINT64_FORMAT, offset, size);
5402 goto beach;
5403 }
5404 short_buffer:
5405 {
5406 GST_WARNING_OBJECT (avi, "Short read at offset %" G_GUINT64_FORMAT
5407 ", only got %" G_GSIZE_FORMAT "/%" G_GUINT64_FORMAT
5408 " bytes (truncated file?)", offset, gst_buffer_get_size (buf), size);
5409 gst_buffer_unref (buf);
5410 ret = GST_FLOW_EOS;
5411 goto beach;
5412 }
5413 }
5414
5415 /*
5416 * Read data. If we have an index it delegates to
5417 * gst_avi_demux_process_next_entry().
5418 */
5419 static GstFlowReturn
gst_avi_demux_stream_data(GstAviDemux * avi)5420 gst_avi_demux_stream_data (GstAviDemux * avi)
5421 {
5422 guint32 tag = 0;
5423 guint32 size = 0;
5424 gint stream_nr = 0;
5425 GstFlowReturn res = GST_FLOW_OK;
5426
5427 if (G_UNLIKELY (avi->have_eos)) {
5428 /* Clean adapter, we're done */
5429 gst_adapter_clear (avi->adapter);
5430 return GST_FLOW_EOS;
5431 }
5432
5433 if (G_UNLIKELY (avi->todrop)) {
5434 guint drop;
5435
5436 if ((drop = gst_adapter_available (avi->adapter))) {
5437 if (drop > avi->todrop)
5438 drop = avi->todrop;
5439 GST_DEBUG_OBJECT (avi, "Dropping %d bytes", drop);
5440 gst_adapter_flush (avi->adapter, drop);
5441 avi->todrop -= drop;
5442 avi->offset += drop;
5443 }
5444 }
5445
5446 /* Iterate until need more data, so adapter won't grow too much */
5447 while (1) {
5448 if (G_UNLIKELY (!gst_avi_demux_peek_chunk_info (avi, &tag, &size))) {
5449 return GST_FLOW_OK;
5450 }
5451
5452 GST_DEBUG ("Trying chunk (%" GST_FOURCC_FORMAT "), size %d",
5453 GST_FOURCC_ARGS (tag), size);
5454
5455 if (G_LIKELY ((tag & 0xff) >= '0' && (tag & 0xff) <= '9' &&
5456 ((tag >> 8) & 0xff) >= '0' && ((tag >> 8) & 0xff) <= '9')) {
5457 GST_LOG ("Chunk ok");
5458 } else if ((tag & 0xffff) == (('x' << 8) | 'i')) {
5459 GST_DEBUG ("Found sub-index tag");
5460 if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
5461 /* accept 0 size buffer here */
5462 avi->abort_buffering = FALSE;
5463 GST_DEBUG (" skipping %d bytes for now", size);
5464 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5465 }
5466 return GST_FLOW_OK;
5467 } else if (tag == GST_RIFF_TAG_RIFF) {
5468 /* RIFF tags can appear in ODML files, just jump over them */
5469 if (gst_adapter_available (avi->adapter) >= 12) {
5470 GST_DEBUG ("Found RIFF tag, skipping RIFF header");
5471 gst_adapter_flush (avi->adapter, 12);
5472 continue;
5473 }
5474 return GST_FLOW_OK;
5475 } else if (tag == GST_RIFF_TAG_idx1) {
5476 GST_DEBUG ("Found index tag");
5477 if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
5478 /* accept 0 size buffer here */
5479 avi->abort_buffering = FALSE;
5480 GST_DEBUG (" skipping %d bytes for now", size);
5481 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5482 }
5483 return GST_FLOW_OK;
5484 } else if (tag == GST_RIFF_TAG_LIST) {
5485 /* movi chunks might be grouped in rec list */
5486 if (gst_adapter_available (avi->adapter) >= 12) {
5487 GST_DEBUG ("Found LIST tag, skipping LIST header");
5488 gst_adapter_flush (avi->adapter, 12);
5489 continue;
5490 }
5491 return GST_FLOW_OK;
5492 } else if (tag == GST_RIFF_TAG_JUNK || tag == GST_RIFF_TAG_JUNQ) {
5493 /* rec list might contain JUNK chunks */
5494 GST_DEBUG ("Found JUNK tag");
5495 if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
5496 /* accept 0 size buffer here */
5497 avi->abort_buffering = FALSE;
5498 GST_DEBUG (" skipping %d bytes for now", size);
5499 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5500 }
5501 return GST_FLOW_OK;
5502 } else {
5503 GST_DEBUG ("No more stream chunks, send EOS");
5504 avi->have_eos = TRUE;
5505 return GST_FLOW_EOS;
5506 }
5507
5508 if (G_UNLIKELY (!gst_avi_demux_peek_chunk (avi, &tag, &size))) {
5509 /* supposedly one hopes to catch a nicer chunk later on ... */
5510 /* FIXME ?? give up here rather than possibly ending up going
5511 * through the whole file */
5512 if (avi->abort_buffering) {
5513 avi->abort_buffering = FALSE;
5514 if (size) {
5515 gst_adapter_flush (avi->adapter, 8);
5516 return GST_FLOW_OK;
5517 }
5518 } else {
5519 return GST_FLOW_OK;
5520 }
5521 }
5522 GST_DEBUG ("chunk ID %" GST_FOURCC_FORMAT ", size %u",
5523 GST_FOURCC_ARGS (tag), size);
5524
5525 stream_nr = CHUNKID_TO_STREAMNR (tag);
5526
5527 if (G_UNLIKELY (stream_nr < 0 || stream_nr >= avi->num_streams)) {
5528 /* recoverable */
5529 GST_WARNING ("Invalid stream ID %d (%" GST_FOURCC_FORMAT ")",
5530 stream_nr, GST_FOURCC_ARGS (tag));
5531 avi->offset += 8 + GST_ROUND_UP_2 (size);
5532 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5533 } else {
5534 GstAviStream *stream;
5535 GstClockTime next_ts = 0;
5536 GstBuffer *buf = NULL;
5537 #if 0
5538 guint64 offset;
5539 #endif
5540 gboolean saw_desired_kf = stream_nr != avi->main_stream
5541 || avi->offset >= avi->seek_kf_offset;
5542
5543 if (stream_nr == avi->main_stream && avi->offset == avi->seek_kf_offset) {
5544 GST_DEBUG_OBJECT (avi, "Desired keyframe reached");
5545 avi->seek_kf_offset = 0;
5546 }
5547
5548 if (saw_desired_kf) {
5549 gst_adapter_flush (avi->adapter, 8);
5550 /* get buffer */
5551 if (size) {
5552 buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size));
5553 /* patch the size */
5554 gst_buffer_resize (buf, 0, size);
5555 } else {
5556 buf = NULL;
5557 }
5558 } else {
5559 GST_DEBUG_OBJECT (avi,
5560 "Desired keyframe not yet reached, flushing chunk");
5561 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5562 }
5563
5564 #if 0
5565 offset = avi->offset;
5566 #endif
5567 avi->offset += 8 + GST_ROUND_UP_2 (size);
5568
5569 stream = &avi->stream[stream_nr];
5570
5571 /* set delay (if any)
5572 if (stream->strh->init_frames == stream->current_frame &&
5573 stream->delay == 0)
5574 stream->delay = next_ts;
5575 */
5576
5577 /* parsing of corresponding header may have failed */
5578 if (G_UNLIKELY (!stream->pad)) {
5579 GST_WARNING_OBJECT (avi, "no pad for stream ID %" GST_FOURCC_FORMAT,
5580 GST_FOURCC_ARGS (tag));
5581 if (buf)
5582 gst_buffer_unref (buf);
5583 } else {
5584 /* get time of this buffer */
5585 gst_pad_query_position (stream->pad, GST_FORMAT_TIME,
5586 (gint64 *) & next_ts);
5587
5588 #if 0
5589 gst_avi_demux_add_assoc (avi, stream, next_ts, offset, FALSE);
5590 #endif
5591
5592 /* increment our positions */
5593 stream->current_entry++;
5594 /* as in pull mode, 'total' is either bytes (CBR) or frames (VBR) */
5595 if (stream->strh->type == GST_RIFF_FCC_auds && stream->is_vbr) {
5596 gint blockalign = stream->strf.auds->blockalign;
5597 if (blockalign > 0)
5598 stream->current_total += DIV_ROUND_UP (size, blockalign);
5599 else
5600 stream->current_total++;
5601 } else {
5602 stream->current_total += size;
5603 }
5604 GST_LOG_OBJECT (avi, "current entry %u, total %u",
5605 stream->current_entry, stream->current_total);
5606
5607 /* update current position in the segment */
5608 avi->segment.position = next_ts;
5609
5610 if (saw_desired_kf && buf) {
5611 GstClockTime dur_ts = 0;
5612
5613 /* invert the picture if needed, and append palette for RGB8P */
5614 buf = gst_avi_demux_invert (stream, buf);
5615
5616 gst_pad_query_position (stream->pad, GST_FORMAT_TIME,
5617 (gint64 *) & dur_ts);
5618
5619 GST_BUFFER_DTS (buf) = next_ts;
5620 GST_BUFFER_PTS (buf) = GST_CLOCK_TIME_NONE;
5621 GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
5622 if (stream->strh->type == GST_RIFF_FCC_vids) {
5623 GST_BUFFER_OFFSET (buf) = stream->current_entry - 1;
5624 GST_BUFFER_OFFSET_END (buf) = stream->current_entry;
5625 } else {
5626 GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
5627 GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;
5628 }
5629
5630 GST_DEBUG_OBJECT (avi,
5631 "Pushing buffer with time=%" GST_TIME_FORMAT ", duration %"
5632 GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
5633 " and size %d over pad %s", GST_TIME_ARGS (next_ts),
5634 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
5635 GST_BUFFER_OFFSET (buf), size, GST_PAD_NAME (stream->pad));
5636
5637 /* mark discont when pending */
5638 if (G_UNLIKELY (stream->discont)) {
5639 GST_DEBUG_OBJECT (avi, "Setting DISCONT");
5640 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5641 stream->discont = FALSE;
5642 } else {
5643 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
5644 }
5645
5646 if (stream->alignment > 1)
5647 buf = gst_avi_demux_align_buffer (avi, buf, stream->alignment);
5648 res = gst_pad_push (stream->pad, buf);
5649 buf = NULL;
5650
5651 /* combine flows */
5652 res = gst_avi_demux_combine_flows (avi, stream, res);
5653 if (G_UNLIKELY (res != GST_FLOW_OK)) {
5654 GST_DEBUG ("Push failed; %s", gst_flow_get_name (res));
5655 return res;
5656 }
5657 }
5658 }
5659 }
5660 }
5661
5662 return res;
5663 }
5664
5665 /*
5666 * Send pending tags.
5667 */
5668 static void
push_tag_lists(GstAviDemux * avi)5669 push_tag_lists (GstAviDemux * avi)
5670 {
5671 guint i;
5672 GstTagList *tags;
5673
5674 if (!avi->got_tags)
5675 return;
5676
5677 GST_DEBUG_OBJECT (avi, "Pushing pending tag lists");
5678
5679 for (i = 0; i < avi->num_streams; i++) {
5680 GstAviStream *stream = &avi->stream[i];
5681 GstPad *pad = stream->pad;
5682
5683 tags = stream->taglist;
5684
5685 if (pad && tags) {
5686 GST_DEBUG_OBJECT (pad, "Tags: %" GST_PTR_FORMAT, tags);
5687
5688 gst_pad_push_event (pad, gst_event_new_tag (tags));
5689 stream->taglist = NULL;
5690 }
5691 }
5692
5693 if (!(tags = avi->globaltags))
5694 tags = gst_tag_list_new_empty ();
5695
5696 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
5697 GST_TAG_CONTAINER_FORMAT, "AVI", NULL);
5698
5699 GST_DEBUG_OBJECT (avi, "Global tags: %" GST_PTR_FORMAT, tags);
5700 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
5701 gst_avi_demux_push_event (avi, gst_event_new_tag (tags));
5702 avi->globaltags = NULL;
5703 avi->got_tags = FALSE;
5704 }
5705
5706 static void
gst_avi_demux_loop(GstPad * pad)5707 gst_avi_demux_loop (GstPad * pad)
5708 {
5709 GstFlowReturn res;
5710 GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
5711
5712 switch (avi->state) {
5713 case GST_AVI_DEMUX_START:
5714 res = gst_avi_demux_stream_init_pull (avi);
5715 if (G_UNLIKELY (res != GST_FLOW_OK)) {
5716 GST_WARNING ("stream_init flow: %s", gst_flow_get_name (res));
5717 goto pause;
5718 }
5719 avi->state = GST_AVI_DEMUX_HEADER;
5720 /* fall-through */
5721 case GST_AVI_DEMUX_HEADER:
5722 res = gst_avi_demux_stream_header_pull (avi);
5723 if (G_UNLIKELY (res != GST_FLOW_OK)) {
5724 GST_WARNING ("stream_header flow: %s", gst_flow_get_name (res));
5725 goto pause;
5726 }
5727 avi->state = GST_AVI_DEMUX_MOVI;
5728 break;
5729 case GST_AVI_DEMUX_MOVI:
5730 if (G_UNLIKELY (avi->seg_event)) {
5731 gst_avi_demux_push_event (avi, avi->seg_event);
5732 avi->seg_event = NULL;
5733 }
5734 if (G_UNLIKELY (avi->got_tags)) {
5735 push_tag_lists (avi);
5736 }
5737 /* process each index entry in turn */
5738 res = gst_avi_demux_loop_data (avi);
5739
5740 /* pause when error */
5741 if (G_UNLIKELY (res != GST_FLOW_OK)) {
5742 GST_INFO ("stream_movi flow: %s", gst_flow_get_name (res));
5743 goto pause;
5744 }
5745 break;
5746 default:
5747 GST_ERROR_OBJECT (avi, "unknown state %d", avi->state);
5748 res = GST_FLOW_ERROR;
5749 goto pause;
5750 }
5751
5752 return;
5753
5754 /* ERRORS */
5755 pause:{
5756
5757 gboolean push_eos = FALSE;
5758 GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
5759 gst_pad_pause_task (avi->sinkpad);
5760
5761 if (res == GST_FLOW_EOS) {
5762 /* handle end-of-stream/segment */
5763 /* so align our position with the end of it, if there is one
5764 * this ensures a subsequent will arrive at correct base/acc time */
5765 if (avi->segment.rate > 0.0 &&
5766 GST_CLOCK_TIME_IS_VALID (avi->segment.stop))
5767 avi->segment.position = avi->segment.stop;
5768 else if (avi->segment.rate < 0.0)
5769 avi->segment.position = avi->segment.start;
5770 if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
5771 gint64 stop;
5772 GstEvent *event;
5773 GstMessage *msg;
5774
5775 if ((stop = avi->segment.stop) == -1)
5776 stop = avi->segment.duration;
5777
5778 GST_INFO_OBJECT (avi, "sending segment_done");
5779
5780 msg =
5781 gst_message_new_segment_done (GST_OBJECT_CAST (avi),
5782 GST_FORMAT_TIME, stop);
5783 if (avi->segment_seqnum)
5784 gst_message_set_seqnum (msg, avi->segment_seqnum);
5785 gst_element_post_message (GST_ELEMENT_CAST (avi), msg);
5786
5787 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
5788 if (avi->segment_seqnum)
5789 gst_event_set_seqnum (event, avi->segment_seqnum);
5790 gst_avi_demux_push_event (avi, event);
5791 } else {
5792 push_eos = TRUE;
5793 }
5794 } else if (res == GST_FLOW_NOT_LINKED || res < GST_FLOW_EOS) {
5795 /* for fatal errors we post an error message, wrong-state is
5796 * not fatal because it happens due to flushes and only means
5797 * that we should stop now. */
5798 GST_ELEMENT_FLOW_ERROR (avi, res);
5799 push_eos = TRUE;
5800 }
5801 if (push_eos) {
5802 GstEvent *event;
5803
5804 GST_INFO_OBJECT (avi, "sending eos");
5805 event = gst_event_new_eos ();
5806 if (avi->segment_seqnum)
5807 gst_event_set_seqnum (event, avi->segment_seqnum);
5808 if (!gst_avi_demux_push_event (avi, event) && (res == GST_FLOW_EOS)) {
5809 GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
5810 (NULL), ("got eos but no streams (yet)"));
5811 }
5812 }
5813 }
5814 }
5815
5816
5817 static GstFlowReturn
gst_avi_demux_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)5818 gst_avi_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
5819 {
5820 GstFlowReturn res;
5821 GstAviDemux *avi = GST_AVI_DEMUX (parent);
5822 gint i;
5823
5824 if (GST_BUFFER_IS_DISCONT (buf)) {
5825 GST_DEBUG_OBJECT (avi, "got DISCONT");
5826 gst_adapter_clear (avi->adapter);
5827 /* mark all streams DISCONT */
5828 for (i = 0; i < avi->num_streams; i++)
5829 avi->stream[i].discont = TRUE;
5830 }
5831
5832 GST_DEBUG ("Store %" G_GSIZE_FORMAT " bytes in adapter",
5833 gst_buffer_get_size (buf));
5834 gst_adapter_push (avi->adapter, buf);
5835
5836 switch (avi->state) {
5837 case GST_AVI_DEMUX_START:
5838 if ((res = gst_avi_demux_stream_init_push (avi)) != GST_FLOW_OK) {
5839 GST_WARNING ("stream_init flow: %s", gst_flow_get_name (res));
5840 break;
5841 }
5842 break;
5843 case GST_AVI_DEMUX_HEADER:
5844 if ((res = gst_avi_demux_stream_header_push (avi)) != GST_FLOW_OK) {
5845 GST_WARNING ("stream_header flow: %s", gst_flow_get_name (res));
5846 break;
5847 }
5848 break;
5849 case GST_AVI_DEMUX_MOVI:
5850 if (G_UNLIKELY (avi->seg_event)) {
5851 gst_avi_demux_push_event (avi, avi->seg_event);
5852 avi->seg_event = NULL;
5853 }
5854 if (G_UNLIKELY (avi->got_tags)) {
5855 push_tag_lists (avi);
5856 }
5857 res = gst_avi_demux_stream_data (avi);
5858 break;
5859 case GST_AVI_DEMUX_SEEK:
5860 {
5861 GstEvent *event;
5862
5863 res = GST_FLOW_OK;
5864
5865 /* obtain and parse indexes */
5866 if (avi->stream[0].indexes && !gst_avi_demux_read_subindexes_push (avi))
5867 /* seek in subindex read function failed */
5868 goto index_failed;
5869
5870 if (!avi->stream[0].indexes && !avi->have_index
5871 && avi->avih->flags & GST_RIFF_AVIH_HASINDEX)
5872 gst_avi_demux_stream_index_push (avi);
5873
5874 if (avi->have_index) {
5875 /* use the indexes now to construct nice durations */
5876 gst_avi_demux_calculate_durations_from_index (avi);
5877 } else {
5878 /* still parsing indexes */
5879 break;
5880 }
5881
5882 GST_OBJECT_LOCK (avi);
5883 event = avi->seek_event;
5884 avi->seek_event = NULL;
5885 GST_OBJECT_UNLOCK (avi);
5886
5887 /* calculate and perform seek */
5888 if (!avi_demux_handle_seek_push (avi, avi->sinkpad, event)) {
5889 gst_event_unref (event);
5890 goto seek_failed;
5891 }
5892
5893 gst_event_unref (event);
5894 avi->state = GST_AVI_DEMUX_MOVI;
5895 break;
5896 }
5897 default:
5898 GST_ELEMENT_ERROR (avi, STREAM, FAILED, (NULL),
5899 ("Illegal internal state"));
5900 res = GST_FLOW_ERROR;
5901 break;
5902 }
5903
5904 GST_DEBUG_OBJECT (avi, "state: %d res:%s", avi->state,
5905 gst_flow_get_name (res));
5906
5907 if (G_UNLIKELY (avi->abort_buffering))
5908 goto abort_buffering;
5909
5910 return res;
5911
5912 /* ERRORS */
5913 index_failed:
5914 {
5915 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("failed to read indexes"));
5916 return GST_FLOW_ERROR;
5917 }
5918 seek_failed:
5919 {
5920 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("push mode seek failed"));
5921 return GST_FLOW_ERROR;
5922 }
5923 abort_buffering:
5924 {
5925 avi->abort_buffering = FALSE;
5926 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("unhandled buffer size"));
5927 return GST_FLOW_ERROR;
5928 }
5929 }
5930
5931 static gboolean
gst_avi_demux_sink_activate(GstPad * sinkpad,GstObject * parent)5932 gst_avi_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
5933 {
5934 GstQuery *query;
5935 gboolean pull_mode;
5936
5937 query = gst_query_new_scheduling ();
5938
5939 if (!gst_pad_peer_query (sinkpad, query)) {
5940 gst_query_unref (query);
5941 goto activate_push;
5942 }
5943
5944 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
5945 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
5946 gst_query_unref (query);
5947
5948 if (!pull_mode)
5949 goto activate_push;
5950
5951 GST_DEBUG_OBJECT (sinkpad, "activating pull");
5952 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
5953
5954 activate_push:
5955 {
5956 GST_DEBUG_OBJECT (sinkpad, "activating push");
5957 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
5958 }
5959 }
5960
5961 static gboolean
gst_avi_demux_sink_activate_mode(GstPad * sinkpad,GstObject * parent,GstPadMode mode,gboolean active)5962 gst_avi_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
5963 GstPadMode mode, gboolean active)
5964 {
5965 gboolean res;
5966 GstAviDemux *avi = GST_AVI_DEMUX (parent);
5967
5968 switch (mode) {
5969 case GST_PAD_MODE_PULL:
5970 if (active) {
5971 avi->streaming = FALSE;
5972 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_avi_demux_loop,
5973 sinkpad, NULL);
5974 } else {
5975 res = gst_pad_stop_task (sinkpad);
5976 }
5977 break;
5978 case GST_PAD_MODE_PUSH:
5979 if (active) {
5980 GST_DEBUG ("avi: activating push/chain function");
5981 avi->streaming = TRUE;
5982 } else {
5983 GST_DEBUG ("avi: deactivating push/chain function");
5984 }
5985 res = TRUE;
5986 break;
5987 default:
5988 res = FALSE;
5989 break;
5990 }
5991 return res;
5992 }
5993
5994 #if 0
5995 static void
5996 gst_avi_demux_set_index (GstElement * element, GstIndex * index)
5997 {
5998 GstAviDemux *avi = GST_AVI_DEMUX (element);
5999
6000 GST_OBJECT_LOCK (avi);
6001 if (avi->element_index)
6002 gst_object_unref (avi->element_index);
6003 if (index) {
6004 avi->element_index = gst_object_ref (index);
6005 } else {
6006 avi->element_index = NULL;
6007 }
6008 GST_OBJECT_UNLOCK (avi);
6009 /* object lock might be taken again */
6010 if (index)
6011 gst_index_get_writer_id (index, GST_OBJECT_CAST (element), &avi->index_id);
6012 GST_DEBUG_OBJECT (avi, "Set index %" GST_PTR_FORMAT, avi->element_index);
6013 }
6014
6015 static GstIndex *
6016 gst_avi_demux_get_index (GstElement * element)
6017 {
6018 GstIndex *result = NULL;
6019 GstAviDemux *avi = GST_AVI_DEMUX (element);
6020
6021 GST_OBJECT_LOCK (avi);
6022 if (avi->element_index)
6023 result = gst_object_ref (avi->element_index);
6024 GST_OBJECT_UNLOCK (avi);
6025
6026 GST_DEBUG_OBJECT (avi, "Returning index %" GST_PTR_FORMAT, result);
6027
6028 return result;
6029 }
6030 #endif
6031
6032 static GstStateChangeReturn
gst_avi_demux_change_state(GstElement * element,GstStateChange transition)6033 gst_avi_demux_change_state (GstElement * element, GstStateChange transition)
6034 {
6035 GstStateChangeReturn ret;
6036 GstAviDemux *avi = GST_AVI_DEMUX (element);
6037
6038 switch (transition) {
6039 case GST_STATE_CHANGE_READY_TO_PAUSED:
6040 avi->streaming = FALSE;
6041 gst_segment_init (&avi->segment, GST_FORMAT_TIME);
6042 break;
6043 default:
6044 break;
6045 }
6046
6047 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
6048 if (ret == GST_STATE_CHANGE_FAILURE)
6049 goto done;
6050
6051 switch (transition) {
6052 case GST_STATE_CHANGE_PAUSED_TO_READY:
6053 avi->have_index = FALSE;
6054 gst_avi_demux_reset (avi);
6055 break;
6056 default:
6057 break;
6058 }
6059
6060 done:
6061 return ret;
6062 }
6063