3 #include <gst/video/video.h>
4 #include <gst/video/gstvideosink.h>
8 #include "emotion_gstreamer.h"
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}")));
15 GST_DEBUG_CATEGORY_STATIC(evas_video_sink_debug);
16 #define GST_CAT_DEFAULT evas_video_sink_debug
31 static guint evas_video_sink_signals[LAST_SIGNAL] = { 0, };
33 struct _EvasVideoSinkPrivate {
40 GstVideoFormat format;
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.
52 // Protected by the buffer mutex
56 #define _do_init(bla) \
57 GST_DEBUG_CATEGORY_INIT(evas_video_sink_debug, \
62 GST_BOILERPLATE_FULL(EvasVideoSink,
69 static void unlock_buffer_mutex(EvasVideoSinkPrivate* priv);
71 static void evas_video_sink_render_handler(void *data, void *buf, unsigned int len);
74 evas_video_sink_base_init(gpointer g_class)
76 GstElementClass* element_class;
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>");
86 evas_video_sink_init(EvasVideoSink* sink, EvasVideoSinkClass* klass __UNUSED__)
88 EvasVideoSinkPrivate* priv;
90 sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, EVAS_TYPE_VIDEO_SINK, EvasVideoSinkPrivate);
92 priv->p = ecore_pipe_add(evas_video_sink_render_handler, sink);
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();
102 /**** Object methods ****/
105 evas_video_sink_set_property(GObject * object, guint prop_id,
106 const GValue * value, GParamSpec * pspec)
109 EvasVideoSinkPrivate* priv;
111 sink = EVAS_VIDEO_SINK (object);
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);
121 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127 evas_video_sink_get_property(GObject * object, guint prop_id,
128 GValue * value, GParamSpec * pspec)
131 EvasVideoSinkPrivate* priv;
133 sink = EVAS_VIDEO_SINK (object);
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);
143 g_mutex_lock(priv->buffer_mutex);
144 g_value_set_int(value, priv->width);
145 g_mutex_unlock(priv->buffer_mutex);
148 g_mutex_lock(priv->buffer_mutex);
149 g_value_set_int (value, priv->height);
150 g_mutex_unlock(priv->buffer_mutex);
153 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
159 evas_video_sink_dispose(GObject* object)
162 EvasVideoSinkPrivate* priv;
164 sink = EVAS_VIDEO_SINK(object);
167 if (priv->buffer_mutex) {
168 g_mutex_free(priv->buffer_mutex);
169 priv->buffer_mutex = 0;
172 if (priv->data_cond) {
173 g_cond_free(priv->data_cond);
178 ecore_pipe_del(priv->p);
182 G_OBJECT_CLASS(parent_class)->dispose(object);
186 /**** BaseSink methods ****/
188 gboolean evas_video_sink_set_caps(GstBaseSink *bsink, GstCaps *caps)
191 EvasVideoSinkPrivate* priv;
195 sink = EVAS_VIDEO_SINK(bsink);
198 if (G_UNLIKELY(!gst_video_format_parse_caps(caps, &priv->format, &width, &height))) {
202 if ((width != priv->width) || (height != priv->height))
205 priv->height = height;
206 priv->update_size = TRUE;
210 switch (priv->format)
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);
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);
226 case GST_VIDEO_FORMAT_BGR:
229 case GST_VIDEO_FORMAT_BGRx:
232 case GST_VIDEO_FORMAT_BGRA:
236 printf ("unsupported : %d\n", priv->format);
244 evas_video_sink_start(GstBaseSink* base_sink)
246 EvasVideoSinkPrivate* priv;
249 priv = EVAS_VIDEO_SINK(base_sink)->priv;
250 g_mutex_lock(priv->buffer_mutex);
259 priv->unlocked = FALSE;
262 g_mutex_unlock(priv->buffer_mutex);
267 evas_video_sink_stop(GstBaseSink* base_sink)
269 EvasVideoSinkPrivate* priv = EVAS_VIDEO_SINK(base_sink)->priv;
271 unlock_buffer_mutex(priv);
276 evas_video_sink_unlock(GstBaseSink* object)
280 sink = EVAS_VIDEO_SINK(object);
282 unlock_buffer_mutex(sink->priv);
284 return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock,
289 evas_video_sink_unlock_stop(GstBaseSink* object)
292 EvasVideoSinkPrivate* priv;
294 sink = EVAS_VIDEO_SINK(object);
297 g_mutex_lock(priv->buffer_mutex);
298 priv->unlocked = FALSE;
299 g_mutex_unlock(priv->buffer_mutex);
301 return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop,
306 evas_video_sink_preroll(GstBaseSink* bsink, GstBuffer* buffer)
312 evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
316 EvasVideoSinkPrivate* priv;
319 sink = EVAS_VIDEO_SINK(bsink);
322 g_mutex_lock(priv->buffer_mutex);
324 if (priv->unlocked) {
325 g_mutex_unlock(priv->buffer_mutex);
329 send = gst_buffer_ref(buffer);
330 ret = ecore_pipe_write(priv->p, &send, sizeof(buffer));
332 return GST_FLOW_ERROR;
334 g_cond_wait(priv->data_cond, priv->buffer_mutex);
335 g_mutex_unlock(priv->buffer_mutex);
340 static void evas_video_sink_render_handler(void *data,
344 Emotion_Gstreamer_Video *ev;
345 Emotion_Video_Stream *vstream;
347 EvasVideoSinkPrivate* priv;
349 unsigned char *evas_data;
350 const guint8 *gst_data;
352 GstFormat fmt = GST_FORMAT_TIME;
355 sink = (EvasVideoSink *)data;
358 buffer = *((GstBuffer **)buf);
363 gst_data = GST_BUFFER_DATA(buffer);
364 if (!gst_data) goto exit_point;
365 if (priv->update_size)
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;
372 evas_data = (unsigned char *)evas_object_image_data_get(priv->o, 1);
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;
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];
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;
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];
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;
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++) {
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;
439 if (priv->format == GST_VIDEO_FORMAT_I420) {
441 unsigned char **rows;
443 evas_object_image_pixels_dirty_set(priv->o, 1);
444 rows = (unsigned char **)evas_data;
446 for (i = 0; i < priv->height; i++)
447 rows[i] = &gst_data[i * priv->width];
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)];
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)];
458 if (priv->format == GST_VIDEO_FORMAT_YV12) {
460 unsigned char **rows;
462 evas_object_image_pixels_dirty_set(priv->o, 1);
464 rows = (unsigned char **)evas_data;
466 for (i = 0; i < priv->height; i++)
467 rows[i] = &gst_data[i * priv->width];
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)];
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)];
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);
482 ev = evas_object_data_get(priv->o, "_emotion_gstreamer_video");
483 _emotion_frame_new(ev->obj);
485 vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1);
487 gst_element_query_position(ev->pipeline, &fmt, &pos);
488 ev->position = (double)pos / (double)GST_SECOND;
490 vstream->width = priv->width;
491 vstream->height = priv->height;
492 ev->ratio = (double) priv->width / (double) priv->height;
496 _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time);
497 _emotion_frame_resize(ev->obj, vstream->width, vstream->height, ev->ratio);
501 gst_buffer_unref(buffer);
503 g_mutex_lock(priv->buffer_mutex);
505 if (priv->unlocked) {
506 g_mutex_unlock(priv->buffer_mutex);
510 g_cond_signal(priv->data_cond);
511 g_mutex_unlock(priv->buffer_mutex);
515 unlock_buffer_mutex(EvasVideoSinkPrivate* priv)
517 g_mutex_lock(priv->buffer_mutex);
519 priv->unlocked = TRUE;
520 g_cond_signal(priv->data_cond);
521 g_mutex_unlock(priv->buffer_mutex);
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)
529 typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1, gpointer data2);
530 marshalfunc_VOID__MINIOBJECT callback;
532 gpointer data1, data2;
534 cc = (GCClosure *) closure;
536 g_return_if_fail(n_param_values == 2);
538 if (G_CCLOSURE_SWAP_DATA(closure)) {
539 data1 = closure->data;
540 data2 = g_value_peek_pointer(param_values + 0);
542 data1 = g_value_peek_pointer(param_values + 0);
543 data2 = closure->data;
545 callback = (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data : cc->callback);
547 callback(data1, gst_value_get_mini_object(param_values + 1), data2);
551 evas_video_sink_class_init(EvasVideoSinkClass* klass)
553 GObjectClass* gobject_class;
554 GstBaseSinkClass* gstbase_sink_class;
556 gobject_class = G_OBJECT_CLASS(klass);
557 gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
559 g_type_class_add_private(klass, sizeof(EvasVideoSinkPrivate));
561 gobject_class->set_property = evas_video_sink_set_property;
562 gobject_class->get_property = evas_video_sink_get_property;
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",
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));
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));
579 gobject_class->dispose = evas_video_sink_dispose;
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;
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),
595 marshal_VOID__MINIOBJECT,
596 G_TYPE_NONE, 1, GST_TYPE_BUFFER);
600 gstreamer_plugin_init (GstPlugin * plugin)
602 return gst_element_register (plugin,
605 EVAS_TYPE_VIDEO_SINK);
609 gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev,
616 GstStateChangeReturn res;
618 obj = _emotion_image_get(o);
621 ERR("Not Evas_Object specified");
625 playbin = gst_element_factory_make("playbin2", "playbin");
628 ERR("Unable to create 'playbin' GstElement.");
632 sink = gst_element_factory_make("emotion-sink", "sink");
635 ERR("Unable to create 'emotion-sink' GstElement.");
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);
643 res = gst_element_set_state(playbin, GST_STATE_PAUSED);
644 if (res == GST_STATE_CHANGE_FAILURE)
646 ERR("Unable to set GST_STATE_PAUSED.");
650 res = gst_element_get_state(playbin, NULL, NULL, GST_CLOCK_TIME_NONE);
651 if (res != GST_STATE_CHANGE_SUCCESS)
653 ERR("Unable to get GST_CLOCK_TIME_NONE.");
657 evas_object_data_set(obj, "_emotion_gstreamer_video", ev);
662 gst_object_unref(playbin);