3 #include "emotion_gstreamer.h"
5 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
6 GST_PAD_SINK, GST_PAD_ALWAYS,
7 GST_STATIC_CAPS(GST_VIDEO_CAPS_YUV("{ I420, YV12, YUY2, NV12, ST12, TM12 }") ";"
8 GST_VIDEO_CAPS_BGRx ";" GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_BGRA));
10 GST_DEBUG_CATEGORY_STATIC(evas_video_sink_debug);
11 #define GST_CAT_DEFAULT evas_video_sink_debug
27 static guint evas_video_sink_signals[LAST_SIGNAL] = { 0, };
29 #define _do_init(bla) \
30 GST_DEBUG_CATEGORY_INIT(evas_video_sink_debug, \
35 GST_BOILERPLATE_FULL(EvasVideoSink,
42 static void unlock_buffer_mutex(EvasVideoSinkPrivate* priv);
44 static void evas_video_sink_main_render(void *data);
47 evas_video_sink_base_init(gpointer g_class)
49 GstElementClass* element_class;
51 element_class = GST_ELEMENT_CLASS(g_class);
52 gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sinktemplate));
53 gst_element_class_set_details_simple(element_class, "Evas video sink",
54 "Sink/Video", "Sends video data from a GStreamer pipeline to an Evas object",
55 "Vincent Torri <vtorri@univ-evry.fr>");
59 evas_video_sink_init(EvasVideoSink* sink, EvasVideoSinkClass* klass __UNUSED__)
61 EvasVideoSinkPrivate* priv;
64 sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, EVAS_TYPE_VIDEO_SINK, EvasVideoSinkPrivate);
66 priv->last_buffer = NULL;
69 priv->gformat = GST_VIDEO_FORMAT_UNKNOWN;
70 priv->eformat = EVAS_COLORSPACE_ARGB8888;
71 eina_lock_new(&priv->m);
72 eina_condition_new(&priv->c, &priv->m);
73 priv->unlocked = EINA_FALSE;
77 /**** Object methods ****/
79 _cleanup_priv(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
81 EvasVideoSinkPrivate* priv;
85 eina_lock_take(&priv->m);
88 eina_lock_release(&priv->m);
92 evas_video_sink_set_property(GObject * object, guint prop_id,
93 const GValue * value, GParamSpec * pspec)
96 EvasVideoSinkPrivate* priv;
98 sink = EVAS_VIDEO_SINK (object);
102 case PROP_EVAS_OBJECT:
103 eina_lock_take(&priv->m);
104 evas_object_event_callback_del(priv->o, EVAS_CALLBACK_FREE, _cleanup_priv);
105 priv->o = g_value_get_pointer (value);
106 INF("sink set Evas_Object %p.", priv->o);
107 evas_object_event_callback_add(priv->o, EVAS_CALLBACK_FREE, _cleanup_priv, priv);
108 eina_lock_release(&priv->m);
112 eina_lock_take(&priv->m);
113 priv->ev = g_value_get_pointer (value);
114 eina_lock_release(&priv->m);
117 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118 ERR("invalid property");
124 evas_video_sink_get_property(GObject * object, guint prop_id,
125 GValue * value, GParamSpec * pspec)
128 EvasVideoSinkPrivate* priv;
130 sink = EVAS_VIDEO_SINK (object);
134 case PROP_EVAS_OBJECT:
135 INF("sink get property.");
136 eina_lock_take(&priv->m);
137 g_value_set_pointer(value, priv->o);
138 eina_lock_release(&priv->m);
141 INF("sink get width.");
142 eina_lock_take(&priv->m);
143 g_value_set_int(value, priv->width);
144 eina_lock_release(&priv->m);
147 INF("sink get height.");
148 eina_lock_take(&priv->m);
149 g_value_set_int (value, priv->height);
150 eina_lock_release(&priv->m);
154 eina_lock_take(&priv->m);
155 g_value_set_pointer (value, priv->ev);
156 eina_lock_release(&priv->m);
159 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160 ERR("invalide property");
166 evas_video_sink_dispose(GObject* object)
169 EvasVideoSinkPrivate* priv;
173 sink = EVAS_VIDEO_SINK(object);
176 eina_lock_free(&priv->m);
177 eina_condition_free(&priv->c);
179 if (priv->last_buffer) {
180 gst_buffer_unref(priv->last_buffer);
181 priv->last_buffer = NULL;
184 G_OBJECT_CLASS(parent_class)->dispose(object);
188 /**** BaseSink methods ****/
190 gboolean evas_video_sink_set_caps(GstBaseSink *bsink, GstCaps *caps)
193 EvasVideoSinkPrivate* priv;
194 GstStructure *structure;
195 GstVideoFormat format;
198 sink = EVAS_VIDEO_SINK(bsink);
201 structure = gst_caps_get_structure(caps, 0);
203 if (!((gst_structure_get_int(structure, "width", &priv->width)
204 && gst_structure_get_int(structure, "height", &priv->height))))
207 if (gst_structure_get_fourcc(structure, "format", &fourcc))
211 case GST_MAKE_FOURCC('I', '4', '2', '0'):
212 priv->eformat = EVAS_COLORSPACE_YCBCR422P601_PL;
213 priv->gformat = GST_VIDEO_FORMAT_I420;
214 INF("sink set colorspace I420 [%i, %i]", priv->width, priv->height);
216 case GST_MAKE_FOURCC('Y', 'V', '1', '2'):
217 priv->eformat = EVAS_COLORSPACE_YCBCR422P601_PL;
218 priv->gformat = GST_VIDEO_FORMAT_YV12;
219 INF("sink set colorspace YV12 [%i, %i]", priv->width, priv->height);
221 case GST_MAKE_FOURCC('Y', 'U', 'Y', '2'):
222 priv->eformat = EVAS_COLORSPACE_YCBCR422601_PL;
223 priv->gformat = GST_VIDEO_FORMAT_YUY2;
224 INF("sink set colorspace YUY2 [%i, %i]", priv->width, priv->height);
226 case GST_MAKE_FOURCC('N', 'V', '1', '2'):
227 priv->eformat = EVAS_COLORSPACE_YCBCR420NV12601_PL;
228 INF("sink set colorspace NV12 [%i, %i]", priv->width, priv->height);
230 case GST_MAKE_FOURCC('S', 'T', '1', '2'):
231 case GST_MAKE_FOURCC('T', 'M', '1', '2'):
232 priv->eformat = EVAS_COLORSPACE_YCBCR420TM12601_PL;
233 INF("sink set colorspace ST12 [%i, %i]", priv->width, priv->height);
242 INF("fallback code !");
243 if (!gst_video_format_parse_caps(caps, &format, &priv->width, &priv->height))
245 ERR("Unable to parse caps.");
251 case GST_VIDEO_FORMAT_BGR: priv->eformat = EVAS_COLORSPACE_ARGB8888;
252 INF("sink set colorspace BGR [%i, %i]", priv->width, priv->height);
254 case GST_VIDEO_FORMAT_BGRx: priv->eformat = EVAS_COLORSPACE_ARGB8888;
255 INF("sink set colorspace BGRx [%i, %i]", priv->width, priv->height);
257 case GST_VIDEO_FORMAT_BGRA: priv->eformat = EVAS_COLORSPACE_ARGB8888;
258 INF("sink set colorspace BGRA [%i, %i]", priv->width, priv->height);
261 ERR("unsupported : %d\n", format);
264 priv->gformat = format;
271 evas_video_sink_start(GstBaseSink* base_sink)
273 EvasVideoSinkPrivate* priv;
278 priv = EVAS_VIDEO_SINK(base_sink)->priv;
279 eina_lock_take(&priv->m);
283 priv->unlocked = EINA_FALSE;
284 eina_lock_release(&priv->m);
289 evas_video_sink_stop(GstBaseSink* base_sink)
291 EvasVideoSinkPrivate* priv = EVAS_VIDEO_SINK(base_sink)->priv;
295 unlock_buffer_mutex(priv);
300 evas_video_sink_unlock(GstBaseSink* object)
306 sink = EVAS_VIDEO_SINK(object);
308 unlock_buffer_mutex(sink->priv);
310 return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock,
315 evas_video_sink_unlock_stop(GstBaseSink* object)
318 EvasVideoSinkPrivate* priv;
320 sink = EVAS_VIDEO_SINK(object);
323 INF("sink unlock stop");
325 eina_lock_take(&priv->m);
326 priv->unlocked = FALSE;
327 eina_lock_release(&priv->m);
329 return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop,
334 evas_video_sink_preroll(GstBaseSink* bsink, GstBuffer* buffer)
336 Emotion_Gstreamer_Buffer *send;
337 EvasVideoSinkPrivate *priv;
340 INF("sink preroll %p [%i]", GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
342 if (GST_BUFFER_SIZE(buffer) <= 0)
348 sink = EVAS_VIDEO_SINK(bsink);
351 send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_TRUE);
354 ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
360 evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
362 Emotion_Gstreamer_Buffer *send;
363 EvasVideoSinkPrivate *priv;
366 INF("sink render %p [%i]", GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
368 if (GST_BUFFER_SIZE(buffer) <= 0)
374 sink = EVAS_VIDEO_SINK(bsink);
377 eina_lock_take(&priv->m);
379 if (priv->unlocked) {
381 eina_lock_release(&priv->m);
385 send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_FALSE);
387 eina_lock_release(&priv->m);
388 return GST_FLOW_ERROR;
391 ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
393 eina_condition_wait(&priv->c);
394 eina_lock_release(&priv->m);
400 evas_video_sink_main_render(void *data)
402 Emotion_Gstreamer_Buffer *send;
403 Emotion_Gstreamer_Video *ev = NULL;
404 Emotion_Video_Stream *vstream;
405 EvasVideoSinkPrivate* priv;
407 unsigned char *evas_data;
408 const guint8 *gst_data;
409 GstFormat fmt = GST_FORMAT_TIME;
417 if (!priv) goto exit_point;
418 if (!priv->o) goto exit_point;
420 buffer = send->frame;
421 preroll = send->preroll;
423 if (priv->unlocked) goto exit_point;
425 gst_data = GST_BUFFER_DATA(buffer);
426 if (!gst_data) goto exit_point;
429 if (!ev) goto exit_point;
431 _emotion_gstreamer_video_pipeline_parse(ev, EINA_TRUE);
433 // This prevent a race condition when data are still in the pipe
434 // but the buffer size as changed because of a request from
435 // emotion smart (like on a file set).
436 evas_object_image_size_get(priv->o, &w, &h);
437 if (w != priv->width || h != priv->height)
440 INF("sink main render [%i, %i]", w, h);
442 evas_object_image_size_set(priv->o, priv->width, priv->height);
443 evas_object_image_alpha_set(priv->o, 0);
444 evas_object_image_colorspace_set(priv->o, priv->eformat);
446 evas_data = (unsigned char *)evas_object_image_data_get(priv->o, 1);
448 // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
449 // Here we convert to Evas's BGRA.
450 switch (priv->gformat)
452 case GST_VIDEO_FORMAT_BGR:
454 unsigned char *evas_tmp;
458 evas_tmp = evas_data;
459 /* FIXME: could this be optimized ? */
460 for (x = 0; x < priv->height; x++) {
461 for (y = 0; y < priv->width; y++) {
462 evas_tmp[0] = gst_data[0];
463 evas_tmp[1] = gst_data[1];
464 evas_tmp[2] = gst_data[2];
473 // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
474 // Here we convert to Evas's BGRA.
475 case GST_VIDEO_FORMAT_BGRx:
477 unsigned char *evas_tmp;
481 evas_tmp = evas_data;
482 /* FIXME: could this be optimized ? */
483 for (x = 0; x < priv->height; x++) {
484 for (y = 0; y < priv->width; y++) {
485 evas_tmp[0] = gst_data[0];
486 evas_tmp[1] = gst_data[1];
487 evas_tmp[2] = gst_data[2];
496 // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
497 // Here we convert to Evas's BGRA.
498 case GST_VIDEO_FORMAT_BGRA:
500 unsigned char *evas_tmp;
505 evas_tmp = evas_data;
506 /* FIXME: could this be optimized ? */
507 for (x = 0; x < priv->height; x++) {
508 for (y = 0; y < priv->width; y++) {
510 evas_tmp[0] = (gst_data[0] * alpha) / 255;
511 evas_tmp[1] = (gst_data[1] * alpha) / 255;
512 evas_tmp[2] = (gst_data[2] * alpha) / 255;
521 case GST_VIDEO_FORMAT_I420:
524 const unsigned char **rows;
526 evas_object_image_pixels_dirty_set(priv->o, 1);
527 rows = (const unsigned char **)evas_data;
529 for (i = 0; i < priv->height; i++)
530 rows[i] = &gst_data[i * priv->width];
532 rows += priv->height;
533 for (i = 0; i < (priv->height / 2); i++)
534 rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)];
536 rows += priv->height / 2;
537 for (i = 0; i < (priv->height / 2); i++)
538 rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)];
542 case GST_VIDEO_FORMAT_YV12:
545 const unsigned char **rows;
547 evas_object_image_pixels_dirty_set(priv->o, 1);
549 rows = (const unsigned char **)evas_data;
551 for (i = 0; i < priv->height; i++)
552 rows[i] = &gst_data[i * priv->width];
554 rows += priv->height;
555 for (i = 0; i < (priv->height / 2); i++)
556 rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)];
558 rows += priv->height / 2;
559 for (i = 0; i < (priv->height / 2); i++)
560 rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)];
564 case GST_VIDEO_FORMAT_YUY2:
567 const unsigned char **rows;
569 evas_object_image_pixels_dirty_set(priv->o, 1);
571 rows = (const unsigned char **)evas_data;
573 for (i = 0; i < priv->height; i++)
574 rows[i] = &gst_data[i * priv->width * 2];
579 switch (priv->eformat)
581 case EVAS_COLORSPACE_YCBCR420NV12601_PL:
584 const unsigned char **rows;
586 evas_object_image_pixels_dirty_set(priv->o, 1);
588 rows = (const unsigned char **)evas_data;
590 for (i = 0; i < priv->height; i++)
591 rows[i] = &gst_data[i * priv->width];
593 rows += priv->height;
594 for (i = 0; i < (priv->height / 2); i++)
595 rows[i] = &gst_data[priv->height * priv->width + i * priv->width];
598 case EVAS_COLORSPACE_YCBCR420TM12601_PL:
601 const unsigned char **rows;
603 evas_object_image_pixels_dirty_set(priv->o, 1);
605 rows = (const unsigned char **)evas_data;
607 for (i = 0; i < (priv->height / 32) / 2; i++)
608 rows[i] = &gst_data[i * priv->width * 2 * 32];
610 if ((priv->height / 32) % 2)
611 rows[i] = &gst_data[i * priv->width * 2 * 32];
613 rows += priv->height;
614 for (i = 0; i < ((priv->height / 2) / 32) / 2; ++i)
615 rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2) * 2 * 16];
619 WRN("No way to decode %x colorspace !", priv->eformat);
623 evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height);
624 evas_object_image_data_set(priv->o, evas_data);
625 evas_object_image_pixels_dirty_set(priv->o, 0);
627 _emotion_frame_new(ev->obj);
629 vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1);
631 gst_element_query_position(ev->pipeline, &fmt, &pos);
632 ev->position = (double)pos / (double)GST_SECOND;
634 vstream->width = priv->width;
635 vstream->height = priv->height;
636 ev->ratio = (double) priv->width / (double) priv->height;
638 _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time);
639 _emotion_frame_resize(ev->obj, priv->width, priv->height, ev->ratio);
641 if (priv->last_buffer) gst_buffer_unref(priv->last_buffer);
642 priv->last_buffer = gst_buffer_ref(buffer);
645 emotion_gstreamer_buffer_free(send);
647 if (preroll || !priv->o || !ev) return ;
649 eina_lock_take(&priv->m);
651 eina_condition_signal(&priv->c);
653 eina_lock_release(&priv->m);
657 unlock_buffer_mutex(EvasVideoSinkPrivate* priv)
659 eina_lock_take(&priv->m);
660 priv->unlocked = EINA_TRUE;
662 eina_condition_signal(&priv->c);
663 eina_lock_release(&priv->m);
667 marshal_VOID__MINIOBJECT(GClosure * closure, GValue * return_value __UNUSED__,
668 guint n_param_values, const GValue * param_values,
669 gpointer invocation_hint __UNUSED__, gpointer marshal_data)
671 typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1, gpointer data2);
672 marshalfunc_VOID__MINIOBJECT callback;
674 gpointer data1, data2;
676 cc = (GCClosure *) closure;
678 g_return_if_fail(n_param_values == 2);
680 if (G_CCLOSURE_SWAP_DATA(closure)) {
681 data1 = closure->data;
682 data2 = g_value_peek_pointer(param_values + 0);
684 data1 = g_value_peek_pointer(param_values + 0);
685 data2 = closure->data;
687 callback = (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data : cc->callback);
689 callback(data1, gst_value_get_mini_object(param_values + 1), data2);
693 evas_video_sink_class_init(EvasVideoSinkClass* klass)
695 GObjectClass* gobject_class;
696 GstBaseSinkClass* gstbase_sink_class;
698 gobject_class = G_OBJECT_CLASS(klass);
699 gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
701 g_type_class_add_private(klass, sizeof(EvasVideoSinkPrivate));
703 gobject_class->set_property = evas_video_sink_set_property;
704 gobject_class->get_property = evas_video_sink_get_property;
706 g_object_class_install_property (gobject_class, PROP_EVAS_OBJECT,
707 g_param_spec_pointer ("evas-object", "Evas Object",
708 "The Evas object where the display of the video will be done",
711 g_object_class_install_property (gobject_class, PROP_WIDTH,
712 g_param_spec_int ("width", "Width",
713 "The width of the video",
714 0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
716 g_object_class_install_property (gobject_class, PROP_HEIGHT,
717 g_param_spec_int ("height", "Height",
718 "The height of the video",
719 0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
720 g_object_class_install_property (gobject_class, PROP_EV,
721 g_param_spec_pointer ("ev", "Emotion_Gstreamer_Video",
722 "THe internal data of the emotion object",
725 gobject_class->dispose = evas_video_sink_dispose;
727 gstbase_sink_class->set_caps = evas_video_sink_set_caps;
728 gstbase_sink_class->stop = evas_video_sink_stop;
729 gstbase_sink_class->start = evas_video_sink_start;
730 gstbase_sink_class->unlock = evas_video_sink_unlock;
731 gstbase_sink_class->unlock_stop = evas_video_sink_unlock_stop;
732 gstbase_sink_class->render = evas_video_sink_render;
733 gstbase_sink_class->preroll = evas_video_sink_preroll;
735 evas_video_sink_signals[REPAINT_REQUESTED] = g_signal_new("repaint-requested",
736 G_TYPE_FROM_CLASS(klass),
737 (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
741 marshal_VOID__MINIOBJECT,
742 G_TYPE_NONE, 1, GST_TYPE_BUFFER);
746 gstreamer_plugin_init (GstPlugin * plugin)
748 return gst_element_register (plugin,
751 EVAS_TYPE_VIDEO_SINK);
755 _emotion_gstreamer_pause(void *data, Ecore_Thread *thread)
757 Emotion_Gstreamer_Video *ev = data;
759 if (ecore_thread_check(thread) || !ev->pipeline) return ;
761 gst_element_set_state(ev->pipeline, GST_STATE_PAUSED);
765 _emotion_gstreamer_cancel(void *data, Ecore_Thread *thread)
767 Emotion_Gstreamer_Video *ev = data;
769 ev->threads = eina_list_remove(ev->threads, thread);
771 if (getenv("EMOTION_GSTREAMER_DOT")) GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(ev->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, getenv("EMOTION_GSTREAMER_DOT"));
773 if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
778 _emotion_gstreamer_end(void *data, Ecore_Thread *thread)
780 Emotion_Gstreamer_Video *ev = data;
782 ev->threads = eina_list_remove(ev->threads, thread);
786 gst_element_set_state(ev->pipeline, GST_STATE_PLAYING);
787 ev->play_started = 1;
790 if (getenv("EMOTION_GSTREAMER_DOT")) GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(ev->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, getenv("EMOTION_GSTREAMER_DOT"));
792 if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
795 _emotion_gstreamer_video_pipeline_parse(data, EINA_TRUE);
799 gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev,
808 obj = emotion_object_image_get(o);
811 ERR("Not Evas_Object specified");
815 playbin = gst_element_factory_make("playbin2", "playbin");
818 ERR("Unable to create 'playbin' GstElement.");
822 sink = gst_element_factory_make("emotion-sink", "sink");
825 ERR("Unable to create 'emotion-sink' GstElement.");
829 #define GST_PLAY_FLAG_NATIVE_VIDEO (1 << 6)
830 #define GST_PLAY_FLAG_DOWNLOAD (1 << 7)
831 #define GST_PLAY_FLAG_BUFFERING (1 << 8)
833 g_object_set(G_OBJECT(sink), "evas-object", obj, NULL);
834 g_object_set(G_OBJECT(sink), "ev", ev, NULL);
836 g_object_get(G_OBJECT(playbin), "flags", &flags, NULL);
837 g_object_set(G_OBJECT(playbin), "flags", flags | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_DOWNLOAD | GST_PLAY_FLAG_BUFFERING, NULL);
838 g_object_set(G_OBJECT(playbin), "video-sink", sink, NULL);
839 g_object_set(G_OBJECT(playbin), "uri", uri, NULL);
841 ev->pipeline = playbin;
843 ev->threads = eina_list_append(ev->threads,
844 ecore_thread_run(_emotion_gstreamer_pause,
845 _emotion_gstreamer_end,
846 _emotion_gstreamer_cancel,
849 /** NOTE: you need to set: GST_DEBUG_DUMP_DOT_DIR=/tmp EMOTION_ENGINE=gstreamer to save the $EMOTION_GSTREAMER_DOT file in '/tmp' */
850 /** then call dot -Tpng -oemotion_pipeline.png /tmp/$TIMESTAMP-$EMOTION_GSTREAMER_DOT.dot */
851 if (getenv("EMOTION_GSTREAMER_DOT")) GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(playbin), GST_DEBUG_GRAPH_SHOW_ALL, getenv("EMOTION_GSTREAMER_DOT"));
856 gst_object_unref(playbin);