emotion: better debugging.
[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
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        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);
109        break;
110     case PROP_EV:
111        INF("sink set ev.");
112        eina_lock_take(&priv->m);
113        priv->ev = g_value_get_pointer (value);
114        eina_lock_release(&priv->m);
115        break;
116     default:
117        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118        ERR("invalid property");
119        break;
120    }
121 }
122
123 static void
124 evas_video_sink_get_property(GObject * object, guint prop_id,
125                              GValue * value, GParamSpec * pspec)
126 {
127    EvasVideoSink* sink;
128    EvasVideoSinkPrivate* priv;
129
130    sink = EVAS_VIDEO_SINK (object);
131    priv = sink->priv;
132
133    switch (prop_id) {
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);
139        break;
140     case PROP_WIDTH:
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);
145        break;
146     case PROP_HEIGHT:
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);
151        break;
152     case PROP_EV:
153        INF("sink get ev.");
154        eina_lock_take(&priv->m);
155        g_value_set_pointer (value, priv->ev);
156        eina_lock_release(&priv->m);
157        break;
158     default:
159        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160        ERR("invalide property");
161        break;
162    }
163 }
164
165 static void
166 evas_video_sink_dispose(GObject* object)
167 {
168    EvasVideoSink* sink;
169    EvasVideoSinkPrivate* priv;
170
171    INF("dispose.");
172
173    sink = EVAS_VIDEO_SINK(object);
174    priv = sink->priv;
175
176    eina_lock_free(&priv->m);
177    eina_condition_free(&priv->c);
178
179    if (priv->last_buffer) {
180       gst_buffer_unref(priv->last_buffer);
181       priv->last_buffer = NULL;
182    }
183
184    G_OBJECT_CLASS(parent_class)->dispose(object);
185 }
186
187
188 /**** BaseSink methods ****/
189
190 gboolean evas_video_sink_set_caps(GstBaseSink *bsink, GstCaps *caps)
191 {
192    EvasVideoSink* sink;
193    EvasVideoSinkPrivate* priv;
194    GstStructure *structure;
195    GstVideoFormat format;
196    guint32 fourcc;
197
198    sink = EVAS_VIDEO_SINK(bsink);
199    priv = sink->priv;
200
201    structure = gst_caps_get_structure(caps, 0);
202
203    if (!((gst_structure_get_int(structure, "width", &priv->width)
204           && gst_structure_get_int(structure, "height", &priv->height))))
205      goto test_format;
206
207    if (gst_structure_get_fourcc(structure, "format", &fourcc))
208      {
209         switch (fourcc)
210           {
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);
215               break;
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);
220               break;
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);
225               break;
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);
229               break;
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);
234               break;
235            default:
236               goto test_format;
237           }
238      }
239    else
240      {
241      test_format:
242         INF("fallback code !");
243         if (!gst_video_format_parse_caps(caps, &format, &priv->width, &priv->height))
244           {
245              ERR("Unable to parse caps.");
246              return FALSE;
247           }
248
249         switch (format)
250           {
251            case GST_VIDEO_FORMAT_BGR: priv->eformat = EVAS_COLORSPACE_ARGB8888;
252               INF("sink set colorspace BGR [%i, %i]", priv->width, priv->height);
253               break;
254            case GST_VIDEO_FORMAT_BGRx: priv->eformat = EVAS_COLORSPACE_ARGB8888;
255               INF("sink set colorspace BGRx [%i, %i]", priv->width, priv->height);
256               break;
257            case GST_VIDEO_FORMAT_BGRA: priv->eformat = EVAS_COLORSPACE_ARGB8888;
258               INF("sink set colorspace BGRA [%i, %i]", priv->width, priv->height);
259               break;
260            default:
261               ERR("unsupported : %d\n", format);
262               return FALSE;
263           }
264         priv->gformat = format;
265      }
266
267    return TRUE;
268 }
269
270 static gboolean
271 evas_video_sink_start(GstBaseSink* base_sink)
272 {
273    EvasVideoSinkPrivate* priv;
274    gboolean res = TRUE;
275
276    INF("sink start");
277
278    priv = EVAS_VIDEO_SINK(base_sink)->priv;
279    eina_lock_take(&priv->m);
280    if (!priv->o)
281      res = FALSE;
282    else
283      priv->unlocked = EINA_FALSE;
284    eina_lock_release(&priv->m);
285    return res;
286 }
287
288 static gboolean
289 evas_video_sink_stop(GstBaseSink* base_sink)
290 {
291    EvasVideoSinkPrivate* priv = EVAS_VIDEO_SINK(base_sink)->priv;
292
293    INF("sink stop");
294
295    unlock_buffer_mutex(priv);
296    return TRUE;
297 }
298
299 static gboolean
300 evas_video_sink_unlock(GstBaseSink* object)
301 {
302    EvasVideoSink* sink;
303
304    INF("sink unlock");
305
306    sink = EVAS_VIDEO_SINK(object);
307
308    unlock_buffer_mutex(sink->priv);
309
310    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock,
311                                        (object), TRUE);
312 }
313
314 static gboolean
315 evas_video_sink_unlock_stop(GstBaseSink* object)
316 {
317    EvasVideoSink* sink;
318    EvasVideoSinkPrivate* priv;
319
320    sink = EVAS_VIDEO_SINK(object);
321    priv = sink->priv;
322
323    INF("sink unlock stop");
324
325    eina_lock_take(&priv->m);
326    priv->unlocked = FALSE;
327    eina_lock_release(&priv->m);
328
329    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop,
330                                        (object), TRUE);
331 }
332
333 static GstFlowReturn
334 evas_video_sink_preroll(GstBaseSink* bsink, GstBuffer* buffer)
335 {
336    Emotion_Gstreamer_Buffer *send;
337    EvasVideoSinkPrivate *priv;
338    EvasVideoSink *sink;
339
340    INF("sink preroll %p [%i]", GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
341
342    if (GST_BUFFER_SIZE(buffer) <= 0)
343      {
344         WRN("empty buffer");
345         return GST_FLOW_OK;
346      }
347
348    sink = EVAS_VIDEO_SINK(bsink);
349    priv = sink->priv;
350
351    send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_TRUE);
352
353    if (send)
354      ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
355
356    return GST_FLOW_OK;
357 }
358
359 static GstFlowReturn
360 evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
361 {
362    Emotion_Gstreamer_Buffer *send;
363    EvasVideoSinkPrivate *priv;
364    EvasVideoSink *sink;
365
366    INF("sink render %p [%i]", GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
367
368    if (GST_BUFFER_SIZE(buffer) <= 0)
369      {
370         WRN("empty buffer");
371         return GST_FLOW_OK;
372      }
373
374    sink = EVAS_VIDEO_SINK(bsink);
375    priv = sink->priv;
376
377    eina_lock_take(&priv->m);
378
379    if (priv->unlocked) {
380       ERR("LOCKED");
381       eina_lock_release(&priv->m);
382       return GST_FLOW_OK;
383    }
384
385    send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_FALSE);
386    if (!send) {
387       eina_lock_release(&priv->m);
388       return GST_FLOW_ERROR;
389    }
390
391    ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send);
392
393    eina_condition_wait(&priv->c);
394    eina_lock_release(&priv->m);
395
396    return GST_FLOW_OK;
397 }
398
399 static void
400 evas_video_sink_main_render(void *data)
401 {
402    Emotion_Gstreamer_Buffer *send;
403    Emotion_Gstreamer_Video *ev = NULL;
404    Emotion_Video_Stream *vstream;
405    EvasVideoSinkPrivate* priv;
406    GstBuffer* buffer;
407    unsigned char *evas_data;
408    const guint8 *gst_data;
409    GstFormat fmt = GST_FORMAT_TIME;
410    Evas_Coord w, h;
411    gint64 pos;
412    Eina_Bool preroll;
413
414    send = data;
415
416    priv = send->sink;
417    if (!priv) goto exit_point;
418    if (!priv->o) goto exit_point;
419
420    buffer = send->frame;
421    preroll = send->preroll;
422
423    if (priv->unlocked) goto exit_point;
424
425    gst_data = GST_BUFFER_DATA(buffer);
426    if (!gst_data) goto exit_point;
427
428    ev = send->ev;
429    if (!ev) goto exit_point;
430
431    _emotion_gstreamer_video_pipeline_parse(ev, EINA_TRUE);
432
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)
438      goto exit_point;
439
440    INF("sink main render [%i, %i]", w, h);
441
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);
445
446    evas_data = (unsigned char *)evas_object_image_data_get(priv->o, 1);
447
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)
451      {
452       case GST_VIDEO_FORMAT_BGR:
453         {
454            unsigned char *evas_tmp;
455            int x;
456            int y;
457
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];
465                  evas_tmp[3] = 255;
466                  gst_data += 3;
467                  evas_tmp += 4;
468               }
469            }
470            break;
471         }
472
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:
476         {
477            unsigned char *evas_tmp;
478            int x;
479            int y;
480
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];
488                  evas_tmp[3] = 255;
489                  gst_data += 4;
490                  evas_tmp += 4;
491               }
492            }
493            break;
494         }
495
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:
499         {
500            unsigned char *evas_tmp;
501            int x;
502            int y;
503            unsigned char alpha;
504
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++) {
509                  alpha = gst_data[3];
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;
513                  evas_tmp[3] = alpha;
514                  gst_data += 4;
515                  evas_tmp += 4;
516               }
517            }
518            break;
519         }
520
521       case GST_VIDEO_FORMAT_I420:
522         {
523            int i;
524            const unsigned char **rows;
525
526            evas_object_image_pixels_dirty_set(priv->o, 1);
527            rows = (const unsigned char **)evas_data;
528
529            for (i = 0; i < priv->height; i++)
530              rows[i] = &gst_data[i * priv->width];
531
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)];
535
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)];
539            break;
540         }
541
542       case GST_VIDEO_FORMAT_YV12:
543         {
544            int i;
545            const unsigned char **rows;
546
547            evas_object_image_pixels_dirty_set(priv->o, 1);
548
549            rows = (const unsigned char **)evas_data;
550
551            for (i = 0; i < priv->height; i++)
552              rows[i] = &gst_data[i * priv->width];
553
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)];
557
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)];
561            break;
562         }
563
564       case GST_VIDEO_FORMAT_YUY2:
565         {
566            int i;
567            const unsigned char **rows;
568
569            evas_object_image_pixels_dirty_set(priv->o, 1);
570
571            rows = (const unsigned char **)evas_data;
572
573            for (i = 0; i < priv->height; i++)
574              rows[i] = &gst_data[i * priv->width * 2];
575            break;
576         }
577
578       default:
579          switch (priv->eformat)
580            {
581             case EVAS_COLORSPACE_YCBCR420NV12601_PL:
582               {
583                  int i;
584                  const unsigned char **rows;
585
586                  evas_object_image_pixels_dirty_set(priv->o, 1);
587
588                  rows = (const unsigned char **)evas_data;
589
590                  for (i = 0; i < priv->height; i++)
591                    rows[i] = &gst_data[i * priv->width];
592
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];
596                  break;
597               }
598             case EVAS_COLORSPACE_YCBCR420TM12601_PL:
599               {
600                  int i;
601                  const unsigned char **rows;
602
603                  evas_object_image_pixels_dirty_set(priv->o, 1);
604
605                  rows = (const unsigned char **)evas_data;
606
607                  for (i = 0; i < (priv->height / 32) / 2; i++)
608                    rows[i] = &gst_data[i * priv->width * 2 * 32];
609
610                  if ((priv->height / 32) % 2)
611                    rows[i] = &gst_data[i * priv->width * 2 * 32];
612
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];
616                  break;
617               }
618             default:
619                WRN("No way to decode %x colorspace !", priv->eformat);
620            }
621      }
622
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);
626
627    _emotion_frame_new(ev->obj);
628
629    vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1);
630
631    gst_element_query_position(ev->pipeline, &fmt, &pos);
632    ev->position = (double)pos / (double)GST_SECOND;
633
634    vstream->width = priv->width;
635    vstream->height = priv->height;
636    ev->ratio = (double) priv->width / (double) priv->height;
637
638    _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time);
639    _emotion_frame_resize(ev->obj, priv->width, priv->height, ev->ratio);
640
641    if (priv->last_buffer) gst_buffer_unref(priv->last_buffer);
642    priv->last_buffer = gst_buffer_ref(buffer);
643
644  exit_point:
645    emotion_gstreamer_buffer_free(send);
646
647    if (preroll || !priv->o || !ev) return ;
648
649    eina_lock_take(&priv->m);
650    if (!priv->unlocked)
651      eina_condition_signal(&priv->c);
652
653    eina_lock_release(&priv->m);
654 }
655
656 static void
657 unlock_buffer_mutex(EvasVideoSinkPrivate* priv)
658 {
659    eina_lock_take(&priv->m);
660    priv->unlocked = EINA_TRUE;
661
662    eina_condition_signal(&priv->c);
663    eina_lock_release(&priv->m);
664 }
665
666 static void
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)
670 {
671    typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1, gpointer data2);
672    marshalfunc_VOID__MINIOBJECT callback;
673    GCClosure *cc;
674    gpointer data1, data2;
675
676    cc = (GCClosure *) closure;
677
678    g_return_if_fail(n_param_values == 2);
679
680    if (G_CCLOSURE_SWAP_DATA(closure)) {
681       data1 = closure->data;
682       data2 = g_value_peek_pointer(param_values + 0);
683    } else {
684       data1 = g_value_peek_pointer(param_values + 0);
685       data2 = closure->data;
686    }
687    callback = (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data : cc->callback);
688
689    callback(data1, gst_value_get_mini_object(param_values + 1), data2);
690 }
691
692 static void
693 evas_video_sink_class_init(EvasVideoSinkClass* klass)
694 {
695    GObjectClass* gobject_class;
696    GstBaseSinkClass* gstbase_sink_class;
697
698    gobject_class = G_OBJECT_CLASS(klass);
699    gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
700
701    g_type_class_add_private(klass, sizeof(EvasVideoSinkPrivate));
702
703    gobject_class->set_property = evas_video_sink_set_property;
704    gobject_class->get_property = evas_video_sink_get_property;
705
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",
709                                                           G_PARAM_READWRITE));
710
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));
715
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",
723                                                           G_PARAM_READWRITE));
724
725    gobject_class->dispose = evas_video_sink_dispose;
726
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;
734
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),
738                                                              0,
739                                                              0,
740                                                              0,
741                                                              marshal_VOID__MINIOBJECT,
742                                                              G_TYPE_NONE, 1, GST_TYPE_BUFFER);
743 }
744
745 gboolean
746 gstreamer_plugin_init (GstPlugin * plugin)
747 {
748    return gst_element_register (plugin,
749                                 "emotion-sink",
750                                 GST_RANK_NONE,
751                                 EVAS_TYPE_VIDEO_SINK);
752 }
753
754 static void
755 _emotion_gstreamer_pause(void *data, Ecore_Thread *thread)
756 {
757    Emotion_Gstreamer_Video *ev = data;
758
759    if (ecore_thread_check(thread) || !ev->pipeline) return ;
760
761    gst_element_set_state(ev->pipeline, GST_STATE_PAUSED);
762 }
763
764 static void
765 _emotion_gstreamer_cancel(void *data, Ecore_Thread *thread)
766 {
767    Emotion_Gstreamer_Video *ev = data;
768
769    ev->threads = eina_list_remove(ev->threads, thread);
770
771    if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
772      em_shutdown(ev);
773 }
774
775 static void
776 _emotion_gstreamer_end(void *data, Ecore_Thread *thread)
777 {
778    Emotion_Gstreamer_Video *ev = data;
779
780    ev->threads = eina_list_remove(ev->threads, thread);
781
782    if (ev->play)
783      {
784         gst_element_set_state(ev->pipeline, GST_STATE_PLAYING);
785         ev->play_started = 1;
786      }
787
788    if (ev->in == ev->out && ev->threads == NULL && ev->delete_me)
789      em_shutdown(ev);
790    else
791      _emotion_gstreamer_video_pipeline_parse(data, EINA_TRUE);
792 }
793
794 GstElement *
795 gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev,
796                          Evas_Object *o,
797                          const char *uri)
798 {
799    GstElement *playbin;
800    GstElement *sink;
801    Evas_Object *obj;
802
803    obj = emotion_object_image_get(o);
804    if (!obj)
805      {
806         ERR("Not Evas_Object specified");
807         return NULL;
808      }
809
810    playbin = gst_element_factory_make("playbin2", "playbin");
811    if (!playbin)
812      {
813         ERR("Unable to create 'playbin' GstElement.");
814         return NULL;
815      }
816
817    sink = gst_element_factory_make("emotion-sink", "sink");
818    if (!sink)
819      {
820         ERR("Unable to create 'emotion-sink' GstElement.");
821         goto unref_pipeline;
822      }
823
824    g_object_set(G_OBJECT(sink), "evas-object", obj, NULL);
825    g_object_set(G_OBJECT(sink), "ev", ev, NULL);
826
827    g_object_set(G_OBJECT(playbin), "video-sink", sink, NULL);
828    g_object_set(G_OBJECT(playbin), "uri", uri, NULL);
829
830    ev->pipeline = playbin;
831    ev->sink = sink;
832    ev->threads = eina_list_append(ev->threads,
833                                   ecore_thread_run(_emotion_gstreamer_pause,
834                                                    _emotion_gstreamer_end,
835                                                    _emotion_gstreamer_cancel,
836                                                    ev));
837
838    /** NOTE: you need to set: GST_DEBUG_DUMP_DOT_DIR=/tmp EMOTION_ENGINE=gstreamer to save the $EMOTION_GSTREAMER_DOT file in '/tmp' */
839    /** then call dot -Tpng -oemotion_pipeline.png /tmp/$TIMESTAMP-$EMOTION_GSTREAMER_DOT.dot */
840    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"));
841
842    return playbin;
843
844  unref_pipeline:
845    gst_object_unref(playbin);
846    return NULL;
847 }