X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fmodules%2Fgstreamer%2Femotion_sink.c;h=ae4b27ec63700d9becc8e43aefe5e87380c038e7;hb=a35ad3a70df5254ee4fc06e45d2577efa453ed6a;hp=5ed548ae144f50538ae856cf418e50d3021acdeb;hpb=c7db88f3e482d152584ae54dd20e0546ee8361e4;p=profile%2Fivi%2Femotion.git diff --git a/src/modules/gstreamer/emotion_sink.c b/src/modules/gstreamer/emotion_sink.c index 5ed548a..ae4b27e 100644 --- a/src/modules/gstreamer/emotion_sink.c +++ b/src/modules/gstreamer/emotion_sink.c @@ -1,10 +1,8 @@ -#include - #include "emotion_gstreamer.h" static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS(GST_VIDEO_CAPS_YUV("{ I420, YV12, YUY2 }") ";" + GST_STATIC_CAPS(GST_VIDEO_CAPS_YUV("{ I420, YV12, YUY2, NV12, ST12, TM12 }") ";" GST_VIDEO_CAPS_BGRx ";" GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_BGRA)); GST_DEBUG_CATEGORY_STATIC(evas_video_sink_debug); @@ -40,8 +38,8 @@ GST_BOILERPLATE_FULL(EvasVideoSink, static void unlock_buffer_mutex(EvasVideoSinkPrivate* priv); - static void evas_video_sink_main_render(void *data); +static void evas_video_sink_samsung_main_render(void *data); static void evas_video_sink_base_init(gpointer g_class) @@ -63,17 +61,16 @@ evas_video_sink_init(EvasVideoSink* sink, EvasVideoSinkClass* klass __UNUSED__) INF("sink init"); sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, EVAS_TYPE_VIDEO_SINK, EvasVideoSinkPrivate); priv->o = NULL; - priv->last_buffer = NULL; priv->width = 0; priv->height = 0; - priv->gformat = GST_VIDEO_FORMAT_UNKNOWN; + priv->func = NULL; priv->eformat = EVAS_COLORSPACE_ARGB8888; + priv->samsung = EINA_FALSE; eina_lock_new(&priv->m); eina_condition_new(&priv->c, &priv->m); priv->unlocked = EINA_FALSE; } - /**** Object methods ****/ static void _cleanup_priv(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) @@ -103,12 +100,16 @@ evas_video_sink_set_property(GObject * object, guint prop_id, eina_lock_take(&priv->m); evas_object_event_callback_del(priv->o, EVAS_CALLBACK_FREE, _cleanup_priv); priv->o = g_value_get_pointer (value); + INF("sink set Evas_Object %p.", priv->o); evas_object_event_callback_add(priv->o, EVAS_CALLBACK_FREE, _cleanup_priv, priv); eina_lock_release(&priv->m); break; case PROP_EV: + INF("sink set ev."); eina_lock_take(&priv->m); priv->ev = g_value_get_pointer (value); + if (priv->ev) + priv->ev->samsung = EINA_TRUE; eina_lock_release(&priv->m); break; default: @@ -130,21 +131,25 @@ evas_video_sink_get_property(GObject * object, guint prop_id, switch (prop_id) { case PROP_EVAS_OBJECT: + INF("sink get property."); eina_lock_take(&priv->m); - g_value_set_pointer (value, priv->o); + g_value_set_pointer(value, priv->o); eina_lock_release(&priv->m); break; case PROP_WIDTH: + INF("sink get width."); eina_lock_take(&priv->m); g_value_set_int(value, priv->width); eina_lock_release(&priv->m); break; case PROP_HEIGHT: + INF("sink get height."); eina_lock_take(&priv->m); g_value_set_int (value, priv->height); eina_lock_release(&priv->m); break; case PROP_EV: + INF("sink get ev."); eina_lock_take(&priv->m); g_value_set_pointer (value, priv->ev); eina_lock_release(&priv->m); @@ -162,17 +167,14 @@ evas_video_sink_dispose(GObject* object) EvasVideoSink* sink; EvasVideoSinkPrivate* priv; + INF("dispose."); + sink = EVAS_VIDEO_SINK(object); priv = sink->priv; eina_lock_free(&priv->m); eina_condition_free(&priv->c); - if (priv->last_buffer) { - gst_buffer_unref(priv->last_buffer); - priv->last_buffer = NULL; - } - G_OBJECT_CLASS(parent_class)->dispose(object); } @@ -183,49 +185,74 @@ gboolean evas_video_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { EvasVideoSink* sink; EvasVideoSinkPrivate* priv; + GstStructure *structure; GstVideoFormat format; - int width; - int height; + guint32 fourcc; + unsigned int i; sink = EVAS_VIDEO_SINK(bsink); priv = sink->priv; - if (G_UNLIKELY(!gst_video_format_parse_caps(caps, &format, &width, &height))) { - ERR("Unable to parse caps."); - return FALSE; - } + structure = gst_caps_get_structure(caps, 0); - priv->width = width; - priv->height = height; - - printf("%p format :", priv->o); - switch (format) - { - case GST_VIDEO_FORMAT_I420: priv->eformat = EVAS_COLORSPACE_YCBCR422P601_PL; - printf ("I420\n"); - break; - case GST_VIDEO_FORMAT_YV12: priv->eformat = EVAS_COLORSPACE_YCBCR422P601_PL; - printf ("YV12\n"); - break; - case GST_VIDEO_FORMAT_YUY2: priv->eformat = EVAS_COLORSPACE_YCBCR422601_PL; - printf("YUY2\n"); - break; - case GST_VIDEO_FORMAT_BGR: priv->eformat = EVAS_COLORSPACE_ARGB8888; - printf ("BGR\n"); - break; - case GST_VIDEO_FORMAT_BGRx: priv->eformat = EVAS_COLORSPACE_ARGB8888; - printf ("BGRx\n"); - break; - case GST_VIDEO_FORMAT_BGRA: priv->eformat = EVAS_COLORSPACE_ARGB8888; - printf ("BGRA\n"); - break; - default: - ERR("unsupported : %d\n", format); - return FALSE; - } - priv->gformat = format; + if (gst_structure_get_int(structure, "width", (int*) &priv->width) + && gst_structure_get_int(structure, "height", (int*) &priv->height) + && gst_structure_get_fourcc(structure, "format", &fourcc)) + { + priv->source_height = priv->height; + + for (i = 0; colorspace_fourcc_convertion[i].name != NULL; ++i) + if (fourcc == colorspace_fourcc_convertion[i].fourcc) + { + fprintf(stderr, "Found '%s'\n", colorspace_fourcc_convertion[i].name); + priv->eformat = colorspace_fourcc_convertion[i].eformat; + priv->func = colorspace_fourcc_convertion[i].func; + if (colorspace_fourcc_convertion[i].force_height) + { + priv->height = (priv->height >> 1) << 1; + } + if (priv->ev) + priv->ev->kill_buffer = EINA_TRUE; + return TRUE; + } + + if (fourcc == GST_MAKE_FOURCC('S', 'T', '1', '2')) + { + fprintf(stderr, "Found '%s'\n", "ST12"); + priv->eformat = EVAS_COLORSPACE_YCBCR420TM12601_PL; + priv->samsung = EINA_TRUE; + priv->func = NULL; + if (priv->ev) + { + priv->ev->samsung = EINA_TRUE; + priv->ev->kill_buffer = EINA_TRUE; + } + return TRUE; + } + } - return TRUE; + INF("fallback code !"); + if (!gst_video_format_parse_caps(caps, &format, (int*) &priv->width, (int*) &priv->height)) + { + ERR("Unable to parse caps."); + return FALSE; + } + + priv->source_height = priv->height; + + for (i = 0; colorspace_format_convertion[i].name != NULL; ++i) + if (format == colorspace_format_convertion[i].format) + { + fprintf(stderr, "Found '%s'\n", colorspace_format_convertion[i].name); + priv->eformat = colorspace_format_convertion[i].eformat; + priv->func = colorspace_format_convertion[i].func; + if (priv->ev) + priv->ev->kill_buffer = EINA_FALSE; + return TRUE; + } + + ERR("unsupported : %d\n", format); + return FALSE; } static gboolean @@ -234,6 +261,8 @@ evas_video_sink_start(GstBaseSink* base_sink) EvasVideoSinkPrivate* priv; gboolean res = TRUE; + INF("sink start"); + priv = EVAS_VIDEO_SINK(base_sink)->priv; eina_lock_take(&priv->m); if (!priv->o) @@ -249,6 +278,8 @@ evas_video_sink_stop(GstBaseSink* base_sink) { EvasVideoSinkPrivate* priv = EVAS_VIDEO_SINK(base_sink)->priv; + INF("sink stop"); + unlock_buffer_mutex(priv); return TRUE; } @@ -258,6 +289,8 @@ evas_video_sink_unlock(GstBaseSink* object) { EvasVideoSink* sink; + INF("sink unlock"); + sink = EVAS_VIDEO_SINK(object); unlock_buffer_mutex(sink->priv); @@ -275,6 +308,8 @@ evas_video_sink_unlock_stop(GstBaseSink* object) sink = EVAS_VIDEO_SINK(object); priv = sink->priv; + INF("sink unlock stop"); + eina_lock_take(&priv->m); priv->unlocked = FALSE; eina_lock_release(&priv->m); @@ -290,13 +325,45 @@ evas_video_sink_preroll(GstBaseSink* bsink, GstBuffer* buffer) EvasVideoSinkPrivate *priv; EvasVideoSink *sink; + INF("sink preroll %p [%i]", GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer)); + sink = EVAS_VIDEO_SINK(bsink); priv = sink->priv; + if (GST_BUFFER_SIZE(buffer) <= 0 && !priv->samsung) + { + WRN("empty buffer"); + return GST_FLOW_OK; + } + send = emotion_gstreamer_buffer_alloc(priv, buffer, EINA_TRUE); if (send) - ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send); + { + if (priv->samsung) + { + if (!priv->func) + { + GstStructure *structure; + GstCaps *caps; + gboolean is_multiplane = FALSE; + + caps = GST_BUFFER_CAPS(buffer); + structure = gst_caps_get_structure (caps, 0); + gst_structure_get_boolean(structure, "multiplane", &is_multiplane); + gst_caps_unref(caps); + + if (is_multiplane) + priv->func = _evas_video_st12_multiplane; + else + priv->func = _evas_video_st12; + } + + ecore_main_loop_thread_safe_call_async(evas_video_sink_samsung_main_render, send); + } + else + ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send); + } return GST_FLOW_OK; } @@ -308,6 +375,8 @@ evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer) EvasVideoSinkPrivate *priv; EvasVideoSink *sink; + INF("sink render %p", buffer); + sink = EVAS_VIDEO_SINK(bsink); priv = sink->priv; @@ -325,7 +394,29 @@ evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer) return GST_FLOW_ERROR; } - ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send); + if (priv->samsung) + { + if (!priv->func) + { + GstStructure *structure; + GstCaps *caps; + gboolean is_multiplane = FALSE; + + caps = GST_BUFFER_CAPS(buffer); + structure = gst_caps_get_structure (caps, 0); + gst_structure_get_boolean(structure, "multiplane", &is_multiplane); + gst_caps_unref(caps); + + if (is_multiplane) + priv->func = _evas_video_st12_multiplane; + else + priv->func = _evas_video_st12; + } + + ecore_main_loop_thread_safe_call_async(evas_video_sink_samsung_main_render, send); + } + else + ecore_main_loop_thread_safe_call_async(evas_video_sink_main_render, send); eina_condition_wait(&priv->c); eina_lock_release(&priv->m); @@ -334,224 +425,265 @@ evas_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer) } static void -evas_video_sink_main_render(void *data) +_update_emotion_fps(Emotion_Gstreamer_Video *ev) +{ + double tim; + + if (!debug_fps) return ; + + tim = ecore_time_get(); + ev->frames++; + + if (ev->rlapse == 0.0) + { + ev->rlapse = tim; + ev->flapse = ev->frames; + } + else if ((tim - ev->rlapse) >= 0.5) + { + printf("FRAME: %i, FPS: %3.1f\n", + ev->frames, + (ev->frames - ev->flapse) / (tim - ev->rlapse)); + ev->rlapse = tim; + ev->flapse = ev->frames; + } +} + +static void +evas_video_sink_samsung_main_render(void *data) { Emotion_Gstreamer_Buffer *send; - Emotion_Gstreamer_Video *ev = NULL; Emotion_Video_Stream *vstream; EvasVideoSinkPrivate* priv; GstBuffer* buffer; unsigned char *evas_data; const guint8 *gst_data; GstFormat fmt = GST_FORMAT_TIME; - Evas_Coord w, h; gint64 pos; Eina_Bool preroll; + int stride, elevation; + Evas_Coord w, h; send = data; - priv = send->sink; - if (!priv) goto exit_point; - if (!priv->o) goto exit_point; + if (!send) goto exit_point; + priv = send->sink; buffer = send->frame; preroll = send->preroll; - if (priv->unlocked) goto exit_point; + if (!priv || !priv->o || priv->unlocked) + goto exit_point; + + if (send->ev->send) + { + emotion_gstreamer_buffer_free(send->ev->send); + send->ev->send = NULL; + } + + if (!send->ev->stream && !send->force) + { + send->ev->send = send; + _emotion_frame_new(send->ev->obj); + goto exit_stream; + } + + _emotion_gstreamer_video_pipeline_parse(send->ev, EINA_TRUE); + + /* Getting stride to compute the right size and then fill the object properly */ + /* Y => [0] and UV in [1] */ + if (priv->func == _evas_video_st12_multiplane) + { + const GstMultiPlaneImageBuffer *mp_buf = (const GstMultiPlaneImageBuffer *) buffer; + + stride = mp_buf->stride[0]; + elevation = mp_buf->elevation[0]; + priv->width = mp_buf->width[0]; + priv->height = mp_buf->height[0]; - gst_data = GST_BUFFER_DATA(buffer); - if (!gst_data) goto exit_point; + gst_data = (const guint8 *) mp_buf; + } + else + { + const SCMN_IMGB *imgb = (const SCMN_IMGB *) GST_BUFFER_MALLOCDATA(buffer); + + stride = imgb->stride[0]; + elevation = imgb->elevation[0]; + priv->width = imgb->width[0]; + priv->height = imgb->height[0]; + + gst_data = (const guint8 *) imgb; + } + + evas_object_geometry_get(priv->o, NULL, NULL, &w, &h); + + send->ev->fill.width = (double) stride / priv->width; + send->ev->fill.height = (double) elevation / priv->height; + + evas_object_image_alpha_set(priv->o, 0); + evas_object_image_colorspace_set(priv->o, priv->eformat); + evas_object_image_size_set(priv->o, stride, elevation); + + _update_emotion_fps(send->ev); + + evas_data = evas_object_image_data_get(priv->o, 1); + + if (priv->func) + priv->func(evas_data, gst_data, stride, elevation, elevation); + else + WRN("No way to decode %x colorspace !", priv->eformat); + + evas_object_image_data_set(priv->o, evas_data); + evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height); + evas_object_image_pixels_dirty_set(priv->o, 0); + + if (!preroll && send->ev->play_started) + { + _emotion_playback_started(send->ev->obj); + send->ev->play_started = 0; + } + + if (!send->force) + { + _emotion_frame_new(send->ev->obj); + } + + vstream = eina_list_nth(send->ev->video_streams, send->ev->video_stream_nbr - 1); + + gst_element_query_position(send->ev->pipeline, &fmt, &pos); + send->ev->position = (double)pos / (double)GST_SECOND; + + if (vstream) + { + vstream->width = priv->width; + vstream->height = priv->height; + + _emotion_video_pos_update(send->ev->obj, send->ev->position, vstream->length_time); + } + + send->ev->ratio = (double) priv->width / (double) priv->height; + _emotion_frame_refill(send->ev->obj, send->ev->fill.width, send->ev->fill.height); + _emotion_frame_resize(send->ev->obj, priv->width, priv->height, send->ev->ratio); + + buffer = gst_buffer_ref(buffer); + if (send->ev->last_buffer) gst_buffer_unref(send->ev->last_buffer); + send->ev->last_buffer = buffer; + + exit_point: + emotion_gstreamer_buffer_free(send); + + exit_stream: + if (preroll || !priv->o) return ; + + if (!priv->unlocked) + eina_condition_signal(&priv->c); +} + +static void +evas_video_sink_main_render(void *data) +{ + Emotion_Gstreamer_Buffer *send; + Emotion_Gstreamer_Video *ev = NULL; + Emotion_Video_Stream *vstream; + EvasVideoSinkPrivate* priv; + GstBuffer* buffer; + unsigned char *evas_data; + GstFormat fmt = GST_FORMAT_TIME; + gint64 pos; + Eina_Bool preroll; + + send = data; + if (!send) goto exit_point; + + priv = send->sink; + buffer = send->frame; + preroll = send->preroll; ev = send->ev; - if (!ev) goto exit_point; + + if (!priv || !priv->o || priv->unlocked) + goto exit_point; + + if (ev->send && send != ev->send) + { + emotion_gstreamer_buffer_free(ev->send); + ev->send = NULL; + } + + if (!ev->stream && !send->force) + { + ev->send = send; + _emotion_frame_new(ev->obj); + evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height); + goto exit_stream; + } _emotion_gstreamer_video_pipeline_parse(ev, EINA_TRUE); - // This prevent a race condition when data are still in the pipe - // but the buffer size as changed because of a request from - // emotion smart (like on a file set). - evas_object_image_size_get(priv->o, &w, &h); - if (w != priv->width || h != priv->height) - goto exit_point; + INF("sink main render [%i, %i] (source height: %i)", priv->width, priv->height, priv->source_height); - evas_object_image_size_set(priv->o, priv->width, priv->height); evas_object_image_alpha_set(priv->o, 0); evas_object_image_colorspace_set(priv->o, priv->eformat); + evas_object_image_size_set(priv->o, priv->width, priv->height); - evas_data = (unsigned char *)evas_object_image_data_get(priv->o, 1); - - // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't. - // Here we convert to Evas's BGRA. - switch (priv->gformat) - { - case GST_VIDEO_FORMAT_BGR: - { - unsigned char *evas_tmp; - int x; - int y; - - evas_tmp = evas_data; - /* FIXME: could this be optimized ? */ - for (x = 0; x < priv->height; x++) { - for (y = 0; y < priv->width; y++) { - evas_tmp[0] = gst_data[0]; - evas_tmp[1] = gst_data[1]; - evas_tmp[2] = gst_data[2]; - evas_tmp[3] = 255; - gst_data += 3; - evas_tmp += 4; - } - } - break; - } - - // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't. - // Here we convert to Evas's BGRA. - case GST_VIDEO_FORMAT_BGRx: - { - unsigned char *evas_tmp; - int x; - int y; - - evas_tmp = evas_data; - /* FIXME: could this be optimized ? */ - for (x = 0; x < priv->height; x++) { - for (y = 0; y < priv->width; y++) { - evas_tmp[0] = gst_data[0]; - evas_tmp[1] = gst_data[1]; - evas_tmp[2] = gst_data[2]; - evas_tmp[3] = 255; - gst_data += 4; - evas_tmp += 4; - } - } - break; - } - - // Evas's BGRA has pre-multiplied alpha while GStreamer's doesn't. - // Here we convert to Evas's BGRA. - case GST_VIDEO_FORMAT_BGRA: - { - unsigned char *evas_tmp; - int x; - int y; - unsigned char alpha; - - evas_tmp = evas_data; - /* FIXME: could this be optimized ? */ - for (x = 0; x < priv->height; x++) { - for (y = 0; y < priv->width; y++) { - alpha = gst_data[3]; - evas_tmp[0] = (gst_data[0] * alpha) / 255; - evas_tmp[1] = (gst_data[1] * alpha) / 255; - evas_tmp[2] = (gst_data[2] * alpha) / 255; - evas_tmp[3] = alpha; - gst_data += 4; - evas_tmp += 4; - } - } - break; - } - - case GST_VIDEO_FORMAT_I420: - { - int i; - const unsigned char **rows; - - evas_object_image_pixels_dirty_set(priv->o, 1); - rows = (const unsigned char **)evas_data; - - for (i = 0; i < priv->height; i++) - rows[i] = &gst_data[i * priv->width]; - - rows += priv->height; - for (i = 0; i < (priv->height / 2); i++) - rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)]; - - rows += priv->height / 2; - for (i = 0; i < (priv->height / 2); i++) - rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)]; - break; - } - - case GST_VIDEO_FORMAT_YV12: - { - int i; - const unsigned char **rows; - - evas_object_image_pixels_dirty_set(priv->o, 1); - - rows = (const unsigned char **)evas_data; - - for (i = 0; i < priv->height; i++) - rows[i] = &gst_data[i * priv->width]; - - rows += priv->height; - for (i = 0; i < (priv->height / 2); i++) - rows[i] = &gst_data[priv->height * priv->width + priv->height * (priv->width /4) + i * (priv->width / 2)]; - - rows += priv->height / 2; - for (i = 0; i < (priv->height / 2); i++) - rows[i] = &gst_data[priv->height * priv->width + i * (priv->width / 2)]; - break; - } - - case GST_VIDEO_FORMAT_YUY2: - { - int i; - const unsigned char **rows; - - evas_object_image_pixels_dirty_set(priv->o, 1); - - rows = (const unsigned char **)evas_data; - - for (i = 0; i < priv->height; i++) - rows[i] = &gst_data[i * priv->width * 2]; - break; - } - } + evas_data = evas_object_image_data_get(priv->o, 1); + + if (priv->func) + priv->func(evas_data, GST_BUFFER_DATA(buffer), priv->width, priv->source_height, priv->height); + else + WRN("No way to decode %x colorspace !", priv->eformat); - evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height); evas_object_image_data_set(priv->o, evas_data); + evas_object_image_data_update_add(priv->o, 0, 0, priv->width, priv->height); evas_object_image_pixels_dirty_set(priv->o, 0); - _emotion_frame_new(ev->obj); + if (!preroll && ev->play_started) + { + _emotion_playback_started(ev->obj); + ev->play_started = 0; + } - vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1); + if (!send->force) + { + _emotion_frame_new(ev->obj); + } gst_element_query_position(ev->pipeline, &fmt, &pos); ev->position = (double)pos / (double)GST_SECOND; - vstream->width = priv->width; - vstream->height = priv->height; + vstream = eina_list_nth(ev->video_streams, ev->video_stream_nbr - 1); + + if (vstream) + { + vstream->width = priv->width; + vstream->height = priv->height; + _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time); + } + ev->ratio = (double) priv->width / (double) priv->height; - _emotion_video_pos_update(ev->obj, ev->position, vstream->length_time); _emotion_frame_resize(ev->obj, priv->width, priv->height, ev->ratio); - if (priv->last_buffer) gst_buffer_unref(priv->last_buffer); - priv->last_buffer = gst_buffer_ref(buffer); + buffer = gst_buffer_ref(buffer); + if (ev->last_buffer) gst_buffer_unref(ev->last_buffer); + ev->last_buffer = buffer; exit_point: emotion_gstreamer_buffer_free(send); - if (preroll || !priv->o || !ev) return ; + exit_stream: + if (preroll || !priv->o) return ; - eina_lock_take(&priv->m); if (!priv->unlocked) eina_condition_signal(&priv->c); - - eina_lock_release(&priv->m); } static void unlock_buffer_mutex(EvasVideoSinkPrivate* priv) { - eina_lock_take(&priv->m); priv->unlocked = EINA_TRUE; eina_condition_signal(&priv->c); - eina_lock_release(&priv->m); } static void @@ -646,10 +778,17 @@ static void _emotion_gstreamer_pause(void *data, Ecore_Thread *thread) { Emotion_Gstreamer_Video *ev = data; + gboolean res; if (ecore_thread_check(thread) || !ev->pipeline) return ; gst_element_set_state(ev->pipeline, GST_STATE_PAUSED); + res = gst_element_get_state(ev->pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + if (res == GST_STATE_CHANGE_NO_PREROLL) + { + gst_element_set_state(ev->pipeline, GST_STATE_PLAYING); + gst_element_get_state(ev->pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + } } static void @@ -659,7 +798,9 @@ _emotion_gstreamer_cancel(void *data, Ecore_Thread *thread) ev->threads = eina_list_remove(ev->threads, thread); - if (ev->in == ev->out && ev->threads == NULL && ev->delete_me) + 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")); + + if (ev->in == ev->out && ev->delete_me) em_shutdown(ev); } @@ -676,20 +817,131 @@ _emotion_gstreamer_end(void *data, Ecore_Thread *thread) ev->play_started = 1; } - if (ev->in == ev->out && ev->threads == NULL && ev->delete_me) + 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")); + + if (ev->in == ev->out && ev->delete_me) em_shutdown(ev); else _emotion_gstreamer_video_pipeline_parse(data, EINA_TRUE); } +static void +_video_resize(void *data, Evas_Object *obj __UNUSED__, const Evas_Video_Surface *surface __UNUSED__, + Evas_Coord w, Evas_Coord h) +{ + Emotion_Gstreamer_Video *ev = data; + +#ifdef HAVE_ECORE_X + ecore_x_window_resize(ev->win, w, h); +#endif + fprintf(stderr, "resize: %i, %i\n", w, h); +} + +static void +_video_move(void *data, Evas_Object *obj __UNUSED__, const Evas_Video_Surface *surface __UNUSED__, + Evas_Coord x, Evas_Coord y) +{ + Emotion_Gstreamer_Video *ev = data; +#ifdef HAVE_ECORE_X + unsigned int pos[2]; + + fprintf(stderr, "move: %i, %i\n", x, y); + pos[0] = x; pos[1] = y; + ecore_x_window_prop_card32_set(ev->win, ECORE_X_ATOM_E_VIDEO_POSITION, pos, 2); +#endif +} + +#if 0 +/* Much better idea to always feed the XvImageSink and let him handle optimizing the rendering as we do */ +static void +_block_pad_unlink_cb(GstPad *pad, gboolean blocked, gpointer user_data) +{ + if (blocked) + { + Emotion_Gstreamer_Video *ev = user_data; + GstEvent *gev; + + gst_pad_unlink(ev->teepad, ev->xvpad); + gev = gst_event_new_eos(); + gst_pad_send_event(ev->xvpad, gev); + gst_pad_set_blocked_async(pad, FALSE, _block_pad_unlink_cb, NULL); + } +} + +static void +_block_pad_link_cb(GstPad *pad, gboolean blocked, gpointer user_data) +{ + if (blocked) + { + Emotion_Gstreamer_Video *ev = user_data; + + gst_pad_link(ev->teepad, ev->xvpad); + if (ev->play) + gst_element_set_state(ev->xvsink, GST_STATE_PLAYING); + else + gst_element_set_state(ev->xvsink, GST_STATE_PAUSED); + gst_pad_set_blocked_async(pad, FALSE, _block_pad_link_cb, NULL); + } +} +#endif + +static void +_video_show(void *data, Evas_Object *obj __UNUSED__, const Evas_Video_Surface *surface __UNUSED__) +{ + Emotion_Gstreamer_Video *ev = data; + + fprintf(stderr, "show xv\n"); +#ifdef HAVE_ECORE_X + ecore_x_window_show(ev->win); +#endif + /* gst_pad_set_blocked_async(ev->teepad, TRUE, _block_pad_link_cb, ev); */ +} + +static void +_video_hide(void *data, Evas_Object *obj __UNUSED__, const Evas_Video_Surface *surface __UNUSED__) +{ + Emotion_Gstreamer_Video *ev = data; + + fprintf(stderr, "hide xv\n"); +#ifdef HAVE_ECORE_X + ecore_x_window_hide(ev->win); +#endif + /* gst_pad_set_blocked_async(ev->teepad, TRUE, _block_pad_unlink_cb, ev); */ +} + +static void +_video_update_pixels(void *data, Evas_Object *obj __UNUSED__, const Evas_Video_Surface *surface __UNUSED__) +{ + Emotion_Gstreamer_Video *ev = data; + Emotion_Gstreamer_Buffer *send; + + if (!ev->send) return ; + + send = ev->send; + send->force = EINA_TRUE; + ev->send = NULL; + evas_video_sink_main_render(send); +} + GstElement * gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev, Evas_Object *o, const char *uri) { GstElement *playbin; - GstElement *sink; + GstElement *bin = NULL; + GstElement *esink = NULL; + GstElement *xvsink = NULL; + GstElement *tee = NULL; + GstElement *queue = NULL; Evas_Object *obj; + GstPad *pad; + GstPad *teepad; + int flags; +#if defined HAVE_ECORE_X && defined HAVE_XOVERLAY_H + const char *engine; + Eina_List *engines; +#endif obj = emotion_object_image_get(o); if (!obj) @@ -698,6 +950,9 @@ gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev, return NULL; } + if (!uri) + return NULL; + playbin = gst_element_factory_make("playbin2", "playbin"); if (!playbin) { @@ -705,21 +960,189 @@ gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev, return NULL; } - sink = gst_element_factory_make("emotion-sink", "sink"); - if (!sink) + bin = gst_bin_new(NULL); + if (!bin) + { + ERR("Unable to create GstBin !"); + goto unref_pipeline; + } + + tee = gst_element_factory_make("tee", NULL); + if (!tee) + { + ERR("Unable to create 'tee' GstElement."); + goto unref_pipeline; + } + +#if defined HAVE_ECORE_X && defined HAVE_XOVERLAY_H + if (window_manager_video) + { + engines = evas_render_method_list(); + + engine = eina_list_nth(engines, evas_output_method_get(evas_object_evas_get(obj)) - 1); + + if (ev->priority && engine && strstr(engine, "_x11") != NULL) + { + Ecore_Evas *ee; + Evas_Coord x, y, w, h; + Ecore_X_Window win; + Ecore_X_Window parent; + + evas_object_geometry_get(obj, &x, &y, &w, &h); + + ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); + + if (w < 4) w = 4; + if (h < 2) h = 2; + + /* Here we really need to have the help of the window manager, this code will change when we update E17. */ + parent = (Ecore_X_Window) ecore_evas_window_get(ee); + fprintf(stderr, "parent: %x\n", parent); + + win = ecore_x_window_new(0, x, y, w, h); + fprintf(stderr, "creating window: %x [%i, %i, %i, %i]\n", win, x, y, w, h); + if (win) + { + Ecore_X_Window_State state[] = { ECORE_X_WINDOW_STATE_SKIP_TASKBAR, ECORE_X_WINDOW_STATE_SKIP_PAGER }; + + ecore_x_netwm_window_state_set(win, state, 2); + ecore_x_window_hide(win); + xvsink = gst_element_factory_make("xvimagesink", NULL); + if (xvsink) + { + unsigned int pos[2]; + +#ifdef HAVE_X_OVERLAY_SET + gst_x_overlay_set_window_handle(GST_X_OVERLAY(xvsink), win); +#else + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(xvsink), win); +#endif + ev->win = win; + + ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_E_VIDEO_PARENT, &parent, 1); + + pos[0] = x; pos[1] = y; + ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_E_VIDEO_POSITION, pos, 2); + } + else + { + fprintf(stderr, "destroying win: %x\n", win); + ecore_x_window_free(win); + } + } + } + evas_render_method_list_free(engines); + } +#else +# warning "no ecore_x or xoverlay" +#endif + + esink = gst_element_factory_make("emotion-sink", "sink"); + if (!esink) { ERR("Unable to create 'emotion-sink' GstElement."); goto unref_pipeline; } - g_object_set(G_OBJECT(sink), "evas-object", obj, NULL); - g_object_set(G_OBJECT(sink), "ev", ev, NULL); + g_object_set(G_OBJECT(esink), "evas-object", obj, NULL); + g_object_set(G_OBJECT(esink), "ev", ev, NULL); + + evas_object_image_pixels_get_callback_set(obj, NULL, NULL); + + /* We need queue to force each video sink to be in its own thread */ + queue = gst_element_factory_make("queue", NULL); + if (!queue) + { + ERR("Unable to create 'queue' GstElement."); + goto unref_pipeline; + } + + gst_bin_add_many(GST_BIN(bin), tee, queue, esink, NULL); + gst_element_link_many(queue, esink, NULL); + + /* link both sink to GstTee */ + pad = gst_element_get_pad(queue, "sink"); + teepad = gst_element_get_request_pad(tee, "src%d"); + gst_pad_link(teepad, pad); + gst_object_unref(pad); + gst_object_unref(teepad); + + if (xvsink) + { + GstElement *fakeeos; + + queue = gst_element_factory_make("queue", NULL); + fakeeos = GST_ELEMENT(GST_BIN(g_object_new(GST_TYPE_FAKEEOS_BIN, "name", "eosbin", NULL))); + if (queue && fakeeos) + { + GstPad *queue_pad; + + gst_bin_add_many(GST_BIN(bin), fakeeos, NULL); + + gst_bin_add_many(GST_BIN(fakeeos), queue, xvsink, NULL); + gst_element_link_many(queue, xvsink, NULL); + queue_pad = gst_element_get_pad(queue, "sink"); + gst_element_add_pad(fakeeos, gst_ghost_pad_new("sink", queue_pad)); + + pad = gst_element_get_pad(fakeeos, "sink"); + teepad = gst_element_get_request_pad(tee, "src%d"); + gst_pad_link(teepad, pad); + + xvsink = fakeeos; + + ev->teepad = teepad; + ev->xvpad = pad; + } + else + { + if (fakeeos) gst_object_unref(fakeeos); + if (queue) gst_object_unref(queue); + gst_object_unref(xvsink); + xvsink = NULL; + } + } + + teepad = gst_element_get_pad(tee, "sink"); + gst_element_add_pad(bin, gst_ghost_pad_new("sink", teepad)); + gst_object_unref(teepad); + +#define GST_PLAY_FLAG_NATIVE_VIDEO (1 << 6) +#define GST_PLAY_FLAG_DOWNLOAD (1 << 7) +#define GST_PLAY_FLAG_AUDIO (1 << 1) +#define GST_PLAY_FLAG_NATIVE_AUDIO (1 << 5) - g_object_set(G_OBJECT(playbin), "video-sink", sink, NULL); + g_object_get(G_OBJECT(playbin), "flags", &flags, NULL); + g_object_set(G_OBJECT(playbin), "flags", flags | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_DOWNLOAD | GST_PLAY_FLAG_NATIVE_AUDIO, NULL); + g_object_set(G_OBJECT(playbin), "video-sink", bin, NULL); g_object_set(G_OBJECT(playbin), "uri", uri, NULL); + evas_object_image_pixels_get_callback_set(obj, NULL, NULL); + + ev->stream = EINA_TRUE; + + if (xvsink) + { + Evas_Video_Surface video; + + video.version = EVAS_VIDEO_SURFACE_VERSION; + video.data = ev; + video.parent = NULL; + video.move = _video_move; + video.resize = _video_resize; + video.show = _video_show; + video.hide = _video_hide; + video.update_pixels = _video_update_pixels; + + evas_object_image_video_surface_set(obj, &video); + ev->stream = EINA_FALSE; + } + + eina_stringshare_replace(&ev->uri, uri); ev->pipeline = playbin; - ev->sink = sink; + ev->sink = bin; + ev->esink = esink; + ev->xvsink = xvsink; + ev->tee = tee; ev->threads = eina_list_append(ev->threads, ecore_thread_run(_emotion_gstreamer_pause, _emotion_gstreamer_end, @@ -733,6 +1156,10 @@ gstreamer_video_sink_new(Emotion_Gstreamer_Video *ev, return playbin; unref_pipeline: + gst_object_unref(xvsink); + gst_object_unref(esink); + gst_object_unref(tee); + gst_object_unref(bin); gst_object_unref(playbin); return NULL; }