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 */
48 GstCollectPads *collect;
54 struct _GstGpacMuxClass
56 GstElementClass parent_class;
59 /* elementfactory information */
60 static const GstElementDetails gst_gpac_mux_details =
61 GST_ELEMENT_DETAILS ("Gpac muxer",
64 "Hallyson Melo <hallyson.melo@indt.org.br");
66 /* GpacMux signals and args */
73 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
76 GST_STATIC_CAPS ("video/quicktime")
79 static GstStaticPadTemplate audio_sink_factory = GST_STATIC_PAD_TEMPLATE ("audio_%d",
84 "mpegversion = (int) 4, "
85 "channels = (int) { 1, 2 }, "
86 "rate = (int) [ 8000, 96000 ]")
88 //GST_STATIC_CAPS ("audio/mpeg, mpegversion = 1, layer = 3")
90 static GstStaticPadTemplate video_sink_factory = GST_STATIC_PAD_TEMPLATE ("video_%d",
93 GST_STATIC_CAPS ("video/mpeg, mpegversion = 4")
96 static void gst_gpac_mux_base_init (gpointer g_class);
97 static void gst_gpac_mux_class_init (GstGpacMuxClass * klass);
98 static void gst_gpac_mux_init (GstGpacMux * gpac_mux);
99 static void gst_gpac_mux_finalize (GObject * object);
102 gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux);
104 static gboolean gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event);
105 static GstPad *gst_gpac_mux_request_new_pad (GstElement * element,
106 GstPadTemplate * templ, const gchar * name);
107 static void gst_gpac_mux_release_pad (GstElement * element, GstPad * pad);
109 static GstStateChangeReturn gst_gpac_mux_change_state (GstElement * element,
110 GstStateChange transition);
112 static GstElementClass *parent_class = NULL;
115 gst_gpac_mux_get_type (void)
117 static GType gpac_mux_type = 0;
119 if (G_UNLIKELY (gpac_mux_type == 0)) {
120 static const GTypeInfo gpac_mux_info = {
121 sizeof (GstGpacMuxClass),
122 gst_gpac_mux_base_init,
124 (GClassInitFunc) gst_gpac_mux_class_init,
129 (GInstanceInitFunc) gst_gpac_mux_init,
133 g_type_register_static (GST_TYPE_ELEMENT, "GstGpacMux", &gpac_mux_info,
136 return gpac_mux_type;
140 gst_gpac_mux_base_init (gpointer g_class)
142 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
144 gst_element_class_add_pad_template (element_class,
145 gst_static_pad_template_get (&src_factory));
146 gst_element_class_add_pad_template (element_class,
147 gst_static_pad_template_get (&audio_sink_factory));
148 gst_element_class_add_pad_template (element_class,
149 gst_static_pad_template_get (&video_sink_factory));
151 gst_element_class_set_details (element_class, &gst_gpac_mux_details);
155 gst_gpac_mux_class_init (GstGpacMuxClass * klass)
157 GObjectClass *gobject_class;
158 GstElementClass *gstelement_class;
160 gobject_class = (GObjectClass *) klass;
161 gstelement_class = (GstElementClass *) klass;
163 parent_class = g_type_class_peek_parent (klass);
165 gobject_class->finalize = gst_gpac_mux_finalize;
167 gstelement_class->request_new_pad = gst_gpac_mux_request_new_pad;
168 gstelement_class->release_pad = gst_gpac_mux_release_pad;
170 gstelement_class->change_state = gst_gpac_mux_change_state;
175 gst_gpac_mux_init (GstGpacMux * gpac_mux)
177 GstElementClass *klass = GST_ELEMENT_GET_CLASS (gpac_mux);
180 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
182 gst_pad_set_event_function (gpac_mux->srcpad, gst_gpac_mux_handle_src_event);
183 gst_element_add_pad (GST_ELEMENT (gpac_mux), gpac_mux->srcpad);
185 gpac_mux->collect = gst_collect_pads_new ();
186 gst_collect_pads_set_function (gpac_mux->collect,
187 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_gpac_mux_collected),
190 /* Opens gpac library */
192 gpac_mux->file = gf_isom_open("/tmp/gpac.mp4", GF_ISOM_OPEN_WRITE, NULL);
193 gf_isom_set_storage_mode(gpac_mux->file, GF_ISOM_STORE_FLAT /*STREAMABLE*/);
195 //gst_gpac_mux_clear (gpac_mux);
199 gst_gpac_mux_finalize (GObject * object)
201 GstGpacMux *gpac_mux;
203 gpac_mux = GST_GPAC_MUX (object);
205 if (gpac_mux->collect) {
206 gst_object_unref (gpac_mux->collect);
207 gpac_mux->collect = NULL;
210 G_OBJECT_CLASS (parent_class)->finalize (object);
214 gst_gpac_mux_gpac_pad_destroy_notify (GstCollectData * data)
216 GstGpacPad *gpacpad = (GstGpacPad *) data;
219 /* if (gpacpad->pagebuffers) {
220 while ((buf = g_queue_pop_head (gpacpad->pagebuffers)) != NULL) {
221 gst_buffer_unref (buf);
223 g_queue_free (gpacpad->pagebuffers);
224 gpacpad->pagebuffers = NULL;
228 static GstPadLinkReturn
229 gst_gpac_mux_sinkconnect (GstPad * pad, GstPad * peer)
231 GstGpacMux *gpac_mux;
233 gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad));
235 GST_DEBUG_OBJECT (gpac_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
237 gst_object_unref (gpac_mux);
239 return GST_PAD_LINK_OK;
243 gst_gpac_mux_request_new_pad (GstElement * element,
244 GstPadTemplate * templ, const gchar * req_name)
246 GstGpacMux *gpac_mux;
248 GstElementClass *klass;
249 gchar *padname = NULL;
251 gboolean is_video = FALSE;
253 g_return_val_if_fail (templ != NULL, NULL);
255 if (templ->direction != GST_PAD_SINK)
256 goto wrong_direction;
258 g_return_val_if_fail (GST_IS_GPAC_MUX (element), NULL);
259 gpac_mux = GST_GPAC_MUX (element);
261 klass = GST_ELEMENT_GET_CLASS (element);
263 if (req_name == NULL || strlen (req_name) < 6) {
264 /* no name given when requesting the pad, use random serial number */
267 /* parse serial number from requested padname */
268 serial = atoi (&req_name[5]);
271 if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
273 padname = g_strdup_printf ("video_%d", serial);
274 } else if (templ != gst_element_class_get_pad_template (klass, "audio_%d")) {
277 padname = g_strdup_printf ("audio_%d", serial);
283 /* create new pad with the name */
284 GST_DEBUG_OBJECT (gpac_mux, "Creating new pad for serial %d", serial);
285 printf ("XXXX new pad from template\n");
286 newpad = gst_pad_new_from_template (templ, padname);
289 /* construct our own wrapper data structure for the pad to
290 * keep track of its status */
294 gpacpad = (GstGpacPad *)
295 gst_collect_pads_add_pad_full (gpac_mux->collect, newpad,
296 sizeof (GstGpacPad), gst_gpac_mux_gpac_pad_destroy_notify);
299 gpacpad->is_video = is_video;
300 if (gpacpad->is_video) {
301 gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0,
302 GF_ISOM_MEDIA_VISUAL, 10 * 1000 /* time scale */);
304 gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0,
305 GF_ISOM_MEDIA_AUDIO, 48000 /*time scale */);
308 if (gpacpad->track_number == 0) {
309 g_warning ("Error while adding the new gpac track");
311 gf_isom_set_track_enabled(gpac_mux->file, gpacpad->track_number, 1);
313 GF_ESD *esd = gf_odf_desc_esd_new (0);
314 esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number);
316 gf_isom_new_mpeg4_description( gpac_mux->file, gpacpad->track_number,
317 esd, NULL, NULL, &(gpacpad->di));
319 gf_isom_set_visual_info (gpac_mux->file, gpacpad->track_number, gpacpad->di, 320, 240);//fixme
320 //gf_isom_set_cts_packing (gpac_mux->file, gpacpad->track_number, 0);
322 GF_ESD *esd = gf_odf_desc_esd_new (2);
323 esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number);
325 gf_isom_new_mpeg4_description(gpac_mux->file, gpacpad->track_number,
326 esd, NULL, NULL, &(gpacpad->di));
328 gf_isom_set_audio_info(gpac_mux->file, gpacpad->track_number,
329 gpacpad->di, 48000, 2 /*num channels */, 16);
335 /* setup some pad functions */
336 gst_pad_set_link_function (newpad, gst_gpac_mux_sinkconnect);
337 /* dd the pad to the element */
338 gst_element_add_pad (element, newpad);
345 g_warning ("gpac_mux: request pad that is not a SINK pad\n");
350 g_warning ("gpac_mux: this is not our template!\n");
356 gst_gpac_mux_release_pad (GstElement * element, GstPad * pad)
358 GstGpacMux *gpac_mux;
360 gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad));
362 gst_collect_pads_remove_pad (gpac_mux->collect, pad);
363 gst_element_remove_pad (element, pad);
368 gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event)
372 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
376 /* disable seeking for now */
382 return gst_pad_event_default (pad, event);
386 gst_gpac_mux_push_buffer (GstGpacMux * mux, GstBuffer * buffer)
390 /* fix up OFFSET and OFFSET_END again */
391 GST_BUFFER_OFFSET (buffer) = mux->offset;
392 mux->offset += GST_BUFFER_SIZE (buffer);
393 GST_BUFFER_OFFSET_END (buffer) = mux->offset;
395 /* Ensure we have monotonically increasing timestamps in the output. */
396 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
397 if (mux->last_ts != GST_CLOCK_TIME_NONE &&
398 GST_BUFFER_TIMESTAMP (buffer) < mux->last_ts)
399 GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
401 mux->last_ts = GST_BUFFER_TIMESTAMP (buffer);
405 caps = gst_pad_get_negotiated_caps (mux->srcpad);
406 gst_buffer_set_caps (buffer, caps);
408 gst_caps_unref (caps);
411 return gst_pad_push (mux->srcpad, buffer);
415 all_pads_eos (GstCollectPads * pads)
418 gboolean alleos = TRUE;
423 GstCollectData *data = (GstCollectData *) iter->data;
425 buf = gst_collect_pads_peek (pads, data);
428 gst_buffer_unref (buf);
438 gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux)
440 GstFlowReturn ret = GST_FLOW_OK;
445 GST_LOG_OBJECT (gpac_mux, "collected");
447 iter = gpac_mux->collect->data;
449 GstCollectData *data = (GstCollectData *) iter->data;
450 GstGpacPad *pad = (GstGpacPad *) data;
452 buf = gst_collect_pads_pop (gpac_mux->collect, data);
454 iter = g_slist_next (iter);
457 GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
460 printf ("xxxxxx buffer size: %d\n", GST_BUFFER_SIZE(buf)); fflush (stdout);
461 if (pad->frame_count < 300) {
462 GF_ISOSample *sample = gf_isom_sample_new();
464 sample->dataLength = GST_BUFFER_SIZE(buf);
465 sample->data = GST_BUFFER_DATA(buf);
466 sample->CTS_Offset = 0;
470 sample->DTS += 1000*pad->frame_count;
473 sample->DTS = 2048*pad->frame_count;
476 gf_isom_add_sample(gpac_mux->file, pad->track_number,
480 gf_isom_sample_del(&sample);
482 printf ("xxxx frames %d\n", pad->frame_count);
484 } else if (pad->frame_count == 300) {
485 printf ("XXX closing gpac output file\n"); fflush (stdout);
486 gf_isom_close (gpac_mux->file);
489 /* gstreamer output (push) */
490 if (gst_gpac_mux_push_buffer (gpac_mux, buf) != GST_FLOW_OK) {
491 printf ("EEEEEEEE push failed\n");
494 iter = g_slist_next (iter);
503 /* reset all variables in the gpac pads. */
505 gst_gpac_mux_init_collectpads (GstCollectPads * collect)
509 walk = collect->data;
511 GstGpacPad *gpacpad = (GstGpacPad *) walk->data;
513 //ogg_stream_init (&gpacpad->stream, gpacpad->serial);
514 //gpacpad->packetno = 0;
515 //gpacpad->pageno = 0;
516 //gpacpad->eos = FALSE;
517 /* we assume there will be some control data first for this pad */
518 //gpacpad->state = GST_GPAC_PAD_STATE_CONTROL;
519 //gpacpad->new_page = TRUE;
520 //gpacpad->first_delta = FALSE;
521 //gpacpad->prev_delta = FALSE;
522 //gpacpad->pagebuffers = g_queue_new ();
524 walk = g_slist_next (walk);
528 /* Clear all buffers from the collectpads object */
530 gst_gpac_mux_clear_collectpads (GstCollectPads * collect)
534 for (walk = collect->data; walk; walk = g_slist_next (walk)) {
535 GstGpacPad *gpacpad = (GstGpacPad *) walk->data;
538 //gpac_stream_clear (&gpacpad->stream);
540 while ((buf = g_queue_pop_head (gpacpad->pagebuffers)) != NULL) {
541 gst_buffer_unref (buf);
543 g_queue_free (gpacpad->pagebuffers);
544 gpacpad->pagebuffers = NULL;*/
548 static GstStateChangeReturn
549 gst_gpac_mux_change_state (GstElement * element, GstStateChange transition)
551 GstGpacMux *gpac_mux;
552 GstStateChangeReturn ret;
554 gpac_mux = GST_GPAC_MUX (element);
556 switch (transition) {
557 case GST_STATE_CHANGE_NULL_TO_READY:
559 case GST_STATE_CHANGE_READY_TO_PAUSED:
560 //gst_gpac_mux_clear (gpac_mux);
561 //gst_gpac_mux_init_collectpads (gpac_mux->collect);
562 gst_collect_pads_start (gpac_mux->collect);
564 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
566 case GST_STATE_CHANGE_PAUSED_TO_READY:
567 gst_collect_pads_stop (gpac_mux->collect);
573 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
575 switch (transition) {
576 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
578 case GST_STATE_CHANGE_PAUSED_TO_READY:
579 gst_gpac_mux_clear_collectpads (gpac_mux->collect);
581 case GST_STATE_CHANGE_READY_TO_NULL:
592 gst_gpac_mux_plugin_init (GstPlugin * plugin)
594 return gst_element_register (plugin, "gpacmux", GST_RANK_NONE,
598 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
601 "Muxes audio and video",
602 gst_gpac_mux_plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,