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>
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.
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.
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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
21 /* Element-Checklist-Version: 5 */
24 * SECTION:element-avidemux
26 * Demuxes an .avi file into raw or compressed audio and/or video streams.
28 * This element supports both push and pull-based scheduling, depending on the
29 * capabilities of the upstream elements.
32 * <title>Example launch line</title>
34 * gst-launch filesrc location=test.avi ! avidemux name=demux demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! 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.
41 * Last reviewed on 2006-12-29 (0.10.6)
48 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
49 * with newer GLib versions (>= 2.31.0) */
50 #define GLIB_DISABLE_DEPRECATION_WARNINGS
55 #ifdef DIVX_DRM /* need to check to use same define */
60 #include "gst/riff/riff-media.h"
61 #include "gstavidemux.h"
63 #include <gst/gst-i18n-plugin.h>
64 #include <gst/base/gstadapter.h>
67 #define DIV_ROUND_UP(s,v) (((s) + ((v)-1)) / (v))
69 #define GST_AVI_KEYFRAME 1
70 #ifdef AVIDEMUX_MODIFICATION
71 #define GST_AVI_NON_KEYFRAME 0
73 #define ENTRY_IS_KEYFRAME(e) ((e)->flags == GST_AVI_KEYFRAME)
74 #define ENTRY_SET_KEYFRAME(e) ((e)->flags = GST_AVI_KEYFRAME)
75 #define ENTRY_UNSET_KEYFRAME(e) ((e)->flags = 0)
78 GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
79 #define GST_CAT_DEFAULT avidemux_debug
81 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
84 GST_STATIC_CAPS ("video/x-msvideo")
87 #ifdef AVIDEMUX_MODIFICATION
88 /*Modification: Frame type enumeraions used to generat the index-table */
102 H264_NUT_EOSTREAM = 11,
108 static void gst_avi_demux_base_init (GstAviDemuxClass * klass);
109 static void gst_avi_demux_class_init (GstAviDemuxClass * klass);
110 static void gst_avi_demux_init (GstAviDemux * avi);
111 static void gst_avi_demux_finalize (GObject * object);
113 static void gst_avi_demux_reset (GstAviDemux * avi);
116 static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
118 static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
119 static gboolean gst_avi_demux_handle_sink_event (GstPad * pad,
121 static gboolean gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event);
124 static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
126 static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
127 static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
128 static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format,
129 gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
131 static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
132 static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
134 static gboolean gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
136 static void gst_avi_demux_loop (GstPad * pad);
137 static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
138 static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
140 static gboolean gst_avi_demux_activate_push (GstPad * pad, gboolean active);
141 static GstFlowReturn gst_avi_demux_chain (GstPad * pad, GstBuffer * buf);
143 static void gst_avi_demux_set_index (GstElement * element, GstIndex * index);
144 static GstIndex *gst_avi_demux_get_index (GstElement * element);
145 static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
146 GstStateChange transition);
147 static void gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi);
148 static void gst_avi_demux_get_buffer_info (GstAviDemux * avi,
149 GstAviStream * stream, guint entry_n, GstClockTime * timestamp,
150 GstClockTime * ts_end, guint64 * offset, guint64 * offset_end);
152 static void gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf);
153 #ifdef AVIDEMUX_MODIFICATION
154 /*Modification: Added function to find out the frame_type for index-table generation */
156 gst_avi_demux_find_frame_type (GstAviStream *stream, GstBuffer *buf, int *frame_type);
157 static void gst_avidemux_forward_trickplay (GstAviDemux * avi, GstAviStream * stream, guint64 *timestamp);
158 static void gst_avidemux_backward_trickplay (GstAviDemux * avi, GstAviStream * stream, guint64 *timestamp);
159 static GstFlowReturn gst_avidemux_seek_to_previous_keyframe (GstAviDemux *avi);
162 static GstElementClass *parent_class = NULL;
166 typedef enum drmErrorCodes
173 DRM_NEVER_REGISTERED,
177 #define DIVX_SDK_PLUGIN_NAME "libmm_divxsdk.so"
179 static gboolean init_divx_plugin (GstAviDemux * avi)
182 avi->divx_handle = dlopen (DIVX_SDK_PLUGIN_NAME, RTLD_LAZY);
183 if (!avi->divx_handle) {
184 GST_ERROR ("dlopen failed [%s]", dlerror());
187 GST_DEBUG("dlopen success");
189 avi->divx_init = dlsym (avi->divx_handle, "divx_init");
190 if ((error = dlerror()) != NULL) {
191 GST_ERROR ("[%s][%d]", __func__, __LINE__);
195 avi->divx_commit = dlsym (avi->divx_handle, "divx_commit");
196 if ((error = dlerror()) != NULL) {
197 GST_ERROR ("[%s][%d] %p", __func__, __LINE__, avi->divx_commit);
201 avi->divx_decrypt_audio = dlsym (avi->divx_handle, "divx_decrypt_audio");
202 GST_ERROR ("[%s][%d] %p", __func__, __LINE__, avi->divx_decrypt_audio);
203 if ((error = dlerror()) != NULL) {
207 avi->divx_prepare_video_bitstream = dlsym (avi->divx_handle, "divx_prepare_video_bitstream");
208 if ((error = dlerror()) != NULL) {
209 GST_ERROR ("[%s][%d]", __func__, __LINE__);
213 avi->divx_finalize = dlsym (avi->divx_handle, "divx_finalize");
214 if ((error = dlerror()) != NULL) {
215 GST_ERROR ("[%s][%d]", __func__, __LINE__);
222 GST_ERROR ("error : %s", error);
223 dlclose(avi->divx_handle);
224 avi->divx_handle = NULL;
229 /* ---------------------------------------------------- DIVX DRM Code : Start -----------------------------------------------------------------*/
231 gst_avi_demux_init_divx_drm (GstAviDemux * avi, uint8_t* drm_info)
235 if (init_divx_plugin (avi) == FALSE) {
236 GST_ERROR_OBJECT (avi, "Loading plugin failed....");
240 avi->drmContext = avi->divx_init (drm_info, &error_code);
241 if (avi->drmContext) {
242 GST_DEBUG_OBJECT (avi,"%s init success: drmContext = %p\n", __func__, avi->drmContext);
244 GST_ERROR_OBJECT (avi,"%s failed to init... error code = %d \n", __func__, error_code);
248 error_code = avi->divx_commit (avi->drmContext);
249 if (error_code == DRM_SUCCESS) {
250 GST_DEBUG_OBJECT (avi,"%s commit success: drmContext = %p\n", __func__, avi->drmContext);
252 GST_ERROR_OBJECT (avi,"%s failed to commit... error code = %d \n", __func__, error_code);
260 gst_avi_demux_send_divx_tag (GstAviDemux * avi)
263 GstTagList *tags = NULL;
264 GST_ERROR_OBJECT (avi, "*********** posting divx drm tags!!!!!!!!!!!!!!!!!!");
265 tags = gst_tag_list_new_full ("drm_divx", "1", NULL);
267 ret = gst_avi_demux_push_event (avi, gst_event_new_tag (tags) );
268 GST_ERROR_OBJECT (avi, "*********** posting tags returns [%d] !!!!!!!!!!!!!!!!!!", ret);
272 /* ---------------------------------------------------- DIVX DRM Code : End -----------------------------------------------------------------*/
274 /* GObject methods */
277 gst_avi_demux_get_type (void)
279 static GType avi_demux_type = 0;
281 if (!avi_demux_type) {
282 static const GTypeInfo avi_demux_info = {
283 sizeof (GstAviDemuxClass),
284 (GBaseInitFunc) gst_avi_demux_base_init,
286 (GClassInitFunc) gst_avi_demux_class_init,
289 sizeof (GstAviDemux),
291 (GInstanceInitFunc) gst_avi_demux_init,
295 g_type_register_static (GST_TYPE_ELEMENT,
296 "GstAviDemux", &avi_demux_info, 0);
299 return avi_demux_type;
303 gst_avi_demux_base_init (GstAviDemuxClass * klass)
305 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
306 GstPadTemplate *videosrctempl, *audiosrctempl, *subsrctempl;
307 GstCaps *audcaps, *vidcaps, *subcaps;
309 audcaps = gst_riff_create_audio_template_caps ();
310 gst_caps_append (audcaps, gst_caps_new_simple ("audio/x-avi-unknown", NULL));
311 audiosrctempl = gst_pad_template_new ("audio_%02d",
312 GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
314 vidcaps = gst_riff_create_video_template_caps ();
315 gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
316 gst_caps_append (vidcaps, gst_caps_new_simple ("video/x-avi-unknown", NULL));
317 videosrctempl = gst_pad_template_new ("video_%02d",
318 GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
320 subcaps = gst_caps_new_simple ("application/x-subtitle-avi", NULL);
321 subsrctempl = gst_pad_template_new ("subtitle_%02d",
322 GST_PAD_SRC, GST_PAD_SOMETIMES, subcaps);
323 gst_element_class_add_pad_template (element_class, audiosrctempl);
324 gst_element_class_add_pad_template (element_class, videosrctempl);
325 gst_element_class_add_pad_template (element_class, subsrctempl);
326 gst_element_class_add_static_pad_template (element_class, &sink_templ);
327 gst_object_unref (audiosrctempl);
328 gst_object_unref (videosrctempl);
329 gst_object_unref (subsrctempl);
330 gst_element_class_set_details_simple (element_class, "Avi demuxer",
332 "Demultiplex an avi file into audio and video",
333 "Erik Walthinsen <omega@cse.ogi.edu>, "
334 "Wim Taymans <wim.taymans@chello.be>, "
335 "Thijs Vermeir <thijsvermeir@gmail.com>");
339 gst_avi_demux_class_init (GstAviDemuxClass * klass)
341 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
342 GObjectClass *gobject_class = (GObjectClass *) klass;
344 GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
345 0, "Demuxer for AVI streams");
347 parent_class = g_type_class_peek_parent (klass);
349 gobject_class->finalize = gst_avi_demux_finalize;
350 gstelement_class->change_state =
351 GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
353 gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_avi_demux_set_index);
354 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_avi_demux_get_index);
357 gst_tag_register ("drm_divx", GST_TAG_FLAG_META,
360 _("a tag that is specific to DivX DRM File"),
366 gst_avi_demux_init (GstAviDemux * avi)
368 avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
369 gst_pad_set_activate_function (avi->sinkpad,
370 GST_DEBUG_FUNCPTR (gst_avi_demux_sink_activate));
371 gst_pad_set_activatepull_function (avi->sinkpad,
372 GST_DEBUG_FUNCPTR (gst_avi_demux_sink_activate_pull));
373 gst_pad_set_activatepush_function (avi->sinkpad,
374 GST_DEBUG_FUNCPTR (gst_avi_demux_activate_push));
375 gst_pad_set_chain_function (avi->sinkpad,
376 GST_DEBUG_FUNCPTR (gst_avi_demux_chain));
377 gst_pad_set_event_function (avi->sinkpad,
378 GST_DEBUG_FUNCPTR (gst_avi_demux_handle_sink_event));
379 gst_element_add_pad (GST_ELEMENT_CAST (avi), avi->sinkpad);
381 avi->adapter = gst_adapter_new ();
383 gst_avi_demux_reset (avi);
387 gst_avi_demux_finalize (GObject * object)
389 GstAviDemux *avi = GST_AVI_DEMUX (object);
391 GST_DEBUG ("AVI: finalize");
393 g_object_unref (avi->adapter);
395 G_OBJECT_CLASS (parent_class)->finalize (object);
399 gst_avi_demux_reset_stream (GstAviDemux * avi, GstAviStream * stream)
401 g_free (stream->strh);
402 g_free (stream->strf.data);
403 g_free (stream->name);
404 g_free (stream->index);
405 g_free (stream->indexes);
406 #ifdef AVIDEMUX_MODIFICATION
407 if (stream->trickplay_info)
408 g_free (stream->trickplay_info);
411 if (stream->initdata)
412 gst_buffer_unref (stream->initdata);
413 if (stream->extradata)
414 gst_buffer_unref (stream->extradata);
416 if (stream->exposed) {
417 gst_pad_set_active (stream->pad, FALSE);
418 gst_element_remove_pad (GST_ELEMENT_CAST (avi), stream->pad);
420 gst_object_unref (stream->pad);
422 if (stream->taglist) {
423 gst_tag_list_free (stream->taglist);
424 stream->taglist = NULL;
426 memset (stream, 0, sizeof (GstAviStream));
430 gst_avi_demux_reset (GstAviDemux * avi)
434 GST_DEBUG ("AVI: reset");
436 for (i = 0; i < avi->num_streams; i++)
437 gst_avi_demux_reset_stream (avi, &avi->stream[i]);
439 avi->header_state = GST_AVI_DEMUX_HEADER_TAG_LIST;
440 avi->num_streams = 0;
441 avi->num_v_streams = 0;
442 avi->num_a_streams = 0;
443 avi->num_t_streams = 0;
444 avi->main_stream = -1;
446 avi->state = GST_AVI_DEMUX_START;
448 avi->building_index = FALSE;
450 avi->index_offset = 0;
454 if (avi->element_index)
455 gst_object_unref (avi->element_index);
456 avi->element_index = NULL;
458 if (avi->close_seg_event) {
459 gst_event_unref (avi->close_seg_event);
460 avi->close_seg_event = NULL;
462 if (avi->seg_event) {
463 gst_event_unref (avi->seg_event);
464 avi->seg_event = NULL;
466 if (avi->seek_event) {
467 gst_event_unref (avi->seek_event);
468 avi->seek_event = NULL;
472 gst_tag_list_free (avi->globaltags);
473 avi->globaltags = NULL;
475 avi->got_tags = TRUE; /* we always want to push global tags */
476 avi->have_eos = FALSE;
477 avi->seekable = TRUE;
479 gst_adapter_clear (avi->adapter);
481 gst_segment_init (&avi->segment, GST_FORMAT_TIME);
486 avi->divx_finalize (avi->drmContext);
487 free(avi->drmContext);
488 avi->drmContext = NULL;
491 if (avi->divx_handle)
493 dlclose(avi->divx_handle);
494 avi->divx_handle = NULL;
500 /* GstElement methods */
503 static const GstFormat *
504 gst_avi_demux_get_src_formats (GstPad * pad)
506 GstAviStream *stream = gst_pad_get_element_private (pad);
508 static const GstFormat src_a_formats[] = {
514 static const GstFormat src_v_formats[] = {
520 return (stream->strh->type == GST_RIFF_FCC_auds ?
521 src_a_formats : src_v_formats);
525 /* assumes stream->strf.auds->av_bps != 0 */
526 static inline GstClockTime
527 avi_stream_convert_bytes_to_time_unchecked (GstAviStream * stream,
530 return gst_util_uint64_scale_int (bytes, GST_SECOND,
531 stream->strf.auds->av_bps);
534 static inline guint64
535 avi_stream_convert_time_to_bytes_unchecked (GstAviStream * stream,
538 return gst_util_uint64_scale_int (time, stream->strf.auds->av_bps,
542 /* assumes stream->strh->rate != 0 */
543 static inline GstClockTime
544 avi_stream_convert_frames_to_time_unchecked (GstAviStream * stream,
547 return gst_util_uint64_scale (frames, stream->strh->scale * GST_SECOND,
551 static inline guint64
552 avi_stream_convert_time_to_frames_unchecked (GstAviStream * stream,
555 return gst_util_uint64_scale (time, stream->strh->rate,
556 stream->strh->scale * GST_SECOND);
560 gst_avi_demux_src_convert (GstPad * pad,
561 GstFormat src_format,
562 gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
564 GstAviStream *stream = gst_pad_get_element_private (pad);
568 "Received src_format:%s, src_value:%" G_GUINT64_FORMAT
569 ", dest_format:%s", gst_format_get_name (src_format), src_value,
570 gst_format_get_name (*dest_format));
572 if (G_UNLIKELY (src_format == *dest_format)) {
573 *dest_value = src_value;
576 if (G_UNLIKELY (!stream->strh || !stream->strf.data)) {
580 if (G_UNLIKELY (stream->strh->type == GST_RIFF_FCC_vids &&
581 (src_format == GST_FORMAT_BYTES
582 || *dest_format == GST_FORMAT_BYTES))) {
587 switch (src_format) {
588 case GST_FORMAT_TIME:
589 switch (*dest_format) {
590 case GST_FORMAT_BYTES:
591 *dest_value = gst_util_uint64_scale_int (src_value,
592 stream->strf.auds->av_bps, GST_SECOND);
594 case GST_FORMAT_DEFAULT:
596 gst_util_uint64_scale_round (src_value, stream->strh->rate,
597 stream->strh->scale * GST_SECOND);
604 case GST_FORMAT_BYTES:
605 switch (*dest_format) {
606 case GST_FORMAT_TIME:
607 if (stream->strf.auds->av_bps != 0) {
608 *dest_value = avi_stream_convert_bytes_to_time_unchecked (stream,
618 case GST_FORMAT_DEFAULT:
619 switch (*dest_format) {
620 case GST_FORMAT_TIME:
622 avi_stream_convert_frames_to_time_unchecked (stream, src_value);
635 "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
636 gst_format_get_name (*dest_format), *dest_value);
640 static const GstQueryType *
641 gst_avi_demux_get_src_query_types (GstPad * pad)
643 static const GstQueryType src_types[] = {
655 gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
658 GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
660 GstAviStream *stream = gst_pad_get_element_private (pad);
662 if (!stream->strh || !stream->strf.data)
663 return gst_pad_query_default (pad, query);
665 switch (GST_QUERY_TYPE (query)) {
666 case GST_QUERY_POSITION:{
669 GST_DEBUG ("pos query for stream %u: frames %u, bytes %u",
670 stream->num, stream->current_entry, stream->current_total);
672 /* FIXME, this looks clumsy */
673 if (stream->strh->type == GST_RIFF_FCC_auds) {
674 if (stream->is_vbr) {
676 pos = gst_util_uint64_scale ((gint64) stream->current_entry *
677 stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
678 GST_DEBUG_OBJECT (avi, "VBR convert frame %u, time %"
679 GST_TIME_FORMAT, stream->current_entry, GST_TIME_ARGS (pos));
680 } else if (stream->strf.auds->av_bps != 0) {
682 pos = gst_util_uint64_scale (stream->current_total, GST_SECOND,
683 (guint64) stream->strf.auds->av_bps);
684 GST_DEBUG_OBJECT (avi,
685 "CBR convert bytes %u, time %" GST_TIME_FORMAT,
686 stream->current_total, GST_TIME_ARGS (pos));
687 } else if (stream->idx_n != 0 && stream->total_bytes != 0) {
688 /* calculate timestamps based on percentage of length */
689 guint64 xlen = avi->avih->us_frame *
690 avi->avih->tot_frames * GST_USECOND;
692 if (stream->is_vbr) {
693 pos = gst_util_uint64_scale (xlen, stream->current_entry,
695 GST_DEBUG_OBJECT (avi, "VBR perc convert frame %u, time %"
696 GST_TIME_FORMAT, stream->current_entry, GST_TIME_ARGS (pos));
698 pos = gst_util_uint64_scale (xlen, stream->current_total,
699 stream->total_bytes);
700 GST_DEBUG_OBJECT (avi,
701 "CBR perc convert bytes %u, time %" GST_TIME_FORMAT,
702 stream->current_total, GST_TIME_ARGS (pos));
709 if (stream->strh->rate != 0) {
710 pos = gst_util_uint64_scale ((guint64) stream->current_entry *
711 stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
713 pos = stream->current_entry * avi->avih->us_frame * GST_USECOND;
717 GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
718 gst_query_set_position (query, GST_FORMAT_TIME, pos);
720 GST_WARNING ("pos query failed");
723 case GST_QUERY_DURATION:
726 GstClockTime duration;
728 /* only act on audio or video streams */
729 if (stream->strh->type != GST_RIFF_FCC_auds &&
730 stream->strh->type != GST_RIFF_FCC_vids) {
735 /* take stream duration, fall back to avih duration */
736 if ((duration = stream->duration) == -1)
737 duration = avi->duration;
739 gst_query_parse_duration (query, &fmt, NULL);
742 case GST_FORMAT_TIME:
743 gst_query_set_duration (query, fmt, duration);
745 case GST_FORMAT_DEFAULT:
748 GST_DEBUG_OBJECT (query, "total frames is %" G_GUINT32_FORMAT,
751 if (stream->idx_n >= 0)
752 gst_query_set_duration (query, fmt, stream->idx_n);
753 else if (gst_pad_query_convert (pad, GST_FORMAT_TIME,
754 duration, &fmt, &dur))
755 gst_query_set_duration (query, fmt, dur);
764 case GST_QUERY_SEEKING:{
767 gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
768 if (fmt == GST_FORMAT_TIME) {
769 gboolean seekable = TRUE;
771 if (avi->streaming) {
772 seekable = avi->seekable;
775 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
776 0, stream->duration);
781 case GST_QUERY_CONVERT:{
782 GstFormat src_fmt, dest_fmt;
783 gint64 src_val, dest_val;
785 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
786 if ((res = gst_avi_demux_src_convert (pad, src_fmt, src_val, &dest_fmt,
788 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
790 res = gst_pad_query_default (pad, query);
794 res = gst_pad_query_default (pad, query);
798 gst_object_unref (avi);
803 static const GstEventMask *
804 gst_avi_demux_get_event_mask (GstPad * pad)
806 static const GstEventMask masks[] = {
807 {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
816 gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before)
818 GstAviStream *stream;
819 GstIndexEntry *entry;
821 gint64 val, min = offset;
823 for (i = 0; i < avi->num_streams; i++) {
824 stream = &avi->stream[i];
826 entry = gst_index_get_assoc_entry (avi->element_index, stream->index_id,
827 before ? GST_INDEX_LOOKUP_BEFORE : GST_INDEX_LOOKUP_AFTER,
828 GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, offset);
832 gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
833 GST_DEBUG_OBJECT (avi, "stream %d, previous entry at %"
834 G_GUINT64_FORMAT, i, val);
842 GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
843 stream->current_entry = 0;
844 stream->current_total = 0;
848 gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
849 GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT,
852 gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &val);
853 stream->current_total = val;
854 gst_index_entry_assoc_map (entry, GST_FORMAT_DEFAULT, &val);
855 stream->current_entry = val;
862 gst_avi_demux_index_entry_offset_search (GstAviIndexEntry * entry,
865 if (entry->offset < *offset)
867 else if (entry->offset > *offset)
873 gst_avi_demux_seek_streams_index (GstAviDemux * avi, guint64 offset,
876 GstAviStream *stream;
877 GstAviIndexEntry *entry;
879 gint64 val, min = offset;
882 for (i = 0; i < avi->num_streams; i++) {
883 stream = &avi->stream[i];
885 /* compensate for chunk header */
888 gst_util_array_binary_search (stream->index, stream->idx_n,
889 sizeof (GstAviIndexEntry),
890 (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
891 before ? GST_SEARCH_MODE_BEFORE : GST_SEARCH_MODE_AFTER, &offset, NULL);
895 index = entry - stream->index;
899 val = stream->index[index].offset;
900 GST_DEBUG_OBJECT (avi,
901 "stream %d, previous entry at %" G_GUINT64_FORMAT, i, val);
909 GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
910 stream->current_entry = 0;
911 stream->current_total = 0;
915 val = stream->index[index].offset - 8;
916 GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT, i,
919 stream->current_total = stream->index[index].total;
920 stream->current_entry = index;
926 #define GST_AVI_SEEK_PUSH_DISPLACE (4 * GST_SECOND)
929 gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event)
932 GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
934 GST_DEBUG_OBJECT (avi,
935 "have event type %s: %p on sink pad", GST_EVENT_TYPE_NAME (event), event);
937 switch (GST_EVENT_TYPE (event)) {
938 case GST_EVENT_NEWSEGMENT:
942 gint64 start, stop, time, offset = 0;
946 /* some debug output */
947 gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
948 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
949 &start, &stop, &time);
950 gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
952 GST_DEBUG_OBJECT (avi,
953 "received format %d newsegment %" GST_SEGMENT_FORMAT, format,
956 /* chain will send initial newsegment after pads have been added */
957 if (avi->state != GST_AVI_DEMUX_MOVI) {
958 GST_DEBUG_OBJECT (avi, "still starting, eating event");
962 /* we only expect a BYTE segment, e.g. following a seek */
963 if (format != GST_FORMAT_BYTES) {
964 GST_DEBUG_OBJECT (avi, "unsupported segment format, ignoring");
968 if (avi->have_index) {
969 GstAviIndexEntry *entry;
970 guint i = 0, index = 0, k = 0;
971 GstAviStream *stream;
973 /* compensate chunk header, stored index offset points after header */
975 /* find which stream we're on */
977 stream = &avi->stream[i];
979 /* find the index for start bytes offset */
980 entry = gst_util_array_binary_search (stream->index,
981 stream->idx_n, sizeof (GstAviIndexEntry),
982 (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
983 GST_SEARCH_MODE_AFTER, &start, NULL);
987 index = entry - stream->index;
989 /* we are on the stream with a chunk start offset closest to start */
990 if (!offset || stream->index[index].offset < offset) {
991 offset = stream->index[index].offset;
994 /* exact match needs no further searching */
995 if (stream->index[index].offset == start)
997 } while (++i < avi->num_streams);
1000 stream = &avi->stream[k];
1002 /* so we have no idea what is to come, or where we are */
1004 GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
1008 /* get the ts corresponding to start offset bytes for the stream */
1009 gst_avi_demux_get_buffer_info (avi, stream, index,
1010 (GstClockTime *) & time, NULL, NULL, NULL);
1011 } else if (avi->element_index) {
1012 GstIndexEntry *entry;
1014 /* Let's check if we have an index entry for this position */
1015 entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id,
1016 GST_INDEX_LOOKUP_AFTER, GST_ASSOCIATION_FLAG_NONE,
1017 GST_FORMAT_BYTES, start);
1019 /* we can not go where we have not yet been before ... */
1021 GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
1025 gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
1026 gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &offset);
1028 GST_WARNING_OBJECT (avi, "no index data, forcing EOS");
1032 stop = GST_CLOCK_TIME_NONE;
1034 /* set up segment and send downstream */
1035 gst_segment_set_newsegment_full (&avi->segment, update, rate, arate,
1036 GST_FORMAT_TIME, time, stop, time);
1037 GST_DEBUG_OBJECT (avi, "Pushing newseg update %d, rate %g, "
1038 "applied rate %g, format %d, start %" G_GINT64_FORMAT ", "
1039 "stop %" G_GINT64_FORMAT, update, rate, arate, GST_FORMAT_TIME,
1041 gst_avi_demux_push_event (avi,
1042 gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
1045 GST_DEBUG_OBJECT (avi, "next chunk expected at %" G_GINT64_FORMAT, start);
1047 /* adjust state for streaming thread accordingly */
1048 if (avi->have_index)
1049 gst_avi_demux_seek_streams_index (avi, offset, FALSE);
1051 gst_avi_demux_seek_streams (avi, offset, FALSE);
1053 /* set up streaming thread */
1054 g_assert (offset >= start);
1055 avi->offset = start;
1056 avi->todrop = offset - start;
1059 gst_event_unref (event);
1063 /* set up for EOS */
1064 avi->have_eos = TRUE;
1069 if (avi->state != GST_AVI_DEMUX_MOVI) {
1070 gst_event_unref (event);
1071 GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
1072 (NULL), ("got eos and didn't receive a complete header object"));
1073 } else if (!gst_avi_demux_push_event (avi, event)) {
1074 GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
1075 (NULL), ("got eos but no streams (yet)"));
1079 case GST_EVENT_FLUSH_STOP:
1083 gst_adapter_clear (avi->adapter);
1084 avi->have_eos = FALSE;
1085 for (i = 0; i < avi->num_streams; i++) {
1086 avi->stream[i].last_flow = GST_FLOW_OK;
1087 avi->stream[i].discont = TRUE;
1089 /* fall through to default case so that the event gets passed downstream */
1092 res = gst_pad_event_default (pad, event);
1096 gst_object_unref (avi);
1102 gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
1104 gboolean res = TRUE;
1105 GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
1107 GST_DEBUG_OBJECT (avi,
1108 "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
1110 switch (GST_EVENT_TYPE (event)) {
1111 case GST_EVENT_SEEK:
1112 if (!avi->streaming) {
1113 res = gst_avi_demux_handle_seek (avi, pad, event);
1115 res = gst_avi_demux_handle_seek_push (avi, pad, event);
1117 gst_event_unref (event);
1120 case GST_EVENT_NAVIGATION:
1122 gst_event_unref (event);
1125 res = gst_pad_event_default (pad, event);
1129 gst_object_unref (avi);
1134 /* streaming helper (push) */
1137 * gst_avi_demux_peek_chunk_info:
1139 * @tag: holder for tag
1140 * @size: holder for tag size
1142 * Peek next chunk info (tag and size)
1144 * Returns: TRUE when one chunk info has been got
1147 gst_avi_demux_peek_chunk_info (GstAviDemux * avi, guint32 * tag, guint32 * size)
1149 const guint8 *data = NULL;
1151 if (gst_adapter_available (avi->adapter) < 8)
1154 data = gst_adapter_peek (avi->adapter, 8);
1155 *tag = GST_READ_UINT32_LE (data);
1156 *size = GST_READ_UINT32_LE (data + 4);
1162 * gst_avi_demux_peek_chunk:
1164 * @tag: holder for tag
1165 * @size: holder for tag size
1167 * Peek enough data for one full chunk
1169 * Returns: %TRUE when one chunk has been got
1172 gst_avi_demux_peek_chunk (GstAviDemux * avi, guint32 * tag, guint32 * size)
1174 guint32 peek_size = 0;
1177 if (!gst_avi_demux_peek_chunk_info (avi, tag, size))
1180 /* size 0 -> empty data buffer would surprise most callers,
1181 * large size -> do not bother trying to squeeze that into adapter,
1182 * so we throw poor man's exception, which can be caught if caller really
1183 * wants to handle 0 size chunk */
1184 if (!(*size) || (*size) >= (1 << 30))
1187 peek_size = (*size + 1) & ~1;
1188 available = gst_adapter_available (avi->adapter);
1190 GST_DEBUG_OBJECT (avi,
1191 "Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT
1192 ", %d bytes available", *size, GST_FOURCC_ARGS (*tag), available);
1194 if (available < (8 + peek_size))
1202 GST_INFO_OBJECT (avi, "Failed to peek");
1207 GST_INFO_OBJECT (avi,
1208 "Invalid/unexpected chunk size %d for tag %" GST_FOURCC_FORMAT, *size,
1209 GST_FOURCC_ARGS (*tag));
1210 /* chain should give up */
1211 avi->abort_buffering = TRUE;
1216 GST_INFO_OBJECT (avi, "need more %d < %" G_GUINT32_FORMAT,
1217 available, 8 + peek_size);
1225 * gst_avi_demux_parse_file_header:
1226 * @element: caller element (used for errors/debug).
1227 * @buf: input data to be used for parsing.
1229 * "Open" a RIFF/AVI file. The buffer should be at least 12
1230 * bytes long. Takes ownership of @buf.
1232 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
1233 * Throws an error, caller should error out (fatal).
1236 gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
1241 stamp = gst_util_get_timestamp ();
1243 /* riff_parse posts an error */
1244 if (!gst_riff_parse_file_header (element, buf, &doctype))
1247 if (doctype != GST_RIFF_RIFF_AVI)
1250 stamp = gst_util_get_timestamp () - stamp;
1251 GST_DEBUG_OBJECT (element, "header parsing took %" GST_TIME_FORMAT,
1252 GST_TIME_ARGS (stamp));
1259 GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
1260 ("File is not an AVI file: %" GST_FOURCC_FORMAT,
1261 GST_FOURCC_ARGS (doctype)));
1267 * Read AVI file tag when streaming
1269 static GstFlowReturn
1270 gst_avi_demux_stream_init_push (GstAviDemux * avi)
1272 if (gst_adapter_available (avi->adapter) >= 12) {
1275 tmp = gst_adapter_take_buffer (avi->adapter, 12);
1277 GST_DEBUG ("Parsing avi header");
1278 if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), tmp)) {
1279 return GST_FLOW_ERROR;
1281 GST_DEBUG ("header ok");
1284 avi->state = GST_AVI_DEMUX_HEADER;
1292 static GstFlowReturn
1293 gst_avi_demux_stream_init_pull (GstAviDemux * avi)
1296 GstBuffer *buf = NULL;
1298 res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
1299 if (res != GST_FLOW_OK)
1301 else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
1311 GST_DEBUG_OBJECT (avi, "error parsing file header");
1312 return GST_FLOW_ERROR;
1316 /* AVI header handling */
1318 * gst_avi_demux_parse_avih:
1319 * @avi: caller element (used for errors/debug).
1320 * @buf: input data to be used for parsing.
1321 * @avih: pointer to structure (filled in by function) containing
1322 * stream information (such as flags, number of streams, etc.).
1324 * Read 'avih' header. Discards buffer after use.
1326 * Returns: TRUE on success, FALSE otherwise. Throws an error if
1327 * the header is invalid. The caller should error out
1331 gst_avi_demux_parse_avih (GstAviDemux * avi,
1332 GstBuffer * buf, gst_riff_avih ** _avih)
1334 gst_riff_avih *avih;
1339 if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih))
1340 goto avih_too_small;
1342 avih = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1344 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1345 avih->us_frame = GUINT32_FROM_LE (avih->us_frame);
1346 avih->max_bps = GUINT32_FROM_LE (avih->max_bps);
1347 avih->pad_gran = GUINT32_FROM_LE (avih->pad_gran);
1348 avih->flags = GUINT32_FROM_LE (avih->flags);
1349 avih->tot_frames = GUINT32_FROM_LE (avih->tot_frames);
1350 avih->init_frames = GUINT32_FROM_LE (avih->init_frames);
1351 avih->streams = GUINT32_FROM_LE (avih->streams);
1352 avih->bufsize = GUINT32_FROM_LE (avih->bufsize);
1353 avih->width = GUINT32_FROM_LE (avih->width);
1354 avih->height = GUINT32_FROM_LE (avih->height);
1355 avih->scale = GUINT32_FROM_LE (avih->scale);
1356 avih->rate = GUINT32_FROM_LE (avih->rate);
1357 avih->start = GUINT32_FROM_LE (avih->start);
1358 avih->length = GUINT32_FROM_LE (avih->length);
1362 GST_INFO_OBJECT (avi, "avih tag found:");
1363 GST_INFO_OBJECT (avi, " us_frame %u", avih->us_frame);
1364 GST_INFO_OBJECT (avi, " max_bps %u", avih->max_bps);
1365 GST_INFO_OBJECT (avi, " pad_gran %u", avih->pad_gran);
1366 GST_INFO_OBJECT (avi, " flags 0x%08x", avih->flags);
1367 GST_INFO_OBJECT (avi, " tot_frames %u", avih->tot_frames);
1368 GST_INFO_OBJECT (avi, " init_frames %u", avih->init_frames);
1369 GST_INFO_OBJECT (avi, " streams %u", avih->streams);
1370 GST_INFO_OBJECT (avi, " bufsize %u", avih->bufsize);
1371 GST_INFO_OBJECT (avi, " width %u", avih->width);
1372 GST_INFO_OBJECT (avi, " height %u", avih->height);
1373 GST_INFO_OBJECT (avi, " scale %u", avih->scale);
1374 GST_INFO_OBJECT (avi, " rate %u", avih->rate);
1375 GST_INFO_OBJECT (avi, " start %u", avih->start);
1376 GST_INFO_OBJECT (avi, " length %u", avih->length);
1379 gst_buffer_unref (buf);
1381 if (avih->us_frame != 0 && avih->tot_frames != 0)
1383 (guint64) avih->us_frame * (guint64) avih->tot_frames * 1000;
1385 avi->duration = GST_CLOCK_TIME_NONE;
1387 GST_INFO_OBJECT (avi, " header duration %" GST_TIME_FORMAT,
1388 GST_TIME_ARGS (avi->duration));
1395 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No buffer"));
1400 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
1401 ("Too small avih (%d available, %d needed)",
1402 GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
1403 gst_buffer_unref (buf);
1409 * gst_avi_demux_parse_superindex:
1410 * @avi: caller element (used for debugging/errors).
1411 * @buf: input data to use for parsing.
1412 * @locations: locations in the file (byte-offsets) that contain
1413 * the actual indexes (see get_avi_demux_parse_subindex()).
1414 * The array ends with GST_BUFFER_OFFSET_NONE.
1416 * Reads superindex (openDML-2 spec stuff) from the provided data.
1418 * Returns: TRUE on success, FALSE otherwise. Indexes should be skipped
1419 * on error, but they are not fatal.
1422 gst_avi_demux_parse_superindex (GstAviDemux * avi,
1423 GstBuffer * buf, guint64 ** _indexes)
1433 size = buf ? GST_BUFFER_SIZE (buf) : 0;
1437 data = GST_BUFFER_DATA (buf);
1439 /* check type of index. The opendml2 specs state that
1440 * there should be 4 dwords per array entry. Type can be
1441 * either frame or field (and we don't care). */
1442 if (GST_READ_UINT16_LE (data) != 4 ||
1443 (data[2] & 0xfe) != 0x0 || data[3] != 0x0) {
1444 GST_WARNING_OBJECT (avi,
1445 "Superindex for stream has unexpected "
1446 "size_entry %d (bytes) or flags 0x%02x/0x%02x",
1447 GST_READ_UINT16_LE (data), data[2], data[3]);
1448 bpe = GST_READ_UINT16_LE (data) * 4;
1450 num = GST_READ_UINT32_LE (&data[4]);
1452 GST_DEBUG_OBJECT (avi, "got %d indexes", num);
1454 /* this can't work out well ... */
1455 if (num > G_MAXUINT32 >> 1 || bpe < 8) {
1456 goto invalid_params;
1459 indexes = g_new (guint64, num + 1);
1460 for (i = 0; i < num; i++) {
1461 if (size < 24 + bpe * (i + 1))
1463 indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
1464 GST_DEBUG_OBJECT (avi, "index %d at %" G_GUINT64_FORMAT, i, indexes[i]);
1466 indexes[i] = GST_BUFFER_OFFSET_NONE;
1467 *_indexes = indexes;
1469 gst_buffer_unref (buf);
1476 GST_ERROR_OBJECT (avi,
1477 "Not enough data to parse superindex (%d available, 24 needed)", size);
1479 gst_buffer_unref (buf);
1484 GST_ERROR_OBJECT (avi, "invalid index parameters (num = %d, bpe = %d)",
1487 gst_buffer_unref (buf);
1492 /* add an entry to the index of a stream. @num should be an estimate of the
1493 * total amount of index entries for all streams and is used to dynamically
1494 * allocate memory for the index entries. */
1495 static inline gboolean
1496 gst_avi_demux_add_index (GstAviDemux * avi, GstAviStream * stream,
1497 guint num, GstAviIndexEntry * entry)
1499 /* ensure index memory */
1500 if (G_UNLIKELY (stream->idx_n >= stream->idx_max)) {
1501 guint idx_max = stream->idx_max;
1502 GstAviIndexEntry *new_idx;
1504 /* we need to make some more room */
1506 /* initial size guess, assume each stream has an equal amount of entries,
1507 * overshoot with at least 8K */
1508 idx_max = (num / avi->num_streams) + (8192 / sizeof (GstAviIndexEntry));
1510 idx_max += 8192 / sizeof (GstAviIndexEntry);
1511 GST_DEBUG_OBJECT (avi, "expanded index from %u to %u",
1512 stream->idx_max, idx_max);
1514 new_idx = g_try_renew (GstAviIndexEntry, stream->index, idx_max);
1515 /* out of memory, if this fails stream->index is untouched. */
1516 if (G_UNLIKELY (!new_idx))
1519 stream->index = new_idx;
1520 stream->idx_max = idx_max;
1523 /* update entry total and stream stats. The entry total can be converted to
1524 * the timestamp of the entry easily. */
1525 if (stream->strh->type == GST_RIFF_FCC_auds) {
1528 if (stream->is_vbr) {
1529 entry->total = stream->total_blocks;
1531 entry->total = stream->total_bytes;
1533 blockalign = stream->strf.auds->blockalign;
1535 stream->total_blocks += DIV_ROUND_UP (entry->size, blockalign);
1537 stream->total_blocks++;
1539 if (stream->is_vbr) {
1540 entry->total = stream->idx_n;
1542 entry->total = stream->total_bytes;
1545 stream->total_bytes += entry->size;
1546 if (ENTRY_IS_KEYFRAME (entry))
1547 stream->n_keyframes++;
1550 GST_LOG_OBJECT (avi,
1551 "Adding stream %u, index entry %d, kf %d, size %u "
1552 ", offset %" G_GUINT64_FORMAT ", total %" G_GUINT64_FORMAT, stream->num,
1553 stream->idx_n, ENTRY_IS_KEYFRAME (entry), entry->size, entry->offset,
1555 stream->index[stream->idx_n++] = *entry;
1560 /* given @entry_n in @stream, calculate info such as timestamps and
1561 * offsets for the entry. */
1563 gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
1564 guint entry_n, GstClockTime * timestamp, GstClockTime * ts_end,
1565 guint64 * offset, guint64 * offset_end)
1567 GstAviIndexEntry *entry;
1569 entry = &stream->index[entry_n];
1571 if (stream->is_vbr) {
1572 /* VBR stream next timestamp */
1573 if (stream->strh->type == GST_RIFF_FCC_auds) {
1576 avi_stream_convert_frames_to_time_unchecked (stream, entry->total);
1578 *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
1583 avi_stream_convert_frames_to_time_unchecked (stream, entry_n);
1585 *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
1588 } else if (stream->strh->type == GST_RIFF_FCC_auds) {
1589 /* constant rate stream */
1592 avi_stream_convert_bytes_to_time_unchecked (stream, entry->total);
1594 *ts_end = avi_stream_convert_bytes_to_time_unchecked (stream,
1595 entry->total + entry->size);
1597 if (stream->strh->type == GST_RIFF_FCC_vids) {
1598 /* video offsets are the frame number */
1602 *offset_end = entry_n + 1;
1604 /* no offsets for audio */
1612 /* collect and debug stats about the indexes for all streams.
1613 * This method is also responsible for filling in the stream duration
1614 * as measured by the amount of index entries.
1616 * Returns TRUE if the index is not empty, else FALSE */
1618 gst_avi_demux_do_index_stats (GstAviDemux * avi)
1620 guint total_idx = 0;
1622 #ifndef GST_DISABLE_GST_DEBUG
1623 guint total_max = 0;
1626 /* get stream stats now */
1627 for (i = 0; i < avi->num_streams; i++) {
1628 GstAviStream *stream;
1630 if (G_UNLIKELY (!(stream = &avi->stream[i])))
1632 if (G_UNLIKELY (!stream->strh))
1634 if (G_UNLIKELY (!stream->index || stream->idx_n == 0))
1637 /* we interested in the end_ts of the last entry, which is the total
1638 * duration of this stream */
1639 gst_avi_demux_get_buffer_info (avi, stream, stream->idx_n - 1,
1640 NULL, &stream->idx_duration, NULL, NULL);
1642 total_idx += stream->idx_n;
1643 #ifndef GST_DISABLE_GST_DEBUG
1644 total_max += stream->idx_max;
1646 GST_INFO_OBJECT (avi, "Stream %d, dur %" GST_TIME_FORMAT ", %6u entries, "
1647 "%5u keyframes, entry size = %2u, total size = %10u, allocated %10u",
1648 i, GST_TIME_ARGS (stream->idx_duration), stream->idx_n,
1649 stream->n_keyframes, (guint) sizeof (GstAviIndexEntry),
1650 (guint) (stream->idx_n * sizeof (GstAviIndexEntry)),
1651 (guint) (stream->idx_max * sizeof (GstAviIndexEntry)));
1653 total_idx *= sizeof (GstAviIndexEntry);
1654 #ifndef GST_DISABLE_GST_DEBUG
1655 total_max *= sizeof (GstAviIndexEntry);
1657 GST_INFO_OBJECT (avi, "%u bytes for index vs %u ideally, %u wasted",
1658 total_max, total_idx, total_max - total_idx);
1660 if (total_idx == 0) {
1661 GST_WARNING_OBJECT (avi, "Index is empty !");
1668 * gst_avi_demux_parse_subindex:
1670 * @buf: input data to use for parsing.
1671 * @stream: stream context.
1672 * @entries_list: a list (returned by the function) containing all the
1673 * indexes parsed in this specific subindex. The first
1674 * entry is also a pointer to allocated memory that needs
1675 * to be free´ed. May be NULL if no supported indexes were
1678 * Reads superindex (openDML-2 spec stuff) from the provided data.
1679 * The buffer should contain a GST_RIFF_TAG_ix?? chunk.
1681 * Returns: TRUE on success, FALSE otherwise. Errors are fatal, we
1682 * throw an error, caller should bail out asap.
1685 gst_avi_demux_parse_subindex (GstAviDemux * avi, GstAviStream * stream,
1697 size = GST_BUFFER_SIZE (buf);
1703 data = GST_BUFFER_DATA (buf);
1705 /* We don't support index-data yet */
1707 goto not_implemented;
1709 /* check type of index. The opendml2 specs state that
1710 * there should be 4 dwords per array entry. Type can be
1711 * either frame or field (and we don't care). */
1712 bpe = (data[2] & 0x01) ? 12 : 8;
1713 if (GST_READ_UINT16_LE (data) != bpe / 4 ||
1714 (data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
1715 GST_WARNING_OBJECT (avi,
1716 "Superindex for stream %d has unexpected "
1717 "size_entry %d (bytes) or flags 0x%02x/0x%02x",
1718 stream->num, GST_READ_UINT16_LE (data), data[2], data[3]);
1719 bpe = GST_READ_UINT16_LE (data) * 4;
1721 num = GST_READ_UINT32_LE (&data[4]);
1722 baseoff = GST_READ_UINT64_LE (&data[12]);
1724 /* If there's nothing, just return ! */
1728 GST_INFO_OBJECT (avi, "Parsing subindex, nr_entries = %6d", num);
1730 for (i = 0; i < num; i++) {
1731 GstAviIndexEntry entry;
1733 if (size < 24 + bpe * (i + 1))
1736 /* fill in offset and size. offset contains the keyframe flag in the
1738 entry.offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * i]);
1739 entry.size = GST_READ_UINT32_LE (&data[24 + bpe * i + 4]);
1741 if (stream->strh->type == GST_RIFF_FCC_auds) {
1742 /* all audio frames are keyframes */
1743 ENTRY_SET_KEYFRAME (&entry);
1745 /* else read flags */
1746 entry.flags = (entry.size & 0x80000000) ? 0 : GST_AVI_KEYFRAME;
1748 entry.size &= ~0x80000000;
1751 if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
1754 gst_buffer_unref (buf);
1761 GST_ERROR_OBJECT (avi,
1762 "Not enough data to parse subindex (%d available, 24 needed)", size);
1763 gst_buffer_unref (buf);
1764 return TRUE; /* continue */
1768 GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL),
1769 ("Subindex-is-data is not implemented"));
1770 gst_buffer_unref (buf);
1775 GST_DEBUG_OBJECT (avi, "the index is empty");
1776 gst_buffer_unref (buf);
1781 GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
1782 ("Cannot allocate memory for %u*%u=%u bytes",
1783 (guint) sizeof (GstAviIndexEntry), num,
1784 (guint) sizeof (GstAviIndexEntry) * num));
1785 gst_buffer_unref (buf);
1791 * Create and push a flushing seek event upstream
1794 perform_seek_to_offset (GstAviDemux * demux, guint64 offset)
1799 GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
1802 gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1803 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
1804 GST_SEEK_TYPE_NONE, -1);
1806 res = gst_pad_push_event (demux->sinkpad, event);
1809 demux->offset = offset;
1814 * Read AVI index when streaming
1817 gst_avi_demux_read_subindexes_push (GstAviDemux * avi)
1819 guint32 tag = 0, size;
1820 GstBuffer *buf = NULL;
1823 GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
1825 if (avi->odml_subidxs[avi->odml_subidx] != avi->offset)
1828 if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
1831 /* this is the ODML chunk we expect */
1832 odml_stream = avi->odml_stream;
1834 if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + odml_stream / 10,
1835 '0' + odml_stream % 10)) &&
1836 (tag != GST_MAKE_FOURCC ('0' + odml_stream / 10,
1837 '0' + odml_stream % 10, 'i', 'x'))) {
1838 GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
1839 GST_FOURCC_ARGS (tag));
1843 avi->offset += 8 + GST_ROUND_UP_2 (size);
1844 /* flush chunk header so we get just the 'size' payload data */
1845 gst_adapter_flush (avi->adapter, 8);
1846 buf = gst_adapter_take_buffer (avi->adapter, size);
1848 if (!gst_avi_demux_parse_subindex (avi, &avi->stream[odml_stream], buf))
1851 /* we parsed the index, go to next subindex */
1854 if (avi->odml_subidxs[avi->odml_subidx] == GST_BUFFER_OFFSET_NONE) {
1855 /* we reached the end of the indexes for this stream, move to the next
1856 * stream to handle the first index */
1858 avi->odml_subidx = 0;
1860 if (avi->odml_stream < avi->num_streams) {
1861 /* there are more indexes */
1862 avi->odml_subidxs = avi->stream[avi->odml_stream].indexes;
1864 /* we're done, get stream stats now */
1865 avi->have_index = gst_avi_demux_do_index_stats (avi);
1871 /* seek to next index */
1872 return perform_seek_to_offset (avi, avi->odml_subidxs[avi->odml_subidx]);
1879 gst_avi_demux_read_subindexes_pull (GstAviDemux * avi)
1885 GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
1887 for (n = 0; n < avi->num_streams; n++) {
1888 GstAviStream *stream = &avi->stream[n];
1890 if (stream->indexes == NULL)
1893 for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
1894 if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi), avi->sinkpad,
1895 &stream->indexes[i], &tag, &buf) != GST_FLOW_OK)
1897 else if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
1898 '0' + stream->num % 10)) &&
1899 (tag != GST_MAKE_FOURCC ('0' + stream->num / 10,
1900 '0' + stream->num % 10, 'i', 'x'))) {
1901 /* Some ODML files (created by god knows what muxer) have a ##ix format
1902 * instead of the 'official' ix##. They are still valid though. */
1903 GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
1904 GST_FOURCC_ARGS (tag));
1905 gst_buffer_unref (buf);
1909 if (!gst_avi_demux_parse_subindex (avi, stream, buf))
1913 g_free (stream->indexes);
1914 stream->indexes = NULL;
1916 /* get stream stats now */
1917 avi->have_index = gst_avi_demux_do_index_stats (avi);
1921 * gst_avi_demux_riff_parse_vprp:
1922 * @element: caller element (used for debugging/error).
1923 * @buf: input data to be used for parsing, stripped from header.
1924 * @vprp: a pointer (returned by this function) to a filled-in vprp
1925 * structure. Caller should free it.
1927 * Parses a video stream´s vprp. This function takes ownership of @buf.
1929 * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
1930 * should be skipped on error, but it is not fatal.
1933 gst_avi_demux_riff_parse_vprp (GstElement * element,
1934 GstBuffer * buf, gst_riff_vprp ** _vprp)
1936 gst_riff_vprp *vprp;
1939 g_return_val_if_fail (buf != NULL, FALSE);
1940 g_return_val_if_fail (_vprp != NULL, FALSE);
1942 if (GST_BUFFER_SIZE (buf) < G_STRUCT_OFFSET (gst_riff_vprp, field_info))
1945 vprp = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1947 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1948 vprp->format_token = GUINT32_FROM_LE (vprp->format_token);
1949 vprp->standard = GUINT32_FROM_LE (vprp->standard);
1950 vprp->vert_rate = GUINT32_FROM_LE (vprp->vert_rate);
1951 vprp->hor_t_total = GUINT32_FROM_LE (vprp->hor_t_total);
1952 vprp->vert_lines = GUINT32_FROM_LE (vprp->vert_lines);
1953 vprp->aspect = GUINT32_FROM_LE (vprp->aspect);
1954 vprp->width = GUINT32_FROM_LE (vprp->width);
1955 vprp->height = GUINT32_FROM_LE (vprp->height);
1956 vprp->fields = GUINT32_FROM_LE (vprp->fields);
1960 /* calculate fields based on size */
1961 k = (GST_BUFFER_SIZE (buf) - G_STRUCT_OFFSET (gst_riff_vprp, field_info)) /
1963 if (vprp->fields > k) {
1964 GST_WARNING_OBJECT (element,
1965 "vprp header indicated %d fields, only %d available", vprp->fields, k);
1968 if (vprp->fields > GST_RIFF_VPRP_VIDEO_FIELDS) {
1969 GST_WARNING_OBJECT (element,
1970 "vprp header indicated %d fields, at most %d supported", vprp->fields,
1971 GST_RIFF_VPRP_VIDEO_FIELDS);
1972 vprp->fields = GST_RIFF_VPRP_VIDEO_FIELDS;
1974 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1975 for (k = 0; k < vprp->fields; k++) {
1976 gst_riff_vprp_video_field_desc *fd;
1978 fd = &vprp->field_info[k];
1979 fd->compressed_bm_height = GUINT32_FROM_LE (fd->compressed_bm_height);
1980 fd->compressed_bm_width = GUINT32_FROM_LE (fd->compressed_bm_width);
1981 fd->valid_bm_height = GUINT32_FROM_LE (fd->valid_bm_height);
1982 fd->valid_bm_width = GUINT16_FROM_LE (fd->valid_bm_width);
1983 fd->valid_bm_x_offset = GUINT16_FROM_LE (fd->valid_bm_x_offset);
1984 fd->valid_bm_y_offset = GUINT32_FROM_LE (fd->valid_bm_y_offset);
1985 fd->video_x_t_offset = GUINT32_FROM_LE (fd->video_x_t_offset);
1986 fd->video_y_start = GUINT32_FROM_LE (fd->video_y_start);
1991 GST_INFO_OBJECT (element, "vprp tag found in context vids:");
1992 GST_INFO_OBJECT (element, " format_token %d", vprp->format_token);
1993 GST_INFO_OBJECT (element, " standard %d", vprp->standard);
1994 GST_INFO_OBJECT (element, " vert_rate %d", vprp->vert_rate);
1995 GST_INFO_OBJECT (element, " hor_t_total %d", vprp->hor_t_total);
1996 GST_INFO_OBJECT (element, " vert_lines %d", vprp->vert_lines);
1997 GST_INFO_OBJECT (element, " aspect %d:%d", vprp->aspect >> 16,
1998 vprp->aspect & 0xffff);
1999 GST_INFO_OBJECT (element, " width %d", vprp->width);
2000 GST_INFO_OBJECT (element, " height %d", vprp->height);
2001 GST_INFO_OBJECT (element, " fields %d", vprp->fields);
2002 for (k = 0; k < vprp->fields; k++) {
2003 gst_riff_vprp_video_field_desc *fd;
2005 fd = &(vprp->field_info[k]);
2006 GST_INFO_OBJECT (element, " field %u description:", k);
2007 GST_INFO_OBJECT (element, " compressed_bm_height %d",
2008 fd->compressed_bm_height);
2009 GST_INFO_OBJECT (element, " compressed_bm_width %d",
2010 fd->compressed_bm_width);
2011 GST_INFO_OBJECT (element, " valid_bm_height %d",
2012 fd->valid_bm_height);
2013 GST_INFO_OBJECT (element, " valid_bm_width %d", fd->valid_bm_width);
2014 GST_INFO_OBJECT (element, " valid_bm_x_offset %d",
2015 fd->valid_bm_x_offset);
2016 GST_INFO_OBJECT (element, " valid_bm_y_offset %d",
2017 fd->valid_bm_y_offset);
2018 GST_INFO_OBJECT (element, " video_x_t_offset %d",
2019 fd->video_x_t_offset);
2020 GST_INFO_OBJECT (element, " video_y_start %d", fd->video_y_start);
2023 gst_buffer_unref (buf);
2032 GST_ERROR_OBJECT (element,
2033 "Too small vprp (%d available, at least %d needed)",
2034 GST_BUFFER_SIZE (buf),
2035 (int) G_STRUCT_OFFSET (gst_riff_vprp, field_info));
2036 gst_buffer_unref (buf);
2042 gst_avi_demux_expose_streams (GstAviDemux * avi, gboolean force)
2046 GST_DEBUG_OBJECT (avi, "force : %d", force);
2048 for (i = 0; i < avi->num_streams; i++) {
2049 GstAviStream *stream = &avi->stream[i];
2051 if (force || stream->idx_n != 0) {
2052 GST_LOG_OBJECT (avi, "Added pad %s with caps %" GST_PTR_FORMAT,
2053 GST_PAD_NAME (stream->pad), GST_PAD_CAPS (stream->pad));
2054 gst_element_add_pad ((GstElement *) avi, stream->pad);
2056 if (avi->element_index)
2057 gst_index_get_writer_id (avi->element_index,
2058 GST_OBJECT_CAST (stream->pad), &stream->index_id);
2060 stream->exposed = TRUE;
2061 if (avi->main_stream == -1)
2062 avi->main_stream = i;
2064 GST_WARNING_OBJECT (avi, "Stream #%d doesn't have any entry, removing it",
2066 gst_avi_demux_reset_stream (avi, stream);
2073 typedef struct _gst_riff_strd {
2080 gst_riff_parse_strd (GstAviDemux * avi,
2083 g_return_val_if_fail (buf != NULL, FALSE);
2085 if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strd))
2088 GST_DEBUG_OBJECT (avi, " version %d", ((gst_riff_strd*)GST_BUFFER_DATA(buf))->version);
2089 GST_DEBUG_OBJECT (avi, " drm_size %d", ((gst_riff_strd*)GST_BUFFER_DATA(buf))->drm_size);
2091 return gst_avi_demux_init_divx_drm (avi, GST_BUFFER_DATA(buf)+sizeof(gst_riff_strd));
2096 GST_ERROR_OBJECT (avi,
2097 "Too small strh (%d available, %d needed)",
2098 GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_strd));
2099 gst_buffer_unref (buf);
2106 /* buf contains LIST chunk data, and will be padded to even size,
2107 * since some buggy files do not account for the padding of chunks
2108 * within a LIST in the size of the LIST */
2110 gst_avi_demux_roundup_list (GstAviDemux * avi, GstBuffer ** buf)
2112 gint size = GST_BUFFER_SIZE (*buf);
2114 if (G_UNLIKELY (size & 1)) {
2117 GST_DEBUG_OBJECT (avi, "rounding up dubious list size %d", size);
2118 obuf = gst_buffer_new_and_alloc (size + 1);
2119 memcpy (GST_BUFFER_DATA (obuf), GST_BUFFER_DATA (*buf), size);
2120 /* assume 0 padding, at least makes outcome deterministic */
2121 (GST_BUFFER_DATA (obuf))[size] = 0;
2122 gst_buffer_replace (buf, obuf);
2127 * gst_avi_demux_parse_stream:
2128 * @avi: calling element (used for debugging/errors).
2129 * @buf: input buffer used to parse the stream.
2131 * Parses all subchunks in a strl chunk (which defines a single
2132 * stream). Discards the buffer after use. This function will
2133 * increment the stream counter internally.
2135 * Returns: whether the stream was identified successfully.
2136 * Errors are not fatal. It does indicate the stream
2140 gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
2142 GstAviStream *stream;
2143 GstElementClass *klass;
2144 GstPadTemplate *templ;
2145 GstBuffer *sub = NULL;
2148 gchar *codec_name = NULL, *padname = NULL;
2149 const gchar *tag_name;
2150 GstCaps *caps = NULL;
2152 GstElement *element;
2153 gboolean got_strh = FALSE, got_strf = FALSE, got_vprp = FALSE;
2154 gst_riff_vprp *vprp = NULL;
2156 element = GST_ELEMENT_CAST (avi);
2158 GST_DEBUG_OBJECT (avi, "Parsing stream");
2160 gst_avi_demux_roundup_list (avi, &buf);
2162 if (avi->num_streams >= GST_AVI_DEMUX_MAX_STREAMS) {
2163 GST_WARNING_OBJECT (avi,
2164 "maximum no of streams (%d) exceeded, ignoring stream",
2165 GST_AVI_DEMUX_MAX_STREAMS);
2166 gst_buffer_unref (buf);
2167 /* not a fatal error, let's say */
2171 stream = &avi->stream[avi->num_streams];
2173 /* initial settings */
2174 stream->idx_duration = GST_CLOCK_TIME_NONE;
2175 stream->hdr_duration = GST_CLOCK_TIME_NONE;
2176 stream->duration = GST_CLOCK_TIME_NONE;
2178 while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
2179 /* sub can be NULL if the chunk is empty */
2181 GST_DEBUG_OBJECT (avi, "ignoring empty chunk %" GST_FOURCC_FORMAT,
2182 GST_FOURCC_ARGS (tag));
2186 case GST_RIFF_TAG_strh:
2188 gst_riff_strh *strh;
2191 GST_WARNING_OBJECT (avi, "Ignoring additional strh chunk");
2194 if (!gst_riff_parse_strh (element, sub, &stream->strh)) {
2195 /* ownership given away */
2197 GST_WARNING_OBJECT (avi, "Failed to parse strh chunk");
2201 strh = stream->strh;
2202 /* sanity check; stream header frame rate matches global header
2204 if (stream->strh->type == GST_RIFF_FCC_vids) {
2206 GstClockTime h_dur = avi->avih->us_frame * GST_USECOND;
2208 s_dur = gst_util_uint64_scale (GST_SECOND, strh->scale, strh->rate);
2209 GST_DEBUG_OBJECT (avi, "verifying stream framerate %d/%d, "
2210 "frame duration = %d ms", strh->rate, strh->scale,
2211 (gint) (s_dur / GST_MSECOND));
2212 if (h_dur > (10 * GST_MSECOND) && (s_dur > 10 * h_dur)) {
2213 strh->rate = GST_SECOND / GST_USECOND;
2214 strh->scale = h_dur / GST_USECOND;
2215 GST_DEBUG_OBJECT (avi, "correcting stream framerate to %d/%d",
2216 strh->rate, strh->scale);
2219 /* determine duration as indicated by header */
2220 stream->hdr_duration = gst_util_uint64_scale ((guint64) strh->length *
2221 strh->scale, GST_SECOND, (guint64) strh->rate);
2222 GST_INFO ("Stream duration according to header: %" GST_TIME_FORMAT,
2223 GST_TIME_ARGS (stream->hdr_duration));
2224 if (stream->hdr_duration == 0)
2225 stream->hdr_duration = GST_CLOCK_TIME_NONE;
2230 case GST_RIFF_TAG_strf:
2232 gboolean res = FALSE;
2235 GST_WARNING_OBJECT (avi, "Ignoring additional strf chunk");
2239 GST_ERROR_OBJECT (avi, "Found strf chunk before strh chunk");
2242 switch (stream->strh->type) {
2243 case GST_RIFF_FCC_vids:
2244 stream->is_vbr = TRUE;
2245 res = gst_riff_parse_strf_vids (element, sub,
2246 &stream->strf.vids, &stream->extradata);
2248 GST_DEBUG_OBJECT (element, "marking video as VBR, res %d", res);
2250 case GST_RIFF_FCC_auds:
2252 gst_riff_parse_strf_auds (element, sub, &stream->strf.auds,
2253 &stream->extradata);
2257 stream->is_vbr = (stream->strh->samplesize == 0)
2258 && stream->strh->scale > 1
2259 && stream->strf.auds->blockalign != 1;
2260 GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d",
2261 stream->is_vbr, res);
2262 /* we need these or we have no way to come up with timestamps */
2263 if ((!stream->is_vbr && !stream->strf.auds->av_bps) ||
2264 (stream->is_vbr && (!stream->strh->scale ||
2265 !stream->strh->rate))) {
2266 GST_WARNING_OBJECT (element,
2267 "invalid audio header, ignoring stream");
2270 /* some more sanity checks */
2271 if (stream->is_vbr) {
2272 if (stream->strf.auds->blockalign <= 4) {
2273 /* that would mean (too) many frames per chunk,
2274 * so not likely set as expected */
2275 GST_DEBUG_OBJECT (element,
2276 "suspicious blockalign %d for VBR audio; "
2277 "overriding to 1 frame per chunk",
2278 stream->strf.auds->blockalign);
2279 /* this should top any likely value */
2280 stream->strf.auds->blockalign = (1 << 12);
2284 case GST_RIFF_FCC_iavs:
2285 stream->is_vbr = TRUE;
2286 res = gst_riff_parse_strf_iavs (element, sub,
2287 &stream->strf.iavs, &stream->extradata);
2289 GST_DEBUG_OBJECT (element, "marking iavs as VBR, res %d", res);
2291 case GST_RIFF_FCC_txts:
2292 /* nothing to parse here */
2293 stream->is_vbr = (stream->strh->samplesize == 0)
2294 && (stream->strh->scale > 1);
2298 GST_ERROR_OBJECT (avi,
2299 "Don´t know how to handle stream type %" GST_FOURCC_FORMAT,
2300 GST_FOURCC_ARGS (stream->strh->type));
2304 gst_buffer_unref (sub);
2312 case GST_RIFF_TAG_vprp:
2315 GST_WARNING_OBJECT (avi, "Ignoring additional vprp chunk");
2319 GST_ERROR_OBJECT (avi, "Found vprp chunk before strh chunk");
2323 GST_ERROR_OBJECT (avi, "Found vprp chunk before strf chunk");
2327 if (!gst_avi_demux_riff_parse_vprp (element, sub, &vprp)) {
2328 GST_WARNING_OBJECT (avi, "Failed to parse vprp chunk");
2329 /* not considered fatal */
2337 case GST_RIFF_TAG_strd:
2339 GST_DEBUG_OBJECT (avi, "******************* strd tag found:");
2340 if (gst_riff_parse_strd (avi, sub) == FALSE) {
2341 GST_ELEMENT_ERROR(avi, STREAM, DECRYPT,
2342 ("DivX initialization failed"),
2343 ("gst_avi_demux_init_divx_drm() failed") );
2348 if (stream->initdata)
2349 gst_buffer_unref (stream->initdata);
2350 stream->initdata = sub;
2353 case GST_RIFF_TAG_strn:
2354 g_free (stream->name);
2357 g_strndup ((gchar *) GST_BUFFER_DATA (sub),
2358 (gsize) GST_BUFFER_SIZE (sub));
2359 gst_buffer_unref (sub);
2362 stream->name = g_strdup ("");
2364 GST_DEBUG_OBJECT (avi, "stream name: %s", stream->name);
2367 gst_avi_demux_parse_idit (avi, sub);
2370 if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
2371 tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
2372 '0' + avi->num_streams % 10)) {
2373 g_free (stream->indexes);
2374 gst_avi_demux_parse_superindex (avi, sub, &stream->indexes);
2375 stream->superindex = TRUE;
2379 GST_WARNING_OBJECT (avi,
2380 "Unknown stream header tag %" GST_FOURCC_FORMAT ", ignoring",
2381 GST_FOURCC_ARGS (tag));
2383 case GST_RIFF_TAG_JUNQ:
2384 case GST_RIFF_TAG_JUNK:
2388 gst_buffer_unref (sub);
2394 GST_WARNING_OBJECT (avi, "Failed to find strh chunk");
2399 GST_WARNING_OBJECT (avi, "Failed to find strf chunk");
2403 /* get class to figure out the template */
2404 klass = GST_ELEMENT_GET_CLASS (avi);
2406 /* we now have all info, let´s set up a pad and a caps and be done */
2407 /* create stream name + pad */
2408 switch (stream->strh->type) {
2409 case GST_RIFF_FCC_vids:{
2412 fourcc = (stream->strf.vids->compression) ?
2413 stream->strf.vids->compression : stream->strh->fcc_handler;
2414 padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
2415 templ = gst_element_class_get_pad_template (klass, "video_%02d");
2416 caps = gst_riff_create_video_caps (fourcc, stream->strh,
2417 stream->strf.vids, stream->extradata, stream->initdata, &codec_name);
2419 caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
2420 GST_TYPE_FOURCC, fourcc, NULL);
2421 } else if (got_vprp && vprp) {
2422 guint32 aspect_n, aspect_d;
2425 aspect_n = vprp->aspect >> 16;
2426 aspect_d = vprp->aspect & 0xffff;
2427 /* calculate the pixel aspect ratio using w/h and aspect ratio */
2428 n = aspect_n * stream->strf.vids->height;
2429 d = aspect_d * stream->strf.vids->width;
2431 gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2433 /* very local, not needed elsewhere */
2437 tag_name = GST_TAG_VIDEO_CODEC;
2438 avi->num_v_streams++;
2441 case GST_RIFF_FCC_auds:{
2442 padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
2443 templ = gst_element_class_get_pad_template (klass, "audio_%02d");
2444 caps = gst_riff_create_audio_caps (stream->strf.auds->format,
2445 stream->strh, stream->strf.auds, stream->extradata,
2446 stream->initdata, &codec_name);
2448 caps = gst_caps_new_simple ("audio/x-avi-unknown", "codec_id",
2449 G_TYPE_INT, stream->strf.auds->format, NULL);
2451 tag_name = GST_TAG_AUDIO_CODEC;
2452 avi->num_a_streams++;
2455 case GST_RIFF_FCC_iavs:{
2456 guint32 fourcc = stream->strh->fcc_handler;
2458 padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
2459 templ = gst_element_class_get_pad_template (klass, "video_%02d");
2460 caps = gst_riff_create_iavs_caps (fourcc, stream->strh,
2461 stream->strf.iavs, stream->extradata, stream->initdata, &codec_name);
2463 caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
2464 GST_TYPE_FOURCC, fourcc, NULL);
2466 tag_name = GST_TAG_VIDEO_CODEC;
2467 avi->num_v_streams++;
2470 case GST_RIFF_FCC_txts:{
2471 padname = g_strdup_printf ("subtitle_%02d", avi->num_t_streams);
2472 templ = gst_element_class_get_pad_template (klass, "subtitle_%02d");
2473 caps = gst_caps_new_simple ("application/x-subtitle-avi", NULL);
2475 avi->num_t_streams++;
2479 g_return_val_if_reached (FALSE);
2482 /* no caps means no stream */
2484 GST_ERROR_OBJECT (element, "Did not find caps for stream %s", padname);
2488 GST_DEBUG_OBJECT (element, "codec-name=%s",
2489 (codec_name ? codec_name : "NULL"));
2490 GST_DEBUG_OBJECT (element, "caps=%" GST_PTR_FORMAT, caps);
2492 /* set proper settings and add it */
2494 gst_object_unref (stream->pad);
2495 pad = stream->pad = gst_pad_new_from_template (templ, padname);
2498 gst_pad_use_fixed_caps (pad);
2500 gst_pad_set_formats_function (pad,
2501 GST_DEBUG_FUNCPTR (gst_avi_demux_get_src_formats));
2502 gst_pad_set_event_mask_function (pad,
2503 GST_DEBUG_FUNCPTR (gst_avi_demux_get_event_mask));
2505 gst_pad_set_event_function (pad,
2506 GST_DEBUG_FUNCPTR (gst_avi_demux_handle_src_event));
2507 gst_pad_set_query_type_function (pad,
2508 GST_DEBUG_FUNCPTR (gst_avi_demux_get_src_query_types));
2509 gst_pad_set_query_function (pad,
2510 GST_DEBUG_FUNCPTR (gst_avi_demux_handle_src_query));
2512 gst_pad_set_convert_function (pad,
2513 GST_DEBUG_FUNCPTR (gst_avi_demux_src_convert));
2516 stream->num = avi->num_streams;
2518 stream->start_entry = 0;
2519 stream->step_entry = 0;
2520 stream->stop_entry = 0;
2522 stream->current_entry = -1;
2523 stream->current_total = 0;
2525 stream->last_flow = GST_FLOW_OK;
2526 stream->discont = TRUE;
2528 stream->total_bytes = 0;
2529 stream->total_blocks = 0;
2530 stream->n_keyframes = 0;
2533 stream->idx_max = 0;
2535 gst_pad_set_element_private (pad, stream);
2538 #ifdef AVIDEMUX_MODIFICATION
2539 stream->trickplay_info = g_new0 (TrickPlayInfo, 1);
2540 stream->trickplay_info->prev_kidx = 0;
2541 stream->trickplay_info->next_kidx = 0;
2542 stream->trickplay_info->kidxs_dur_diff = 0;
2544 gst_pad_set_caps (pad, caps);
2545 gst_pad_set_active (pad, TRUE);
2546 gst_caps_unref (caps);
2550 if (!stream->taglist)
2551 stream->taglist = gst_tag_list_new ();
2553 avi->got_tags = TRUE;
2555 gst_tag_list_add (stream->taglist, GST_TAG_MERGE_APPEND, tag_name,
2557 g_free (codec_name);
2560 gst_buffer_unref (buf);
2567 /* unref any mem that may be in use */
2569 gst_buffer_unref (buf);
2571 gst_buffer_unref (sub);
2573 g_free (codec_name);
2574 gst_avi_demux_reset_stream (avi, stream);
2581 * gst_avi_demux_parse_odml:
2582 * @avi: calling element (used for debug/error).
2583 * @buf: input buffer to be used for parsing.
2585 * Read an openDML-2.0 extension header. Fills in the frame number
2586 * in the avi demuxer object when reading succeeds.
2589 gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf)
2593 GstBuffer *sub = NULL;
2595 while (gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
2598 case GST_RIFF_TAG_dmlh:{
2599 gst_riff_dmlh dmlh, *_dmlh;
2602 /* sub == NULL is possible and means an empty buffer */
2603 size = sub ? GST_BUFFER_SIZE (sub) : 0;
2606 if (size < sizeof (gst_riff_dmlh)) {
2607 GST_ERROR_OBJECT (avi,
2608 "DMLH entry is too small (%d bytes, %d needed)",
2609 size, (int) sizeof (gst_riff_dmlh));
2612 _dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (sub);
2613 dmlh.totalframes = GST_READ_UINT32_LE (&_dmlh->totalframes);
2615 GST_INFO_OBJECT (avi, "dmlh tag found: totalframes: %u",
2618 avi->avih->tot_frames = dmlh.totalframes;
2623 GST_WARNING_OBJECT (avi,
2624 "Unknown tag %" GST_FOURCC_FORMAT " in ODML header",
2625 GST_FOURCC_ARGS (tag));
2627 case GST_RIFF_TAG_JUNQ:
2628 case GST_RIFF_TAG_JUNK:
2630 /* skip and move to next chunk */
2632 gst_buffer_unref (sub);
2639 gst_buffer_unref (buf);
2644 gst_avi_demux_index_last (GstAviDemux * avi, GstAviStream * stream)
2646 return stream->idx_n;
2649 /* find a previous entry in the index with the given flags */
2651 gst_avi_demux_index_prev (GstAviDemux * avi, GstAviStream * stream,
2652 guint last, gboolean keyframe)
2654 GstAviIndexEntry *entry;
2657 for (i = last; i > 0; i--) {
2658 entry = &stream->index[i - 1];
2659 if (!keyframe || ENTRY_IS_KEYFRAME (entry)) {
2667 gst_avi_demux_index_next (GstAviDemux * avi, GstAviStream * stream,
2668 guint last, gboolean keyframe)
2670 GstAviIndexEntry *entry;
2673 for (i = last + 1; i < stream->idx_n; i++) {
2674 entry = &stream->index[i];
2675 if (!keyframe || ENTRY_IS_KEYFRAME (entry)) {
2679 return stream->idx_n - 1;
2683 gst_avi_demux_index_entry_search (GstAviIndexEntry * entry, guint64 * total)
2685 if (entry->total < *total)
2687 else if (entry->total > *total)
2693 * gst_avi_demux_index_for_time:
2695 * @stream: the stream
2696 * @time: a time position
2698 * Finds the index entry which time is less or equal than the requested time.
2699 * Try to avoid binary search when we can convert the time to an index
2700 * position directly (for example for video frames with a fixed duration).
2702 * Returns: the found position in the index.
2705 gst_avi_demux_index_for_time (GstAviDemux * avi,
2706 GstAviStream * stream, guint64 time)
2711 GST_LOG_OBJECT (avi, "search time:%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
2713 /* easy (and common) cases */
2714 if (time == 0 || stream->idx_n == 0)
2716 if (time >= stream->idx_duration)
2717 return stream->idx_n - 1;
2719 /* figure out where we need to go. For that we convert the time to an
2720 * index entry or we convert it to a total and then do a binary search. */
2721 if (stream->is_vbr) {
2722 /* VBR stream next timestamp */
2723 if (stream->strh->type == GST_RIFF_FCC_auds) {
2724 total = avi_stream_convert_time_to_frames_unchecked (stream, time);
2726 index = avi_stream_convert_time_to_frames_unchecked (stream, time);
2729 /* constant rate stream */
2730 total = avi_stream_convert_time_to_bytes_unchecked (stream, time);
2734 GstAviIndexEntry *entry;
2736 /* no index, find index with binary search on total */
2737 GST_LOG_OBJECT (avi, "binary search for entry with total %"
2738 G_GUINT64_FORMAT, total);
2740 entry = gst_util_array_binary_search (stream->index,
2741 stream->idx_n, sizeof (GstAviIndexEntry),
2742 (GCompareDataFunc) gst_avi_demux_index_entry_search,
2743 GST_SEARCH_MODE_BEFORE, &total, NULL);
2745 if (entry == NULL) {
2746 GST_LOG_OBJECT (avi, "not found, assume index 0");
2749 index = entry - stream->index;
2750 GST_LOG_OBJECT (avi, "found at %u", index);
2753 GST_LOG_OBJECT (avi, "converted time to index %u", index);
2759 static inline GstAviStream *
2760 gst_avi_demux_stream_for_id (GstAviDemux * avi, guint32 id)
2763 GstAviStream *stream;
2765 /* get the stream for this entry */
2766 stream_nr = CHUNKID_TO_STREAMNR (id);
2767 if (G_UNLIKELY (stream_nr >= avi->num_streams)) {
2768 GST_WARNING_OBJECT (avi, "invalid stream nr %d", stream_nr);
2771 stream = &avi->stream[stream_nr];
2772 if (G_UNLIKELY (!stream->strh)) {
2773 GST_WARNING_OBJECT (avi, "Unhandled stream %d, skipping", stream_nr);
2780 * gst_avi_demux_parse_index:
2781 * @avi: calling element (used for debugging/errors).
2782 * @buf: buffer containing the full index.
2784 * Read index entries from the provided buffer.
2785 * The buffer should contain a GST_RIFF_TAG_idx1 chunk.
2788 gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
2793 gst_riff_index_entry *index;
2795 GstAviStream *stream;
2796 GstAviIndexEntry entry = {0};
2802 data = GST_BUFFER_DATA (buf);
2803 size = GST_BUFFER_SIZE (buf);
2805 stamp = gst_util_get_timestamp ();
2807 /* see how many items in the index */
2808 num = size / sizeof (gst_riff_index_entry);
2812 GST_INFO_OBJECT (avi, "Parsing index, nr_entries = %6d", num);
2814 index = (gst_riff_index_entry *) data;
2816 /* figure out if the index is 0 based or relative to the MOVI start */
2817 entry.offset = GST_READ_UINT32_LE (&index[0].offset);
2818 if (entry.offset < avi->offset) {
2819 avi->index_offset = avi->offset + 8;
2820 GST_DEBUG ("index_offset = %" G_GUINT64_FORMAT, avi->index_offset);
2822 avi->index_offset = 0;
2823 GST_DEBUG ("index is 0 based");
2826 for (i = 0, n = 0; i < num; i++) {
2827 id = GST_READ_UINT32_LE (&index[i].id);
2828 entry.offset = GST_READ_UINT32_LE (&index[i].offset);
2830 /* some sanity checks */
2831 if (G_UNLIKELY (id == GST_RIFF_rec || id == 0 ||
2832 (entry.offset == 0 && n > 0)))
2835 #ifdef DIVX_DRM /* need to check using same define */
2836 if ( id == GST_MAKE_FOURCC('0','0','d','d') )
2838 GST_DEBUG("Skipping Encrypt data chunk");
2843 /* get the stream for this entry */
2844 stream = gst_avi_demux_stream_for_id (avi, id);
2845 if (G_UNLIKELY (!stream))
2848 /* handle offset and size */
2849 entry.offset += avi->index_offset + 8;
2850 entry.size = GST_READ_UINT32_LE (&index[i].size);
2853 if (stream->strh->type == GST_RIFF_FCC_auds) {
2854 /* all audio frames are keyframes */
2855 ENTRY_SET_KEYFRAME (&entry);
2858 /* else read flags */
2859 flags = GST_READ_UINT32_LE (&index[i].flags);
2860 if (flags & GST_RIFF_IF_KEYFRAME) {
2861 ENTRY_SET_KEYFRAME (&entry);
2863 ENTRY_UNSET_KEYFRAME (&entry);
2868 if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
2873 gst_buffer_unref (buf);
2875 /* get stream stats now */
2876 avi->have_index = gst_avi_demux_do_index_stats (avi);
2878 stamp = gst_util_get_timestamp () - stamp;
2879 GST_DEBUG_OBJECT (avi, "index parsing took %" GST_TIME_FORMAT,
2880 GST_TIME_ARGS (stamp));
2887 GST_DEBUG_OBJECT (avi, "empty index");
2888 gst_buffer_unref (buf);
2893 GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
2894 ("Cannot allocate memory for %u*%u=%u bytes",
2895 (guint) sizeof (GstAviIndexEntry), num,
2896 (guint) sizeof (GstAviIndexEntry) * num));
2897 gst_buffer_unref (buf);
2903 * gst_avi_demux_stream_index:
2904 * @avi: avi demuxer object.
2906 * Seeks to index and reads it.
2909 gst_avi_demux_stream_index (GstAviDemux * avi)
2912 guint64 offset = avi->offset;
2917 GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset);
2919 /* get chunk information */
2920 res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2921 if (res != GST_FLOW_OK)
2923 else if (GST_BUFFER_SIZE (buf) < 8)
2926 /* check tag first before blindy trying to read 'size' bytes */
2927 tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
2928 size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
2929 if (tag == GST_RIFF_TAG_LIST) {
2930 /* this is the movi tag */
2931 GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
2932 (8 + GST_ROUND_UP_2 (size)));
2933 offset += 8 + GST_ROUND_UP_2 (size);
2934 gst_buffer_unref (buf);
2935 res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2936 if (res != GST_FLOW_OK)
2938 else if (GST_BUFFER_SIZE (buf) < 8)
2940 tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
2941 size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
2944 if (tag != GST_RIFF_TAG_idx1)
2949 gst_buffer_unref (buf);
2951 GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset);
2953 /* read chunk, advance offset */
2954 if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi),
2955 avi->sinkpad, &offset, &tag, &buf) != GST_FLOW_OK)
2958 GST_DEBUG ("will parse index chunk size %u for tag %"
2959 GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
2961 gst_avi_demux_parse_index (avi, buf);
2963 #ifndef GST_DISABLE_GST_DEBUG
2964 /* debug our indexes */
2967 GstAviStream *stream;
2969 for (i = 0; i < avi->num_streams; i++) {
2970 stream = &avi->stream[i];
2971 GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes",
2972 i, stream->idx_n, stream->total_bytes);
2981 GST_DEBUG_OBJECT (avi,
2982 "pull range failed: pos=%" G_GUINT64_FORMAT " size=8", offset);
2987 GST_DEBUG_OBJECT (avi, "Buffer is too small");
2988 gst_buffer_unref (buf);
2993 GST_WARNING_OBJECT (avi,
2994 "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT,
2995 GST_FOURCC_ARGS (tag));
2996 gst_buffer_unref (buf);
3001 GST_WARNING_OBJECT (avi, "Empty index data (idx1) after movi chunk");
3002 gst_buffer_unref (buf);
3008 * gst_avi_demux_stream_index_push:
3009 * @avi: avi demuxer object.
3014 gst_avi_demux_stream_index_push (GstAviDemux * avi)
3016 guint64 offset = avi->idx1_offset;
3021 GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset);
3023 /* get chunk information */
3024 if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
3027 /* check tag first before blindly trying to read 'size' bytes */
3028 if (tag == GST_RIFF_TAG_LIST) {
3029 /* this is the movi tag */
3030 GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
3031 (8 + GST_ROUND_UP_2 (size)));
3032 avi->idx1_offset = offset + 8 + GST_ROUND_UP_2 (size);
3033 /* issue seek to allow chain function to handle it and return! */
3034 perform_seek_to_offset (avi, avi->idx1_offset);
3038 if (tag != GST_RIFF_TAG_idx1)
3041 GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset);
3043 /* flush chunk header */
3044 gst_adapter_flush (avi->adapter, 8);
3045 /* read chunk payload */
3046 buf = gst_adapter_take_buffer (avi->adapter, size);
3049 /* advance offset */
3050 offset += 8 + GST_ROUND_UP_2 (size);
3052 GST_DEBUG ("will parse index chunk size %u for tag %"
3053 GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
3055 avi->offset = avi->first_movi_offset;
3056 gst_avi_demux_parse_index (avi, buf);
3058 #ifndef GST_DISABLE_GST_DEBUG
3059 /* debug our indexes */
3062 GstAviStream *stream;
3064 for (i = 0; i < avi->num_streams; i++) {
3065 stream = &avi->stream[i];
3066 GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes",
3067 i, stream->idx_n, stream->total_bytes);
3076 GST_DEBUG_OBJECT (avi,
3077 "taking data from adapter failed: pos=%" G_GUINT64_FORMAT " size=%u",
3083 GST_WARNING_OBJECT (avi,
3084 "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT,
3085 GST_FOURCC_ARGS (tag));
3091 * gst_avi_demux_peek_tag:
3093 * Returns the tag and size of the next chunk
3095 static GstFlowReturn
3096 gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
3099 GstFlowReturn res = GST_FLOW_OK;
3100 GstBuffer *buf = NULL;
3104 res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
3105 if (res != GST_FLOW_OK)
3108 bufsize = GST_BUFFER_SIZE (buf);
3112 bufdata = GST_BUFFER_DATA (buf);
3114 *tag = GST_READ_UINT32_LE (bufdata);
3115 *size = GST_READ_UINT32_LE (bufdata + 4);
3117 GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %"
3118 G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag),
3119 *size, offset + 8, offset + 8 + (gint64) * size);
3122 gst_buffer_unref (buf);
3129 GST_DEBUG_OBJECT (avi, "pull_ranged returned %s", gst_flow_get_name (res));
3134 GST_DEBUG_OBJECT (avi, "got %d bytes which is <> 8 bytes", bufsize);
3135 res = GST_FLOW_ERROR;
3141 * gst_avi_demux_next_data_buffer:
3143 * Returns the offset and size of the next buffer
3144 * Position is the position of the buffer (after tag and size)
3146 static GstFlowReturn
3147 gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
3148 guint32 * tag, guint * size)
3150 guint64 off = *offset;
3155 res = gst_avi_demux_peek_tag (avi, off, tag, &_size);
3156 if (res != GST_FLOW_OK)
3158 if (*tag == GST_RIFF_TAG_LIST || *tag == GST_RIFF_TAG_RIFF)
3159 off += 8 + 4; /* skip tag + size + subtag */
3171 * gst_avi_demux_stream_scan:
3172 * @avi: calling element (used for debugging/errors).
3174 * Scan the file for all chunks to "create" a new index.
3178 gst_avi_demux_stream_scan (GstAviDemux * avi)
3181 GstAviStream *stream;
3190 * - implement non-seekable source support.
3192 GST_DEBUG_OBJECT (avi, "Creating index");
3194 /* get the size of the file */
3195 format = GST_FORMAT_BYTES;
3196 if (!gst_pad_query_peer_duration (avi->sinkpad, &format, &tmplength))
3200 /* guess the total amount of entries we expect */
3204 GstAviIndexEntry entry;
3206 #ifdef AVIDEMUX_MODIFICATION
3207 gint frame_type = GST_AVI_KEYFRAME;
3210 /* start reading data buffers to find the id and offset */
3211 res = gst_avi_demux_next_data_buffer (avi, &pos, &tag, &size);
3212 if (G_UNLIKELY (res != GST_FLOW_OK))
3216 stream = gst_avi_demux_stream_for_id (avi, tag);
3217 if (G_UNLIKELY (!stream))
3219 #ifdef AVIDEMUX_MODIFICATION
3220 /* generating index table with key frames */
3221 if (stream->strh->type == GST_RIFF_FCC_vids) {
3222 GstBuffer *buf = NULL;
3225 res = gst_pad_pull_range (avi->sinkpad, pos, size, &buf);
3226 if (res != GST_FLOW_OK) {
3227 gst_buffer_unref (buf);
3228 GST_ERROR ("Pull failed....\n\n");
3231 ret = gst_avi_demux_find_frame_type (stream, buf, &frame_type);
3234 gst_buffer_unref (buf);
3236 entry.flags = frame_type;
3238 /* we can't figure out the keyframes, assume they all are */
3239 entry.flags = GST_AVI_KEYFRAME;
3244 /* and add to the index of this stream */
3245 if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
3249 /* update position */
3250 pos += GST_ROUND_UP_2 (size);
3251 if (G_UNLIKELY (pos > length)) {
3252 GST_WARNING_OBJECT (avi,
3253 "Stopping index lookup since we are further than EOF");
3259 avi->have_index = gst_avi_demux_do_index_stats (avi);
3266 GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
3267 ("Cannot allocate memory for %u*%u=%u bytes",
3268 (guint) sizeof (GstAviIndexEntry), num,
3269 (guint) sizeof (GstAviIndexEntry) * num));
3275 gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
3279 GstAviStream *stream;
3281 total = GST_CLOCK_TIME_NONE;
3283 /* all streams start at a timestamp 0 */
3284 for (i = 0; i < avi->num_streams; i++) {
3285 GstClockTime duration, hduration;
3286 gst_riff_strh *strh;
3288 stream = &avi->stream[i];
3289 if (G_UNLIKELY (!stream || !stream->idx_n || !(strh = stream->strh)))
3292 /* get header duration for the stream */
3293 hduration = stream->hdr_duration;
3294 /* index duration calculated during parsing */
3295 duration = stream->idx_duration;
3297 /* now pick a good duration */
3298 if (GST_CLOCK_TIME_IS_VALID (duration)) {
3299 /* index gave valid duration, use that */
3300 GST_INFO ("Stream %p duration according to index: %" GST_TIME_FORMAT,
3301 stream, GST_TIME_ARGS (duration));
3303 /* fall back to header info to calculate a duration */
3304 duration = hduration;
3306 GST_INFO ("Setting duration of stream #%d to %" GST_TIME_FORMAT,
3307 i, GST_TIME_ARGS (duration));
3308 /* set duration for the stream */
3309 stream->duration = duration;
3311 /* find total duration */
3312 if (total == GST_CLOCK_TIME_NONE ||
3313 (GST_CLOCK_TIME_IS_VALID (duration) && duration > total))
3317 if (GST_CLOCK_TIME_IS_VALID (total) && (total > 0)) {
3318 /* now update the duration for those streams where we had none */
3319 for (i = 0; i < avi->num_streams; i++) {
3320 stream = &avi->stream[i];
3322 if (!GST_CLOCK_TIME_IS_VALID (stream->duration)
3323 || stream->duration == 0) {
3324 stream->duration = total;
3326 GST_INFO ("Stream %p duration according to total: %" GST_TIME_FORMAT,
3327 stream, GST_TIME_ARGS (total));
3332 /* and set the total duration in the segment. */
3333 GST_INFO ("Setting total duration to: %" GST_TIME_FORMAT,
3334 GST_TIME_ARGS (total));
3336 gst_segment_set_duration (&avi->segment, GST_FORMAT_TIME, total);
3339 #ifdef AVIDEMUX_MODIFICATION
3341 gst_avi_demux_calculate_durations_from_strh (GstAviDemux * avi)
3345 GstAviStream *stream;
3347 total = GST_CLOCK_TIME_NONE;
3349 /* all streams start at a timestamp 0 */
3350 for (i = 0; i < avi->num_streams; i++) {
3351 GstClockTime hduration;
3352 gst_riff_strh *strh;
3354 stream = &avi->stream[i];
3356 if (G_UNLIKELY (!stream || !(strh = stream->strh)))
3359 /* get header duration for the stream */
3360 hduration = stream->hdr_duration;
3362 /* check duration */
3363 if (GST_CLOCK_TIME_IS_VALID (hduration)) {
3364 GST_INFO ("Stream %p duration according to strh: %" GST_TIME_FORMAT,
3365 stream, GST_TIME_ARGS (hduration));
3368 GST_INFO ("Setting duration of stream #%d to %" GST_TIME_FORMAT,
3369 i, GST_TIME_ARGS (hduration));
3371 /* set duration for the stream */
3372 stream->duration = hduration;
3374 /* find total duration */
3375 if (total == GST_CLOCK_TIME_NONE ||
3376 (GST_CLOCK_TIME_IS_VALID (hduration) && hduration > total))
3380 if (GST_CLOCK_TIME_IS_VALID (total) && (total > 0)) {
3381 /* now update the duration for those streams where we had none */
3382 for (i = 0; i < avi->num_streams; i++) {
3383 stream = &avi->stream[i];
3385 if (!GST_CLOCK_TIME_IS_VALID (stream->duration)
3386 || stream->duration == 0) {
3387 stream->duration = total;
3389 GST_INFO ("Stream %p duration according to total: %" GST_TIME_FORMAT,
3390 stream, GST_TIME_ARGS (total));
3395 /* and set the total duration in the segment. */
3396 GST_INFO ("Setting total duration to: %" GST_TIME_FORMAT,
3397 GST_TIME_ARGS (total));
3399 gst_segment_set_duration (&avi->segment, GST_FORMAT_TIME, total);
3403 /* returns FALSE if there are no pads to deliver event to,
3404 * otherwise TRUE (whatever the outcome of event sending),
3405 * takes ownership of the event. */
3407 gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event)
3409 gboolean result = FALSE;
3412 GST_DEBUG_OBJECT (avi, "sending %s event to %d streams",
3413 GST_EVENT_TYPE_NAME (event), avi->num_streams);
3415 for (i = 0; i < avi->num_streams; i++) {
3416 GstAviStream *stream = &avi->stream[i];
3420 gst_pad_push_event (stream->pad, gst_event_ref (event));
3423 gst_event_unref (event);
3428 gst_avi_demux_check_seekability (GstAviDemux * avi)
3431 gboolean seekable = FALSE;
3432 gint64 start = -1, stop = -1;
3434 query = gst_query_new_seeking (GST_FORMAT_BYTES);
3435 if (!gst_pad_peer_query (avi->sinkpad, query)) {
3436 GST_DEBUG_OBJECT (avi, "seeking query failed");
3440 gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
3442 /* try harder to query upstream size if we didn't get it the first time */
3443 if (seekable && stop == -1) {
3444 GstFormat fmt = GST_FORMAT_BYTES;
3446 GST_DEBUG_OBJECT (avi, "doing duration query to fix up unset stop");
3447 gst_pad_query_peer_duration (avi->sinkpad, &fmt, &stop);
3450 /* if upstream doesn't know the size, it's likely that it's not seekable in
3451 * practice even if it technically may be seekable */
3452 if (seekable && (start != 0 || stop <= start)) {
3453 GST_DEBUG_OBJECT (avi, "seekable but unknown start/stop -> disable");
3458 GST_INFO_OBJECT (avi, "seekable: %d (%" G_GUINT64_FORMAT " - %"
3459 G_GUINT64_FORMAT ")", seekable, start, stop);
3460 avi->seekable = seekable;
3462 gst_query_unref (query);
3466 * Read AVI headers when streaming
3468 static GstFlowReturn
3469 gst_avi_demux_stream_header_push (GstAviDemux * avi)
3471 GstFlowReturn ret = GST_FLOW_OK;
3476 GstBuffer *buf = NULL, *sub = NULL;
3480 GstTagList *tags = NULL;
3482 GST_DEBUG ("Reading and parsing avi headers: %d", avi->header_state);
3484 switch (avi->header_state) {
3485 case GST_AVI_DEMUX_HEADER_TAG_LIST:
3486 if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3487 avi->offset += 8 + GST_ROUND_UP_2 (size);
3488 if (tag != GST_RIFF_TAG_LIST)
3489 goto header_no_list;
3491 gst_adapter_flush (avi->adapter, 8);
3492 /* Find the 'hdrl' LIST tag */
3493 GST_DEBUG ("Reading %d bytes", size);
3494 buf = gst_adapter_take_buffer (avi->adapter, size);
3496 if (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl)
3497 goto header_no_hdrl;
3501 gst_adapter_flush (avi->adapter, 1);
3503 GST_DEBUG ("'hdrl' LIST tag found. Parsing next chunk");
3505 gst_avi_demux_roundup_list (avi, &buf);
3507 /* the hdrl starts with a 'avih' header */
3508 if (!gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
3510 goto header_no_avih;
3512 if (tag != GST_RIFF_TAG_avih)
3513 goto header_no_avih;
3515 if (!gst_avi_demux_parse_avih (avi, sub, &avi->avih))
3516 goto header_wrong_avih;
3518 GST_DEBUG_OBJECT (avi, "AVI header ok, reading elemnts from header");
3520 /* now, read the elements from the header until the end */
3521 while (gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
3523 /* sub can be NULL on empty tags */
3528 case GST_RIFF_TAG_LIST:
3529 if (GST_BUFFER_SIZE (sub) < 4)
3532 switch (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub))) {
3533 case GST_RIFF_LIST_strl:
3534 if (!(gst_avi_demux_parse_stream (avi, sub))) {
3536 GST_ELEMENT_WARNING (avi, STREAM, DEMUX, (NULL),
3537 ("failed to parse stream, ignoring"));
3542 case GST_RIFF_LIST_odml:
3543 gst_avi_demux_parse_odml (avi, sub);
3547 GST_WARNING_OBJECT (avi,
3548 "Unknown list %" GST_FOURCC_FORMAT " in AVI header",
3549 GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA
3552 case GST_RIFF_TAG_JUNQ:
3553 case GST_RIFF_TAG_JUNK:
3558 gst_avi_demux_parse_idit (avi, sub);
3561 GST_WARNING_OBJECT (avi,
3562 "Unknown off %d tag %" GST_FOURCC_FORMAT " in AVI header",
3563 offset, GST_FOURCC_ARGS (tag));
3565 case GST_RIFF_TAG_JUNQ:
3566 case GST_RIFF_TAG_JUNK:
3568 /* move to next chunk */
3570 gst_buffer_unref (sub);
3575 gst_buffer_unref (buf);
3576 GST_DEBUG ("elements parsed");
3578 /* check parsed streams */
3579 if (avi->num_streams == 0) {
3581 } else if (avi->num_streams != avi->avih->streams) {
3582 GST_WARNING_OBJECT (avi,
3583 "Stream header mentioned %d streams, but %d available",
3584 avi->avih->streams, avi->num_streams);
3586 GST_DEBUG ("Get junk and info next");
3587 avi->header_state = GST_AVI_DEMUX_HEADER_INFO;
3589 /* Need more data */
3593 case GST_AVI_DEMUX_HEADER_INFO:
3594 GST_DEBUG_OBJECT (avi, "skipping junk between header and data ...");
3596 if (gst_adapter_available (avi->adapter) < 12)
3599 data = gst_adapter_peek (avi->adapter, 12);
3600 tag = GST_READ_UINT32_LE (data);
3601 size = GST_READ_UINT32_LE (data + 4);
3602 ltag = GST_READ_UINT32_LE (data + 8);
3604 if (tag == GST_RIFF_TAG_LIST) {
3606 case GST_RIFF_LIST_movi:
3607 gst_adapter_flush (avi->adapter, 12);
3608 if (!avi->first_movi_offset)
3609 avi->first_movi_offset = avi->offset;
3611 avi->idx1_offset = avi->offset + size - 4;
3613 case GST_RIFF_LIST_INFO:
3614 GST_DEBUG ("Found INFO chunk");
3615 if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3616 GST_DEBUG ("got size %d", size);
3618 gst_adapter_flush (avi->adapter, 12);
3620 buf = gst_adapter_take_buffer (avi->adapter, size - 4);
3623 gst_adapter_flush (avi->adapter, 1);
3624 gst_riff_parse_info (GST_ELEMENT_CAST (avi), buf, &tags);
3626 if (avi->globaltags) {
3627 gst_tag_list_insert (avi->globaltags, tags,
3628 GST_TAG_MERGE_REPLACE);
3630 avi->globaltags = tags;
3634 gst_buffer_unref (buf);
3636 avi->offset += GST_ROUND_UP_2 (size) - 4;
3638 GST_DEBUG ("skipping INFO LIST prefix");
3641 /* Need more data */
3646 if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3647 avi->offset += 8 + GST_ROUND_UP_2 (size);
3648 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
3649 // ??? goto iterate; ???
3651 /* Need more data */
3657 if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3658 avi->offset += 8 + GST_ROUND_UP_2 (size);
3659 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
3662 /* Need more data */
3669 GST_WARNING ("unhandled header state: %d", avi->header_state);
3674 GST_DEBUG_OBJECT (avi, "skipping done ... (streams=%u, stream[0].indexes=%p)",
3675 avi->num_streams, avi->stream[0].indexes);
3677 GST_DEBUG ("Found movi chunk. Starting to stream data");
3678 avi->state = GST_AVI_DEMUX_MOVI;
3680 #ifdef AVIDEMUX_MODIFICATION
3681 /*no indexs in push mode, but it could be get from strh chunk */
3682 gst_avi_demux_calculate_durations_from_strh (avi);
3684 /* no indexes in push mode, but it still sets some variables */
3685 gst_avi_demux_calculate_durations_from_index (avi);
3688 gst_avi_demux_expose_streams (avi, TRUE);
3690 /* prepare all streams for index 0 */
3691 for (i = 0; i < avi->num_streams; i++)
3692 avi->stream[i].current_entry = 0;
3694 /* create initial NEWSEGMENT event */
3695 if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
3696 stop = avi->segment.duration;
3698 GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop);
3701 gst_event_unref (avi->seg_event);
3702 avi->seg_event = gst_event_new_new_segment_full
3703 (FALSE, avi->segment.rate, avi->segment.applied_rate, GST_FORMAT_TIME,
3704 avi->segment.start, stop, avi->segment.time);
3706 gst_avi_demux_check_seekability (avi);
3708 /* at this point we know all the streams and we can signal the no more
3710 GST_DEBUG_OBJECT (avi, "signaling no more pads");
3711 gst_element_no_more_pads (GST_ELEMENT_CAST (avi));
3718 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
3719 return GST_FLOW_ERROR;
3723 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3724 ("Invalid AVI header (no LIST at start): %"
3725 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3726 return GST_FLOW_ERROR;
3730 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3731 ("Invalid AVI header (no hdrl at start): %"
3732 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3733 gst_buffer_unref (buf);
3734 return GST_FLOW_ERROR;
3738 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3739 ("Invalid AVI header (no avih at start): %"
3740 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3742 gst_buffer_unref (sub);
3744 gst_buffer_unref (buf);
3745 return GST_FLOW_ERROR;
3749 gst_buffer_unref (buf);
3750 return GST_FLOW_ERROR;
3755 gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d,
3756 gint h, gint min, gint s)
3761 date = g_date_new_dmy (d, m, y);
3762 if (!g_date_valid (date)) {
3764 GST_WARNING_OBJECT (avi, "Refusing to add invalid date %d-%d-%d", y, m, d);
3769 dt = gst_date_time_new_local_time (y, m, d, h, min, s);
3771 if (avi->globaltags == NULL)
3772 avi->globaltags = gst_tag_list_new ();
3774 gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date,
3778 gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
3780 gst_date_time_unref (dt);
3785 gst_avi_demux_parse_idit_nums_only (GstAviDemux * avi, gchar * data)
3788 gint hr = 0, min = 0, sec = 0;
3791 GST_DEBUG ("data : '%s'", data);
3793 ret = sscanf (data, "%d:%d:%d %d:%d:%d", &y, &m, &d, &hr, &min, &sec);
3795 /* Attempt YYYY/MM/DD/ HH:MM variant (found in CASIO cameras) */
3796 ret = sscanf (data, "%04d/%02d/%02d/ %d:%d", &y, &m, &d, &hr, &min);
3798 GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
3802 gst_avi_demux_add_date_tag (avi, y, m, d, hr, min, sec);
3806 get_month_num (gchar * data, guint size)
3808 if (g_ascii_strncasecmp (data, "jan", 3) == 0) {
3810 } else if (g_ascii_strncasecmp (data, "feb", 3) == 0) {
3812 } else if (g_ascii_strncasecmp (data, "mar", 3) == 0) {
3814 } else if (g_ascii_strncasecmp (data, "apr", 3) == 0) {
3816 } else if (g_ascii_strncasecmp (data, "may", 3) == 0) {
3818 } else if (g_ascii_strncasecmp (data, "jun", 3) == 0) {
3820 } else if (g_ascii_strncasecmp (data, "jul", 3) == 0) {
3822 } else if (g_ascii_strncasecmp (data, "aug", 3) == 0) {
3824 } else if (g_ascii_strncasecmp (data, "sep", 3) == 0) {
3826 } else if (g_ascii_strncasecmp (data, "oct", 3) == 0) {
3828 } else if (g_ascii_strncasecmp (data, "nov", 3) == 0) {
3830 } else if (g_ascii_strncasecmp (data, "dec", 3) == 0) {
3838 gst_avi_demux_parse_idit_text (GstAviDemux * avi, gchar * data)
3840 gint year, month, day;
3841 gint hour, min, sec;
3846 ret = sscanf (data, "%3s %3s %d %d:%d:%d %d", weekday, monthstr, &day, &hour,
3849 GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
3852 month = get_month_num (monthstr, strlen (monthstr));
3853 gst_avi_demux_add_date_tag (avi, year, month, day, hour, min, sec);
3857 gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf)
3859 gchar *data = (gchar *) GST_BUFFER_DATA (buf);
3860 guint size = GST_BUFFER_SIZE (buf);
3861 gchar *safedata = NULL;
3865 * http://www.eden-foundation.org/products/code/film_date_stamp/index.html
3867 * This tag could be in one of the below formats
3868 * 2005:08:17 11:42:43
3869 * THU OCT 26 16:46:04 2006
3870 * Mon Mar 3 09:44:56 2008
3872 * FIXME: Our date tag doesn't include hours
3875 /* skip eventual initial whitespace */
3876 while (size > 0 && g_ascii_isspace (data[0])) {
3885 /* make a safe copy to add a \0 to the end of the string */
3886 safedata = g_strndup (data, size);
3888 /* test if the first char is a alpha or a number */
3889 if (g_ascii_isdigit (data[0])) {
3890 gst_avi_demux_parse_idit_nums_only (avi, safedata);
3893 } else if (g_ascii_isalpha (data[0])) {
3894 gst_avi_demux_parse_idit_text (avi, safedata);
3902 GST_WARNING_OBJECT (avi, "IDIT tag has no parsable info");
3906 * Read full AVI headers.
3908 static GstFlowReturn
3909 gst_avi_demux_stream_header_pull (GstAviDemux * avi)
3912 GstBuffer *buf, *sub = NULL;
3916 GstElement *element = GST_ELEMENT_CAST (avi);
3918 GstTagList *tags = NULL;
3920 stamp = gst_util_get_timestamp ();
3922 /* the header consists of a 'hdrl' LIST tag */
3923 res = gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag, &buf);
3924 if (res != GST_FLOW_OK)
3925 goto pull_range_failed;
3926 else if (tag != GST_RIFF_TAG_LIST)
3928 else if (GST_BUFFER_SIZE (buf) < 4)
3931 GST_DEBUG_OBJECT (avi, "parsing headers");
3933 /* Find the 'hdrl' LIST tag */
3934 while (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl) {
3935 GST_LOG_OBJECT (avi, "buffer contains %" GST_FOURCC_FORMAT,
3936 GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf))));
3939 gst_buffer_unref (buf);
3941 /* read new chunk */
3942 res = gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag, &buf);
3943 if (res != GST_FLOW_OK)
3944 goto pull_range_failed;
3945 else if (tag != GST_RIFF_TAG_LIST)
3947 else if (GST_BUFFER_SIZE (buf) < 4)
3951 GST_DEBUG_OBJECT (avi, "hdrl LIST tag found");
3953 gst_avi_demux_roundup_list (avi, &buf);
3955 /* the hdrl starts with a 'avih' header */
3956 if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub))
3958 else if (tag != GST_RIFF_TAG_avih)
3960 else if (!gst_avi_demux_parse_avih (avi, sub, &avi->avih))
3963 GST_DEBUG_OBJECT (avi, "AVI header ok, reading elements from header");
3965 /* now, read the elements from the header until the end */
3966 while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
3967 /* sub can be NULL on empty tags */
3972 case GST_RIFF_TAG_LIST:
3977 if (GST_BUFFER_SIZE (sub) < 4)
3980 data = GST_BUFFER_DATA (sub);
3981 fourcc = GST_READ_UINT32_LE (data);
3984 case GST_RIFF_LIST_strl:
3985 if (!(gst_avi_demux_parse_stream (avi, sub))) {
3986 GST_ELEMENT_WARNING (avi, STREAM, DEMUX, (NULL),
3987 ("failed to parse stream, ignoring"));
3990 goto invalid_stream;
3995 case GST_RIFF_LIST_odml:
3996 gst_avi_demux_parse_odml (avi, sub);
3999 case GST_RIFF_LIST_INFO:
4000 GST_BUFFER_DATA (sub) = data + 4;
4001 GST_BUFFER_SIZE (sub) -= 4;
4002 gst_riff_parse_info (element, sub, &tags);
4004 if (avi->globaltags) {
4005 gst_tag_list_insert (avi->globaltags, tags,
4006 GST_TAG_MERGE_REPLACE);
4008 avi->globaltags = tags;
4014 GST_WARNING_OBJECT (avi,
4015 "Unknown list %" GST_FOURCC_FORMAT " in AVI header",
4016 GST_FOURCC_ARGS (fourcc));
4017 GST_MEMDUMP_OBJECT (avi, "Unknown list", GST_BUFFER_DATA (sub),
4018 GST_BUFFER_SIZE (sub));
4020 case GST_RIFF_TAG_JUNQ:
4021 case GST_RIFF_TAG_JUNK:
4027 gst_avi_demux_parse_idit (avi, sub);
4030 GST_WARNING_OBJECT (avi,
4031 "Unknown tag %" GST_FOURCC_FORMAT " in AVI header at off %d",
4032 GST_FOURCC_ARGS (tag), offset);
4033 GST_MEMDUMP_OBJECT (avi, "Unknown tag", GST_BUFFER_DATA (sub),
4034 GST_BUFFER_SIZE (sub));
4036 case GST_RIFF_TAG_JUNQ:
4037 case GST_RIFF_TAG_JUNK:
4040 gst_buffer_unref (sub);
4045 gst_buffer_unref (buf);
4046 GST_DEBUG ("elements parsed");
4048 /* check parsed streams */
4049 if (avi->num_streams == 0)
4051 else if (avi->num_streams != avi->avih->streams) {
4052 GST_WARNING_OBJECT (avi,
4053 "Stream header mentioned %d streams, but %d available",
4054 avi->avih->streams, avi->num_streams);
4057 GST_DEBUG_OBJECT (avi, "skipping junk between header and data, offset=%"
4058 G_GUINT64_FORMAT, avi->offset);
4060 /* Now, find the data (i.e. skip all junk between header and data) */
4066 res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
4067 if (res != GST_FLOW_OK) {
4068 GST_DEBUG_OBJECT (avi, "pull_range failure while looking for tags");
4069 goto pull_range_failed;
4070 } else if (GST_BUFFER_SIZE (buf) < 12) {
4071 GST_DEBUG_OBJECT (avi, "got %d bytes which is less than 12 bytes",
4072 GST_BUFFER_SIZE (buf));
4073 gst_buffer_unref (buf);
4074 return GST_FLOW_ERROR;
4077 data = GST_BUFFER_DATA (buf);
4079 tag = GST_READ_UINT32_LE (data);
4080 size = GST_READ_UINT32_LE (data + 4);
4081 ltag = GST_READ_UINT32_LE (data + 8);
4083 GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u",
4084 GST_FOURCC_ARGS (tag), size);
4085 GST_MEMDUMP ("Tag content", data, GST_BUFFER_SIZE (buf));
4086 gst_buffer_unref (buf);
4089 case GST_RIFF_TAG_LIST:{
4091 case GST_RIFF_LIST_movi:
4092 GST_DEBUG_OBJECT (avi,
4093 "Reached the 'movi' tag, we're done with skipping");
4095 case GST_RIFF_LIST_INFO:
4097 gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag,
4099 if (res != GST_FLOW_OK) {
4100 GST_DEBUG_OBJECT (avi, "couldn't read INFO chunk");
4101 goto pull_range_failed;
4103 GST_DEBUG ("got size %u", GST_BUFFER_SIZE (buf));
4105 GST_DEBUG ("skipping INFO LIST prefix");
4106 avi->offset += (4 - GST_ROUND_UP_2 (size));
4107 gst_buffer_unref (buf);
4111 sub = gst_buffer_create_sub (buf, 4, GST_BUFFER_SIZE (buf) - 4);
4112 gst_riff_parse_info (element, sub, &tags);
4114 if (avi->globaltags) {
4115 gst_tag_list_insert (avi->globaltags, tags,
4116 GST_TAG_MERGE_REPLACE);
4118 avi->globaltags = tags;
4123 gst_buffer_unref (sub);
4126 gst_buffer_unref (buf);
4127 /* gst_riff_read_chunk() has already advanced avi->offset */
4130 GST_WARNING_OBJECT (avi,
4131 "Skipping unknown list tag %" GST_FOURCC_FORMAT,
4132 GST_FOURCC_ARGS (ltag));
4133 avi->offset += 8 + GST_ROUND_UP_2 (size);
4139 GST_WARNING_OBJECT (avi, "Skipping unknown tag %" GST_FOURCC_FORMAT,
4140 GST_FOURCC_ARGS (tag));
4142 case GST_MAKE_FOURCC ('J', 'U', 'N', 'Q'):
4143 case GST_MAKE_FOURCC ('J', 'U', 'N', 'K'):
4144 /* Only get buffer for debugging if the memdump is needed */
4145 if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= 9) {
4146 res = gst_pad_pull_range (avi->sinkpad, avi->offset, size, &buf);
4147 if (res != GST_FLOW_OK) {
4148 GST_DEBUG_OBJECT (avi, "couldn't read INFO chunk");
4149 goto pull_range_failed;
4151 GST_MEMDUMP ("Junk", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
4152 gst_buffer_unref (buf);
4154 avi->offset += 8 + GST_ROUND_UP_2 (size);
4160 GST_DEBUG_OBJECT (avi, "skipping done ... (streams=%u, stream[0].indexes=%p)",
4161 avi->num_streams, avi->stream[0].indexes);
4163 /* create or read stream index (for seeking) */
4164 if (avi->stream[0].indexes != NULL) {
4165 /* we read a super index already (gst_avi_demux_parse_superindex() ) */
4166 gst_avi_demux_read_subindexes_pull (avi);
4168 if (!avi->have_index) {
4169 if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX)
4170 gst_avi_demux_stream_index (avi);
4172 /* still no index, scan */
4173 if (!avi->have_index) {
4174 gst_avi_demux_stream_scan (avi);
4176 /* still no index.. this is a fatal error for now.
4177 * FIXME, we should switch to plain push mode without seeking
4178 * instead of failing. */
4179 if (!avi->have_index)
4183 /* use the indexes now to construct nice durations */
4184 gst_avi_demux_calculate_durations_from_index (avi);
4186 gst_avi_demux_expose_streams (avi, FALSE);
4188 /* create initial NEWSEGMENT event */
4189 if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
4190 stop = avi->segment.duration;
4192 GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop);
4194 /* do initial seek to the default segment values */
4195 gst_avi_demux_do_seek (avi, &avi->segment);
4197 /* prepare initial segment */
4199 gst_event_unref (avi->seg_event);
4200 avi->seg_event = gst_event_new_new_segment_full
4201 (FALSE, avi->segment.rate, avi->segment.applied_rate, GST_FORMAT_TIME,
4202 avi->segment.start, stop, avi->segment.time);
4204 stamp = gst_util_get_timestamp () - stamp;
4205 GST_DEBUG_OBJECT (avi, "pulling header took %" GST_TIME_FORMAT,
4206 GST_TIME_ARGS (stamp));
4208 /* at this point we know all the streams and we can signal the no more
4210 GST_DEBUG_OBJECT (avi, "signaling no more pads");
4211 gst_element_no_more_pads (GST_ELEMENT_CAST (avi));
4218 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4219 ("Invalid AVI header (no LIST at start): %"
4220 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
4221 gst_buffer_unref (buf);
4222 return GST_FLOW_ERROR;
4226 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4227 ("Invalid AVI header (no hdrl at start): %"
4228 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
4229 gst_buffer_unref (buf);
4230 return GST_FLOW_ERROR;
4234 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4235 ("Invalid AVI header (no avih at start): %"
4236 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
4238 gst_buffer_unref (sub);
4239 gst_buffer_unref (buf);
4240 return GST_FLOW_ERROR;
4244 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4245 ("Invalid AVI header (cannot parse avih at start)"));
4246 gst_buffer_unref (buf);
4247 return GST_FLOW_ERROR;
4251 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
4252 return GST_FLOW_ERROR;
4256 GST_WARNING ("file without or too big index");
4257 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4258 ("Could not get/create index"));
4259 return GST_FLOW_ERROR;
4263 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
4264 ("pull_range flow reading header: %s", gst_flow_get_name (res)));
4265 return GST_FLOW_ERROR;
4270 gst_buffer_unref(buf);
4271 return GST_FLOW_ERROR;
4276 /* move a stream to @index */
4278 gst_avi_demux_move_stream (GstAviDemux * avi, GstAviStream * stream,
4279 GstSegment * segment, guint index)
4281 GST_DEBUG_OBJECT (avi, "Move stream %d to %u", stream->num, index);
4283 if (segment->rate < 0.0) {
4285 /* Because we don't know the frame order we need to push from the prev keyframe
4286 * to the next keyframe. If there is a smart decoder downstream he will notice
4287 * that there are too many encoded frames send and return UNEXPECTED when there
4288 * are enough decoded frames to fill the segment. */
4289 #ifdef AVIDEMUX_MODIFICATION
4290 next_key = gst_avi_demux_index_for_time (avi, stream, avi->seek_kf_offset);
4292 next_key = gst_avi_demux_index_next (avi, stream, index, TRUE);
4295 /* FIXME, we go back to 0, we should look at segment.start. We will however
4296 * stop earlier when the see the timestamp < segment.start */
4297 stream->start_entry = 0;
4298 stream->step_entry = index;
4299 stream->current_entry = index;
4300 stream->stop_entry = next_key;
4302 GST_DEBUG_OBJECT (avi, "reverse seek: start %u, step %u, stop %u",
4303 stream->start_entry, stream->step_entry, stream->stop_entry);
4305 stream->start_entry = index;
4306 stream->step_entry = index;
4307 stream->stop_entry = gst_avi_demux_index_last (avi, stream);
4309 if (stream->current_entry != index) {
4310 GST_DEBUG_OBJECT (avi, "Move DISCONT from %u to %u",
4311 stream->current_entry, index);
4312 stream->current_entry = index;
4313 stream->discont = TRUE;
4316 /* update the buffer info */
4317 gst_avi_demux_get_buffer_info (avi, stream, index,
4318 &stream->current_timestamp, &stream->current_ts_end,
4319 &stream->current_offset, &stream->current_offset_end);
4321 GST_DEBUG_OBJECT (avi, "Moved to %u, ts %" GST_TIME_FORMAT
4322 ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
4323 ", off_end %" G_GUINT64_FORMAT, index,
4324 GST_TIME_ARGS (stream->current_timestamp),
4325 GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
4326 stream->current_offset_end);
4328 GST_DEBUG_OBJECT (avi, "Seeking to offset %" G_GUINT64_FORMAT,
4329 stream->index[index].offset);
4333 * Do the actual seeking.
4336 gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
4338 GstClockTime seek_time;
4341 GstAviStream *stream;
4343 seek_time = segment->last_stop;
4345 #ifdef AVIDEMUX_MODIFICATION
4346 avi->seek_kf_offset = seek_time;
4349 keyframe = !!(segment->flags & GST_SEEK_FLAG_KEY_UNIT);
4351 GST_DEBUG_OBJECT (avi, "seek to: %" GST_TIME_FORMAT
4352 " keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe);
4354 /* FIXME, this code assumes the main stream with keyframes is stream 0,
4355 * which is mostly correct... */
4356 stream = &avi->stream[avi->main_stream];
4358 /* get the entry index for the requested position */
4359 index = gst_avi_demux_index_for_time (avi, stream, seek_time);
4360 GST_DEBUG_OBJECT (avi, "Got entry %u", index);
4363 #ifdef AVIDEMUX_MODIFICATION
4364 if(segment->rate < 0.0 && index) {
4365 /* If index is keyframe, reduce index by 1, so that we could fetch prev keyframe for video */
4366 /* This change is done to fix the out of segment issue when seek position is a keyframe position */
4367 if (ENTRY_IS_KEYFRAME (&stream->index[index])) {
4374 /* check if we are already on a keyframe */
4375 if (!ENTRY_IS_KEYFRAME (&stream->index[index])) {
4376 GST_DEBUG_OBJECT (avi, "not keyframe, searching back");
4377 /* now go to the previous keyframe, this is where we should start
4379 index = gst_avi_demux_index_prev (avi, stream, index, TRUE);
4380 GST_DEBUG_OBJECT (avi, "previous keyframe at %u", index);
4383 /* move the main stream to this position */
4384 gst_avi_demux_move_stream (avi, stream, segment, index);
4387 /* when seeking to a keyframe, we update the result seek time
4388 * to the time of the keyframe. */
4389 seek_time = stream->current_timestamp;
4390 GST_DEBUG_OBJECT (avi, "keyframe adjusted to %" GST_TIME_FORMAT,
4391 GST_TIME_ARGS (seek_time));
4394 /* the seek time is also the last_stop and stream time when going
4396 segment->last_stop = seek_time;
4398 #ifdef AVIDEMUX_MODIFICATION
4399 /*initialization of rate params */
4400 stream->trickplay_info->prev_kidx =0;
4401 stream->trickplay_info->next_kidx=0;
4402 stream->trickplay_info->kidxs_dur_diff=0;
4403 stream->trickplay_info->start_pos = segment->last_stop;
4404 /* Adjust seek_time to video keyframe's timestamp so that audio can align to that position */
4405 if(segment->rate < 0.0)
4406 seek_time = stream->current_timestamp;
4408 if (segment->rate > 0.0)
4409 segment->time = seek_time;
4412 /* now set DISCONT and align the other streams */
4413 for (i = 0; i < avi->num_streams; i++) {
4414 GstAviStream *ostream;
4416 ostream = &avi->stream[i];
4417 if ((ostream == stream) || (ostream->index == NULL))
4420 /* get the entry index for the requested position */
4421 index = gst_avi_demux_index_for_time (avi, ostream, seek_time);
4423 /* move to previous keyframe */
4424 if (!ENTRY_IS_KEYFRAME (&ostream->index[index]))
4425 index = gst_avi_demux_index_prev (avi, ostream, index, TRUE);
4427 gst_avi_demux_move_stream (avi, ostream, segment, index);
4429 GST_DEBUG_OBJECT (avi, "done seek to: %" GST_TIME_FORMAT,
4430 GST_TIME_ARGS (seek_time));
4436 * Handle seek event in pull mode.
4439 gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
4444 GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
4445 gint64 cur = 0, stop;
4448 GstSegment seeksegment = { 0, };
4452 GST_DEBUG_OBJECT (avi, "doing seek with event");
4454 gst_event_parse_seek (event, &rate, &format, &flags,
4455 &cur_type, &cur, &stop_type, &stop);
4457 /* we have to have a format as the segment format. Try to convert
4459 if (format != GST_FORMAT_TIME) {
4460 GstFormat fmt = GST_FORMAT_TIME;
4461 gboolean res = TRUE;
4463 if (cur_type != GST_SEEK_TYPE_NONE)
4464 res = gst_pad_query_convert (pad, format, cur, &fmt, &cur);
4465 if (res && stop_type != GST_SEEK_TYPE_NONE)
4466 res = gst_pad_query_convert (pad, format, stop, &fmt, &stop);
4472 GST_DEBUG_OBJECT (avi,
4473 "seek requested: rate %g cur %" GST_TIME_FORMAT " stop %"
4474 GST_TIME_FORMAT, rate, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
4475 /* FIXME: can we do anything with rate!=1.0 */
4477 GST_DEBUG_OBJECT (avi, "doing seek without event");
4482 /* save flush flag */
4483 flush = flags & GST_SEEK_FLAG_FLUSH;
4486 GstEvent *fevent = gst_event_new_flush_start ();
4488 /* for a flushing seek, we send a flush_start on all pads. This will
4489 * eventually stop streaming with a WRONG_STATE. We can thus eventually
4490 * take the STREAM_LOCK. */
4491 GST_DEBUG_OBJECT (avi, "sending flush start");
4492 gst_avi_demux_push_event (avi, gst_event_ref (fevent));
4493 gst_pad_push_event (avi->sinkpad, fevent);
4495 /* a non-flushing seek, we PAUSE the task so that we can take the
4497 GST_DEBUG_OBJECT (avi, "non flushing seek, pausing task");
4498 gst_pad_pause_task (avi->sinkpad);
4501 /* wait for streaming to stop */
4502 GST_DEBUG_OBJECT (avi, "wait for streaming to stop");
4503 GST_PAD_STREAM_LOCK (avi->sinkpad);
4505 /* copy segment, we need this because we still need the old
4506 * segment when we close the current segment. */
4507 memcpy (&seeksegment, &avi->segment, sizeof (GstSegment));
4510 GST_DEBUG_OBJECT (avi, "configuring seek");
4511 gst_segment_set_seek (&seeksegment, rate, format, flags,
4512 cur_type, cur, stop_type, stop, &update);
4515 #ifdef AVIDEMUX_MODIFICATION
4516 if (cur != GST_CLOCK_TIME_NONE)
4517 gst_segment_set_last_stop (&seeksegment, GST_FORMAT_TIME, cur);
4520 /* do the seek, seeksegment.last_stop contains the new position, this
4521 * actually never fails. */
4522 gst_avi_demux_do_seek (avi, &seeksegment);
4524 gst_event_replace (&avi->close_seg_event, NULL);
4526 GstEvent *fevent = gst_event_new_flush_stop ();
4528 GST_DEBUG_OBJECT (avi, "sending flush stop");
4529 gst_avi_demux_push_event (avi, gst_event_ref (fevent));
4530 gst_pad_push_event (avi->sinkpad, fevent);
4531 } else if (avi->segment_running) {
4532 /* we are running the current segment and doing a non-flushing seek,
4533 * close the segment first based on the last_stop. */
4534 GST_DEBUG_OBJECT (avi, "closing running segment %" G_GINT64_FORMAT
4535 " to %" G_GINT64_FORMAT, avi->segment.start, avi->segment.last_stop);
4536 avi->close_seg_event = gst_event_new_new_segment_full (TRUE,
4537 avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
4538 avi->segment.start, avi->segment.last_stop, avi->segment.time);
4541 /* now update the real segment info */
4542 memcpy (&avi->segment, &seeksegment, sizeof (GstSegment));
4544 /* post the SEGMENT_START message when we do segmented playback */
4545 if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4546 gst_element_post_message (GST_ELEMENT_CAST (avi),
4547 gst_message_new_segment_start (GST_OBJECT_CAST (avi),
4548 avi->segment.format, avi->segment.last_stop));
4551 /* prepare for streaming again */
4552 if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
4553 stop = avi->segment.duration;
4555 /* queue the segment event for the streaming thread. */
4557 gst_event_unref (avi->seg_event);
4558 if (avi->segment.rate > 0.0) {
4559 /* forwards goes from last_stop to stop */
4560 avi->seg_event = gst_event_new_new_segment_full (FALSE,
4561 avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
4562 avi->segment.last_stop, stop, avi->segment.time);
4564 #ifdef AVIDEMUX_MODIFICATION
4565 avi->segment.start = 0;
4566 avi->segment.time = 0;
4568 /* reverse goes from start to last_stop */
4569 avi->seg_event = gst_event_new_new_segment_full (FALSE,
4570 avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
4571 avi->segment.start, avi->segment.last_stop, avi->segment.time);
4574 if (!avi->streaming) {
4575 avi->segment_running = TRUE;
4576 gst_pad_start_task (avi->sinkpad, (GstTaskFunction) gst_avi_demux_loop,
4579 /* reset the last flow and mark discont, seek is always DISCONT */
4580 for (i = 0; i < avi->num_streams; i++) {
4581 GST_DEBUG_OBJECT (avi, "marking DISCONT");
4582 avi->stream[i].last_flow = GST_FLOW_OK;
4583 avi->stream[i].discont = TRUE;
4585 GST_PAD_STREAM_UNLOCK (avi->sinkpad);
4592 GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted.");
4598 * Handle seek event in push mode.
4601 avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, GstEvent * event)
4606 GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
4609 GstAviStream *stream;
4613 GstSegment seeksegment;
4616 /* check we have the index */
4617 if (!avi->have_index) {
4618 GST_DEBUG_OBJECT (avi, "no seek index built, seek aborted.");
4621 GST_DEBUG_OBJECT (avi, "doing push-based seek with event");
4624 gst_event_parse_seek (event, &rate, &format, &flags,
4625 &cur_type, &cur, &stop_type, &stop);
4627 if (format != GST_FORMAT_TIME) {
4628 GstFormat fmt = GST_FORMAT_TIME;
4629 gboolean res = TRUE;
4631 if (cur_type != GST_SEEK_TYPE_NONE)
4632 res = gst_pad_query_convert (pad, format, cur, &fmt, &cur);
4633 if (res && stop_type != GST_SEEK_TYPE_NONE)
4634 res = gst_pad_query_convert (pad, format, stop, &fmt, &stop);
4636 GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted.");
4643 /* let gst_segment handle any tricky stuff */
4644 GST_DEBUG_OBJECT (avi, "configuring seek");
4645 memcpy (&seeksegment, &avi->segment, sizeof (GstSegment));
4646 gst_segment_set_seek (&seeksegment, rate, format, flags,
4647 cur_type, cur, stop_type, stop, &update);
4649 keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
4650 cur = seeksegment.last_stop;
4652 GST_DEBUG_OBJECT (avi,
4653 "Seek requested: ts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT
4654 ", kf %u, rate %lf", GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), keyframe,
4658 GST_DEBUG_OBJECT (avi, "negative rate seek not supported in push mode");
4662 /* FIXME, this code assumes the main stream with keyframes is stream 0,
4663 * which is mostly correct... */
4664 str_num = avi->main_stream;
4665 stream = &avi->stream[str_num];
4667 /* get the entry index for the requested position */
4668 index = gst_avi_demux_index_for_time (avi, stream, cur);
4669 GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT,
4670 str_num, index, GST_TIME_ARGS (cur));
4672 /* check if we are already on a keyframe */
4673 if (!ENTRY_IS_KEYFRAME (&stream->index[index])) {
4674 GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back");
4675 /* now go to the previous keyframe, this is where we should start
4677 index = gst_avi_demux_index_prev (avi, stream, index, TRUE);
4678 GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", index);
4681 gst_avi_demux_get_buffer_info (avi, stream, index,
4682 &stream->current_timestamp, &stream->current_ts_end,
4683 &stream->current_offset, &stream->current_offset_end);
4685 /* re-use cur to be the timestamp of the seek as it _will_ be */
4686 cur = stream->current_timestamp;
4688 min_offset = stream->index[index].offset;
4689 avi->seek_kf_offset = min_offset - 8;
4691 GST_DEBUG_OBJECT (avi,
4692 "Seek to: ts %" GST_TIME_FORMAT " (on str %u, idx %u, offset %"
4693 G_GUINT64_FORMAT ")", GST_TIME_ARGS (stream->current_timestamp), str_num,
4696 for (n = 0; n < avi->num_streams; n++) {
4697 GstAviStream *str = &avi->stream[n];
4700 if (n == avi->main_stream)
4703 /* get the entry index for the requested position */
4704 idx = gst_avi_demux_index_for_time (avi, str, cur);
4705 GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT, n,
4706 idx, GST_TIME_ARGS (cur));
4708 /* check if we are already on a keyframe */
4709 if (!ENTRY_IS_KEYFRAME (&str->index[idx])) {
4710 GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back");
4711 /* now go to the previous keyframe, this is where we should start
4713 idx = gst_avi_demux_index_prev (avi, str, idx, TRUE);
4714 GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", idx);
4717 gst_avi_demux_get_buffer_info (avi, str, idx,
4718 &str->current_timestamp, &str->current_ts_end,
4719 &str->current_offset, &str->current_offset_end);
4721 if (str->index[idx].offset < min_offset) {
4722 min_offset = str->index[idx].offset;
4723 GST_DEBUG_OBJECT (avi,
4724 "Found an earlier offset at %" G_GUINT64_FORMAT ", str %u",
4732 GST_DEBUG_OBJECT (avi,
4733 "Seek performed: str %u, offset %" G_GUINT64_FORMAT ", idx %u, ts %"
4734 GST_TIME_FORMAT ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
4735 ", off_end %" G_GUINT64_FORMAT, str_num, min_offset, index,
4736 GST_TIME_ARGS (stream->current_timestamp),
4737 GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
4738 stream->current_offset_end);
4740 /* index data refers to data, not chunk header (for pull mode convenience) */
4742 GST_DEBUG_OBJECT (avi, "seeking to chunk at offset %" G_GUINT64_FORMAT,
4745 if (!perform_seek_to_offset (avi, min_offset)) {
4746 GST_DEBUG_OBJECT (avi, "seek event failed!");
4754 * Handle whether we can perform the seek event or if we have to let the chain
4755 * function handle seeks to build the seek indexes first.
4758 gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
4761 /* check for having parsed index already */
4762 if (!avi->have_index) {
4764 gboolean building_index;
4766 GST_OBJECT_LOCK (avi);
4767 /* handle the seek event in the chain function */
4768 avi->state = GST_AVI_DEMUX_SEEK;
4770 /* copy the event */
4771 if (avi->seek_event)
4772 gst_event_unref (avi->seek_event);
4773 avi->seek_event = gst_event_ref (event);
4775 /* set the building_index flag so that only one thread can setup the
4776 * structures for index seeking. */
4777 building_index = avi->building_index;
4778 if (!building_index) {
4779 avi->building_index = TRUE;
4780 if (avi->stream[0].indexes) {
4781 avi->odml_stream = 0;
4782 avi->odml_subidxs = avi->stream[avi->odml_stream].indexes;
4783 offset = avi->odml_subidxs[0];
4785 offset = avi->idx1_offset;
4788 GST_OBJECT_UNLOCK (avi);
4790 if (!building_index) {
4791 /* seek to the first subindex or legacy index */
4792 GST_INFO_OBJECT (avi,
4793 "Seeking to legacy index/first subindex at %" G_GUINT64_FORMAT,
4795 return perform_seek_to_offset (avi, offset);
4798 /* FIXME: we have to always return true so that we don't block the seek
4800 * Note: maybe it is OK to return true if we're still building the index */
4804 return avi_demux_handle_seek_push (avi, pad, event);
4808 * Helper for gst_avi_demux_invert()
4811 swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes)
4813 memcpy (tmp, d1, bytes);
4814 memcpy (d1, d2, bytes);
4815 memcpy (d2, tmp, bytes);
4819 #define gst_avi_demux_is_uncompressed(fourcc) \
4821 (fourcc == GST_RIFF_DIB || \
4822 fourcc == GST_RIFF_rgb || \
4823 fourcc == GST_RIFF_RGB || fourcc == GST_RIFF_RAW))
4826 * Invert DIB buffers... Takes existing buffer and
4827 * returns either the buffer or a new one (with old
4828 * one dereferenced).
4829 * FIXME: can't we preallocate tmp? and remember stride, bpp?
4832 gst_avi_demux_invert (GstAviStream * stream, GstBuffer * buf)
4839 if (stream->strh->type != GST_RIFF_FCC_vids)
4842 if (!gst_avi_demux_is_uncompressed (stream->strh->fcc_handler)) {
4843 return buf; /* Ignore non DIB buffers */
4846 s = gst_caps_get_structure (GST_PAD_CAPS (stream->pad), 0);
4847 if (!gst_structure_get_int (s, "bpp", &bpp)) {
4848 GST_WARNING ("Failed to retrieve depth from caps");
4852 if (stream->strf.vids == NULL) {
4853 GST_WARNING ("Failed to retrieve vids for stream");
4857 h = stream->strf.vids->height;
4858 w = stream->strf.vids->width;
4859 stride = GST_ROUND_UP_4 (w * (bpp / 8));
4861 buf = gst_buffer_make_writable (buf);
4862 if (GST_BUFFER_SIZE (buf) < (stride * h)) {
4863 GST_WARNING ("Buffer is smaller than reported Width x Height x Depth");
4867 tmp = g_malloc (stride);
4869 for (y = 0; y < h / 2; y++) {
4870 swap_line (GST_BUFFER_DATA (buf) + stride * y,
4871 GST_BUFFER_DATA (buf) + stride * (h - 1 - y), tmp, stride);
4880 gst_avi_demux_add_assoc (GstAviDemux * avi, GstAviStream * stream,
4881 GstClockTime timestamp, guint64 offset, gboolean keyframe)
4883 /* do not add indefinitely for open-ended streaming */
4884 if (G_UNLIKELY (avi->element_index && avi->seekable)) {
4885 GST_LOG_OBJECT (avi, "adding association %" GST_TIME_FORMAT "-> %"
4886 G_GUINT64_FORMAT, GST_TIME_ARGS (timestamp), offset);
4887 gst_index_add_association (avi->element_index, avi->index_id,
4888 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
4889 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, timestamp,
4890 GST_FORMAT_BYTES, offset, NULL);
4891 /* current_entry is DEFAULT (frame #) */
4892 gst_index_add_association (avi->element_index, stream->index_id,
4893 keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT :
4894 GST_ASSOCIATION_FLAG_DELTA_UNIT, GST_FORMAT_TIME, timestamp,
4895 GST_FORMAT_BYTES, offset, GST_FORMAT_DEFAULT, stream->current_entry,
4901 * Returns the aggregated GstFlowReturn.
4903 static GstFlowReturn
4904 gst_avi_demux_combine_flows (GstAviDemux * avi, GstAviStream * stream,
4908 gboolean unexpected = FALSE, not_linked = TRUE;
4910 /* store the value */
4911 stream->last_flow = ret;
4913 /* any other error that is not-linked or eos can be returned right away */
4914 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
4917 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
4918 for (i = 0; i < avi->num_streams; i++) {
4919 GstAviStream *ostream = &avi->stream[i];
4921 ret = ostream->last_flow;
4922 /* no unexpected or unlinked, return */
4923 if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
4926 /* we check to see if we have at least 1 unexpected or all unlinked */
4927 unexpected |= (ret == GST_FLOW_UNEXPECTED);
4928 not_linked &= (ret == GST_FLOW_NOT_LINKED);
4930 /* when we get here, we all have unlinked or unexpected */
4932 ret = GST_FLOW_NOT_LINKED;
4933 else if (unexpected)
4934 ret = GST_FLOW_UNEXPECTED;
4936 GST_LOG_OBJECT (avi, "combined %s to return %s",
4937 gst_flow_get_name (stream->last_flow), gst_flow_get_name (ret));
4941 /* move @stream to the next position in its index */
4942 static GstFlowReturn
4943 gst_avi_demux_advance (GstAviDemux * avi, GstAviStream * stream,
4946 guint old_entry, new_entry;
4948 old_entry = stream->current_entry;
4950 new_entry = old_entry + 1;
4952 /* see if we reached the end */
4953 if (new_entry >= stream->stop_entry) {
4954 if (avi->segment.rate < 0.0) {
4956 #ifdef AVIDEMUX_MODIFICATION
4957 GST_DEBUG_OBJECT (avi, "backward reached stop %u", stream->stop_entry);
4960 if (stream->step_entry == stream->start_entry) {
4961 /* we stepped all the way to the start, eos */
4962 GST_DEBUG_OBJECT (avi, "reverse reached start %u", stream->start_entry);
4965 /* backwards, stop becomes step, find a new step */
4966 stream->stop_entry = stream->step_entry;
4967 stream->step_entry = gst_avi_demux_index_prev (avi, stream,
4968 stream->stop_entry, TRUE);
4970 GST_DEBUG_OBJECT (avi,
4971 "reverse playback jump: start %u, step %u, stop %u",
4972 stream->start_entry, stream->step_entry, stream->stop_entry);
4974 /* and start from the previous keyframe now */
4975 new_entry = stream->step_entry;
4979 GST_DEBUG_OBJECT (avi, "forward reached stop %u", stream->stop_entry);
4984 if (new_entry != old_entry) {
4985 stream->current_entry = new_entry;
4986 stream->current_total = stream->index[new_entry].total;
4988 if (new_entry == old_entry + 1) {
4989 GST_DEBUG_OBJECT (avi, "moved forwards from %u to %u",
4990 old_entry, new_entry);
4991 /* we simply moved one step forwards, reuse current info */
4992 stream->current_timestamp = stream->current_ts_end;
4993 stream->current_offset = stream->current_offset_end;
4994 gst_avi_demux_get_buffer_info (avi, stream, new_entry,
4995 NULL, &stream->current_ts_end, NULL, &stream->current_offset_end);
4997 /* we moved DISCONT, full update */
4998 gst_avi_demux_get_buffer_info (avi, stream, new_entry,
4999 &stream->current_timestamp, &stream->current_ts_end,
5000 &stream->current_offset, &stream->current_offset_end);
5001 /* and MARK discont for this stream */
5002 stream->last_flow = GST_FLOW_OK;
5003 stream->discont = TRUE;
5004 GST_DEBUG_OBJECT (avi, "Moved from %u to %u, ts %" GST_TIME_FORMAT
5005 ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
5006 ", off_end %" G_GUINT64_FORMAT, old_entry, new_entry,
5007 GST_TIME_ARGS (stream->current_timestamp),
5008 GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
5009 stream->current_offset_end);
5017 GST_DEBUG_OBJECT (avi, "we are EOS");
5018 /* setting current_timestamp to -1 marks EOS */
5019 stream->current_timestamp = -1;
5020 return GST_FLOW_UNEXPECTED;
5024 /* find the stream with the lowest current position when going forwards or with
5025 * the highest position when going backwards, this is the stream
5026 * we should push from next */
5028 gst_avi_demux_find_next (GstAviDemux * avi, gfloat rate)
5030 guint64 min_time, max_time;
5031 guint stream_num, i;
5034 min_time = G_MAXUINT64;
5037 for (i = 0; i < avi->num_streams; i++) {
5039 GstAviStream *stream;
5041 stream = &avi->stream[i];
5043 /* ignore streams that finished */
5044 if (stream->last_flow == GST_FLOW_UNEXPECTED)
5047 position = stream->current_timestamp;
5049 /* position of -1 is EOS */
5050 if (position != -1) {
5051 #ifdef AVIDEMUX_MODIFICATION
5052 if (position < min_time) {
5053 min_time = position;
5057 if (rate > 0.0 && position < min_time) {
5058 min_time = position;
5060 } else if (rate < 0.0 && position >= max_time) {
5061 max_time = position;
5070 static GstFlowReturn
5071 gst_avi_demux_loop_data (GstAviDemux * avi)
5073 GstFlowReturn ret = GST_FLOW_OK;
5075 GstAviStream *stream;
5076 gboolean processed = FALSE;
5078 guint64 offset, size;
5079 GstClockTime timestamp, duration;
5080 guint64 out_offset, out_offset_end;
5082 GstAviIndexEntry *entry;
5085 stream_num = gst_avi_demux_find_next (avi, avi->segment.rate);
5088 if (G_UNLIKELY (stream_num == -1)) {
5089 GST_DEBUG_OBJECT (avi, "all streams are EOS");
5093 /* we have the stream now */
5094 stream = &avi->stream[stream_num];
5096 /* skip streams without pads */
5098 GST_DEBUG_OBJECT (avi, "skipping entry from stream %d without pad",
5103 /* get the timing info for the entry */
5104 timestamp = stream->current_timestamp;
5105 duration = stream->current_ts_end - timestamp;
5106 out_offset = stream->current_offset;
5107 out_offset_end = stream->current_offset_end;
5109 /* get the entry data info */
5110 entry = &stream->index[stream->current_entry];
5111 offset = entry->offset;
5113 keyframe = ENTRY_IS_KEYFRAME (entry);
5116 #ifdef AVIDEMUX_MODIFICATION
5117 /* Forward trickplay */
5118 if(avi->segment.rate > 1.0 && stream->strh->type == GST_RIFF_FCC_vids) {
5119 gst_avidemux_forward_trickplay (avi, stream, ×tamp);
5120 } else if(avi->segment.rate < 0.0 && stream->strh->type == GST_RIFF_FCC_vids) {
5121 gst_avidemux_backward_trickplay (avi, stream, ×tamp);
5125 /* skip empty entries */
5127 GST_DEBUG_OBJECT (avi, "Skipping entry %u (%" G_GUINT64_FORMAT ", %p)",
5128 stream->current_entry, size, stream->pad);
5132 if (avi->segment.rate > 0.0) {
5133 /* only check this for fowards playback for now */
5134 #ifdef AVIDEMUX_MODIFICATION
5135 if (GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
5136 && (timestamp > avi->segment.stop)) {
5138 if (keyframe && GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
5139 && (timestamp > avi->segment.stop)) {
5145 GST_LOG ("reading buffer (size=%" G_GUINT64_FORMAT "), stream %d, pos %"
5146 G_GUINT64_FORMAT " (0x%" G_GINT64_MODIFIER "x), kf %d", size,
5147 stream_num, offset, offset, keyframe);
5149 /* FIXME, check large chunks and cut them up */
5151 /* pull in the data */
5152 ret = gst_pad_pull_range (avi->sinkpad, offset, size, &buf);
5153 if (ret != GST_FLOW_OK)
5156 /* check for short buffers, this is EOS as well */
5157 if (GST_BUFFER_SIZE (buf) < size)
5160 /* invert the picture if needed */
5161 buf = gst_avi_demux_invert (stream, buf);
5163 /* mark non-keyframes */
5165 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
5167 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
5169 GST_BUFFER_TIMESTAMP (buf) = timestamp;
5170 GST_BUFFER_DURATION (buf) = duration;
5171 GST_BUFFER_OFFSET (buf) = out_offset;
5172 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
5174 /* mark discont when pending */
5175 if (stream->discont) {
5176 GST_DEBUG_OBJECT (avi, "setting DISCONT flag");
5177 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5178 stream->discont = FALSE;
5181 gst_avi_demux_add_assoc (avi, stream, timestamp, offset, keyframe);
5183 gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
5185 /* update current position in the segment */
5186 gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, timestamp);
5187 #ifdef AVIDEMUX_MODIFICATION
5188 GST_DEBUG_OBJECT (avi, " %s : Pushing buffer of size %u, ts %"
5189 GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
5190 ", off_end %" G_GUINT64_FORMAT,
5191 stream_num ? "Audio" : "Video", GST_BUFFER_SIZE (buf), GST_TIME_ARGS (timestamp),
5192 GST_TIME_ARGS (duration), out_offset, out_offset_end);
5194 GST_DEBUG_OBJECT (avi, "Pushing buffer of size %u, ts %"
5195 GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
5196 ", off_end %" G_GUINT64_FORMAT,
5197 GST_BUFFER_SIZE (buf), GST_TIME_ARGS (timestamp),
5198 GST_TIME_ARGS (duration), out_offset, out_offset_end);
5203 #define CHUNK_ID_LEN 4
5204 #define CHUNK_SIZE_LEN 4
5205 #define DD_CHUNK_DATA_LEN 10
5206 #define DD_CHUNK_TOTAL_LEN CHUNK_ID_LEN+CHUNK_SIZE_LEN+DD_CHUNK_DATA_LEN
5208 if (avi->drmContext)// this is drm
5210 GstBuffer* encrypted_buf = NULL;
5212 if (stream->strh->type == GST_RIFF_FCC_auds) { /* Audio Stream */
5213 if (DRM_SUCCESS == avi->divx_decrypt_audio (avi->drmContext, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf))) {
5214 GST_DEBUG_OBJECT (avi, "drmDecryptAudio() Success : buffer = %d", GST_BUFFER_SIZE(buf));
5216 GST_ERROR_OBJECT (avi, "drmDecryptAudio () Failed : buffer = %d", GST_BUFFER_SIZE(buf));
5218 ret = gst_pad_push (stream->pad, buf);
5220 } else if (stream->strh->type == GST_RIFF_FCC_vids) { /* Video Stream */
5222 /* Read previous dd chunk */
5223 GstBuffer* dd_chunk_buf = NULL;
5224 if (GST_FLOW_OK != gst_pad_pull_range (avi->sinkpad,
5225 offset-(CHUNK_ID_LEN+CHUNK_SIZE_LEN+DD_CHUNK_TOTAL_LEN),
5226 DD_CHUNK_TOTAL_LEN, &dd_chunk_buf)) {
5227 GST_ERROR_OBJECT (avi, "pull range failed");
5229 guint8 tempBuffer[256] = { 0, };
5230 guint32 tempBufferLength = 0;
5233 ret = avi->divx_prepare_video_bitstream (avi->drmContext,
5234 GST_BUFFER_DATA(dd_chunk_buf)+(CHUNK_ID_LEN+CHUNK_SIZE_LEN),
5237 &tempBufferLength );
5239 if (ret == DRM_SUCCESS) {
5240 /* Create new buffer and copy retrieved tempBuffer and original buffer to created buffer */
5241 encrypted_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE(buf)+tempBufferLength);
5242 if (encrypted_buf) {
5243 /* FIXME: Can be enhance merge buffer code */
5244 memcpy (GST_BUFFER_DATA(encrypted_buf), tempBuffer, tempBufferLength);
5245 memcpy (GST_BUFFER_DATA(encrypted_buf)+tempBufferLength, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
5246 gst_buffer_copy_metadata (encrypted_buf, buf, GST_BUFFER_COPY_ALL);
5248 /* relase buf because we will push encrypted_buf instead of buf */
5249 gst_buffer_unref (buf);
5251 GST_ERROR_OBJECT (avi, "gst_buffer_new_and_alloc() failed!!!!");
5254 GST_ERROR_OBJECT (avi, "divx_prepare_video_bitstream failed!!!! ret = [%d]", ret);
5258 /* Release DD-chunk Buffer */
5260 gst_buffer_unref (dd_chunk_buf);
5262 /* Push encrypted_buf if is valid, otherwise push original buffer */
5264 ret = gst_pad_push (stream->pad, encrypted_buf);
5266 ret = gst_pad_push (stream->pad, buf);
5269 /* This is normal file */
5270 ret = gst_pad_push (stream->pad, buf);
5273 ret = gst_pad_push (stream->pad, buf);
5276 /* mark as processed, we increment the frame and byte counters then
5277 * leave the while loop and return the GstFlowReturn */
5280 if (avi->segment.rate < 0) {
5281 if (timestamp > avi->segment.stop && ret == GST_FLOW_UNEXPECTED) {
5282 /* In reverse playback we can get a GST_FLOW_UNEXPECTED when
5283 * we are at the end of the segment, so we just need to jump
5284 * back to the previous section. */
5285 GST_DEBUG_OBJECT (avi, "downstream has reached end of segment");
5290 /* move to next item */
5291 ret = gst_avi_demux_advance (avi, stream, ret);
5294 ret = gst_avi_demux_combine_flows (avi, stream, ret);
5295 } while (!processed);
5303 GST_DEBUG_OBJECT (avi, "No samples left for any streams - EOS");
5304 ret = GST_FLOW_UNEXPECTED;
5309 GST_LOG_OBJECT (avi, "Found keyframe after segment,"
5310 " setting EOS (%" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ")",
5311 GST_TIME_ARGS (timestamp), GST_TIME_ARGS (avi->segment.stop));
5312 ret = GST_FLOW_UNEXPECTED;
5313 /* move to next stream */
5318 GST_DEBUG_OBJECT (avi, "pull range failed: pos=%" G_GUINT64_FORMAT
5319 " size=%" G_GUINT64_FORMAT, offset, size);
5324 GST_WARNING_OBJECT (avi, "Short read at offset %" G_GUINT64_FORMAT
5325 ", only got %d/%" G_GUINT64_FORMAT " bytes (truncated file?)", offset,
5326 GST_BUFFER_SIZE (buf), size);
5327 gst_buffer_unref (buf);
5328 ret = GST_FLOW_UNEXPECTED;
5334 * Read data. If we have an index it delegates to
5335 * gst_avi_demux_process_next_entry().
5337 static GstFlowReturn
5338 gst_avi_demux_stream_data (GstAviDemux * avi)
5343 GstFlowReturn res = GST_FLOW_OK;
5344 GstFormat format = GST_FORMAT_TIME;
5346 if (G_UNLIKELY (avi->have_eos)) {
5347 /* Clean adapter, we're done */
5348 gst_adapter_clear (avi->adapter);
5349 return GST_FLOW_UNEXPECTED;
5352 if (G_UNLIKELY (avi->todrop)) {
5355 if ((drop = gst_adapter_available (avi->adapter))) {
5356 if (drop > avi->todrop)
5358 GST_DEBUG_OBJECT (avi, "Dropping %d bytes", drop);
5359 gst_adapter_flush (avi->adapter, drop);
5360 avi->todrop -= drop;
5361 avi->offset += drop;
5365 /* Iterate until need more data, so adapter won't grow too much */
5367 if (G_UNLIKELY (!gst_avi_demux_peek_chunk_info (avi, &tag, &size))) {
5371 GST_DEBUG ("Trying chunk (%" GST_FOURCC_FORMAT "), size %d",
5372 GST_FOURCC_ARGS (tag), size);
5374 if (G_LIKELY ((tag & 0xff) >= '0' && (tag & 0xff) <= '9' &&
5375 ((tag >> 8) & 0xff) >= '0' && ((tag >> 8) & 0xff) <= '9')) {
5376 GST_LOG ("Chunk ok");
5377 } else if ((tag & 0xffff) == (('x' << 8) | 'i')) {
5378 GST_DEBUG ("Found sub-index tag");
5379 if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
5380 /* accept 0 size buffer here */
5381 avi->abort_buffering = FALSE;
5382 GST_DEBUG (" skipping %d bytes for now", size);
5383 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5386 } else if (tag == GST_RIFF_TAG_RIFF) {
5387 /* RIFF tags can appear in ODML files, just jump over them */
5388 if (gst_adapter_available (avi->adapter) >= 12) {
5389 GST_DEBUG ("Found RIFF tag, skipping RIFF header");
5390 gst_adapter_flush (avi->adapter, 12);
5394 } else if (tag == GST_RIFF_TAG_idx1) {
5395 GST_DEBUG ("Found index tag");
5396 if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
5397 /* accept 0 size buffer here */
5398 avi->abort_buffering = FALSE;
5399 GST_DEBUG (" skipping %d bytes for now", size);
5400 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5403 } else if (tag == GST_RIFF_TAG_LIST) {
5404 /* movi chunks might be grouped in rec list */
5405 if (gst_adapter_available (avi->adapter) >= 12) {
5406 GST_DEBUG ("Found LIST tag, skipping LIST header");
5407 gst_adapter_flush (avi->adapter, 12);
5411 } else if (tag == GST_RIFF_TAG_JUNK || tag == GST_RIFF_TAG_JUNQ) {
5412 /* rec list might contain JUNK chunks */
5413 GST_DEBUG ("Found JUNK tag");
5414 if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
5415 /* accept 0 size buffer here */
5416 avi->abort_buffering = FALSE;
5417 GST_DEBUG (" skipping %d bytes for now", size);
5418 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5422 GST_DEBUG ("No more stream chunks, send EOS");
5423 avi->have_eos = TRUE;
5424 return GST_FLOW_UNEXPECTED;
5427 if (G_UNLIKELY (!gst_avi_demux_peek_chunk (avi, &tag, &size))) {
5428 /* supposedly one hopes to catch a nicer chunk later on ... */
5429 /* FIXME ?? give up here rather than possibly ending up going
5430 * through the whole file */
5431 if (avi->abort_buffering) {
5432 avi->abort_buffering = FALSE;
5434 gst_adapter_flush (avi->adapter, 8);
5441 GST_DEBUG ("chunk ID %" GST_FOURCC_FORMAT ", size %u",
5442 GST_FOURCC_ARGS (tag), size);
5444 stream_nr = CHUNKID_TO_STREAMNR (tag);
5446 if (G_UNLIKELY (stream_nr < 0 || stream_nr >= avi->num_streams)) {
5448 GST_WARNING ("Invalid stream ID %d (%" GST_FOURCC_FORMAT ")",
5449 stream_nr, GST_FOURCC_ARGS (tag));
5450 avi->offset += 8 + GST_ROUND_UP_2 (size);
5451 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5453 GstAviStream *stream;
5454 GstClockTime next_ts = 0;
5455 GstBuffer *buf = NULL;
5457 gboolean saw_desired_kf = stream_nr != avi->main_stream
5458 || avi->offset >= avi->seek_kf_offset;
5460 if (stream_nr == avi->main_stream && avi->offset == avi->seek_kf_offset) {
5461 GST_DEBUG_OBJECT (avi, "Desired keyframe reached");
5462 avi->seek_kf_offset = 0;
5465 if (saw_desired_kf) {
5466 gst_adapter_flush (avi->adapter, 8);
5469 buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size));
5470 /* patch the size */
5471 GST_BUFFER_SIZE (buf) = size;
5476 GST_DEBUG_OBJECT (avi,
5477 "Desired keyframe not yet reached, flushing chunk");
5478 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
5481 offset = avi->offset;
5482 avi->offset += 8 + GST_ROUND_UP_2 (size);
5484 stream = &avi->stream[stream_nr];
5486 /* set delay (if any)
5487 if (stream->strh->init_frames == stream->current_frame &&
5489 stream->delay = next_ts;
5492 /* parsing of corresponding header may have failed */
5493 if (G_UNLIKELY (!stream->pad)) {
5494 GST_WARNING_OBJECT (avi, "no pad for stream ID %" GST_FOURCC_FORMAT,
5495 GST_FOURCC_ARGS (tag));
5497 gst_buffer_unref (buf);
5499 /* get time of this buffer */
5500 gst_pad_query_position (stream->pad, &format, (gint64 *) & next_ts);
5501 if (G_UNLIKELY (format != GST_FORMAT_TIME))
5504 gst_avi_demux_add_assoc (avi, stream, next_ts, offset, FALSE);
5506 /* increment our positions */
5507 stream->current_entry++;
5508 stream->current_total += size;
5510 /* update current position in the segment */
5511 gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, next_ts);
5513 if (saw_desired_kf && buf) {
5514 GstClockTime dur_ts = 0;
5516 /* invert the picture if needed */
5517 buf = gst_avi_demux_invert (stream, buf);
5519 gst_pad_query_position (stream->pad, &format, (gint64 *) & dur_ts);
5520 if (G_UNLIKELY (format != GST_FORMAT_TIME))
5523 GST_BUFFER_TIMESTAMP (buf) = next_ts;
5524 GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
5525 if (stream->strh->type == GST_RIFF_FCC_vids) {
5526 GST_BUFFER_OFFSET (buf) = stream->current_entry - 1;
5527 GST_BUFFER_OFFSET_END (buf) = stream->current_entry;
5529 GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
5530 GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;
5533 gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
5534 GST_DEBUG_OBJECT (avi,
5535 "Pushing buffer with time=%" GST_TIME_FORMAT ", duration %"
5536 GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
5537 " and size %d over pad %s", GST_TIME_ARGS (next_ts),
5538 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
5539 GST_BUFFER_OFFSET (buf), size, GST_PAD_NAME (stream->pad));
5541 /* mark discont when pending */
5542 if (G_UNLIKELY (stream->discont)) {
5543 GST_DEBUG_OBJECT (avi, "Setting DISCONT");
5544 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
5545 stream->discont = FALSE;
5547 res = gst_pad_push (stream->pad, buf);
5551 res = gst_avi_demux_combine_flows (avi, stream, res);
5552 if (G_UNLIKELY (res != GST_FLOW_OK)) {
5553 GST_DEBUG ("Push failed; %s", gst_flow_get_name (res));
5567 GST_DEBUG_OBJECT (avi, "format %s != GST_FORMAT_TIME",
5568 gst_format_get_name (format));
5569 res = GST_FLOW_ERROR;
5575 * Send pending tags.
5578 push_tag_lists (GstAviDemux * avi)
5586 GST_DEBUG_OBJECT (avi, "Pushing pending tag lists");
5588 for (i = 0; i < avi->num_streams; i++) {
5589 GstAviStream *stream = &avi->stream[i];
5590 GstPad *pad = stream->pad;
5592 tags = stream->taglist;
5595 GST_DEBUG_OBJECT (pad, "Tags: %" GST_PTR_FORMAT, tags);
5597 gst_element_found_tags_for_pad (GST_ELEMENT_CAST (avi), pad, tags);
5598 stream->taglist = NULL;
5602 if (!(tags = avi->globaltags))
5603 tags = gst_tag_list_new ();
5605 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
5606 GST_TAG_CONTAINER_FORMAT, "AVI", NULL);
5608 GST_DEBUG_OBJECT (avi, "Global tags: %" GST_PTR_FORMAT, tags);
5609 gst_element_found_tags (GST_ELEMENT_CAST (avi), tags);
5610 avi->globaltags = NULL;
5611 avi->got_tags = FALSE;
5615 gst_avi_demux_loop (GstPad * pad)
5618 GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
5620 switch (avi->state) {
5621 case GST_AVI_DEMUX_START:
5622 res = gst_avi_demux_stream_init_pull (avi);
5623 if (G_UNLIKELY (res != GST_FLOW_OK)) {
5624 GST_WARNING ("stream_init flow: %s", gst_flow_get_name (res));
5627 avi->state = GST_AVI_DEMUX_HEADER;
5629 case GST_AVI_DEMUX_HEADER:
5630 res = gst_avi_demux_stream_header_pull (avi);
5631 if (G_UNLIKELY (res != GST_FLOW_OK)) {
5632 GST_WARNING ("stream_header flow: %s", gst_flow_get_name (res));
5636 /* Send tag to decoder, so decoder can knows that this is divx drm file */
5637 if (avi->drmContext)
5638 gst_avi_demux_send_divx_tag (avi);
5641 avi->state = GST_AVI_DEMUX_MOVI;
5643 case GST_AVI_DEMUX_MOVI:
5644 if (G_UNLIKELY (avi->close_seg_event)) {
5645 gst_avi_demux_push_event (avi, avi->close_seg_event);
5646 avi->close_seg_event = NULL;
5648 if (G_UNLIKELY (avi->seg_event)) {
5649 gst_avi_demux_push_event (avi, avi->seg_event);
5650 avi->seg_event = NULL;
5652 if (G_UNLIKELY (avi->got_tags)) {
5653 push_tag_lists (avi);
5655 /* process each index entry in turn */
5656 res = gst_avi_demux_loop_data (avi);
5658 #ifdef AVIDEMUX_MODIFICATION
5659 if (avi->segment.rate < 0.0 && res == GST_FLOW_UNEXPECTED) {
5660 GST_DEBUG_OBJECT(avi, "Seeking to previous keyframe");
5661 res = gst_avidemux_seek_to_previous_keyframe (avi);
5665 /* pause when error */
5666 if (G_UNLIKELY (res != GST_FLOW_OK)) {
5667 GST_INFO ("stream_movi flow: %s", gst_flow_get_name (res));
5672 GST_ERROR_OBJECT (avi, "unknown state %d", avi->state);
5673 res = GST_FLOW_ERROR;
5682 gboolean push_eos = FALSE;
5683 GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
5684 avi->segment_running = FALSE;
5685 gst_pad_pause_task (avi->sinkpad);
5688 if (res == GST_FLOW_UNEXPECTED) {
5689 /* handle end-of-stream/segment */
5690 if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
5693 if ((stop = avi->segment.stop) == -1)
5694 stop = avi->segment.duration;
5696 GST_INFO_OBJECT (avi, "sending segment_done");
5698 #ifdef AVIDEMUX_MODIFICATION
5699 if (avi->segment.rate >= 0) {
5700 /* Sending segment done at the end of segment */
5701 gst_element_post_message(GST_ELEMENT_CAST (avi),
5702 gst_message_new_segment_done (GST_OBJECT_CAST (avi), GST_FORMAT_TIME, stop));
5704 /* Sending segment done at the start of segment */
5705 gst_element_post_message(GST_ELEMENT_CAST (avi),
5706 gst_message_new_segment_done (GST_OBJECT_CAST (avi), GST_FORMAT_TIME, avi->segment.start));
5709 gst_element_post_message
5710 (GST_ELEMENT_CAST (avi),
5711 gst_message_new_segment_done (GST_OBJECT_CAST (avi),
5712 GST_FORMAT_TIME, stop));
5717 } else if (res == GST_FLOW_NOT_LINKED || res < GST_FLOW_UNEXPECTED) {
5718 /* for fatal errors we post an error message, wrong-state is
5719 * not fatal because it happens due to flushes and only means
5720 * that we should stop now. */
5721 GST_ELEMENT_ERROR (avi, STREAM, FAILED,
5722 (_("Internal data stream error.")),
5723 ("streaming stopped, reason %s", gst_flow_get_name (res)));
5727 GST_INFO_OBJECT (avi, "sending eos");
5728 if (!gst_avi_demux_push_event (avi, gst_event_new_eos ()) &&
5729 (res == GST_FLOW_UNEXPECTED)) {
5730 GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
5731 (NULL), ("got eos but no streams (yet)"));
5738 static GstFlowReturn
5739 gst_avi_demux_chain (GstPad * pad, GstBuffer * buf)
5742 GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
5745 if (GST_BUFFER_IS_DISCONT (buf)) {
5746 GST_DEBUG_OBJECT (avi, "got DISCONT");
5747 gst_adapter_clear (avi->adapter);
5748 /* mark all streams DISCONT */
5749 for (i = 0; i < avi->num_streams; i++)
5750 avi->stream[i].discont = TRUE;
5753 GST_DEBUG ("Store %d bytes in adapter", GST_BUFFER_SIZE (buf));
5754 gst_adapter_push (avi->adapter, buf);
5756 switch (avi->state) {
5757 case GST_AVI_DEMUX_START:
5758 if ((res = gst_avi_demux_stream_init_push (avi)) != GST_FLOW_OK) {
5759 GST_WARNING ("stream_init flow: %s", gst_flow_get_name (res));
5763 case GST_AVI_DEMUX_HEADER:
5764 if ((res = gst_avi_demux_stream_header_push (avi)) != GST_FLOW_OK) {
5765 GST_WARNING ("stream_header flow: %s", gst_flow_get_name (res));
5769 case GST_AVI_DEMUX_MOVI:
5770 if (G_UNLIKELY (avi->close_seg_event)) {
5771 gst_avi_demux_push_event (avi, avi->close_seg_event);
5772 avi->close_seg_event = NULL;
5774 if (G_UNLIKELY (avi->seg_event)) {
5775 gst_avi_demux_push_event (avi, avi->seg_event);
5776 avi->seg_event = NULL;
5778 if (G_UNLIKELY (avi->got_tags)) {
5779 push_tag_lists (avi);
5781 res = gst_avi_demux_stream_data (avi);
5783 case GST_AVI_DEMUX_SEEK:
5789 /* obtain and parse indexes */
5790 if (avi->stream[0].indexes && !gst_avi_demux_read_subindexes_push (avi))
5791 /* seek in subindex read function failed */
5794 if (!avi->stream[0].indexes && !avi->have_index
5795 && avi->avih->flags & GST_RIFF_AVIH_HASINDEX)
5796 gst_avi_demux_stream_index_push (avi);
5798 if (avi->have_index) {
5799 /* use the indexes now to construct nice durations */
5800 gst_avi_demux_calculate_durations_from_index (avi);
5802 /* still parsing indexes */
5806 GST_OBJECT_LOCK (avi);
5807 event = avi->seek_event;
5808 avi->seek_event = NULL;
5809 GST_OBJECT_UNLOCK (avi);
5811 /* calculate and perform seek */
5812 if (!avi_demux_handle_seek_push (avi, avi->sinkpad, event))
5815 gst_event_unref (event);
5816 avi->state = GST_AVI_DEMUX_MOVI;
5820 GST_ELEMENT_ERROR (avi, STREAM, FAILED, (NULL),
5821 ("Illegal internal state"));
5822 res = GST_FLOW_ERROR;
5826 GST_DEBUG_OBJECT (avi, "state: %d res:%s", avi->state,
5827 gst_flow_get_name (res));
5829 if (G_UNLIKELY (avi->abort_buffering))
5830 goto abort_buffering;
5837 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("failed to read indexes"));
5838 return GST_FLOW_ERROR;
5842 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("push mode seek failed"));
5843 return GST_FLOW_ERROR;
5847 avi->abort_buffering = FALSE;
5848 GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("unhandled buffer size"));
5849 return GST_FLOW_ERROR;
5854 gst_avi_demux_sink_activate (GstPad * sinkpad)
5856 if (gst_pad_check_pull_range (sinkpad)) {
5857 GST_DEBUG ("going to pull mode");
5858 return gst_pad_activate_pull (sinkpad, TRUE);
5860 GST_DEBUG ("going to push (streaming) mode");
5861 return gst_pad_activate_push (sinkpad, TRUE);
5866 gst_avi_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
5868 GstAviDemux *avi = GST_AVI_DEMUX (GST_OBJECT_PARENT (sinkpad));
5871 avi->segment_running = TRUE;
5872 avi->streaming = FALSE;
5873 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_avi_demux_loop,
5876 avi->segment_running = FALSE;
5877 return gst_pad_stop_task (sinkpad);
5882 gst_avi_demux_activate_push (GstPad * pad, gboolean active)
5884 GstAviDemux *avi = GST_AVI_DEMUX (GST_OBJECT_PARENT (pad));
5887 GST_DEBUG ("avi: activating push/chain function");
5888 avi->streaming = TRUE;
5890 /* create index for some push based seeking if not provided */
5891 GST_OBJECT_LOCK (avi);
5892 if (!avi->element_index) {
5893 GST_DEBUG_OBJECT (avi, "creating index");
5894 avi->element_index = gst_index_factory_make ("memindex");
5896 GST_OBJECT_UNLOCK (avi);
5897 /* object lock might be taken again */
5898 gst_index_get_writer_id (avi->element_index, GST_OBJECT_CAST (avi),
5902 GST_DEBUG ("avi: deactivating push/chain function");
5909 gst_avi_demux_set_index (GstElement * element, GstIndex * index)
5911 GstAviDemux *avi = GST_AVI_DEMUX (element);
5913 GST_OBJECT_LOCK (avi);
5914 if (avi->element_index)
5915 gst_object_unref (avi->element_index);
5917 avi->element_index = gst_object_ref (index);
5919 avi->element_index = NULL;
5921 GST_OBJECT_UNLOCK (avi);
5922 /* object lock might be taken again */
5924 gst_index_get_writer_id (index, GST_OBJECT_CAST (element), &avi->index_id);
5925 GST_DEBUG_OBJECT (avi, "Set index %" GST_PTR_FORMAT, avi->element_index);
5929 gst_avi_demux_get_index (GstElement * element)
5931 GstIndex *result = NULL;
5932 GstAviDemux *avi = GST_AVI_DEMUX (element);
5934 GST_OBJECT_LOCK (avi);
5935 if (avi->element_index)
5936 result = gst_object_ref (avi->element_index);
5937 GST_OBJECT_UNLOCK (avi);
5939 GST_DEBUG_OBJECT (avi, "Returning index %" GST_PTR_FORMAT, result);
5944 static GstStateChangeReturn
5945 gst_avi_demux_change_state (GstElement * element, GstStateChange transition)
5947 GstStateChangeReturn ret;
5948 GstAviDemux *avi = GST_AVI_DEMUX (element);
5950 switch (transition) {
5951 case GST_STATE_CHANGE_READY_TO_PAUSED:
5952 avi->streaming = FALSE;
5953 gst_segment_init (&avi->segment, GST_FORMAT_TIME);
5959 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5960 if (ret == GST_STATE_CHANGE_FAILURE)
5963 switch (transition) {
5964 case GST_STATE_CHANGE_PAUSED_TO_READY:
5965 avi->have_index = FALSE;
5966 gst_avi_demux_reset (avi);
5975 #ifdef AVIDEMUX_MODIFICATION
5976 /*Modification: Added function to find out the frame_type for index-table generation */
5979 gst_avi_demux_find_frame_type (GstAviStream *stream, GstBuffer *buf, int *frame_type)
5981 unsigned char *buff = NULL;
5982 unsigned int buff_len = 0;
5984 if (!stream || !buf || !frame_type) {
5985 GST_ERROR ("Invalid arguments..");
5989 buff = GST_BUFFER_DATA (buf);
5990 buff_len = GST_BUFFER_SIZE (buf);
5992 if ((NULL == buff) || buff_len < 5) {
5993 GST_ERROR ("Invalid buffer...");
5997 switch (stream->strh->fcc_handler) {
5998 /* mpeg stream parsing case */
5999 case GST_MAKE_FOURCC ('X', 'V', 'I', 'D'):
6000 case GST_MAKE_FOURCC ('x', 'v', 'i', 'd'):
6001 case GST_MAKE_FOURCC ('D', 'X', '5', '0'):
6002 case GST_MAKE_FOURCC ('d', 'i', 'v', 'x'):
6003 case GST_MAKE_FOURCC ('D', 'I', 'V', 'X'):
6004 case GST_MAKE_FOURCC ('B', 'L', 'Z', '0'):
6005 case GST_MAKE_FOURCC ('F', 'M', 'P', '4'):
6006 case GST_MAKE_FOURCC ('U', 'M', 'P', '4'):
6007 case GST_MAKE_FOURCC ('F', 'F', 'D', 'S'):
6008 case GST_MAKE_FOURCC ('M', 'P', 'E', 'G'):
6009 case GST_MAKE_FOURCC ('M', 'P', 'G', 'I'):
6010 case GST_MAKE_FOURCC ('m', 'p', 'g', '1'):
6011 case GST_MAKE_FOURCC ('M', 'P', 'G', '1'):
6012 case GST_MAKE_FOURCC ('P', 'I', 'M', '1'):
6013 case GST_MAKE_FOURCC ('M', 'P', 'G', '2'):
6014 case GST_MAKE_FOURCC ('m', 'p', 'g', '2'):
6015 case GST_MAKE_FOURCC ('P', 'I', 'M', '2'):
6016 case GST_MAKE_FOURCC ('D', 'V', 'R', ' '): {
6018 gboolean found_vop_code = FALSE;
6020 for (idx=0; idx< (buff_len-4); idx++) {
6021 /* Find VOP start frame which should be in every frame */
6022 if (buff[idx] == 0x00 && buff[idx+1] == 0x00 && buff[idx+2] == 0x01 && buff[idx+3] == 0xB6) {
6023 GST_DEBUG ("Found VOP start code...");
6024 found_vop_code = TRUE;
6029 if (!found_vop_code) {
6030 GST_ERROR ("Invalid input stream : There isn't any VOP header");
6034 if ((buff[idx] == 0x00) && (buff[idx+1] == 0x00) && (buff[idx+2] == 0x01)) {
6035 if(buff[idx+3] == 0xB6) {
6036 switch (buff[idx+4] & 0xC0) {
6038 GST_DEBUG ("Found Key-Frame");
6039 *frame_type = GST_AVI_KEYFRAME;
6042 GST_DEBUG ("Found Non-Key frame.. value = %x", buff[idx+4]);
6043 *frame_type = GST_AVI_NON_KEYFRAME;
6050 case GST_MAKE_FOURCC ('H', '2', '6', '3'):
6051 case GST_MAKE_FOURCC ('h', '2', '6', '3'):
6052 case GST_MAKE_FOURCC ('i', '2', '6', '3'):
6053 case GST_MAKE_FOURCC ('U', '2', '6', '3'):
6054 case GST_MAKE_FOURCC ('v', 'i', 'v', '1'):
6055 case GST_MAKE_FOURCC ('T', '2', '6', '3'): {
6056 /* FIXME: H263 Frame Parsing is yet to be done.*/
6057 *frame_type = GST_AVI_KEYFRAME;
6060 case GST_MAKE_FOURCC ('X', '2', '6', '4'):
6061 case GST_MAKE_FOURCC ('x', '2', '6', '4'):
6062 case GST_MAKE_FOURCC ('H', '2', '6', '4'):
6063 case GST_MAKE_FOURCC ('h', '2', '6', '4'):
6064 case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):
6065 case GST_MAKE_FOURCC ('A', 'V', 'C', '1'): {
6067 gint nalu_type = H264_NUT_UNKNOWN;
6069 /* H264 Frame Parsing */
6071 if (buff[idx+0] == 0x00 &&
6072 buff[idx+1] == 0x00 &&
6073 ((buff [idx+2] == 0x01) || ((buff [idx+2] == 0x00) && (buff [idx+3] == 0x01)))) {
6075 if (buff [idx+2] == 0x01) {
6076 nalu_type = buff[idx +3] & 0x1f;
6077 } else if ((buff [idx+2] == 0x00) && (buff [idx+3] == 0x01)) {
6078 nalu_type = buff[idx +4] & 0x1f;
6082 if ((nalu_type == H264_NUT_SPS) ||
6083 (nalu_type == H264_NUT_PPS) ||
6084 (nalu_type == H264_NUT_SEI) ||
6085 (nalu_type == H264_NUT_AUD)) {
6086 GST_LOG ("Skipping NALU SPS/PPS/SEI/AUD...");
6087 } else if (nalu_type == H264_NUT_IDR) {
6088 GST_DEBUG ("Found KEY frame...\n");
6089 *frame_type = GST_AVI_KEYFRAME;
6091 } else if ((nalu_type == H264_NUT_SLICE) ||
6092 (nalu_type == H264_NUT_DPA) ||
6093 (nalu_type == H264_NUT_DPB) ||
6094 (nalu_type == H264_NUT_DPC) ||
6095 (nalu_type == H264_NUT_EOSEQ) ||
6096 (nalu_type == H264_NUT_EOSTREAM)) {
6097 *frame_type = GST_AVI_NON_KEYFRAME;
6100 GST_DEBUG ("Unknown frame type, val = %d...", *frame_type);
6101 *frame_type = GST_AVI_NON_KEYFRAME;
6106 }while (idx < (buff_len - 4));
6110 //default make all frames as key frames
6111 *frame_type = GST_AVI_KEYFRAME;
6119 static void gst_avidemux_forward_trickplay (GstAviDemux * avi, GstAviStream * stream, guint64 *timestamp)
6121 guint32 nsamples = 0; /* Number of samples between two consecutive keyframes */
6122 guint64 next_kindex_timestamp;
6123 guint64 prev_kindex_timestamp;
6125 if (*timestamp < stream->trickplay_info->start_pos) {
6126 GST_LOG_OBJECT (avi, "Received shown sample... not applying trickplay algo");
6130 if (stream->trickplay_info->next_kidx == 0) {
6131 stream->trickplay_info->next_kidx = stream->trickplay_info->prev_kidx = stream->current_entry;
6133 /* while loop to handle multiple consecutive key frames */
6135 if((stream->trickplay_info->next_kidx +1)>=stream->idx_n) {
6136 GST_DEBUG_OBJECT(avi,"eos");
6140 /* find previous key frame */
6141 stream->trickplay_info->next_kidx = gst_avi_demux_index_next(avi, stream, stream->trickplay_info->next_kidx +1, TRUE);
6143 /* based no.of sample between key frame and rate, drop frames */
6144 GST_DEBUG_OBJECT (avi, "current index :%d, next key index : %d", stream->current_entry, stream->trickplay_info->next_kidx);
6146 /* find no.of samples between present and previous key frames */
6147 nsamples = stream->trickplay_info->next_kidx - stream->trickplay_info->prev_kidx;
6149 /* find corresponding timestamps of present and previous keyframes */
6150 next_kindex_timestamp = avi_stream_convert_frames_to_time_unchecked (stream, stream->trickplay_info->next_kidx);
6151 prev_kindex_timestamp = avi_stream_convert_frames_to_time_unchecked (stream, stream->trickplay_info->prev_kidx);
6153 /* find average duration between key frames */
6154 stream->trickplay_info->kidxs_dur_diff = (next_kindex_timestamp - prev_kindex_timestamp)/nsamples;
6156 stream->trickplay_info->show_samples = nsamples / avi->segment.rate;
6158 GST_DEBUG_OBJECT (avi, " duration between keyframes:%"GST_TIME_FORMAT, GST_TIME_ARGS(stream->trickplay_info->kidxs_dur_diff));
6160 if(stream->trickplay_info->show_samples) {
6161 GST_DEBUG_OBJECT (avi, "samples to display between two key frames = %d",
6162 stream->trickplay_info->show_samples);
6163 /* found no. of samples to show between key frames */
6164 *timestamp = avi_stream_convert_frames_to_time_unchecked (stream, stream->current_entry);
6166 } else if ((!stream->trickplay_info->show_samples) &&
6167 (stream->trickplay_info->next_kidx >= (stream->idx_n-1))){
6168 /* shown samples required to show between 2 key frames */
6169 stream->current_entry = stream->trickplay_info->next_kidx -1; /* next_kidx -1 is because advance_sample will increment */
6170 stream->trickplay_info->next_kidx = 0;
6174 stream->discont = TRUE;
6176 stream->trickplay_info->show_samples--;
6177 prev_kindex_timestamp = avi_stream_convert_frames_to_time_unchecked (stream, stream->trickplay_info->prev_kidx);
6178 *timestamp = prev_kindex_timestamp +
6179 (stream->current_entry - stream->trickplay_info->prev_kidx) * avi->segment.rate * stream->trickplay_info->kidxs_dur_diff;
6181 if (stream->trickplay_info->show_samples == 0) {
6182 /* shown samples required to show between 2 key frames */
6183 GST_DEBUG_OBJECT (avi, "reached end of keyframe interval....Jumping to next key index = %d", stream->trickplay_info->next_kidx);
6184 stream->current_entry= stream->trickplay_info->next_kidx -1; /* next_kidx -1 is because advance_sample will increment */
6185 stream->trickplay_info->next_kidx = 0;
6186 stream->discont = TRUE;
6192 gst_avidemux_backward_trickplay (GstAviDemux * avi, GstAviStream * stream, guint64 *timestamp)
6196 /* backward trick play */
6197 GST_DEBUG_OBJECT (avi, "backward trickplay start");
6198 index = gst_avi_demux_index_for_time(avi,stream,avi->seek_kf_offset);
6199 gst_avi_demux_move_stream (avi, stream, &avi->segment, index);
6202 static GstFlowReturn
6203 gst_avidemux_seek_to_previous_keyframe (GstAviDemux *avi)
6206 GstAviStream *stream;
6207 GstClockTime seek_time;
6209 guint64 time_position;
6211 gdouble minusone = -1;
6214 /* FIXME, this code assumes the main stream with keyframes is stream 0,
6215 * which is mostly correct... */
6216 stream = &avi->stream[avi->main_stream];
6218 if(stream->current_entry <= stream->start_entry) {
6219 /* Video stream reached start of the clip. So stop seeking to previous and send newsegment
6220 from _loop function, so that normal playback resumes*/
6224 index = stream->current_entry;
6225 time_position = avi_stream_convert_frames_to_time_unchecked (stream, index);
6226 duration= stream->current_ts_end - time_position;
6228 if((time_position - (minusone *avi->segment.rate)*duration)>0)
6229 time_position -= (minusone *avi->segment.rate)*duration;
6233 avi->seek_kf_offset = time_position;
6235 GST_DEBUG_OBJECT (avi, " seek_kf_offset after:%"GST_TIME_FORMAT, GST_TIME_ARGS(avi->seek_kf_offset));
6237 index = gst_avi_demux_index_for_time(avi,stream,time_position);
6239 index = gst_avi_demux_index_prev (avi, stream, index, TRUE);
6241 gst_avi_demux_move_stream (avi, stream, &avi->segment, index);
6243 seek_time = avi_stream_convert_frames_to_time_unchecked (stream, index);
6244 GST_DEBUG_OBJECT (avi, " seek_time is :%"GST_TIME_FORMAT, GST_TIME_ARGS(seek_time));
6247 stream->last_flow = GST_FLOW_OK;
6248 stream->discont = TRUE;
6250 /* Aligning other stream */
6251 for (i = 0; i < avi->num_streams; i++) {
6252 GstAviStream *ostream;
6254 ostream = &avi->stream[i];
6255 if ((ostream == stream) || (ostream->index == NULL))
6258 /* get the entry index for the requested position */
6259 index = gst_avi_demux_index_for_time (avi, ostream, seek_time);
6261 gst_avi_demux_move_stream (avi, ostream, &avi->segment, index);
6263 ostream->last_flow = GST_FLOW_OK;
6264 ostream->discont = TRUE;
6268 return GST_FLOW_UNEXPECTED;