From a6bd5dda420520a509fc1cee26e131a13401348f Mon Sep 17 00:00:00 2001 From: Thomas Guillem Date: Fri, 25 Mar 2016 09:05:38 +0100 Subject: [PATCH] emotion/libvlc: use vlc vout display module The vlc vout display module adds key and mouse event support. It improves performances since a video filter is not needed anymore to scale the image, and direct rendering with vlc avcodec module is now possible (less memcpy). --- src/modules/emotion/libvlc/emotion_libvlc.c | 417 ++++---------------- 1 file changed, 70 insertions(+), 347 deletions(-) diff --git a/src/modules/emotion/libvlc/emotion_libvlc.c b/src/modules/emotion/libvlc/emotion_libvlc.c index e256a56ec3..98f6fdfe67 100644 --- a/src/modules/emotion/libvlc/emotion_libvlc.c +++ b/src/modules/emotion/libvlc/emotion_libvlc.c @@ -39,8 +39,6 @@ #endif #define CRI(...) EINA_LOG_DOM_CRIT(_emotion_libvlc_log_domain, __VA_ARGS__) -#define SINK_MAX_PLANES 3 - static int _emotion_libvlc_log_domain = -1; static Eina_Bool debug_fps = EINA_FALSE; static libvlc_instance_t *libvlc = NULL; @@ -68,6 +66,7 @@ struct _Emotion_LibVLC /* options */ int video_mute; + int video_mute_force; int audio_mute; int spu_mute; int audio_vol; @@ -90,24 +89,12 @@ struct _Emotion_LibVLC Eina_Bool seeking; Eina_Bool started; Eina_Bool invalidate_tracks; +}; - /* sink, must be locked by ev->lock */ - struct - { - int width; - int height; - Evas_Colorspace colorspace; - void *data; - Eina_Bool waiting; - Eina_Bool is_yuv; - - unsigned int nb_planes; - unsigned int lines[SINK_MAX_PLANES]; - unsigned int pitches[SINK_MAX_PLANES]; - unsigned int yuv_height[SINK_MAX_PLANES]; - unsigned char *yuv_data[SINK_MAX_PLANES]; - unsigned int yuv_planes_order[SINK_MAX_PLANES]; - } sink; +struct close_data +{ + libvlc_media_player_t *mp; + Evas_Object *evas_obj; }; static const libvlc_event_type_t mp_events[] = { @@ -178,119 +165,6 @@ emotion_mainloop_call_locked(Emotion_LibVLC *ev, Ecore_Cb callback) ecore_main_loop_thread_safe_call_async(callback, ev); } -/* Take the ev->lock from a sink mainloop callback. - * Returns false if the ev object is destroyed or is there is no evas object. */ -static Eina_Bool -emotion_mainloop_sink_lock(Emotion_LibVLC *ev) -{ - if (emotion_mainloop_lock(ev)) - { - if (!ev->evas_obj) - { - eina_lock_release(&ev->lock); - return EINA_FALSE; - } - else - return EINA_TRUE; - } - else - return EINA_FALSE; -} - -/* Release the ev->lock from a sink mainloop callback and signal that the - * callback is processed. */ -static void -emotion_mainloop_sink_signal_unlock(Emotion_LibVLC *ev) -{ - ev->sink.waiting = EINA_FALSE; - eina_condition_signal(&ev->wait); - eina_lock_release(&ev->lock); -} - -/* Send a sink mainloop callback and wait. */ -static void -emotion_mainloop_sink_call_wait_locked(Emotion_LibVLC *ev, Ecore_Cb callback) -{ - ev->sink.waiting = EINA_TRUE; - - emotion_mainloop_call_locked(ev, callback); - - while (ev->evas_obj && ev->sink.waiting) - eina_condition_wait(&ev->wait); -} - -/* Sink mainloop callback, sent by libvlc_video_on_lock. */ -static void -emotion_mainloop_sink_pic_lock(void *data) -{ - Emotion_LibVLC *ev = data; - if (!emotion_mainloop_sink_lock(ev)) return; - - ev->sink.data = evas_object_image_data_get(ev->evas_obj, 1); - - emotion_mainloop_sink_signal_unlock(ev); -} - -/* Sink mainloop callback, sent by libvlc_video_on_unlock. */ -static void -emotion_mainloop_sink_pic_unlock(void *data) -{ - Emotion_LibVLC *ev = data; - - if (!emotion_mainloop_sink_lock(ev)) return; - - if (!ev->sink.data) - goto end; - - if (ev->sink.is_yuv) - { - unsigned int i, j; - const unsigned char **rows = (const unsigned char **)ev->sink.data; - - for (i = 0; i < ev->sink.nb_planes; ++i) - for (j = 0; j < ev->sink.yuv_height[i]; ++j) - *(rows++) = &ev->sink.yuv_data[i][j * ev->sink.pitches[i]]; - } - evas_object_image_data_set(ev->evas_obj, ev->sink.data); - ev->sink.data = NULL; - -end: - emotion_mainloop_sink_signal_unlock(ev); -} - -/* Sink mainloop callback, sent by libvlc_video_on_display. */ -static void -emotion_mainloop_sink_display(void *data) -{ - Emotion_LibVLC *ev = data; - - if (!emotion_mainloop_sink_lock(ev)) return; - - evas_object_image_data_update_add(ev->evas_obj, 0, 0, ev->sink.width, - ev->sink.height); - _emotion_frame_new(ev->obj); - - emotion_mainloop_sink_signal_unlock(ev); -} - -/* Sink mainloop callback, sent by libvlc_video_on_format. */ -static void -emotion_mainloop_sink_format(void *data) -{ - Emotion_LibVLC *ev = data; - - if (!emotion_mainloop_sink_lock(ev)) return; - - evas_object_image_pixels_get_callback_set(ev->evas_obj, NULL, NULL); - evas_object_image_alpha_set(ev->evas_obj, 0); - evas_object_image_colorspace_set(ev->evas_obj, ev->sink.colorspace); - evas_object_image_size_set(ev->evas_obj, ev->sink.width, ev->sink.height); - _emotion_frame_resize(ev->obj, ev->sink.width, ev->sink.height, - ev->sink.width / (double)ev->sink.height); - - emotion_mainloop_sink_signal_unlock(ev); -} - /* Process one libvlc event from the mainloop. */ static void emotion_mainloop_event(Emotion_LibVLC *ev, const libvlc_event_t *event) @@ -369,190 +243,6 @@ emotion_mainloop_event_list(void *data) } } -/* Libvlc callback, see libvlc_video_set_callbacks and libvlc_video_lock_cb. */ -static void * -libvlc_video_on_lock(void *opaque, void **pixels) -{ - Emotion_LibVLC *ev = opaque; - - eina_lock_take(&ev->lock); - if (!ev->evas_obj) goto end; - - emotion_mainloop_sink_call_wait_locked(ev, emotion_mainloop_sink_pic_lock); -end: - if (ev->sink.data) - { - if (ev->sink.is_yuv) - { - unsigned int i; - - for (i = 0; i < ev->sink.nb_planes; ++i) - pixels[i] = ev->sink.yuv_data[ev->sink.yuv_planes_order[i]]; - } - else - pixels[0] = ev->sink.data; - } - eina_lock_release(&ev->lock); - - return NULL; -} - -/* Libvlc callback, see libvlc_video_set_callbacks and libvlc_video_unlock_cb. - * */ -static void -libvlc_video_on_unlock(void *opaque, void *picture EINA_UNUSED, - void *const *pixels EINA_UNUSED) -{ - Emotion_LibVLC *ev = opaque; - - eina_lock_take(&ev->lock); - if (!ev->evas_obj) goto end; - - emotion_mainloop_sink_call_wait_locked(ev, emotion_mainloop_sink_pic_unlock); -end: - eina_lock_release(&ev->lock); -} - -/* Libvlc callback, see libvlc_video_set_callbacks and libvlc_video_display_cb. - * */ -static void -libvlc_video_on_display(void *opaque, void *picture EINA_UNUSED) -{ - Emotion_LibVLC *ev = opaque; - - eina_lock_take(&ev->lock); - if (!ev->evas_obj) goto end; - - emotion_mainloop_sink_call_wait_locked(ev, emotion_mainloop_sink_display); -end: - eina_lock_release(&ev->lock); -} - -#define ALIGN32(x) (((x) + 31) & ~(31)) - -/* Libvlc callback, see libvlc_video_set_format_callbacks and - * libvlc_video_format_cb. */ -static unsigned int -libvlc_video_on_format(void **opaque, char *chroma, - unsigned int *width, unsigned int *height, - unsigned int *pitches, - unsigned int *lines) -{ - Emotion_LibVLC *ev = *opaque; - - eina_lock_take(&ev->lock); - if (!ev->evas_obj) goto end; - - INF("request video format: %s, size: %dx%d", chroma, *width, *height); - - ev->sink.width = *width; - ev->sink.height = *height; - - if (!strcmp(chroma, "RV32")) - { - ev->sink.colorspace = EVAS_COLORSPACE_ARGB8888; - ev->sink.nb_planes = 1; - ev->sink.lines[0] = ev->sink.height; - ev->sink.pitches[0] = ev->sink.width * 4; - } - /* Not implemented yet */ -#if 0 - else if (!strcmp(chroma, "RV16")) - { - ev->sink.colorspace = EVAS_COLORSPACE_RGB565_A5P; - ev->sink.nb_planes = 1; - ev->sink.lines[0] = ev->sink.height; - ev->sink.pitches[0] = ev->sink.width * 2; - } -#endif - else - { - /* YUV */ - - unsigned int i; - - /* default planes order */ - for (i = 0; i < ev->sink.nb_planes; ++i) - ev->sink.yuv_planes_order[i] = i; - - if (!strcmp(chroma, "YUY2")) - { - ev->sink.colorspace = EVAS_COLORSPACE_YCBCR422601_PL; - ev->sink.nb_planes = 1; - ev->sink.yuv_height[0] = ev->sink.height; - ev->sink.pitches[0] = ev->sink.width * 2; - } - /* FIXME: SIGSEGV in evas_gl_common_texture_nv12_update */ -#if 0 - else if (!strcmp(chroma, "NV12")) - { - ev->sink.colorspace = EVAS_COLORSPACE_YCBCR420NV12601_PL; - ev->sink.nb_planes = 2; - ev->sink.yuv_height[0] = ev->sink.height; - ev->sink.pitches[0] = ev->sink.width; - ev->sink.yuv_height[1] = ev->sink.height / 2; - ev->sink.pitches[1] = ev->sink.width; - } -#endif - else - { - /* YV12 or I420 */ - if (strcmp(chroma, "YV12") && strcmp(chroma, "I420")) - { - strcpy(chroma, "I420"); - INF("native format not available, using: %s", chroma); - } - ev->sink.colorspace = EVAS_COLORSPACE_YCBCR422P601_PL; - ev->sink.nb_planes = 3; - ev->sink.yuv_height[0] = ev->sink.height; - ev->sink.pitches[0] = ev->sink.width; - ev->sink.yuv_height[1] = ev->sink.yuv_height[2] = ev->sink.height / 2; - ev->sink.pitches[1] = ev->sink.pitches[2] = ev->sink.width / 2; - - if (!strcmp(chroma, "YV12")) - { - /* Cb and Cr inverted for YV12 */ - ev->sink.yuv_planes_order[0] = 0; - ev->sink.yuv_planes_order[1] = 2; - ev->sink.yuv_planes_order[2] = 1; - } - } - - assert(ev->sink.nb_planes <= SINK_MAX_PLANES); - - /* Align pitches/lines and alloc planes */ - for (i = 0; i < ev->sink.nb_planes; ++i) - { - ev->sink.lines[i] = ALIGN32(ev->sink.yuv_height[i]); - ev->sink.pitches[i] = ALIGN32(ev->sink.pitches[i]); - ev->sink.yuv_data[i] = malloc(ev->sink.lines[i] - * ev->sink.pitches[i]); - if (!ev->sink.yuv_data[i]) - { - for (i = 0; i < ev->sink.nb_planes; ++i) - { - free(ev->sink.yuv_data[i]); - ev->sink.lines[i] = 0; - ev->sink.pitches[i] = 0; - } - ev->sink.nb_planes = 0; - goto end; - } - } - ev->sink.is_yuv = EINA_TRUE; - } - - assert(ev->sink.nb_planes > 0); - - memcpy(lines, ev->sink.lines, ev->sink.nb_planes * sizeof(unsigned int)); - memcpy(pitches, ev->sink.pitches, ev->sink.nb_planes * sizeof(unsigned int)); - - emotion_mainloop_sink_call_wait_locked(ev, emotion_mainloop_sink_format); -end: - eina_lock_release(&ev->lock); - return ev->sink.nb_planes; -} - /* Libvlc callback, see libvlc_event_manager_t. */ static void libvlc_on_mp_event(const libvlc_event_t *event, void *opaque) @@ -581,6 +271,18 @@ libvlc_on_mp_event(const libvlc_event_t *event, void *opaque) } } +static void +evas_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, + void *event EINA_UNUSED) +{ + Emotion_LibVLC *ev = data; + int w, h; + + evas_object_image_size_get(ev->evas_obj, &w, &h); + _emotion_frame_resize(ev->obj, w, h, w / (double) h); + eo_event_callback_call(ev->obj, EMOTION_OBJECT_EVENT_FRAME_DECODE, NULL); +} + /* Returns true if libvlc mediaplayer is ready to process commands. */ static Eina_Bool libvlc_mp_is_ready(Emotion_LibVLC *ev) @@ -746,6 +448,8 @@ em_file_open(void *video, ev->opt.no_video = EINA_TRUE; } + evas_object_image_pixels_get_callback_set(ev->evas_obj, NULL, NULL); + ev->invalidate_tracks = true; /* Create libvlc_media */ @@ -772,11 +476,16 @@ em_file_open(void *video, libvlc_media_player_set_video_title_display(ev->mp, libvlc_position_disable, 0); - /* Set sink callbacks */ - libvlc_video_set_format_callbacks(ev->mp, libvlc_video_on_format, NULL); - libvlc_video_set_callbacks(ev->mp, libvlc_video_on_lock, - libvlc_video_on_unlock, - libvlc_video_on_display, ev); + evas_object_ref(ev->evas_obj); + if (libvlc_media_player_set_evas_object(ev->mp, ev->evas_obj) == -1) + { + CRI("libvlc_media_player_set_evas_object failed"); + libvlc_media_add_option(ev->m, ":no-video"); + ev->video_mute = ev->video_mute_force = 1; + } + + evas_object_event_callback_add(ev->evas_obj, EVAS_CALLBACK_IMAGE_RESIZE, + evas_resize_cb, ev); if (ev->audio_vol != -1) libvlc_audio_set_volume(ev->mp, ev->audio_vol); @@ -790,6 +499,25 @@ error: return EINA_FALSE; } +static void +emotion_close_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + struct close_data *close_data = data; + + libvlc_media_player_release(close_data->mp); +} + +static void +emotion_close_mainloop_cb(void *data, + Ecore_Thread *thread EINA_UNUSED) +{ + struct close_data *close_data = data; + + evas_object_unref(close_data->evas_obj); + free(close_data); + _emotion_pending_ecore_end(); +} + static void em_file_close(void *video) { @@ -798,19 +526,30 @@ em_file_close(void *video) if (ev->mp) { + struct close_data *close_data; libvlc_event_manager_t *event_m; + evas_object_event_callback_del(ev->evas_obj, EVAS_CALLBACK_IMAGE_RESIZE, + evas_resize_cb); + event_m = libvlc_media_player_event_manager(ev->mp); for (i = 0; mp_events[i] != -1; ++i) libvlc_event_detach(event_m, mp_events[i], libvlc_on_mp_event, ev); - /* Abort libvlc callbacks */ - eina_lock_take(&ev->lock); - ev->evas_obj = NULL; - eina_condition_signal(&ev->wait); - eina_lock_release(&ev->lock); + libvlc_media_player_set_evas_object(ev->mp, NULL); - libvlc_media_player_release(ev->mp); + close_data = malloc(sizeof(struct close_data)); + if (close_data) + { + close_data->evas_obj = ev->evas_obj; + close_data->mp = ev->mp; + _emotion_pending_ecore_begin(); + ecore_thread_run(emotion_close_cb, + emotion_close_mainloop_cb, + NULL, close_data); + } + + ev->evas_obj = NULL; ev->mp = NULL; if (ev->seeking) @@ -818,23 +557,6 @@ em_file_close(void *video) ev->seeking = EINA_FALSE; _emotion_seek_done(ev->obj); } - if (ev->sink.data) - { - /* unlock already locked buffer */ - evas_object_image_data_set(ev->evas_obj, ev->sink.data); - ev->sink.data = NULL; - } - - /* free image data */ - evas_object_image_size_set(ev->evas_obj, 1, 1); - evas_object_image_data_set(ev->evas_obj, NULL); - - /* free yuv data */ - if (ev->sink.is_yuv) - { - for (i = 0; i < ev->sink.nb_planes; ++i) - free(ev->sink.yuv_data[i]); - } } if (ev->m) { @@ -866,7 +588,6 @@ em_file_close(void *video) ev->vis = EMOTION_VIS_NONE; ev->started = ev->seeking = ev->invalidate_tracks = EINA_FALSE; ev->pos = ev->len = ev->buffer_cache = 0.0; - memset(&ev->sink, 0, sizeof(ev->sink)); } static void @@ -1171,13 +892,13 @@ em_event_feed(void *video, int event) static void em_event_mouse_button_feed(void *video EINA_UNUSED, int button EINA_UNUSED, int x EINA_UNUSED, int y EINA_UNUSED) { - /* FIXME */ + /* Handled directly by VLC evas vout module */ } static void em_event_mouse_move_feed(void *video EINA_UNUSED, int x EINA_UNUSED, int y EINA_UNUSED) { - /* FIXME */ + /* Handled directly by VLC evas vout module */ } static int @@ -1265,6 +986,8 @@ em_video_channel_mute_set(void *video, { Emotion_LibVLC *ev = video; + if (ev->video_mute_force) + return; ev->video_mute = mute; if (libvlc_mp_is_ready(ev))