emotion: oops forgotten file.
[profile/ivi/emotion.git] / src / modules / gstreamer / emotion_sink.c
1 #include <glib.h>
2 #include <gst/gst.h>
3 #include <gst/video/video.h>
4 #include <gst/video/gstvideosink.h>
5
6 #include <Ecore.h>
7
8 #include "emotion_gstreamer.h"
9
10 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
11                                                                    GST_PAD_SINK, GST_PAD_ALWAYS,
12                                                                    GST_STATIC_CAPS(GST_VIDEO_CAPS_BGRx ";" GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_BGRA ";" GST_VIDEO_CAPS_YUV("{I420,YV12}")));
13
14
15 GST_DEBUG_CATEGORY_STATIC(evas_video_sink_debug);
16 #define GST_CAT_DEFAULT evas_video_sink_debug
17
18 enum {
19   REPAINT_REQUESTED,
20   LAST_SIGNAL
21 };
22
23 enum {
24   PROP_0,
25   PROP_EVAS_OBJECT,
26   PROP_WIDTH,
27   PROP_HEIGHT,
28   PROP_LAST,
29 };
30
31 static guint evas_video_sink_signals[LAST_SIGNAL] = { 0, };
32
33 struct _EvasVideoSinkPrivate {
34    Evas_Object *o;
35    Ecore_Pipe *p;
36
37    int width;
38    int height;
39    gboolean update_size;
40    GstVideoFormat format;
41
42    GMutex* buffer_mutex;
43    GCond* data_cond;
44
45    // If this is TRUE all processing should finish ASAP
46    // This is necessary because there could be a race between
47    // unlock() and render(), where unlock() wins, signals the
48    // GCond, then render() tries to render a frame although
49    // everything else isn't running anymore. This will lead
50    // to deadlocks because render() holds the stream lock.
51    //
52    // Protected by the buffer mutex
53    gboolean unlocked;
54 };
55
56 #define _do_init(bla)                                   \
57   GST_DEBUG_CATEGORY_INIT(evas_video_sink_debug,        \
58                           "evassink",                   \
59                           0,                            \
60                           "evas video sink")
61
62 GST_BOILERPLATE_FULL(EvasVideoSink,
63                      evas_video_sink,
64                      GstVideoSink,
65                      GST_TYPE_VIDEO_SINK,
66                      _do_init);
67
68
69 static void unlock_buffer_mutex(EvasVideoSinkPrivate* priv);
70
71 static void evas_video_sink_render_handler(void *data, void *buf, unsigned int len);
72
73 static void
74 evas_video_sink_base_init(gpointer g_class)
75 {
76    GstElementClass* element_class;
77
78    element_class = GST_ELEMENT_CLASS(g_class);
79    gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sinktemplate));
80    gst_element_class_set_details_simple(element_class, "Evas video sink",
81                                         "Sink/Video", "Sends video data from a GStreamer pipeline to an Evas object",
82                                         "Vincent Torri <vtorri@univ-evry.fr>");
83 }
84
85 static void
86 evas_video_sink_init(EvasVideoSink* sink, EvasVideoSinkClass* klass __UNUSED__)
87 {
88    EvasVideoSinkPrivate* priv;
89
90    sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, EVAS_TYPE_VIDEO_SINK, EvasVideoSinkPrivate);
91    priv->o = NULL;
92    priv->p = ecore_pipe_add(evas_video_sink_render_handler, sink);
93    priv->width = 0;
94    priv->height = 0;
95    priv->update_size = TRUE;
96    priv->format = GST_VIDEO_FORMAT_UNKNOWN;
97    priv->data_cond = g_cond_new();
98    priv->buffer_mutex = g_mutex_new();
99 }
100
101
102 /**** Object methods ****/
103
104 static void
105 evas_video_sink_set_property(GObject * object, guint prop_id,
106                              const GValue * value, GParamSpec * pspec)
107 {
108    EvasVideoSink* sink;
109    EvasVideoSinkPrivate* priv;
110
111    sink = EVAS_VIDEO_SINK (object);
112    priv = sink->priv;
113
114    switch (prop_id) {
115     case PROP_EVAS_OBJECT:
116        g_mutex_lock(priv->buffer_mutex);
117        priv->o = g_value_get_pointer (value);
118        g_mutex_unlock(priv->buffer_mutex);
119        break;
120     default:
121        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
122        break;
123    }
124 }
125
126 static void
127 evas_video_sink_get_property(GObject * object, guint prop_id,
128                              GValue * value, GParamSpec * pspec)
129 {
130    EvasVideoSink* sink;
131    EvasVideoSinkPrivate* priv;
132
133    sink = EVAS_VIDEO_SINK (object);
134    priv = sink->priv;
135
136    switch (prop_id) {
137     case PROP_EVAS_OBJECT:
138        g_mutex_lock(priv->buffer_mutex);
139        g_value_set_pointer (value, priv->o);
140        g_mutex_unlock(priv->buffer_mutex);
141        break;
142     case PROP_WIDTH:
143        g_mutex_lock(priv->buffer_mutex);
144        g_value_set_int(value, priv->width);
145        g_mutex_unlock(priv->buffer_mutex);
146        break;
147     case PROP_HEIGHT:
148        g_mutex_lock(priv->buffer_mutex);
149        g_value_set_int (value, priv->height);
150        g_mutex_unlock(priv->buffer_mutex);
151        break;
152     default:
153        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
154        break;
155    }
156 }
157
158 static void
159 evas_video_sink_dispose(GObject* object)
160 {
161    EvasVideoSink* sink;
162    EvasVideoSinkPrivate* priv;
163
164    sink = EVAS_VIDEO_SINK(object);
165    priv = sink->priv;
166
167    if (priv->buffer_mutex) {
168       g_mutex_free(priv->buffer_mutex);
169       priv->buffer_mutex = 0;
170    }
171
172    if (priv->data_cond) {
173       g_cond_free(priv->data_cond);
174       priv->data_cond = 0;
175    }
176
177    if (priv->p) {
178       ecore_pipe_del(priv->p);
179       priv->p = NULL;
180    }
181
182    G_OBJECT_CLASS(parent_class)->dispose(object);
183 }
184
185
186 /**** BaseSink methods ****/
187
188 gboolean evas_video_sink_set_caps(GstBaseSink *bsink, GstCaps *caps)
189 {
190    EvasVideoSink* sink;
191    EvasVideoSinkPrivate* priv;
192    int width;
193    int height;
194
195    sink = EVAS_VIDEO_SINK(bsink);
196    priv = sink->priv;
197
198    if (G_UNLIKELY(!gst_video_format_parse_caps(caps, &priv->format, &width, &height))) {
199       return FALSE;
200    }
201
202    if ((width != priv->width) || (height != priv->height))
203      {
204         priv->width = width;
205         priv->height = height;
206         priv->update_size = TRUE;
207      }
208
209    printf("format :");
210    switch (priv->format)
211      {
212       case GST_VIDEO_FORMAT_I420:
213          evas_object_image_size_set(priv->o, priv->width, priv->height);
214          evas_object_image_fill_set(priv->o, 0, 0, priv->width, priv->height);
215          evas_object_image_colorspace_set(priv->o, EVAS_COLORSPACE_YCBCR422P601_PL);
216          evas_object_image_alpha_set(priv->o, 0);
217          printf ("I420\n");
218          break;
219       case GST_VIDEO_FORMAT_YV12:
220          evas_object_image_size_set(priv->o, priv->width, priv->height);
221          evas_object_image_fill_set(priv->o, 0, 0, priv->width, priv->height);
222          evas_object_image_colorspace_set(priv->o, EVAS_COLORSPACE_YCBCR422P601_PL);
223          evas_object_image_alpha_set(priv->o, 0);
224          printf ("YV12\n");
225          break;
226       case GST_VIDEO_FORMAT_BGR:
227          printf ("BGR\n");
228          break;
229       case GST_VIDEO_FORMAT_BGRx:
230          printf ("BGRx\n");
231          break;
232       case GST_VIDEO_FORMAT_BGRA:
233          printf ("BGRA\n");
234          break;
235       default:
236          printf ("unsupported : %d\n", priv->format);
237          return FALSE;
238      }
239
240    return TRUE;
241 }
242
243 static gboolean
244 evas_video_sink_start(GstBaseSink* base_sink)
245 {
246    EvasVideoSinkPrivate* priv;
247    gboolean res = TRUE;
248
249    priv = EVAS_VIDEO_SINK(base_sink)->priv;
250    g_mutex_lock(priv->buffer_mutex);
251    if (!priv->o)
252      res = FALSE;
253    else
254      {
255         if (!priv->p)
256           res = FALSE;
257         else
258           {
259              priv->unlocked = FALSE;
260           }
261      }
262    g_mutex_unlock(priv->buffer_mutex);
263    return res;
264 }
265
266 static gboolean
267 evas_video_sink_stop(GstBaseSink* base_sink)
268 {
269    EvasVideoSinkPrivate* priv = EVAS_VIDEO_SINK(base_sink)->priv;
270
271    unlock_buffer_mutex(priv);
272    return TRUE;
273 }
274
275 static gboolean
276 evas_video_sink_unlock(GstBaseSink* object)
277 {
278    EvasVideoSink* sink;
279
280    sink = EVAS_VIDEO_SINK(object);
281
282    unlock_buffer_mutex(sink->priv);
283
284    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock,
285                                        (object), TRUE);
286 }
287
288 static gboolean
289 evas_video_sink_unlock_stop(GstBaseSink* object)
290 {
291    EvasVideoSink* sink;
292    EvasVideoSinkPrivate* priv;
293
294    sink = EVAS_VIDEO_SINK(object);
295    priv = sink->priv;
296
297    g_mutex_lock(priv->buffer_mutex);
298    priv->unlocked = FALSE;
299    g_mutex_unlock(priv->buffer_mutex);
300
301    return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop,
302                                        (object), TRUE);
303 }
304
305 static GstFlowReturn
306 evas_video_sink_preroll(GstBaseSink* bsink, GstBuffer* buffer)
307 {
308    return GST_FLOW_OK;
309 }
310
311 static GstFlowReturn
312 evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
313 {
314    GstBuffer *send;
315    EvasVideoSink* sink;
316    EvasVideoSinkPrivate* priv;
317    Eina_Bool ret;
318
319    sink = EVAS_VIDEO_SINK(bsink);
320    priv = sink->priv;
321
322    g_mutex_lock(priv->buffer_mutex);
323
324    if (priv->unlocked) {
325       g_mutex_unlock(priv->buffer_mutex);
326       return GST_FLOW_OK;
327    }
328
329    send = gst_buffer_ref(buffer);
330    ret = ecore_pipe_write(priv->p, &send, sizeof(buffer));
331    if (!ret)
332      return GST_FLOW_ERROR;
333
334    g_cond_wait(priv->data_cond, priv->buffer_mutex);
335    g_mutex_unlock(priv->buffer_mutex);
336
337    return GST_FLOW_OK;
338 }
339
340 static void evas_video_sink_render_handler(void *data,
341                                            void *buf,
342                                            unsigned int len)
343 {
344    Emotion_Gstreamer_Video *ev;
345    Emotion_Video_Stream *vstream;
346    EvasVideoSink* sink;
347    EvasVideoSinkPrivate* priv;
348    GstBuffer* buffer;
349    unsigned char *evas_data;
350    const guint8 *gst_data;
351    GstQuery *query;
352    GstFormat fmt = GST_FORMAT_TIME;
353    gint64 pos;
354
355    sink = (EvasVideoSink *)data;
356    priv = sink->priv;
357
358    buffer = *((GstBuffer **)buf);
359
360    if (priv->unlocked)
361      goto exit_point;
362
363    gst_data = GST_BUFFER_DATA(buffer);
364    if (!gst_data) goto exit_point;
365    if (priv->update_size)
366      {
367         evas_object_image_size_set(priv->o, priv->width, priv->height);
368         evas_object_image_fill_set(priv->o, 0, 0, priv->width, priv->height);
369         priv->update_size = FALSE;
370      }
371
372    evas_data = (unsigned char *)evas_object_image_data_get(priv->o, 1);
373
374    // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
375    // Here we convert to Evas's BGRA.
376    if (priv->format == GST_VIDEO_FORMAT_BGR) {
377       unsigned char *evas_tmp;
378       int x;
379       int y;
380
381       evas_tmp = evas_data;
382       /* FIXME: could this be optimized ? */
383       for (x = 0; x < priv->height; x++) {
384          for (y = 0; y < priv->width; y++) {
385             evas_tmp[0] = gst_data[0];
386             evas_tmp[1] = gst_data[1];
387             evas_tmp[2] = gst_data[2];
388             evas_tmp[3] = 255;
389             gst_data += 3;
390             evas_tmp += 4;
391          }
392       }
393    }
394
395    // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
396    // Here we convert to Evas's BGRA.
397    if (priv->format == GST_VIDEO_FORMAT_BGRx) {
398       unsigned char *evas_tmp;
399       int x;
400       int y;
401
402       evas_tmp = evas_data;
403       /* FIXME: could this be optimized ? */
404       for (x = 0; x < priv->height; x++) {
405          for (y = 0; y < priv->width; y++) {
406             evas_tmp[0] = gst_data[0];
407             evas_tmp[1] = gst_data[1];
408             evas_tmp[2] = gst_data[2];
409             evas_tmp[3] = 255;
410             gst_data += 4;
411             evas_tmp += 4;
412          }
413       }
414    }
415
416    // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't.
417    // Here we convert to Evas's BGRA.
418    if (priv->format == GST_VIDEO_FORMAT_BGRA) {
419       unsigned char *evas_tmp;
420       int x;
421       int y;
422       unsigned char alpha;
423
424       evas_tmp = evas_data;
425       /* FIXME: could this be optimized ? */
426       for (x = 0; x < priv->height; x++) {
427          for (y = 0; y < priv->width; y++) {
428             alpha = gst_data[3];
429             evas_tmp[0] = (gst_data[0] * alpha) / 255;
430             evas_tmp[1] = (gst_data[1] * alpha) / 255;
431             evas_tmp[2] = (gst_data[2] * alpha) / 255;
432             evas_tmp[3] = alpha;
433             gst_data += 4;
434             evas_tmp += 4;
435          }
436       }
437    }
438
439    if (priv->format == GST_VIDEO_FORMAT_I420) {
440       int i;
441       unsigned char **rows;
442
443       evas_object_image_pixels_dirty_set(priv->o, 1);
444       rows = (unsigned char **)evas_data;
445
446       for (i = 0; i < priv->height; i++)
447         rows[i] = &gst_data[i * priv->width];
448
449       rows += priv->height;
450       for (i = 0; i < (priv->height / 2); i++)
451         rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)];
452
453       rows += priv->height / 2;
454       for (i = 0; i < (priv->height / 2); i++)
455         rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)];
456    }
457
458    if (priv->format == GST_VIDEO_FORMAT_YV12) {
459       int i;
460       unsigned char **rows;
461
462       evas_object_image_pixels_dirty_set(priv->o, 1);
463
464       rows = (unsigned char **)evas_data;
465
466       for (i = 0; i < priv->height; i++)
467         rows[i] = &gst_data[i * priv->width];
468
469       rows += priv->height;
470       for (i = 0; i < (priv->height / 2); i++)
471         rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)];
472
473       rows += priv->height / 2;
474       for (i = 0; i < (priv->height / 2); i++)
475         rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)];
476    }
477
478    evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height);
479    evas_object_image_data_set(priv->o, evas_data);
480    evas_object_image_pixels_dirty_set(priv->o, 0);
481
482    ev = evas_object_data_get(priv->o, "_emotion_gstreamer_video");
483    _emotion_frame_new(ev->obj);
484
485    vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1);
486
487    gst_element_query_position(ev->pipeline, &fmt, &pos);
488    ev->position = (double)pos / (double)GST_SECOND;
489
490    vstream->width = priv->width;
491    vstream->height = priv->height;
492    ev->ratio = (double) priv->width / (double) priv->height;
493
494    if (vstream)
495      {
496         _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time);
497         _emotion_frame_resize(ev->obj, vstream->width, vstream->height, ev->ratio);
498      }
499
500  exit_point:
501    gst_buffer_unref(buffer);
502
503    g_mutex_lock(priv->buffer_mutex);
504
505    if (priv->unlocked) {
506       g_mutex_unlock(priv->buffer_mutex);
507       return;
508    }
509
510    g_cond_signal(priv->data_cond);
511    g_mutex_unlock(priv->buffer_mutex);
512 }
513
514 static void
515 unlock_buffer_mutex(EvasVideoSinkPrivate* priv)
516 {
517    g_mutex_lock(priv->buffer_mutex);
518
519    priv->unlocked = TRUE;
520    g_cond_signal(priv->data_cond);
521    g_mutex_unlock(priv->buffer_mutex);
522 }
523
524 static void
525 marshal_VOID__MINIOBJECT(GClosure * closure, GValue * return_value __UNUSED__,
526                          guint n_param_values, const GValue * param_values,
527                          gpointer invocation_hint __UNUSED__, gpointer marshal_data)
528 {
529    typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1, gpointer data2);
530    marshalfunc_VOID__MINIOBJECT callback;
531    GCClosure *cc;
532    gpointer data1, data2;
533
534    cc = (GCClosure *) closure;
535
536    g_return_if_fail(n_param_values == 2);
537
538    if (G_CCLOSURE_SWAP_DATA(closure)) {
539       data1 = closure->data;
540       data2 = g_value_peek_pointer(param_values + 0);
541    } else {
542       data1 = g_value_peek_pointer(param_values + 0);
543       data2 = closure->data;
544    }
545    callback = (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data : cc->callback);
546
547    callback(data1, gst_value_get_mini_object(param_values + 1), data2);
548 }
549
550 static void
551 evas_video_sink_class_init(EvasVideoSinkClass* klass)
552 {
553    GObjectClass* gobject_class;
554    GstBaseSinkClass* gstbase_sink_class;
555
556    gobject_class = G_OBJECT_CLASS(klass);
557    gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
558
559    g_type_class_add_private(klass, sizeof(EvasVideoSinkPrivate));
560
561    gobject_class->set_property = evas_video_sink_set_property;
562    gobject_class->get_property = evas_video_sink_get_property;
563
564    g_object_class_install_property (gobject_class, PROP_EVAS_OBJECT,
565                                     g_param_spec_pointer ("evas-object", "Evas Object",
566                                                           "The Evas object where the display of the video will be done",
567                                                           G_PARAM_READWRITE));
568
569    g_object_class_install_property (gobject_class, PROP_WIDTH,
570                                     g_param_spec_int ("width", "Width",
571                                                       "The width of the video",
572                                                       0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
573
574    g_object_class_install_property (gobject_class, PROP_HEIGHT,
575                                     g_param_spec_int ("height", "Height",
576                                                       "The height of the video",
577                                                       0, 65536, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
578
579    gobject_class->dispose = evas_video_sink_dispose;
580
581    gstbase_sink_class->set_caps = evas_video_sink_set_caps;
582    gstbase_sink_class->stop = evas_video_sink_stop;
583    gstbase_sink_class->start = evas_video_sink_start;
584    gstbase_sink_class->unlock = evas_video_sink_unlock;
585    gstbase_sink_class->unlock_stop = evas_video_sink_unlock_stop;
586    gstbase_sink_class->render = evas_video_sink_render;
587    gstbase_sink_class->preroll = evas_video_sink_preroll;
588
589    evas_video_sink_signals[REPAINT_REQUESTED] = g_signal_new("repaint-requested",
590                                                              G_TYPE_FROM_CLASS(klass),
591                                                              (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
592                                                              0,
593                                                              0,
594                                                              0,
595                                                              marshal_VOID__MINIOBJECT,
596                                                              G_TYPE_NONE, 1, GST_TYPE_BUFFER);
597 }
598
599 gboolean
600 gstreamer_plugin_init (GstPlugin * plugin)
601 {
602    return gst_element_register (plugin,
603                                 "emotion-sink",
604                                 GST_RANK_NONE,
605                                 EVAS_TYPE_VIDEO_SINK);
606 }
607
608 GstElement *
609 gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev,
610                          Evas_Object *o,
611                          const char *uri)
612 {
613    GstElement *playbin;
614    GstElement *sink;
615    Evas_Object *obj;
616    GstStateChangeReturn res;
617
618    obj = _emotion_image_get(o);
619    if (!obj)
620      {
621         ERR("Not Evas_Object specified");
622         return NULL;
623      }
624
625    playbin = gst_element_factory_make("playbin2", "playbin");
626    if (!playbin)
627      {
628         ERR("Unable to create 'playbin' GstElement.");
629         return NULL;
630      }
631
632    sink = gst_element_factory_make("emotion-sink", "sink");
633    if (!sink)
634      {
635         ERR("Unable to create 'emotion-sink' GstElement.");
636         goto unref_pipeline;
637      }
638
639    g_object_set(G_OBJECT(playbin), "video-sink", sink, NULL);
640    g_object_set(G_OBJECT(playbin), "uri", uri, NULL);
641    g_object_set(G_OBJECT(sink), "evas-object", obj, NULL);
642
643    res = gst_element_set_state(playbin, GST_STATE_PAUSED);
644    if (res == GST_STATE_CHANGE_FAILURE)
645      {
646         ERR("Unable to set GST_STATE_PAUSED.");
647         goto unref_pipeline;
648      }
649
650    res = gst_element_get_state(playbin, NULL, NULL, GST_CLOCK_TIME_NONE);
651    if (res != GST_STATE_CHANGE_SUCCESS)
652      {
653         ERR("Unable to get GST_CLOCK_TIME_NONE.");
654         goto unref_pipeline;
655      }
656
657    evas_object_data_set(obj, "_emotion_gstreamer_video", ev);
658
659    return playbin;
660
661  unref_pipeline:
662    gst_object_unref(playbin);
663    return NULL;
664 }