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