diff options
author | Ronald S. Bultje <rbultje@ronald.bitfreak.net> | 2003-12-07 20:00:41 +0000 |
---|---|---|
committer | Ronald S. Bultje <rbultje@ronald.bitfreak.net> | 2003-12-07 20:00:41 +0000 |
commit | 12e856c26d690c02cc05a2e945b49e2df538307a (patch) | |
tree | cbb1cf43fa0dcbb477775b3ade47e4b16e057b5e /gst/avi | |
parent | 396a5c812901cfb1e16a8b4f55f122e229293b98 (diff) |
Riff, EBML, fourcc etc. work. Not fully finished, but better than what we used to have and definately worth a first b...
Original commit message from CVS:
Riff, EBML, fourcc etc. work. Not fully finished, but better than
what we used to have and definately worth a first broad testing.
I've revived rifflib. Rifflib used to be a bytestream-for-riff, which
just dup'ed bytestream. I've rewritten rifflib to be a modern riff-
chunk parser that uses bytestream fully, plus adds some extra functions
so that riff file parsing becomes extremely easy. It also contains some
small usability functions for strh/strf and metadata parsing. Note that
it doesn't use the new tagging yet, that's a TODO.
Avidemux has been rewritten to use this. I think we all agreed that
avidemux was pretty much a big mess, which is because it used all
sort of bytestream magic all around the place. It was just ugly.
This is a lot nicer, very complete and safe. I think this is far more
robust than what the old avidemux could ever have been. Of course, it
might contain bugs, please let me know.
EBML writing has also been implemented. This is useful for matroska.
I'm intending to modify avidemux (with a riffwriter) similarly. Maybe
I'll change wavparse/-enc too to use rifflib.
Lastly, several plugins have been modified to use rifflib's fourcc
parsing instead of their own. this puts fourcc parsing in one central
place, which should make it a lot simpler to add new fourccs. We might
want to move this to its own lib instead of rifflib.
Enjoy!
Diffstat (limited to 'gst/avi')
-rw-r--r-- | gst/avi/Makefile.am | 11 | ||||
-rw-r--r-- | gst/avi/avi-ids.h | 48 | ||||
-rw-r--r-- | gst/avi/gstavi.c | 53 | ||||
-rw-r--r-- | gst/avi/gstavidemux.c | 2460 | ||||
-rw-r--r-- | gst/avi/gstavidemux.h | 122 | ||||
-rw-r--r-- | gst/avi/gstavimux.h | 3 |
6 files changed, 1161 insertions, 1536 deletions
diff --git a/gst/avi/Makefile.am b/gst/avi/Makefile.am index 0b81ccc3..cbea2b3a 100644 --- a/gst/avi/Makefile.am +++ b/gst/avi/Makefile.am @@ -1,11 +1,14 @@ - plugin_LTLIBRARIES = libgstavi.la -libgstavi_la_SOURCES = gstavidemux.c gstavimux.c +libgstavi_la_SOURCES = \ + gstavi.c \ + gstavidemux.c \ + gstavimux.c noinst_HEADERS = \ - gstavimux.h \ - gstavidemux.h + avi-ids.h \ + gstavimux.h \ + gstavidemux.h libgstavi_la_CFLAGS = $(GST_CFLAGS) libgstavi_la_LIBADD = diff --git a/gst/avi/avi-ids.h b/gst/avi/avi-ids.h new file mode 100644 index 00000000..f9e2dd31 --- /dev/null +++ b/gst/avi/avi-ids.h @@ -0,0 +1,48 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_AVI_H__ +#define __GST_AVI_H__ + +#include <gst/gst.h> + +typedef struct _gst_riff_avih { + guint32 us_frame; /* microsec per frame */ + guint32 max_bps; /* byte/s overall */ + guint32 pad_gran; /* pad_granularity */ + guint32 flags; +/* flags values */ +#define GST_RIFF_AVIH_HASINDEX 0x00000010 /* has idx1 chunk */ +#define GST_RIFF_AVIH_MUSTUSEINDEX 0x00000020 /* must use idx1 chunk to determine order */ +#define GST_RIFF_AVIH_ISINTERLEAVED 0x00000100 /* AVI file is interleaved */ +#define GST_RIFF_AVIH_WASCAPTUREFILE 0x00010000 /* specially allocated used for capturing real time video */ +#define GST_RIFF_AVIH_COPYRIGHTED 0x00020000 /* contains copyrighted data */ + guint32 tot_frames; /* # of frames (all) */ + guint32 init_frames; /* initial frames (???) */ + guint32 streams; + guint32 bufsize; /* suggested buffer size */ + guint32 width; + guint32 height; + guint32 scale; + guint32 rate; + guint32 start; + guint32 length; +} gst_riff_avih; + +#endif /* __GST_AVI_H__ */ diff --git a/gst/avi/gstavi.c b/gst/avi/gstavi.c new file mode 100644 index 00000000..da30d434 --- /dev/null +++ b/gst/avi/gstavi.c @@ -0,0 +1,53 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com> + * + * gstavi.c: plugin registering + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstavidemux.h" +#include "gstavimux.h" + +static gboolean +plugin_init (GstPlugin *plugin) +{ + if (!gst_library_load ("riff")) + return FALSE; + + 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, + GST_VERSION_MINOR, + "avi", + "AVI stream handling", + plugin_init, + VERSION, + "LGPL", + GST_PACKAGE, + GST_ORIGIN +) diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index ad4531a0..5f19fce5 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -17,15 +17,15 @@ * Boston, MA 02111-1307, USA. */ - -/* #define GST_DEBUG_ENABLED */ #ifdef HAVE_CONFIG_H #include "config.h" #endif + #include <string.h> +#include "gst/riff/riff-media.h" #include "gstavidemux.h" -#include "gstavimux.h" +#include "avi-ids.h" GST_DEBUG_CATEGORY_STATIC (avidemux_debug); #define GST_CAT_DEFAULT avidemux_debug @@ -38,8 +38,6 @@ enum { enum { ARG_0, - ARG_BITRATE, - ARG_METADATA, ARG_STREAMINFO, /* FILL ME */ }; @@ -55,10 +53,11 @@ GST_PAD_TEMPLATE_FACTORY (sink_templ, ) ); -static void gst_avi_demux_base_init (gpointer g_class); +static void gst_avi_demux_base_init (GstAviDemuxClass *klass); static void gst_avi_demux_class_init (GstAviDemuxClass *klass); -static void gst_avi_demux_init (GstAviDemux *avi_demux); +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, @@ -90,15 +89,7 @@ static void gst_avi_demux_get_property (GObject *object, GValue *value, GParamSpec *pspec); -static GstCaps * gst_avi_demux_audio_caps (guint16 codec_id, - gst_riff_strf_auds *strf, GstAviDemux *avi_demux); -static GstCaps * gst_avi_demux_video_caps (guint32 codec_fcc, - gst_riff_strh *strh, gst_riff_strf_vids *strf, - GstAviDemux *avi_demux); -static GstCaps * gst_avi_demux_iavs_caps (void); - -static GstPadTemplate *videosrctempl, *audiosrctempl; -static GstElementClass *parent_class = NULL; +static GstRiffReadClass *parent_class = NULL; /*static guint gst_avi_demux_signals[LAST_SIGNAL] = { 0 }; */ GType @@ -108,88 +99,60 @@ gst_avi_demux_get_type(void) if (!avi_demux_type) { static const GTypeInfo avi_demux_info = { - sizeof(GstAviDemuxClass), - gst_avi_demux_base_init, + sizeof (GstAviDemuxClass), + (GBaseInitFunc) gst_avi_demux_base_init, NULL, - (GClassInitFunc)gst_avi_demux_class_init, + (GClassInitFunc) gst_avi_demux_class_init, NULL, NULL, - sizeof(GstAviDemux), + sizeof (GstAviDemux), 0, - (GInstanceInitFunc)gst_avi_demux_init, + (GInstanceInitFunc) gst_avi_demux_init, }; - avi_demux_type = g_type_register_static(GST_TYPE_ELEMENT, "GstAviDemux", &avi_demux_info, 0); + + avi_demux_type = + g_type_register_static (GST_TYPE_RIFF_READ, + "GstAviDemux", + &avi_demux_info, 0); } + return avi_demux_type; } static void -gst_avi_demux_base_init (gpointer g_class) +gst_avi_demux_base_init (GstAviDemuxClass *klass) { static GstElementDetails gst_avi_demux_details = GST_ELEMENT_DETAILS ( "Avi demuxer", "Codec/Demuxer", "Demultiplex an avi file into audio and video", "Erik Walthinsen <omega@cse.ogi.edu>\n" - "Wim Taymans <wim.taymans@chello.be>" + "Wim Taymans <wim.taymans@chello.be>\n" + "Ronald Bultje <rbultje@ronald.bitfreak.net>" ); - static guint32 vid_list[] = { - GST_MAKE_FOURCC('I','4','2','0'), - GST_MAKE_FOURCC('Y','U','Y','2'), - GST_MAKE_FOURCC('M','J','P','G'), - GST_MAKE_FOURCC('D','V','S','D'), - GST_MAKE_FOURCC('W','M','V','1'), - GST_MAKE_FOURCC('W','M','V','2'), - GST_MAKE_FOURCC('M','P','G','4'), - GST_MAKE_FOURCC('M','P','4','2'), - GST_MAKE_FOURCC('M','P','4','3'), - GST_MAKE_FOURCC('H','F','Y','U'), - GST_MAKE_FOURCC('D','I','V','3'), - GST_MAKE_FOURCC('M','P','E','G'), - GST_MAKE_FOURCC('H','2','6','3'), - GST_MAKE_FOURCC('D','I','V','X'), - GST_MAKE_FOURCC('X','V','I','D'), - GST_MAKE_FOURCC('3','I','V','1'), - 0 /* end */ - }; - static gint aud_list[] = { - GST_RIFF_WAVE_FORMAT_MPEGL3, - GST_RIFF_WAVE_FORMAT_MPEGL12, - GST_RIFF_WAVE_FORMAT_PCM, - GST_RIFF_WAVE_FORMAT_VORBIS1, - GST_RIFF_WAVE_FORMAT_A52, - GST_RIFF_WAVE_FORMAT_ALAW, - GST_RIFF_WAVE_FORMAT_MULAW, - -1 /* end */ - }; - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gint i = 0; - GstCaps *audcaps = NULL, *vidcaps = NULL, *temp; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstPadTemplate *videosrctempl, *audiosrctempl; + GstCaps *audcaps, *vidcaps; - for (i = 0; aud_list[i] != -1; i++) { - temp = gst_avi_demux_audio_caps (aud_list[i], NULL, NULL); - audcaps = gst_caps_append (audcaps, temp); - } + audcaps = gst_riff_create_audio_template_caps (); audiosrctempl = gst_pad_template_new ("audio_%02d", GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps, NULL); - for (i = 0; vid_list[i] != 0; i++) { - temp = gst_avi_demux_video_caps (vid_list[i], NULL, NULL, NULL); - vidcaps = gst_caps_append (vidcaps, temp); - } - vidcaps = gst_caps_append (vidcaps, - gst_avi_demux_iavs_caps ()); + + vidcaps = gst_caps_append ( + gst_riff_create_video_template_caps (), + gst_riff_create_iavs_template_caps ()); videosrctempl = gst_pad_template_new ("video_%02d", GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps, NULL); + gst_element_class_add_pad_template (element_class, audiosrctempl); gst_element_class_add_pad_template (element_class, videosrctempl); gst_element_class_add_pad_template (element_class, GST_PAD_TEMPLATE_GET (sink_templ)); gst_element_class_set_details (element_class, &gst_avi_demux_details); - } static void @@ -201,17 +164,14 @@ gst_avi_demux_class_init (GstAviDemuxClass *klass) gobject_class = (GObjectClass*)klass; gstelement_class = (GstElementClass*)klass; - g_object_class_install_property (G_OBJECT_CLASS(klass), ARG_BITRATE, - g_param_spec_long ("bitrate","bitrate","bitrate", - G_MINLONG, G_MAXLONG, 0, G_PARAM_READABLE)); /* CHECKME */ - g_object_class_install_property (gobject_class, ARG_METADATA, - g_param_spec_boxed ("metadata", "Metadata", "Metadata", - GST_TYPE_CAPS, G_PARAM_READABLE)); g_object_class_install_property (gobject_class, ARG_STREAMINFO, g_param_spec_boxed ("streaminfo", "Streaminfo", "Streaminfo", GST_TYPE_CAPS, G_PARAM_READABLE)); - parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux", + 0, "Demuxer for AVI streams"); + + parent_class = g_type_class_ref (GST_TYPE_RIFF_READ); gobject_class->get_property = gst_avi_demux_get_property; @@ -220,295 +180,57 @@ gst_avi_demux_class_init (GstAviDemuxClass *klass) } static void -gst_avi_demux_init (GstAviDemux *avi_demux) +gst_avi_demux_init (GstAviDemux *avi) { - GST_FLAG_SET (avi_demux, GST_ELEMENT_EVENT_AWARE); - - avi_demux->sinkpad = gst_pad_new_from_template ( - GST_PAD_TEMPLATE_GET (sink_templ), "sink"); - gst_element_add_pad (GST_ELEMENT (avi_demux), avi_demux->sinkpad); + GST_FLAG_SET (avi, GST_ELEMENT_EVENT_AWARE); - gst_element_set_loop_function (GST_ELEMENT (avi_demux), gst_avi_demux_loop); -} + avi->sinkpad = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (sink_templ), "sink"); + gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad); + GST_RIFF_READ (avi)->sinkpad = avi->sinkpad; -static gboolean -gst_avi_demux_avih (GstAviDemux *avi_demux) -{ - gst_riff_avih *avih; - guint8 *avihdata; - GstByteStream *bs = avi_demux->bs; - guint32 got_bytes; - - got_bytes = gst_bytestream_peek_bytes (bs, &avihdata, sizeof (gst_riff_avih)); - avih = (gst_riff_avih *) avihdata; - - if (got_bytes == sizeof (gst_riff_avih)) { - avi_demux->avih.us_frame = GUINT32_FROM_LE (avih->us_frame); - avi_demux->avih.max_bps = GUINT32_FROM_LE (avih->max_bps); - avi_demux->avih.pad_gran = GUINT32_FROM_LE (avih->pad_gran); - avi_demux->avih.flags = GUINT32_FROM_LE (avih->flags); - avi_demux->avih.tot_frames = GUINT32_FROM_LE (avih->tot_frames); - avi_demux->avih.init_frames = GUINT32_FROM_LE (avih->init_frames); - avi_demux->avih.streams = GUINT32_FROM_LE (avih->streams); - avi_demux->avih.bufsize = GUINT32_FROM_LE (avih->bufsize); - avi_demux->avih.width = GUINT32_FROM_LE (avih->width); - avi_demux->avih.height = GUINT32_FROM_LE (avih->height); - avi_demux->avih.scale = GUINT32_FROM_LE (avih->scale); - avi_demux->avih.rate = GUINT32_FROM_LE (avih->rate); - avi_demux->avih.start = GUINT32_FROM_LE (avih->start); - avi_demux->avih.length = GUINT32_FROM_LE (avih->length); - - GST_INFO ( "gst_avi_demux: avih tag found"); - GST_INFO ( "gst_avi_demux: us_frame %d", avi_demux->avih.us_frame); - GST_INFO ( "gst_avi_demux: max_bps %d", avi_demux->avih.max_bps); - GST_INFO ( "gst_avi_demux: pad_gran %d", avi_demux->avih.pad_gran); - GST_INFO ( "gst_avi_demux: flags 0x%08x", avi_demux->avih.flags); - GST_INFO ( "gst_avi_demux: tot_frames %d", avi_demux->avih.tot_frames); - GST_INFO ( "gst_avi_demux: init_frames %d", avi_demux->avih.init_frames); - GST_INFO ( "gst_avi_demux: streams %d", avi_demux->avih.streams); - GST_INFO ( "gst_avi_demux: bufsize %d", avi_demux->avih.bufsize); - GST_INFO ( "gst_avi_demux: width %d", avi_demux->avih.width); - GST_INFO ( "gst_avi_demux: height %d", avi_demux->avih.height); - GST_INFO ( "gst_avi_demux: scale %d", avi_demux->avih.scale); - GST_INFO ( "gst_avi_demux: rate %d", avi_demux->avih.rate); - GST_INFO ( "gst_avi_demux: start %d", avi_demux->avih.start); - GST_INFO ( "gst_avi_demux: length %d", avi_demux->avih.length); - - return TRUE; - } - return FALSE; -} - -static gboolean -gst_avi_demux_strh (GstAviDemux *avi_demux) -{ - gst_riff_strh *strh; - guint8 *strhdata; - GstByteStream *bs = avi_demux->bs; - guint32 got_bytes; - - got_bytes = gst_bytestream_peek_bytes (bs, &strhdata, sizeof (gst_riff_strh)); - strh = (gst_riff_strh *) strhdata; - - if (got_bytes == sizeof (gst_riff_strh)) { - avi_stream_context *target; - - avi_demux->fcc_type = GUINT32_FROM_LE (strh->type); - - target = &avi_demux->stream[avi_demux->num_streams]; - - target->num = avi_demux->num_streams; - - target->strh.type = avi_demux->fcc_type; - target->strh.fcc_handler = GUINT32_FROM_LE (strh->fcc_handler); - target->strh.flags = GUINT32_FROM_LE (strh->flags); - target->strh.priority = GUINT32_FROM_LE (strh->priority); - target->strh.init_frames = GUINT32_FROM_LE (strh->init_frames); - target->strh.scale = GUINT32_FROM_LE (strh->scale); - target->strh.rate = GUINT32_FROM_LE (strh->rate); - target->strh.start = GUINT32_FROM_LE (strh->start); - target->strh.length = GUINT32_FROM_LE (strh->length); - target->strh.bufsize = GUINT32_FROM_LE (strh->bufsize); - target->strh.quality = GUINT32_FROM_LE (strh->quality); - target->strh.samplesize = GUINT32_FROM_LE (strh->samplesize); - - if (!target->strh.scale) - target->strh.scale = 1; /* avoid division by zero */ - if (!target->strh.rate) - target->strh.rate = 1; /* avoid division by zero */ - - GST_INFO ( "gst_avi_demux: strh tag found"); - GST_INFO ( "gst_avi_demux: type 0x%08x (%s)", - target->strh.type, gst_riff_id_to_fourcc (strh->type)); - GST_INFO ( "gst_avi_demux: fcc_handler 0x%08x (%s)", - target->strh.fcc_handler, gst_riff_id_to_fourcc (strh->fcc_handler)); - GST_INFO ( "gst_avi_demux: flags 0x%08x", strh->flags); - GST_INFO ( "gst_avi_demux: priority %d", target->strh.priority); - GST_INFO ( "gst_avi_demux: init_frames %d", target->strh.init_frames); - GST_INFO ( "gst_avi_demux: scale %d", target->strh.scale); - GST_INFO ( "gst_avi_demux: rate %d", target->strh.rate); - GST_INFO ( "gst_avi_demux: start %d", target->strh.start); - GST_INFO ( "gst_avi_demux: length %d", target->strh.length); - GST_INFO ( "gst_avi_demux: bufsize %d", target->strh.bufsize); - GST_INFO ( "gst_avi_demux: quality %d", target->strh.quality); - GST_INFO ( "gst_avi_demux: samplesize %d", target->strh.samplesize); - - target->delay = 0LL; - target->total_bytes = 0LL; - target->total_frames = 0; - target->end_pos = -1; - target->current_frame = 0; - target->current_byte = 0; - target->need_flush = FALSE; - target->skip = 0; - - avi_demux->avih.bufsize = MAX (avi_demux->avih.bufsize, target->strh.bufsize); - - return TRUE; - } - return FALSE; -} - -static void -gst_avi_demux_dmlh (GstAviDemux *avi_demux) -{ - gst_riff_dmlh *dmlh; - guint8 *dmlhdata; - GstByteStream *bs = avi_demux->bs; - guint32 got_bytes; + gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop); + gst_avi_demux_reset (avi); - got_bytes = gst_bytestream_peek_bytes (bs, &dmlhdata, sizeof (gst_riff_dmlh)); - dmlh = (gst_riff_dmlh *) dmlhdata; + avi->streaminfo = NULL; + avi->index_entries = NULL; + memset (&avi->stream, 0, sizeof (avi->stream)); } static void -gst_avi_demux_strn (GstAviDemux *avi_demux, gint len) +gst_avi_demux_reset (GstAviDemux *avi) { - gchar *name; - guint8 *namedata; - GstByteStream *bs = avi_demux->bs; - guint32 got_bytes; - - got_bytes = gst_bytestream_peek_bytes (bs, &namedata, len); - name = (gchar *) namedata; - if (got_bytes != len) - return; - - GST_DEBUG ("Stream name: \"%s\"", name); -} - -static void -gst_avi_demux_metadata (GstAviDemux *avi_demux, gint len) -{ - guint32 got_bytes; - GstByteStream *bs = avi_demux->bs; - gst_riff_chunk *temp_chunk, chunk; - guint8 *tempdata; - gchar *name, *type; - GstProps *props; - GstPropsEntry *entry; - - props = gst_props_empty_new (); - - while (len > 0) { - got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_chunk)); - temp_chunk = (gst_riff_chunk *) tempdata; - - /* fixup for our big endian friends */ - chunk.id = GUINT32_FROM_LE (temp_chunk->id); - chunk.size = GUINT32_FROM_LE (temp_chunk->size); + gint i; - gst_bytestream_flush (bs, sizeof (gst_riff_chunk)); - if (got_bytes != sizeof (gst_riff_chunk)) - return; - len -= sizeof (gst_riff_chunk); + for (i = 0; i < avi->num_streams; i++) { + g_free (avi->stream[i].strh); + gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad); + } + memset (&avi->stream, 0, sizeof (avi->stream)); - /* don't care about empty entries - move on */ - if (chunk.size == 0) - continue; + avi->num_streams = 0; + avi->num_v_streams = 0; + avi->num_a_streams = 0; - got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, chunk.size); - name = (gchar *) tempdata; - gst_bytestream_flush (bs, (chunk.size + 1) & ~1); - if (got_bytes != chunk.size) - return; - len -= ((chunk.size + 1) & ~1); - - /* we now have an info string in 'name' of type 'chunk.id' - find 'type' */ - switch (chunk.id) { - case GST_RIFF_INFO_IARL: - type = "Location"; - break; - case GST_RIFF_INFO_IART: - type = "Artist"; - break; - case GST_RIFF_INFO_ICMS: - type = "Commissioner"; - break; - case GST_RIFF_INFO_ICMT: - type = "Comment"; - break; - case GST_RIFF_INFO_ICOP: - type = "Copyright"; - break; - case GST_RIFF_INFO_ICRD: - type = "Creation Date"; - break; - case GST_RIFF_INFO_ICRP: - type = "Cropped"; - break; - case GST_RIFF_INFO_IDIM: - type = "Dimensions"; - break; - case GST_RIFF_INFO_IDPI: - type = "Dots per Inch"; - break; - case GST_RIFF_INFO_IENG: - type = "Engineer"; - break; - case GST_RIFF_INFO_IGNR: - type = "Genre"; - break; - case GST_RIFF_INFO_IKEY: - type = "Keywords"; - break; - case GST_RIFF_INFO_ILGT: - type = "Lightness"; - break; - case GST_RIFF_INFO_IMED: - type = "Medium"; - break; - case GST_RIFF_INFO_INAM: - type = "Title"; /* "Name" */ - break; - case GST_RIFF_INFO_IPLT: - type = "Palette"; - break; - case GST_RIFF_INFO_IPRD: - type = "Product"; - break; - case GST_RIFF_INFO_ISBJ: - type = "Subject"; - break; - case GST_RIFF_INFO_ISFT: - type = "Encoder"; /* "Software" */ - break; - case GST_RIFF_INFO_ISHP: - type = "Sharpness"; - break; - case GST_RIFF_INFO_ISRC: - type = "Source"; - break; - case GST_RIFF_INFO_ISRF: - type = "Source Form"; - break; - case GST_RIFF_INFO_ITCH: - type = "Technician"; - break; - default: - type = NULL; - break; - } + avi->state = GST_AVI_DEMUX_START; + avi->level_up = 0; - if (type) { - /* create props entry */ - entry = gst_props_entry_new (type, GST_PROPS_STRING (name)); - gst_props_add_entry (props, entry); - } + if (avi->index_entries) { + g_free (avi->index_entries); + avi->index_entries = NULL; } + avi->index_size = 0; - gst_props_debug(props); + avi->num_frames = 0; + avi->us_per_frame = 0; - gst_caps_replace_sink (&avi_demux->metadata, - gst_caps_new("avi_metadata", - "application/x-gst-metadata", - props)); + avi->seek_offset = (guint64) -1; - g_object_notify(G_OBJECT(avi_demux), "metadata"); + gst_caps_replace (&avi->streaminfo, NULL); } static void -gst_avi_demux_streaminfo (GstAviDemux *avi_demux) +gst_avi_demux_streaminfo (GstAviDemux *avi) { GstProps *props; @@ -516,781 +238,110 @@ gst_avi_demux_streaminfo (GstAviDemux *avi_demux) /* compression formats are added later - a bit hacky */ - gst_caps_replace_sink (&avi_demux->streaminfo, - gst_caps_new("avi_streaminfo", - "application/x-gst-streaminfo", - props)); + gst_caps_replace_sink (&avi->streaminfo, + gst_caps_new ("avi_streaminfo", + "application/x-gst-streaminfo", + props)); - /*g_object_notify(G_OBJECT(avi_demux), "streaminfo");*/ + /*g_object_notify(G_OBJECT(avi), "streaminfo");*/ } -/* video/audio pad/caps stuff */ - -#ifdef G_HAVE_ISO_VARARGS - -#define GST_AVI_VID_CAPS_NEW(name, mimetype, ...) \ - (strf != NULL) ? \ - GST_CAPS_NEW (name, \ - mimetype, \ - "width", GST_PROPS_INT (width), \ - "height", GST_PROPS_INT (height), \ - "framerate", GST_PROPS_FLOAT (framerate), \ - __VA_ARGS__) \ - : \ - GST_CAPS_NEW (name, \ - mimetype, \ - "width", GST_PROPS_INT_RANGE (16, 4096), \ - "height", GST_PROPS_INT_RANGE (16, 4096), \ - "framerate", GST_PROPS_FLOAT_RANGE (0., G_MAXFLOAT), \ - __VA_ARGS__) - -#elif defined(G_HAVE_GNUC_VARARGS) - -#define GST_AVI_VID_CAPS_NEW(name, mimetype, props...) \ - (strf != NULL) ? \ - GST_CAPS_NEW (name, \ - mimetype, \ - "width", GST_PROPS_INT (width), \ - "height", GST_PROPS_INT (height), \ - "framerate", GST_PROPS_FLOAT (framerate), \ - ##props) \ - : \ - GST_CAPS_NEW (name, \ - mimetype, \ - "width", GST_PROPS_INT_RANGE (16, 4096), \ - "height", GST_PROPS_INT_RANGE (16, 4096), \ - "framerate", GST_PROPS_FLOAT_RANGE (0., G_MAXFLOAT), \ - ##props) -#endif - -static GstCaps * -gst_avi_demux_video_caps (guint32 codec_fcc, - gst_riff_strh *strh, - gst_riff_strf_vids *strf, - GstAviDemux *avi_demux) +static gst_avi_index_entry * +gst_avi_demux_index_next (GstAviDemux *avi, + gint stream_nr, + gint start, + guint32 flags) { - GstCaps *caps = NULL; - gchar *codecname = NULL; - gint width = -1, height = -1; - gdouble framerate = 0.; - - if (strf != NULL) { - width = GUINT32_FROM_LE (strf->width); - height = GUINT32_FROM_LE (strf->height); - } - if (strh != NULL) { - framerate = 1. * GUINT32_FROM_LE (strh->rate) / - GUINT32_FROM_LE (strh->scale); /* fps */ - } - - switch (codec_fcc) { - case GST_MAKE_FOURCC('I','4','2','0'): - case GST_MAKE_FOURCC('Y','U','Y','2'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_raw", - "video/x-raw-yuv", - "format", GST_PROPS_FOURCC (codec_fcc) - ); - codecname = g_strdup_printf("Raw Video (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('M','J','P','G'): /* YUY2 MJPEG */ - case GST_MAKE_FOURCC('J','P','E','G'): /* generic (mostly RGB) MJPEG */ - case GST_MAKE_FOURCC('P','I','X','L'): /* Miro/Pinnacle fourccs */ - case GST_MAKE_FOURCC('V','I','X','L'): /* Miro/Pinnacle fourccs */ - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_jpeg", - "video/x-jpeg", - NULL - ); - codecname = g_strdup_printf("Motion-JPEG (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('H','F','Y','U'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_hfyu", - "video/x-huffyuv", - NULL - ); - codecname = g_strdup_printf("HuffYUV (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('M','P','E','G'): - case GST_MAKE_FOURCC('M','P','G','I'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_mpeg", - "video/mpeg", - "systemstream", GST_PROPS_BOOLEAN (FALSE), - "mpegversion", GST_PROPS_BOOLEAN (1) - ); - codecname = g_strdup_printf("MPEG-1 (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('H','2','6','3'): - case GST_MAKE_FOURCC('i','2','6','3'): - case GST_MAKE_FOURCC('L','2','6','3'): - case GST_MAKE_FOURCC('M','2','6','3'): - case GST_MAKE_FOURCC('V','D','O','W'): - case GST_MAKE_FOURCC('V','I','V','O'): - case GST_MAKE_FOURCC('x','2','6','3'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_263", - "video/x-h263", - NULL - ); - codecname = g_strdup_printf("H263-compatible (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('D','I','V','3'): - case GST_MAKE_FOURCC('D','I','V','4'): - case GST_MAKE_FOURCC('D','I','V','5'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_divx3", - "video/x-divx", - "divxversion", GST_PROPS_INT(3) - ); - codecname = g_strdup_printf("DivX-3.x (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('d','i','v','x'): - case GST_MAKE_FOURCC('D','I','V','X'): - case GST_MAKE_FOURCC('D','X','5','0'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_divx5", - "video/x-divx", - "divxversion", GST_PROPS_INT(5) - ); - codecname = g_strdup_printf("DivX 4.x/5.x (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('X','V','I','D'): - case GST_MAKE_FOURCC('x','v','i','d'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src", - "video/x-xvid", - NULL - ); - codecname = g_strdup_printf("XviD (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('M','P','G','4'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src", - "video/x-msmpeg", - "msmpegversion", GST_PROPS_INT (41) - ); - codecname = g_strdup_printf("MS MPEG-4.1 (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('M','P','4','2'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src", - "video/x-msmpeg", - "msmpegversion", GST_PROPS_INT (42) - ); - codecname = g_strdup_printf("MS MPEG-4.2 (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('M','P','4','3'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src", - "video/x-msmpeg", - "msmpegversion", GST_PROPS_INT (43) - ); - codecname = g_strdup_printf("MS MPEG-4.3 (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('3','I','V','1'): - case GST_MAKE_FOURCC('3','I','V','2'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_3ivx", - "video/x-3ivx", - NULL - ); - codecname = g_strdup_printf("3ivX (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('D','V','S','D'): - case GST_MAKE_FOURCC('d','v','s','d'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src", - "video/x-dv", - "systemstream", GST_PROPS_BOOLEAN (FALSE) - ); - codecname = g_strdup_printf("Digital Video type 2 (" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('W','M','V','1'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_wmv1", - "video/x-wmv", - "wmvversion", GST_PROPS_INT (1) - ); - codecname = g_strdup_printf("Windows Media Format 1 (" - GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - case GST_MAKE_FOURCC('W','M','V','2'): - caps = GST_AVI_VID_CAPS_NEW ( - "avidemux_video_src_wmv2", - "video/x-wmv", - "wmvversion", GST_PROPS_INT (2) - ); - codecname = g_strdup_printf("Windows Media Format 2 (" - GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS(codec_fcc)); - break; - - default: - g_warning ("avidemux: unkown video format " GST_FOURCC_FORMAT, - GST_FOURCC_ARGS(codec_fcc)); - break; - } - - /* set video codec info on streaminfo caps */ - if (avi_demux != NULL && codecname != NULL) { - GstPropsEntry *entry; - entry = gst_props_entry_new("videocodec", - GST_PROPS_STRING(codecname)); - gst_props_add_entry(avi_demux->streaminfo->properties, entry); - } - if (codecname != NULL) { - g_free(codecname); - } - - return caps; -} - -static void -gst_avi_demux_strf_vids (GstAviDemux *avi_demux) -{ - gst_riff_strf_vids *strf; - gst_riff_strh *strh; - guint8 *strfdata; - GstPad *srcpad; - GstCaps *caps = NULL; - avi_stream_context *stream; - GstByteStream *bs = avi_demux->bs; - guint32 got_bytes; - gchar *padname; - - got_bytes = gst_bytestream_peek_bytes (bs, &strfdata, sizeof (gst_riff_strf_vids)); - strf = (gst_riff_strf_vids *) strfdata; - if (got_bytes != sizeof (gst_riff_strf_vids)) - return; - - padname = g_strdup_printf ("video_%02d", avi_demux->num_v_streams); - srcpad = gst_pad_new_from_template (videosrctempl, padname); - g_free (padname); - - /* let's try some gstreamer-like mime-type caps */ - strh = &avi_demux->stream[avi_demux->num_streams].strh; - caps = gst_avi_demux_video_caps (GUINT32_FROM_LE(strf->compression), - strh, strf, avi_demux); - - if (caps != NULL) { - gst_pad_try_set_caps (srcpad, caps); - } - gst_pad_set_formats_function (srcpad, gst_avi_demux_get_src_formats); - gst_pad_set_event_mask_function (srcpad, gst_avi_demux_get_event_mask); - gst_pad_set_event_function (srcpad, gst_avi_demux_handle_src_event); - gst_pad_set_query_type_function (srcpad, gst_avi_demux_get_src_query_types); - gst_pad_set_query_function (srcpad, gst_avi_demux_handle_src_query); - gst_pad_set_convert_function (srcpad, gst_avi_demux_src_convert); - - stream = &avi_demux->stream[avi_demux->num_streams]; - stream->pad = srcpad; - gst_pad_set_element_private (srcpad, stream); - avi_demux->num_streams++; - avi_demux->num_v_streams++; - - gst_element_add_pad (GST_ELEMENT (avi_demux), srcpad); -} - -#ifdef G_HAVE_ISO_VARARGS - -#define GST_AVI_AUD_CAPS_NEW(name, mimetype, ...) \ - (strf != NULL) ? \ - GST_CAPS_NEW (name, \ - mimetype, \ - "rate", GST_PROPS_INT (rate), \ - "channels", GST_PROPS_INT (channels), \ - __VA_ARGS__) \ - : \ - GST_CAPS_NEW (name, \ - mimetype, \ - "rate", GST_PROPS_INT_RANGE (8000, 96000), \ - "channels", GST_PROPS_INT_RANGE (1, 2), \ - __VA_ARGS__) - - -#elif defined(G_HAVE_GNUC_VARARGS) - -#define GST_AVI_AUD_CAPS_NEW(name, mimetype, props...) \ - (strf != NULL) ? \ - GST_CAPS_NEW (name, \ - mimetype, \ - "rate", GST_PROPS_INT (rate), \ - "channels", GST_PROPS_INT (channels), \ - ##props) \ - : \ - GST_CAPS_NEW (name, \ - mimetype, \ - "rate", GST_PROPS_INT_RANGE (8000, 96000), \ - "channels", GST_PROPS_INT_RANGE (1, 2), \ - ##props) -#endif - -static GstCaps * -gst_avi_demux_audio_caps (guint16 codec_id, - gst_riff_strf_auds *strf, - GstAviDemux *avi_demux) -{ - GstCaps *caps = NULL; - gchar *codecname = NULL; - gint rate = -1, channels = -1; - - if (strf != NULL) { - rate = GUINT32_FROM_LE (strf->rate); - channels = GUINT16_FROM_LE (strf->channels); - } + gint i; + gst_avi_index_entry *entry = NULL; - switch (codec_id) { - case GST_RIFF_WAVE_FORMAT_MPEGL3: /* mp3 */ - caps = GST_AVI_AUD_CAPS_NEW ("avi_demux_audio_src_mp3", - "audio/mpeg", - "layer", GST_PROPS_INT (3)); - codecname = g_strdup_printf("MPEG-1 layer 3 audio (0x%04x)", - codec_id); - break; + for (i = start; i < avi->index_size; i++) { + entry = &avi->index_entries[i]; - case GST_RIFF_WAVE_FORMAT_MPEGL12: /* mp1 or mp2 */ - caps = GST_AVI_AUD_CAPS_NEW ("avi_demux_audio_src_mp12", - "audio/mpeg", - "layer", GST_PROPS_INT (2)); - codecname = g_strdup_printf("MPEG-1 layer 1/2 audio (0x%04x)", - codec_id); + if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) { break; - - case GST_RIFF_WAVE_FORMAT_PCM: /* PCM/wav */ { - GstPropsEntry *width = NULL, *depth = NULL, *signedness = NULL; - - if (strf != NULL) { - gint ba = GUINT16_FROM_LE (strf->blockalign); - gint ch = GUINT16_FROM_LE (strf->channels); - gint ws = GUINT16_FROM_LE (strf->size); - - width = gst_props_entry_new ("width", - GST_PROPS_INT (ba * 8 / ch)); - depth = gst_props_entry_new ("depth", - GST_PROPS_INT (ws)); - signedness = gst_props_entry_new ("signed", - GST_PROPS_BOOLEAN (ws != 8)); - } else { - signedness = gst_props_entry_new ("signed", - GST_PROPS_LIST ( - GST_PROPS_BOOLEAN (TRUE), - GST_PROPS_BOOLEAN (FALSE))); - width = gst_props_entry_new ("width", - GST_PROPS_LIST ( - GST_PROPS_INT (8), - GST_PROPS_INT (16))); - depth = gst_props_entry_new ("depth", - GST_PROPS_LIST ( - GST_PROPS_INT (8), - GST_PROPS_INT (16))); - } - - caps = GST_AVI_AUD_CAPS_NEW ("avi_demux_audio_src_pcm", - "audio/x-raw-int", - "endianness", - GST_PROPS_INT (G_LITTLE_ENDIAN)); - gst_props_add_entry (caps->properties, width); - gst_props_add_entry (caps->properties, depth); - gst_props_add_entry (caps->properties, signedness); - - codecname = g_strdup_printf("Raw PCM/WAV (0x%04x)", - codec_id); } - break; - - case GST_RIFF_WAVE_FORMAT_MULAW: - if (strf != NULL && strf->size != 8) { - g_warning ("invalid depth (%d) of mulaw audio, overwriting.", - strf->size); - } - caps = GST_AVI_AUD_CAPS_NEW ("avidemux_audio_src", - "audio/x-mulaw", - NULL); - codecname = g_strdup_printf("A-law encoded (0x%04x)", - codec_id); - break; - - case GST_RIFF_WAVE_FORMAT_ALAW: - if (strf != NULL && strf->size != 8) { - g_warning ("invalid depth (%d) of alaw audio, overwriting.", - strf->size); - } - caps = GST_AVI_AUD_CAPS_NEW ("avidemux_audio_src", - "audio/x-alaw", - NULL); - codecname = g_strdup_printf("A-law encoded (0x%04x)", - codec_id); - break; - - case GST_RIFF_WAVE_FORMAT_VORBIS1: /* ogg/vorbis mode 1 */ - case GST_RIFF_WAVE_FORMAT_VORBIS2: /* ogg/vorbis mode 2 */ - case GST_RIFF_WAVE_FORMAT_VORBIS3: /* ogg/vorbis mode 3 */ - case GST_RIFF_WAVE_FORMAT_VORBIS1PLUS: /* ogg/vorbis mode 1+ */ - case GST_RIFF_WAVE_FORMAT_VORBIS2PLUS: /* ogg/vorbis mode 2+ */ - case GST_RIFF_WAVE_FORMAT_VORBIS3PLUS: /* ogg/vorbis mode 3+ */ - caps = GST_AVI_AUD_CAPS_NEW ("asf_demux_audio_src_vorbis", - "audio/x-vorbis", - NULL); - codecname = g_strdup_printf("Vorbis (0x%04x)", - codec_id); - break; - - case GST_RIFF_WAVE_FORMAT_A52: - caps = GST_AVI_AUD_CAPS_NEW ("asf_demux_audio_src_ac3", - "audio/x-ac3", - NULL); - codecname = g_strdup_printf("AC-3 (0x%04x)", - codec_id); - break; - - default: - g_warning ("avidemux: unkown audio format 0x%04x", - codec_id); - break; - } - - if (avi_demux != NULL && codecname != NULL) { - /* set audio codec in streaminfo */ - GstPropsEntry *entry; - entry = gst_props_entry_new("audiocodec", - GST_PROPS_STRING(codecname)); - gst_props_add_entry(avi_demux->streaminfo->properties, entry); - } - if (codecname != NULL) { - g_free (codecname); - } - - return caps; -} - -static void -gst_avi_demux_strf_auds (GstAviDemux *avi_demux) -{ - gst_riff_strf_auds *strf; - guint8 *strfdata; - GstPad *srcpad; - GstCaps *caps = NULL; - avi_stream_context *stream; - GstByteStream *bs = avi_demux->bs; - guint32 got_bytes; - gchar *padname; - - got_bytes = gst_bytestream_peek_bytes (bs, &strfdata, sizeof (gst_riff_strf_auds)); - strf = (gst_riff_strf_auds *) strfdata; - if (got_bytes != sizeof (gst_riff_strf_auds)) - return; - - GST_INFO ( "gst_avi_demux: strf tag found in context auds"); - GST_INFO ( "gst_avi_demux: format %d", GUINT16_FROM_LE (strf->format)); - GST_INFO ( "gst_avi_demux: channels %d", GUINT16_FROM_LE (strf->channels)); - GST_INFO ( "gst_avi_demux: rate %d", GUINT32_FROM_LE (strf->rate)); - GST_INFO ( "gst_avi_demux: av_bps %d", GUINT32_FROM_LE (strf->av_bps)); - GST_INFO ( "gst_avi_demux: blockalign %d", GUINT16_FROM_LE (strf->blockalign)); - GST_INFO ( "gst_avi_demux: size %d", GUINT16_FROM_LE (strf->size)); - - padname = g_strdup_printf ("audio_%02d", - avi_demux->num_a_streams); - srcpad = gst_pad_new_from_template (audiosrctempl, padname); - g_free (padname); - - caps = gst_avi_demux_audio_caps (GUINT16_FROM_LE (strf->format), - strf, avi_demux); - - if (caps != NULL) { - gst_pad_try_set_caps(srcpad, caps); } - gst_pad_set_formats_function (srcpad, gst_avi_demux_get_src_formats); - gst_pad_set_event_mask_function (srcpad, gst_avi_demux_get_event_mask); - gst_pad_set_event_function (srcpad, gst_avi_demux_handle_src_event); - gst_pad_set_query_type_function (srcpad, gst_avi_demux_get_src_query_types); - gst_pad_set_query_function (srcpad, gst_avi_demux_handle_src_query); - gst_pad_set_convert_function (srcpad, gst_avi_demux_src_convert); - - stream = &avi_demux->stream[avi_demux->num_streams]; - stream->pad = srcpad; - gst_pad_set_element_private (srcpad, stream); - avi_demux->num_streams++; - avi_demux->num_a_streams++; - - gst_element_add_pad (GST_ELEMENT (avi_demux), srcpad); -} - -static GstCaps * -gst_avi_demux_iavs_caps (void) -{ - return GST_CAPS_NEW ("avi_type_dv", - "video/x-dv", - "systemstream", GST_PROPS_BOOLEAN (TRUE)); -} -static void -gst_avi_demux_strf_iavs (GstAviDemux *avi_demux) -{ - gst_riff_strf_iavs *strf; - guint8 *strfdata; - GstPad *srcpad; - GstCaps *caps = NULL; - avi_stream_context *stream; - GstByteStream *bs = avi_demux->bs; - guint32 got_bytes; - gchar *padname; - GstPropsEntry *entry; - - got_bytes = gst_bytestream_peek_bytes (bs, &strfdata, sizeof (gst_riff_strf_iavs)); - strf = (gst_riff_strf_iavs *) strfdata; - if (got_bytes != sizeof (gst_riff_strf_iavs)) - return; - - GST_INFO ( "gst_avi_demux: strf tag found in context iavs"); - GST_INFO ( "gst_avi_demux: DVAAuxSrc %08x", GUINT32_FROM_LE (strf->DVAAuxSrc)); - GST_INFO ( "gst_avi_demux: DVAAuxCtl %08x", GUINT32_FROM_LE (strf->DVAAuxCtl)); - GST_INFO ( "gst_avi_demux: DVAAuxSrc1 %08x", GUINT32_FROM_LE (strf->DVAAuxSrc1)); - GST_INFO ( "gst_avi_demux: DVAAuxCtl1 %08x", GUINT32_FROM_LE (strf->DVAAuxCtl1)); - GST_INFO ( "gst_avi_demux: DVVAuxSrc %08x", GUINT32_FROM_LE (strf->DVVAuxSrc)); - GST_INFO ( "gst_avi_demux: DVVAuxCtl %08x", GUINT32_FROM_LE (strf->DVVAuxCtl)); - GST_INFO ( "gst_avi_demux: DVReserved1 %08x", GUINT32_FROM_LE (strf->DVReserved1)); - GST_INFO ( "gst_avi_demux: DVReserved2 %08x", GUINT32_FROM_LE (strf->DVReserved2)); - - padname = g_strdup_printf ("video_%02d", - avi_demux->num_v_streams); - srcpad = gst_pad_new_from_template (videosrctempl, padname); - g_free (padname); - - caps = gst_avi_demux_iavs_caps (); - entry = gst_props_entry_new("videocodec", - GST_PROPS_STRING("Digital Video type 1")); - gst_props_add_entry(avi_demux->streaminfo->properties, entry); - - if (caps != NULL) { - gst_pad_try_set_caps(srcpad, caps); - } - gst_pad_set_formats_function (srcpad, gst_avi_demux_get_src_formats); - gst_pad_set_event_mask_function (srcpad, gst_avi_demux_get_event_mask); - gst_pad_set_event_function (srcpad, gst_avi_demux_handle_src_event); - gst_pad_set_query_type_function (srcpad, gst_avi_demux_get_src_query_types); - gst_pad_set_query_function (srcpad, gst_avi_demux_handle_src_query); - gst_pad_set_convert_function (srcpad, gst_avi_demux_src_convert); - - stream = &avi_demux->stream[avi_demux->num_streams]; - stream->pad = srcpad; - gst_pad_set_element_private (srcpad, stream); - avi_demux->num_streams++; - avi_demux->num_v_streams++; - - gst_element_add_pad (GST_ELEMENT (avi_demux), srcpad); -} - -static void -gst_avi_debug_entry (const gchar *prefix, gst_avi_index_entry *entry) -{ - GST_DEBUG ("%s: %05d %d %08llx %05d %14" G_GINT64_FORMAT " %08x %08x (%d) %08x", - prefix, entry->index_nr, entry->stream_nr, - (unsigned long long)entry->bytes_before, - entry->frames_before, entry->ts, entry->flags, entry->offset, - entry->offset, entry->size); + return entry; } -static void -gst_avi_demux_parse_index (GstAviDemux *avi_demux, - gulong filepos, gulong offset) +static gst_avi_index_entry * +gst_avi_demux_index_entry_for_time (GstAviDemux *avi, + gint stream_nr, + guint64 time, + guint32 flags) { - GstBuffer *buf; - gulong index_size; - guint32 got_bytes; + gst_avi_index_entry *entry = NULL, *last_entry = NULL; gint i; - gst_riff_index_entry *entry; - guint32 id; - if (!gst_bytestream_seek (avi_demux->bs, filepos + offset, GST_SEEK_METHOD_SET)) { - GST_INFO ( "avidemux: could not seek to index"); - return; - } + i = -1; do { - guint32 remaining; - GstEvent *event; - - got_bytes = gst_bytestream_read (avi_demux->bs, &buf, 8); - if (got_bytes == 8) - break; - - gst_bytestream_get_status (avi_demux->bs, &remaining, &event); - gst_event_unref (event); - } while (TRUE); - - if (GST_BUFFER_OFFSET (buf) != filepos + offset || GST_BUFFER_SIZE (buf) != 8) { - GST_INFO ( "avidemux: could not get index, got %" G_GINT64_FORMAT " %d, expected %ld", - GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf), filepos + offset); - goto end; - } - - id = GUINT32_FROM_LE (*(guint32 *)GST_BUFFER_DATA (buf)); - - if (id != GST_RIFF_TAG_idx1) { - GST_INFO ( "avidemux: no index found"); - goto end; - } - - index_size = GUINT32_FROM_LE(*(guint32 *)(GST_BUFFER_DATA (buf) + 4)); - gst_buffer_unref (buf); - - gst_bytestream_size_hint (avi_demux->bs, index_size); - - got_bytes = gst_bytestream_read (avi_demux->bs, &buf, index_size); - if (got_bytes < index_size) { - GST_INFO ( "avidemux: error reading index"); - goto end; - } - - avi_demux->index_size = index_size/sizeof(gst_riff_index_entry); - GST_INFO ( "avidemux: index size %lu", avi_demux->index_size); - - avi_demux->index_entries = g_malloc (avi_demux->index_size * sizeof (gst_avi_index_entry)); - - entry = (gst_riff_index_entry *) GST_BUFFER_DATA (buf); - - for (i = 0; i < avi_demux->index_size; i++) { - avi_stream_context *stream; - gint stream_nr; - gst_avi_index_entry *target = &avi_demux->index_entries[i]; - GstFormat format; - guint32 id; - - id = GUINT32_FROM_LE (entry[i].id); - stream_nr = CHUNKID_TO_STREAMNR (id); - if (stream_nr > avi_demux->num_streams || stream_nr < 0) { - avi_demux->index_entries[i].stream_nr = -1; - continue; - } - - target->stream_nr = stream_nr; - stream = &avi_demux->stream[stream_nr]; - - target->index_nr = i; - target->flags = GUINT32_FROM_LE (entry[i].flags); - target->size = GUINT32_FROM_LE (entry[i].size); - target->offset = GUINT32_FROM_LE (entry[i].offset); - - /* figure out if the index is 0 based or relative to the MOVI start */ - if (i == 0) { - if (target->offset < filepos) - avi_demux->index_offset = filepos - 4; - else - avi_demux->index_offset = 0; - } + entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags); + if (!entry) + return NULL; - target->bytes_before = stream->total_bytes; - target->frames_before = stream->total_frames; + i = entry->index_nr; - format = GST_FORMAT_TIME; - if (stream->strh.type == GST_RIFF_FCC_auds) { - /* all audio frames are keyframes */ - target->flags |= GST_RIFF_IF_KEYFRAME; - } - - /* constant rate stream */ - if (stream->strh.samplesize && stream->strh.type == GST_RIFF_FCC_auds) { - gst_pad_convert (stream->pad, GST_FORMAT_BYTES, stream->total_bytes, - &format, &target->ts); - } - /* VBR stream */ - else { - gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, stream->total_frames, - &format, &target->ts); + if (entry->ts <= time) { + last_entry = entry; } - gst_avi_debug_entry ("index", target); - - stream->total_bytes += target->size; - stream->total_frames++; - } - for (i = 0; i < avi_demux->num_streams; i++) { - avi_stream_context *stream; - - stream = &avi_demux->stream[i]; - GST_DEBUG ("stream %i: %d frames, %" G_GINT64_FORMAT " bytes", - i, stream->total_frames, stream->total_bytes); - } - gst_buffer_unref (buf); + } while (entry->ts <= time); -end: - GST_DEBUG ("index offset at %08lx", filepos); - - if (!gst_bytestream_seek (avi_demux->bs, filepos, GST_SEEK_METHOD_SET)) { - GST_INFO ( "avidemux: could not seek back to movi"); - return; - } + return last_entry; } -static gst_avi_index_entry* -gst_avi_demux_index_next (GstAviDemux *avi_demux, gint stream_nr, gint start, guint32 flags) +static gst_avi_index_entry * +gst_avi_demux_index_entry_for_byte (GstAviDemux *avi, + gint stream_nr, + guint64 byte, + guint32 flags) { + gst_avi_index_entry *entry = NULL, *last_entry = NULL; gint i; - gst_avi_index_entry *entry = NULL; - for (i = start; i < avi_demux->index_size; i++) { - entry = &avi_demux->index_entries[i]; + i = -1; + do { + entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags); + if (!entry) + return NULL; - if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) { - break; + i = entry->index_nr; + + if (entry->bytes_before <= byte) { + last_entry = entry; } - } + } while (entry->bytes_before <= byte); - return entry; + return last_entry; } -static gst_avi_index_entry* -gst_avi_demux_index_entry_for_time (GstAviDemux *avi_demux, gint stream_nr, guint64 time, guint32 flags) +static gst_avi_index_entry * +gst_avi_demux_index_entry_for_frame (GstAviDemux *avi, + gint stream_nr, + guint32 frame, + guint32 flags) { gst_avi_index_entry *entry = NULL, *last_entry = NULL; gint i; i = -1; do { - entry = gst_avi_demux_index_next (avi_demux, stream_nr, i + 1, flags); + entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags); if (!entry) return NULL; i = entry->index_nr; - if (entry->ts <= time) { + if (entry->frames_before <= frame) { last_entry = entry; } - } - while (entry->ts <= time); + } while (entry->frames_before <= frame); return last_entry; } -static const GstFormat* +static const GstFormat * gst_avi_demux_get_src_formats (GstPad *pad) { avi_stream_context *stream = gst_pad_get_element_private (pad); @@ -1307,28 +358,36 @@ gst_avi_demux_get_src_formats (GstPad *pad) 0 }; - return (stream->strh.type == GST_RIFF_FCC_auds ? src_a_formats : src_v_formats); + return (stream->strh->type == GST_RIFF_FCC_auds ? + src_a_formats : src_v_formats); } static gboolean -gst_avi_demux_src_convert (GstPad *pad, GstFormat src_format, gint64 src_value, - GstFormat *dest_format, gint64 *dest_value) +gst_avi_demux_src_convert (GstPad *pad, + GstFormat src_format, + gint64 src_value, + GstFormat *dest_format, + gint64 *dest_value) { gboolean res = TRUE; + /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));*/ avi_stream_context *stream = gst_pad_get_element_private (pad); - if (stream->strh.type != GST_RIFF_FCC_auds && - (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) + if (stream->strh->type != GST_RIFF_FCC_auds && + (src_format == GST_FORMAT_BYTES || + *dest_format == GST_FORMAT_BYTES)) return FALSE; switch (src_format) { case GST_FORMAT_TIME: switch (*dest_format) { case GST_FORMAT_BYTES: - *dest_value = src_value * stream->strh.rate / (stream->strh.scale * GST_SECOND); + *dest_value = src_value * stream->strh->rate / + (stream->strh->scale * GST_SECOND); break; case GST_FORMAT_DEFAULT: - *dest_value = 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; @@ -1338,7 +397,7 @@ gst_avi_demux_src_convert (GstPad *pad, GstFormat src_format, gint64 src_value, case GST_FORMAT_BYTES: switch (*dest_format) { case GST_FORMAT_TIME: - *dest_value = ((gfloat)src_value) * GST_SECOND / stream->strh.rate; + *dest_value = ((gfloat) src_value) * GST_SECOND / stream->strh->rate; break; default: res = FALSE; @@ -1348,7 +407,8 @@ gst_avi_demux_src_convert (GstPad *pad, GstFormat src_format, gint64 src_value, case GST_FORMAT_DEFAULT: switch (*dest_format) { case GST_FORMAT_TIME: - *dest_value = ((((gfloat)src_value) * stream->strh.scale) / stream->strh.rate) * GST_SECOND; + *dest_value = ((((gfloat) src_value) * stream->strh->scale) / + stream->strh->rate) * GST_SECOND; break; default: res = FALSE; @@ -1362,7 +422,7 @@ gst_avi_demux_src_convert (GstPad *pad, GstFormat src_format, gint64 src_value, return res; } -static const GstQueryType* +static const GstQueryType * gst_avi_demux_get_src_query_types (GstPad *pad) { static const GstQueryType src_types[] = { @@ -1375,31 +435,34 @@ 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, + GstQueryType type, + GstFormat *format, + gint64 *value) { gboolean res = TRUE; - //GstAviDemux *avi_demux = GST_AVI_DEMUX (gst_pad_get_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; + *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) { + 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) - *value = stream->strh.length * stream->strh.samplesize; - else if (stream->strh.type == GST_RIFF_FCC_vids) - *value = stream->strh.length; + if (stream->strh->type == GST_RIFF_FCC_auds) + *value = stream->strh->length * stream->strh->samplesize; + else if (stream->strh->type == GST_RIFF_FCC_vids) + *value = stream->strh->length; else res = FALSE; break; @@ -1411,20 +474,23 @@ gst_avi_demux_handle_src_query (GstPad *pad, GstQueryType type, case GST_QUERY_POSITION: switch (*format) { case GST_FORMAT_TIME: - if (stream->strh.samplesize && stream->strh.type == GST_RIFF_FCC_auds) { - //*value = (((gfloat)stream->current_byte) * stream->strh.scale / stream->strh.rate) * GST_SECOND; - *value = ((gfloat)stream->current_byte) * GST_SECOND / stream->strh.rate; + if (stream->strh->samplesize && + stream->strh->type == GST_RIFF_FCC_auds) { + *value = ((gfloat) stream->current_byte) * GST_SECOND / + stream->strh->rate; } else { - *value = (((gfloat)stream->current_frame) * stream->strh.scale / stream->strh.rate) * GST_SECOND; + *value = (((gfloat) stream->current_frame) * stream->strh->scale / + stream->strh->rate) * GST_SECOND; } 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; + if (stream->strh->samplesize && + stream->strh->type == GST_RIFF_FCC_auds) + *value = stream->current_byte * stream->strh->samplesize; else *value = stream->current_frame; break; @@ -1442,22 +508,22 @@ gst_avi_demux_handle_src_query (GstPad *pad, GstQueryType type, } static gint32 -gst_avi_demux_sync_streams (GstAviDemux *avi_demux, guint64 time) +gst_avi_demux_sync_streams (GstAviDemux *avi, + guint64 time) { gint i; guint32 min_index = G_MAXUINT; avi_stream_context *stream; gst_avi_index_entry *entry; - for (i = 0; i < avi_demux->num_streams; i++) { - stream = &avi_demux->stream[i]; + for (i = 0; i < avi->num_streams; i++) { + stream = &avi->stream[i]; GST_DEBUG ("finding %d for time %" G_GINT64_FORMAT, i, time); - entry = gst_avi_demux_index_entry_for_time (avi_demux, stream->num, time, GST_RIFF_IF_KEYFRAME); + entry = gst_avi_demux_index_entry_for_time (avi, stream->num, time, + GST_RIFF_IF_KEYFRAME); if (entry) { - gst_avi_debug_entry ("sync entry", entry); - min_index = MIN (entry->index_nr, min_index); } } @@ -1465,15 +531,16 @@ gst_avi_demux_sync_streams (GstAviDemux *avi_demux, guint64 time) /* now we know the entry we need to sync on. calculate number of frames to * skip fro there on and the stream stats */ - for (i = 0; i < avi_demux->num_streams; i++) { + for (i = 0; i < avi->num_streams; i++) { gst_avi_index_entry *next_entry; - stream = &avi_demux->stream[i]; + stream = &avi->stream[i]; /* next entry */ - next_entry = gst_avi_demux_index_next (avi_demux, stream->num, min_index, 0); + next_entry = gst_avi_demux_index_next (avi, stream->num, + min_index, 0); /* next entry with keyframe */ - entry = gst_avi_demux_index_next (avi_demux, stream->num, min_index, GST_RIFF_IF_KEYFRAME); - gst_avi_debug_entry ("final sync", entry); + entry = gst_avi_demux_index_next (avi, stream->num, min_index, + GST_RIFF_IF_KEYFRAME); stream->current_byte = next_entry->bytes_before; stream->current_frame = next_entry->frames_before; @@ -1481,13 +548,15 @@ gst_avi_demux_sync_streams (GstAviDemux *avi_demux, guint64 time) GST_DEBUG ("%d skip %d", stream->num, stream->skip); } + GST_DEBUG ("final index at %d", min_index); return min_index; } static gboolean -gst_avi_demux_send_event (GstElement *element, GstEvent *event) +gst_avi_demux_send_event (GstElement *element, + GstEvent *event) { const GList *pads; @@ -1502,6 +571,7 @@ gst_avi_demux_send_event (GstElement *element, GstEvent *event) gst_event_ref (event); if (gst_avi_demux_handle_src_event (pad, event)) { gst_event_unref (event); + return TRUE; } } @@ -1510,15 +580,15 @@ gst_avi_demux_send_event (GstElement *element, GstEvent *event) } gst_event_unref (event); + return FALSE; } -static const GstEventMask* +static const GstEventMask * gst_avi_demux_get_event_mask (GstPad *pad) { static const GstEventMask masks[] = { { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT }, - { GST_EVENT_SEEK_SEGMENT, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT }, { 0, } }; @@ -1526,54 +596,65 @@ gst_avi_demux_get_event_mask (GstPad *pad) } static gboolean -gst_avi_demux_handle_src_event (GstPad *pad, GstEvent *event) +gst_avi_demux_handle_src_event (GstPad *pad, + GstEvent *event) { gboolean res = TRUE; - GstAviDemux *avi_demux = GST_AVI_DEMUX (gst_pad_get_parent (pad)); + GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); avi_stream_context *stream; stream = gst_pad_get_element_private (pad); switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK_SEGMENT: - stream->end_pos = GST_EVENT_SEEK_ENDOFFSET (event); case GST_EVENT_SEEK: - GST_DEBUG ("seek format %d, %08x", GST_EVENT_SEEK_FORMAT (event), stream->strh.type); + GST_DEBUG ("seek format %d, %08x", GST_EVENT_SEEK_FORMAT (event), + stream->strh->type); + switch (GST_EVENT_SEEK_FORMAT (event)) { case GST_FORMAT_BYTES: case GST_FORMAT_DEFAULT: - break; - case GST_FORMAT_TIME: - { - gst_avi_index_entry *seek_entry, *entry; + case GST_FORMAT_TIME: { + gst_avi_index_entry *seek_entry, *entry = NULL; gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event); guint32 flags; guint64 min_index; - /* no seek on audio yet */ - if (stream->strh.type == GST_RIFF_FCC_auds) { + if (stream->strh->type == GST_RIFF_FCC_auds) { res = FALSE; goto done; } GST_DEBUG ("seeking to %" G_GINT64_FORMAT, desired_offset); 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, + desired_offset, + flags); + break; + case GST_FORMAT_DEFAULT: + entry = gst_avi_demux_index_entry_for_frame (avi, stream->num, + desired_offset, + flags); + break; + case GST_FORMAT_TIME: + entry = gst_avi_demux_index_entry_for_time (avi, stream->num, + desired_offset, + flags); + break; + } - entry = gst_avi_demux_index_entry_for_time (avi_demux, stream->num, desired_offset, GST_RIFF_IF_KEYFRAME); if (entry) { - desired_offset = entry->ts; - min_index = gst_avi_demux_sync_streams (avi_demux, desired_offset); - seek_entry = &avi_demux->index_entries[min_index]; - - gst_avi_debug_entry ("syncing to entry", seek_entry); - - avi_demux->seek_offset = seek_entry->offset + avi_demux->index_offset; - avi_demux->seek_pending = TRUE; - avi_demux->last_seek = seek_entry->ts; - } - else { - GST_DEBUG ("no index entry found for time %" G_GINT64_FORMAT, desired_offset); + min_index = gst_avi_demux_sync_streams (avi, entry->ts); + seek_entry = &avi->index_entries[min_index]; + + avi->seek_offset = seek_entry->offset + avi->index_offset; + avi->last_seek = entry->ts; + } else { + GST_DEBUG ("no index entry found for format=%d value=%" + G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), + desired_offset); res = FALSE; } break; @@ -1594,408 +675,851 @@ done: return res; } -static gboolean -gst_avi_demux_handle_sink_event (GstAviDemux *avi_demux) +/* + * "Open" a RIFF file. + */ + +gboolean +gst_avi_demux_stream_init (GstAviDemux *avi) { - guint32 remaining; - GstEvent *event; - GstEventType type; - gboolean res = TRUE; - - gst_bytestream_get_status (avi_demux->bs, &remaining, &event); + GstRiffRead *riff = GST_RIFF_READ (avi); + guint32 doctype; - type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; - GST_DEBUG ("avidemux: event %p %d", event, type); + if (!gst_riff_read_header (riff, &doctype)) + return FALSE; + if (doctype != GST_RIFF_RIFF_AVI) { + gst_element_error (GST_ELEMENT (avi), "Not an AVI file"); + return FALSE; + } - switch (type) { - case GST_EVENT_EOS: - gst_bytestream_flush (avi_demux->bs, remaining); - gst_pad_event_default (avi_demux->sinkpad, event); - res = FALSE; - goto done; - case GST_EVENT_FLUSH: - g_warning ("flush event"); + return TRUE; +} + +/* + * Read 'avih' header. + */ + +gboolean +gst_avi_demux_stream_avih (GstAviDemux *avi, + guint32 *flags, + guint32 *streams) +{ + GstRiffRead *riff = GST_RIFF_READ (avi); + guint32 tag; + GstBuffer *buf; + 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), sizeof (gst_riff_avih)); + gst_buffer_unref (buf); + return FALSE; + } + + avih = (gst_riff_avih *) GST_BUFFER_DATA (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_buffer_unref (buf); + + return TRUE; +} + +/* + * Add a stream. + */ + +static gboolean +gst_avi_demux_add_stream (GstAviDemux *avi) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (avi); + GstRiffRead *riff = GST_RIFF_READ (avi); + guint32 tag; + gst_riff_strh *strh; + gchar *name = NULL, *padname = NULL; + GstCaps *caps = NULL; + GstPadTemplate *templ = NULL; + GstPad *pad; + avi_stream_context *stream; + 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; + + /* 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 (GST_ELEMENT (avi), + "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 (riff, &strf.vids)) + return FALSE; + break; + case GST_RIFF_FCC_auds: + if (!gst_riff_read_strf_auds (riff, &strf.auds)) + return FALSE; + break; + case GST_RIFF_FCC_iavs: + if (!gst_riff_read_strf_iavs (riff, &strf.iavs)) + return FALSE; break; - case GST_EVENT_DISCONTINUOUS: - { - gint i; - GstEvent *discont; + default: + g_warning ("Unknown stream type " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (strh->type)); + goto skip_stream; + } - for (i = 0; i < avi_demux->num_streams; i++) { - avi_stream_context *stream = &avi_demux->stream[i]; + /* 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; + } - if (GST_PAD_IS_USABLE (stream->pad)) { - GST_DEBUG ("sending discont on %d %" G_GINT64_FORMAT " + %" G_GINT64_FORMAT " = %" G_GINT64_FORMAT, - i, avi_demux->last_seek, stream->delay, avi_demux->last_seek + stream->delay); + switch (tag) { + case GST_RIFF_TAG_strn: + if (name) + g_free (name); + if (!gst_riff_read_ascii (riff, &tag, &name)) + return FALSE; + break; - discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, - avi_demux->last_seek + stream->delay , NULL); + default: + GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header", + GST_FOURCC_ARGS (tag)); + /* fall-through */ + + case GST_RIFF_TAG_strd: /* what is this? */ + case GST_RIFF_TAG_JUNK: + if (!gst_riff_read_skip (riff)) + return FALSE; + break; + } - gst_pad_push (stream->pad, GST_DATA (discont)); - } - } + if (avi->level_up) { + avi->level_up--; break; } + } + + /* create stream name + pad */ + switch (strh->type) { + case GST_RIFF_FCC_vids: + padname = g_strdup_printf ("video_%02d", avi->num_v_streams); + templ = gst_element_class_get_pad_template (klass, "video_%02d"); + caps = gst_riff_create_video_caps (strf.vids->compression, strh, strf.vids); + g_free (strf.vids); + avi->num_v_streams++; + break; + case GST_RIFF_FCC_auds: + 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 (strf.auds->format, strh, strf.auds); + g_free (strf.auds); + avi->num_a_streams++; + break; + case GST_RIFF_FCC_iavs: + 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); + g_free (strf.iavs); + avi->num_v_streams++; + break; default: - g_warning ("unhandled event %d", type); + g_assert (0); + } + + /* set proper settings and add it */ + pad = gst_pad_new_from_template (templ, padname); + g_free (padname); + if (caps != NULL) + gst_pad_try_set_caps (pad, caps); + + 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); + 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); + gst_pad_set_convert_function (pad, gst_avi_demux_src_convert); + + stream = &avi->stream[avi->num_streams]; + stream->pad = pad; + stream->strh = strh; + stream->num = avi->num_streams; + stream->delay = 0LL; + stream->total_bytes = 0LL; + stream->total_frames = 0; + stream->current_frame = 0; + stream->current_byte = 0; + stream->current_entry = -1; + stream->skip = 0; + gst_pad_set_element_private (pad, stream); + avi->num_streams++; + + gst_element_add_pad (GST_ELEMENT (avi), 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 */ + avi->num_streams++; + + return TRUE; /* recoverable */ +} + +/* + * Read an openDML-2.0 extension header. + */ + +static gboolean +gst_avi_demux_stream_odml (GstAviDemux *avi) +{ + 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; + } + + switch (tag) { + case GST_RIFF_TAG_dmlh: { + gst_riff_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)", + GST_BUFFER_SIZE (buf), sizeof (gst_riff_dmlh)); + gst_buffer_unref (buf); + break; + } + dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (buf); + +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + dmlh->totalframes = GUINT32_FROM_LE (dmlh->totalframes); +#endif + + GST_INFO ("dmlh tag found:"); + GST_INFO (" totalframes: %u", dmlh->totalframes); + + avi->num_frames = dmlh->totalframes; + gst_buffer_unref (buf); + break; + } + + default: + GST_WARNING ("Unknown tag " 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 (avi->level_up) { + avi->level_up--; break; + } + } + + return TRUE; +} + +/* + * Seek to index, read it, seek back. + */ + +gboolean +gst_avi_demux_stream_index (GstAviDemux *avi) +{ + GstBuffer *buf = NULL; + guint i; + GstEvent *event; + GstRiffRead *riff = GST_RIFF_READ (avi); + guint64 pos_before, pos_after, length; + guint32 tag; + + /* 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 (!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) { + g_warning ("File said that it has an index, but there is no index data!"); + goto end; + } + + /* 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; + } + + /* read index */ + if (!gst_riff_read_data (riff, &tag, &buf)) + return FALSE; + + /* parse all entries */ + avi->index_size = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry); + avi->index_entries = g_malloc (avi->index_size * sizeof (gst_avi_index_entry)); + GST_INFO ("%u index entries", avi->index_size); + + for (i = 0; i < avi->index_size; i++) { + gst_riff_index_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->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 = &avi->index_entries[i]; + + stream_nr = CHUNKID_TO_STREAMNR (entry->id); + if (stream_nr >= avi->num_streams || stream_nr < 0) { + g_warning ("Index entry %d has invalid stream nr %d", + i, stream_nr); + target->stream_nr = -1; + continue; + } + target->stream_nr = stream_nr; + stream = &avi->stream[stream_nr]; + + target->index_nr = i; + target->flags = entry->flags; + target->size = entry->size; + target->offset = entry->offset; + + /* figure out if the index is 0 based or relative to the MOVI start */ + if (i == 0) { + if (target->offset < pos_before) + avi->index_offset = pos_before + 8; + else + avi->index_offset = 0; + } + + target->bytes_before = stream->total_bytes; + target->frames_before = stream->total_frames; + + format = GST_FORMAT_TIME; + if (stream->strh->type == GST_RIFF_FCC_auds) { + /* all audio frames are keyframes */ + target->flags |= GST_RIFF_IF_KEYFRAME; + } + + 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, &target->ts); + } else { + /* VBR stream */ + gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT, + stream->total_frames, &format, &target->ts); + } + + stream->total_bytes += target->size; + stream->total_frames++; + } + + /* debug our indexes */ + for (i = 0; i < avi->num_streams; i++) { + avi_stream_context *stream; + + stream = &avi->stream[i]; + GST_DEBUG ("stream %u: %u frames, %" G_GINT64_FORMAT " bytes", + 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))) + return FALSE; gst_event_unref (event); -done: + return TRUE; +} - return res; +/* + * Scan the file for all chunks to "create" a new index. + */ + +gboolean +gst_avi_demux_stream_scan (GstAviDemux *avi) +{ + //GstRiffRead *riff = GST_RIFF_READ (avi); + + /* FIXME */ + + return TRUE; } +/* + * Read full AVI headers. + */ -static void -gst_avi_demux_loop (GstElement *element) +gboolean +gst_avi_demux_stream_header (GstAviDemux *avi) { - GstAviDemux *avi_demux; - gst_riff_riff chunk; - guint32 flush = 0; - guint32 got_bytes; - GstByteStream *bs; - guint64 pos; - - avi_demux = GST_AVI_DEMUX (element); - - bs = avi_demux->bs; - - if (avi_demux->seek_pending) { - GST_DEBUG ("avidemux: seek pending to %" G_GINT64_FORMAT " %08llx", - avi_demux->seek_offset, (unsigned long long)avi_demux->seek_offset); - - if (!gst_bytestream_seek (avi_demux->bs, - avi_demux->seek_offset, - GST_SEEK_METHOD_SET)) - { - GST_INFO ( "avidemux: could not seek"); + GstRiffRead *riff = GST_RIFF_READ (avi); + guint32 tag, flags, streams; + + /* the header consists of a 'hdrl' LIST tag */ + if (!(tag = gst_riff_peek_tag (riff, NULL))) + return FALSE; + if (tag != GST_RIFF_TAG_LIST) { + gst_element_error (GST_ELEMENT (avi), + "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) { + gst_element_error (GST_ELEMENT (avi), + "Invalid AVI header (no hdrl at start): " + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)); + return FALSE; + } + + /* the hdrl starts with a 'avih' header */ + if (!(tag = gst_riff_peek_tag (riff, NULL))) + return FALSE; + if (tag != GST_RIFF_TAG_avih) { + gst_element_error (GST_ELEMENT (avi), + "Invalid AVI header (no avih at start): " + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)); + return FALSE; + } + 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; + } + + 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; + + case GST_RIFF_LIST_INFO: + if (!gst_riff_read_list (riff, &tag) || + !gst_riff_read_info (riff)) + 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; + } + + break; + + default: + GST_WARNING ("Unknown tag " 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 (avi->level_up) { + avi->level_up--; + break; } - avi_demux->seek_pending = FALSE; } - pos = gst_bytestream_tell (bs); - do { - gst_riff_riff *temp_chunk; - guint8 *tempdata; - guint32 skipsize; - - /* read first two dwords to get chunktype and size */ - while (TRUE) { - got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_chunk)); - temp_chunk = (gst_riff_riff *) tempdata; - if (got_bytes < sizeof (gst_riff_chunk)) { - if (!gst_avi_demux_handle_sink_event (avi_demux)) - return; - } - else break; + if (avi->num_streams != streams) { + g_warning ("Stream header mentioned %d streams, but %d available", + streams, avi->num_streams); + } + + /* we've got streaminfo now */ + g_object_notify (G_OBJECT(avi), "streaminfo"); + + /* 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 (!gst_riff_read_skip (riff)) + return FALSE; + continue; } + break; + } + + /* create or read stream index (for seeking) */ + if (flags & GST_RIFF_AVIH_HASINDEX) { + if (!gst_avi_demux_stream_index (avi)) + return FALSE; + } else { + if (!gst_avi_demux_stream_scan (avi)) + return FALSE; + } + + return TRUE; +} + +/* + * Handle seek. + */ + +static gboolean +gst_avi_demux_handle_seek (GstAviDemux *avi) +{ + GstRiffRead *riff = GST_RIFF_READ (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. */ + + if (!(event = gst_riff_read_seek (riff, avi->seek_offset))) + return FALSE; + gst_event_unref (event); + + for (i = 0; i < avi->num_streams; i++) { + avi_stream_context *stream = &avi->stream[i]; + + if (GST_PAD_IS_USABLE (stream->pad)) { + event = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, + avi->last_seek + stream->delay , NULL); + gst_pad_push (stream->pad, GST_DATA (event)); + } + } + + return TRUE; +} + +/* + * Read data. + */ - chunk.id = GUINT32_FROM_LE (temp_chunk->id); - chunk.size = GUINT32_FROM_LE (temp_chunk->size); +gboolean +gst_avi_demux_stream_data (GstAviDemux *avi) +{ + GstRiffRead *riff = GST_RIFF_READ (avi); + guint32 tag; + guint stream_nr; + gst_avi_index_entry *entry; + + if (avi->seek_offset != (guint64) -1) { + if (!gst_avi_demux_handle_seek (avi)) + return FALSE; + avi->seek_offset = (guint64) -1; + } - switch (chunk.id) { - case GST_RIFF_TAG_RIFF: + /* peek first (for the end of this 'list/movi' section) */ + if (!(tag = gst_riff_peek_tag (riff, &avi->level_up))) + return FALSE; + + /* if we're at top-level, we didn't read the 'movi' + * list tag yet. This can also be 'AVIX' in case of + * openDML-2.0 AVI files. Lastly, it might be idx1, + * in which case we skip it so we come at EOS. */ + while (g_list_length (riff->level) < 2) { + if (!(tag = gst_riff_peek_tag (riff, NULL))) + return FALSE; + + switch (tag) { case GST_RIFF_TAG_LIST: - /* read complete list chunk */ - while (TRUE) { - got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_list)); - temp_chunk = (gst_riff_riff *) tempdata; - if (got_bytes < sizeof (gst_riff_list)) { - if (!gst_avi_demux_handle_sink_event (avi_demux)) - return; - } - else break; + if (!(tag = gst_riff_peek_list (riff))) + return FALSE; + + switch (tag) { + case GST_RIFF_LIST_AVIX: + case GST_RIFF_LIST_movi: + if (!gst_riff_read_list (riff, &tag)) + return FALSE; + /* we're now going to read buffers! */ + break; + + default: + GST_WARNING ("Unknown list " GST_FOURCC_FORMAT " before AVI data", + GST_FOURCC_ARGS (tag)); + /* fall-through */ + + case GST_RIFF_TAG_JUNK: + if (!gst_riff_read_skip (riff)) + return FALSE; + break; } - chunk.type = GUINT32_FROM_LE (temp_chunk->type); - skipsize = sizeof (gst_riff_list); + break; + default: - skipsize = sizeof (gst_riff_chunk); + GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " before AVI data", + GST_FOURCC_ARGS (tag)); + /* fall-through */ + + case GST_RIFF_TAG_idx1: + case GST_RIFF_TAG_JUNK: + if (!gst_riff_read_skip (riff)) + return FALSE; break; } - gst_bytestream_flush_fast (bs, skipsize); - } - while (FALSE); + } + + /* And then, we get the data */ + if (!(tag = gst_riff_peek_tag (riff, NULL))) + return FALSE; + stream_nr = CHUNKID_TO_STREAMNR (tag); + if (stream_nr < 0 || stream_nr >= avi->num_streams) { + /* recoverable */ + g_warning ("Invalid stream ID %d (" GST_FOURCC_FORMAT ")", + stream_nr, GST_FOURCC_ARGS (tag)); + if (!gst_riff_read_skip (riff)) + return FALSE; + } else { + avi_stream_context *stream; + GstClockTime next_ts; + GstFormat format; + GstBuffer *buf; - /* need to flush an even number of bytes at the end */ - flush = (chunk.size + 1) & ~1; + /* get buffer */ + if (!gst_riff_read_data (riff, &tag, &buf)) + return FALSE; - switch (avi_demux->state) { - case GST_AVI_DEMUX_START: - if (chunk.id != GST_RIFF_TAG_RIFF && - chunk.type != GST_RIFF_RIFF_AVI) { - gst_element_error (element, "This doesn't appear to be an AVI file %08x %08x", chunk.id, chunk.type); - return; - } - avi_demux->state = GST_AVI_DEMUX_HEADER; - /* we are not going to flush lists */ - flush = 0; - break; - case GST_AVI_DEMUX_HEADER: - GST_DEBUG ("riff tag: %4.4s %08x", (gchar *)&chunk.id, chunk.size); - switch (chunk.id) { - case GST_RIFF_TAG_LIST: - GST_DEBUG ("list type: %4.4s", (gchar *)&chunk.type); - switch (chunk.type) { - case GST_RIFF_LIST_movi: - { - guint64 filepos; - - filepos = gst_bytestream_tell (bs); - - gst_avi_demux_parse_index (avi_demux, filepos , chunk.size - 4); - - if (avi_demux->avih.bufsize) { - gst_bytestream_size_hint (avi_demux->bs, avi_demux->avih.bufsize); - } - - avi_demux->state = GST_AVI_DEMUX_MOVI; - /* and tell the bastards that we have stream info too */ - gst_props_debug(avi_demux->streaminfo->properties); - g_object_notify(G_OBJECT(avi_demux), "streaminfo"); - break; - } - case GST_RIFF_LIST_INFO: - gst_avi_demux_metadata (avi_demux, chunk.size); - break; - default: - break; - } - flush = 0; - break; - case GST_RIFF_TAG_avih: - gst_avi_demux_avih (avi_demux); - break; - case GST_RIFF_TAG_strh: - gst_avi_demux_strh (avi_demux); - break; - case GST_RIFF_TAG_strf: - switch (avi_demux->fcc_type) { - case GST_RIFF_FCC_vids: - gst_avi_demux_strf_vids (avi_demux); - break; - case GST_RIFF_FCC_auds: - gst_avi_demux_strf_auds (avi_demux); - break; - case GST_RIFF_FCC_iavs: - gst_avi_demux_strf_iavs (avi_demux); - break; - case GST_RIFF_FCC_pads: - case GST_RIFF_FCC_txts: - default: - GST_INFO ( "gst_avi_demux_chain: strh type %s not supported", - gst_riff_id_to_fourcc (avi_demux->fcc_type)); - break; - } - break; - case GST_RIFF_TAG_strn: - gst_avi_demux_strn (avi_demux, chunk.size); - break; - case GST_RIFF_TAG_dmlh: - gst_avi_demux_dmlh (avi_demux); - break; - case GST_RIFF_TAG_JUNK: - case GST_RIFF_ISFT: - break; - default: - GST_DEBUG (" ***** unknown chunkid %08x", chunk.id); - break; + /* get time of this buffer */ + stream = &avi->stream[stream_nr]; + entry = gst_avi_demux_index_next (avi, stream_nr, + stream->current_entry + 1, 0); + if (entry) { + stream->current_entry = entry->index_nr; + if (entry->flags & GST_RIFF_IF_KEYFRAME) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_KEY_UNIT); } - break; - case GST_AVI_DEMUX_MOVI: - switch (chunk.id) { - case GST_RIFF_00dc: - case GST_RIFF_00db: - case GST_RIFF_00__: - case GST_RIFF_01wb: - { - gint stream_id; - avi_stream_context *stream; - gint64 next_ts; - GstFormat format; - - stream_id = CHUNKID_TO_STREAMNR (chunk.id); - - stream = &avi_demux->stream[stream_id]; - - GST_LOG_OBJECT (avi_demux, "gst_avi_demux_chain: tag found %08x size %08x stream_id %d", - chunk.id, chunk.size, stream_id); - - format = GST_FORMAT_TIME; - gst_pad_query (stream->pad, GST_QUERY_POSITION, &format, &next_ts); - - if (stream->strh.init_frames == stream->current_frame && stream->delay == 0) - stream->delay = next_ts; - - stream->current_frame++; - stream->current_byte += chunk.size; - - if (stream->skip) { - stream->skip--; - } - else { - if (GST_PAD_IS_USABLE (stream->pad)) { - if (next_ts >= stream->end_pos) { - gst_pad_push (stream->pad, GST_DATA (gst_event_new (GST_EVENT_EOS))); - GST_DEBUG ("end stream %d: %" G_GINT64_FORMAT " %d %" G_GINT64_FORMAT, - stream_id, next_ts, stream->current_frame - 1, - stream->end_pos); - } - else { - GstBuffer *buf; - guint32 got_bytes; - - if (chunk.size) { - GstClockTime dur_ts; - got_bytes = gst_bytestream_peek (avi_demux->bs, &buf, chunk.size); - - 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; - - if (stream->need_flush) { - /* FIXME, do some flush event here */ - stream->need_flush = FALSE; - } - GST_LOG_OBJECT (avi_demux, "send stream %d: %" - G_GINT64_FORMAT " %d %" G_GINT64_FORMAT " %08x", - stream_id, next_ts, stream->current_frame - 1, - stream->delay, chunk.size); - - gst_pad_push(stream->pad, GST_DATA (buf)); - } - } - } - } - break; - } - default: - GST_DEBUG (" ***** unknown chunkid %08x", chunk.id); - break; + } + format = GST_FORMAT_TIME; + gst_pad_query (stream->pad, GST_QUERY_POSITION, + &format, &next_ts); + + /* set delay (if any) */ + if (stream->strh->init_frames == stream->current_frame && + stream->delay == 0) + stream->delay = next_ts; + + stream->current_frame++; + stream->current_byte += GST_BUFFER_SIZE (buf); + + /* should we skip this data? */ + if (stream->skip) { + stream->skip--; + gst_buffer_unref (buf); + } else { + if (!stream->pad || !GST_PAD_IS_USABLE (stream->pad)) { + gst_buffer_unref (buf); + } else { + GstClockTime dur_ts; + + 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; + + gst_pad_push (stream->pad, GST_DATA (buf)); } - break; + } } - while (flush) { - gboolean res; - - res = gst_bytestream_flush (avi_demux->bs, flush); - if (!res) { - guint32 remaining; - GstEvent *event; + return TRUE; +} - gst_bytestream_get_status (avi_demux->bs, &remaining, &event); - gst_event_unref (event); - } - else +static void +gst_avi_demux_loop (GstElement *element) +{ + GstAviDemux *avi = GST_AVI_DEMUX (element); + + switch (avi->state) { + case GST_AVI_DEMUX_START: + if (!gst_avi_demux_stream_init (avi)) + return; + avi->state = GST_AVI_DEMUX_HEADER; + /* fall-through */ + + case GST_AVI_DEMUX_HEADER: + if (!gst_avi_demux_stream_header (avi)) + return; + avi->state = GST_AVI_DEMUX_MOVI; + /* fall-through */ + + case GST_AVI_DEMUX_MOVI: + if (!gst_avi_demux_stream_data (avi)) + return; break; + + default: + g_assert (0); } } static GstElementStateReturn gst_avi_demux_change_state (GstElement *element) { - GstAviDemux *avi_demux = GST_AVI_DEMUX (element); + GstAviDemux *avi = GST_AVI_DEMUX (element); switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_NULL_TO_READY: - break; case GST_STATE_READY_TO_PAUSED: - avi_demux->bs = gst_bytestream_new (avi_demux->sinkpad); - avi_demux->last_seek = 0; - avi_demux->state = GST_AVI_DEMUX_START; - avi_demux->num_streams = 0; - avi_demux->num_v_streams = 0; - avi_demux->num_a_streams = 0; - avi_demux->index_entries = NULL; - avi_demux->index_size = 0; - avi_demux->seek_pending = 0; - avi_demux->metadata = NULL; - gst_avi_demux_streaminfo(avi_demux); - break; - case GST_STATE_PAUSED_TO_PLAYING: - break; - case GST_STATE_PLAYING_TO_PAUSED: + gst_avi_demux_streaminfo (avi); break; case GST_STATE_PAUSED_TO_READY: - gst_bytestream_destroy (avi_demux->bs); - gst_caps_replace (&avi_demux->metadata, NULL); - gst_caps_replace (&avi_demux->streaminfo, NULL); - break; - case GST_STATE_READY_TO_NULL: + gst_avi_demux_reset (avi); break; default: break; } - parent_class->change_state (element); + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); return GST_STATE_SUCCESS; } static void -gst_avi_demux_get_property (GObject *object, guint prop_id, GValue *value, +gst_avi_demux_get_property (GObject *object, + guint prop_id, + GValue *value, GParamSpec *pspec) { - GstAviDemux *src; - - g_return_if_fail (GST_IS_AVI_DEMUX (object)); - - src = GST_AVI_DEMUX (object); + GstAviDemux *avi = GST_AVI_DEMUX (object); switch (prop_id) { - case ARG_BITRATE: - break; - case ARG_METADATA: - g_value_set_boxed(value, src->metadata); - break; case ARG_STREAMINFO: - g_value_set_boxed(value, src->streaminfo); + g_value_set_boxed (value, avi->streaminfo); break; default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } - -static gboolean -plugin_init (GstPlugin *plugin) -{ - if (!gst_library_load ("gstbytestream")) - return FALSE; - if (!gst_library_load ("gstriff")) - return FALSE; - if (!gst_library_load("gstvideo")) - return FALSE; - - GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux", 0, "Demuxer for AVI video"); - - if (!gst_element_register (plugin, "avidemux", GST_RANK_PRIMARY, - GST_TYPE_AVI_DEMUX)) { - return FALSE; - } - - if (!gst_element_register (plugin, "avimux", GST_RANK_PRIMARY, - GST_TYPE_AVIMUX)) { - return FALSE; - } - - return TRUE; -} - - -GST_PLUGIN_DEFINE ( - GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "avimux", - "AVI stream handling", - plugin_init, - VERSION, - GST_LICENSE, - GST_PACKAGE, - GST_ORIGIN -) - diff --git a/gst/avi/gstavidemux.h b/gst/avi/gstavidemux.h index a9334283..15eec1f3 100644 --- a/gst/avi/gstavidemux.h +++ b/gst/avi/gstavidemux.h @@ -17,30 +17,26 @@ * Boston, MA 02111-1307, USA. */ - #ifndef __GST_AVI_DEMUX_H__ #define __GST_AVI_DEMUX_H__ - #include <gst/gst.h> -#include <gst/bytestream/bytestream.h> -#include <gst/riff/riff.h> -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ +#include "gst/riff/riff-ids.h" +#include "gst/riff/riff-read.h" + +G_BEGIN_DECLS #define GST_TYPE_AVI_DEMUX \ - (gst_avi_demux_get_type()) + (gst_avi_demux_get_type ()) #define GST_AVI_DEMUX(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVI_DEMUX,GstAviDemux)) + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_AVI_DEMUX, GstAviDemux)) #define GST_AVI_DEMUX_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVI_DEMUX,GstAviDemux)) + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_AVI_DEMUX, GstAviDemux)) #define GST_IS_AVI_DEMUX(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVI_DEMUX)) + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_AVI_DEMUX)) #define GST_IS_AVI_DEMUX_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVI_DEMUX)) - + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_AVI_DEMUX)) #define GST_AVI_DEMUX_MAX_STREAMS 16 @@ -48,85 +44,85 @@ extern "C" { ((((chunkid) & 0xff) - '0') * 10 + \ (((chunkid) >> 8) & 0xff) - '0') -typedef struct _GstAviDemux GstAviDemux; -typedef struct _GstAviDemuxClass GstAviDemuxClass; - -typedef struct -{ - gint index_nr; - gint stream_nr; - guint64 ts; - guint32 flags; - guint32 offset; - gint size; - guint64 bytes_before; - guint32 frames_before; +typedef struct { + gint index_nr; + gint stream_nr; + guint64 ts; + guint32 flags; + guint32 offset; + gint size; + guint64 bytes_before; + guint32 frames_before; } gst_avi_index_entry; -typedef struct -{ - GstPad *pad; - gint num; - gst_riff_strh strh; - guint32 current_frame; - guint32 current_byte; - guint64 delay; - gboolean need_flush; - guint64 end_pos; +typedef struct { + /* index of this streamcontext */ + guint num; + + /* pad, strh */ + GstPad *pad; + gst_riff_strh *strh; - guint64 total_bytes; - guint32 total_frames; + /* current position (byte, frame, time) */ + guint current_frame; + guint64 current_byte; + gint current_entry; - guint32 skip; + /* delay in time (init_frames) */ + guint64 delay; + /* stream length */ + guint64 total_bytes; + guint32 total_frames; + + guint32 skip; } avi_stream_context; -typedef enum -{ +typedef enum { GST_AVI_DEMUX_START, GST_AVI_DEMUX_HEADER, GST_AVI_DEMUX_MOVI, } GstAviDemuxState; -struct _GstAviDemux { - GstElement element; +typedef struct _GstAviDemux { + GstRiffRead parent; /* pads */ - GstPad *sinkpad, *srcpad; + GstPad *sinkpad; /* AVI decoding state */ - guint32 fcc_type; GstAviDemuxState state; + guint level_up; - GstByteStream *bs; - + /* index */ gst_avi_index_entry *index_entries; - gulong index_size; - gulong index_offset; - - gst_riff_avih avih; + guint index_size; + guint64 index_offset; + /* streams */ guint num_streams; guint num_v_streams; guint num_a_streams; avi_stream_context stream[GST_AVI_DEMUX_MAX_STREAMS]; - gboolean seek_pending; - gint64 seek_offset; - guint64 last_seek; + /* some stream info for length */ + guint32 us_per_frame; + guint32 num_frames; - GstCaps *metadata, *streaminfo; -}; + /* seeking */ + guint64 seek_offset; + guint64 last_seek; -struct _GstAviDemuxClass { - GstElementClass parent_class; -}; + /* info */ + GstCaps *streaminfo; +} GstAviDemux; -GType gst_avi_demux_get_type (void); +typedef struct _GstAviDemuxClass { + GstRiffReadClass parent_class; +} GstAviDemuxClass; -#ifdef __cplusplus -} -#endif /* __cplusplus */ +GType gst_avi_demux_get_type (void); +G_END_DECLS #endif /* __GST_AVI_DEMUX_H__ */ diff --git a/gst/avi/gstavimux.h b/gst/avi/gstavimux.h index 31c64c38..452b7221 100644 --- a/gst/avi/gstavimux.h +++ b/gst/avi/gstavimux.h @@ -23,7 +23,8 @@ #include <gst/gst.h> -#include <gst/riff/riff.h> +#include <gst/riff/riff-ids.h> +#include "avi-ids.h" #ifdef __cplusplus |