diff options
author | Ronald S. Bultje <rbultje@ronald.bitfreak.net> | 2002-02-02 16:27:20 +0000 |
---|---|---|
committer | Ronald S. Bultje <rbultje@ronald.bitfreak.net> | 2002-02-02 16:27:20 +0000 |
commit | e8ace28405656e829134ba2e746c61bd54d233c7 (patch) | |
tree | 47ddd81812b1cdf53a33afd139ad5fed291c0e89 /gst/avi/gstavimux.c | |
parent | a7928f4f94de2f29a0638b621b6d23abd40ad420 (diff) |
WARNING: avimux is still broken, but less broken than it used to be... Code is under heavy development and will work ...
Original commit message from CVS:
WARNING: avimux is still broken, but less broken than it used to be... Code is under heavy development and will work sooner or later... Uploaded for generic development and testing purposes, not intended for generic use whatsoever
Diffstat (limited to 'gst/avi/gstavimux.c')
-rw-r--r-- | gst/avi/gstavimux.c | 917 |
1 files changed, 803 insertions, 114 deletions
diff --git a/gst/avi/gstavimux.c b/gst/avi/gstavimux.c index d79b6ee2..12f13c6c 100644 --- a/gst/avi/gstavimux.c +++ b/gst/avi/gstavimux.c @@ -1,5 +1,5 @@ -/* Gnome-Streamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> +/* AVI muxer plugin for GStreamer + * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,13 +17,28 @@ * Boston, MA 02111-1307, USA. */ +/* based on: + * - the old avimuxer (by Wim Taymans) + * - xawtv's aviwriter (by Gerd Knorr) + * - mjpegtools' avilib (by Rainer Johanni) + * - openDML large-AVI docs + */ + +#include <config.h> #include <stdlib.h> #include <string.h> #include "gstavimux.h" +#ifndef LE_FROM_GUINT16 +#define LE_FROM_GUINT16 GUINT16_FROM_LE +#endif + +#ifndef LE_FROM_GUINT32 +#define LE_FROM_GUINT32 GUINT32_FROM_LE +#endif /* elementfactory information */ @@ -32,10 +47,10 @@ gst_avimux_details = { ".avi mux", "Mux/Video", - "Encodes audio and video into an avi stream", + "Muxes audio and video into an avi stream", VERSION, - "Wim Taymans <wim.taymans@chello.be>", - "(C) 2000", + "Ronald Bultje <rbultje@ronald.bitfreak.net>", + "(C) 2002", }; /* AviMux signals and args */ @@ -46,7 +61,7 @@ enum { enum { ARG_0, - /* FILL ME */ + ARG_BIGFILE, }; GST_PADTEMPLATE_FACTORY (src_factory, @@ -54,44 +69,116 @@ GST_PADTEMPLATE_FACTORY (src_factory, GST_PAD_SRC, GST_PAD_ALWAYS, GST_CAPS_NEW ( - "sink_video", + "avimux_src_video", "video/avi", NULL ) ) GST_PADTEMPLATE_FACTORY (video_sink_factory, - "video_%02d", + "video_[00-63]", GST_PAD_SINK, GST_PAD_REQUEST, GST_CAPS_NEW ( - "sink_video", + "avimux_sink_video", "video/avi", "format", GST_PROPS_STRING ("strf_vids") + ), + GST_CAPS_NEW ( + "avimux_sink_video", + "video/raw", + "format", GST_PROPS_LIST ( + GST_PROPS_FOURCC (GST_MAKE_FOURCC('Y','U','Y','2')), + GST_PROPS_FOURCC (GST_MAKE_FOURCC('I','4','2','0')), + GST_PROPS_FOURCC (GST_MAKE_FOURCC('Y','4','1','P')) + ), + "width", GST_PROPS_INT_RANGE (16, 4096), + "height", GST_PROPS_INT_RANGE (16, 4096) + ), + GST_CAPS_NEW ( + "avimux_sink_video", + "video/raw", + "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC('R','G','B',' ')), + "width", GST_PROPS_INT_RANGE (16, 4096), + "height", GST_PROPS_INT_RANGE (16, 4096), + "depth", GST_PROPS_LIST( + GST_PROPS_INT(16), + GST_PROPS_INT(16), + GST_PROPS_INT(24), + GST_PROPS_INT(32) + ), + "bpp", GST_PROPS_LIST( + GST_PROPS_INT(15), + GST_PROPS_INT(16), + GST_PROPS_INT(24), + GST_PROPS_INT(32) + ) + ), + GST_CAPS_NEW ( + "avimux_sink_video", + "video/jpeg", + "width", GST_PROPS_INT_RANGE (16, 4096), + "height", GST_PROPS_INT_RANGE (16, 4096) ) ) GST_PADTEMPLATE_FACTORY (audio_sink_factory, - "audio_%02d", + "audio_[00-63]", GST_PAD_SINK, GST_PAD_REQUEST, GST_CAPS_NEW ( - "sink_audio", + "avimux_sink_audio", "video/avi", "format", GST_PROPS_STRING ("strf_auds") + ), + GST_CAPS_NEW ( + "avimux_sink_audio", + "audio/raw", + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_LIST ( + GST_PROPS_BOOLEAN (TRUE), + GST_PROPS_BOOLEAN (FALSE) + ), + "width", GST_PROPS_LIST ( + GST_PROPS_INT (8), + GST_PROPS_INT (16) + ), + "depth", GST_PROPS_LIST ( + GST_PROPS_INT (8), + GST_PROPS_INT (16) + ), + "rate", GST_PROPS_INT_RANGE (11025, 44100), + "channels", GST_PROPS_INT_RANGE (1, 2) + ), + GST_CAPS_NEW ( + "avimux_sink_audio", + "audio/mp3", + NULL ) ) -static void gst_avimux_class_init (GstAviMuxClass *klass); -static void gst_avimux_init (GstAviMux *avimux); - -static void gst_avimux_chain (GstPad *pad, GstBuffer *buf); -static GstPad* gst_avimux_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name); - -static void gst_avimux_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); -static void gst_avimux_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); - +static void gst_avimux_class_init (GstAviMuxClass *klass); +static void gst_avimux_init (GstAviMux *avimux); + +static void gst_avimux_chain (GstPad *pad, + GstBuffer *buf); +static gboolean gst_avimux_handle_event (GstPad *pad, + GstEvent *event); +static GstPad* gst_avimux_request_new_pad (GstElement *element, + GstPadTemplate *templ, + const gchar *name); +static void gst_avimux_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gst_avimux_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static GstElementStateReturn gst_avimux_change_state (GstElement *element); static GstElementClass *parent_class = NULL; /*static guint gst_avimux_signals[LAST_SIGNAL] = { 0 }; */ @@ -129,61 +216,160 @@ gst_avimux_class_init (GstAviMuxClass *klass) parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - gobject_class->set_property = gst_avimux_set_property; - gobject_class->get_property = gst_avimux_get_property; + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BIGFILE, + g_param_spec_boolean("bigfile","Bigfile Support","Whether to capture large or small AVI files", + 0,G_PARAM_READWRITE)); gstelement_class->request_new_pad = gst_avimux_request_new_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; } static void gst_avimux_init (GstAviMux *avimux) { + gint i; avimux->srcpad = gst_pad_new_from_template ( GST_PADTEMPLATE_GET (src_factory), "src"); gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad); - avimux->state = GST_AVIMUX_INITIAL; - avimux->riff = NULL; + GST_FLAG_SET (GST_ELEMENT(avimux), GST_ELEMENT_EVENT_AWARE); + /*gst_pad_set_event_function(avimux->srcpad, gst_avimux_handle_event);*/ + + for (i=0;i<MAX_NUM_AUDIO_PADS;i++) + avimux->audiosinkpad[i] = NULL; avimux->num_audio_pads = 0; + for (i=0;i<MAX_NUM_VIDEO_PADS;i++) + avimux->videosinkpad[i] = NULL; avimux->num_video_pads = 0; - avimux->next_time = 0; - avimux->riff = gst_riff_encoder_new (GST_RIFF_RIFF_AVI); - avimux->aviheader = g_malloc0 (sizeof (gst_riff_avih)); + avimux->num_frames = 0; + + /* audio/video/AVI header initialisation */ + memset(&(avimux->avi_hdr),0,sizeof(gst_riff_avih)); + memset(&(avimux->vids_hdr),0,sizeof(gst_riff_strh)); + memset(&(avimux->vids),0,sizeof(gst_riff_strf_vids)); + memset(&(avimux->auds_hdr),0,sizeof(gst_riff_strh)); + memset(&(avimux->auds),0,sizeof(gst_riff_strf_auds)); + avimux->vids_hdr.type = GST_MAKE_FOURCC('v','i','d','s'); + avimux->vids_hdr.rate = 1000000; + avimux->auds_hdr.type = GST_MAKE_FOURCC('a','u','d','s'); + + avimux->idx = NULL; + + avimux->write_header = TRUE; + + avimux->enable_large_avi = TRUE; } static GstPadConnectReturn -gst_avimux_sinkconnect (GstPad *pad, GstCaps *caps) +gst_avimux_sinkconnect (GstPad *pad, GstCaps *vscaps) { GstAviMux *avimux; - const gchar* format = gst_caps_get_string (caps, "format"); - gint padnum = GPOINTER_TO_INT (gst_pad_get_element_private (pad)); + GstCaps *caps; avimux = GST_AVIMUX (gst_pad_get_parent (pad)); - GST_DEBUG (0, "avimux: sinkconnect triggered on %s (%d), %s\n", gst_pad_get_name (pad), - padnum, format); - - if (!strncmp (format, "strf_vids", 9)) { - gst_riff_strf_vids *strf_vids = g_malloc(sizeof(gst_riff_strf_vids)); - - strf_vids->size = sizeof(gst_riff_strf_vids); - strf_vids->width = gst_caps_get_int (caps, "width"); - strf_vids->height = gst_caps_get_int (caps, "height");; - strf_vids->planes = gst_caps_get_int (caps, "planes");; - strf_vids->bit_cnt = gst_caps_get_int (caps, "bit_cnt");; - strf_vids->compression = gst_caps_get_fourcc_int (caps, "compression");; - strf_vids->image_size = gst_caps_get_int (caps, "image_size");; - strf_vids->xpels_meter = gst_caps_get_int (caps, "xpels_meter");; - strf_vids->ypels_meter = gst_caps_get_int (caps, "ypels_meter");; - strf_vids->num_colors = gst_caps_get_int (caps, "num_colors");; - strf_vids->imp_colors = gst_caps_get_int (caps, "imp_colors");; - - avimux->video_header[padnum] = strf_vids; + GST_DEBUG (0, "avimux: sinkconnect triggered on %s (%d)\n", gst_pad_get_name (pad)); + + for (caps = vscaps; caps != NULL; caps = vscaps = vscaps->next) + { + const gchar* format = gst_caps_get_string(caps, "format"); + const gchar* mimetype = gst_caps_get_mime(caps); + + if (!strcmp (mimetype, "video/avi")) + { + if (!strncmp (format, "strf_vids", 9)) { + avimux->vids.size = sizeof(gst_riff_strf_vids); + avimux->vids.width = gst_caps_get_int (caps, "width"); + avimux->vids.height = gst_caps_get_int (caps, "height"); + avimux->vids.planes = gst_caps_get_int (caps, "planes"); + avimux->vids.bit_cnt = gst_caps_get_int (caps, "bit_cnt"); + avimux->vids.compression = gst_caps_get_fourcc_int (caps, "compression"); + avimux->vids.image_size = gst_caps_get_int (caps, "image_size"); + avimux->vids.xpels_meter = gst_caps_get_int (caps, "xpels_meter"); + avimux->vids.ypels_meter = gst_caps_get_int (caps, "ypels_meter"); + avimux->vids.num_colors = gst_caps_get_int (caps, "num_colors"); + avimux->vids.imp_colors = gst_caps_get_int (caps, "imp_colors"); + } + else if (!strncmp (format, "strf_auds", 9)) { + avimux->auds.format = gst_caps_get_int (caps, "format"); + avimux->auds.channels = gst_caps_get_int (caps, "channels"); + avimux->auds.rate = gst_caps_get_int (caps, "rate"); + avimux->auds.av_bps = gst_caps_get_int (caps, "av_bps"); + avimux->auds.blockalign = gst_caps_get_int (caps, "blockalign"); + avimux->auds.size = gst_caps_get_int (caps, "size"); + } + goto done; + } + else if (!strcmp (mimetype, "video/raw")) + { + switch (gst_caps_get_fourcc_int(caps, "format")) + { + case GST_MAKE_FOURCC('Y','U','Y','2'): + case GST_MAKE_FOURCC('I','4','2','0'): + case GST_MAKE_FOURCC('Y','4','1','P'): + case GST_MAKE_FOURCC('R','G','B',' '): + avimux->vids.size = sizeof(gst_riff_strf_vids); + avimux->vids.width = gst_caps_get_int (caps, "width"); + avimux->vids.height = gst_caps_get_int (caps, "height"); + avimux->vids.planes = 1; + switch (gst_caps_get_fourcc_int(caps, "format")) + { + case GST_MAKE_FOURCC('Y','U','Y','2'): + avimux->vids.bit_cnt = 16; /* YUY2 */ + break; + case GST_MAKE_FOURCC('R','G','B',' '): + avimux->vids.bit_cnt = gst_caps_get_fourcc_int(caps, "bpp"); /* RGB */ + break; + case GST_MAKE_FOURCC('Y','4','1','P'): + case GST_MAKE_FOURCC('I','4','2','0'): + avimux->vids.bit_cnt = 12; /* Y41P or I420 */ + break; + } + avimux->vids.compression = gst_caps_get_fourcc_int(caps, "format"); + avimux->vids.image_size = avimux->vids.height * avimux->vids.width; + goto done; + default: + break; + } + } + else if (!strcmp (mimetype, "video/jpeg")) + { + avimux->vids.size = sizeof(gst_riff_strf_vids); + avimux->vids.width = gst_caps_get_int (caps, "width"); + avimux->vids.height = gst_caps_get_int (caps, "height"); + avimux->vids.planes = 1; + avimux->vids.bit_cnt = 24; + avimux->vids.compression = GST_MAKE_FOURCC('M','J','P','G'); + avimux->vids.image_size = avimux->vids.height * avimux->vids.width; + goto done; + } + else if (!strcmp (mimetype, "audio/raw")) + { + avimux->auds.format = GST_RIFF_WAVE_FORMAT_PCM; + avimux->auds.channels = gst_caps_get_int (caps, "channels"); + avimux->auds.rate = gst_caps_get_int (caps, "rate"); + avimux->auds.av_bps = gst_caps_get_int (caps, "width")*avimux->auds.rate* + avimux->auds.channels/8; + avimux->auds.blockalign = gst_caps_get_int (caps, "width")*avimux->auds.channels/8; + avimux->auds.size = gst_caps_get_int (caps, "depth"); + goto done; + } + else if (!strcmp (mimetype, "audio/mp3")) + { + /* we don't need to do anything here, compressed mp3 contains it all */ + avimux->auds.format = gst_caps_get_int(caps, "layer")==3? + GST_RIFF_WAVE_FORMAT_MPEGL3:GST_RIFF_WAVE_FORMAT_MPEGL12; + goto done; + } } - else if (!strncmp (format, "strf_auds", 9)) { + return GST_PAD_CONNECT_REFUSED; - } +done: return GST_PAD_CONNECT_OK; } @@ -208,19 +394,21 @@ gst_avimux_request_new_pad (GstElement *element, avimux = GST_AVIMUX (element); if (templ == GST_PADTEMPLATE_GET (audio_sink_factory)) { + g_return_val_if_fail(avimux->num_audio_pads == 0 /*< MAX_NUM_AUDIO_PADS*/, NULL); name = g_strdup_printf ("audio_%02d", avimux->num_audio_pads); newpad = gst_pad_new_from_template (templ, name); gst_pad_set_element_private (newpad, GINT_TO_POINTER (avimux->num_audio_pads)); - avimux->audio_pad[avimux->num_audio_pads] = newpad; + avimux->audiosinkpad[avimux->num_audio_pads] = newpad; avimux->num_audio_pads++; } else if (templ == GST_PADTEMPLATE_GET (video_sink_factory)) { + g_return_val_if_fail(avimux->num_video_pads == 0 /*< MAX_NUM_VIDEO_PADS*/, NULL); name = g_strdup_printf ("video_%02d", avimux->num_video_pads); newpad = gst_pad_new_from_template (templ, name); gst_pad_set_element_private (newpad, GINT_TO_POINTER (avimux->num_video_pads)); - avimux->video_pad[avimux->num_video_pads] = newpad; + avimux->videosinkpad[avimux->num_video_pads] = newpad; avimux->num_video_pads++; } else { @@ -235,38 +423,459 @@ gst_avimux_request_new_pad (GstElement *element, return newpad; } +/* 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) */ + +static GstBuffer * +gst_avimux_riff_get_avi_header (GstAviMux *avimux) +{ + GstBuffer *buffer; + guint8 *buffdata; + guint16 temp16; + guint32 temp32; + + buffer = gst_buffer_new(); + + /* first, let's see what actually needs to be in the buffer */ + GST_BUFFER_SIZE(buffer) = 0; + GST_BUFFER_SIZE(buffer) += 32 + sizeof(gst_riff_avih); /* avi header */ + if (avimux->num_video_pads) + { /* we have video */ + GST_BUFFER_SIZE(buffer) += 28 + sizeof(gst_riff_strh) + sizeof(gst_riff_strf_vids); /* vid hdr */ + GST_BUFFER_SIZE(buffer) += 24; /* odml header */ + } + if (avimux->num_audio_pads) + { /* we have audio */ + GST_BUFFER_SIZE(buffer) += 28 + sizeof(gst_riff_strh) + sizeof(gst_riff_strf_auds); /* aud hdr */ + } + /* this is the "riff size" */ + avimux->header_size = GST_BUFFER_SIZE(buffer); + GST_BUFFER_SIZE(buffer) += 12; /* avi data header */ + + /* allocate the buffer */ + buffdata = GST_BUFFER_DATA(buffer) = g_malloc(GST_BUFFER_SIZE(buffer)); + + /* avi header metadata */ + memcpy(buffdata, "RIFF", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->header_size + avimux->idx_size + avimux->data_size); + memcpy(buffdata, &temp32, 4); buffdata += 4; + memcpy(buffdata, "AVI ", 4); buffdata += 4; + memcpy(buffdata, "LIST", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->header_size - 4*5); + memcpy(buffdata, &temp32, 4); buffdata += 4; + memcpy(buffdata, "hdrl", 4); buffdata += 4; + memcpy(buffdata, "avih", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(gst_riff_avih)); + memcpy(buffdata, &temp32, 4); buffdata += 4; + /* the AVI header itself */ + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.us_frame); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.max_bps); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.pad_gran); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.flags); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.tot_frames); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.init_frames); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.streams); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.bufsize); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.width); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.height); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.scale); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.rate); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.start); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->avi_hdr.length); + memcpy(buffdata, &temp32, 4); buffdata += 4; + + /* video header metadata */ + memcpy(buffdata, "LIST", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh) + sizeof(gst_riff_strf_vids) + 4*5); + memcpy(buffdata, &temp32, 4); buffdata += 4; + memcpy(buffdata, "strl", 4); buffdata += 4; + /* generic header */ + memcpy(buffdata, "strh", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh)); + memcpy(buffdata, &temp32, 4); buffdata += 4; + /* the actual header */ + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.type); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.fcc_handler); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.flags); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.priority); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.init_frames); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.scale); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.rate); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.start); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.length); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.bufsize); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.quality); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids_hdr.samplesize); + memcpy(buffdata, &temp32, 4); buffdata += 4; + /* the video header */ + memcpy(buffdata, "strf", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strf_vids)); + memcpy(buffdata, &temp32, 4); buffdata += 4; + /* the actual header */ + temp32 = LE_FROM_GUINT32(avimux->vids.size); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids.width); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids.height); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp16 = LE_FROM_GUINT16(avimux->vids.planes); + memcpy(buffdata, &temp16, 2); buffdata += 2; + temp16 = LE_FROM_GUINT16(avimux->vids.bit_cnt); + memcpy(buffdata, &temp16, 2); buffdata += 2; + temp32 = LE_FROM_GUINT32(avimux->vids.compression); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids.image_size); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids.xpels_meter); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids.ypels_meter); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids.num_colors); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->vids.imp_colors); + memcpy(buffdata, &temp32, 4); buffdata += 4; + + /* audio header */ + memcpy(buffdata, "LIST", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh) + sizeof(gst_riff_strf_auds) + 4*5); + memcpy(buffdata, &temp32, 4); buffdata += 4; + memcpy(buffdata, "strl", 4); buffdata += 4; + /* generic header */ + memcpy(buffdata, "strh", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh)); + memcpy(buffdata, &temp32, 4); buffdata += 4; + /* the actual header */ + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.type); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.fcc_handler); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.flags); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.priority); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.init_frames); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.scale); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.rate); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.start); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.length); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.bufsize); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.quality); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds_hdr.samplesize); + memcpy(buffdata, &temp32, 4); buffdata += 4; + /* the audio header */ + memcpy(buffdata, "strf", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strf_vids)); + memcpy(buffdata, &temp32, 4); buffdata += 4; + /* the actual header */ + temp16 = LE_FROM_GUINT16(avimux->auds.format); + memcpy(buffdata, &temp16, 2); buffdata += 2; + temp16 = LE_FROM_GUINT16(avimux->auds.channels); + memcpy(buffdata, &temp16, 2); buffdata += 2; + temp32 = LE_FROM_GUINT32(avimux->auds.rate); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->auds.av_bps); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp16 = LE_FROM_GUINT16(avimux->auds.blockalign); + memcpy(buffdata, &temp16, 2); buffdata += 2; + temp16 = LE_FROM_GUINT16(avimux->auds.size); + memcpy(buffdata, &temp16, 2); buffdata += 2; + + /* odml header */ + memcpy(buffdata, "LIST", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(guint32)+4*3); + memcpy(buffdata, &temp32, 4); buffdata += 4; + memcpy(buffdata, "odml", 4); buffdata += 4; + memcpy(buffdata, "dmlh", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(sizeof(guint32)); + memcpy(buffdata, &temp32, 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->total_frames); + memcpy(buffdata, &temp32, 4); buffdata += 4; + + /* avi data header */ + memcpy(buffdata, "LIST", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(avimux->data_size); + memcpy(buffdata, &temp32, 4); buffdata += 4; + memcpy(buffdata, "movi", 4); buffdata += 4; + + return buffer; +} + +static GstBuffer * +gst_avimux_riff_get_avix_header (guint32 datax_size) +{ + GstBuffer *buffer; + guint8 *buffdata; + guint32 temp32; + + buffer = gst_buffer_new(); + GST_BUFFER_SIZE(buffer) = 24; + buffdata = GST_BUFFER_DATA(buffer) = g_malloc(GST_BUFFER_SIZE(buffer)); + + memcpy(buffdata, "LIST", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(datax_size+4*4); + memcpy(buffdata, &temp32, 4); buffdata += 4; + memcpy(buffdata, "AVIX", 4); buffdata += 4; + memcpy(buffdata, "LIST", 4); buffdata += 4; + temp32 = LE_FROM_GUINT32(datax_size); + memcpy(buffdata, &temp32, 4); buffdata += 4; + memcpy(buffdata, "movi", 4); buffdata += 4; + + return buffer; +} + +static GstBuffer * +gst_avimux_riff_get_video_header (guint32 video_frame_size) +{ + GstBuffer *buffer; + guint32 temp32; + + buffer = gst_buffer_new(); + GST_BUFFER_DATA(buffer) = g_malloc(8); + GST_BUFFER_SIZE(buffer) = 8; + memcpy(GST_BUFFER_DATA(buffer), "00db", 4); + temp32 = LE_FROM_GUINT32(video_frame_size); + memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4); + + return buffer; +} + +static GstBuffer * +gst_avimux_riff_get_audio_header (guint32 audio_sample_size) +{ + GstBuffer *buffer; + guint32 temp32; + + buffer = gst_buffer_new(); + GST_BUFFER_DATA(buffer) = g_malloc(8); + GST_BUFFER_SIZE(buffer) = 8; + memcpy(GST_BUFFER_DATA(buffer), "01wb", 4); + temp32 = LE_FROM_GUINT32(audio_sample_size); + memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4); + + return buffer; +} + +/* some other usable functions (thankyou xawtv ;-) ) */ + static void -gst_avimux_make_header (GstAviMux *avimux) +gst_avimux_add_index (GstAviMux *avimux, guint32 fourcc, guint32 flags, guint32 size) { - gint i; + guint32 temp32; + + if (avimux->idx_index == avimux->idx_count) + { + avimux->idx_count += 256; + avimux->idx = realloc(avimux->idx, avimux->idx_count*sizeof(gst_riff_index_entry)); + } + temp32 = LE_FROM_GUINT32(fourcc); + memcpy(&(avimux->idx[avimux->idx_index].id), &temp32, 4); + avimux->idx[avimux->idx_index].flags = LE_FROM_GUINT32(flags); + avimux->idx[avimux->idx_index].offset = LE_FROM_GUINT32(avimux->idx_offset-avimux->header_size-8); + avimux->idx[avimux->idx_index].size = LE_FROM_GUINT32(size); + avimux->idx_index++; + avimux->idx_offset += size + sizeof(gst_riff_index_entry); +} + +static void +gst_avimux_write_index (GstAviMux *avimux) +{ + GstBuffer *buffer; + guint32 temp32; + + buffer = gst_buffer_new(); + GST_BUFFER_SIZE(buffer) = 8; + GST_BUFFER_DATA(buffer) = g_malloc(8); + memcpy(GST_BUFFER_DATA(buffer), "idx1", 4); + temp32 = LE_FROM_GUINT32(avimux->idx_index * sizeof(gst_riff_index_entry)); + memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4); + gst_pad_push(avimux->srcpad, buffer); + + buffer = gst_buffer_new(); + GST_BUFFER_SIZE(buffer) = avimux->idx_index * sizeof(gst_riff_index_entry); + GST_BUFFER_DATA(buffer) = (unsigned char*) avimux->idx; + avimux->idx = NULL; /* will be free()'ed by gsT_buffer_unref() */ + avimux->total_data += GST_BUFFER_SIZE(buffer); + gst_pad_push(avimux->srcpad, buffer); + + avimux->idx_size += avimux->idx_index * sizeof(gst_riff_index_entry) + 8; + + /* update header */ + avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX; +} + +static void +gst_avimux_bigfile(GstAviMux *avimux, gboolean last) +{ + GstBuffer *header; + GstEvent *event; + + if (avimux->is_bigfile) + { + /* sarch back */ + event = gst_event_new_seek(GST_SEEK_BYTEOFFSET_SET, avimux->avix_start, TRUE); + gst_pad_push(avimux->srcpad, GST_BUFFER(event)); + + /* rewrite AVIX header */ + header = gst_avimux_riff_get_avix_header(avimux->datax_size); + gst_pad_push(avimux->srcpad, header); + + /* go back to current location */ + event = gst_event_new_seek(GST_SEEK_BYTEOFFSET_SET, avimux->total_data, TRUE); + gst_pad_push(avimux->srcpad, GST_BUFFER(event)); + } + avimux->avix_start = avimux->total_data; + + if (last) + return; + + avimux->is_bigfile = TRUE; + avimux->numx_frames = 0; + avimux->datax_size = 0; + + header = gst_avimux_riff_get_avix_header(0); + avimux->total_data += GST_BUFFER_SIZE(header); + gst_pad_push(avimux->srcpad, header); +} + +/* enough header blabla now, let's go on to actually writing the headers */ + +static void +gst_avimux_start_file (GstAviMux *avimux) +{ + GstBuffer *header; + + avimux->total_data = 0; + avimux->total_frames = 0; + avimux->data_size = 4; /* ? */ + avimux->datax_size = 0; + avimux->num_frames = 0; + avimux->numx_frames = 0; + avimux->audio_size = 0; + + avimux->idx_index = 0; + avimux->idx_offset = avimux->header_size + 12; + avimux->idx_size = 0; + avimux->idx_count = 0; + avimux->idx = NULL; + + avimux->is_bigfile = FALSE; + header = gst_avimux_riff_get_avi_header(avimux); + avimux->total_data += GST_BUFFER_SIZE(header); + + gst_pad_push(avimux->srcpad, header); + + avimux->write_header = FALSE; +} + +static void +gst_avimux_stop_file (GstAviMux *avimux) +{ + GstEvent *event; + GstBuffer *header; + + /* if bigfile, rewrite header, else write indexes */ + if (avimux->num_video_pads) + { + if (avimux->is_bigfile) + { + gst_avimux_bigfile(avimux, TRUE); + avimux->idx_size = 0; + } + else + { + gst_avimux_write_index(avimux); + } + } + + /* statistics/total_frames/... */ + avimux->avi_hdr.tot_frames = avimux->num_frames; + if (avimux->num_video_pads) + avimux->auds_hdr.length = avimux->num_frames; + if (avimux->num_audio_pads) + avimux->auds_hdr.length = avimux->audio_size/avimux->auds_hdr.scale; + + /* seek and rewrite the header */ + header = gst_avimux_riff_get_avi_header(avimux); + event = gst_event_new_seek(GST_SEEK_BYTEOFFSET_SET, 0, TRUE); + gst_pad_push(avimux->srcpad, GST_BUFFER(event)); + gst_pad_push(avimux->srcpad, header); + + avimux->write_header = TRUE; +} + +static void +gst_avimux_restart_file (GstAviMux *avimux) +{ + GstEvent *event; - gst_riff_strh strh; + gst_avimux_stop_file(avimux); - avimux->aviheader->us_frame = 40000; - avimux->aviheader->streams = avimux->num_video_pads + avimux->num_audio_pads; - avimux->aviheader->width = -1; - avimux->aviheader->height = -1; - gst_riff_encoder_avih(avimux->riff, avimux->aviheader, sizeof(gst_riff_avih)); + event = gst_event_new(GST_EVENT_NEW_MEDIA); + gst_pad_push(avimux->srcpad, GST_BUFFER(event)); + + /*gst_avimux_start_file(avimux);*/ +} - memset(&strh, 0, sizeof(gst_riff_strh)); - strh.scale = 40000; +/* handle events (search) */ +static gboolean +gst_avimux_handle_event (GstPad *pad, GstEvent *event) +{ + GstAviMux *avimux; + GstEventType type; - gst_riff_encoder_strh(avimux->riff, GST_RIFF_FCC_vids, &strh, sizeof(gst_riff_strh)); + avimux = GST_AVIMUX (gst_pad_get_parent (pad)); + + type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; - for (i=0; i<avimux->num_video_pads; i++) { - gst_riff_encoder_strf(avimux->riff, avimux->video_header[i], sizeof(gst_riff_strf_vids)); + switch (type) { + case GST_EVENT_NEW_MEDIA: + gst_avimux_restart_file(avimux); + break; + default: + break; } + + return TRUE; } static void gst_avimux_chain (GstPad *pad, GstBuffer *buf) { GstAviMux *avimux; - guchar *data; - gulong size; - const gchar *padname; - gint channel; GstBuffer *newbuf; + const gchar *padname = gst_pad_get_name (pad); g_return_if_fail (pad != NULL); g_return_if_fail (GST_IS_PAD (pad)); @@ -274,85 +883,166 @@ gst_avimux_chain (GstPad *pad, GstBuffer *buf) g_return_if_fail (GST_BUFFER_DATA (buf) != NULL); avimux = GST_AVIMUX (gst_pad_get_parent (pad)); + + if (GST_IS_EVENT(buf)) + { + gst_avimux_handle_event(pad, GST_EVENT(buf)); + return; + } - data = (guchar *)GST_BUFFER_DATA(buf); - size = GST_BUFFER_SIZE(buf); - - switch(avimux->state) { - case GST_AVIMUX_INITIAL: - GST_DEBUG (0,"gst_avimux_chain: writing header\n"); - gst_avimux_make_header(avimux); - newbuf = gst_riff_encoder_get_and_reset_buffer(avimux->riff); - gst_pad_push(avimux->srcpad, newbuf); - avimux->state = GST_AVIMUX_MOVI; - case GST_AVIMUX_MOVI: - padname = gst_pad_get_name (pad); - channel = GPOINTER_TO_INT (gst_pad_get_element_private (pad)); - - if (strncmp(padname, "audio_", 6) == 0) { - GST_DEBUG (0,"gst_avimux_chain: got audio buffer in from channel %02d %lu\n", channel, size); - gst_riff_encoder_chunk(avimux->riff, GST_RIFF_01wb, NULL, size); - newbuf = gst_riff_encoder_get_and_reset_buffer(avimux->riff); - gst_pad_push(avimux->srcpad, newbuf); - } - else if (strncmp(padname, "video_", 6) == 0) { - GST_DEBUG (0,"gst_avimux_chain: got video buffer in from channel %02d %lu\n", channel, size); - gst_riff_encoder_chunk(avimux->riff, GST_RIFF_00db, NULL, size); - newbuf = gst_riff_encoder_get_and_reset_buffer(avimux->riff); - GST_DEBUG (0,"gst_avimux_chain: encoded %u\n", GST_BUFFER_SIZE(newbuf)); - gst_pad_push(avimux->srcpad, newbuf); - } - GST_BUFFER_SIZE(buf) = (GST_BUFFER_SIZE(buf)+1)&~1; - gst_pad_push(avimux->srcpad, buf); - break; - default: - break; + if (avimux->write_header) + gst_avimux_start_file(avimux); + + if (strncmp(padname, "audio_", 6) == 0) + { + /* write a audio header + index entry */ + newbuf = gst_avimux_riff_get_audio_header(GST_BUFFER_SIZE(buf)); + avimux->total_data += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf); + + if (avimux->is_bigfile) + { + avimux->datax_size += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf); + } + else + { + avimux->data_size += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf); + avimux->audio_size += GST_BUFFER_SIZE(buf); + gst_avimux_add_index(avimux, avimux->auds.format, 0x0, GST_BUFFER_SIZE(buf)); + } + + gst_pad_push(avimux->srcpad, newbuf); + } + else if (strncmp(padname, "video_", 6) == 0) + { + /* write a video header + index entry */ + GST_BUFFER_SIZE(buf) = (GST_BUFFER_SIZE(buf)+3)&~3; + + if ((avimux->is_bigfile?avimux->datax_size:avimux->data_size)+GST_BUFFER_SIZE(buf)>1024*1024*2000) + { + if (avimux->enable_large_avi) + gst_avimux_bigfile(avimux, FALSE); + else + gst_avimux_restart_file(avimux); + } + + newbuf = gst_avimux_riff_get_video_header(GST_BUFFER_SIZE(buf)); + avimux->total_data += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf); + avimux->total_frames++; + + if (avimux->is_bigfile) + { + avimux->datax_size += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf); + avimux->numx_frames++; + } + else + { + avimux->data_size += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf); + avimux->num_frames++; + gst_avimux_add_index(avimux, avimux->vids.compression, 0x12, GST_BUFFER_SIZE(buf)); + } + + gst_pad_push(avimux->srcpad, newbuf); + } + else + { + g_warning("Unknown padname \'%s\'\n", padname); + return; } - /*gst_buffer_unref(buf); */ + /* data */ + gst_pad_push(avimux->srcpad, buf); } -static void -gst_avimux_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +static void +gst_avimux_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - GstAviMux *src; + GstAviMux *avimux; /* it's not null if we got it, but it might not be ours */ g_return_if_fail(GST_IS_AVIMUX(object)); - src = GST_AVIMUX(object); + avimux = GST_AVIMUX(object); - switch(prop_id) { + switch (prop_id) + { + case ARG_BIGFILE: + g_value_set_boolean(value, avimux->enable_large_avi); + break; default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } -static void -gst_avimux_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +static void +gst_avimux_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - GstAviMux *src; + GstAviMux *avimux; /* it's not null if we got it, but it might not be ours */ g_return_if_fail(GST_IS_AVIMUX(object)); - src = GST_AVIMUX(object); + avimux = GST_AVIMUX(object); - switch(prop_id) { + switch (prop_id) + { + case ARG_BIGFILE: + avimux->enable_large_avi = g_value_get_boolean(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static GstElementStateReturn +gst_avimux_change_state (GstElement *element) +{ + GstAviMux *avimux; + gint transition = GST_STATE_TRANSITION (element); + + /* TODO: PLAY->READY pauses the timer (for fps calculations) */ + + g_return_val_if_fail(GST_IS_AVIMUX(element), GST_STATE_FAILURE); + + avimux = GST_AVIMUX(element); + + switch (transition) { + case GST_STATE_READY_TO_PAUSED: + /*gst_avimux_start_file(avimux);*/ + break; + case GST_STATE_PAUSED_TO_PLAYING: + break; + case GST_STATE_PLAYING_TO_PAUSED: + gst_avimux_stop_file(avimux); + break; + case GST_STATE_PAUSED_TO_READY: + /*gst_avimux_stop_file(avimux);*/ + break; + } + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; +} + static gboolean plugin_init (GModule *module, GstPlugin *plugin) { GstElementFactory *factory; /* this filter needs the riff parser */ +#if 0 if (!gst_library_load ("gstriff")) { gst_info ("avimux: could not load support library: 'gstriff'\n"); return FALSE; } +#endif /* create an elementfactory for the avimux element */ factory = gst_elementfactory_new ("avimux", GST_TYPE_AVIMUX, @@ -374,4 +1064,3 @@ GstPluginDesc plugin_desc = { "avimux", plugin_init }; - |