5ed548ae144f50538ae856cf418e50d3021acdeb
[profile/ivi/emotion.git] / src / modules / gstreamer / emotion_sink.c
1 #include <Ecore.h>
2
3 #include "emotion_gstreamer.h"
4
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 }") ";"
8                                                                                    GST_VIDEO_CAPS_BGRx ";" GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_BGRA));
9
10 GST_DEBUG_CATEGORY_STATIC(evas_video_sink_debug);
11 #define GST_CAT_DEFAULT evas_video_sink_debug
12
13 enum {
14   REPAINT_REQUESTED,
15   LAST_SIGNAL
16 };
17
18 enum {
19   PROP_0,
20   PROP_EVAS_OBJECT,
21   PROP_WIDTH,
22   PROP_HEIGHT,
23   PROP_EV,
24   PROP_LAST
25 };
26
27 static guint evas_video_sink_signals[LAST_SIGNAL] = { 0, };
28
29 #define _do_init(bla)                                   \
30   GST_DEBUG_CATEGORY_INIT(evas_video_sink_debug,        \
31                           "emotion-sink",               \
32                           0,                            \
33                           "emotion video sink")
34
35 GST_BOILERPLATE_FULL(EvasVideoSink,
36                      evas_video_sink,
37                      GstVideoSink,
38                      GST_TYPE_VIDEO_SINK,
39                      _do_init);
40
41
42 static void unlock_buffer_mutex(EvasVideoSinkPrivate* priv);
43
44 static void evas_video_sink_main_render(void *data);
45
46 static void
47 evas_video_sink_base_init(gpointer g_class)
48 {
49    GstElementClass* element_class;
50
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>");
56 }
57
58 static void
59 evas_video_sink_init(EvasVideoSink* sink, EvasVideoSinkClass* klass __UNUSED__)
60 {
61    EvasVideoSinkPrivate* priv;
62
63    INF("sink init");
64    sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, EVAS_TYPE_VIDEO_SINK, EvasVideoSinkPrivate);
65    priv->o = NULL;
66    priv->last_buffer = NULL;
67    priv->width = 0;
68    priv->height = 0;
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;
74 }
75
76
77 /**** Object methods ****/
78 static void
79 _cleanup_priv(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
80 {
81    EvasVideoSinkPrivate* priv;
82
83    priv = data;
84
85    eina_lock_take(&priv->m);
86    if (priv->o == obj)
87      priv->o = NULL;
88    eina_lock_release(&priv->m);
89 }
90
91 static void
92 evas_video_sink_set_property(GObject * object, guint prop_id,
93                              const GValue * value, GParamSpec * pspec)
94 {
95    EvasVideoSink* sink;
96    EvasVideoSinkPrivate* priv;
97
98    sink = EVAS_VIDEO_SINK (object);
99    priv = sink->priv;
100
101    switch (prop_id) {
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        evas_object_event_callback_add(priv->o, EVAS_CALLBACK_FREE, _cleanup_priv, priv);
107        eina_lock_release(&priv->m);
108        break;
109     case PROP_EV:
110        eina_lock_take(&priv->m);
111        priv->ev = g_value_get_pointer (value);
112        eina_lock_release(&priv->m);
113        break;
114     default:
115        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116        ERR("invalid property");
117        break;
118    }
119 }
120
121 static void
122 evas_video_sink_get_property(GObject * object, guint prop_id,
123                              GValue * value, GParamSpec * pspec)
124 {
125    EvasVideoSink* sink;
126    EvasVideoSinkPrivate* priv;
127
128    sink = EVAS_VIDEO_SINK (object);
129    priv = sink->priv;
130
131    switch (prop_id) {
132     case PROP_EVAS_OBJECT:
133        eina_lock_take(&priv->m);
134        g_value_set_pointer (value, priv->o);
135        eina_lock_release(&priv->m);
136        break;
137     case PROP_WIDTH:
138        eina_lock_take(&priv->m);
139        g_value_set_int(value, priv->width);
140        eina_lock_release(&priv->m);
141        break;
142     case PROP_HEIGHT:
143        eina_lock_take(&priv->m);
144        g_value_set_int (value, priv->height);
145        eina_lock_release(&priv->m);
146        break;
147     case PROP_EV:
148        eina_lock_take(&priv->m);
149        g_value_set_pointer (value, priv->ev);
150        eina_lock_release(&priv->m);
151        break;
152     default:
153        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
154        ERR("invalide property");
155        break;
156    }
157 }
158
159 static void
160 evas_video_sink_dispose(GObject* object)
161 {
162    EvasVideoSink* sink;
163    EvasVideoSinkPrivate* priv;
164
165    sink = EVAS_VIDEO_SINK(object);
166    priv = sink->priv;
167
168    eina_lock_free(&priv->m);
169    eina_condition_free(&priv->c);
170
171    if (priv->last_buffer) {
172       gst_buffer_unref(priv->last_buffer);
173       priv->last_buffer = NULL;
174    }
175
176    G_OBJECT_CLASS(parent_class)->dispose(object);
177 }
178
179
180 /**** BaseSink methods ****/
181
182 gboolean evas_video_sink_set_caps(GstBaseSink *bsink, GstCaps *caps)
183 {
184    EvasVideoSink* sink;
185    EvasVideoSinkPrivate* priv;
186    GstVideoFormat format;
187    int width;
188    int height;
189
190    sink = EVAS_VIDEO_SINK(bsink);
191    priv = sink->priv;
192
193    if (G_UNLIKELY(!gst_video_format_parse_caps(caps, &format, &width, &height))) {
194       ERR("Unable to parse caps.");
195       return FALSE;
196    }
197
198    priv->width = width;
199    priv->height = height;
200
201    printf("%p format :", priv->o);
202    switch (format)
203      {
204       case GST_VIDEO_FORMAT_I420: priv->eformat = EVAS_COLORSPACE_YCBCR422P601_PL;
205          printf ("I420\n");
206          break;
207       case GST_VIDEO_FORMAT_YV12: priv->eformat = EVAS_COLORSPACE_YCBCR422P601_PL;
208          printf ("YV12\n");
209          break;
210       case GST_VIDEO_FORMAT_YUY2: priv->eformat = EVAS_COLORSPACE_YCBCR422601_PL;
211          printf("YUY2\n");
212          break;
213       case GST_VIDEO_FORMAT_BGR: priv->eformat = EVAS_COLORSPACE_ARGB8888;
214          printf ("BGR\n");
215          break;
216       case GST_VIDEO_FORMAT_BGRx: priv->eformat = EVAS_COLORSPACE_ARGB8888;
217          printf ("BGRx\n");
218          break;
219       case GST_VIDEO_FORMAT_BGRA: priv->eformat = EVAS_COLORSPACE_ARGB8888;
220          printf ("BGRA\n");
221          break;
222       default:
223          ERR("unsupported : %d\n", format);
224          return FALSE;
225      }
226    priv->gformat = format;
227
228    return TRUE;
229 }
230
231 static gboolean
232 evas_video_sink_start(GstBaseSink* base_sink)
233 {
234    EvasVideoSinkPrivate* priv;
235    gboolean res = TRUE;
236
237    priv = EVAS_VIDEO_SINK(base_sink)->priv;
238    eina_lock_take(&priv->m);
239    if (!priv->o)
240      res = FALSE;
241    else
242      priv->unlocked = EINA_FALSE;
243    eina_lock_release(&priv->m);
244    return res;
245 }
246
247 static gboolean
248 evas_video_sink_stop(GstBaseSink* base_sink)
249 {
250    EvasVideoSinkPrivate* priv = EVAS_VIDEO_SINK(base_sink)->priv;
251
252    unlock_buffer_mutex(priv);
253    return TRUE;
254 }
255
256 static gboolean
257 evas_video_sink_unlock(GstBaseSink* object)
258 {
259    EvasVideoSink* sink;
260
261    sink = EVAS_VIDEO_SINK(object);
262
263    unlock_buffer_mutex(sink->priv);
264
265    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock,
266                                        (object), TRUE);
267 }
268
269 static gboolean
270 evas_video_sink_unlock_stop(GstBaseSink* object)
271 {
272    EvasVideoSink* sink;
273    EvasVideoSinkPrivate* priv;
274
275    sink = EVAS_VIDEO_SINK(object);
276    priv = sink->priv;
277
278    eina_lock_take(&priv->m);
279    priv->unlocked = FALSE;
280    eina_lock_release(&priv->m);
281
282    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop,
283                                        (object), TRUE);
284 }
285
286 static GstFlowReturn
287 evas_video_sink_preroll(GstBaseSink* bsink, GstBuffer* buffer)
288 {
289    Emotion_Gstreamer_Buffer *send;
290    EvasVideoSinkPrivate *priv;
291    EvasVideoSink *sink;
292
293    sink = EVAS_VIDEO_SINK(bsink);
294    priv = sink->priv;
295
296    send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_TRUE);
297
298    if (send)
299      ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
300
301    return GST_FLOW_OK;
302 }
303
304 static GstFlowReturn
305 evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
306 {
307    Emotion_Gstreamer_Buffer *send;
308    EvasVideoSinkPrivate *priv;
309    EvasVideoSink *sink;
310
311    sink = EVAS_VIDEO_SINK(bsink);
312    priv = sink->priv;
313
314    eina_lock_take(&priv->m);
315
316    if (priv->unlocked) {
317       ERR("LOCKED");
318       eina_lock_release(&priv->m);
319       return GST_FLOW_OK;
320    }
321
322    send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_FALSE);
323    if (!send) {
324       eina_lock_release(&priv->m);
325       return GST_FLOW_ERROR;
326    }
327
328    ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
329
330    eina_condition_wait(&priv->c);
331    eina_lock_release(&priv->m);
332
333    return GST_FLOW_OK;
334 }
335
336 static void
337 evas_video_sink_main_render(void *data)
338 {
339    Emotion_Gstreamer_Buffer *send;
340    Emotion_Gstreamer_Video *ev = NULL;
341    Emotion_Video_Stream *vstream;
342    EvasVideoSinkPrivate* priv;
343    GstBuffer* buffer;
344    unsigned char *evas_data;
345    const guint8 *gst_data;
346    GstFormat fmt = GST_FORMAT_TIME;
347    Evas_Coord w, h;
348    gint64 pos;
349    Eina_Bool preroll;
350
351    send = data;
352
353    priv = send->sink;
354    if (!priv) goto exit_point;
355    if (!priv->o) goto exit_point;
356
357    buffer = send->frame;
358    preroll = send->preroll;
359
360    if (priv->unlocked) goto exit_point;
361
362    gst_data = GST_BUFFER_DATA(buffer);
363    if (!gst_data) goto exit_point;
364
365    ev = send->ev;
366    if (!ev) goto exit_point;
367
368    _emotion_gstreamer_video_pipeline_parse(ev, EINA_TRUE);
369
370    // This prevent a race condition when data are still in the pipe
371    // but the buffer size as changed because of a request from
372    // emotion smart (like on a file set).
373    evas_object_image_size_get(priv->o, &w, &h);
374    if (w != priv->width || h != priv->height)
375      goto exit_point;
376
377    evas_object_image_size_set(priv->o, priv->width, priv->height);
378    evas_object_image_alpha_set(priv->o, 0);
379    evas_object_image_colorspace_set(priv->o, priv->eformat);
380
381    evas_data = (unsigned char *)evas_object_image_data_get(priv->o, 1);
382
383    // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
384    // Here we convert to Evas's BGRA.
385    switch (priv->gformat)
386      {
387       case GST_VIDEO_FORMAT_BGR:
388         {
389            unsigned char *evas_tmp;
390            int x;
391            int y;
392
393            evas_tmp = evas_data;
394            /* FIXME: could this be optimized ? */
395            for (x = 0; x < priv->height; x++) {
396               for (y = 0; y < priv->width; y++) {
397                  evas_tmp[0] = gst_data[0];
398                  evas_tmp[1] = gst_data[1];
399                  evas_tmp[2] = gst_data[2];
400                  evas_tmp[3] = 255;
401                  gst_data += 3;
402                  evas_tmp += 4;
403               }
404            }
405            break;
406         }
407
408         // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
409         // Here we convert to Evas's BGRA.
410       case GST_VIDEO_FORMAT_BGRx:
411         {
412            unsigned char *evas_tmp;
413            int x;
414            int y;
415
416            evas_tmp = evas_data;
417            /* FIXME: could this be optimized ? */
418            for (x = 0; x < priv->height; x++) {
419               for (y = 0; y < priv->width; y++) {
420                  evas_tmp[0] = gst_data[0];
421                  evas_tmp[1] = gst_data[1];
422                  evas_tmp[2] = gst_data[2];
423                  evas_tmp[3] = 255;
424                  gst_data += 4;
425                  evas_tmp += 4;
426               }
427            }
428            break;
429         }
430
431         // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
432         // Here we convert to Evas's BGRA.
433       case GST_VIDEO_FORMAT_BGRA:
434         {
435            unsigned char *evas_tmp;
436            int x;
437            int y;
438            unsigned char alpha;
439
440            evas_tmp = evas_data;
441            /* FIXME: could this be optimized ? */
442            for (x = 0; x < priv->height; x++) {
443               for (y = 0; y < priv->width; y++) {
444                  alpha = gst_data[3];
445                  evas_tmp[0] = (gst_data[0] * alpha) / 255;
446                  evas_tmp[1] = (gst_data[1] * alpha) / 255;
447                  evas_tmp[2] = (gst_data[2] * alpha) / 255;
448                  evas_tmp[3] = alpha;
449                  gst_data += 4;
450                  evas_tmp += 4;
451               }
452            }
453            break;
454         }
455
456       case GST_VIDEO_FORMAT_I420:
457         {
458            int i;
459            const unsigned char **rows;
460
461            evas_object_image_pixels_dirty_set(priv->o, 1);
462            rows = (const unsigned char **)evas_data;
463
464            for (i = 0; i < priv->height; i++)
465              rows[i] = &gst_data[i * priv->width];
466
467            rows += priv->height;
468            for (i = 0; i < (priv->height / 2); i++)
469              rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)];
470
471            rows += priv->height / 2;
472            for (i = 0; i < (priv->height / 2); i++)
473              rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)];
474            break;
475         }
476
477       case GST_VIDEO_FORMAT_YV12:
478         {
479            int i;
480            const unsigned char **rows;
481
482            evas_object_image_pixels_dirty_set(priv->o, 1);
483
484            rows = (const unsigned char **)evas_data;
485
486            for (i = 0; i < priv->height; i++)
487              rows[i] = &gst_data[i * priv->width];
488
489            rows += priv->height;
490            for (i = 0; i < (priv->height / 2); i++)
491              rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)];
492
493            rows += priv->height / 2;
494            for (i = 0; i < (priv->height / 2); i++)
495              rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)];
496            break;
497         }
498
499       case GST_VIDEO_FORMAT_YUY2:
500         {
501            int i;
502            const unsigned char **rows;
503
504            evas_object_image_pixels_dirty_set(priv->o, 1);
505
506            rows = (const unsigned char **)evas_data;
507
508            for (i = 0; i < priv->height; i++)
509              rows[i] = &gst_data[i * priv->width * 2];
510            break;
511         }
512      }
513
514    evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height);
515    evas_object_image_data_set(priv->o, evas_data);
516    evas_object_image_pixels_dirty_set(priv->o, 0);
517
518    _emotion_frame_new(ev->obj);
519
520    vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1);
521
522    gst_element_query_position(ev->pipeline, &fmt, &pos);
523    ev->position = (double)pos / (double)GST_SECOND;
524
525    vstream->width = priv->width;
526    vstream->height = priv->height;
527    ev->ratio = (double) priv->width / (double) priv->height;
528
529    _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time);
530    _emotion_frame_resize(ev->obj, priv->width, priv->height, ev->ratio);
531
532    if (priv->last_buffer) gst_buffer_unref(priv->last_buffer);
533    priv->last_buffer = gst_buffer_ref(buffer);
534
535  exit_point:
536    emotion_gstreamer_buffer_free(send);
537
538    if (preroll || !priv->o || !ev) return ;
539
540    eina_lock_take(&priv->m);
541    if (!priv->unlocked)
542      eina_condition_signal(&priv->c);
543
544    eina_lock_release(&priv->m);
545 }
546
547 static void
548 unlock_buffer_mutex(EvasVideoSinkPrivate* priv)
549 {
550    eina_lock_take(&priv->m);
551    priv->unlocked = EINA_TRUE;
552
553    eina_condition_signal(&priv->c);
554    eina_lock_release(&priv->m);
555 }
556
557 static void
558 marshal_VOID__MINIOBJECT(GClosure * closure, GValue * return_value __UNUSED__,
559                          guint n_param_values, const GValue * param_values,
560                          gpointer invocation_hint __UNUSED__, gpointer marshal_data)
561 {
562    typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1, gpointer data2);
563    marshalfunc_VOID__MINIOBJECT callback;
564    GCClosure *cc;
565    gpointer data1, data2;
566
567    cc = (GCClosure *) closure;
568
569    g_return_if_fail(n_param_values == 2);
570
571    if (G_CCLOSURE_SWAP_DATA(closure)) {
572       data1 = closure->data;
573       data2 = g_value_peek_pointer(param_values + 0);
574    } else {
575       data1 = g_value_peek_pointer(param_values + 0);
576       data2 = closure->data;
577    }
578    callback = (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data : cc->callback);
579
580    callback(data1, gst_value_get_mini_object(param_values + 1), data2);
581 }
582
583 static void
584 evas_video_sink_class_init(EvasVideoSinkClass* klass)
585 {
586    GObjectClass* gobject_class;
587    GstBaseSinkClass* gstbase_sink_class;
588
589    gobject_class = G_OBJECT_CLASS(klass);
590    gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
591
592    g_type_class_add_private(klass, sizeof(EvasVideoSinkPrivate));
593
594    gobject_class->set_property = evas_video_sink_set_property;
595    gobject_class->get_property = evas_video_sink_get_property;
596
597    g_object_class_install_property (gobject_class, PROP_EVAS_OBJECT,
598                                     g_param_spec_pointer ("evas-object", "Evas Object",
599                                                           "The Evas object where the display of the video will be done",
600                                                           G_PARAM_READWRITE));
601
602    g_object_class_install_property (gobject_class, PROP_WIDTH,
603                                     g_param_spec_int ("width", "Width",
604                                                       "The width of the video",
605                                                       0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
606
607    g_object_class_install_property (gobject_class, PROP_HEIGHT,
608                                     g_param_spec_int ("height", "Height",
609                                                       "The height of the video",
610                                                       0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
611    g_object_class_install_property (gobject_class, PROP_EV,
612                                     g_param_spec_pointer ("ev", "Emotion_Gstreamer_Video",
613                                                           "THe internal data of the emotion object",
614                                                           G_PARAM_READWRITE));
615
616    gobject_class->dispose = evas_video_sink_dispose;
617
618    gstbase_sink_class->set_caps = evas_video_sink_set_caps;
619    gstbase_sink_class->stop = evas_video_sink_stop;
620    gstbase_sink_class->start = evas_video_sink_start;
621    gstbase_sink_class->unlock = evas_video_sink_unlock;
622    gstbase_sink_class->unlock_stop = evas_video_sink_unlock_stop;
623    gstbase_sink_class->render = evas_video_sink_render;
624    gstbase_sink_class->preroll = evas_video_sink_preroll;
625
626    evas_video_sink_signals[REPAINT_REQUESTED] = g_signal_new("repaint-requested",
627                                                              G_TYPE_FROM_CLASS(klass),
628                                                              (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
629                                                              0,
630                                                              0,
631                                                              0,
632                                                              marshal_VOID__MINIOBJECT,
633                                                              G_TYPE_NONE, 1, GST_TYPE_BUFFER);
634 }
635
636 gboolean
637 gstreamer_plugin_init (GstPlugin * plugin)
638 {
639    return gst_element_register (plugin,
640                                 "emotion-sink",
641                                 GST_RANK_NONE,
642                                 EVAS_TYPE_VIDEO_SINK);
643 }
644
645 static void
646 _emotion_gstreamer_pause(void *data, Ecore_Thread *thread)
647 {
648    Emotion_Gstreamer_Video *ev = data;
649
650    if (ecore_thread_check(thread) || !ev->pipeline) return ;
651
652    gst_element_set_state(ev->pipeline, GST_STATE_PAUSED);
653 }
654
655 static void
656 _emotion_gstreamer_cancel(void *data, Ecore_Thread *thread)
657 {
658    Emotion_Gstreamer_Video *ev = data;
659
660    ev->threads = eina_list_remove(ev->threads, thread);
661
662    if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
663      em_shutdown(ev);
664 }
665
666 static void
667 _emotion_gstreamer_end(void *data, Ecore_Thread *thread)
668 {
669    Emotion_Gstreamer_Video *ev = data;
670
671    ev->threads = eina_list_remove(ev->threads, thread);
672
673    if (ev->play)
674      {
675         gst_element_set_state(ev->pipeline, GST_STATE_PLAYING);
676         ev->play_started = 1;
677      }
678
679    if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
680      em_shutdown(ev);
681    else
682      _emotion_gstreamer_video_pipeline_parse(data, EINA_TRUE);
683 }
684
685 GstElement *
686 gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev,
687                          Evas_Object *o,
688                          const char *uri)
689 {
690    GstElement *playbin;
691    GstElement *sink;
692    Evas_Object *obj;
693
694    obj = emotion_object_image_get(o);
695    if (!obj)
696      {
697         ERR("Not Evas_Object specified");
698         return NULL;
699      }
700
701    playbin = gst_element_factory_make("playbin2", "playbin");
702    if (!playbin)
703      {
704         ERR("Unable to create 'playbin' GstElement.");
705         return NULL;
706      }
707
708    sink = gst_element_factory_make("emotion-sink", "sink");
709    if (!sink)
710      {
711         ERR("Unable to create 'emotion-sink' GstElement.");
712         goto unref_pipeline;
713      }
714
715    g_object_set(G_OBJECT(sink), "evas-object", obj, NULL);
716    g_object_set(G_OBJECT(sink), "ev", ev, NULL);
717
718    g_object_set(G_OBJECT(playbin), "video-sink", sink, NULL);
719    g_object_set(G_OBJECT(playbin), "uri", uri, NULL);
720
721    ev->pipeline = playbin;
722    ev->sink = sink;
723    ev->threads = eina_list_append(ev->threads,
724                                   ecore_thread_run(_emotion_gstreamer_pause,
725                                                    _emotion_gstreamer_end,
726                                                    _emotion_gstreamer_cancel,
727                                                    ev));
728
729    /** NOTE: you need to set: GST_DEBUG_DUMP_DOT_DIR=/tmp EMOTION_ENGINE=gstreamer to save the $EMOTION_GSTREAMER_DOT file in '/tmp' */
730    /** then call dot -Tpng -oemotion_pipeline.png /tmp/$TIMESTAMP-$EMOTION_GSTREAMER_DOT.dot */
731    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"));
732
733    return playbin;
734
735  unref_pipeline:
736    gst_object_unref(playbin);
737    return NULL;
738 }