diff -r 000000000000 -r ec8c246e9d29 gst-gpac/src/gpacmux.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gst-gpac/src/gpacmux.c Thu Feb 21 17:54:48 2008 +0000 @@ -0,0 +1,604 @@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include + +/* gpac includes */ +#include + +#define GST_TYPE_GPAC_MUX (gst_gpac_mux_get_type()) +#define GST_GPAC_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GPAC_MUX, GstGpacMux)) +#define GST_GPAC_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GPAC_MUX, GstGpacMux)) +#define GST_IS_GPAC_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GPAC_MUX)) +#define GST_IS_GPAC_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GPAC_MUX)) + +typedef struct _GstGpacMux GstGpacMux; +typedef struct _GstGpacMuxClass GstGpacMuxClass; + +typedef enum +{ + GST_GPAC_PAD_STATE_CONTROL = 0, + GST_GPAC_PAD_STATE_DATA = 1 +} +GstGpacPadState; + +typedef struct +{ + GstCollectData collect; /* we extend the CollectData */ + + gint track_number; + guint32 di; /* outDescriptionIndex */ + + guint32 frame_count; + gboolean is_video; + +} GstGpacPad; + +struct _GstGpacMux +{ + GstElement element; + + GstPad *srcpad; + GstCollectPads *collect; + + GF_ISOFile *file; + +}; + +struct _GstGpacMuxClass +{ + GstElementClass parent_class; +}; + +/* elementfactory information */ +static const GstElementDetails gst_gpac_mux_details = +GST_ELEMENT_DETAILS ("Gpac muxer", + "Codec/Muxer", + "mux mp4 streams", + "Hallyson Melo finalize = gst_gpac_mux_finalize; + + gstelement_class->request_new_pad = gst_gpac_mux_request_new_pad; + gstelement_class->release_pad = gst_gpac_mux_release_pad; + + gstelement_class->change_state = gst_gpac_mux_change_state; + +} + +static void +gst_gpac_mux_init (GstGpacMux * gpac_mux) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (gpac_mux); + + gpac_mux->srcpad = + gst_pad_new_from_template (gst_element_class_get_pad_template (klass, + "src"), "src"); + gst_pad_set_event_function (gpac_mux->srcpad, gst_gpac_mux_handle_src_event); + gst_element_add_pad (GST_ELEMENT (gpac_mux), gpac_mux->srcpad); + + gpac_mux->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (gpac_mux->collect, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_gpac_mux_collected), + gpac_mux); + + /* Opens gpac library */ + /* FIXME */ + gpac_mux->file = gf_isom_open("/tmp/gpac.mp4", GF_ISOM_OPEN_WRITE, NULL); + gf_isom_set_storage_mode(gpac_mux->file, GF_ISOM_STORE_FLAT /*STREAMABLE*/); + + //gst_gpac_mux_clear (gpac_mux); +} + +static void +gst_gpac_mux_finalize (GObject * object) +{ + GstGpacMux *gpac_mux; + + gpac_mux = GST_GPAC_MUX (object); + + if (gpac_mux->collect) { + gst_object_unref (gpac_mux->collect); + gpac_mux->collect = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_gpac_mux_gpac_pad_destroy_notify (GstCollectData * data) +{ + GstGpacPad *gpacpad = (GstGpacPad *) data; + GstBuffer *buf; + +/* if (gpacpad->pagebuffers) { + while ((buf = g_queue_pop_head (gpacpad->pagebuffers)) != NULL) { + gst_buffer_unref (buf); + } + g_queue_free (gpacpad->pagebuffers); + gpacpad->pagebuffers = NULL; + }*/ +} + +static GstPadLinkReturn +gst_gpac_mux_sinkconnect (GstPad * pad, GstPad * peer) +{ + GstGpacMux *gpac_mux; + + gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad)); + + GST_DEBUG_OBJECT (gpac_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad)); + + gst_object_unref (gpac_mux); + + return GST_PAD_LINK_OK; +} + +static GstPad * +gst_gpac_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * req_name) +{ + GstGpacMux *gpac_mux; + GstPad *newpad; + GstElementClass *klass; + gchar *padname = NULL; + gint serial; + gboolean is_video = FALSE; + + g_return_val_if_fail (templ != NULL, NULL); + + if (templ->direction != GST_PAD_SINK) + goto wrong_direction; + + g_return_val_if_fail (GST_IS_GPAC_MUX (element), NULL); + gpac_mux = GST_GPAC_MUX (element); + + klass = GST_ELEMENT_GET_CLASS (element); + + if (req_name == NULL || strlen (req_name) < 6) { + /* no name given when requesting the pad, use random serial number */ + serial = rand (); + } else { + /* parse serial number from requested padname */ + serial = atoi (&req_name[5]); + } + + if (templ == gst_element_class_get_pad_template (klass, "video_%d")) { + is_video = TRUE; + padname = g_strdup_printf ("video_%d", serial); + } else if (templ != gst_element_class_get_pad_template (klass, "audio_%d")) { + goto wrong_template; + } else { + padname = g_strdup_printf ("audio_%d", serial); + } + + + { + + /* create new pad with the name */ + GST_DEBUG_OBJECT (gpac_mux, "Creating new pad for serial %d", serial); + printf ("XXXX new pad from template\n"); + newpad = gst_pad_new_from_template (templ, padname); + g_free (padname); + + /* construct our own wrapper data structure for the pad to + * keep track of its status */ + { + GstGpacPad *gpacpad; + + gpacpad = (GstGpacPad *) + gst_collect_pads_add_pad_full (gpac_mux->collect, newpad, + sizeof (GstGpacPad), gst_gpac_mux_gpac_pad_destroy_notify); + + /* gpac new track */ + gpacpad->is_video = is_video; + if (gpacpad->is_video) { + gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0, + GF_ISOM_MEDIA_VISUAL, 10 * 1000 /* time scale */); + } else { + gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0, + GF_ISOM_MEDIA_AUDIO, 48000 /*time scale */); + + } + if (gpacpad->track_number == 0) { + g_warning ("Error while adding the new gpac track"); + } else { + gf_isom_set_track_enabled(gpac_mux->file, gpacpad->track_number, 1); + if (is_video) { + GF_ESD *esd = gf_odf_desc_esd_new (0); + esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number); + + gf_isom_new_mpeg4_description( gpac_mux->file, gpacpad->track_number, + esd, NULL, NULL, &(gpacpad->di)); + + gf_isom_set_visual_info (gpac_mux->file, gpacpad->track_number, gpacpad->di, 320, 240);//fixme + //gf_isom_set_cts_packing (gpac_mux->file, gpacpad->track_number, 0); + } else { + GF_ESD *esd = gf_odf_desc_esd_new (2); + esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number); + + gf_isom_new_mpeg4_description(gpac_mux->file, gpacpad->track_number, + esd, NULL, NULL, &(gpacpad->di)); + + gf_isom_set_audio_info(gpac_mux->file, gpacpad->track_number, + gpacpad->di, 48000, 2 /*num channels */, 16); + } + } + } + } + + /* setup some pad functions */ + gst_pad_set_link_function (newpad, gst_gpac_mux_sinkconnect); + /* dd the pad to the element */ + gst_element_add_pad (element, newpad); + + return newpad; + + /* ERRORS */ +wrong_direction: + { + g_warning ("gpac_mux: request pad that is not a SINK pad\n"); + return NULL; + } +wrong_template: + { + g_warning ("gpac_mux: this is not our template!\n"); + return NULL; + } +} + +static void +gst_gpac_mux_release_pad (GstElement * element, GstPad * pad) +{ + GstGpacMux *gpac_mux; + + gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad)); + + gst_collect_pads_remove_pad (gpac_mux->collect, pad); + gst_element_remove_pad (element, pad); +} + +/* handle events */ +static gboolean +gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event) +{ + GstEventType type; + + type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; + + switch (type) { + case GST_EVENT_SEEK: + /* disable seeking for now */ + return FALSE; + default: + break; + } + + return gst_pad_event_default (pad, event); +} + +static GstFlowReturn +gst_gpac_mux_push_buffer (GstGpacMux * mux, GstBuffer * buffer) +{ + GstCaps *caps; +#if 0 + /* fix up OFFSET and OFFSET_END again */ + GST_BUFFER_OFFSET (buffer) = mux->offset; + mux->offset += GST_BUFFER_SIZE (buffer); + GST_BUFFER_OFFSET_END (buffer) = mux->offset; + + /* Ensure we have monotonically increasing timestamps in the output. */ + if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { + if (mux->last_ts != GST_CLOCK_TIME_NONE && + GST_BUFFER_TIMESTAMP (buffer) < mux->last_ts) + GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts; + else + mux->last_ts = GST_BUFFER_TIMESTAMP (buffer); + } +#endif + + caps = gst_pad_get_negotiated_caps (mux->srcpad); + gst_buffer_set_caps (buffer, caps); + if (caps != NULL) { + gst_caps_unref (caps); + } + + return gst_pad_push (mux->srcpad, buffer); +} + +static gboolean +all_pads_eos (GstCollectPads * pads) +{ + GSList *iter; + gboolean alleos = TRUE; + + iter = pads->data; + while (iter) { + GstBuffer *buf; + GstCollectData *data = (GstCollectData *) iter->data; + + buf = gst_collect_pads_peek (pads, data); + if (buf) { + alleos = FALSE; + gst_buffer_unref (buf); + goto beach; + } + iter = iter->next; + } +beach: + return alleos; +} + +static GstFlowReturn +gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux) +{ + GstFlowReturn ret = GST_FLOW_OK; + GSList *iter; + GstBuffer *buf; + gint i = 0; + + GST_LOG_OBJECT (gpac_mux, "collected"); + + iter = gpac_mux->collect->data; + while (iter) { + GstCollectData *data = (GstCollectData *) iter->data; + GstGpacPad *pad = (GstGpacPad *) data; + + buf = gst_collect_pads_pop (gpac_mux->collect, data); + if (buf == NULL) { + iter = g_slist_next (iter); + continue; + } + GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf); + + /* gpac output */ + printf ("xxxxxx buffer size: %d\n", GST_BUFFER_SIZE(buf)); fflush (stdout); + if (pad->frame_count < 300) { + GF_ISOSample *sample = gf_isom_sample_new(); + + sample->dataLength = GST_BUFFER_SIZE(buf); + sample->data = GST_BUFFER_DATA(buf); + sample->CTS_Offset = 0; + + if (pad->is_video) { + sample->IsRAP = 0; + sample->DTS += 1000*pad->frame_count; + } else { + sample->IsRAP = 0; + sample->DTS = 2048*pad->frame_count; + } + + gf_isom_add_sample(gpac_mux->file, pad->track_number, + pad->di, sample); + sample->data = NULL; + + gf_isom_sample_del(&sample); + + printf ("xxxx frames %d\n", pad->frame_count); + + } else if (pad->frame_count == 300) { + printf ("XXX closing gpac output file\n"); fflush (stdout); + gf_isom_close (gpac_mux->file); + } + + /* gstreamer output (push) */ + if (gst_gpac_mux_push_buffer (gpac_mux, buf) != GST_FLOW_OK) { + printf ("EEEEEEEE push failed\n"); + } + + iter = g_slist_next (iter); + pad->frame_count++; + i++; + } + + /* fixme */ + return ret; +} + +/* reset all variables in the gpac pads. */ +static void +gst_gpac_mux_init_collectpads (GstCollectPads * collect) +{ + GSList *walk; + + walk = collect->data; + while (walk) { + GstGpacPad *gpacpad = (GstGpacPad *) walk->data; + + //ogg_stream_init (&gpacpad->stream, gpacpad->serial); + //gpacpad->packetno = 0; + //gpacpad->pageno = 0; + //gpacpad->eos = FALSE; + /* we assume there will be some control data first for this pad */ + //gpacpad->state = GST_GPAC_PAD_STATE_CONTROL; + //gpacpad->new_page = TRUE; + //gpacpad->first_delta = FALSE; + //gpacpad->prev_delta = FALSE; + //gpacpad->pagebuffers = g_queue_new (); + + walk = g_slist_next (walk); + } +} + +/* Clear all buffers from the collectpads object */ +static void +gst_gpac_mux_clear_collectpads (GstCollectPads * collect) +{ + GSList *walk; + + for (walk = collect->data; walk; walk = g_slist_next (walk)) { + GstGpacPad *gpacpad = (GstGpacPad *) walk->data; + GstBuffer *buf; + + //gpac_stream_clear (&gpacpad->stream); +/* + while ((buf = g_queue_pop_head (gpacpad->pagebuffers)) != NULL) { + gst_buffer_unref (buf); + } + g_queue_free (gpacpad->pagebuffers); + gpacpad->pagebuffers = NULL;*/ + } +} + +static GstStateChangeReturn +gst_gpac_mux_change_state (GstElement * element, GstStateChange transition) +{ + GstGpacMux *gpac_mux; + GstStateChangeReturn ret; + + gpac_mux = GST_GPAC_MUX (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + //gst_gpac_mux_clear (gpac_mux); + //gst_gpac_mux_init_collectpads (gpac_mux->collect); + gst_collect_pads_start (gpac_mux->collect); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (gpac_mux->collect); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_gpac_mux_clear_collectpads (gpac_mux->collect); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; + +} + +static gboolean +gst_gpac_mux_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "gpacmux", GST_RANK_NONE, + GST_TYPE_GPAC_MUX); +} + +GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "gpacmux", + "Muxes audio and video", + gst_gpac_mux_plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) +