09accdd2a475310bf3dbd786b88e066897864d75
[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, 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 static void evas_video_sink_main_render(void *data);
44 static void evas_video_sink_samsung_main_render(void *data);
45
46 static void
47 _evas_video_bgrx_step(unsigned char *evas_data, const unsigned char *gst_data,
48                       unsigned int w, unsigned int h __UNUSED__, unsigned int output_height, unsigned int step)
49 {
50    unsigned int x;
51    unsigned int y;
52
53    for (y = 0; y < output_height; ++y)
54      {
55         for (x = 0; x < w; x++)
56           {
57              evas_data[0] = gst_data[0];
58              evas_data[1] = gst_data[1];
59              evas_data[2] = gst_data[2];
60              evas_data[3] = 255;
61              gst_data += step;
62              evas_data += 4;
63           }
64      }
65 }
66
67 static void
68 _evas_video_bgr(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h, unsigned int output_height)
69 {
70    _evas_video_bgrx_step(evas_data, gst_data, w, h, output_height, 3);
71 }
72
73 static void
74 _evas_video_bgrx(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h, unsigned int output_height)
75 {
76    _evas_video_bgrx_step(evas_data, gst_data, w, h, output_height, 4);
77 }
78
79 static void
80 _evas_video_bgra(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h __UNUSED__, unsigned int output_height)
81 {
82    unsigned int x;
83    unsigned int y;
84
85    for (y = 0; y < output_height; ++y)
86      {
87         unsigned char alpha;
88
89         for (x = 0; x < w; ++x)
90           {
91              alpha = gst_data[3];
92              evas_data[0] = (gst_data[0] * alpha) / 255;
93              evas_data[1] = (gst_data[1] * alpha) / 255;
94              evas_data[2] = (gst_data[2] * alpha) / 255;
95              evas_data[3] = alpha;
96              gst_data += 4;
97              evas_data += 4;
98           }
99      }
100 }
101
102 static void
103 _evas_video_i420(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h, unsigned int output_height)
104 {
105    const unsigned char **rows;
106    unsigned int i, j;
107    unsigned int rh;
108
109    rh = output_height;
110
111    rows = (const unsigned char **)evas_data;
112
113    for (i = 0; i < rh; i++)
114      rows[i] = &gst_data[i * w];
115
116    for (j = 0; j < (rh / 2); j++, i++)
117      rows[i] = &gst_data[h * w + j * (w / 2)];
118
119    for (j = 0; j < (rh / 2); j++, i++)
120      rows[i] = &gst_data[h * w + h * (w / 4) + j * (w / 2)];
121    WRN("I420: %i (max: %i for height %i, real height = %i) = %p", i, 2 * h, h, rh, evas_data);
122 }
123
124 static void
125 _evas_video_yv12(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h, unsigned int output_height)
126 {
127    const unsigned char **rows;
128    unsigned int i, j;
129    unsigned int rh;
130
131    rh = output_height;
132
133    rows = (const unsigned char **)evas_data;
134
135    for (i = 0; i < rh; i++)
136      rows[i] = &gst_data[i * w];
137
138    for (j = 0; j < (rh / 2); j++, i++)
139      rows[i] = &gst_data[h * w + h * (w / 4) + j * (w / 2)];
140
141    for (j = 0; j < (rh / 2); j++, i++)
142      rows[i] = &gst_data[h * w + j * (w / 2)];
143    WRN("YV12: %i (max: %i for height %i, real height = %i) = %p", i, 2 * h, h, rh, evas_data);
144 }
145
146 static void
147 _evas_video_yuy2(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h __UNUSED__, unsigned int output_height)
148 {
149    const unsigned char **rows;
150    unsigned int i;
151
152    rows = (const unsigned char **)evas_data;
153
154    for (i = 0; i < output_height; i++)
155      rows[i] = &gst_data[i * w * 2];
156 }
157
158 static void
159 _evas_video_nv12(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h, unsigned int output_height)
160 {
161    const unsigned char **rows;
162    unsigned int i, j;
163    unsigned int rh;
164
165    rh = output_height;
166
167    rows = (const unsigned char **)evas_data;
168
169    for (i = 0; i < rh; i++)
170      rows[i] = &gst_data[i * w];
171
172    for (j = 0; j < (rh / 2); j++, i++)
173      rows[i] = &gst_data[h * w + j * w];
174 }
175
176 static void
177 _evas_video_mt12(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h, unsigned int output_height __UNUSED__)
178 {
179    const unsigned char **rows;
180    unsigned int i;
181    unsigned int j;
182
183    rows = (const unsigned char **)evas_data;
184
185    for (i = 0; i < (h / 32) / 2; i++)
186      rows[i] = &gst_data[i * w * 2 * 32];
187
188    if ((h / 32) % 2)
189      {
190         rows[i] = &gst_data[i * w * 2 * 32];
191         i++;
192      }
193
194    for (j = 0; j < ((h / 2) / 32) / 2; ++j, ++i)
195      rows[i] = &gst_data[h * w + j * (w / 2) * 2 * 16];
196 }
197
198 static void
199 _evas_video_st12_multiplane(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h, unsigned int output_height __UNUSED__)
200 {
201    const GstMultiPlaneImageBuffer *mp_buf = (const GstMultiPlaneImageBuffer *) gst_data;
202    const unsigned char **rows;
203    unsigned int i;
204    unsigned int j;
205
206    rows = (const unsigned char **)evas_data;
207
208    for (i = 0; i < (h / 32) / 2; i++)
209      rows[i] = mp_buf->uaddr[0] + i * w * 2 * 32;
210    if ((h / 32) % 2)
211      {
212         rows[i] = mp_buf->uaddr[0] + i * w * 2 * 32;
213         i++;
214      }
215
216    for (j = 0; j < ((h / 2) / 16) / 2; j++, i++)
217      rows[i] = mp_buf->uaddr[1] + j * w * 2 * 16;
218    if (((h / 2) / 16) % 2)
219      rows[i] = mp_buf->uaddr[0] + j * w * 2 * 16;
220 }
221
222 static void
223 _evas_video_st12(unsigned char *evas_data, const unsigned char *gst_data, unsigned int w, unsigned int h, unsigned int output_height __UNUSED__)
224 {
225    const SCMN_IMGB *imgb = (const SCMN_IMGB *) gst_data;
226    const unsigned char **rows;
227    unsigned int i, j;
228
229    rows = (const unsigned char **)evas_data;
230
231    for (i = 0; i < (h / 32) / 2; i++)
232      rows[i] = imgb->uaddr[0] + i * w * 2 * 32;
233    if ((h / 32) % 2)
234      {
235         rows[i] = imgb->uaddr[0] + i * w * 2 * 32;
236         i++;
237      }
238
239    for (j = 0; j < ((h / 2) / 16) / 2; j++, i++)
240      rows[i] = imgb->uaddr[1] + j * w * 2 * 16;
241    if (((h / 2) / 16) % 2)
242      rows[i] = imgb->uaddr[1] + j * w * 2 * 16;
243 }
244
245 static const struct {
246    guint32 fourcc;
247    Evas_Colorspace eformat;
248    Evas_Video_Convert_Cb func;
249    Eina_Bool force_height;
250 } colorspace_fourcc_convertion[] = {
251   { GST_MAKE_FOURCC('I', '4', '2', '0'), EVAS_COLORSPACE_YCBCR422P601_PL, _evas_video_i420, EINA_TRUE },
252   { GST_MAKE_FOURCC('Y', 'V', '1', '2'), EVAS_COLORSPACE_YCBCR422P601_PL, _evas_video_yv12, EINA_TRUE },
253   { GST_MAKE_FOURCC('Y', 'U', 'Y', '2'), EVAS_COLORSPACE_YCBCR422601_PL, _evas_video_yuy2, EINA_FALSE },
254   { GST_MAKE_FOURCC('N', 'V', '1', '2'), EVAS_COLORSPACE_YCBCR420NV12601_PL, _evas_video_nv12, EINA_TRUE },
255   { GST_MAKE_FOURCC('T', 'M', '1', '2'), EVAS_COLORSPACE_YCBCR420TM12601_PL, _evas_video_mt12, EINA_TRUE }
256 };
257
258 static const struct {
259    GstVideoFormat format;
260    Evas_Colorspace eformat;
261    Evas_Video_Convert_Cb func;
262 } colorspace_format_convertion[] = {
263   { GST_VIDEO_FORMAT_BGR, EVAS_COLORSPACE_ARGB8888, _evas_video_bgr },
264   { GST_VIDEO_FORMAT_BGRx, EVAS_COLORSPACE_ARGB8888, _evas_video_bgrx },
265   { GST_VIDEO_FORMAT_BGRA, EVAS_COLORSPACE_ARGB8888, _evas_video_bgra }
266 };
267
268 static void
269 evas_video_sink_base_init(gpointer g_class)
270 {
271    GstElementClass* element_class;
272
273    element_class = GST_ELEMENT_CLASS(g_class);
274    gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sinktemplate));
275    gst_element_class_set_details_simple(element_class, "Evas video sink",
276                                         "Sink/Video", "Sends video data from a GStreamer pipeline to an Evas object",
277                                         "Vincent Torri <vtorri@univ-evry.fr>");
278 }
279
280 static void
281 evas_video_sink_init(EvasVideoSink* sink, EvasVideoSinkClass* klass __UNUSED__)
282 {
283    EvasVideoSinkPrivate* priv;
284
285    INF("sink init");
286    sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, EVAS_TYPE_VIDEO_SINK, EvasVideoSinkPrivate);
287    priv->o = NULL;
288    priv->last_buffer = NULL;
289    priv->width = 0;
290    priv->height = 0;
291    priv->func = NULL;
292    priv->eformat = EVAS_COLORSPACE_ARGB8888;
293    priv->samsung = EINA_FALSE;
294    eina_lock_new(&priv->m);
295    eina_condition_new(&priv->c, &priv->m);
296    priv->unlocked = EINA_FALSE;
297 }
298
299 /**** Object methods ****/
300 static void
301 _cleanup_priv(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
302 {
303    EvasVideoSinkPrivate* priv;
304
305    priv = data;
306
307    eina_lock_take(&priv->m);
308    if (priv->o == obj)
309      priv->o = NULL;
310    eina_lock_release(&priv->m);
311 }
312
313 static void
314 evas_video_sink_set_property(GObject * object, guint prop_id,
315                              const GValue * value, GParamSpec * pspec)
316 {
317    EvasVideoSink* sink;
318    EvasVideoSinkPrivate* priv;
319
320    sink = EVAS_VIDEO_SINK (object);
321    priv = sink->priv;
322
323    switch (prop_id) {
324     case PROP_EVAS_OBJECT:
325        eina_lock_take(&priv->m);
326        evas_object_event_callback_del(priv->o, EVAS_CALLBACK_FREE, _cleanup_priv);
327        priv->o = g_value_get_pointer (value);
328        INF("sink set Evas_Object %p.", priv->o);
329        evas_object_event_callback_add(priv->o, EVAS_CALLBACK_FREE, _cleanup_priv, priv);
330        eina_lock_release(&priv->m);
331        break;
332     case PROP_EV:
333        INF("sink set ev.");
334        eina_lock_take(&priv->m);
335        priv->ev = g_value_get_pointer (value);
336        eina_lock_release(&priv->m);
337        break;
338     default:
339        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
340        ERR("invalid property");
341        break;
342    }
343 }
344
345 static void
346 evas_video_sink_get_property(GObject * object, guint prop_id,
347                              GValue * value, GParamSpec * pspec)
348 {
349    EvasVideoSink* sink;
350    EvasVideoSinkPrivate* priv;
351
352    sink = EVAS_VIDEO_SINK (object);
353    priv = sink->priv;
354
355    switch (prop_id) {
356     case PROP_EVAS_OBJECT:
357        INF("sink get property.");
358        eina_lock_take(&priv->m);
359        g_value_set_pointer(value, priv->o);
360        eina_lock_release(&priv->m);
361        break;
362     case PROP_WIDTH:
363        INF("sink get width.");
364        eina_lock_take(&priv->m);
365        g_value_set_int(value, priv->width);
366        eina_lock_release(&priv->m);
367        break;
368     case PROP_HEIGHT:
369        INF("sink get height.");
370        eina_lock_take(&priv->m);
371        g_value_set_int (value, priv->height);
372        eina_lock_release(&priv->m);
373        break;
374     case PROP_EV:
375        INF("sink get ev.");
376        eina_lock_take(&priv->m);
377        g_value_set_pointer (value, priv->ev);
378        eina_lock_release(&priv->m);
379        break;
380     default:
381        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382        ERR("invalide property");
383        break;
384    }
385 }
386
387 static void
388 evas_video_sink_dispose(GObject* object)
389 {
390    EvasVideoSink* sink;
391    EvasVideoSinkPrivate* priv;
392
393    INF("dispose.");
394
395    sink = EVAS_VIDEO_SINK(object);
396    priv = sink->priv;
397
398    eina_lock_free(&priv->m);
399    eina_condition_free(&priv->c);
400
401    if (priv->last_buffer) {
402       gst_buffer_unref(priv->last_buffer);
403       priv->last_buffer = NULL;
404    }
405
406    G_OBJECT_CLASS(parent_class)->dispose(object);
407 }
408
409
410 /**** BaseSink methods ****/
411
412 gboolean evas_video_sink_set_caps(GstBaseSink *bsink, GstCaps *caps)
413 {
414    EvasVideoSink* sink;
415    EvasVideoSinkPrivate* priv;
416    GstStructure *structure;
417    GstVideoFormat format;
418    guint32 fourcc;
419    unsigned int i;
420
421    sink = EVAS_VIDEO_SINK(bsink);
422    priv = sink->priv;
423
424    structure = gst_caps_get_structure(caps, 0);
425
426    if (gst_structure_get_int(structure, "width", (int*) &priv->width)
427        && gst_structure_get_int(structure, "height", (int*) &priv->height)
428        && gst_structure_get_fourcc(structure, "format", &fourcc))
429      {
430         priv->source_height = priv->height;
431
432         for (i = 0; i < sizeof (colorspace_fourcc_convertion) / sizeof (colorspace_fourcc_convertion[0]); ++i)
433           if (fourcc == colorspace_fourcc_convertion[i].fourcc)
434             {
435                priv->eformat = colorspace_fourcc_convertion[i].eformat;
436                priv->func = colorspace_fourcc_convertion[i].func;
437                if (colorspace_fourcc_convertion[i].force_height)
438                  {
439                     priv->height = (priv->height >> 1) << 1;
440                  }
441                return TRUE;
442             }
443
444         if (fourcc == GST_MAKE_FOURCC('S', 'T', '1', '2'))
445           {
446              priv->eformat = EVAS_COLORSPACE_YCBCR420TM12601_PL;
447              priv->samsung = EINA_TRUE;
448              priv->func = NULL;
449              return TRUE;
450           }
451      }
452
453    INF("fallback code !");
454    if (!gst_video_format_parse_caps(caps, &format, (int*) &priv->width, (int*) &priv->height))
455      {
456         ERR("Unable to parse caps.");
457         return FALSE;
458      }
459
460    priv->source_height = priv->height;
461
462    for (i = 0; i < sizeof (colorspace_format_convertion) / sizeof (colorspace_format_convertion[0]); ++i)
463      if (format == colorspace_format_convertion[i].format)
464        {
465           priv->eformat = colorspace_format_convertion[i].eformat;
466           priv->func = colorspace_format_convertion[i].func;
467           return TRUE;
468        }
469
470    ERR("unsupported : %d\n", format);
471    return FALSE;
472 }
473
474 static gboolean
475 evas_video_sink_start(GstBaseSink* base_sink)
476 {
477    EvasVideoSinkPrivate* priv;
478    gboolean res = TRUE;
479
480    INF("sink start");
481
482    priv = EVAS_VIDEO_SINK(base_sink)->priv;
483    eina_lock_take(&priv->m);
484    if (!priv->o)
485      res = FALSE;
486    else
487      priv->unlocked = EINA_FALSE;
488    eina_lock_release(&priv->m);
489    return res;
490 }
491
492 static gboolean
493 evas_video_sink_stop(GstBaseSink* base_sink)
494 {
495    EvasVideoSinkPrivate* priv = EVAS_VIDEO_SINK(base_sink)->priv;
496
497    INF("sink stop");
498
499    unlock_buffer_mutex(priv);
500    return TRUE;
501 }
502
503 static gboolean
504 evas_video_sink_unlock(GstBaseSink* object)
505 {
506    EvasVideoSink* sink;
507
508    INF("sink unlock");
509
510    sink = EVAS_VIDEO_SINK(object);
511
512    unlock_buffer_mutex(sink->priv);
513
514    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock,
515                                        (object), TRUE);
516 }
517
518 static gboolean
519 evas_video_sink_unlock_stop(GstBaseSink* object)
520 {
521    EvasVideoSink* sink;
522    EvasVideoSinkPrivate* priv;
523
524    sink = EVAS_VIDEO_SINK(object);
525    priv = sink->priv;
526
527    INF("sink unlock stop");
528
529    eina_lock_take(&priv->m);
530    priv->unlocked = FALSE;
531    eina_lock_release(&priv->m);
532
533    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop,
534                                        (object), TRUE);
535 }
536
537 static GstFlowReturn
538 evas_video_sink_preroll(GstBaseSink* bsink, GstBuffer* buffer)
539 {
540    Emotion_Gstreamer_Buffer *send;
541    EvasVideoSinkPrivate *priv;
542    EvasVideoSink *sink;
543
544    INF("sink preroll %p [%i]", GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
545
546    sink = EVAS_VIDEO_SINK(bsink);
547    priv = sink->priv;
548
549    if (GST_BUFFER_SIZE(buffer) <= 0 && !priv->samsung)
550      {
551         WRN("empty buffer");
552         return GST_FLOW_OK;
553      }
554
555    send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_TRUE);
556
557    if (send)
558      {
559         if (priv->samsung)
560           {
561              if (!priv->func)
562                {
563                   GstStructure *structure;
564                   GstCaps *caps;
565                   gboolean is_multiplane = FALSE;
566
567                   caps = GST_BUFFER_CAPS(buffer);
568                   structure = gst_caps_get_structure (caps, 0);
569                   gst_structure_get_boolean(structure, "multiplane", &is_multiplane);
570
571                   if (is_multiplane)
572                     priv->func = _evas_video_st12_multiplane;
573                   else
574                     priv->func = _evas_video_st12;
575                }
576
577              ecore_main_loop_thread_safe_call_async(evas_video_sink_samsung_main_render, send);
578           }
579         else
580           ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
581      }
582
583    return GST_FLOW_OK;
584 }
585
586 static GstFlowReturn
587 evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
588 {
589    Emotion_Gstreamer_Buffer *send;
590    EvasVideoSinkPrivate *priv;
591    EvasVideoSink *sink;
592
593    INF("sink render %p [%i]", GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
594
595    sink = EVAS_VIDEO_SINK(bsink);
596    priv = sink->priv;
597
598    eina_lock_take(&priv->m);
599
600    if (priv->unlocked) {
601       ERR("LOCKED");
602       eina_lock_release(&priv->m);
603       return GST_FLOW_OK;
604    }
605
606    send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_FALSE);
607    if (!send) {
608       eina_lock_release(&priv->m);
609       return GST_FLOW_ERROR;
610    }
611
612    if (priv->samsung)
613      {
614         if (!priv->func)
615           {
616              GstStructure *structure;
617              GstCaps *caps;
618              gboolean is_multiplane = FALSE;
619
620              caps = GST_BUFFER_CAPS(buffer);
621              structure = gst_caps_get_structure (caps, 0);
622              gst_structure_get_boolean(structure, "multiplane", &is_multiplane);
623
624              if (is_multiplane)
625                priv->func = _evas_video_st12_multiplane;
626              else
627                priv->func = _evas_video_st12;
628           }
629
630         ecore_main_loop_thread_safe_call_async(evas_video_sink_samsung_main_render, send);
631      }
632    else
633      ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
634
635    eina_condition_wait(&priv->c);
636    eina_lock_release(&priv->m);
637
638    return GST_FLOW_OK;
639 }
640
641 static void
642 evas_video_sink_samsung_main_render(void *data)
643 {
644    Emotion_Gstreamer_Buffer *send;
645    Emotion_Video_Stream *vstream;
646    EvasVideoSinkPrivate* priv;
647    GstBuffer* buffer;
648    unsigned char *evas_data;
649    const guint8 *gst_data;
650    GstFormat fmt = GST_FORMAT_TIME;
651    gint64 pos;
652    Eina_Bool preroll;
653    int stride, elevation;
654    Evas_Coord w, h;
655
656    send = data;
657
658    if (!send) goto exit_point;
659
660    priv = send->sink;
661    buffer = send->frame;
662    preroll = send->preroll;
663
664    if (!priv || !priv->o || priv->unlocked || !send->ev)
665      goto exit_point;
666
667    _emotion_gstreamer_video_pipeline_parse(send->ev, EINA_TRUE);
668
669    /* Getting stride to compute the right size and then fill the object properly */
670    /* Y => [0] and UV in [1] */
671    if (priv->func == _evas_video_st12_multiplane)
672      {
673         const GstMultiPlaneImageBuffer *mp_buf = (const GstMultiPlaneImageBuffer *) buffer;
674
675         stride = mp_buf->stride[0];
676         elevation = mp_buf->elevation[0];
677         priv->width = mp_buf->width[0];
678         priv->height = mp_buf->height[0];
679
680         gst_data = (const guint8 *) mp_buf;
681      }
682    else
683      {
684         const SCMN_IMGB *imgb = (const SCMN_IMGB *) GST_BUFFER_MALLOCDATA(buffer);
685
686         stride = imgb->stride[0];
687         elevation = imgb->elevation[0];
688         priv->width = imgb->width[0];
689         priv->height = imgb->height[0];
690
691         gst_data = (const guint8 *) imgb;
692      }
693
694    INF("sink main render [%i, %i] - [%i, %i]", priv->width, priv->height, stride, elevation);
695
696    evas_object_image_alpha_set(priv->o, 0);
697    evas_object_image_colorspace_set(priv->o, priv->eformat);
698    evas_object_image_size_set(priv->o, stride, elevation);
699    evas_object_geometry_get(priv->o, NULL, NULL, &w, &h);
700    evas_object_image_fill_set(priv->o, 0, 0, stride * w / priv->width, elevation * h / priv->height);
701
702    evas_data = evas_object_image_data_get(priv->o, 1);
703
704    if (priv->func)
705      priv->func(evas_data, gst_data, stride, elevation, elevation);
706    else
707      WRN("No way to decode %x colorspace !", priv->eformat);
708
709    evas_object_image_data_set(priv->o, evas_data);
710    evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height);
711    evas_object_image_pixels_dirty_set(priv->o, 0);
712
713    _emotion_frame_new(send->ev->obj);
714
715    vstream = eina_list_nth(send->ev->video_streams, send->ev->video_stream_nbr - 1);
716
717    gst_element_query_position(send->ev->pipeline, &fmt, &pos);
718    send->ev->position = (double)pos / (double)GST_SECOND;
719
720    vstream->width = priv->width;
721    vstream->height = priv->height;
722    send->ev->ratio = (double) priv->width / (double) priv->height;
723
724    _emotion_video_pos_update(send->ev->obj, send->ev->position, vstream->length_time);
725    _emotion_frame_resize(send->ev->obj, priv->width, priv->height, send->ev->ratio);
726
727    if (priv->last_buffer) gst_buffer_unref(priv->last_buffer);
728    priv->last_buffer = gst_buffer_ref(buffer);
729
730  exit_point:
731    emotion_gstreamer_buffer_free(send);
732
733    if (preroll || !priv->o || !send->ev) return ;
734
735    eina_lock_take(&priv->m);
736    if (!priv->unlocked)
737      eina_condition_signal(&priv->c);
738
739    eina_lock_release(&priv->m);
740 }
741
742 static void
743 evas_video_sink_main_render(void *data)
744 {
745    Emotion_Gstreamer_Buffer *send;
746    Emotion_Gstreamer_Video *ev = NULL;
747    Emotion_Video_Stream *vstream;
748    EvasVideoSinkPrivate* priv;
749    GstBuffer* buffer;
750    unsigned char *evas_data;
751    GstFormat fmt = GST_FORMAT_TIME;
752    gint64 pos;
753    Eina_Bool preroll;
754
755    send = data;
756
757    if (!send) goto exit_point;
758
759    priv = send->sink;
760    buffer = send->frame;
761    preroll = send->preroll;
762    ev = send->ev;
763
764    if (!priv || !priv->o || priv->unlocked || !ev)
765      goto exit_point;
766
767    _emotion_gstreamer_video_pipeline_parse(ev, EINA_TRUE);
768
769    INF("sink main render [%i, %i] (source height: %i)", priv->width, priv->height, priv->source_height);
770
771    evas_object_image_alpha_set(priv->o, 0);
772    evas_object_image_colorspace_set(priv->o, priv->eformat);
773    evas_object_image_size_set(priv->o, priv->width, priv->height);
774
775    evas_data = evas_object_image_data_get(priv->o, 1);
776
777    if (priv->func)
778      priv->func(evas_data, GST_BUFFER_DATA(buffer), priv->width, priv->source_height, priv->height);
779    else
780      WRN("No way to decode %x colorspace !", priv->eformat);
781
782    evas_object_image_data_set(priv->o, evas_data);
783    evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height);
784    evas_object_image_pixels_dirty_set(priv->o, 0);
785
786    _emotion_frame_new(ev->obj);
787
788    vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1);
789
790    gst_element_query_position(ev->pipeline, &fmt, &pos);
791    ev->position = (double)pos / (double)GST_SECOND;
792
793    vstream->width = priv->width;
794    vstream->height = priv->height;
795    ev->ratio = (double) priv->width / (double) priv->height;
796
797    _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time);
798    _emotion_frame_resize(ev->obj, priv->width, priv->height, ev->ratio);
799
800    if (priv->last_buffer) gst_buffer_unref(priv->last_buffer);
801    priv->last_buffer = gst_buffer_ref(buffer);
802
803  exit_point:
804    emotion_gstreamer_buffer_free(send);
805
806    if (preroll || !priv->o || !ev) return ;
807
808    eina_lock_take(&priv->m);
809    if (!priv->unlocked)
810      eina_condition_signal(&priv->c);
811
812    eina_lock_release(&priv->m);
813 }
814
815 static void
816 unlock_buffer_mutex(EvasVideoSinkPrivate* priv)
817 {
818    eina_lock_take(&priv->m);
819    priv->unlocked = EINA_TRUE;
820
821    eina_condition_signal(&priv->c);
822    eina_lock_release(&priv->m);
823 }
824
825 static void
826 marshal_VOID__MINIOBJECT(GClosure * closure, GValue * return_value __UNUSED__,
827                          guint n_param_values, const GValue * param_values,
828                          gpointer invocation_hint __UNUSED__, gpointer marshal_data)
829 {
830    typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1, gpointer data2);
831    marshalfunc_VOID__MINIOBJECT callback;
832    GCClosure *cc;
833    gpointer data1, data2;
834
835    cc = (GCClosure *) closure;
836
837    g_return_if_fail(n_param_values == 2);
838
839    if (G_CCLOSURE_SWAP_DATA(closure)) {
840       data1 = closure->data;
841       data2 = g_value_peek_pointer(param_values + 0);
842    } else {
843       data1 = g_value_peek_pointer(param_values + 0);
844       data2 = closure->data;
845    }
846    callback = (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data : cc->callback);
847
848    callback(data1, gst_value_get_mini_object(param_values + 1), data2);
849 }
850
851 static void
852 evas_video_sink_class_init(EvasVideoSinkClass* klass)
853 {
854    GObjectClass* gobject_class;
855    GstBaseSinkClass* gstbase_sink_class;
856
857    gobject_class = G_OBJECT_CLASS(klass);
858    gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
859
860    g_type_class_add_private(klass, sizeof(EvasVideoSinkPrivate));
861
862    gobject_class->set_property = evas_video_sink_set_property;
863    gobject_class->get_property = evas_video_sink_get_property;
864
865    g_object_class_install_property (gobject_class, PROP_EVAS_OBJECT,
866                                     g_param_spec_pointer ("evas-object", "Evas Object",
867                                                           "The Evas object where the display of the video will be done",
868                                                           G_PARAM_READWRITE));
869
870    g_object_class_install_property (gobject_class, PROP_WIDTH,
871                                     g_param_spec_int ("width", "Width",
872                                                       "The width of the video",
873                                                       0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
874
875    g_object_class_install_property (gobject_class, PROP_HEIGHT,
876                                     g_param_spec_int ("height", "Height",
877                                                       "The height of the video",
878                                                       0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
879    g_object_class_install_property (gobject_class, PROP_EV,
880                                     g_param_spec_pointer ("ev", "Emotion_Gstreamer_Video",
881                                                           "THe internal data of the emotion object",
882                                                           G_PARAM_READWRITE));
883
884    gobject_class->dispose = evas_video_sink_dispose;
885
886    gstbase_sink_class->set_caps = evas_video_sink_set_caps;
887    gstbase_sink_class->stop = evas_video_sink_stop;
888    gstbase_sink_class->start = evas_video_sink_start;
889    gstbase_sink_class->unlock = evas_video_sink_unlock;
890    gstbase_sink_class->unlock_stop = evas_video_sink_unlock_stop;
891    gstbase_sink_class->render = evas_video_sink_render;
892    gstbase_sink_class->preroll = evas_video_sink_preroll;
893
894    evas_video_sink_signals[REPAINT_REQUESTED] = g_signal_new("repaint-requested",
895                                                              G_TYPE_FROM_CLASS(klass),
896                                                              (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
897                                                              0,
898                                                              0,
899                                                              0,
900                                                              marshal_VOID__MINIOBJECT,
901                                                              G_TYPE_NONE, 1, GST_TYPE_BUFFER);
902 }
903
904 gboolean
905 gstreamer_plugin_init (GstPlugin * plugin)
906 {
907    return gst_element_register (plugin,
908                                 "emotion-sink",
909                                 GST_RANK_NONE,
910                                 EVAS_TYPE_VIDEO_SINK);
911 }
912
913 static void
914 _emotion_gstreamer_pause(void *data, Ecore_Thread *thread)
915 {
916    Emotion_Gstreamer_Video *ev = data;
917
918    if (ecore_thread_check(thread) || !ev->pipeline) return ;
919
920    gst_element_set_state(ev->pipeline, GST_STATE_PAUSED);
921 }
922
923 static void
924 _emotion_gstreamer_cancel(void *data, Ecore_Thread *thread)
925 {
926    Emotion_Gstreamer_Video *ev = data;
927
928    ev->threads = eina_list_remove(ev->threads, thread);
929
930    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"));
931
932    if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
933      em_shutdown(ev);
934 }
935
936 static void
937 _emotion_gstreamer_end(void *data, Ecore_Thread *thread)
938 {
939    Emotion_Gstreamer_Video *ev = data;
940
941    ev->threads = eina_list_remove(ev->threads, thread);
942
943    if (ev->play)
944      {
945         gst_element_set_state(ev->pipeline, GST_STATE_PLAYING);
946         ev->play_started = 1;
947      }
948
949    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"));
950
951    if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
952      em_shutdown(ev);
953    else
954      _emotion_gstreamer_video_pipeline_parse(data, EINA_TRUE);
955 }
956
957 GstElement *
958 gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev,
959                          Evas_Object *o,
960                          const char *uri)
961 {
962    GstElement *playbin;
963    GstElement *sink;
964    Evas_Object *obj;
965    int flags;
966
967    obj = emotion_object_image_get(o);
968    if (!obj)
969      {
970         ERR("Not Evas_Object specified");
971         return NULL;
972      }
973
974    playbin = gst_element_factory_make("playbin2", "playbin");
975    if (!playbin)
976      {
977         ERR("Unable to create 'playbin' GstElement.");
978         return NULL;
979      }
980
981    sink = gst_element_factory_make("emotion-sink", "sink");
982    if (!sink)
983      {
984         ERR("Unable to create 'emotion-sink' GstElement.");
985         goto unref_pipeline;
986      }
987
988 #define GST_PLAY_FLAG_NATIVE_VIDEO  (1 << 6)
989 #define GST_PLAY_FLAG_DOWNLOAD      (1 << 7)
990 #define GST_PLAY_FLAG_BUFFERING     (1 << 8)
991
992    g_object_set(G_OBJECT(sink), "evas-object", obj, NULL);
993    g_object_set(G_OBJECT(sink), "ev", ev, NULL);
994
995    evas_object_image_pixels_get_callback_set(obj, NULL, NULL);
996
997    g_object_get(G_OBJECT(playbin), "flags", &flags, NULL);
998    g_object_set(G_OBJECT(playbin), "flags", flags | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_DOWNLOAD | GST_PLAY_FLAG_BUFFERING, NULL);
999    g_object_set(G_OBJECT(playbin), "video-sink", sink, NULL);
1000    g_object_set(G_OBJECT(playbin), "uri", uri, NULL);
1001
1002    ev->pipeline = playbin;
1003    ev->sink = sink;
1004    ev->threads = eina_list_append(ev->threads,
1005                                   ecore_thread_run(_emotion_gstreamer_pause,
1006                                                    _emotion_gstreamer_end,
1007                                                    _emotion_gstreamer_cancel,
1008                                                    ev));
1009
1010    /** NOTE: you need to set: GST_DEBUG_DUMP_DOT_DIR=/tmp EMOTION_ENGINE=gstreamer to save the $EMOTION_GSTREAMER_DOT file in '/tmp' */
1011    /** then call dot -Tpng -oemotion_pipeline.png /tmp/$TIMESTAMP-$EMOTION_GSTREAMER_DOT.dot */
1012    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"));
1013
1014    return playbin;
1015
1016  unref_pipeline:
1017    gst_object_unref(playbin);
1018    return NULL;
1019 }