diff --git a/ChangeLog b/ChangeLog index e96d4026fc..309eb13518 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2005-06-30 Ronald S. Bultje + + * gst/avi/Makefile.am: + * gst/avi/gstavi.c: (plugin_init): + * gst/avi/gstavidemux.c: (gst_avi_demux_get_type), + (gst_avi_demux_class_init), (gst_avi_demux_init), + (gst_avi_demux_reset), (gst_avi_demux_index_next), + (gst_avi_demux_index_entry_for_time), + (gst_avi_demux_index_entry_for_byte), + (gst_avi_demux_index_entry_for_frame), (gst_avi_demux_src_convert), + (gst_avi_demux_handle_src_query), (gst_avi_demux_handle_src_event), + (gst_avi_demux_parse_file_header), (gst_avi_demux_stream_init), + (gst_avi_demux_parse_avih), (gst_avi_demux_parse_superindex), + (gst_avi_demux_parse_subindex), (gst_avi_demux_read_subindexes), + (gst_avi_demux_parse_stream), (gst_avi_demux_parse_odml), + (gst_avi_demux_parse_index), (gst_avi_demux_stream_index), + (gst_avi_demux_stream_scan), (gst_avi_demux_massage_index), + (gst_avi_demux_stream_header), (gst_avi_demux_handle_seek), + (gst_avi_demux_process_next_entry), (gst_avi_demux_stream_data), + (gst_avi_demux_loop), (gst_avi_demux_sink_activate), + (gst_avi_demux_sink_activate_pull): + * gst/avi/gstavidemux.h: + Port from -THREADED to HEAD, and fix for latest API changes of + the day. Keep avimux dead for now. + 2005-06-29 Wim Taymans * ext/shout2/gstshout2.c: (gst_shout2send_render): diff --git a/gst/avi/Makefile.am b/gst/avi/Makefile.am index cbea2b3ad5..8db68a3120 100644 --- a/gst/avi/Makefile.am +++ b/gst/avi/Makefile.am @@ -2,16 +2,17 @@ plugin_LTLIBRARIES = libgstavi.la libgstavi_la_SOURCES = \ gstavi.c \ - gstavidemux.c \ - gstavimux.c + gstavidemux.c noinst_HEADERS = \ avi-ids.h \ gstavimux.h \ gstavidemux.h -libgstavi_la_CFLAGS = $(GST_CFLAGS) +libgstavi_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_LIBS) \ + -DGETTEXT_PACKAGE="\"gst-plugins\"" libgstavi_la_LIBADD = -libgstavi_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstavi_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) \ + ../../../gst-plugins-base/gst-libs/gst/riff/libgstriff-0.9.la -EXTRA_DIST = README +EXTRA_DIST = README gstavimux.c diff --git a/gst/avi/gstavi.c b/gst/avi/gstavi.c index 4ab32d7b46..91adeb406d 100644 --- a/gst/avi/gstavi.c +++ b/gst/avi/gstavi.c @@ -26,23 +26,21 @@ #include "gst/gst-i18n-plugin.h" #include "gstavidemux.h" -#include "gstavimux.h" +/*#include "gstavimux.h"*/ static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_library_load ("riff")) - return FALSE; + gst_riff_init (); #ifdef ENABLE_NLS setlocale (LC_ALL, ""); bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); #endif /* ENABLE_NLS */ - return (gst_element_register (plugin, "avidemux", - GST_RANK_PRIMARY, - GST_TYPE_AVI_DEMUX) && - gst_element_register (plugin, "avimux", GST_RANK_NONE, GST_TYPE_AVIMUX)); + return (gst_element_register (plugin, "avidemux", GST_RANK_PRIMARY, GST_TYPE_AVI_DEMUX) /*&& + gst_element_register (plugin, "avimux", GST_RANK_NONE, GST_TYPE_AVIMUX) */ + ); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index 99c14cc88e..19d5e7a87d 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -44,24 +44,29 @@ static void gst_avi_demux_class_init (GstAviDemuxClass * klass); static void gst_avi_demux_init (GstAviDemux * avi); static void gst_avi_demux_reset (GstAviDemux * avi); -static void gst_avi_demux_loop (GstElement * element); - -static gboolean gst_avi_demux_send_event (GstElement * element, - GstEvent * event); +#if 0 static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad); +#endif static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event); + +#if 0 static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad); +#endif static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad); -static gboolean gst_avi_demux_handle_src_query (GstPad * pad, - GstQueryType type, GstFormat * format, gint64 * value); +static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query); static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value); +static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi); +static void gst_avi_demux_loop (GstPad * pad); +static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad); +static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad, + gboolean active); static GstElementStateReturn gst_avi_demux_change_state (GstElement * element); -static GstRiffReadClass *parent_class = NULL; +static GstElementClass *parent_class = NULL; GType gst_avi_demux_get_type (void) @@ -82,7 +87,7 @@ gst_avi_demux_get_type (void) }; avi_demux_type = - g_type_register_static (GST_TYPE_RIFF_READ, + g_type_register_static (GST_TYPE_ELEMENT, "GstAviDemux", &avi_demux_info, 0); } @@ -122,33 +127,27 @@ gst_avi_demux_base_init (GstAviDemuxClass * klass) static void gst_avi_demux_class_init (GstAviDemuxClass * klass) { - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux", 0, "Demuxer for AVI streams"); - parent_class = g_type_class_ref (GST_TYPE_RIFF_READ); + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); gstelement_class->change_state = gst_avi_demux_change_state; - gstelement_class->send_event = gst_avi_demux_send_event; } static void gst_avi_demux_init (GstAviDemux * avi) { - GST_FLAG_SET (avi, GST_ELEMENT_EVENT_AWARE); - avi->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ), "sink"); + gst_pad_set_activate_function (avi->sinkpad, gst_avi_demux_sink_activate); + gst_pad_set_activatepull_function (avi->sinkpad, + gst_avi_demux_sink_activate_pull); gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad); - GST_RIFF_READ (avi)->sinkpad = avi->sinkpad; - gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop); gst_avi_demux_reset (avi); avi->index_entries = NULL; @@ -162,8 +161,13 @@ gst_avi_demux_reset (GstAviDemux * avi) for (i = 0; i < avi->num_streams; i++) { g_free (avi->stream[i].strh); + g_free (avi->stream[i].strf.data); + g_free (avi->stream[i].name); + if (avi->stream[i].initdata) + gst_buffer_unref (avi->stream[i].initdata); + if (avi->stream[i].extradata) + gst_buffer_unref (avi->stream[i].extradata); gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad); - gst_caps_free (avi->stream[i].caps); } memset (&avi->stream, 0, sizeof (avi->stream)); @@ -172,35 +176,32 @@ gst_avi_demux_reset (GstAviDemux * avi) avi->num_a_streams = 0; avi->state = GST_AVI_DEMUX_START; - avi->level_up = 0; + avi->offset = 0; - if (avi->index_entries) { - g_free (avi->index_entries); - avi->index_entries = NULL; - } + g_free (avi->index_entries); + avi->index_entries = NULL; avi->index_size = 0; avi->index_offset = 0; avi->current_entry = 0; - - avi->num_frames = 0; - avi->us_per_frame = 0; + g_free (avi->avih); + avi->avih = NULL; avi->seek_offset = (guint64) - 1; + if (avi->seek_event) + gst_event_unref (avi->seek_event); + avi->seek_event = NULL; } static gst_avi_index_entry * -gst_avi_demux_index_next (GstAviDemux * avi, - gint stream_nr, gint start, guint32 flags) +gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start) { gint i; gst_avi_index_entry *entry = NULL; for (i = start; i < avi->index_size; i++) { entry = &avi->index_entries[i]; - - if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) { + if (entry->stream_nr == stream_nr) break; - } } return entry; @@ -215,16 +216,15 @@ gst_avi_demux_index_entry_for_time (GstAviDemux * avi, i = -1; do { - entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags); + entry = gst_avi_demux_index_next (avi, stream_nr, i + 1); if (!entry) return NULL; i = entry->index_nr; - if (entry->ts <= time) { + if (entry->ts <= time && (entry->flags & flags) == flags) last_entry = entry; - } - } while (entry->ts <= time); + } while (entry->ts < time); return last_entry; } @@ -238,16 +238,15 @@ gst_avi_demux_index_entry_for_byte (GstAviDemux * avi, i = -1; do { - entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags); + entry = gst_avi_demux_index_next (avi, stream_nr, i + 1); if (!entry) return NULL; i = entry->index_nr; - if (entry->bytes_before <= byte) { + if (entry->bytes_before <= byte && (entry->flags & flags) == flags) last_entry = entry; - } - } while (entry->bytes_before <= byte); + } while (entry->bytes_before < byte); return last_entry; } @@ -261,20 +260,20 @@ gst_avi_demux_index_entry_for_frame (GstAviDemux * avi, i = -1; do { - entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags); + entry = gst_avi_demux_index_next (avi, stream_nr, i + 1); if (!entry) return NULL; i = entry->index_nr; - if (entry->frames_before <= frame) { + if (entry->frames_before <= frame && (entry->flags & flags) == flags) last_entry = entry; - } - } while (entry->frames_before <= frame); + } while (entry->frames_before < frame); return last_entry; } +#if 0 static const GstFormat * gst_avi_demux_get_src_formats (GstPad * pad) { @@ -295,6 +294,7 @@ gst_avi_demux_get_src_formats (GstPad * pad) return (stream->strh->type == GST_RIFF_FCC_auds ? src_a_formats : src_v_formats); } +#endif static gboolean gst_avi_demux_src_convert (GstPad * pad, @@ -303,9 +303,14 @@ gst_avi_demux_src_convert (GstPad * pad, { gboolean res = TRUE; - /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */ avi_stream_context *stream = gst_pad_get_element_private (pad); + if (src_format == *dest_format) { + *dest_value = src_value; + return TRUE; + } + if (!stream->strh || !stream->strf.data) + return FALSE; if (stream->strh->type == GST_RIFF_FCC_vids && (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) return FALSE; @@ -314,7 +319,7 @@ gst_avi_demux_src_convert (GstPad * pad, case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: - *dest_value = src_value * stream->bitrate / GST_SECOND; + *dest_value = src_value * stream->strf.auds->av_bps / GST_SECOND; break; case GST_FORMAT_DEFAULT: *dest_value = src_value * stream->strh->rate / @@ -328,7 +333,11 @@ gst_avi_demux_src_convert (GstPad * pad, case GST_FORMAT_BYTES: switch (*dest_format) { case GST_FORMAT_TIME: - *dest_value = ((gfloat) src_value) * GST_SECOND / stream->bitrate; + if (stream->strf.auds->av_bps != 0) { + *dest_value = ((gfloat) src_value) * GST_SECOND / + stream->strf.auds->av_bps; + } else + res = FALSE; break; default: res = FALSE; @@ -366,91 +375,52 @@ gst_avi_demux_get_src_query_types (GstPad * pad) } static gboolean -gst_avi_demux_handle_src_query (GstPad * pad, - GstQueryType type, GstFormat * format, gint64 * value) +gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query) { gboolean res = TRUE; - GstAviDemux *demux = GST_AVI_DEMUX (gst_pad_get_parent (pad)); + GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad)); - /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */ avi_stream_context *stream = gst_pad_get_element_private (pad); - switch (type) { - case GST_QUERY_TOTAL: - switch (*format) { - case GST_FORMAT_TIME: - *value = (((gfloat) stream->strh->scale) * stream->strh->length / - stream->strh->rate) * GST_SECOND; - break; - case GST_FORMAT_BYTES: - if (stream->strh->type == GST_RIFF_FCC_auds) { - *value = stream->total_bytes; - } else - res = FALSE; - break; - case GST_FORMAT_DEFAULT: - if (stream->strh->type == GST_RIFF_FCC_auds) { - if (!stream->strh->samplesize) - *value = stream->total_frames; - else - *value = stream->total_bytes / stream->strh->samplesize; - } else if (stream->strh->type == GST_RIFF_FCC_vids) - *value = stream->strh->length; - else - res = FALSE; - break; - default: - res = FALSE; - break; - } - break; - case GST_QUERY_POSITION: - switch (*format) { - case GST_FORMAT_TIME: - if (stream->strh->type == GST_RIFF_FCC_auds) { - if (!stream->strh->samplesize) { - *value = GST_SECOND * stream->current_frame * - stream->strh->scale / stream->strh->rate; - } else if (stream->bitrate != 0) { - *value = ((gfloat) stream->current_byte) * GST_SECOND / - stream->bitrate; - } else if (stream->total_frames != 0 && stream->total_bytes != 0) { - /* calculate timestamps based on video size */ - guint64 len = demux->us_per_frame * demux->num_frames * - GST_USECOND; + if (!stream->strh || !stream->strf.data) + return FALSE; - if (!stream->strh->samplesize) - *value = len * stream->current_frame / stream->total_frames; - else - *value = len * stream->current_byte / stream->total_bytes; - } else { - res = FALSE; - } - } else { - if (stream->strh->rate != 0) { - *value = ((gfloat) stream->current_frame * stream->strh->scale * - GST_SECOND / stream->strh->rate); - } else { - *value = stream->current_frame * demux->us_per_frame * - GST_USECOND; - } - } - break; - case GST_FORMAT_BYTES: - *value = stream->current_byte; - break; - case GST_FORMAT_DEFAULT: - if (stream->strh->samplesize && - stream->strh->type == GST_RIFF_FCC_auds) - *value = stream->current_byte / stream->strh->samplesize; + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION:{ + gint64 len, pos; + + len = (((gfloat) stream->strh->scale) * stream->strh->length / + stream->strh->rate) * GST_SECOND; + if (stream->strh->type == GST_RIFF_FCC_auds) { + if (!stream->strh->samplesize) { + pos = GST_SECOND * stream->current_frame * + stream->strh->scale / stream->strh->rate; + } else if (stream->strf.auds->av_bps != 0) { + pos = ((gfloat) stream->current_byte) * GST_SECOND / + stream->strf.auds->av_bps; + } else if (stream->total_frames != 0 && stream->total_bytes != 0) { + /* calculate timestamps based on video size */ + guint64 xlen = demux->avih->us_frame * + demux->avih->tot_frames * GST_USECOND; + + if (!stream->strh->samplesize) + pos = xlen * stream->current_frame / stream->total_frames; else - *value = stream->current_frame; - break; - default: + pos = xlen * stream->current_byte / stream->total_bytes; + } else { res = FALSE; - break; + } + } else { + if (stream->strh->rate != 0) { + pos = ((gfloat) stream->current_frame * stream->strh->scale * + GST_SECOND / stream->strh->rate); + } else { + pos = stream->current_frame * demux->avih->us_frame * GST_USECOND; + } } + gst_query_set_position (query, GST_FORMAT_TIME, pos, len); break; + } default: res = FALSE; break; @@ -459,43 +429,7 @@ gst_avi_demux_handle_src_query (GstPad * pad, return res; } -static GstCaps * -gst_avi_demux_src_getcaps (GstPad * pad) -{ - avi_stream_context *stream = gst_pad_get_element_private (pad); - - return gst_caps_copy (stream->caps); -} - -static gboolean -gst_avi_demux_send_event (GstElement * element, GstEvent * event) -{ - const GList *pads; - - pads = gst_element_get_pad_list (element); - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - - if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) { - /* we ref the event here as we might have to try again if the event - * failed on this pad */ - gst_event_ref (event); - if (gst_avi_demux_handle_src_event (pad, event)) { - gst_event_unref (event); - - return TRUE; - } - } - - pads = g_list_next (pads); - } - - gst_event_unref (event); - - return FALSE; -} - +#if 0 static const GstEventMask * gst_avi_demux_get_event_mask (GstPad * pad) { @@ -506,12 +440,13 @@ gst_avi_demux_get_event_mask (GstPad * pad) return masks; } +#endif static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event) { gboolean res = TRUE; - GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); + GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad)); avi_stream_context *stream; GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, @@ -532,7 +467,7 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event) case GST_FORMAT_BYTES: case GST_FORMAT_DEFAULT: case GST_FORMAT_TIME:{ - gst_avi_index_entry *entry = NULL; + gst_avi_index_entry *entry = NULL, *real; gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event); guint32 flags; @@ -542,32 +477,42 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event) flags = GST_RIFF_IF_KEYFRAME; switch (GST_EVENT_SEEK_FORMAT (event)) { case GST_FORMAT_BYTES: - entry = gst_avi_demux_index_entry_for_byte (avi, stream->num, + entry = gst_avi_demux_index_entry_for_byte (avi, 0, //stream->num, desired_offset, flags); + real = gst_avi_demux_index_entry_for_byte (avi, stream->num, + desired_offset, 0); break; case GST_FORMAT_DEFAULT: - entry = gst_avi_demux_index_entry_for_frame (avi, stream->num, + entry = gst_avi_demux_index_entry_for_frame (avi, 0, //stream->num, desired_offset, flags); + real = gst_avi_demux_index_entry_for_frame (avi, stream->num, + desired_offset, 0); break; case GST_FORMAT_TIME: - entry = gst_avi_demux_index_entry_for_time (avi, stream->num, + entry = gst_avi_demux_index_entry_for_time (avi, 0, //stream->num, desired_offset, flags); + real = gst_avi_demux_index_entry_for_time (avi, stream->num, + desired_offset, 0); break; } + if (!(flags & GST_SEEK_FLAG_ACCURATE)) + real = entry; + if (entry) { avi->seek_offset = entry->offset + avi->index_offset; - avi->last_seek = entry->ts; + avi->last_seek = real->ts; avi->seek_flush = (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH); avi->seek_entry = entry->index_nr; GST_DEBUG_OBJECT (avi, "Will seek to entry %d", avi->seek_entry); + res = gst_avi_demux_handle_seek (avi); } else { GST_DEBUG_OBJECT (avi, "no index entry found for format=%d value=%" G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), desired_offset); res = FALSE; } - GST_LOG ("seek done\n"); + GST_LOG ("seek done"); break; } default: @@ -585,120 +530,154 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event) return res; } -/* - * "Open" a RIFF file. +/** + * gst_avi_demux_parse_file_header: + * @element: caller element (used for errors/debug). + * @buf: input data to be used for parsing. + * + * "Open" a RIFF/AVI file. The buffer should be at least 12 + * bytes long. Discards buffer after use. + * + * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise. + * Throws an error, caller should error out (fatal). */ static gboolean -gst_avi_demux_stream_init (GstAviDemux * avi) +gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf) { - GstRiffRead *riff = GST_RIFF_READ (avi); guint32 doctype; - if (!gst_riff_read_header (riff, &doctype)) + if (!gst_riff_parse_file_header (element, buf, &doctype)) return FALSE; + if (doctype != GST_RIFF_RIFF_AVI) { - GST_ELEMENT_ERROR (avi, STREAM, WRONG_TYPE, (NULL), (NULL)); + GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL), + ("File is not an AVI file: " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (doctype))); return FALSE; } return TRUE; } -/* - * Read 'avih' header. +static GstFlowReturn +gst_avi_demux_stream_init (GstAviDemux * avi) +{ + GstFlowReturn res; + GstBuffer *buf = NULL; + + if ((res = gst_pad_pull_range (avi->sinkpad, + avi->offset, 12, &buf)) != GST_FLOW_OK) + return res; + else if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), buf)) + return GST_FLOW_ERROR; + + avi->offset += 12; + + return GST_FLOW_OK; +} + +/** + * gst_avi_demux_parse_avih: + * @element: caller element (used for errors/debug). + * @buf: input data to be used for parsing. + * @avih: pointer to structure (filled in by function) containing + * stream information (such as flags, number of streams, etc.). + * + * Read 'avih' header. Discards buffer after use. + * + * Returns: TRUE on success, FALSE otherwise. Throws an error if + * the header is invalid. The caller should error out + * (fatal). */ static gboolean -gst_avi_demux_stream_avih (GstAviDemux * avi, - guint32 * flags, guint32 * streams) +gst_avi_demux_parse_avih (GstElement * element, + GstBuffer * buf, gst_riff_avih ** _avih) { - GstRiffRead *riff = GST_RIFF_READ (avi); - guint32 tag; - GstBuffer *buf; - gst_riff_avih avih, *_avih; + gst_riff_avih *avih; - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - - if (tag != GST_RIFF_TAG_avih) { - g_warning ("Not a avih chunk"); - gst_buffer_unref (buf); - return FALSE; - } - if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih)) { - g_warning ("Too small avih (%d available, %d needed)", - GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)); - gst_buffer_unref (buf); + if (!buf || GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih)) { + GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL), + ("Too small avih (%d available, %d needed)", + GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih))); + if (buf) + gst_buffer_unref (buf); return FALSE; } - _avih = (gst_riff_avih *) GST_BUFFER_DATA (buf); - avih.us_frame = GUINT32_FROM_LE (_avih->us_frame); - avih.max_bps = GUINT32_FROM_LE (_avih->max_bps); - avih.pad_gran = GUINT32_FROM_LE (_avih->pad_gran); - avih.flags = GUINT32_FROM_LE (_avih->flags); - avih.tot_frames = GUINT32_FROM_LE (_avih->tot_frames); - avih.init_frames = GUINT32_FROM_LE (_avih->init_frames); - avih.streams = GUINT32_FROM_LE (_avih->streams); - avih.bufsize = GUINT32_FROM_LE (_avih->bufsize); - avih.width = GUINT32_FROM_LE (_avih->width); - avih.height = GUINT32_FROM_LE (_avih->height); - avih.scale = GUINT32_FROM_LE (_avih->scale); - avih.rate = GUINT32_FROM_LE (_avih->rate); - avih.start = GUINT32_FROM_LE (_avih->start); - avih.length = GUINT32_FROM_LE (_avih->length); + avih = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); + +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + avih->us_frame = GUINT32_FROM_LE (avih->us_frame); + avih->max_bps = GUINT32_FROM_LE (avih->max_bps); + avih->pad_gran = GUINT32_FROM_LE (avih->pad_gran); + avih->flags = GUINT32_FROM_LE (avih->flags); + avih->tot_frames = GUINT32_FROM_LE (avih->tot_frames); + avih->init_frames = GUINT32_FROM_LE (avih->init_frames); + avih->streams = GUINT32_FROM_LE (avih->streams); + avih->bufsize = GUINT32_FROM_LE (avih->bufsize); + avih->width = GUINT32_FROM_LE (avih->width); + avih->height = GUINT32_FROM_LE (avih->height); + avih->scale = GUINT32_FROM_LE (avih->scale); + avih->rate = GUINT32_FROM_LE (avih->rate); + avih->start = GUINT32_FROM_LE (avih->start); + avih->length = GUINT32_FROM_LE (avih->length); +#endif /* debug stuff */ - GST_INFO ("avih tag found:"); - GST_INFO (" us_frame %u", avih.us_frame); - GST_INFO (" max_bps %u", avih.max_bps); - GST_INFO (" pad_gran %u", avih.pad_gran); - GST_INFO (" flags 0x%08x", avih.flags); - GST_INFO (" tot_frames %u", avih.tot_frames); - GST_INFO (" init_frames %u", avih.init_frames); - GST_INFO (" streams %u", avih.streams); - GST_INFO (" bufsize %u", avih.bufsize); - GST_INFO (" width %u", avih.width); - GST_INFO (" height %u", avih.height); - GST_INFO (" scale %u", avih.scale); - GST_INFO (" rate %u", avih.rate); - GST_INFO (" start %u", avih.start); - GST_INFO (" length %u", avih.length); - - avi->num_frames = avih.tot_frames; - avi->us_per_frame = avih.us_frame; - *streams = avih.streams; - *flags = avih.flags; + GST_INFO_OBJECT (element, "avih tag found:"); + GST_INFO_OBJECT (element, " us_frame %u", avih->us_frame); + GST_INFO_OBJECT (element, " max_bps %u", avih->max_bps); + GST_INFO_OBJECT (element, " pad_gran %u", avih->pad_gran); + GST_INFO_OBJECT (element, " flags 0x%08x", avih->flags); + GST_INFO_OBJECT (element, " tot_frames %u", avih->tot_frames); + GST_INFO_OBJECT (element, " init_frames %u", avih->init_frames); + GST_INFO_OBJECT (element, " streams %u", avih->streams); + GST_INFO_OBJECT (element, " bufsize %u", avih->bufsize); + GST_INFO_OBJECT (element, " width %u", avih->width); + GST_INFO_OBJECT (element, " height %u", avih->height); + GST_INFO_OBJECT (element, " scale %u", avih->scale); + GST_INFO_OBJECT (element, " rate %u", avih->rate); + GST_INFO_OBJECT (element, " start %u", avih->start); + GST_INFO_OBJECT (element, " length %u", avih->length); + *_avih = avih; gst_buffer_unref (buf); return TRUE; } -/* - * Read superindex/subindex (openDML-2). +/** + * gst_avi_demux_parse_superindex: + * @element: caller element (used for debugging/errors). + * @buf: input data to use for parsing. + * @locations: locations in the file (byte-offsets) that contain + * the actual indexes (see get_avi_demux_parse_subindex()). + * The array ends with GST_BUFFER_OFFSET_NONE. + * + * Reads superindex (openDML-2 spec stuff) from the provided data. + * + * Returns: TRUE on success, FALSE otherwise. Indexes should be skipped + * on error, but they are not fatal. */ static gboolean -gst_avi_demux_read_superindex (GstAviDemux * avi, - gint stream_nr, guint64 ** locations) +gst_avi_demux_parse_superindex (GstElement * element, + GstBuffer * buf, guint64 ** _indexes) { - GstRiffRead *riff = GST_RIFF_READ (avi); - guint32 tag; - GstBuffer *buf; - guint8 *data; + guint8 *data = GST_BUFFER_DATA (buf); gint bpe = 16, num, i; guint64 *indexes; - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - data = GST_BUFFER_DATA (buf); - if (tag != GST_MAKE_FOURCC ('i', 'n', 'd', 'x') && - tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream_nr / 10, - '0' + stream_nr % 10)) { - g_warning ("Not an indx/ix## chunk"); - gst_buffer_unref (buf); + *_indexes = NULL; + + if (!buf || GST_BUFFER_SIZE (buf) < 24) { + GST_ERROR_OBJECT (element, + "Not enough data to parse superindex (%d available, %d needed)", + GST_BUFFER_SIZE (buf), 24); + if (buf) + gst_buffer_unref (buf); return FALSE; } @@ -707,7 +686,8 @@ gst_avi_demux_read_superindex (GstAviDemux * avi, * either frame or field (and we don't care). */ if (GST_READ_UINT16_LE (data) != 4 || (data[2] & 0xfe) != 0x0 || data[3] != 0x0) { - GST_WARNING ("Superindex for stream %d has unexpected " + GST_WARNING_OBJECT (element, + "Superindex for stream %d has unexpected " "size_entry %d (bytes) or flags 0x%02x/0x%02x", GST_READ_UINT16_LE (data), data[2], data[3]); bpe = GST_READ_UINT16_LE (data) * 4; @@ -720,566 +700,477 @@ gst_avi_demux_read_superindex (GstAviDemux * avi, break; indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]); } - indexes[i] = 0; - *locations = indexes; + indexes[i] = GST_BUFFER_OFFSET_NONE; + *_indexes = indexes; gst_buffer_unref (buf); return TRUE; } +/** + * gst_avi_demux_parse_subindex: + * @element: caller element (used for errors/debug). + * @buf: input data to use for parsing. + * @stream: stream context. + * @entries_list: a list (returned by the function) containing all the + * indexes parsed in this specific subindex. The first + * entry is also a pointer to allocated memory that needs + * to be free´ed. May be NULL if no supported indexes were + * found. + * + * Reads superindex (openDML-2 spec stuff) from the provided data. + * The buffer will be discarded after use. + * + * Returns: TRUE on success, FALSE otherwise. Errors are fatal, we + * throw an error, caller should bail out asap. + */ + static gboolean +gst_avi_demux_parse_subindex (GstElement * element, + GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list) +{ + guint8 *data = GST_BUFFER_DATA (buf); + gint bpe, num, x; + guint64 baseoff; + gst_avi_index_entry *entries, *entry; + GList *entries_list = NULL; + GstFormat format = GST_FORMAT_TIME; + + /* check size */ + if (!buf || GST_BUFFER_SIZE (buf) < 24) { + GST_ERROR_OBJECT (element, + "Not enough data to parse subindex (%d available, %d needed)", + GST_BUFFER_SIZE (buf), 24); + if (buf) + gst_buffer_unref (buf); + *_entries_list = NULL; + return TRUE; /* continue */ + } + + /* We don't support index-data yet */ + if (data[3] & 0x80) { + GST_ELEMENT_ERROR (element, STREAM, NOT_IMPLEMENTED, (NULL), + ("Subindex-is-data is not implemented")); + gst_buffer_unref (buf); + return FALSE; + } + + /* check type of index. The opendml2 specs state that + * there should be 4 dwords per array entry. Type can be + * either frame or field (and we don't care). */ + bpe = (data[2] & 0x01) ? 12 : 8; + if (GST_READ_UINT16_LE (data) != bpe / 4 || + (data[2] & 0xfe) != 0x0 || data[3] != 0x1) { + GST_WARNING_OBJECT (element, + "Superindex for stream %d has unexpected " + "size_entry %d (bytes) or flags 0x%02x/0x%02x", + GST_READ_UINT16_LE (data), data[2], data[3]); + bpe = GST_READ_UINT16_LE (data) * 4; + } + num = GST_READ_UINT32_LE (&data[4]); + baseoff = GST_READ_UINT64_LE (&data[12]); + + entries = g_new (gst_avi_index_entry, num); + for (x = 0; x < num; x++) { + entry = &entries[x]; + + if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1)) + break; + + /* fill in */ + entry->offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * x]); + entry->size = GST_READ_UINT32_LE (&data[24 + bpe * x + 4]); + entry->flags = (entry->size & 0x80000000) ? 0 : GST_RIFF_IF_KEYFRAME; + entry->size &= ~0x80000000; + entry->index_nr = x; + entry->stream_nr = stream->num; + + /* timestamps */ + if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) { + /* constant rate stream */ + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, + stream->total_bytes, &format, &entry->ts); + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, + stream->total_bytes + entry->size, &format, &entry->dur); + } else { + /* VBR stream */ + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, + stream->total_frames, &format, &entry->ts); + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, + stream->total_frames + 1, &format, &entry->dur); + } + entry->dur -= entry->ts; + + /* stream position */ + entry->bytes_before = stream->total_bytes; + stream->total_bytes += entry->size; + entry->frames_before = stream->total_frames; + stream->total_frames++; + + entries_list = g_list_prepend (entries_list, entry); + } + + GST_LOG_OBJECT (element, "Read %d index entries", x); + + gst_buffer_unref (buf); + + if (x > 0) { + *_entries_list = g_list_reverse (entries_list); + } else { + *_entries_list = NULL; + g_free (entries); + } + + return TRUE; +} + +static void gst_avi_demux_read_subindexes (GstAviDemux * avi, GList ** index, GList ** alloc_list) { - GstRiffRead *riff = GST_RIFF_READ (avi); - guint64 pos = gst_bytestream_tell (riff->bs), - length = gst_bytestream_length (riff->bs), baseoff; - GstEvent *event; - GList *list = NULL; - gst_avi_index_entry *entries, *entry; + GList *list; guint32 tag; GstBuffer *buf; - guint8 *data; - GstFormat format = GST_FORMAT_TIME; - gint bpe, num, x, i, n; + gint i, n; for (n = 0; n < avi->num_streams; n++) { avi_stream_context *stream = &avi->stream[n]; - for (i = 0; stream->indexes[i] != 0; i++) { - /* eos check again */ - if (stream->indexes[i] + 8 >= length) { - GST_WARNING ("Subindex %d for stream %d doesn't exist", i, n); + for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) { + if (gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad, + &stream->indexes[i], &tag, &buf) != GST_FLOW_OK) continue; - } - - /* seek to that point */ - if (!(event = gst_riff_read_seek (riff, stream->indexes[i]))) { - g_list_free (list); - return FALSE; - } - gst_event_unref (event); - if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) { - g_list_free (list); - return FALSE; - } - - /* eos check again */ - if (GST_READ_UINT32_LE (&data[4]) + gst_bytestream_tell (riff->bs) > - length) { - GST_WARNING ("Subindex %d for stream %d lies outside file", i, n); - continue; - } - - /* now read */ - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - data = GST_BUFFER_DATA (buf); - if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10, + else if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10, '0' + stream->num % 10)) { - GST_WARNING ("Not an ix## chunk (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS (tag)); + GST_ERROR_OBJECT (GST_ELEMENT (avi), + "Not an ix## chunk (" GST_FOURCC_FORMAT ")", GST_FOURCC_ARGS (tag)); gst_buffer_unref (buf); continue; } - /* We don't support index-data yet */ - if (data[3] & 0x80) { - GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL), - ("Subindex-is-data is not implemented")); - return FALSE; + if (!gst_avi_demux_parse_subindex (GST_ELEMENT (avi), buf, stream, &list)) + continue; + if (list) { + *alloc_list = g_list_append (*alloc_list, list->data); + *index = g_list_concat (*index, list); } - - /* check type of index. The opendml2 specs state that - * there should be 4 dwords per array entry. Type can be - * either frame or field (and we don't care). */ - bpe = (data[2] & 0x01) ? 12 : 8; - if (GST_READ_UINT16_LE (data) != bpe / 4 || - (data[2] & 0xfe) != 0x0 || data[3] != 0x1) { - GST_WARNING ("Superindex for stream %d has unexpected " - "size_entry %d (bytes) or flags 0x%02x/0x%02x", - GST_READ_UINT16_LE (data), data[2], data[3]); - bpe = GST_READ_UINT16_LE (data) * 4; - } - num = GST_READ_UINT32_LE (&data[4]); - baseoff = GST_READ_UINT64_LE (&data[12]); - - entries = g_new (gst_avi_index_entry, num); - *alloc_list = g_list_append (*alloc_list, entries); - for (x = 0; x < num; x++) { - entry = &entries[x]; - - if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1)) - break; - - /* fill in */ - entry->offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * x]); - entry->size = GST_READ_UINT32_LE (&data[24 + bpe * x + 4]); - entry->flags = (entry->size & 0x80000000) ? 0 : GST_RIFF_IF_KEYFRAME; - entry->size &= ~0x80000000; - entry->index_nr = x; - entry->stream_nr = stream->num; - - /* timestamps */ - if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) { - /* constant rate stream */ - gst_pad_convert (stream->pad, GST_FORMAT_BYTES, - stream->total_bytes, &format, &entry->ts); - gst_pad_convert (stream->pad, GST_FORMAT_BYTES, - stream->total_bytes + entry->size, &format, &entry->dur); - } else { - /* VBR stream */ - gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, - stream->total_frames, &format, &entry->ts); - gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, - stream->total_frames + 1, &format, &entry->dur); - } - entry->dur -= entry->ts; - - /* stream position */ - entry->bytes_before = stream->total_bytes; - stream->total_bytes += entry->size; - entry->frames_before = stream->total_frames; - stream->total_frames++; - - list = g_list_prepend (list, entry); - } - - GST_LOG ("Read %d index entries in subindex %d for stream %d " - "at location %" G_GUINT64_FORMAT, num, i, n, stream->indexes[i]); - - gst_buffer_unref (buf); } g_free (stream->indexes); stream->indexes = NULL; } - - /* seek back */ - if (!(event = gst_riff_read_seek (riff, pos))) { - g_list_free (list); - return FALSE; - } - gst_event_unref (event); - *index = g_list_reverse (list); - - return TRUE; } -/* - * Add a stream. +/** + * gst_avi_demux_parse_stream: + * @element: calling element (used for debugging/errors). + * @buf: input buffer used to parse the stream. + * + * Parses all subchunks in a strl chunk (which defines a single + * stream). Discards the buffer after use. This function will + * increment the stream counter internally. + * + * Returns: whether the stream was identified successfully. + * Errors are not fatal. It does indicate the stream + * was skipped. */ static gboolean -gst_avi_demux_add_stream (GstAviDemux * avi) +gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf) { - GstElementClass *klass = GST_ELEMENT_GET_CLASS (avi); - GstRiffRead *riff = GST_RIFF_READ (avi); - guint32 tag; - gst_riff_strh *strh; - GstBuffer *extradata = NULL, *initdata = NULL; - gchar *name = NULL, *padname = NULL; + GstAviDemux *avi = GST_AVI_DEMUX (element); + avi_stream_context *stream = &avi->stream[avi->num_streams]; + GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); + GstPadTemplate *templ; + GstBuffer *sub = NULL; + guint offset = 4; + guint32 tag = 0; + gchar *codec_name = NULL, *padname = NULL; + const gchar *tag_name; GstCaps *caps = NULL; - GstPadTemplate *templ = NULL; GstPad *pad; - avi_stream_context *stream; - gint blockalign = 0, bitrate = 0, width = 0, height = 0; - guint64 *locations = NULL; - GstTagList *list = gst_tag_list_new (); - gboolean have_tag = FALSE; - union - { - gst_riff_strf_vids *vids; - gst_riff_strf_auds *auds; - gst_riff_strf_iavs *iavs; - } - strf; - /* the stream starts with a 'strh' header */ - if (!(tag = gst_riff_peek_tag (riff, NULL))) - return FALSE; - if (tag != GST_RIFF_TAG_strh) { - g_warning ("Invalid stream header (no strh at begin)"); - goto skip_stream; - } - if (!gst_riff_read_strh (riff, &strh)) - return FALSE; + GST_DEBUG_OBJECT (element, "Parsing stream"); - /* then comes a 'strf' of that specific type */ - if (!(tag = gst_riff_peek_tag (riff, NULL))) - return FALSE; - if (tag != GST_RIFF_TAG_strf) { - GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), - ("Invalid AVI header (no strf as second tag)")); - goto skip_stream; - } - switch (strh->type) { - case GST_RIFF_FCC_vids: - if (!gst_riff_read_strf_vids_with_data (riff, &strf.vids, &extradata)) - return FALSE; - break; - case GST_RIFF_FCC_auds: - if (!gst_riff_read_strf_auds_with_data (riff, &strf.auds, &extradata)) - return FALSE; - break; - case GST_RIFF_FCC_iavs: - if (!gst_riff_read_strf_iavs (riff, &strf.iavs)) - return FALSE; - break; - default: - g_warning ("Unknown stream type " GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (strh->type)); - goto skip_stream; - } + /* read strh */ + if (!buf || !gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) || + tag != GST_RIFF_TAG_strh) { + GST_ERROR_OBJECT (element, + "Failed to find strh chunk (tag: " GST_FOURCC_FORMAT ")", + buf ? GST_BUFFER_SIZE (buf) : 0, GST_FOURCC_ARGS (tag)); + goto fail; + } else if (!gst_riff_parse_strh (element, sub, &stream->strh)) + goto fail; - /* read other things */ - while (TRUE) { - if (!(tag = gst_riff_peek_tag (riff, &avi->level_up))) - return FALSE; - else if (avi->level_up) { - avi->level_up--; - break; + /* read strf */ + if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) || + tag != GST_RIFF_TAG_strf) { + GST_ERROR_OBJECT (element, + "Failed to find strh chunk (size: %d, tag: " + GST_FOURCC_FORMAT ")", buf ? GST_BUFFER_SIZE (buf) : 0, + GST_FOURCC_ARGS (tag)); + goto fail; + } else { + gboolean res = FALSE; + + switch (stream->strh->type) { + case GST_RIFF_FCC_vids: + res = gst_riff_parse_strf_vids (element, sub, + &stream->strf.vids, &stream->extradata); + break; + case GST_RIFF_FCC_auds: + res = gst_riff_parse_strf_auds (element, sub, + &stream->strf.auds, &stream->extradata); + break; + case GST_RIFF_FCC_iavs: + res = gst_riff_parse_strf_iavs (element, sub, + &stream->strf.iavs, &stream->extradata); + break; + default: + GST_ERROR_OBJECT (element, + "Don´t know how to handle stream type " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (stream->strh->type)); + break; } + if (!res) + goto fail; + } + + /* read strd/strn */ + while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) { switch (tag) { case GST_RIFF_TAG_strd: - if (initdata) - gst_buffer_unref (initdata); - if (!gst_riff_read_data (riff, &tag, &initdata)) - return FALSE; + if (stream->initdata) + gst_buffer_unref (stream->initdata); + stream->initdata = sub; break; - case GST_RIFF_TAG_strn: - g_free (name); - if (!gst_riff_read_ascii (riff, &tag, &name)) - return FALSE; + g_free (stream->name); + stream->name = g_new (gchar, GST_BUFFER_SIZE (sub)); + memcpy (stream->name, GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub)); + stream->name[GST_BUFFER_SIZE (sub)] = '\0'; + gst_buffer_unref (sub); break; - default: if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') || - tag == GST_MAKE_FOURCC ('i', 'x', avi->num_streams / 10, - avi->num_streams % 10)) { - g_free (locations); - if (!gst_avi_demux_read_superindex (avi, - avi->num_streams, &locations)) - return FALSE; + tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10, + '0' + avi->num_streams % 10)) { + g_free (stream->indexes); + gst_avi_demux_parse_superindex (element, sub, &stream->indexes); break; } - GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header", + GST_WARNING_OBJECT (element, + "Unknown stream header tag " GST_FOURCC_FORMAT ", ignoring", GST_FOURCC_ARGS (tag)); /* fall-through */ - case GST_RIFF_TAG_JUNK: - if (!gst_riff_read_skip (riff)) - return FALSE; + gst_buffer_unref (sub); break; } - - if (avi->level_up) { - avi->level_up--; - break; - } } + /* we now have all info, let´s set up a pad and a caps and be done */ /* create stream name + pad */ - switch (strh->type) { + switch (stream->strh->type) { case GST_RIFF_FCC_vids: - { - char *codec_name = NULL; - guint32 tag; - padname = g_strdup_printf ("video_%02d", avi->num_v_streams); templ = gst_element_class_get_pad_template (klass, "video_%02d"); - if (strf.vids->compression) - tag = strf.vids->compression; - else - tag = strh->fcc_handler; - caps = gst_riff_create_video_caps_with_data (tag, - strh, strf.vids, extradata, initdata, &codec_name); - if (codec_name) { - gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC, - codec_name, NULL); - have_tag = TRUE; - g_free (codec_name); - } - width = strf.vids->width; - height = strf.vids->height; - g_free (strf.vids); + caps = gst_riff_create_video_caps (stream->strf.vids->compression ? + stream->strf.vids->compression : stream->strh->fcc_handler, + stream->strh, stream->strf.vids, + stream->extradata, stream->initdata, &codec_name); + tag_name = GST_TAG_VIDEO_CODEC; avi->num_v_streams++; break; - } case GST_RIFF_FCC_auds: - { - char *codec_name = NULL; - padname = g_strdup_printf ("audio_%02d", avi->num_a_streams); templ = gst_element_class_get_pad_template (klass, "audio_%02d"); - caps = - gst_riff_create_audio_caps_with_data (strf.auds->format, strh, - strf.auds, extradata, initdata, &codec_name); - if (codec_name) { - gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_AUDIO_CODEC, - codec_name, NULL); - have_tag = TRUE; - g_free (codec_name); - } - blockalign = strf.auds->blockalign; - bitrate = strf.auds->av_bps; - g_free (strf.auds); + caps = gst_riff_create_audio_caps (stream->strf.auds->format, + stream->strh, stream->strf.auds, + stream->extradata, stream->initdata, &codec_name); + tag_name = GST_TAG_AUDIO_CODEC; avi->num_a_streams++; break; - } case GST_RIFF_FCC_iavs: - { - char *codec_name = NULL; - padname = g_strdup_printf ("video_%02d", avi->num_v_streams); templ = gst_element_class_get_pad_template (klass, "video_%02d"); - caps = gst_riff_create_iavs_caps (strh->fcc_handler, strh, strf.iavs, - &codec_name); - if (codec_name) { - gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC, - codec_name, NULL); - have_tag = TRUE; - g_free (codec_name); - } - g_free (strf.iavs); + caps = gst_riff_create_iavs_caps (stream->strh->fcc_handler, + stream->strh, stream->strf.iavs, + stream->extradata, stream->initdata, &codec_name); + tag_name = GST_TAG_VIDEO_CODEC; avi->num_v_streams++; break; - } default: - g_assert (0); + g_assert_not_reached (); + } + + /* no caps means no stream */ + if (!caps) { + GST_ERROR_OBJECT (element, "Did not find caps for stream %s", padname); + goto fail; } /* set proper settings and add it */ - pad = gst_pad_new_from_template (templ, padname); + pad = stream->pad = gst_pad_new_from_template (templ, padname); g_free (padname); + gst_pad_use_fixed_caps (pad); +#if 0 gst_pad_set_formats_function (pad, gst_avi_demux_get_src_formats); gst_pad_set_event_mask_function (pad, gst_avi_demux_get_event_mask); +#endif gst_pad_set_event_function (pad, gst_avi_demux_handle_src_event); gst_pad_set_query_type_function (pad, gst_avi_demux_get_src_query_types); gst_pad_set_query_function (pad, gst_avi_demux_handle_src_query); +#if 0 gst_pad_set_convert_function (pad, gst_avi_demux_src_convert); - gst_pad_set_getcaps_function (pad, gst_avi_demux_src_getcaps); +#endif - stream = &avi->stream[avi->num_streams]; - stream->caps = caps ? caps : gst_caps_new_empty (); - stream->pad = pad; - stream->strh = strh; stream->num = avi->num_streams; - stream->delay = 0LL; - stream->total_bytes = 0LL; + stream->total_bytes = 0; stream->total_frames = 0; stream->current_frame = 0; stream->current_byte = 0; stream->current_entry = -1; - stream->skip = 0; - stream->blockalign = blockalign; - stream->bitrate = bitrate; - stream->width = width; - stream->height = height; - stream->indexes = locations; gst_pad_set_element_private (pad, stream); avi->num_streams++; - - /* auto-negotiates */ + gst_pad_set_active (pad, TRUE); + gst_pad_set_caps (pad, caps); gst_element_add_pad (GST_ELEMENT (avi), pad); - - /* clean something up */ - if (initdata) - gst_buffer_unref (initdata); - if (extradata) - gst_buffer_unref (extradata); - - if (have_tag) { - GstEvent *event = gst_event_new_tag (list); - - gst_element_found_tags (GST_ELEMENT (avi), list); - gst_pad_push (pad, GST_DATA (event)); - } else { - gst_tag_list_free (list); - } + GST_LOG_OBJECT (element, "Added pad %s", gst_pad_get_name (pad)); return TRUE; -skip_stream: - while (TRUE) { - if (!(tag = gst_riff_peek_tag (riff, &avi->level_up))) - return FALSE; - if (avi->level_up) { - avi->level_up--; - break; - } - if (!gst_riff_read_skip (riff)) - return FALSE; - } - - /* add a "NULL" stream */ +fail: + /* unref any mem that may be in use */ + if (buf) + gst_buffer_unref (buf); + if (sub) + gst_buffer_unref (sub); + g_free (stream->strh); + g_free (stream->strf.data); + g_free (stream->name); + g_free (stream->indexes); + if (stream->initdata) + gst_buffer_unref (stream->initdata); + if (stream->extradata) + gst_buffer_unref (stream->extradata); + memset (stream, 0, sizeof (avi_stream_context)); avi->num_streams++; - return TRUE; /* recoverable */ + return FALSE; } -/* - * Read an openDML-2.0 extension header. +/** + * gst_avi_demux_parse_odml: + * @element: calling element (used for debug/error). + * @buf: input buffer to be used for parsing. + * + * Read an openDML-2.0 extension header. Fills in the frame number + * in the avi demuxer object when reading succeeds. */ -static gboolean -gst_avi_demux_stream_odml (GstAviDemux * avi) +static void +gst_avi_demux_parse_odml (GstElement * element, GstBuffer * buf) { - GstRiffRead *riff = GST_RIFF_READ (avi); - guint32 tag; - - /* read contents */ - while (TRUE) { - if (!(tag = gst_riff_peek_tag (riff, &avi->level_up))) - return FALSE; - else if (avi->level_up) { - avi->level_up--; - break; - } + GstAviDemux *avi = GST_AVI_DEMUX (element); + guint32 tag = 0; + guint offset = 4; + GstBuffer *sub = NULL; + while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) { switch (tag) { case GST_RIFF_TAG_dmlh:{ gst_riff_dmlh dmlh, *_dmlh; - GstBuffer *buf; - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_dmlh)) { - g_warning ("DMLH entry is too small (%d bytes, %d needed)", + if (GST_BUFFER_SIZE (sub) < sizeof (gst_riff_dmlh)) { + GST_ERROR_OBJECT (element, + "DMLH entry is too small (%d bytes, %d needed)", GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_dmlh)); - gst_buffer_unref (buf); + gst_buffer_unref (sub); break; } _dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (buf); dmlh.totalframes = GUINT32_FROM_LE (_dmlh->totalframes); - GST_INFO ("dmlh tag found:"); - GST_INFO (" totalframes: %u", dmlh.totalframes); + GST_INFO_OBJECT (element, "dmlh tag found:"); + GST_INFO_OBJECT (element, " totalframes: %u", dmlh.totalframes); - avi->num_frames = dmlh.totalframes; - gst_buffer_unref (buf); + avi->avih->tot_frames = dmlh.totalframes; + gst_buffer_unref (sub); break; } default: - GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header", + GST_WARNING_OBJECT (element, + "Unknown tag " GST_FOURCC_FORMAT " in ODML header", GST_FOURCC_ARGS (tag)); /* fall-through */ - case GST_RIFF_TAG_JUNK: - if (!gst_riff_read_skip (riff)) - return FALSE; + gst_buffer_unref (sub); break; } - - if (avi->level_up) { - avi->level_up--; - break; - } } - return TRUE; + gst_buffer_unref (buf); } -/* - * Seek to index, read it, seek back. - * Return value indicates if we can continue processing. It - * does not indicate if index-reading succeeded. +/** + * gst_avi_demux_parse_index: + * @element: calling element (used for debugging/errors). + * @buf: buffer containing the full index. + * @entries_list: list (returned by this function) containing the index + * entries parsed from the buffer. The first in the list + * is also a pointer to the allocated data and should be + * free'ed at some point. + * + * Read index entries from the provided buffer. */ -static gboolean -gst_avi_demux_stream_index (GstAviDemux * avi, - GList ** index, GList ** alloc_list) +static void +gst_avi_demux_parse_index (GstElement * element, + GstBuffer * buf, GList ** _entries_list) { - GList *list = NULL; - GstBuffer *buf = NULL; - guint i; - GstEvent *event; - GstRiffRead *riff = GST_RIFF_READ (avi); - guint64 pos_before, pos_after, length; - guint32 tag; - guint index_size; - gst_avi_index_entry *index_entries = NULL; - gboolean first = TRUE; + GstAviDemux *avi = GST_AVI_DEMUX (element); + guint64 pos_before = avi->offset; + gst_avi_index_entry *entries = NULL; + guint8 *data; + GList *entries_list = NULL; + guint i, num, n; - /* first, we need to know the current position (to seek back - * when we're done) and the total length of the file. */ - length = gst_bytestream_length (riff->bs); - pos_before = gst_bytestream_tell (riff->bs); - - /* skip movi - */ - if (pos_before + 8 > length) { - return TRUE; - } else { - guint8 *data; - - if (gst_bytestream_peek_bytes (riff->bs, &data, 8) == 8) { - guint len = GST_READ_UINT32_LE (&data[4]); - - if (pos_before + 8 + len >= length) { - GST_WARNING ("No index avail"); - return TRUE; - } - } - } - /* hmm... */ - if (!gst_riff_read_skip (riff)) - return FALSE; - - /* assure that we've got data left */ - pos_after = gst_bytestream_tell (riff->bs); - if (pos_after + 8 > length) { - GST_WARNING ("File said that it has an index, but there is no index data!"); - goto end; + if (!buf) { + *_entries_list = NULL; + return; } - /* assure that it's an index */ - if (!(tag = gst_riff_peek_tag (riff, NULL))) - return FALSE; - if (tag != GST_RIFF_TAG_idx1) { - g_warning ("No index after data, but " GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (tag)); - goto end; - } + data = GST_BUFFER_DATA (buf); + num = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry); + entries = g_new (gst_avi_index_entry, num); - /* read index */ - if (!gst_riff_read_data (riff, &tag, &buf)) - return FALSE; - - /* parse all entries */ - index_size = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry); - index_entries = g_malloc (index_size * sizeof (gst_avi_index_entry)); - GST_INFO ("%u index entries", avi->index_size); - - for (i = 0; i < index_size; i++) { + for (i = 0, n = 0; i < num; i++) { gst_riff_index_entry entry, *_entry; avi_stream_context *stream; gint stream_nr; gst_avi_index_entry *target; GstFormat format; - _entry = &((gst_riff_index_entry *) GST_BUFFER_DATA (buf))[i]; + _entry = &((gst_riff_index_entry *) data)[i]; entry.id = GUINT32_FROM_LE (_entry->id); entry.offset = GUINT32_FROM_LE (_entry->offset); entry.flags = GUINT32_FROM_LE (_entry->flags); entry.size = GUINT32_FROM_LE (_entry->size); - target = &index_entries[i]; + target = &entries[n]; - /* Don't index reclists. ID = 0 and offset = 0 (where this is not the - * first chunk) indicate broken indexes. Rebuild from there. */ if (entry.id == GST_RIFF_rec || entry.id == 0 || - (entry.offset == 0 && i > 0)) + (entry.offset == 0 && n > 0)) continue; stream_nr = CHUNKID_TO_STREAMNR (entry.id); if (stream_nr >= avi->num_streams || stream_nr < 0) { - GST_WARNING ("Index entry %d has invalid stream nr %d", i, stream_nr); - target->stream_nr = -1; + GST_WARNING_OBJECT (element, + "Index entry %d has invalid stream nr %d", i, stream_nr); continue; } target->stream_nr = stream_nr; @@ -1291,12 +1182,11 @@ gst_avi_demux_stream_index (GstAviDemux * avi, target->offset = entry.offset + 8; /* figure out if the index is 0 based or relative to the MOVI start */ - if (first) { + if (n == 0) { if (target->offset < pos_before) avi->index_offset = pos_before + 8; else avi->index_offset = 0; - first = FALSE; } target->bytes_before = stream->total_bytes; @@ -1310,15 +1200,15 @@ gst_avi_demux_stream_index (GstAviDemux * avi, if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) { /* constant rate stream */ - gst_pad_convert (stream->pad, GST_FORMAT_BYTES, + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, stream->total_bytes, &format, &target->ts); - gst_pad_convert (stream->pad, GST_FORMAT_BYTES, + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, stream->total_bytes + target->size, &format, &target->dur); } else { /* VBR stream */ - gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, stream->total_frames, &format, &target->ts); - gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, stream->total_frames + 1, &format, &target->dur); } target->dur -= target->ts; @@ -1326,15 +1216,68 @@ gst_avi_demux_stream_index (GstAviDemux * avi, stream->total_bytes += target->size; stream->total_frames++; - GST_DEBUG_OBJECT (avi, - "Adding index entry %d (%d) for stream %d of size %u " + GST_DEBUG ("Adding index entry %d (%d) for stream %d of size %u " "at offset %" G_GUINT64_FORMAT " and time %" GST_TIME_FORMAT, target->index_nr, stream->total_frames - 1, target->stream_nr, target->size, target->offset, GST_TIME_ARGS (target->ts)); - list = g_list_prepend (list, target); + entries_list = g_list_prepend (entries_list, target); + + n++; } + *_entries_list = g_list_reverse (entries_list); + gst_buffer_unref (buf); +} + +/** + * gst_avi_demux_stream_index: + * @avi: avi demuxer object. + * @index: list of index entries, returned by this function. + * @alloc_list: list of allocated data, returned by this function. + * + * Seeks to index and reads it. + */ + +static void +gst_avi_demux_stream_index (GstAviDemux * avi, + GList ** index, GList ** alloc_list) +{ + guint64 offset = avi->offset; + GstBuffer *buf; + guint32 tag; + gint i; + + *alloc_list = NULL; + *index = NULL; + + /* get position */ + if (gst_pad_pull_range (avi->sinkpad, offset, 8, &buf) != GST_FLOW_OK) + return; + else if (!buf || GST_BUFFER_SIZE (buf) < 8) { + if (buf) + gst_buffer_unref (buf); + return; + } + offset += 8 + GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); + gst_buffer_unref (buf); + + /* get size */ + if (gst_riff_read_chunk (GST_ELEMENT (avi), + avi->sinkpad, &offset, &tag, &buf) != GST_FLOW_OK) + return; + else if (tag != GST_RIFF_TAG_idx1) { + GST_ERROR_OBJECT (avi, + "No index data after movi chunk, but " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (tag)); + gst_buffer_unref (buf); + return; + } + + gst_avi_demux_parse_index (GST_ELEMENT (avi), buf, index); + if (*index) + *alloc_list = g_list_append (*alloc_list, (*index)->data); + /* debug our indexes */ for (i = 0; i < avi->num_streams; i++) { avi_stream_context *stream; @@ -1344,26 +1287,11 @@ gst_avi_demux_stream_index (GstAviDemux * avi, i, stream->total_frames, stream->total_bytes); } -end: - if (buf) - gst_buffer_unref (buf); - - /* seek back to the data */ - if (!(event = gst_riff_read_seek (riff, pos_before))) { - g_free (index_entries); - g_list_free (list); - return FALSE; - } - gst_event_unref (event); - - if (list) - *index = g_list_reverse (list); - if (index_entries) - *alloc_list = g_list_prepend (*alloc_list, index_entries); - - return TRUE; + return; } +#if 0 + /* * Sync to next data chunk. */ @@ -1583,15 +1511,15 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, /* timestamps */ if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) { /* constant rate stream */ - gst_pad_convert (stream->pad, GST_FORMAT_BYTES, + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, stream->total_bytes, &format, &entry->ts); - gst_pad_convert (stream->pad, GST_FORMAT_BYTES, + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, stream->total_bytes + entry->size, &format, &entry->dur); } else { /* VBR stream */ - gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, stream->total_frames, &format, &entry->ts); - gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, stream->total_frames + 1, &format, &entry->dur); } entry->dur -= entry->ts; @@ -1626,6 +1554,7 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, return TRUE; } +#endif /* * Massage index. @@ -1637,7 +1566,7 @@ gst_avi_demux_stream_scan (GstAviDemux * avi, * order. The end result should be a smoother playing AVI. */ -static gint G_GNUC_UNUSED +static gint sort (gst_avi_index_entry * a, gst_avi_index_entry * b) { if (a->ts > b->ts) @@ -1662,32 +1591,29 @@ gst_avi_demux_massage_index (GstAviDemux * avi, /* init frames */ for (i = 0; i < avi->num_streams; i++) { GstFormat fmt = GST_FORMAT_TIME; + guint64 delay = 0; stream = &avi->stream[i]; if (stream->strh->type == GST_RIFF_FCC_vids) { - if (!gst_pad_convert (stream->pad, - GST_FORMAT_DEFAULT, stream->strh->init_frames, - &fmt, &stream->delay)) { - stream->delay = 0; + if (!gst_avi_demux_src_convert (stream->pad, + GST_FORMAT_DEFAULT, stream->strh->init_frames, &fmt, &delay)) { + delay = 0; } } else { - if (!gst_pad_convert (stream->pad, - GST_FORMAT_DEFAULT, stream->strh->init_frames, - &fmt, &stream->delay)) { - stream->delay = 0; + if (!gst_avi_demux_src_convert (stream->pad, + GST_FORMAT_DEFAULT, stream->strh->init_frames, &fmt, &delay)) { + delay = 0; } } GST_LOG ("Adding init_time=%" GST_TIME_FORMAT " to stream %d", - GST_TIME_ARGS (stream->delay), i); - } - for (one = list; one != NULL; one = one->next) { - entry = one->data; + GST_TIME_ARGS (delay), i); - if (entry->stream_nr >= avi->num_streams) - continue; + for (one = list; one != NULL; one = one->next) { + entry = one->data; - stream = &avi->stream[entry->stream_nr]; - entry->ts += stream->delay; + if (entry->stream_nr == i) + entry->ts += delay; + } } GST_LOG ("I'm now going to cut large chunks into smaller pieces"); @@ -1709,7 +1635,7 @@ gst_avi_demux_massage_index (GstAviDemux * avi, * the allocation of index entries could be improved. */ stream = &avi->stream[entry->stream_nr]; if (entry->dur > MAX_DURATION && stream->strh->type == GST_RIFF_FCC_auds) { - guint32 ideal_size = stream->bitrate / 10; + guint32 ideal_size = stream->strf.auds->av_bps / 10; gst_avi_index_entry *entries; gint old_size, num_added; GList *one2; @@ -1749,7 +1675,7 @@ gst_avi_demux_massage_index (GstAviDemux * avi, entry2->size = old_size; } - entry2->dur = GST_SECOND * entry2->size / stream->bitrate; + entry2->dur = GST_SECOND * entry2->size / stream->strf.auds->av_bps; if (i != 0) { entry2->index_nr++; entry2->ts += entry->dur; @@ -1795,141 +1721,149 @@ gst_avi_demux_massage_index (GstAviDemux * avi, * Read full AVI headers. */ -gboolean +static GstFlowReturn gst_avi_demux_stream_header (GstAviDemux * avi) { - GstRiffRead *riff = GST_RIFF_READ (avi); - guint32 tag, flags, streams; + GstFlowReturn res; + GstBuffer *buf, *sub = NULL; + guint32 tag; GList *index = NULL, *alloc = NULL; + guint offset = 4; /* the header consists of a 'hdrl' LIST tag */ - if (!(tag = gst_riff_peek_tag (riff, NULL))) - return FALSE; - if (tag != GST_RIFF_TAG_LIST) { + if ((res = gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad, + &avi->offset, &tag, &buf)) != GST_FLOW_OK) + return res; + else if (tag != GST_RIFF_TAG_LIST) { GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("Invalid AVI header (no LIST at start): " GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag))); - return FALSE; - } - if (!gst_riff_read_list (riff, &tag)) - return FALSE; - if (tag != GST_RIFF_LIST_hdrl) { + return GST_FLOW_ERROR; + } else if (GST_BUFFER_SIZE (buf) < 4 || + GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl) { GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("Invalid AVI header (no hdrl at start): " GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag))); - return FALSE; + gst_buffer_unref (buf); + return GST_FLOW_ERROR; } /* the hdrl starts with a 'avih' header */ - if (!(tag = gst_riff_peek_tag (riff, NULL))) - return FALSE; - if (tag != GST_RIFF_TAG_avih) { + if (!gst_riff_parse_chunk (GST_ELEMENT (avi), buf, &offset, &tag, &sub) || + tag != GST_RIFF_TAG_avih) { GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("Invalid AVI header (no avih at start): " GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag))); - return FALSE; + if (sub) + gst_buffer_unref (sub); + gst_buffer_unref (buf); + return GST_FLOW_ERROR; + } else if (!gst_avi_demux_parse_avih (GST_ELEMENT (avi), sub, &avi->avih)) { + gst_buffer_unref (buf); + return GST_FLOW_ERROR; } - if (!gst_avi_demux_stream_avih (avi, &flags, &streams)) - return FALSE; /* now, read the elements from the header until the end */ - while (TRUE) { - if (!(tag = gst_riff_peek_tag (riff, &avi->level_up))) - return FALSE; - else if (avi->level_up) { - avi->level_up--; - break; - } - + while (gst_riff_parse_chunk (GST_ELEMENT (avi), buf, &offset, &tag, &sub)) { switch (tag) { case GST_RIFF_TAG_LIST: - if (!(tag = gst_riff_peek_list (riff))) - return FALSE; - - switch (tag) { - case GST_RIFF_LIST_strl: - if (!gst_riff_read_list (riff, &tag) || - !gst_avi_demux_add_stream (avi)) - return FALSE; - break; - - case GST_RIFF_LIST_odml: - if (!gst_riff_read_list (riff, &tag) || - !gst_avi_demux_stream_odml (avi)) - return FALSE; - break; - - default: - GST_WARNING ("Unknown list " GST_FOURCC_FORMAT " in AVI header", - GST_FOURCC_ARGS (tag)); - /* fall-through */ - - case GST_RIFF_TAG_JUNK: - if (!gst_riff_read_skip (riff)) - return FALSE; - break; + if (!sub || GST_BUFFER_SIZE (sub) < 4) { + if (sub) + gst_buffer_unref (sub); + break; } + switch (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub))) { + case GST_RIFF_LIST_strl: + gst_avi_demux_parse_stream (GST_ELEMENT (avi), sub); + break; + case GST_RIFF_LIST_odml: + gst_avi_demux_parse_odml (GST_ELEMENT (avi), sub); + break; + default: + GST_WARNING_OBJECT (avi, + "Unknown list " GST_FOURCC_FORMAT " in AVI header", + GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub)))); + /* fall-through */ + case GST_RIFF_TAG_JUNK: + gst_buffer_unref (sub); + break; + } break; - default: - GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header", - GST_FOURCC_ARGS (tag)); + GST_WARNING_OBJECT (avi, + "Unknown off %d tag " GST_FOURCC_FORMAT " in AVI header", + offset, GST_FOURCC_ARGS (tag)); /* fall-through */ - case GST_RIFF_TAG_JUNK: - if (!gst_riff_read_skip (riff)) - return FALSE; + gst_buffer_unref (sub); break; } - - if (avi->level_up) { - avi->level_up--; - break; - } } + gst_buffer_unref (buf); - if (avi->num_streams != streams) { - g_warning ("Stream header mentioned %d streams, but %d available", - streams, avi->num_streams); + /* check parsed streams */ + if (avi->num_streams == 0) { + GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found")); + return GST_FLOW_ERROR; + } else if (avi->num_streams != avi->avih->streams) { + GST_WARNING_OBJECT (avi, + "Stream header mentioned %d streams, but %d available", + avi->avih->streams, avi->num_streams); } /* Now, find the data (i.e. skip all junk between header and data) */ - while (1) { - if (!(tag = gst_riff_peek_tag (riff, NULL))) - return FALSE; - if (tag != GST_RIFF_TAG_LIST) { - if (!gst_riff_read_skip (riff)) - return FALSE; - continue; - } - if (!(tag = gst_riff_peek_list (riff))) - return FALSE; - if (tag != GST_RIFF_LIST_movi) { - if (tag == GST_RIFF_LIST_INFO) { - if (!gst_riff_read_list (riff, &tag) || !gst_riff_read_info (riff)) - return FALSE; - } else if (!gst_riff_read_skip (riff)) { - return FALSE; + do { + guint size; + guint32 tag, ltag; + + if ((res = gst_pad_pull_range (avi->sinkpad, avi->offset, + 12, &buf)) != GST_FLOW_OK) + return res; + else if (!buf || GST_BUFFER_SIZE (buf) < 12) + return GST_FLOW_ERROR; + + tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)); + size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); + ltag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 8); + gst_buffer_unref (buf); + + if (tag == GST_RIFF_TAG_LIST) { + switch (ltag) { + case GST_RIFF_LIST_movi: + goto done; + case GST_RIFF_LIST_INFO: + if ((res = gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad, + &avi->offset, &tag, &buf)) != GST_FLOW_OK) + return res; + else { + GstTagList *t; + + sub = gst_buffer_create_sub (buf, 4, GST_BUFFER_SIZE (buf) - 4); + gst_riff_parse_info (GST_ELEMENT (avi), sub, &t); + if (t) + gst_tag_list_free (t); + gst_buffer_unref (buf); + } + default: + avi->offset += 8 + ((size + 1) & ~1); + break; } - continue; + } else { + avi->offset += 8 + ((size + 1) & ~1); } - break; - } + } while (1); +done: /* create or read stream index (for seeking) */ if (avi->stream[0].indexes != NULL) { - if (!gst_avi_demux_read_subindexes (avi, &index, &alloc)) - return FALSE; + gst_avi_demux_read_subindexes (avi, &index, &alloc); } if (!index) { - if (flags & GST_RIFF_AVIH_HASINDEX) { - if (!gst_avi_demux_stream_index (avi, &index, &alloc)) { - g_list_foreach (alloc, (GFunc) g_free, NULL); - g_list_free (alloc); - return FALSE; - } + if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX) { + gst_avi_demux_stream_index (avi, &index, &alloc); } +#if 0 /* some indexes are incomplete, continue streaming from there */ if (!index || avi->stream[0].total_frames < avi->num_frames) { if (!gst_avi_demux_stream_scan (avi, &index, &alloc)) { @@ -1938,6 +1872,7 @@ gst_avi_demux_stream_header (GstAviDemux * avi) return FALSE; } } +#endif } if (index) { gst_avi_demux_massage_index (avi, index, alloc); @@ -1945,6 +1880,10 @@ gst_avi_demux_stream_header (GstAviDemux * avi) g_list_free (index); g_list_foreach (alloc, (GFunc) g_free, NULL); g_list_free (alloc); + + GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL), + ("Indexless reading todo")); + return GST_FLOW_ERROR; } /* at this point we know all the streams and we can signal the no more @@ -1952,7 +1891,7 @@ gst_avi_demux_stream_header (GstAviDemux * avi) GST_DEBUG_OBJECT (avi, "signaling no more pads"); gst_element_no_more_pads (GST_ELEMENT (avi)); - return TRUE; + return GST_FLOW_OK; } /* @@ -1963,81 +1902,60 @@ static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi) { guint i; - GstEvent *event; /* FIXME: if we seek in an openDML file, we will have multiple * primary levels. Seeking in between those will cause havoc. */ GST_LOG ("Seeking to entry %d", avi->seek_entry); - avi->current_entry = avi->seek_entry; for (i = 0; i < avi->num_streams; i++) { avi_stream_context *stream = &avi->stream[i]; - if (GST_PAD_IS_USABLE (stream->pad)) { - if (avi->seek_flush) { - event = gst_event_new (GST_EVENT_FLUSH); - gst_pad_push (stream->pad, GST_DATA (event)); - } - event = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, - avi->last_seek, NULL); - gst_pad_push (stream->pad, GST_DATA (event)); - } + gst_pad_push_event (stream->pad, gst_event_new_flush (FALSE)); } + GST_STREAM_LOCK (avi->sinkpad); + + avi->current_entry = avi->seek_entry; + avi->seek_event = gst_event_new_discontinuous (1.0, + GST_FORMAT_TIME, avi->last_seek, + (gint64) (((gfloat) avi->stream[0].strh->scale) * + avi->stream[0].strh->length / + avi->stream[0].strh->rate) * GST_SECOND, NULL); + + for (i = 0; i < avi->num_streams; i++) { + avi_stream_context *stream = &avi->stream[i]; + + gst_pad_push_event (stream->pad, gst_event_new_flush (TRUE)); + } + gst_pad_start_task (avi->sinkpad, (GstTaskFunction) gst_avi_demux_loop, + avi->sinkpad); + + GST_STREAM_UNLOCK (avi->sinkpad); + return TRUE; } -/* - * Invert DIB buffers... Takes existing buffer and - * returns either the buffer or a new one (with old - * one dereferenced). - */ - -static inline void -swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes) -{ - memcpy (tmp, d1, bytes); - memcpy (d1, d2, bytes); - memcpy (d2, tmp, bytes); -} - -static GstBuffer * -gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf) -{ - gint y, h = stream->height, w = stream->width; - guint8 *tmp = g_malloc (w); - - buf = gst_buffer_copy_on_write (buf); - - for (y = 0; y < h / 2; y++) { - swap_line (GST_BUFFER_DATA (buf) + w * y, - GST_BUFFER_DATA (buf) + w * (h - 1 - y), tmp, w); - } - - g_free (tmp); - - return buf; -} - -static gboolean +static GstFlowReturn gst_avi_demux_process_next_entry (GstAviDemux * avi) { - GstRiffRead *riff = GST_RIFF_READ (avi); + GstFlowReturn res; gboolean processed = FALSE; do { if (avi->current_entry >= avi->index_size) { - gst_bytestream_seek (riff->bs, 0, GST_SEEK_METHOD_END); + gint n; - /* get eos */ - GST_LOG ("Handled last index entry, setting EOS"); - gst_riff_peek_tag (GST_RIFF_READ (avi), &avi->level_up); - gst_pad_event_default (avi->sinkpad, gst_event_new (GST_EVENT_EOS)); - processed = TRUE; + GST_LOG_OBJECT (avi, "Handled last index entry, setting EOS (%d > %d)", + avi->current_entry, avi->index_size); + for (n = 0; n < avi->num_streams; n++) { + if (avi->stream[n].pad) + gst_pad_push_event (avi->stream[n].pad, + gst_event_new (GST_EVENT_EOS)); + } + return GST_FLOW_WRONG_STATE; } else { GstBuffer *buf; - guint got; gst_avi_index_entry *entry = &avi->index_entries[avi->current_entry++]; avi_stream_context *stream; @@ -2046,82 +1964,68 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi) "Entry has non-existing stream nr %d", entry->stream_nr); continue; } - stream = &avi->stream[entry->stream_nr]; + if (entry->size == 0 || !stream->pad) { + GST_DEBUG_OBJECT (avi, "Skipping entry %d (%d, %p)", + avi->current_entry - 1, entry->size, stream->pad); + goto next; + } else if (GST_CLOCK_TIME_IS_VALID (avi->last_seek)) { + if (stream->strh->type != GST_RIFF_FCC_vids && + entry->ts < avi->last_seek) { + GST_DEBUG_OBJECT (avi, "Doing keyframe sync"); + goto next; + } else if (entry->ts >= avi->last_seek) { + avi->last_seek = GST_CLOCK_TIME_NONE; + } + } - if (GST_PAD_IS_USABLE (stream->pad) && entry->size > 0) { - guint64 needed_off = entry->offset + avi->index_offset, pos; - guint32 remain; - - pos = gst_bytestream_tell (riff->bs); - gst_bytestream_get_status (riff->bs, &remain, NULL); - if (pos <= needed_off && needed_off - pos <= remain) { - gst_bytestream_flush_fast (riff->bs, needed_off - pos); - } else { - GstEvent *event; - - event = gst_riff_read_seek (riff, needed_off); - if (event) - gst_event_unref (event); - else { - GST_ELEMENT_ERROR (avi, RESOURCE, READ, (NULL), (NULL)); - return FALSE; - } - } - if (!(buf = gst_riff_read_element_data (riff, entry->size, &got))) { - GST_ERROR ("Failed to read %d bytes of data", entry->size); - return FALSE; - } - if (stream->strh->fcc_handler == GST_MAKE_FOURCC ('D', 'I', 'B', ' ')) { - buf = gst_avi_demux_invert (stream, buf); - } - if (entry->flags & GST_RIFF_IF_KEYFRAME) { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_KEY_UNIT); - } + if ((res = gst_pad_pull_range (avi->sinkpad, entry->offset + + avi->index_offset, entry->size, &buf)) != GST_FLOW_OK) + return res; + else { + if (!(entry->flags & GST_RIFF_IF_KEYFRAME)) + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); GST_BUFFER_TIMESTAMP (buf) = entry->ts; GST_BUFFER_DURATION (buf) = entry->dur; + gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad)); GST_DEBUG_OBJECT (avi, "Processing buffer of size %d and time %" GST_TIME_FORMAT " on pad %s", GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), gst_pad_get_name (stream->pad)); - gst_pad_push (stream->pad, GST_DATA (buf)); - processed = TRUE; - } else { - GST_DEBUG_OBJECT (avi, - "Unusable pad or zero chunksize, skipping entry"); + if ((res = gst_pad_push (stream->pad, buf)) != GST_FLOW_OK) + return res; processed = TRUE; } + next: stream->current_frame = entry->frames_before + 1; stream->current_byte = entry->bytes_before + entry->size; } } while (!processed); - return TRUE; + return GST_FLOW_OK; } /* * Read data. */ -gboolean +static GstFlowReturn gst_avi_demux_stream_data (GstAviDemux * avi) { - GstRiffRead *riff = GST_RIFF_READ (avi); - guint32 tag; - guint stream_nr; - +#if 0 if (avi->seek_offset != (guint64) - 1) { if (!gst_avi_demux_handle_seek (avi)) return FALSE; avi->seek_offset = (guint64) - 1; } +#endif /* if we have a avi->index_entries[], we don't want to read * the stream linearly, but seek to the next ts/index_entry. */ - if (avi->index_entries != NULL) { - return gst_avi_demux_process_next_entry (avi); - } - + //if (avi->index_entries != NULL) { + return gst_avi_demux_process_next_entry (avi); + //} +#if 0 if (!gst_avi_demux_sync (avi, &tag, FALSE)) return FALSE; stream_nr = CHUNKID_TO_STREAMNR (tag); @@ -2165,10 +2069,6 @@ gst_avi_demux_stream_data (GstAviDemux * avi) } else { GstClockTime dur_ts; - if (stream->strh->fcc_handler == GST_MAKE_FOURCC ('D', 'I', 'B', ' ')) { - buf = gst_avi_demux_invert (stream, buf); - } - GST_BUFFER_TIMESTAMP (buf) = next_ts; gst_pad_query (stream->pad, GST_QUERY_POSITION, &format, &dur_ts); GST_BUFFER_DURATION (buf) = dur_ts - next_ts; @@ -2181,34 +2081,78 @@ gst_avi_demux_stream_data (GstAviDemux * avi) } return TRUE; +#endif } static void -gst_avi_demux_loop (GstElement * element) +gst_avi_demux_loop (GstPad * pad) { - GstAviDemux *avi = GST_AVI_DEMUX (element); + GstFlowReturn res; + GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (avi->sinkpad); switch (avi->state) { case GST_AVI_DEMUX_START: - if (!gst_avi_demux_stream_init (avi)) - return; + if ((res = gst_avi_demux_stream_init (avi)) != GST_FLOW_OK) + goto pause; avi->state = GST_AVI_DEMUX_HEADER; /* fall-through */ - case GST_AVI_DEMUX_HEADER: - if (!gst_avi_demux_stream_header (avi)) - return; + if ((res = gst_avi_demux_stream_header (avi)) != GST_FLOW_OK) + goto pause; avi->state = GST_AVI_DEMUX_MOVI; break; - case GST_AVI_DEMUX_MOVI: - if (!gst_avi_demux_stream_data (avi)) - return; - break; + if (avi->seek_event) { + gint i; + for (i = 0; i < avi->num_streams; i++) { + avi_stream_context *stream = &avi->stream[i]; + + gst_pad_push_event (stream->pad, gst_event_ref (avi->seek_event)); + } + gst_event_unref (avi->seek_event); + avi->seek_event = NULL; + } + + if ((res = gst_avi_demux_stream_data (avi)) != GST_FLOW_OK) + goto pause; + break; default: - g_assert (0); + g_assert_not_reached (); } + + GST_STREAM_UNLOCK (avi->sinkpad); + + return; + +pause: + GST_LOG_OBJECT (avi, "pausing task"); + gst_pad_pause_task (avi->sinkpad); + GST_STREAM_UNLOCK (pad); +} + +static gboolean +gst_avi_demux_sink_activate (GstPad * sinkpad) +{ + if (gst_pad_check_pull_range (sinkpad)) + return gst_pad_activate_pull (sinkpad, TRUE); + + return FALSE; +} + +static gboolean +gst_avi_demux_sink_activate_pull (GstPad * sinkpad, gboolean active) +{ + if (active) { + /* if we have a scheduler we can start the task */ + gst_pad_start_task (sinkpad, (GstTaskFunction) gst_avi_demux_loop, sinkpad); + } else { + gst_pad_stop_task (sinkpad); + } + + return TRUE; } static GstElementStateReturn diff --git a/gst/avi/gstavidemux.h b/gst/avi/gstavidemux.h index c5db25816a..e99ba63785 100644 --- a/gst/avi/gstavidemux.h +++ b/gst/avi/gstavidemux.h @@ -22,6 +22,7 @@ #include +#include "avi-ids.h" #include "gst/riff/riff-ids.h" #include "gst/riff/riff-read.h" @@ -61,25 +62,25 @@ typedef struct { /* pad, strh */ GstPad *pad; - GstCaps *caps; gst_riff_strh *strh; - gint blockalign, bitrate; - gint width, height; + union { + gst_riff_strf_vids *vids; + gst_riff_strf_auds *auds; + gst_riff_strf_iavs *iavs; + gpointer data; + } strf; + GstBuffer *extradata, *initdata; + gchar *name; /* current position (byte, frame, time) */ guint current_frame; guint64 current_byte; gint current_entry; - /* delay in time (init_frames) */ - guint64 delay; - /* stream length */ guint64 total_bytes; guint32 total_frames; - guint32 skip; - guint64 *indexes; } avi_stream_context; @@ -90,14 +91,14 @@ typedef enum { } GstAviDemuxState; typedef struct _GstAviDemux { - GstRiffRead parent; + GstElement parent; /* pads */ GstPad *sinkpad; /* AVI decoding state */ GstAviDemuxState state; - guint level_up; + guint64 offset; /* index */ gst_avi_index_entry *index_entries; @@ -112,18 +113,18 @@ typedef struct _GstAviDemux { avi_stream_context stream[GST_AVI_DEMUX_MAX_STREAMS]; /* some stream info for length */ - guint32 us_per_frame; - guint32 num_frames; + gst_riff_avih *avih; /* seeking */ guint64 seek_offset; guint64 last_seek; gint seek_entry; gboolean seek_flush; + GstEvent *seek_event; } GstAviDemux; typedef struct _GstAviDemuxClass { - GstRiffReadClass parent_class; + GstElementClass parent_class; } GstAviDemuxClass; GType gst_avi_demux_get_type (void);