? .new.gstavimux Index: Makefile.am =================================================================== RCS file: /cvs/gstreamer/gst-plugins-good/gst/avi/Makefile.am,v retrieving revision 1.26 diff -u -u -r1.26 Makefile.am --- Makefile.am 23 Sep 2005 04:22:56 -0000 1.26 +++ Makefile.am 23 Apr 2006 23:15:08 -0000 @@ -2,6 +2,7 @@ libgstavi_la_SOURCES = \ gstavi.c \ + gstavimux.c \ gstavidemux.c noinst_HEADERS = \ @@ -13,8 +14,9 @@ -I$(top_srcdir)/gst-libs libgstavi_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) \ + $(GST_BASE_LIBS) \ $(GST_LIBS) \ -lgstriff-@GST_MAJORMINOR@ libgstavi_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -EXTRA_DIST = README gstavimux.c +EXTRA_DIST = README Index: gstavi.c =================================================================== RCS file: /cvs/gstreamer/gst-plugins-good/gst/avi/gstavi.c,v retrieving revision 1.7 diff -u -u -r1.7 gstavi.c --- gstavi.c 14 Nov 2005 02:13:29 -0000 1.7 +++ gstavi.c 23 Apr 2006 23:15:08 -0000 @@ -26,7 +26,7 @@ #include "gst/gst-i18n-plugin.h" #include "gstavidemux.h" -/*#include "gstavimux.h"*/ +#include "gstavimux.h" static gboolean plugin_init (GstPlugin * plugin) @@ -38,9 +38,9 @@ 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, Index: gstavidemux.c =================================================================== RCS file: /cvs/gstreamer/gst-plugins-good/gst/avi/gstavidemux.c,v retrieving revision 1.177 retrieving revision 1.164 diff -u -u -r1.177 -r1.164 --- gstavidemux.c 21 Apr 2006 18:07:10 -0000 1.177 +++ gstavidemux.c 6 Feb 2006 12:18:45 -0000 1.164 @@ -144,7 +144,9 @@ static void gst_avi_demux_init (GstAviDemux * avi) { - avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink"); + 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); @@ -299,13 +301,11 @@ case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: - *dest_value = - gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps, - GST_SECOND); + *dest_value = src_value * stream->strf.auds->av_bps / GST_SECOND; break; case GST_FORMAT_DEFAULT: - *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate, - stream->strh->scale * GST_SECOND); + *dest_value = src_value * stream->strh->rate / + (stream->strh->scale * GST_SECOND); break; default: res = FALSE; @@ -316,8 +316,8 @@ switch (*dest_format) { case GST_FORMAT_TIME: if (stream->strf.auds->av_bps != 0) { - *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, - stream->strf.auds->av_bps); + *dest_value = ((gfloat) src_value) * GST_SECOND / + stream->strf.auds->av_bps; } else res = FALSE; break; @@ -329,9 +329,8 @@ case GST_FORMAT_DEFAULT: switch (*dest_format) { case GST_FORMAT_TIME: - *dest_value = - gst_util_uint64_scale (src_value, - stream->strh->scale * GST_SECOND, stream->strh->rate); + *dest_value = ((((gfloat) src_value) * stream->strh->scale) / + stream->strh->rate) * GST_SECOND; break; default: res = FALSE; @@ -378,30 +377,27 @@ if (stream->strh->type == GST_RIFF_FCC_auds) { if (!stream->strh->samplesize) { - pos = gst_util_uint64_scale_int ((gint64) stream->current_frame * - stream->strh->scale, GST_SECOND, stream->strh->rate); + pos = GST_SECOND * stream->current_frame * + stream->strh->scale / stream->strh->rate; } else if (stream->strf.auds->av_bps != 0) { - pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND, - stream->strf.auds->av_bps); + 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 = gst_util_uint64_scale_int (xlen, stream->current_frame, - stream->total_frames); + pos = xlen * stream->current_frame / stream->total_frames; else - pos = gst_util_uint64_scale_int (xlen, stream->current_byte, - stream->total_bytes); + pos = xlen * stream->current_byte / stream->total_bytes; } else { res = FALSE; } } else { if (stream->strh->rate != 0) { - pos = - gst_util_uint64_scale_int ((guint64) stream->current_frame * - stream->strh->scale, GST_SECOND, stream->strh->rate); + pos = ((gfloat) stream->current_frame * stream->strh->scale * + GST_SECOND / stream->strh->rate); } else { pos = stream->current_frame * demux->avih->us_frame * GST_USECOND; } @@ -412,25 +408,11 @@ } case GST_QUERY_DURATION: { - if (stream->strh->type != GST_RIFF_FCC_auds && - stream->strh->type != GST_RIFF_FCC_vids) { - res = FALSE; - break; - } - - /* use duration from the index if we have an - * index instead of trusting the stream header */ - if (GST_CLOCK_TIME_IS_VALID (stream->idx_duration)) { - gst_query_set_duration (query, GST_FORMAT_TIME, stream->idx_duration); - } else { - gint64 len; - - len = - gst_util_uint64_scale ((guint64) stream->strh->length * - stream->strh->scale, GST_SECOND, stream->strh->rate); + gint64 len; - gst_query_set_duration (query, GST_FORMAT_TIME, len); - } + len = (((gfloat) stream->strh->scale) * stream->strh->length / + stream->strh->rate) * GST_SECOND; + gst_query_set_duration (query, GST_FORMAT_TIME, len); break; } default: @@ -473,8 +455,6 @@ switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { - /* FIXME, this seeking code is not correct, look at wavparse for - * a better example */ GstFormat format; GstSeekFlags flags; gdouble rate; @@ -504,10 +484,8 @@ tstop = stop; } - if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) { - res = FALSE; - goto done; - } + duration = (((gfloat) stream->strh->scale) * stream->strh->length / + stream->strh->rate) * GST_SECOND; switch (start_type) { case GST_SEEK_TYPE_CUR: @@ -559,7 +537,6 @@ done: gst_event_unref (event); - GST_DEBUG_OBJECT (avi, "returning %d", res); return res; } @@ -583,19 +560,14 @@ if (!gst_riff_parse_file_header (element, buf, &doctype)) return FALSE; - if (doctype != GST_RIFF_RIFF_AVI) - goto not_avi; - - return TRUE; - - /* ERRORS */ -not_avi: - { + if (doctype != GST_RIFF_RIFF_AVI) { 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; } static GstFlowReturn @@ -608,19 +580,11 @@ avi->offset, 12, &buf)) != GST_FLOW_OK) return res; else if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), buf)) - goto wrong_header; + return GST_FLOW_ERROR; avi->offset += 12; return GST_FLOW_OK; - - /* ERRORS */ -wrong_header: - { - GST_DEBUG_OBJECT (avi, "error parsing file header"); - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } } /** @@ -643,11 +607,14 @@ { gst_riff_avih *avih; - if (buf == NULL) - goto no_buffer; - - if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih)) - goto avih_too_small; + 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 = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); @@ -689,21 +656,6 @@ gst_buffer_unref (buf); return TRUE; - - /* ERRORS */ -no_buffer: - { - GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL), ("No buffer")); - return FALSE; - } -avih_too_small: - { - GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL), - ("Too small avih (%d available, %d needed)", - GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih))); - gst_buffer_unref (buf); - return FALSE; - } } /** @@ -724,19 +676,20 @@ gst_avi_demux_parse_superindex (GstElement * element, GstBuffer * buf, guint64 ** _indexes) { - guint8 *data; + guint8 *data = GST_BUFFER_DATA (buf); gint bpe = 16, num, i; guint64 *indexes; *_indexes = NULL; - if (buf == NULL) - goto no_buffer; - - if (GST_BUFFER_SIZE (buf) < 24) - goto too_small; - - data = GST_BUFFER_DATA (buf); + 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; + } /* check type of index. The opendml2 specs state that * there should be 4 dwords per array entry. Type can be @@ -763,21 +716,6 @@ gst_buffer_unref (buf); return TRUE; - - /* ERRORS */ -no_buffer: - { - GST_ERROR_OBJECT (element, "No buffer"); - return FALSE; - } -too_small: - { - GST_ERROR_OBJECT (element, - "Not enough data to parse superindex (%d available, %d needed)", - GST_BUFFER_SIZE (buf), 24); - gst_buffer_unref (buf); - return FALSE; - } } /** @@ -811,15 +749,23 @@ gint64 tmp; /* check size */ - if (buf == NULL) - goto no_buffer; - - if (GST_BUFFER_SIZE (buf) < 24) - goto too_small; + 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) - goto not_implemented; + 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 @@ -892,29 +838,6 @@ } return TRUE; - - /* ERRORS */ -no_buffer: - { - GST_ERROR_OBJECT (element, "No buffer"); - return TRUE; /* continue */ - } -too_small: - { - GST_ERROR_OBJECT (element, - "Not enough data to parse subindex (%d available, %d needed)", - GST_BUFFER_SIZE (buf), 24); - gst_buffer_unref (buf); - *_entries_list = NULL; - return TRUE; /* continue */ - } -not_implemented: - { - GST_ELEMENT_ERROR (element, STREAM, NOT_IMPLEMENTED, (NULL), - ("Subindex-is-data is not implemented")); - gst_buffer_unref (buf); - return FALSE; - } } static void @@ -1033,7 +956,6 @@ /* read strd/strn */ while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) { - /* sub can be NULL if the chunk is empty */ switch (tag) { case GST_RIFF_TAG_strd: if (stream->initdata) @@ -1042,15 +964,11 @@ break; case GST_RIFF_TAG_strn: g_free (stream->name); - if (sub != NULL) { - stream->name = g_new (gchar, GST_BUFFER_SIZE (sub) + 1); - memcpy (stream->name, GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub)); - stream->name[GST_BUFFER_SIZE (sub)] = '\0'; - gst_buffer_unref (sub); - sub = NULL; - } else { - stream->name = g_strdup (""); - } + stream->name = g_new (gchar, GST_BUFFER_SIZE (sub) + 1); + memcpy (stream->name, GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub)); + stream->name[GST_BUFFER_SIZE (sub)] = '\0'; + gst_buffer_unref (sub); + sub = NULL; break; default: if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') || @@ -1065,10 +983,8 @@ GST_FOURCC_ARGS (tag)); /* fall-through */ case GST_RIFF_TAG_JUNK: - if (sub != NULL) { - gst_buffer_unref (sub); - sub = NULL; - } + gst_buffer_unref (sub); + sub = NULL; break; } } @@ -1137,16 +1053,19 @@ gst_object_unref (stream->pad); pad = stream->pad = gst_pad_new_from_template (templ, padname); stream->last_flow = GST_FLOW_OK; - stream->idx_duration = GST_CLOCK_TIME_NONE; 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); +#endif stream->num = avi->num_streams; stream->total_bytes = 0; @@ -1172,27 +1091,24 @@ return TRUE; - /* ERRORS */ 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++; + /* 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 FALSE; - } + return FALSE; } /** @@ -1361,10 +1277,9 @@ stream->total_bytes += target->size; stream->total_frames++; - GST_DEBUG_OBJECT (avi, - "Adding index entry %d (%d) flags %08x 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->flags, + target->index_nr, stream->total_frames - 1, target->stream_nr, target->size, target->offset, GST_TIME_ARGS (target->ts)); entries_list = g_list_prepend (entries_list, target); @@ -1412,8 +1327,13 @@ if (gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad, &offset, &tag, &buf) != GST_FLOW_OK) return; - else if (tag != GST_RIFF_TAG_idx1) - goto no_index; + 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) @@ -1429,15 +1349,6 @@ } return; - -no_index: - { - GST_ERROR_OBJECT (avi, - "No index data after movi chunk, but %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (tag)); - gst_buffer_unref (buf); - return; - } } #if 0 @@ -1605,10 +1516,9 @@ *size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); gst_buffer_unref (buf); - GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %" - G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag), - *size, offset + 8, offset + 8 + (gint64) * size); - + GST_LOG_OBJECT (avi, + "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %lld -- %lld", + GST_FOURCC_ARGS (*tag), *size, offset, offset + (guint64) * size); beach: return res; } @@ -1632,7 +1542,9 @@ if ((res = gst_avi_demux_peek_tag (avi, off, tag, &siz)) != GST_FLOW_OK) break; if (*tag == GST_RIFF_TAG_LIST) - off += 12; + off += 8; + else if (*tag == GST_RIFF_LIST_movi) + off += 4; else { *offset = off + 8; *size = siz; @@ -1662,6 +1574,7 @@ guint64 length; gint64 tmplength; guint32 tag; + GstPad *peer; GList *list = NULL; guint index_size = 0; @@ -1672,10 +1585,12 @@ GST_LOG_OBJECT (avi, "Creating index %s existing index", (*index) ? "with" : "without"); - if (!gst_pad_query_peer_duration (avi->sinkpad, &format, &tmplength)) + if (!(peer = gst_pad_get_peer (avi->sinkpad))) + return FALSE; + if (!(gst_pad_query_duration (peer, &format, &tmplength))) return FALSE; - length = tmplength; + gst_object_unref (peer); if (*index) { entry = g_list_last (*index)->data; @@ -1684,9 +1599,10 @@ pos++; if (pos < length) { - GST_LOG_OBJECT (avi, "Incomplete index, seeking to last valid entry @ %" + GST_LOG ("Incomplete index, seeking to last valid entry @ %" G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT " (%" G_GUINT64_FORMAT "+%u)", pos, length, entry->offset, entry->size); + } else { return TRUE; } @@ -1702,59 +1618,59 @@ &size)) != GST_FLOW_OK) break; stream_nr = CHUNKID_TO_STREAMNR (tag); - if (stream_nr >= 0 && stream_nr < avi->num_streams) { - stream = &avi->stream[stream_nr]; + if (stream_nr < 0 || stream_nr >= avi->num_streams) + continue; + stream = &avi->stream[stream_nr]; - /* pre-allocate */ - if (index_size % 1024 == 0) { - entries = g_new (gst_avi_index_entry, 1024); - *alloc_list = g_list_prepend (*alloc_list, entries); - } - entry = &entries[index_size % 1024]; - - entry->index_nr = index_size++; - entry->stream_nr = stream_nr; - entry->flags = GST_RIFF_IF_KEYFRAME; - entry->offset = pos - avi->index_offset; - entry->size = size; - - /* timestamps */ - if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) { - format = GST_FORMAT_TIME; - /* constant rate stream */ - gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, - stream->total_bytes, &format, &tmpts); - entry->ts = tmpts; - gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, - stream->total_bytes + entry->size, &format, &tmpdur); - entry->dur = tmpdur; - } else { - format = GST_FORMAT_TIME; - /* VBR stream */ - gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, - stream->total_frames, &format, &tmpts); - entry->ts = tmpts; - gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, - stream->total_frames + 1, &format, &tmpdur); - entry->dur = tmpdur; - } - 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_DEBUG_OBJECT (avi, "Added index entry %d (in stream: %d), offset %" - G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT " for stream %d", - index_size - 1, entry->frames_before, entry->offset, - GST_TIME_ARGS (entry->ts), entry->stream_nr); + /* pre-allocate */ + if (index_size % 1024 == 0) { + entries = g_new (gst_avi_index_entry, 1024); + *alloc_list = g_list_prepend (*alloc_list, entries); + } + entry = &entries[index_size % 1024]; + + entry->index_nr = index_size++; + entry->stream_nr = stream_nr; + entry->flags = GST_RIFF_IF_KEYFRAME; + entry->offset = pos - avi->index_offset; + entry->size = size; + + /* timestamps */ + if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) { + format = GST_FORMAT_TIME; + /* constant rate stream */ + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, + stream->total_bytes, &format, &tmpts); + entry->ts = tmpts; + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES, + stream->total_bytes + entry->size, &format, &tmpdur); + entry->dur = tmpdur; + } else { + format = GST_FORMAT_TIME; + /* VBR stream */ + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, + stream->total_frames, &format, &tmpts); + entry->ts = tmpts; + gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT, + stream->total_frames + 1, &format, &tmpdur); + entry->dur = tmpdur; } + 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_DEBUG_OBJECT (avi, "Added index entry %d (in stream: %d), offset %" + G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT " for stream %d", + index_size - 1, entry->frames_before, entry->offset, + GST_TIME_ARGS (entry->ts), entry->stream_nr); /* update position */ - pos += GST_ROUND_UP_2 (size); + pos += ((size + 1) & ~1); if (pos > length) { GST_WARNING_OBJECT (avi, "Stopping index lookup since we are further than EOF"); @@ -1858,33 +1774,29 @@ { gst_avi_index_entry *entry; avi_stream_context *stream; - guint32 avih_init_frames; gint i; GList *one; - GST_LOG_OBJECT (avi, "Starting index massage"); - - avih_init_frames = avi->avih->init_frames; + GST_LOG ("Starting index massage"); /* init frames */ for (i = 0; i < avi->num_streams; i++) { GstFormat fmt = GST_FORMAT_TIME; gint64 delay = 0; - guint32 init_frames; stream = &avi->stream[i]; - - init_frames = stream->strh->init_frames; - - if (init_frames >= avih_init_frames) - init_frames -= avih_init_frames; - - if (!gst_avi_demux_src_convert (stream->pad, - GST_FORMAT_DEFAULT, init_frames, &fmt, &delay)) { - delay = 0; + if (stream->strh->type == GST_RIFF_FCC_vids) { + if (!gst_avi_demux_src_convert (stream->pad, + GST_FORMAT_DEFAULT, stream->strh->init_frames, &fmt, &delay)) { + delay = 0; + } + } else { + if (!gst_avi_demux_src_convert (stream->pad, + GST_FORMAT_DEFAULT, stream->strh->init_frames, &fmt, &delay)) { + delay = 0; + } } - - GST_LOG_OBJECT (avi, "Adding init_time=%" GST_TIME_FORMAT " to stream %d", + GST_LOG ("Adding init_time=%" GST_TIME_FORMAT " to stream %d", GST_TIME_ARGS (delay), i); for (one = list; one != NULL; one = one->next) { @@ -1895,7 +1807,7 @@ } } - GST_LOG_OBJECT (avi, "I'm now going to cut large chunks into smaller pieces"); + GST_LOG ("I'm now going to cut large chunks into smaller pieces"); /* cut chunks in small (seekable) pieces */ for (i = 0; i < avi->num_streams; i++) { @@ -1914,18 +1826,11 @@ * 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; + guint32 ideal_size = stream->strf.auds->av_bps / 10; gst_avi_index_entry *entries; gint old_size, num_added; GList *one2; - /* cut in 1/10th of a second */ - ideal_size = stream->strf.auds->av_bps / 10; - - /* ensure chunk size is multiple of blockalign */ - if (stream->strf.auds->blockalign > 1) - ideal_size -= ideal_size % stream->strf.auds->blockalign; - /* copy index */ old_size = entry->size; num_added = (entry->size - 1) / ideal_size; @@ -1974,12 +1879,12 @@ } } - GST_LOG_OBJECT (avi, "I'm now going to reorder the index entries for time"); + GST_LOG ("I'm now going to reorder the index entries for time"); /* re-order for time */ list = g_list_sort (list, (GCompareFunc) sort); - GST_LOG_OBJECT (avi, "Filling in index array"); + GST_LOG ("Filling in index array"); avi->index_size = g_list_length (list); avi->index_entries = g_new (gst_avi_index_entry, avi->index_size); @@ -1989,40 +1894,18 @@ avi->index_entries[i].index_nr = i; } - GST_LOG_OBJECT (avi, "Freeing original index list"); + GST_LOG ("Freeing original index list"); g_list_foreach (alloc_list, (GFunc) g_free, NULL); g_list_free (alloc_list); g_list_free (list); for (i = 0; i < avi->num_streams; i++) { - GST_LOG_OBJECT (avi, "Stream %d, %d frames, %" G_GUINT64_FORMAT " bytes", i, + GST_LOG ("Stream %d, %d frames, %" G_GUINT64_FORMAT " bytes", i, avi->stream[i].total_frames, avi->stream[i].total_bytes); } - GST_LOG_OBJECT (avi, "Index massaging done"); -} - -static void -gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi) -{ - gst_avi_index_entry *entry; - GstClockTime end_time = GST_CLOCK_TIME_NONE; - gint stream, i; - - /* we assume all streams start at a timestamp of 0 for now */ - for (stream = 0; stream < avi->num_streams; ++stream) { - i = 0; - while ((entry = gst_avi_demux_index_next (avi, stream, i))) { - end_time = entry->ts + entry->dur; - ++i; - } - if (GST_CLOCK_TIME_IS_VALID (end_time)) { - avi->stream[stream].idx_duration = end_time; - GST_INFO ("Stream %d duration according to index: %" GST_TIME_FORMAT, - stream, GST_TIME_ARGS (avi->stream[stream].idx_duration)); - } - } + GST_LOG ("Index massaging done"); } static gboolean @@ -2057,45 +1940,46 @@ 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) - goto no_list; - else if (GST_BUFFER_SIZE (buf) < 4) - goto no_header; - - /* Find the 'hdrl' LIST tag */ - while (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl) { - GST_LOG_OBJECT (avi, "buffer contains %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)))); - - /* Eat up */ + 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 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))); gst_buffer_unref (buf); - 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) - goto no_list; - else if (GST_BUFFER_SIZE (buf) < 4) - goto no_header; + return GST_FLOW_ERROR; } /* the hdrl starts with a 'avih' header */ if (!gst_riff_parse_chunk (GST_ELEMENT (avi), buf, &offset, &tag, &sub) || - tag != GST_RIFF_TAG_avih) - goto no_avih; - else if (!gst_avi_demux_parse_avih (GST_ELEMENT (avi), sub, &avi->avih)) - goto invalid_avih; + 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))); + if (sub) { + gst_buffer_unref (sub); + sub = NULL; + } + 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; + } /* now, read the elements from the header until the end */ while (gst_riff_parse_chunk (GST_ELEMENT (avi), buf, &offset, &tag, &sub)) { - /* sub can be NULL on empty tags */ - if (!sub) - continue; - switch (tag) { case GST_RIFF_TAG_LIST: - if (GST_BUFFER_SIZE (sub) < 4) { - gst_buffer_unref (sub); - sub = NULL; + if (!sub || GST_BUFFER_SIZE (sub) < 4) { + if (sub) { + gst_buffer_unref (sub); + sub = NULL; + } break; } @@ -2114,8 +1998,10 @@ GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub)))); /* fall-through */ case GST_RIFF_TAG_JUNK: - gst_buffer_unref (sub); - sub = NULL; + if (sub) { + gst_buffer_unref (sub); + sub = NULL; + } break; } break; @@ -2125,17 +2011,20 @@ offset, GST_FOURCC_ARGS (tag)); /* fall-through */ case GST_RIFF_TAG_JUNK: - gst_buffer_unref (sub); - sub = NULL; + if (sub) { + gst_buffer_unref (sub); + sub = NULL; + } break; } } gst_buffer_unref (buf); /* check parsed streams */ - if (avi->num_streams == 0) - goto no_streams; - else if (avi->num_streams != avi->avih->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); @@ -2206,20 +2095,22 @@ gst_avi_demux_stream_scan (avi, &index, &alloc); } - if (!index) - goto no_index; + if (index) { + gst_avi_demux_massage_index (avi, index, alloc); + } else { + g_list_free (index); + g_list_foreach (alloc, (GFunc) g_free, NULL); + g_list_free (alloc); - gst_avi_demux_massage_index (avi, index, alloc); - gst_avi_demux_calculate_durations_from_index (avi); + GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL), + ("Could not get/create index")); + return GST_FLOW_ERROR; + } /* send initial discont */ avi->segment_start = 0; - avi->segment_stop = - gst_util_uint64_scale_int ((gint64) avi->stream[0].strh->scale * - avi->stream[0].strh->length, GST_SECOND, avi->stream[0].strh->rate); - - GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, avi->segment_stop); - + avi->segment_stop = (gint64) (((gfloat) avi->stream[0].strh->scale) * + avi->stream[0].strh->length / avi->stream[0].strh->rate) * GST_SECOND; avi->seek_event = gst_event_new_new_segment (FALSE, avi->segment_rate, GST_FORMAT_TIME, avi->segment_start, avi->segment_stop, avi->segment_start); @@ -2230,62 +2121,6 @@ gst_element_no_more_pads (GST_ELEMENT (avi)); return GST_FLOW_OK; - - /* ERRORS */ -no_list: - { - GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), - ("Invalid AVI header (no LIST at start): %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag))); - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } -no_header: - { - GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), - ("Invalid AVI header (no hdrl at start): %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag))); - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } -no_avih: - { - GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), - ("Invalid AVI header (no avih at start): %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag))); - if (sub) { - gst_buffer_unref (sub); - sub = NULL; - } - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } -invalid_avih: - { - GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), - ("Invalid AVI header (cannot parse avih at start)")); - if (sub) { - gst_buffer_unref (sub); - sub = NULL; - } - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } -no_streams: - { - GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found")); - return GST_FLOW_ERROR; - } -no_index: - { - g_list_free (index); - g_list_foreach (alloc, (GFunc) g_free, NULL); - g_list_free (alloc); - - GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL), - ("Could not get/create index")); - return GST_FLOW_ERROR; - } } /* @@ -2334,7 +2169,7 @@ } } - GST_DEBUG_OBJECT (avi, "seek: %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT + GST_DEBUG ("seek: %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " keyframe seeking:%d update:%d", GST_TIME_ARGS (avi->segment_start), GST_TIME_ARGS (avi->segment_stop), keyframe, update); @@ -2385,30 +2220,20 @@ static GstBuffer * gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf) { - GstStructure *s; - gint y, h = stream->strf.vids->height; - gint bpp, stride; + gint y, h = stream->strf.vids->height, w = stream->strf.vids->width; guint8 *tmp = NULL; - s = gst_caps_get_structure (GST_PAD_CAPS (stream->pad), 0); - if (!gst_structure_get_int (s, "bpp", &bpp)) { - GST_WARNING ("Failed to retrieve depth from caps"); - return buf; - } - - stride = stream->strf.vids->width * (bpp / 8); - buf = gst_buffer_make_writable (buf); - if (GST_BUFFER_SIZE (buf) < (stride * h)) { - GST_WARNING ("Buffer is smaller than reported Width x Height x Depth"); + if (GST_BUFFER_SIZE (buf) < (w * h)) { + GST_WARNING ("Buffer is smaller than reported Width x Height"); return buf; } - tmp = g_malloc (stride); + tmp = g_malloc (w); for (y = 0; y < h / 2; y++) { - swap_line (GST_BUFFER_DATA (buf) + stride * y, - GST_BUFFER_DATA (buf) + stride * (h - 1 - y), tmp, stride); + swap_line (GST_BUFFER_DATA (buf) + w * y, + GST_BUFFER_DATA (buf) + w * (h - 1 - y), tmp, w); } g_free (tmp); @@ -2443,7 +2268,14 @@ if (avi->current_entry >= avi->index_size) { GST_LOG_OBJECT (avi, "Handled last index entry, setting EOS (%d > %d)", avi->current_entry, avi->index_size); - goto eos; + if (avi->segment_flags & GST_SEEK_FLAG_SEGMENT) + gst_element_post_message + (GST_ELEMENT (avi), + gst_message_new_segment_done (GST_OBJECT (avi), GST_FORMAT_TIME, + avi->segment_stop)); + else + gst_avi_demux_send_event (avi, gst_event_new_eos ()); + return GST_FLOW_WRONG_STATE; } else { GstBuffer *buf; gst_avi_index_entry *entry = &avi->index_entries[avi->current_entry++]; @@ -2455,16 +2287,6 @@ continue; } - if ((entry->flags & GST_RIFF_IF_KEYFRAME) - && GST_CLOCK_TIME_IS_VALID (entry->ts) - && GST_CLOCK_TIME_IS_VALID (avi->segment_stop) - && (entry->ts > avi->segment_stop)) { - GST_LOG_OBJECT (avi, "Found keyframe after segment," - " setting EOS (%" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ")", - GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (avi->segment_stop)); - goto eos; - } - stream = &avi->stream[entry->stream_nr]; if (entry->size == 0 || !stream->pad) { @@ -2474,38 +2296,30 @@ } if ((res = gst_pad_pull_range (avi->sinkpad, entry->offset + - avi->index_offset, entry->size, &buf)) != GST_FLOW_OK) { - return res; - } - - if (GST_BUFFER_SIZE (buf) < entry->size) { - GST_WARNING_OBJECT (avi, "Short read at offset %" G_GUINT64_FORMAT - ", only got %d/%d bytes (truncated file?)", entry->offset + - avi->index_offset, GST_BUFFER_SIZE (buf), entry->size); - goto eos; - } - - 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_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_NAME (stream->pad)); - res = gst_pad_push (stream->pad, buf); - stream->last_flow = res; - if (res != GST_FLOW_OK && res != GST_FLOW_NOT_LINKED) { - GST_DEBUG_OBJECT (avi, "Flow on pad %s: %s", - GST_PAD_NAME (stream->pad), gst_flow_get_name (res)); + avi->index_offset, entry->size, &buf)) != GST_FLOW_OK) return res; + else { + 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_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_NAME (stream->pad)); + res = gst_pad_push (stream->pad, buf); + stream->last_flow = res; + if (res != GST_FLOW_OK && res != GST_FLOW_NOT_LINKED) { + GST_DEBUG_OBJECT (avi, "Flow on pad %s: %s", + GST_PAD_NAME (stream->pad), gst_flow_get_name (res)); + return res; + } + processed = TRUE; } - processed = TRUE; - next: stream->current_frame = entry->frames_before + 1; stream->current_byte = entry->bytes_before + entry->size; @@ -2513,17 +2327,6 @@ } while (!processed); return GST_FLOW_OK; - -eos: - /* handle end-of-stream/segment */ - if (avi->segment_flags & GST_SEEK_FLAG_SEGMENT) - gst_element_post_message - (GST_ELEMENT (avi), - gst_message_new_segment_done (GST_OBJECT (avi), GST_FORMAT_TIME, - avi->segment_stop)); - else - gst_avi_demux_send_event (avi, gst_event_new_eos ()); - return GST_FLOW_WRONG_STATE; } /* @@ -2681,7 +2484,6 @@ static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element, GstStateChange transition) { - GstStateChangeReturn ret; GstAviDemux *avi = GST_AVI_DEMUX (element); switch (transition) { @@ -2691,10 +2493,13 @@ break; } + if (GST_ELEMENT_CLASS (parent_class)->change_state) { + GstStateChangeReturn ret; - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - goto done; + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret != GST_STATE_CHANGE_SUCCESS) + return ret; + } switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: @@ -2704,6 +2509,5 @@ break; } -done: - return ret; + return GST_STATE_CHANGE_SUCCESS; } Index: gstavidemux.h =================================================================== RCS file: /cvs/gstreamer/gst-plugins-good/gst/avi/gstavidemux.h,v retrieving revision 1.32 retrieving revision 1.31 diff -u -u -r1.32 -r1.31 --- gstavidemux.h 3 Mar 2006 17:51:16 -0000 1.32 +++ gstavidemux.h 4 Feb 2006 15:41:43 -0000 1.31 @@ -82,9 +82,6 @@ guint64 total_bytes; guint32 total_frames; - /* stream length according to index */ - GstClockTime idx_duration; - guint64 *indexes; } avi_stream_context; Index: gstavimux.c =================================================================== RCS file: /cvs/gstreamer/gst-plugins-good/gst/avi/gstavimux.c,v retrieving revision 1.79 diff -u -u -r1.79 gstavimux.c --- gstavimux.c 8 Apr 2006 21:21:43 -0000 1.79 +++ gstavimux.c 23 Apr 2006 23:15:08 -0000 @@ -1,5 +1,6 @@ /* AVI muxer plugin for GStreamer * Copyright (C) 2002 Ronald Bultje + * (C) 2006 Mark Nauwelaerts * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -54,9 +55,14 @@ enum { ARG_0, - ARG_BIGFILE + ARG_BIGFILE, + ARG_FORCEFOURCC }; +GST_DEBUG_CATEGORY_STATIC (avimux_debug); +#define GST_CAT_DEFAULT avimux_debug + + static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, @@ -145,8 +151,12 @@ static void gst_avimux_base_init (gpointer g_class); static void gst_avimux_class_init (GstAviMuxClass * klass); static void gst_avimux_init (GstAviMux * avimux); +static void gst_avimux_finalize (GObject * object); +#if 0 static void gst_avimux_loop (GstElement * element); +#endif +static GstFlowReturn gst_avimux_collect_pads (GstCollectPads * pads, GstAviMux * avimux); static gboolean gst_avimux_handle_event (GstPad * pad, GstEvent * event); static GstPad *gst_avimux_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * name); @@ -198,7 +208,7 @@ { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); static GstElementDetails gst_avimux_details = - GST_ELEMENT_DETAILS ("Avi muxer", + GST_ELEMENT_DETAILS ("Avi multiplexer", "Codec/Muxer", "Muxes audio and video into an avi stream", "Ronald Bultje "); @@ -222,21 +232,31 @@ gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; - parent_class = g_type_class_peek_parent (klass); + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + GST_DEBUG_CATEGORY_INIT (avimux_debug, "avimux", + 0, "Muxer for AVI streams"); + + gobject_class->finalize = gst_avimux_finalize; + + gobject_class->get_property = gst_avimux_get_property; + gobject_class->set_property = gst_avimux_set_property; g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIGFILE, g_param_spec_boolean ("bigfile", "Bigfile Support", "Support for openDML-2.0 (big) AVI files", 0, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FORCEFOURCC, + g_param_spec_string ("forcefourcc", "Force fourcc", + "Force fourcc string on the AVI header", NULL, G_PARAM_READWRITE)); + gstelement_class->request_new_pad = gst_avimux_request_new_pad; gstelement_class->release_pad = gst_avimux_release_pad; gstelement_class->change_state = gst_avimux_change_state; - - gstelement_class->get_property = gst_avimux_get_property; - gstelement_class->set_property = gst_avimux_set_property; } +#if 0 static const GstEventMask * gst_avimux_get_event_masks (GstPad * pad) { @@ -247,6 +267,7 @@ return gst_avimux_sink_event_masks; } +#endif static void gst_avimux_init (GstAviMux * avimux) @@ -258,11 +279,9 @@ "src"), "src"); gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad); - GST_OBJECT_FLAG_SET (GST_ELEMENT (avimux), GST_ELEMENT_EVENT_AWARE); - - avimux->audiosinkpad = NULL; + avimux->audiocollectdata = NULL; avimux->audio_pad_connected = FALSE; - avimux->videosinkpad = NULL; + avimux->videocollectdata = NULL; avimux->video_pad_connected = FALSE; avimux->audio_buffer_queue = NULL; @@ -278,11 +297,12 @@ memset (&(avimux->auds), 0, sizeof (gst_riff_strf_auds)); avimux->vids_hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's'); avimux->vids_hdr.rate = 1; - avimux->avi_hdr.max_bps = 10000000; + avimux->avi_hdr.max_bps = 0; avimux->auds_hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's'); avimux->vids_hdr.quality = 0xFFFFFFFF; avimux->auds_hdr.quality = 0xFFFFFFFF; avimux->tags = NULL; + avimux->tags_snap = NULL; avimux->idx = NULL; @@ -290,11 +310,23 @@ avimux->enable_large_avi = TRUE; - gst_element_set_loop_function (GST_ELEMENT (avimux), gst_avimux_loop); + avimux->collect = gst_collect_pads_new(); + gst_collect_pads_set_function (avimux->collect, + (GstCollectPadsFunction) (GST_DEBUG_FUNCPTR (gst_avimux_collect_pads)), avimux); } -static GstPadLinkReturn -gst_avimux_vidsinkconnect (GstPad * pad, const GstCaps * vscaps) +static void +gst_avimux_finalize (GObject * object) +{ + GstAviMux *mux = GST_AVIMUX (object); + + gst_object_unref (mux->collect); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_avimux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps) { GstAviMux *avimux; GstStructure *structure; @@ -302,9 +334,9 @@ const GValue *fps; gboolean ret; - avimux = GST_AVIMUX (gst_pad_get_parent (pad)); + avimux = GST_AVIMUX (GST_PAD_PARENT (pad)); - GST_DEBUG ("avimux: video sinkconnect triggered on %s", + GST_DEBUG ("avimux: video set_caps triggered on %s", gst_pad_get_name (pad)); structure = gst_caps_get_structure (vscaps, 0); @@ -317,10 +349,8 @@ ret &= gst_structure_get_int (structure, "height", &avimux->vids.height); fps = gst_structure_get_value (structure, "framerate"); ret &= (fps != NULL && GST_VALUE_HOLDS_FRACTION (fps)); - if (!ret) { - gst_object_unref (avimux); - return GST_PAD_LINK_REFUSED; - } + if (!ret) + return FALSE; // GST_PAD_LINK_REFUSED; avimux->vids_hdr.rate = gst_value_get_fraction_numerator (fps); avimux->vids_hdr.scale = gst_value_get_fraction_denominator (fps); @@ -389,9 +419,15 @@ avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G'); } + if (avimux->force_fourcc != NULL && strlen (avimux->force_fourcc) == 4) { + avimux->vids.compression = GST_MAKE_FOURCC (avimux->force_fourcc[0], + avimux->force_fourcc[1], + avimux->force_fourcc[2], + avimux->force_fourcc[3]); + } + if (!avimux->vids.compression) { - gst_object_unref (avimux); - return GST_PAD_LINK_DELAYED; + return FALSE; // GST_PAD_LINK_DELAYED; } } @@ -400,23 +436,20 @@ avimux->avi_hdr.width = avimux->vids.width; avimux->avi_hdr.height = avimux->vids.height; avimux->avi_hdr.us_frame = avimux->vids_hdr.scale; - - gst_object_unref (avimux); - - return GST_PAD_LINK_OK; + return TRUE; // GST_PAD_LINK_OK; } -static GstPadLinkReturn -gst_avimux_audsinkconnect (GstPad * pad, const GstCaps * vscaps) +static gboolean +gst_avimux_audsink_set_caps (GstPad * pad, GstCaps * vscaps) { GstAviMux *avimux; GstStructure *structure; const gchar *mimetype; int i; - avimux = GST_AVIMUX (gst_pad_get_parent (pad)); + avimux = GST_AVIMUX (GST_PAD_PARENT (pad)); - GST_DEBUG ("avimux: audio sinkconnect triggered on %s", + GST_DEBUG ("avimux: audio set_caps triggered on %s", gst_pad_get_name (pad)); structure = gst_caps_get_structure (vscaps, 0); @@ -469,29 +502,25 @@ avimux->auds.size = 16; if (!avimux->auds.format) { - gst_object_unref (avimux); - return GST_PAD_LINK_REFUSED; + return FALSE; // GST_PAD_LINK_REFUSED; } } avimux->auds_hdr.rate = avimux->auds.blockalign * avimux->auds.rate; avimux->auds_hdr.samplesize = avimux->auds.blockalign; avimux->auds_hdr.scale = 1; - - gst_object_unref (avimux); - - return GST_PAD_LINK_OK; + return TRUE; // GST_PAD_LINK_OK; } static void gst_avimux_pad_link (GstPad * pad, GstPad * peer, gpointer data) { GstAviMux *avimux = GST_AVIMUX (data); - const gchar *padname = gst_pad_get_name (pad); + const gchar *padname = GST_PAD_NAME (pad); - if (pad == avimux->audiosinkpad) { + if (avimux->audiocollectdata && pad == avimux->audiocollectdata->pad) { avimux->audio_pad_connected = TRUE; - } else if (pad == avimux->videosinkpad) { + } else if (avimux->videocollectdata && pad == avimux->videocollectdata->pad) { avimux->video_pad_connected = TRUE; } else { g_warning ("Unknown padname '%s'", padname); @@ -505,20 +534,23 @@ gst_avimux_pad_unlink (GstPad * pad, GstPad * peer, gpointer data) { GstAviMux *avimux = GST_AVIMUX (data); - const gchar *padname = gst_pad_get_name (pad); + const gchar *padname = GST_PAD_NAME (pad); - if (pad == avimux->audiosinkpad) { + if (avimux->audiocollectdata && pad == avimux->audiocollectdata->pad) { avimux->audio_pad_connected = FALSE; - } else if (pad == avimux->videosinkpad) { + } else if (avimux->videocollectdata && pad == avimux->videocollectdata->pad) { avimux->video_pad_connected = FALSE; } else { g_warning ("Unknown padname '%s'", padname); return; } - GST_DEBUG ("pad '%s' unlinked", padname); + gst_collect_pads_remove_pad (avimux->collect, pad); + GST_DEBUG ("pad '%s' unlinked and removed from collect", padname); } +/* TODO GstCollectPads will block if it has to manage a non-linked pad; + * best to upgrade it so it helps all muxers using it */ static GstPad * gst_avimux_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * req_name) @@ -539,27 +571,37 @@ avimux = GST_AVIMUX (element); if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) { - g_return_val_if_fail (avimux->audiosinkpad == NULL, NULL); + if (avimux->audiocollectdata) + return NULL; newpad = gst_pad_new_from_template (templ, "audio_00"); - gst_pad_set_link_function (newpad, gst_avimux_audsinkconnect); - avimux->audiosinkpad = newpad; + gst_pad_set_setcaps_function (newpad, gst_avimux_audsink_set_caps); + avimux->audiocollectdata = gst_collect_pads_add_pad (avimux->collect, + newpad, sizeof (GstCollectData)); } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) { - g_return_val_if_fail (avimux->videosinkpad == NULL, NULL); + if (avimux->videocollectdata) + return NULL; newpad = gst_pad_new_from_template (templ, "video_00"); - gst_pad_set_link_function (newpad, gst_avimux_vidsinkconnect); - avimux->videosinkpad = newpad; + gst_pad_set_setcaps_function (newpad, gst_avimux_vidsink_set_caps); + avimux->videocollectdata = gst_collect_pads_add_pad (avimux->collect, + newpad, sizeof (GstCollectData)); } else { g_warning ("avimux: this is not our template!\n"); return NULL; } + /* TODO hacked way to override/extend the event function of GstCollectPads; + * because it sets its own event function giving the element no access to events */ + avimux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); + gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_avimux_handle_event)); + g_signal_connect (newpad, "linked", G_CALLBACK (gst_avimux_pad_link), (gpointer) avimux); g_signal_connect (newpad, "unlinked", G_CALLBACK (gst_avimux_pad_unlink), (gpointer) avimux); gst_element_add_pad (element, newpad); +#if 0 gst_pad_set_event_mask_function (newpad, gst_avimux_get_event_masks); - +#endif return newpad; } @@ -568,22 +610,29 @@ { GstAviMux *avimux = GST_AVIMUX (element); - if (pad == avimux->videosinkpad) { - avimux->videosinkpad = NULL; - } else if (pad == avimux->audiosinkpad) { - avimux->audiosinkpad = NULL; + if (avimux->videocollectdata && pad == avimux->videocollectdata->pad) { + avimux->videocollectdata = NULL; + } else if (avimux->audiocollectdata && pad == avimux->audiocollectdata->pad) { + avimux->audiocollectdata = NULL; } else { g_warning ("Unknown pad %s", gst_pad_get_name (pad)); return; } GST_DEBUG ("Removed pad '%s'", gst_pad_get_name (pad)); + gst_collect_pads_remove_pad (avimux->collect, pad); gst_element_remove_pad (element, pad); } /* maybe some of these functions should be moved to riff.h? */ /* DISCLAIMER: this function is ugly. So be it (i.e. it makes the rest easier) */ +// so is this struct + +typedef struct _GstMarkedBuffer { + guint *highmark; + GstBuffer *buffer; +} GstMarkedBuffer; static void gst_avimux_write_tag (const GstTagList * list, const gchar * tag, gpointer data) @@ -604,8 +653,9 @@ 0, NULL} }; gint n, len, plen; - GstBuffer *buf = data; - guint8 *buffdata = GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf); + GstBuffer *buf = ((GstMarkedBuffer*) data)->buffer; + guint *highmark = ((GstMarkedBuffer*) data)->highmark; + guint8 *buffdata = GST_BUFFER_DATA (buf) + *highmark; gchar *str; for (n = 0; rifftags[n].fcc != 0; n++) { @@ -615,18 +665,21 @@ plen = len + 1; if (plen & 1) plen++; - if (GST_BUFFER_MAXSIZE (buf) >= GST_BUFFER_SIZE (buf) + 8 + plen) { + if (GST_BUFFER_SIZE (buf) >= *highmark + 8 + plen) { GST_WRITE_UINT32_LE (buffdata, rifftags[n].fcc); GST_WRITE_UINT32_LE (buffdata + 4, len + 1); memcpy (buffdata + 8, str, len); buffdata[8 + len] = 0; - GST_BUFFER_SIZE (buf) += 8 + plen; + *highmark += 8 + plen; + GST_DEBUG ("writing tag in buffer %p, highmark at %d", + buf, *highmark); } break; } } } + static GstBuffer * gst_avimux_riff_get_avi_header (GstAviMux * avimux) { @@ -635,6 +688,7 @@ GstBuffer *buffer; guint8 *buffdata; guint size = 0; + guint highmark = 0; /* first, let's see what actually needs to be in the buffer */ size += 32 + sizeof (gst_riff_avih); /* avi header */ @@ -649,10 +703,12 @@ avimux->header_size = size; size += 12; /* avi data header */ + GST_DEBUG ("creating avi header, header_size %u, data_size %u, idx_size %u", + avimux->header_size, avimux->data_size, avimux->idx_size); + /* tags */ iface_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (avimux)); - if (iface_tags || avimux->tags) { - size += 1024; + if ((iface_tags || avimux->tags) && !avimux->tags_snap) { if (iface_tags && avimux->tags) { tags = gst_tag_list_merge (iface_tags, avimux->tags, GST_TAG_MERGE_APPEND); @@ -662,18 +718,23 @@ tags = gst_tag_list_copy (avimux->tags); } } else { - tags = NULL; + tags = avimux->tags_snap; } + avimux->tags_snap = tags; + if (avimux->tags_snap) + size += 1024; /* allocate the buffer */ buffer = gst_buffer_new_and_alloc (size); buffdata = GST_BUFFER_DATA (buffer); - GST_BUFFER_SIZE (buffer) = 0; + highmark = 0; + GST_DEBUG ("creating buffer %p, size %d, highmark at 0", + buffer, GST_BUFFER_SIZE (buffer)); /* avi header metadata */ memcpy (buffdata + 0, "RIFF", 4); GST_WRITE_UINT32_LE (buffdata + 4, - avimux->header_size + avimux->idx_size + avimux->data_size); + avimux->header_size + avimux->idx_size + avimux->data_size + avimux->tag_size); memcpy (buffdata + 8, "AVI ", 4); memcpy (buffdata + 12, "LIST", 4); GST_WRITE_UINT32_LE (buffdata + 16, avimux->header_size - 4 * 5); @@ -681,9 +742,10 @@ memcpy (buffdata + 24, "avih", 4); GST_WRITE_UINT32_LE (buffdata + 28, sizeof (gst_riff_avih)); buffdata += 32; - GST_BUFFER_SIZE (buffer) += 32; + highmark += 32; /* the AVI header itself */ + GST_WRITE_UINT32_LE (buffdata + 0, avimux->avi_hdr.us_frame); GST_WRITE_UINT32_LE (buffdata + 4, avimux->avi_hdr.max_bps); GST_WRITE_UINT32_LE (buffdata + 8, avimux->avi_hdr.pad_gran); @@ -699,7 +761,7 @@ GST_WRITE_UINT32_LE (buffdata + 48, avimux->avi_hdr.start); GST_WRITE_UINT32_LE (buffdata + 52, avimux->avi_hdr.length); buffdata += 56; - GST_BUFFER_SIZE (buffer) += 56; + highmark += 56; if (avimux->video_pad_connected) { /* video header metadata */ @@ -739,7 +801,7 @@ GST_WRITE_UINT32_LE (buffdata + 108, avimux->vids.num_colors); GST_WRITE_UINT32_LE (buffdata + 112, avimux->vids.imp_colors); buffdata += 116; - GST_BUFFER_SIZE (buffer) += 116; + highmark += 116; } if (avimux->audio_pad_connected) { @@ -775,7 +837,7 @@ GST_WRITE_UINT16_LE (buffdata + 88, avimux->auds.blockalign); GST_WRITE_UINT16_LE (buffdata + 90, avimux->auds.size); buffdata += 92; - GST_BUFFER_SIZE (buffer) += 92; + highmark += 92; } if (avimux->video_pad_connected) { @@ -787,30 +849,32 @@ GST_WRITE_UINT32_LE (buffdata + 16, sizeof (guint32)); GST_WRITE_UINT32_LE (buffdata + 20, avimux->total_frames); buffdata += 24; - GST_BUFFER_SIZE (buffer) += 24; + highmark += 24; } /* tags */ if (tags) { guint8 *ptr; guint startsize; + GstMarkedBuffer data = { &highmark, buffer }; memcpy (buffdata + 0, "LIST", 4); ptr = buffdata + 4; /* fill in later */ - startsize = GST_BUFFER_SIZE (buffer) + 4; + startsize = highmark + 4; memcpy (buffdata + 8, "INFO", 4); buffdata += 12; - GST_BUFFER_SIZE (buffer) += 12; + highmark += 12; /* 12 bytes is needed for data header */ - GST_BUFFER_MAXSIZE (buffer) -= 12; - gst_tag_list_foreach (tags, gst_avimux_write_tag, buffer); - gst_tag_list_free (tags); - GST_BUFFER_MAXSIZE (buffer) += 12; - buffdata = GST_BUFFER_DATA (buffer) + GST_BUFFER_SIZE (buffer); + GST_BUFFER_SIZE (buffer) -= 12; + gst_tag_list_foreach (tags, gst_avimux_write_tag, &data); + /* do not free tags here, as it refers to the tag snapshot */ + GST_BUFFER_SIZE (buffer) += 12; + buffdata = GST_BUFFER_DATA (buffer) + highmark; /* update list size */ - GST_WRITE_UINT32_LE (ptr, GST_BUFFER_SIZE (buffer) - startsize - 4); + GST_WRITE_UINT32_LE (ptr, highmark - startsize - 4); + avimux->tag_size = highmark - startsize + 4; } /* avi data header */ @@ -818,9 +882,14 @@ GST_WRITE_UINT32_LE (buffdata + 4, avimux->data_size); memcpy (buffdata + 8, "movi", 4); buffdata += 12; - GST_BUFFER_SIZE (buffer) += 12; + highmark += 12; - return buffer; + { /* only the part that is filled in actually makes up the header + * unref the parent as we only need this part from now on */ + GstBuffer* subbuffer = gst_buffer_create_sub (buffer, 0, highmark); + gst_buffer_unref(buffer); + return subbuffer; + } } static GstBuffer * @@ -879,7 +948,7 @@ if (avimux->idx_index == avimux->idx_count) { avimux->idx_count += 256; avimux->idx = - realloc (avimux->idx, + g_realloc (avimux->idx, avimux->idx_count * sizeof (gst_riff_index_entry)); } memcpy (&(avimux->idx[avimux->idx_index].id), code, 4); @@ -889,9 +958,10 @@ avimux->idx_index++; } -static void +static GstFlowReturn gst_avimux_write_index (GstAviMux * avimux) { + GstFlowReturn res; GstBuffer *buffer; guint8 *buffdata; @@ -900,14 +970,18 @@ memcpy (buffdata + 0, "idx1", 4); GST_WRITE_UINT32_LE (buffdata + 4, avimux->idx_index * sizeof (gst_riff_index_entry)); - gst_pad_push (avimux->srcpad, GST_DATA (buffer)); + if ((res = gst_pad_push (avimux->srcpad, buffer)) != GST_FLOW_OK) + return res; buffer = gst_buffer_new (); GST_BUFFER_SIZE (buffer) = avimux->idx_index * sizeof (gst_riff_index_entry); GST_BUFFER_DATA (buffer) = (guint8 *) avimux->idx; + // FIXME make sure this behaves as expected + GST_BUFFER_MALLOCDATA (buffer) = GST_BUFFER_DATA (buffer); avimux->idx = NULL; /* will be free()'ed by gst_buffer_unref() */ avimux->total_data += GST_BUFFER_SIZE (buffer) + 8; - gst_pad_push (avimux->srcpad, GST_DATA (buffer)); + if ((res = gst_pad_push (avimux->srcpad, buffer)) != GST_FLOW_OK) + return res; avimux->idx_size += avimux->idx_index * sizeof (gst_riff_index_entry) + 8; @@ -915,32 +989,34 @@ avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX; } -static void +static GstFlowReturn gst_avimux_bigfile (GstAviMux * avimux, gboolean last) { + GstFlowReturn res = GST_FLOW_OK; GstBuffer *header; GstEvent *event; if (avimux->is_bigfile) { - /* sarch back */ - event = gst_event_new_seek (GST_FORMAT_BYTES | - GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, avimux->avix_start); + /* search back */ + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, + avimux->avix_start, GST_CLOCK_TIME_NONE, avimux->avix_start); /* if the event succeeds */ - gst_pad_push (avimux->srcpad, GST_DATA (event)); + gst_pad_push_event (avimux->srcpad, event); /* rewrite AVIX header */ header = gst_avimux_riff_get_avix_header (avimux->datax_size); - gst_pad_push (avimux->srcpad, GST_DATA (header)); + if ((res = gst_pad_push (avimux->srcpad, header)) != GST_FLOW_OK) + return res; /* go back to current location */ - event = gst_event_new_seek (GST_FORMAT_BYTES | - GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, avimux->total_data); - gst_pad_push (avimux->srcpad, GST_DATA (event)); + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, + avimux->total_data, GST_CLOCK_TIME_NONE, avimux->total_data); + gst_pad_push_event (avimux->srcpad, event); } avimux->avix_start = avimux->total_data; if (last) - return; + return res; avimux->is_bigfile = TRUE; avimux->numx_frames = 0; @@ -948,14 +1024,15 @@ header = gst_avimux_riff_get_avix_header (0); avimux->total_data += GST_BUFFER_SIZE (header); - gst_pad_push (avimux->srcpad, GST_DATA (header)); + return gst_pad_push (avimux->srcpad, header); } /* enough header blabla now, let's go on to actually writing the headers */ -static void +static GstFlowReturn gst_avimux_start_file (GstAviMux * avimux) { + GstFlowReturn res; GstBuffer *header; avimux->total_data = 0; @@ -974,6 +1051,8 @@ avimux->idx_count = 0; avimux->idx = NULL; + avimux->tag_size = 0; + /* header */ avimux->avi_hdr.streams = (avimux->video_pad_connected ? 1 : 0) + @@ -982,27 +1061,31 @@ header = gst_avimux_riff_get_avi_header (avimux); avimux->total_data += GST_BUFFER_SIZE (header); - gst_pad_push (avimux->srcpad, GST_DATA (header)); + res = gst_pad_push (avimux->srcpad, header); avimux->idx_offset = avimux->total_data; avimux->write_header = FALSE; avimux->restart = FALSE; + + return res; } -static void +static GstFlowReturn gst_avimux_stop_file (GstAviMux * avimux) { + GstFlowReturn res = GST_FLOW_OK; GstEvent *event; GstBuffer *header; /* if bigfile, rewrite header, else write indexes */ + /* don't bail out at once if error, still try to re-write header */ if (avimux->video_pad_connected) { if (avimux->is_bigfile) { - gst_avimux_bigfile (avimux, TRUE); + res = gst_avimux_bigfile (avimux, TRUE); avimux->idx_size = 0; } else { - gst_avimux_write_index (avimux); + res = gst_avimux_write_index (avimux); } } @@ -1041,27 +1124,36 @@ /* seek and rewrite the header */ header = gst_avimux_riff_get_avi_header (avimux); - event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, 0); - gst_pad_push (avimux->srcpad, GST_DATA (event)); - gst_pad_push (avimux->srcpad, GST_DATA (header)); - event = gst_event_new_seek (GST_FORMAT_BYTES | - GST_SEEK_METHOD_SET, avimux->total_data); - gst_pad_push (avimux->srcpad, GST_DATA (event)); + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, + 0, GST_CLOCK_TIME_NONE, 0); + gst_pad_push_event (avimux->srcpad, event); + /* the first error survives */ + if (res == GST_FLOW_OK) + res = gst_pad_push (avimux->srcpad, header); + else + gst_pad_push (avimux->srcpad, header); + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, + avimux->total_data, GST_CLOCK_TIME_NONE, avimux->total_data); + gst_pad_push_event (avimux->srcpad, event); avimux->write_header = TRUE; + + return res; } -static void +static GstFlowReturn gst_avimux_restart_file (GstAviMux * avimux) { + GstFlowReturn res; GstEvent *event; - gst_avimux_stop_file (avimux); + if ((res = gst_avimux_stop_file (avimux)) != GST_FLOW_OK) + return res; - event = gst_event_new (GST_EVENT_EOS); - gst_pad_push (avimux->srcpad, GST_DATA (event)); + event = gst_event_new_eos (); + gst_pad_push_event (avimux->srcpad, event); - gst_avimux_start_file (avimux); + return gst_avimux_start_file (avimux); } /* handle events (search) */ @@ -1070,12 +1162,14 @@ { GstAviMux *avimux; GstEventType type; + GstTagList *list; - avimux = GST_AVIMUX (gst_pad_get_parent (pad)); + avimux = GST_AVIMUX (GST_PAD_PARENT (pad)); type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; switch (type) { +#if 0 case GST_EVENT_EOS: /* is this allright? */ if (pad == avimux->videosinkpad) { @@ -1086,25 +1180,29 @@ g_warning ("Unknown pad for EOS!"); } break; +#endif case GST_EVENT_TAG: + gst_event_parse_tag (event, &list); if (avimux->tags) { - gst_tag_list_insert (avimux->tags, gst_event_tag_get_list (event), + gst_tag_list_insert (avimux->tags, list, GST_TAG_MERGE_PREPEND); } else { - avimux->tags = gst_tag_list_copy (gst_event_tag_get_list (event)); + avimux->tags = gst_tag_list_copy (list); } - break; + + /* event is handled here */ + gst_event_unref (event); + return TRUE; default: break; } - gst_event_unref (event); - gst_object_unref (avimux); - - return TRUE; + /* now GstCollectPads can take care of the rest, e.g. EOS */ + return avimux->collect_event (pad, event); } +#if 0 /* fill the internal queue for each available pad */ static void gst_avimux_fill_queue (GstAviMux * avimux) @@ -1137,29 +1235,50 @@ } } } - +#endif /* send extra 'padding' data */ -static void +static GstFlowReturn gst_avimux_send_pad_data (GstAviMux * avimux, gulong num_bytes) { GstBuffer *buffer; - buffer = gst_buffer_new (); - GST_BUFFER_SIZE (buffer) = num_bytes; - GST_BUFFER_DATA (buffer) = g_malloc (num_bytes); + buffer = gst_buffer_new_and_alloc (num_bytes); memset (GST_BUFFER_DATA (buffer), 0, num_bytes); - gst_pad_push (avimux->srcpad, GST_DATA (buffer)); + return gst_pad_push (avimux->srcpad, buffer); } -/* do audio buffer */ +/* strip buffer of time/caps meaning, is now only raw data; + * bit of a work-around for the following ...*/ +/* TODO on basesink: + * - perhaps use a default format like basesrc (to be chosen by derived element) + * and only act on segment, etc that are of such type + * - in any case, basesink could be more careful in deciding when + * to drop buffers, currently it decides on GST_FORMAT_TIME base + * even when its clip_segment is GST_FORMAT_BYTE based, + * and gst_segment_clip feels this reason enough to drop it + * (reasonable doubt is not enough to let pass :-) ) + */ static void +gst_avimux_strip_buffer (GstBuffer * buffer) +{ + GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; + gst_buffer_set_caps (buffer, NULL); +} + +/* do audio buffer */ +static GstFlowReturn gst_avimux_do_audio_buffer (GstAviMux * avimux) { - GstBuffer *data = avimux->audio_buffer_queue, *header; + GstFlowReturn res; + GstBuffer *data, *header; gulong total_size, pad_bytes = 0; + data = avimux->audio_buffer_queue + = gst_collect_pads_pop (avimux->collect, avimux->audiocollectdata); + gst_avimux_strip_buffer (data); + /* write a audio header + index entry */ if (GST_BUFFER_SIZE (data) & 1) { pad_bytes = 2 - (GST_BUFFER_SIZE (data) & 1); @@ -1176,35 +1295,51 @@ gst_avimux_add_index (avimux, "01wb", 0x0, GST_BUFFER_SIZE (data)); } - gst_pad_push (avimux->srcpad, GST_DATA (header)); - gst_pad_push (avimux->srcpad, GST_DATA (data)); + if ((res = gst_pad_push (avimux->srcpad, header)) != GST_FLOW_OK) + return res; + if ((res = gst_pad_push (avimux->srcpad, data)) != GST_FLOW_OK) + return res; + if (pad_bytes) { - gst_avimux_send_pad_data (avimux, pad_bytes); + if ((res = gst_avimux_send_pad_data (avimux, pad_bytes)) != GST_FLOW_OK) + return res; } + + /* if any push above fails, we're in trouble with file consistency anyway */ avimux->total_data += total_size; avimux->idx_offset += total_size; - avimux->audio_buffer_queue = NULL; + return res; } /* do video buffer */ -static void +static GstFlowReturn gst_avimux_do_video_buffer (GstAviMux * avimux) { - GstBuffer *data = avimux->video_buffer_queue, *header; + GstFlowReturn res; + GstBuffer *data, *header; gulong total_size, pad_bytes = 0; - if (avimux->restart) - gst_avimux_restart_file (avimux); + data = avimux->video_buffer_queue + = gst_collect_pads_pop (avimux->collect, avimux->videocollectdata); + gst_avimux_strip_buffer (data); + + if (avimux->restart) { + if ((res = gst_avimux_restart_file (avimux)) != GST_FLOW_OK) + return res; + } /* write a video header + index entry */ if ((avimux->is_bigfile ? avimux->datax_size : avimux->data_size) + GST_BUFFER_SIZE (data) > 1024 * 1024 * 2000) { - if (avimux->enable_large_avi) - gst_avimux_bigfile (avimux, FALSE); - else - gst_avimux_restart_file (avimux); + if (avimux->enable_large_avi) { + if ((res = gst_avimux_bigfile (avimux, FALSE)) != GST_FLOW_OK) + return res; + } else { + if ((res = gst_avimux_restart_file (avimux)) != GST_FLOW_OK) + return res; + } } if (GST_BUFFER_SIZE (data) & 1) { @@ -1220,25 +1355,32 @@ } else { guint flags = 0x2; - if (GST_BUFFER_FLAG_IS_SET (data, GST_BUFFER_KEY_UNIT)) + if (!GST_BUFFER_FLAG_IS_SET (data, GST_BUFFER_FLAG_DELTA_UNIT)) flags |= 0x10; avimux->data_size += total_size; avimux->num_frames++; gst_avimux_add_index (avimux, "00db", flags, GST_BUFFER_SIZE (data)); } - gst_pad_push (avimux->srcpad, GST_DATA (header)); - gst_pad_push (avimux->srcpad, GST_DATA (data)); + if ((res = gst_pad_push (avimux->srcpad, header)) != GST_FLOW_OK) + return res; + if ((res = gst_pad_push (avimux->srcpad, data)) != GST_FLOW_OK) + return res; + if (pad_bytes) { - gst_avimux_send_pad_data (avimux, pad_bytes); + if ((res = gst_avimux_send_pad_data (avimux, pad_bytes)) != GST_FLOW_OK) + return res; } + + /* if any push above fails, we're in trouble with file consistency anyway */ avimux->total_data += total_size; avimux->idx_offset += total_size; - avimux->video_buffer_queue = NULL; + return res; } +#if 0 /* take the oldest buffer in our internal queue and push-it */ static gboolean gst_avimux_do_one_buffer (GstAviMux * avimux) @@ -1282,6 +1424,97 @@ gst_avimux_do_one_buffer (avimux); } +#endif + +/* pick the oldest buffer from the pads and push it */ +static GstFlowReturn +gst_avimux_do_one_buffer (GstAviMux * avimux) +{ + GstFlowReturn res = GST_FLOW_OK; + GstClockTime video_time = GST_CLOCK_TIME_NONE; + GstClockTime audio_time = GST_CLOCK_TIME_NONE; + + if (avimux->videocollectdata && avimux->video_pad_connected) + avimux->video_buffer_queue + = gst_collect_pads_peek (avimux->collect, avimux->videocollectdata); + if (avimux->audiocollectdata && avimux->audio_pad_connected) + avimux->audio_buffer_queue + = gst_collect_pads_peek (avimux->collect, avimux->audiocollectdata); + + /* segment info is used to translate the incoming timestamps + * to outgoing muxed (running) timeline */ + if (avimux->video_buffer_queue) { + video_time = gst_segment_to_running_time (&avimux->videocollectdata->segment, + GST_FORMAT_TIME, + GST_BUFFER_TIMESTAMP (avimux->video_buffer_queue)); + GST_DEBUG ("peeked video buffer %p (time %" GST_TIME_FORMAT ")" + ", running %" GST_TIME_FORMAT, + avimux->video_buffer_queue, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (avimux->video_buffer_queue)), + GST_TIME_ARGS (video_time)); + } + if (avimux->audio_buffer_queue) { + audio_time = gst_segment_to_running_time (&avimux->audiocollectdata->segment, + GST_FORMAT_TIME, + GST_BUFFER_TIMESTAMP (avimux->audio_buffer_queue)); + GST_DEBUG ("peeked audio buffer %p (time %" GST_TIME_FORMAT ")" + ", running %" GST_TIME_FORMAT, + avimux->audio_buffer_queue, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (avimux->audio_buffer_queue)), + GST_TIME_ARGS (audio_time)); + + } + + /* now use re-calculated time to choose */ + if (G_LIKELY (avimux->video_buffer_queue && avimux->audio_buffer_queue)) { + /* either video and audio can be translated, or translate neither */ + if (!GST_CLOCK_TIME_IS_VALID (video_time) + || !GST_CLOCK_TIME_IS_VALID (audio_time)) { + video_time = GST_BUFFER_TIMESTAMP (avimux->video_buffer_queue); + audio_time = GST_BUFFER_TIMESTAMP (avimux->audio_buffer_queue); + } + if (video_time <= audio_time) + res = gst_avimux_do_video_buffer (avimux); + else + res = gst_avimux_do_audio_buffer (avimux); + } else if (avimux->video_buffer_queue || avimux->audio_buffer_queue) { + if (avimux->video_buffer_queue) + res = gst_avimux_do_video_buffer (avimux); + else + res = gst_avimux_do_audio_buffer (avimux); + } else { + /* simply finish off the file and send EOS */ + gst_avimux_stop_file (avimux); + gst_pad_push_event (avimux->srcpad, gst_event_new_eos ()); + return GST_FLOW_WRONG_STATE; + } + + /* unref the peek obtained above */ + if (avimux->video_buffer_queue) { + gst_buffer_unref (avimux->video_buffer_queue); + avimux->video_buffer_queue = NULL; + } + if (avimux->audio_buffer_queue) { + gst_buffer_unref (avimux->audio_buffer_queue); + avimux->audio_buffer_queue = NULL; + } + + return res; +} + +static GstFlowReturn +gst_avimux_collect_pads (GstCollectPads * pads, GstAviMux * avimux) +{ + GstFlowReturn res; + + if (G_UNLIKELY (avimux->write_header)) { + if ((res = gst_avimux_start_file (avimux)) != GST_FLOW_OK) + return res; + } + + return gst_avimux_do_one_buffer (avimux); +} + static void gst_avimux_get_property (GObject * object, @@ -1296,6 +1529,9 @@ case ARG_BIGFILE: g_value_set_boolean (value, avimux->enable_large_avi); break; + case ARG_FORCEFOURCC: + g_value_set_string (value, avimux->force_fourcc); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1315,6 +1551,10 @@ case ARG_BIGFILE: avimux->enable_large_avi = g_value_get_boolean (value); break; + case ARG_FORCEFOURCC: + g_free (avimux->force_fourcc); + avimux->force_fourcc = g_strdup (g_value_get_string (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1331,19 +1571,45 @@ avimux = GST_AVIMUX (element); switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_collect_pads_start (avimux->collect); + break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: avimux->video_pad_eos = avimux->audio_pad_eos = FALSE; break; case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (avimux->collect); + break; + default: + break; + } + + if (GST_ELEMENT_CLASS (parent_class)->change_state) { + GstStateChangeReturn ret; + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret != GST_STATE_CHANGE_SUCCESS) + return ret; + } + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: if (avimux->tags) { gst_tag_list_free (avimux->tags); avimux->tags = NULL; } + if (avimux->tags_snap) { + gst_tag_list_free (avimux->tags_snap); + avimux->tags_snap = NULL; + } + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - return GST_STATE_CHANGE_SUCCESS; } Index: gstavimux.h =================================================================== RCS file: /cvs/gstreamer/gst-plugins-good/gst/avi/gstavimux.h,v retrieving revision 1.13 diff -u -u -r1.13 gstavimux.h --- gstavimux.h 19 Jan 2005 11:01:09 -0000 1.13 +++ gstavimux.h 23 Apr 2006 23:15:08 -0000 @@ -23,6 +23,7 @@ #include +#include #include #include "avi-ids.h" @@ -52,10 +53,12 @@ /* pads */ GstPad *srcpad; - GstPad *audiosinkpad; + GstCollectData *audiocollectdata; gboolean audio_pad_connected, audio_pad_eos; - GstPad *videosinkpad; + GstCollectData *videocollectdata; gboolean video_pad_connected, video_pad_eos; + GstCollectPads *collect; + GstPadEventFunction collect_event; /* the AVI header */ gst_riff_avih avi_hdr; @@ -79,6 +82,8 @@ /* tags */ GstTagList *tags; + GstTagList *tags_snap; + guint32 tag_size; /* information about the AVI index ('idx') */ gst_riff_index_entry *idx; @@ -92,6 +97,8 @@ /* whether to use "large AVI files" or just stick to small indexed files */ gboolean enable_large_avi; + gchar *force_fourcc; + /* in order to be usable as a loopbased element, we need an internal * 'buffered' buffer for each pad, so one for audio, one for video */ GstBuffer *audio_buffer_queue, *video_buffer_queue;