emotion: handle evas NV12 and MT12 colorspace format.
[framework/uifw/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, NV12, ST12, TM12 }") ";"
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    GstStructure *structure;
187    GstVideoFormat format;
188    guint32 fourcc;
189    int width;
190    int height;
191
192    sink = EVAS_VIDEO_SINK(bsink);
193    priv = sink->priv;
194
195    if (!gst_video_format_parse_caps(caps, &format, &width, &height))
196      {
197         ERR("Unable to parse caps.");
198         return FALSE;
199      }
200
201    priv->width = width;
202    priv->height = height;
203
204    structure = gst_caps_get_structure(caps, 0);
205
206    if (gst_structure_get_fourcc(structure, "format", &fourcc))
207      {
208         switch (fourcc)
209           {
210            case GST_MAKE_FOURCC('I', '4', '2', '0'):
211               priv->eformat = EVAS_COLORSPACE_YCBCR422P601_PL;
212               printf ("I420\n");
213               break;
214            case GST_MAKE_FOURCC('Y', 'V', '1', '2'):
215               priv->eformat = EVAS_COLORSPACE_YCBCR422P601_PL;
216               printf ("YV12\n");
217               break;
218            case GST_MAKE_FOURCC('Y', 'U', 'Y', '2'):
219               priv->eformat = EVAS_COLORSPACE_YCBCR422601_PL;
220               printf("YUY2\n");
221               break;
222            case GST_MAKE_FOURCC('N', 'V', '1', '2'):
223               priv->eformat = EVAS_COLORSPACE_YCBCR420NV12601_PL;
224               printf("NV12\n");
225               break;
226            case GST_MAKE_FOURCC('S', 'T', '1', '2'):
227            case GST_MAKE_FOURCC('T', 'M', '1', '2'):
228               priv->eformat = EVAS_COLORSPACE_YCBCR420TM12601_PL;
229               printf("ST12\n");
230               break;
231            default:
232               goto test_format;
233           }
234      }
235    else
236      {
237      test_format:
238         switch (format)
239           {
240            case GST_VIDEO_FORMAT_BGR: priv->eformat = EVAS_COLORSPACE_ARGB8888;
241               printf ("BGR\n");
242               break;
243            case GST_VIDEO_FORMAT_BGRx: priv->eformat = EVAS_COLORSPACE_ARGB8888;
244               printf ("BGRx\n");
245               break;
246            case GST_VIDEO_FORMAT_BGRA: priv->eformat = EVAS_COLORSPACE_ARGB8888;
247               printf ("BGRA\n");
248               break;
249            default:
250               ERR("unsupported : %d\n", format);
251               return FALSE;
252           }
253      }
254    priv->gformat = format;
255
256    return TRUE;
257 }
258
259 static gboolean
260 evas_video_sink_start(GstBaseSink* base_sink)
261 {
262    EvasVideoSinkPrivate* priv;
263    gboolean res = TRUE;
264
265    priv = EVAS_VIDEO_SINK(base_sink)->priv;
266    eina_lock_take(&priv->m);
267    if (!priv->o)
268      res = FALSE;
269    else
270      priv->unlocked = EINA_FALSE;
271    eina_lock_release(&priv->m);
272    return res;
273 }
274
275 static gboolean
276 evas_video_sink_stop(GstBaseSink* base_sink)
277 {
278    EvasVideoSinkPrivate* priv = EVAS_VIDEO_SINK(base_sink)->priv;
279
280    unlock_buffer_mutex(priv);
281    return TRUE;
282 }
283
284 static gboolean
285 evas_video_sink_unlock(GstBaseSink* object)
286 {
287    EvasVideoSink* sink;
288
289    sink = EVAS_VIDEO_SINK(object);
290
291    unlock_buffer_mutex(sink->priv);
292
293    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock,
294                                        (object), TRUE);
295 }
296
297 static gboolean
298 evas_video_sink_unlock_stop(GstBaseSink* object)
299 {
300    EvasVideoSink* sink;
301    EvasVideoSinkPrivate* priv;
302
303    sink = EVAS_VIDEO_SINK(object);
304    priv = sink->priv;
305
306    eina_lock_take(&priv->m);
307    priv->unlocked = FALSE;
308    eina_lock_release(&priv->m);
309
310    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop,
311                                        (object), TRUE);
312 }
313
314 static GstFlowReturn
315 evas_video_sink_preroll(GstBaseSink* bsink, GstBuffer* buffer)
316 {
317    Emotion_Gstreamer_Buffer *send;
318    EvasVideoSinkPrivate *priv;
319    EvasVideoSink *sink;
320
321    sink = EVAS_VIDEO_SINK(bsink);
322    priv = sink->priv;
323
324    send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_TRUE);
325
326    if (send)
327      ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
328
329    return GST_FLOW_OK;
330 }
331
332 static GstFlowReturn
333 evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
334 {
335    Emotion_Gstreamer_Buffer *send;
336    EvasVideoSinkPrivate *priv;
337    EvasVideoSink *sink;
338
339    sink = EVAS_VIDEO_SINK(bsink);
340    priv = sink->priv;
341
342    eina_lock_take(&priv->m);
343
344    if (priv->unlocked) {
345       ERR("LOCKED");
346       eina_lock_release(&priv->m);
347       return GST_FLOW_OK;
348    }
349
350    send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_FALSE);
351    if (!send) {
352       eina_lock_release(&priv->m);
353       return GST_FLOW_ERROR;
354    }
355
356    ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
357
358    eina_condition_wait(&priv->c);
359    eina_lock_release(&priv->m);
360
361    return GST_FLOW_OK;
362 }
363
364 static void
365 evas_video_sink_main_render(void *data)
366 {
367    Emotion_Gstreamer_Buffer *send;
368    Emotion_Gstreamer_Video *ev = NULL;
369    Emotion_Video_Stream *vstream;
370    EvasVideoSinkPrivate* priv;
371    GstBuffer* buffer;
372    unsigned char *evas_data;
373    const guint8 *gst_data;
374    GstFormat fmt = GST_FORMAT_TIME;
375    Evas_Coord w, h;
376    gint64 pos;
377    Eina_Bool preroll;
378
379    send = data;
380
381    priv = send->sink;
382    if (!priv) goto exit_point;
383    if (!priv->o) goto exit_point;
384
385    buffer = send->frame;
386    preroll = send->preroll;
387
388    if (priv->unlocked) goto exit_point;
389
390    gst_data = GST_BUFFER_DATA(buffer);
391    if (!gst_data) goto exit_point;
392
393    ev = send->ev;
394    if (!ev) goto exit_point;
395
396    _emotion_gstreamer_video_pipeline_parse(ev, EINA_TRUE);
397
398    // This prevent a race condition when data are still in the pipe
399    // but the buffer size as changed because of a request from
400    // emotion smart (like on a file set).
401    evas_object_image_size_get(priv->o, &w, &h);
402    if (w != priv->width || h != priv->height)
403      goto exit_point;
404
405    evas_object_image_size_set(priv->o, priv->width, priv->height);
406    evas_object_image_alpha_set(priv->o, 0);
407    evas_object_image_colorspace_set(priv->o, priv->eformat);
408
409    evas_data = (unsigned char *)evas_object_image_data_get(priv->o, 1);
410
411    // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
412    // Here we convert to Evas's BGRA.
413    switch (priv->gformat)
414      {
415       case GST_VIDEO_FORMAT_BGR:
416         {
417            unsigned char *evas_tmp;
418            int x;
419            int y;
420
421            evas_tmp = evas_data;
422            /* FIXME: could this be optimized ? */
423            for (x = 0; x < priv->height; x++) {
424               for (y = 0; y < priv->width; y++) {
425                  evas_tmp[0] = gst_data[0];
426                  evas_tmp[1] = gst_data[1];
427                  evas_tmp[2] = gst_data[2];
428                  evas_tmp[3] = 255;
429                  gst_data += 3;
430                  evas_tmp += 4;
431               }
432            }
433            break;
434         }
435
436         // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
437         // Here we convert to Evas's BGRA.
438       case GST_VIDEO_FORMAT_BGRx:
439         {
440            unsigned char *evas_tmp;
441            int x;
442            int y;
443
444            evas_tmp = evas_data;
445            /* FIXME: could this be optimized ? */
446            for (x = 0; x < priv->height; x++) {
447               for (y = 0; y < priv->width; y++) {
448                  evas_tmp[0] = gst_data[0];
449                  evas_tmp[1] = gst_data[1];
450                  evas_tmp[2] = gst_data[2];
451                  evas_tmp[3] = 255;
452                  gst_data += 4;
453                  evas_tmp += 4;
454               }
455            }
456            break;
457         }
458
459         // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
460         // Here we convert to Evas's BGRA.
461       case GST_VIDEO_FORMAT_BGRA:
462         {
463            unsigned char *evas_tmp;
464            int x;
465            int y;
466            unsigned char alpha;
467
468            evas_tmp = evas_data;
469            /* FIXME: could this be optimized ? */
470            for (x = 0; x < priv->height; x++) {
471               for (y = 0; y < priv->width; y++) {
472                  alpha = gst_data[3];
473                  evas_tmp[0] = (gst_data[0] * alpha) / 255;
474                  evas_tmp[1] = (gst_data[1] * alpha) / 255;
475                  evas_tmp[2] = (gst_data[2] * alpha) / 255;
476                  evas_tmp[3] = alpha;
477                  gst_data += 4;
478                  evas_tmp += 4;
479               }
480            }
481            break;
482         }
483
484       case GST_VIDEO_FORMAT_I420:
485         {
486            int i;
487            const unsigned char **rows;
488
489            evas_object_image_pixels_dirty_set(priv->o, 1);
490            rows = (const unsigned char **)evas_data;
491
492            for (i = 0; i < priv->height; i++)
493              rows[i] = &gst_data[i * priv->width];
494
495            rows += priv->height;
496            for (i = 0; i < (priv->height / 2); i++)
497              rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)];
498
499            rows += priv->height / 2;
500            for (i = 0; i < (priv->height / 2); i++)
501              rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)];
502            break;
503         }
504
505       case GST_VIDEO_FORMAT_YV12:
506         {
507            int i;
508            const unsigned char **rows;
509
510            evas_object_image_pixels_dirty_set(priv->o, 1);
511
512            rows = (const unsigned char **)evas_data;
513
514            for (i = 0; i < priv->height; i++)
515              rows[i] = &gst_data[i * priv->width];
516
517            rows += priv->height;
518            for (i = 0; i < (priv->height / 2); i++)
519              rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)];
520
521            rows += priv->height / 2;
522            for (i = 0; i < (priv->height / 2); i++)
523              rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)];
524            break;
525         }
526
527       case GST_VIDEO_FORMAT_YUY2:
528         {
529            int i;
530            const unsigned char **rows;
531
532            evas_object_image_pixels_dirty_set(priv->o, 1);
533
534            rows = (const unsigned char **)evas_data;
535
536            for (i = 0; i < priv->height; i++)
537              rows[i] = &gst_data[i * priv->width * 2];
538            break;
539         }
540
541       default:
542
543          switch (priv->eformat)
544            {
545             case EVAS_COLORSPACE_YCBCR420NV12601_PL:
546               {
547                  int i;
548                  const unsigned char **rows;
549
550                  evas_object_image_pixels_dirty_set(priv->o, 1);
551
552                  rows = (const unsigned char **)evas_data;
553
554                  for (i = 0; i < priv->height; i++)
555                    rows[i] = &gst_data[i * priv->width];
556
557                  rows += priv->height;
558                  for (i = 0; i < (priv->height / 2); i++)
559                    rows[i] = &gst_data[priv->height * priv->width + i * priv->width];
560                  break;
561               }
562             case EVAS_COLORSPACE_YCBCR420NV12T601_PL:
563               {
564                  int i;
565                  const unsigned char **rows;
566
567                  evas_object_image_pixels_dirty_set(priv->o, 1);
568
569                  rows = (const unsigned char **)evas_data;
570
571                  for (i = 0; i < (priv->height / 32) / 2; i++)
572                    rows[i] = &gst_data[i * priv->width * 2 * 32];
573
574                  if ((priv->height / 32) % 2)
575                    rows[i] = &gst_data[i * priv->width * 2 * 32];
576
577                  rows += priv->height;
578                  for (i = 0; i < ((priv->height / 2) / 32) / 2; ++i)
579                    rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2) * 2 * 16];
580                  break;
581               }
582            }
583      }
584
585    evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height);
586    evas_object_image_data_set(priv->o, evas_data);
587    evas_object_image_pixels_dirty_set(priv->o, 0);
588
589    _emotion_frame_new(ev->obj);
590
591    vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1);
592
593    gst_element_query_position(ev->pipeline, &fmt, &pos);
594    ev->position = (double)pos / (double)GST_SECOND;
595
596    vstream->width = priv->width;
597    vstream->height = priv->height;
598    ev->ratio = (double) priv->width / (double) priv->height;
599
600    _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time);
601    _emotion_frame_resize(ev->obj, priv->width, priv->height, ev->ratio);
602
603    if (priv->last_buffer) gst_buffer_unref(priv->last_buffer);
604    priv->last_buffer = gst_buffer_ref(buffer);
605
606  exit_point:
607    emotion_gstreamer_buffer_free(send);
608
609    if (preroll || !priv->o || !ev) return ;
610
611    eina_lock_take(&priv->m);
612    if (!priv->unlocked)
613      eina_condition_signal(&priv->c);
614
615    eina_lock_release(&priv->m);
616 }
617
618 static void
619 unlock_buffer_mutex(EvasVideoSinkPrivate* priv)
620 {
621    eina_lock_take(&priv->m);
622    priv->unlocked = EINA_TRUE;
623
624    eina_condition_signal(&priv->c);
625    eina_lock_release(&priv->m);
626 }
627
628 static void
629 marshal_VOID__MINIOBJECT(GClosure * closure, GValue * return_value __UNUSED__,
630                          guint n_param_values, const GValue * param_values,
631                          gpointer invocation_hint __UNUSED__, gpointer marshal_data)
632 {
633    typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1, gpointer data2);
634    marshalfunc_VOID__MINIOBJECT callback;
635    GCClosure *cc;
636    gpointer data1, data2;
637
638    cc = (GCClosure *) closure;
639
640    g_return_if_fail(n_param_values == 2);
641
642    if (G_CCLOSURE_SWAP_DATA(closure)) {
643       data1 = closure->data;
644       data2 = g_value_peek_pointer(param_values + 0);
645    } else {
646       data1 = g_value_peek_pointer(param_values + 0);
647       data2 = closure->data;
648    }
649    callback = (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data : cc->callback);
650
651    callback(data1, gst_value_get_mini_object(param_values + 1), data2);
652 }
653
654 static void
655 evas_video_sink_class_init(EvasVideoSinkClass* klass)
656 {
657    GObjectClass* gobject_class;
658    GstBaseSinkClass* gstbase_sink_class;
659
660    gobject_class = G_OBJECT_CLASS(klass);
661    gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
662
663    g_type_class_add_private(klass, sizeof(EvasVideoSinkPrivate));
664
665    gobject_class->set_property = evas_video_sink_set_property;
666    gobject_class->get_property = evas_video_sink_get_property;
667
668    g_object_class_install_property (gobject_class, PROP_EVAS_OBJECT,
669                                     g_param_spec_pointer ("evas-object", "Evas Object",
670                                                           "The Evas object where the display of the video will be done",
671                                                           G_PARAM_READWRITE));
672
673    g_object_class_install_property (gobject_class, PROP_WIDTH,
674                                     g_param_spec_int ("width", "Width",
675                                                       "The width of the video",
676                                                       0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
677
678    g_object_class_install_property (gobject_class, PROP_HEIGHT,
679                                     g_param_spec_int ("height", "Height",
680                                                       "The height of the video",
681                                                       0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
682    g_object_class_install_property (gobject_class, PROP_EV,
683                                     g_param_spec_pointer ("ev", "Emotion_Gstreamer_Video",
684                                                           "THe internal data of the emotion object",
685                                                           G_PARAM_READWRITE));
686
687    gobject_class->dispose = evas_video_sink_dispose;
688
689    gstbase_sink_class->set_caps = evas_video_sink_set_caps;
690    gstbase_sink_class->stop = evas_video_sink_stop;
691    gstbase_sink_class->start = evas_video_sink_start;
692    gstbase_sink_class->unlock = evas_video_sink_unlock;
693    gstbase_sink_class->unlock_stop = evas_video_sink_unlock_stop;
694    gstbase_sink_class->render = evas_video_sink_render;
695    gstbase_sink_class->preroll = evas_video_sink_preroll;
696
697    evas_video_sink_signals[REPAINT_REQUESTED] = g_signal_new("repaint-requested",
698                                                              G_TYPE_FROM_CLASS(klass),
699                                                              (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
700                                                              0,
701                                                              0,
702                                                              0,
703                                                              marshal_VOID__MINIOBJECT,
704                                                              G_TYPE_NONE, 1, GST_TYPE_BUFFER);
705 }
706
707 gboolean
708 gstreamer_plugin_init (GstPlugin * plugin)
709 {
710    return gst_element_register (plugin,
711                                 "emotion-sink",
712                                 GST_RANK_NONE,
713                                 EVAS_TYPE_VIDEO_SINK);
714 }
715
716 static void
717 _emotion_gstreamer_pause(void *data, Ecore_Thread *thread)
718 {
719    Emotion_Gstreamer_Video *ev = data;
720
721    if (ecore_thread_check(thread) || !ev->pipeline) return ;
722
723    gst_element_set_state(ev->pipeline, GST_STATE_PAUSED);
724 }
725
726 static void
727 _emotion_gstreamer_cancel(void *data, Ecore_Thread *thread)
728 {
729    Emotion_Gstreamer_Video *ev = data;
730
731    ev->threads = eina_list_remove(ev->threads, thread);
732
733    if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
734      em_shutdown(ev);
735 }
736
737 static void
738 _emotion_gstreamer_end(void *data, Ecore_Thread *thread)
739 {
740    Emotion_Gstreamer_Video *ev = data;
741
742    ev->threads = eina_list_remove(ev->threads, thread);
743
744    if (ev->play)
745      {
746         gst_element_set_state(ev->pipeline, GST_STATE_PLAYING);
747         ev->play_started = 1;
748      }
749
750    if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
751      em_shutdown(ev);
752    else
753      _emotion_gstreamer_video_pipeline_parse(data, EINA_TRUE);
754 }
755
756 GstElement *
757 gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev,
758                          Evas_Object *o,
759                          const char *uri)
760 {
761    GstElement *playbin;
762    GstElement *sink;
763    Evas_Object *obj;
764
765    obj = emotion_object_image_get(o);
766    if (!obj)
767      {
768         ERR("Not Evas_Object specified");
769         return NULL;
770      }
771
772    playbin = gst_element_factory_make("playbin2", "playbin");
773    if (!playbin)
774      {
775         ERR("Unable to create 'playbin' GstElement.");
776         return NULL;
777      }
778
779    sink = gst_element_factory_make("emotion-sink", "sink");
780    if (!sink)
781      {
782         ERR("Unable to create 'emotion-sink' GstElement.");
783         goto unref_pipeline;
784      }
785
786    g_object_set(G_OBJECT(sink), "evas-object", obj, NULL);
787    g_object_set(G_OBJECT(sink), "ev", ev, NULL);
788
789    g_object_set(G_OBJECT(playbin), "video-sink", sink, NULL);
790    g_object_set(G_OBJECT(playbin), "uri", uri, NULL);
791
792    ev->pipeline = playbin;
793    ev->sink = sink;
794    ev->threads = eina_list_append(ev->threads,
795                                   ecore_thread_run(_emotion_gstreamer_pause,
796                                                    _emotion_gstreamer_end,
797                                                    _emotion_gstreamer_cancel,
798                                                    ev));
799
800    /** NOTE: you need to set: GST_DEBUG_DUMP_DOT_DIR=/tmp EMOTION_ENGINE=gstreamer to save the $EMOTION_GSTREAMER_DOT file in '/tmp' */
801    /** then call dot -Tpng -oemotion_pipeline.png /tmp/$TIMESTAMP-$EMOTION_GSTREAMER_DOT.dot */
802    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"));
803
804    return playbin;
805
806  unref_pipeline:
807    gst_object_unref(playbin);
808    return NULL;
809 }