7 #include <gst/base/gstcollectpads.h>
13 #include <gpac/isomedia.h>
15 #define GST_TYPE_GPAC_MUX (gst_gpac_mux_get_type())
16 #define GST_GPAC_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GPAC_MUX, GstGpacMux))
17 #define GST_GPAC_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GPAC_MUX, GstGpacMux))
18 #define GST_IS_GPAC_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GPAC_MUX))
19 #define GST_IS_GPAC_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GPAC_MUX))
21 typedef struct _GstGpacMux GstGpacMux;
22 typedef struct _GstGpacMuxClass GstGpacMuxClass;
26 GST_GPAC_PAD_STATE_CONTROL = 0,
27 GST_GPAC_PAD_STATE_DATA = 1
33 GstCollectData collect; /* we extend the CollectData */
36 guint32 di; /* outDescriptionIndex */
39 GstBuffer *next_buffer;
53 GstCollectPads *collect;
60 struct _GstGpacMuxClass
62 GstElementClass parent_class;
65 /* elementfactory information */
66 static const GstElementDetails gst_gpac_mux_details =
67 GST_ELEMENT_DETAILS ("Gpac muxer",
70 "Hallyson Melo <hallyson.melo@indt.org.br");
72 /* GpacMux signals and args */
79 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
82 GST_STATIC_CAPS ("video/quicktime")
85 static GstStaticPadTemplate audio_sink_factory = GST_STATIC_PAD_TEMPLATE ("audio_%d",
90 "mpegversion = (int) 4, "
91 "channels = (int) { 1, 2 }, "
92 "rate = (int) [ 8000, 96000 ]")
94 //GST_STATIC_CAPS ("audio/mpeg, mpegversion = 1, layer = 3")
96 static GstStaticPadTemplate video_sink_factory = GST_STATIC_PAD_TEMPLATE ("video_%d",
99 GST_STATIC_CAPS ("video/mpeg, mpegversion = 4")
102 static void gst_gpac_mux_base_init (gpointer g_class);
103 static void gst_gpac_mux_class_init (GstGpacMuxClass * klass);
104 static void gst_gpac_mux_init (GstGpacMux * gpac_mux);
105 static void gst_gpac_mux_finalize (GObject * object);
108 gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux);
110 static gboolean gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event);
111 static GstPad *gst_gpac_mux_request_new_pad (GstElement * element,
112 GstPadTemplate * templ, const gchar * name);
113 static void gst_gpac_mux_release_pad (GstElement * element, GstPad * pad);
115 static GstStateChangeReturn gst_gpac_mux_change_state (GstElement * element,
116 GstStateChange transition);
118 static GstGpacPad* gst_gpac_mux_queue_pads (GstGpacMux * gpac_mux);
119 static GstFlowReturn gst_gpac_mux_process_pad (GstGpacMux * gpac_mux, GstGpacPad *pad);
120 static gboolean gst_gpac_mux_all_pads_eos (GstCollectPads * pads);
121 static gint gst_gpac_mux_compare_pads (GstGpacMux * ogg_mux, GstGpacPad *first,
125 static GstElementClass *parent_class = NULL;
128 gst_gpac_mux_get_type (void)
130 static GType gpac_mux_type = 0;
132 if (G_UNLIKELY (gpac_mux_type == 0)) {
133 static const GTypeInfo gpac_mux_info = {
134 sizeof (GstGpacMuxClass),
135 gst_gpac_mux_base_init,
137 (GClassInitFunc) gst_gpac_mux_class_init,
142 (GInstanceInitFunc) gst_gpac_mux_init,
146 g_type_register_static (GST_TYPE_ELEMENT, "GstGpacMux", &gpac_mux_info,
149 return gpac_mux_type;
153 gst_gpac_mux_base_init (gpointer g_class)
155 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
157 gst_element_class_add_pad_template (element_class,
158 gst_static_pad_template_get (&src_factory));
159 gst_element_class_add_pad_template (element_class,
160 gst_static_pad_template_get (&audio_sink_factory));
161 gst_element_class_add_pad_template (element_class,
162 gst_static_pad_template_get (&video_sink_factory));
164 gst_element_class_set_details (element_class, &gst_gpac_mux_details);
168 gst_gpac_mux_class_init (GstGpacMuxClass * klass)
170 GObjectClass *gobject_class;
171 GstElementClass *gstelement_class;
173 gobject_class = (GObjectClass *) klass;
174 gstelement_class = (GstElementClass *) klass;
176 parent_class = g_type_class_peek_parent (klass);
178 gobject_class->finalize = gst_gpac_mux_finalize;
180 gstelement_class->request_new_pad = gst_gpac_mux_request_new_pad;
181 gstelement_class->release_pad = gst_gpac_mux_release_pad;
183 gstelement_class->change_state = gst_gpac_mux_change_state;
188 gst_gpac_mux_init (GstGpacMux * gpac_mux)
190 GstElementClass *klass = GST_ELEMENT_GET_CLASS (gpac_mux);
193 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
195 gst_pad_set_event_function (gpac_mux->srcpad, gst_gpac_mux_handle_src_event);
196 gst_element_add_pad (GST_ELEMENT (gpac_mux), gpac_mux->srcpad);
198 gpac_mux->collect = gst_collect_pads_new ();
199 gst_collect_pads_set_function (gpac_mux->collect,
200 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_gpac_mux_collected),
203 /* Opens gpac library */
205 gpac_mux->file = gf_isom_open("/tmp/gpac.mp4", GF_ISOM_OPEN_WRITE, NULL);
206 gf_isom_set_storage_mode(gpac_mux->file, GF_ISOM_STORE_FLAT /*STREAMABLE*/);
208 //gst_gpac_mux_clear (gpac_mux);
212 gst_gpac_mux_finalize (GObject * object)
214 GstGpacMux *gpac_mux;
216 gpac_mux = GST_GPAC_MUX (object);
218 if (gpac_mux->collect) {
219 gst_object_unref (gpac_mux->collect);
220 gpac_mux->collect = NULL;
223 G_OBJECT_CLASS (parent_class)->finalize (object);
227 gst_gpac_mux_gpac_pad_destroy_notify (GstCollectData * data)
229 GstGpacPad *gpacpad = (GstGpacPad *) data;
234 static GstPadLinkReturn
235 gst_gpac_mux_sinkconnect (GstPad * pad, GstPad * peer)
237 GstGpacMux *gpac_mux;
239 gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad));
241 GST_DEBUG_OBJECT (gpac_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
243 gst_object_unref (gpac_mux);
245 return GST_PAD_LINK_OK;
249 gst_gpac_mux_request_new_pad (GstElement * element,
250 GstPadTemplate * templ, const gchar * req_name)
252 GstGpacMux *gpac_mux;
254 GstElementClass *klass;
255 gchar *padname = NULL;
257 gboolean is_video = FALSE;
259 g_return_val_if_fail (templ != NULL, NULL);
261 if (templ->direction != GST_PAD_SINK)
262 goto wrong_direction;
264 g_return_val_if_fail (GST_IS_GPAC_MUX (element), NULL);
265 gpac_mux = GST_GPAC_MUX (element);
267 klass = GST_ELEMENT_GET_CLASS (element);
269 if (req_name == NULL || strlen (req_name) < 6) {
270 /* no name given when requesting the pad, use random serial number */
273 /* parse serial number from requested padname */
274 serial = atoi (&req_name[5]);
277 if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
279 padname = g_strdup_printf ("video_%d", serial);
280 } else if (templ != gst_element_class_get_pad_template (klass, "audio_%d")) {
283 padname = g_strdup_printf ("audio_%d", serial);
289 /* create new pad with the name */
290 GST_DEBUG_OBJECT (gpac_mux, "Creating new pad for serial %d", serial);
292 newpad = gst_pad_new_from_template (templ, padname);
295 /* construct our own wrapper data structure for the pad to
296 * keep track of its status */
300 gpacpad = (GstGpacPad *)
301 gst_collect_pads_add_pad_full (gpac_mux->collect, newpad,
302 sizeof (GstGpacPad), gst_gpac_mux_gpac_pad_destroy_notify);
303 gpac_mux->active_pads++;
306 gpacpad->is_video = is_video;
307 if (gpacpad->is_video) {
308 gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0,
309 GF_ISOM_MEDIA_VISUAL, 10 * 1000 /* time scale */);
311 gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0,
312 GF_ISOM_MEDIA_AUDIO, 48000 /*time scale */);
315 if (gpacpad->track_number == 0) {
316 g_warning ("Error while adding the new gpac track");
318 gf_isom_set_track_enabled(gpac_mux->file, gpacpad->track_number, 1);
320 GF_ESD *esd = gf_odf_desc_esd_new (0);
321 esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number);
323 gf_isom_new_mpeg4_description( gpac_mux->file, gpacpad->track_number,
324 esd, NULL, NULL, &(gpacpad->di));
326 gf_isom_set_visual_info (gpac_mux->file, gpacpad->track_number, gpacpad->di, 320, 240);//fixme
327 //gf_isom_set_cts_packing (gpac_mux->file, gpacpad->track_number, 0);
329 GF_ESD *esd = gf_odf_desc_esd_new (2);
330 esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number);
332 gf_isom_new_mpeg4_description(gpac_mux->file, gpacpad->track_number,
333 esd, NULL, NULL, &(gpacpad->di));
335 gf_isom_set_audio_info(gpac_mux->file, gpacpad->track_number,
336 gpacpad->di, 48000, 2 /*num channels */, 16);
342 /* setup some pad functions */
343 gst_pad_set_link_function (newpad, gst_gpac_mux_sinkconnect);
344 /* dd the pad to the element */
345 gst_element_add_pad (element, newpad);
352 g_warning ("gpac_mux: request pad that is not a SINK pad\n");
357 g_warning ("gpac_mux: this is not our template!\n");
363 gst_gpac_mux_release_pad (GstElement * element, GstPad * pad)
365 GstGpacMux *gpac_mux;
367 gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad));
369 gst_collect_pads_remove_pad (gpac_mux->collect, pad);
370 gst_element_remove_pad (element, pad);
375 gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event)
379 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
383 /* disable seeking for now */
389 return gst_pad_event_default (pad, event);
393 gst_gpac_mux_push_buffer (GstGpacMux * mux, GstBuffer * buffer)
397 /* fix up OFFSET and OFFSET_END again */
398 GST_BUFFER_OFFSET (buffer) = mux->offset;
399 mux->offset += GST_BUFFER_SIZE (buffer);
400 GST_BUFFER_OFFSET_END (buffer) = mux->offset;
402 /* Ensure we have monotonically increasing timestamps in the output. */
403 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
404 if (mux->last_ts != GST_CLOCK_TIME_NONE &&
405 GST_BUFFER_TIMESTAMP (buffer) < mux->last_ts)
406 GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
408 mux->last_ts = GST_BUFFER_TIMESTAMP (buffer);
412 caps = gst_pad_get_negotiated_caps (mux->srcpad);
413 gst_buffer_set_caps (buffer, caps);
415 gst_caps_unref (caps);
418 return gst_pad_push (mux->srcpad, buffer);
422 gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux)
424 GstFlowReturn ret = GST_FLOW_OK;
428 GST_LOG_OBJECT (gpac_mux, "collected");
430 active_before = gpac_mux->active_pads;
432 pad = gst_gpac_mux_queue_pads (gpac_mux);
434 return GST_FLOW_WRONG_STATE;
438 ret = gst_gpac_mux_process_pad (gpac_mux, pad);
441 if (gpac_mux->active_pads < active_before) {
442 /* If the active pad count went down, this mean at least one pad has gone
443 * EOS. Since CollectPads only calls _collected() once when all pads are
444 * EOS, and our code doesn't _pop() from all pads we need to check that by
445 * peeking on all pads, else we won't be called again and the muxing will
446 * not terminate (push out EOS). */
447 printf ("XXXX um pad foi desativado %" GST_PTR_FORMAT "\n", pad);
449 /* if all the pads have been removed, flush all pending data */
450 if ((ret == GST_FLOW_OK) && gst_gpac_mux_all_pads_eos (pads)) {
451 GST_LOG_OBJECT (gpac_mux, "no pads remaining, flushing data");
454 pad = gst_gpac_mux_queue_pads (gpac_mux);
456 ret = gst_gpac_mux_process_pad (gpac_mux, pad);
457 } while ((ret == GST_FLOW_OK) && (pad != NULL));
459 /* gpac file close (eos) */
460 // Fixme: this should flush all data to src pad
461 // Fixme: where to release gpac_mux->file?
462 printf ("CCCCCCCCCCCCCCCCcclosing the file\n");
463 gf_isom_close (gpac_mux->file);
466 GST_DEBUG_OBJECT (gpac_mux, "Pushing EOS");
467 gst_pad_push_event (gpac_mux->srcpad, gst_event_new_eos ());
476 gst_gpac_mux_all_pads_eos (GstCollectPads * pads)
479 gboolean alleos = TRUE;
484 GstCollectData *data = (GstCollectData *) walk->data;
486 buf = gst_collect_pads_peek (pads, data);
489 gst_buffer_unref (buf);
500 gst_gpac_mux_process_pad (GstGpacMux *gpac_mux, GstGpacPad *pad)
502 GstFlowReturn ret = GST_FLOW_OK;
503 GF_ISOSample *sample;
505 if (pad->buffer == NULL) {
506 printf ("Buffer is null, wrong state\n");
507 return GST_FLOW_WRONG_STATE;
511 printf ("xxxxxx buffer size: %d\n", GST_BUFFER_SIZE(pad->buffer)); fflush (stdout);
512 printf ("xxxx frames %d\n", pad->frame_count);
514 sample = gf_isom_sample_new();
515 sample->dataLength = GST_BUFFER_SIZE(pad->buffer);
516 sample->data = GST_BUFFER_DATA(pad->buffer);
517 sample->CTS_Offset = 0;
521 sample->DTS += 1000*pad->frame_count;
524 sample->DTS = 2048*pad->frame_count;
527 gf_isom_add_sample(gpac_mux->file, pad->track_number,
531 gf_isom_sample_del(&sample);
534 /* gstreamer output (push) */
535 ret = gst_gpac_mux_push_buffer (gpac_mux, pad->buffer);
538 if (ret != GST_FLOW_OK) {
546 /* reset all variables in the gpac pads. */
548 gst_gpac_mux_init_collectpads (GstCollectPads * collect)
552 iter = collect->data;
554 GstGpacPad *gpacpad = (GstGpacPad *) iter->data;
556 //gpac_stream_init (&gpacpad->stream, gpacpad->serial);
557 //gpacpad->packetno = 0;
558 //gpacpad->pageno = 0;
559 //gpacpad->eos = FALSE;
560 /* we assume there will be some control data first for this pad */
561 //gpacpad->state = GST_GPAC_PAD_STATE_CONTROL;
562 //gpacpad->new_page = TRUE;
563 //gpacpad->first_delta = FALSE;
564 //gpacpad->prev_delta = FALSE;
565 //gpacpad->pagebuffers = g_queue_new ();
567 iter = g_slist_next (iter);
571 /* Clear all buffers from the collectpads object */
573 gst_gpac_mux_clear_collectpads (GstCollectPads * collect)
577 for (iter = collect->data; iter; iter = g_slist_next (iter)) {
578 GstGpacPad *gpacpad = (GstGpacPad *) iter->data;
581 //gpac_stream_clear (&gpacpad->stream);
583 while ((buf = g_queue_pop_head (gpacpad->pagebuffers)) != NULL) {
584 gst_buffer_unref (buf);
586 g_queue_free (gpacpad->pagebuffers);
587 gpacpad->pagebuffers = NULL;*/
591 static GstStateChangeReturn
592 gst_gpac_mux_change_state (GstElement * element, GstStateChange transition)
594 GstGpacMux *gpac_mux;
595 GstStateChangeReturn ret;
597 gpac_mux = GST_GPAC_MUX (element);
599 switch (transition) {
600 case GST_STATE_CHANGE_NULL_TO_READY:
602 case GST_STATE_CHANGE_READY_TO_PAUSED:
603 //gst_gpac_mux_clear (gpac_mux);
604 //gst_gpac_mux_init_collectpads (gpac_mux->collect);
605 gst_collect_pads_start (gpac_mux->collect);
607 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
609 case GST_STATE_CHANGE_PAUSED_TO_READY:
610 gst_collect_pads_stop (gpac_mux->collect);
616 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
618 switch (transition) {
619 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
621 case GST_STATE_CHANGE_PAUSED_TO_READY:
622 gst_gpac_mux_clear_collectpads (gpac_mux->collect);
624 case GST_STATE_CHANGE_READY_TO_NULL:
635 gst_gpac_mux_queue_pads (GstGpacMux * gpac_mux)
637 GstGpacPad *bestpad = NULL;//, *still_hungry = NULL;
640 /* try to make sure we have a buffer from each usable pad first */
641 iter = gpac_mux->collect->data;
644 GstCollectData *data;
646 data = (GstCollectData *) iter->data;
647 pad = (GstGpacPad *) data;
649 iter = g_slist_next (iter);
651 GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
653 /* try to get a new buffer for this pad if needed and possible */
654 if (pad->buffer == NULL) {
657 buf = gst_collect_pads_pop (gpac_mux->collect, data);
658 GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
660 /* On EOS we get a NULL buffer */
662 printf ("EENENENENENEND OF STREAM EOS\n");
663 GST_DEBUG_OBJECT (data->pad, "EOS on pad");
665 /* it's no longer active */
666 gpac_mux->active_pads--;
675 /* we should have a buffer now, see if it is the best pad to
677 if (gst_gpac_mux_compare_pads (gpac_mux, bestpad, pad) > 0) {
678 GST_LOG_OBJECT (data->pad,
679 "new best pad, with buffers %" GST_PTR_FORMAT, pad->buffer);
688 gst_gpac_mux_compare_pads (GstGpacMux * ogg_mux, GstGpacPad *first,
691 guint64 firsttime, secondtime;
693 /* if the first pad doesn't contain anything or is even NULL, return
694 * the second pad as best candidate and vice versa */
695 if (first == NULL || (first->buffer == NULL))
697 if (second == NULL || (second->buffer == NULL))
700 /* no timestamp on first buffer, it must go first */
702 firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
703 if (firsttime == GST_CLOCK_TIME_NONE)
706 /* no timestamp on second buffer, it must go first */
708 secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
709 if (secondtime == GST_CLOCK_TIME_NONE)
712 /* first buffer has higher timestamp, second one should go first */
713 if (secondtime < firsttime)
715 /* second buffer has higher timestamp, first one should go first */
716 else if (secondtime > firsttime)
719 /* same priority if all of the above failed */
726 gst_gpac_mux_plugin_init (GstPlugin * plugin)
728 return gst_element_register (plugin, "gpacmux", GST_RANK_NONE,
732 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
735 "Muxes audio and video",
736 gst_gpac_mux_plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,