cedric pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=afe6f5c3a1adbfc3f1b8ab828021accc55b29341

commit afe6f5c3a1adbfc3f1b8ab828021accc55b29341
Author: Thomas Guillem <tho...@gllm.fr>
Date:   Mon Sep 21 23:19:17 2015 +0200

    emotion: add libvlc module
    
    Summary:
    Everything is implemented except visualization, mouse events and some
    EMOTION_EVENT. Video can output RGBA, YUY2, YV12 or I420.
    
    About the video sink: this emotion module use libvlc callbacks:
    libvlc_video_set_format_callbacks and libvlc_video_set_callbacks. It may be
    temporary. Indeed, an other solution is to add an Evas module inside vlc. 
But
    this vlc module would need to link with emotion in order to use
    _emotion_frame_new and _emotion_frame_resize private functions. I didn't
    succeed to output a frame without these private functions: see
    
https://github.com/tguillem/vlc/commit/15daff4d3fdcd3ca7b485c1f8fabb8c1e1c4138c
    
    List of /* FIXME */:
    
     - Visualization not implemented since there is no API (for now) in libvlc.
    
     - Mouse events not implemented since there is no API (for now) in libvlc.
    
     - Some EMOTION_EVENT are not handled.
    
     - SIGSEGV in evas_gl_common_texture_nv12_update with
       EVAS_COLORSPACE_YCBCR420NV12601_PL colorspace.
    
    Subscribers: cedric
    
    Differential Revision: https://phab.enlightenment.org/D3071
    
    Signed-off-by: Cedric BAIL <ced...@osg.samsung.com>
---
 configure.ac                                |   13 +
 m4/emotion_module.m4                        |   14 +
 src/Makefile_Emotion.am                     |   29 +
 src/lib/emotion/emotion_modules.c           |   14 +
 src/modules/emotion/libvlc/emotion_libvlc.c | 1765 +++++++++++++++++++++++++++
 5 files changed, 1835 insertions(+)

diff --git a/configure.ac b/configure.ac
index 092a86d..ab6a061 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4332,6 +4332,17 @@ AC_ARG_ENABLE([v4l2],
    ],
    [want_v4l2="${efl_lib_optional_eeze}"])
 
+AC_ARG_ENABLE([libvlc],
+   [AS_HELP_STRING([--enable-libvlc],[enable libvlc support. 
@<:@default=disabled@:>@])],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       want_libvlc="yes"
+    else
+       want_libvlc="no"
+    fi
+   ],
+   [want_libvlc="no"])
+
 ### Checks for programs
 
 ### Checks for libraries
@@ -4354,11 +4365,13 @@ have_gst_xoverlay="no"
 EMOTION_MODULE([xine], [${want_xine}])
 EMOTION_MODULE([gstreamer], [${want_gstreamer}])
 EMOTION_MODULE([gstreamer1], [${want_gstreamer1}])
+EMOTION_MODULE([libvlc], [${want_libvlc}])
 EMOTION_MODULE([generic], [${want_emotion_generic}])
 
 EFL_ADD_FEATURE([EMOTION], [xine])
 EFL_ADD_FEATURE([EMOTION], [gstreamer])
 EFL_ADD_FEATURE([EMOTION], [gstreamer1])
+EFL_ADD_FEATURE([EMOTION], [libvlc])
 EFL_ADD_FEATURE([EMOTION], [generic], [${want_emotion_generic}])
 
 EFL_EVAL_PKGS([EMOTION])
diff --git a/m4/emotion_module.m4 b/m4/emotion_module.m4
index 75884e7..51ed0da 100644
--- a/m4/emotion_module.m4
+++ b/m4/emotion_module.m4
@@ -58,6 +58,20 @@ AC_DEFUN([EMOTION_MODULE_DEP_CHECK_GSTREAMER1],
    fi
 ])
 
+
+dnl use: EMOTION_MODULE_DEP_CHECK_LIBVLC(want_static)
+dnl where want_engine = yes or static
+AC_DEFUN([EMOTION_MODULE_DEP_CHECK_LIBVLC],
+[dnl
+   LIBVLC_VER=3.0
+   requirements="libvlc >= ${LIBVLC_VER}"
+   if test "$1" = "static"; then
+      EFL_DEPEND_PKG([EMOTION], [EMOTION_MODULE_LIBVLC], [${requirements}])
+   else
+      PKG_CHECK_MODULES([EMOTION_MODULE_LIBVLC], [${requirements}])
+   fi
+])
+
 dnl use: EMOTION_MODULE_DEP_CHECK_GENERIC(want_static)
 dnl where want_engine = yes or static
 AC_DEFUN([EMOTION_MODULE_DEP_CHECK_GENERIC],
diff --git a/src/Makefile_Emotion.am b/src/Makefile_Emotion.am
index 6b6efca..f16a4a4 100644
--- a/src/Makefile_Emotion.am
+++ b/src/Makefile_Emotion.am
@@ -157,6 +157,35 @@ modules_emotion_gstreamer1_module_la_LIBTOOLFLAGS = 
--tag=disable-static
 endif
 endif
 
+# LibVLC
+EMOTION_LIBVLC_SOURCES =\
+modules/emotion/libvlc/emotion_libvlc.c
+
+if EMOTION_STATIC_BUILD_LIBVLC
+lib_emotion_libemotion_la_SOURCES += $(EMOTION_LIBVLC_SOURCES)
+else
+if EMOTION_BUILD_LIBVLC
+emotionmodulelibvlcdir = $(libdir)/emotion/modules/libvlc/$(MODULE_ARCH)
+emotionmodulelibvlc_LTLIBRARIES = modules/emotion/libvlc/module.la
+
+# Workaround for broken parallel install support in automake (relink issue)
+# http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328
+install_emotionmodulelibvlcLTLIBRARIES = install-emotionmodulelibvlcLTLIBRARIES
+$(install_emotionmodulelibvlcLTLIBRARIES): install-libLTLIBRARIES
+
+modules_emotion_libvlc_module_la_SOURCES = $(EMOTION_LIBVLC_SOURCES)
+modules_emotion_libvlc_module_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
+@EMOTION_CFLAGS@ \
+@EMOTION_MODULE_LIBVLC_CFLAGS@
+modules_emotion_libvlc_module_la_LIBADD = \
+@USE_EMOTION_LIBS@ \
+@EMOTION_MODULE_LIBVLC_LIBS@
+modules_emotion_libvlc_module_la_DEPENDENCIES = @USE_EMOTION_INTERNAL_LIBS@
+modules_emotion_libvlc_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@
+modules_emotion_libvlc_module_la_LIBTOOLFLAGS = --tag=disable-static
+endif
+endif
+
 # Generic
 EMOTION_GENERIC_SOURCES = \
 modules/emotion/generic/emotion_generic.h \
diff --git a/src/lib/emotion/emotion_modules.c 
b/src/lib/emotion/emotion_modules.c
index 6630096..590a61f 100644
--- a/src/lib/emotion/emotion_modules.c
+++ b/src/lib/emotion/emotion_modules.c
@@ -13,6 +13,10 @@ void      xine_module_shutdown(void);
 Eina_Bool gstreamer_module_init(void);
 void      gstreamer_module_shutdown(void);
 #endif
+#ifdef EMOTION_STATIC_BUILD_LIBVLC
+Eina_Bool libvlc_module_init(void);
+void      libvlc_module_shutdown(void);
+#endif
 #ifdef EMOTION_STATIC_BUILD_GENERIC
 Eina_Bool generic_module_init(void);
 void      generic_module_shutdown(void);
@@ -79,6 +83,9 @@ _emotion_modules_load(void)
 #ifdef EMOTION_BUILD_XINE
                      "xine",
 #endif
+#ifdef EMOTION_BUILD_LIBVLC
+                     "libvlc",
+#endif
                      NULL
                   };
                   const char **itr;
@@ -118,6 +125,9 @@ emotion_modules_init(void)
 #if defined(EMOTION_STATIC_BUILD_GSTREAMER) || 
defined(EMOTION_STATIC_BUILD_GSTREAMER1)
    gstreamer_module_init();
 #endif
+#ifdef EMOTION_STATIC_BUILD_LIBVLC
+   libvlc_module_init();
+#endif
 #ifdef EMOTION_STATIC_BUILD_GENERIC
    generic_module_init();
 #endif
@@ -136,6 +146,9 @@ emotion_modules_shutdown(void)
 #if defined(EMOTION_STATIC_BUILD_GSTREAMER) || 
defined(EMOTION_STATIC_BUILD_GSTREAMER1)
    gstreamer_module_shutdown();
 #endif
+#ifdef EMOTION_STATIC_BUILD_LIBVLC
+   libvlc_module_shutdown();
+#endif
 #ifdef EMOTION_STATIC_BUILD_GENERIC
    generic_module_shutdown();
 #endif
@@ -366,6 +379,7 @@ emotion_engine_instance_new(const char *name, Evas_Object 
*obj, Emotion_Module_O
         if (!m) m = _find_mod("xine");
         if (!m) m = _find_mod("gstreamer");
         if (!m) m = _find_mod("gstreamer1");
+        if (!m) m = _find_mod("libvlc");
         if (m) eina_module_load(m);
      }
 
diff --git a/src/modules/emotion/libvlc/emotion_libvlc.c 
b/src/modules/emotion/libvlc/emotion_libvlc.c
new file mode 100644
index 0000000..2b941f0
--- /dev/null
+++ b/src/modules/emotion/libvlc/emotion_libvlc.c
@@ -0,0 +1,1765 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
+#include <Eina.h>
+#include <Evas.h>
+#include <Ecore.h>
+
+#include <vlc/vlc.h>
+#include <vlc/libvlc_media.h>
+#include <vlc/libvlc_media_player.h>
+
+#include "emotion_modules.h"
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_emotion_libvlc_log_domain, __VA_ARGS__)
+
+#ifdef INF
+#undef INF
+#endif
+#define INF(...) EINA_LOG_DOM_INFO(_emotion_libvlc_log_domain, __VA_ARGS__)
+
+#ifdef WRN
+#undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(_emotion_libvlc_log_domain, __VA_ARGS__)
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_emotion_libvlc_log_domain, __VA_ARGS__)
+
+#ifdef CRI
+#undef CRI
+#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;
+
+typedef struct _Emotion_LibVLC Emotion_LibVLC;
+
+static void em_file_close(void *);
+
+struct _Emotion_LibVLC
+{
+   /* Evas object */
+   Evas_Object                   *obj;
+   Evas_Object                   *evas_obj;
+   Emotion_Module_Options         opt;
+
+   /* libvlc */
+   libvlc_media_t                *m;
+   libvlc_media_player_t         *mp;
+   unsigned int                   nb_tracks;
+   libvlc_media_track_t         **tracks;
+   int                            nb_chapters;
+   libvlc_chapter_description_t **chapters;
+   char                          *subtitle_file;
+   char                          *metas[META_TRACK_COUNT];
+
+   /* options */
+   int                            video_mute;
+   int                            audio_mute;
+   int                            audio_vol;
+   Emotion_Vis                    vis;
+
+   /* There can be remaining mainloop callbacks that owns the Emotion_LibVLC
+    * object when em_del is called. Use a ref_count to delete the ev object
+    * when em_del is called and when all callbacks are processed. */
+   unsigned int                   ref_count;
+
+   /* locks */
+   Eina_Lock                      lock;
+   Eina_Condition                 wait;
+   Eina_List                     *event_list;
+
+   /* stats */
+   double                         pos;
+   double                         len;
+   double                         buffer_cache;
+   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;
+};
+
+static const libvlc_event_type_t mp_events[] = {
+   //libvlc_MediaPlayerMediaChanged,
+   //libvlc_MediaPlayerNothingSpecial,
+   //libvlc_MediaPlayerOpening,
+   libvlc_MediaPlayerBuffering,
+   libvlc_MediaPlayerPlaying,
+   //libvlc_MediaPlayerPaused,
+   libvlc_MediaPlayerStopped,
+   libvlc_MediaPlayerForward,
+   //libvlc_MediaPlayerBackward,
+   libvlc_MediaPlayerEndReached,
+   libvlc_MediaPlayerEncounteredError,
+   libvlc_MediaPlayerTimeChanged,
+   //libvlc_MediaPlayerPositionChanged,
+   //libvlc_MediaPlayerSeekableChanged,
+   //libvlc_MediaPlayerPausableChanged,
+   //libvlc_MediaPlayerTitleChanged,
+   //libvlc_MediaPlayerSnapshotTaken,
+   libvlc_MediaPlayerLengthChanged,
+   //libvlc_MediaPlayerVout,
+   //libvlc_MediaPlayerScrambledChanged,
+   libvlc_MediaPlayerESAdded,
+   libvlc_MediaPlayerESDeleted,
+   //libvlc_MediaPlayerESSelected,
+   //libvlc_MediaPlayerCorked,
+   //libvlc_MediaPlayerUncorked,
+   //libvlc_MediaPlayerMuted,
+   //libvlc_MediaPlayerUnmuted,
+   //libvlc_MediaPlayerAudioVolume,
+   //libvlc_MediaPlayerAudioDevice,
+   -1,
+};
+
+static void
+em_del_safe(Emotion_LibVLC *ev)
+{
+   eina_lock_free(&ev->lock);
+   eina_condition_free(&ev->wait);
+   free(ev->subtitle_file);
+   free(ev);
+}
+
+/* Take the ev->lock from a mainloop callback.
+ * Returns false if the ev object is destroyed. */
+static Eina_Bool
+emotion_mainloop_lock(Emotion_LibVLC *ev)
+{
+   eina_lock_take(&ev->lock);
+   _emotion_pending_ecore_end();
+   if (--ev->ref_count == 0)
+     {
+        eina_lock_release(&ev->lock);
+        WRN("callbacks ended, deleting Emotion_LibVLC");
+        em_del_safe(ev);
+        return EINA_FALSE;
+     }
+   return EINA_TRUE;
+}
+
+/* Send a callback to the mainloop */
+static void
+emotion_mainloop_call_locked(Emotion_LibVLC *ev, Ecore_Cb callback)
+{
+   ++ev->ref_count;
+   _emotion_pending_ecore_begin();
+   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)
+{
+   switch (event->type)
+     {
+      case libvlc_MediaPlayerBuffering:
+        ev->buffer_cache = event->u.media_player_buffering.new_cache / 100.0;
+        break;
+
+      case libvlc_MediaPlayerPlaying:
+        if (!ev->started)
+          {
+             _emotion_open_done(ev->obj);
+             _emotion_playback_started(ev->obj);
+             ev->started = EINA_TRUE;
+          }
+        break;
+
+      case libvlc_MediaPlayerStopped:
+      case libvlc_MediaPlayerEndReached:
+      case libvlc_MediaPlayerEncounteredError:
+        _emotion_decode_stop(ev->obj);
+        _emotion_playback_finished(ev->obj);
+        break;
+
+      case libvlc_MediaPlayerTimeChanged:
+      {
+         if (ev->seeking)
+           {
+              _emotion_seek_done(ev->obj);
+              ev->seeking = EINA_FALSE;
+           }
+
+         ev->pos = event->u.media_player_time_changed.new_time / 1000.0;
+         if (ev->pos > 0 && ev->len > 0)
+           _emotion_video_pos_update(ev->obj, ev->pos, ev->len);
+         break;
+      }
+
+      case libvlc_MediaPlayerLengthChanged:
+        ev->len = event->u.media_player_length_changed.new_length / 1000.0;
+        if (ev->pos > 0 && ev->len > 0)
+          _emotion_video_pos_update(ev->obj, ev->pos, ev->len);
+        break;
+
+      case libvlc_MediaPlayerESAdded:
+      case libvlc_MediaPlayerESDeleted:
+        ev->invalidate_tracks = EINA_TRUE;
+        _emotion_channels_change(ev->obj);
+        break;
+     }
+}
+
+/* Mainloop callback, sent by libvlc_on_mp_event. It processes a list of libvlc
+ * event. */
+static void
+emotion_mainloop_event_list(void *data)
+{
+   Emotion_LibVLC *ev = data;
+   Eina_List *event_list;
+   libvlc_event_t *event;
+
+   if (!emotion_mainloop_lock(ev)) return;
+   event_list = ev->event_list;
+   ev->event_list = NULL;
+   eina_lock_release(&ev->lock);
+
+   if (!event_list) return;
+
+   EINA_LIST_FREE(event_list, event)
+     {
+        if (ev->mp)
+          emotion_mainloop_event(ev, event);
+        free(event);
+     }
+}
+
+/* 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)
+{
+   Emotion_LibVLC *ev = opaque;
+
+   if (eina_main_loop_is())
+     {
+        /* Process the event directly */
+        emotion_mainloop_event(ev, event);
+     }
+   else
+     {
+        /* Add the event to a list of events that will be processed by the
+         * mainloop */
+
+        void *data = malloc(sizeof(libvlc_event_t));
+        if (!data) return;
+        memcpy(data, event, sizeof(libvlc_event_t));
+
+        eina_lock_take(&ev->lock);
+        if (!ev->event_list)
+          emotion_mainloop_call_locked(ev, emotion_mainloop_event_list);
+        ev->event_list = eina_list_append(ev->event_list, data);
+        eina_lock_release(&ev->lock);
+     }
+}
+
+/* Returns true if libvlc mediaplayer is ready to process commands. */
+static Eina_Bool
+libvlc_mp_is_ready(Emotion_LibVLC *ev)
+{
+   libvlc_state_t state;
+
+   if (!ev->mp) return EINA_FALSE;
+   state = libvlc_media_player_get_state(ev->mp);
+   return state == libvlc_Playing || state == libvlc_Paused;
+}
+
+/* Fetch all libvlc tracks. */
+static int
+libvlc_fetch_tracks(Emotion_LibVLC *ev)
+{
+   if (ev->invalidate_tracks)
+     {
+        if (ev->nb_tracks)
+          libvlc_media_tracks_release(ev->tracks, ev->nb_tracks);
+        ev->nb_tracks = libvlc_media_tracks_get(ev->m, &ev->tracks);
+        ev->invalidate_tracks = EINA_FALSE;
+     }
+   return ev->nb_tracks;
+}
+
+/* Get a libvlc tracks from a track_id. */
+static libvlc_media_track_t *
+libvlc_get_track(Emotion_LibVLC *ev, libvlc_track_type_t type, int id)
+{
+   unsigned int i;
+
+   if (!ev->m || id < 0 || type == libvlc_track_unknown) return NULL;
+
+   if (!libvlc_fetch_tracks(ev)) return NULL;
+
+   for (i = 0; i < ev->nb_tracks; ++i)
+     {
+        libvlc_media_track_t *track = ev->tracks[i];
+
+        if (track->i_id == id && track->i_type == type)
+          return track;
+     }
+
+   return NULL;
+}
+
+/* Get the current libvlc video track. */
+static libvlc_media_track_t *
+libvlc_get_current_video_track(Emotion_LibVLC *ev)
+{
+   int id = libvlc_video_get_track(ev->mp);
+   return id >= 0 ? libvlc_get_track(ev, libvlc_track_video, id) : NULL;
+}
+
+/* Get a libvlc video track at a pos.
+ * XXX: Libvlc use a track_id to get and select a track. The first track_id 
doesn't
+ * necessarily starts with 0. Emotion use a position (that starts with 0) to
+ * get and select a track. */
+static libvlc_media_track_t *
+libvlc_get_track_at_pos(Emotion_LibVLC *ev,
+                        int pos, libvlc_track_type_t type)
+{
+   unsigned int i;
+
+   if (!ev->m || pos < 0 || type == libvlc_track_unknown) return NULL;
+
+   if (!libvlc_fetch_tracks(ev)) return NULL;
+
+   for (i = 0; i < ev->nb_tracks; ++i)
+     {
+        libvlc_media_track_t *track = ev->tracks[i];
+
+        if (type == track->i_type && pos-- == 0)
+          return track;
+     }
+
+   return NULL;
+}
+
+/* Get the position of the libvlc track.
+ * See libvlc_get_track_at_pos. */
+static int
+libvlc_get_track_pos(Emotion_LibVLC *ev, int id, libvlc_track_type_t type)
+{
+   unsigned int i;
+   int pos = 0;
+
+   if (!ev->m || id < 0 || type == libvlc_track_unknown) return -1;
+
+   if (!libvlc_fetch_tracks(ev)) return -1;
+
+   for (i = 0; i < ev->nb_tracks; ++i)
+     {
+        libvlc_media_track_t *track = ev->tracks[i];
+
+        if (type == track->i_type)
+          {
+             if (id == track->i_id)
+               return pos;
+             else
+               pos++;
+          }
+     }
+
+   return -1;
+}
+
+static void *
+em_add(const Emotion_Engine *api EINA_UNUSED,
+       Evas_Object *obj,
+       const Emotion_Module_Options *opt)
+{
+   Emotion_LibVLC *ev;
+   ev = calloc(1, sizeof(Emotion_LibVLC));
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ev, NULL);
+
+   ev->obj = obj;
+   ev->opt = *opt;
+   eina_lock_new(&ev->lock);
+   eina_condition_new(&ev->wait, &ev->lock);
+   ev->ref_count = 1;
+   ev->audio_mute = -1;
+   ev->audio_vol = -1;
+
+   return ev;
+}
+
+static void
+em_del(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!ev) return;
+
+   em_file_close(video);
+
+   eina_lock_take(&ev->lock);
+   if (--ev->ref_count > 0)
+     {
+        eina_lock_release(&ev->lock);
+        WRN("em_del delayed, some callbacks are still running");
+     }
+   else
+     {
+        eina_lock_release(&ev->lock);
+        em_del_safe(ev);
+     }
+}
+
+static Eina_Bool
+em_file_open(void *video,
+             const char *file)
+{
+   int ret;
+   Emotion_LibVLC *ev = video;
+   libvlc_event_manager_t *event_m;
+
+   if (!file) return EINA_FALSE;
+
+   ev->evas_obj = emotion_object_image_get(ev->obj);
+   if (!ev->evas_obj)
+     {
+        WRN("emotion_object_image_get failed: no video");
+        ev->opt.no_video = EINA_TRUE;
+     }
+
+   ev->invalidate_tracks = true;
+
+   /* Create libvlc_media */
+   ev->m = libvlc_media_new_path(libvlc, file);
+   EINA_SAFETY_ON_NULL_GOTO(ev->m, error);
+
+   if (ev->opt.no_audio)
+     libvlc_media_add_option(ev->m, ":no-audio");
+
+   if (ev->opt.no_video)
+     libvlc_media_add_option(ev->m, ":no-video");
+
+   /* Create libvlc_media_player */
+   ev->mp = libvlc_media_player_new_from_media(ev->m);
+   EINA_SAFETY_ON_NULL_GOTO(ev->mp, error);
+
+   event_m = libvlc_media_player_event_manager(ev->mp);
+   for (int i = 0; mp_events[i] != -1; ++i)
+     libvlc_event_attach(event_m, mp_events[i], libvlc_on_mp_event, ev);
+
+   libvlc_media_player_set_video_title_display(ev->mp,
+                                               libvlc_position_disable, 0);
+
+   /* Set sink callbacks */
+   if (!ev->opt.no_video)
+     {
+        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);
+     }
+
+   if (ev->audio_mute != -1)
+     libvlc_audio_set_mute(ev->mp, 1);
+   if (ev->audio_vol != -1)
+     libvlc_audio_set_volume(ev->mp, ev->audio_vol);
+
+   ret = libvlc_media_player_play(ev->mp);
+   EINA_SAFETY_ON_FALSE_GOTO(ret == 0, error);
+
+   return EINA_TRUE;
+error:
+   em_file_close(video);
+   return EINA_FALSE;
+}
+
+static void
+em_file_close(void *video)
+{
+   Emotion_LibVLC *ev = video;
+   unsigned int i;
+
+   if (ev->mp)
+     {
+        libvlc_event_manager_t *event_m;
+
+        event_m = libvlc_media_player_event_manager(ev->mp);
+        for (int 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_release(ev->mp);
+        ev->mp = NULL;
+
+        if (ev->seeking)
+          {
+             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)
+     {
+        libvlc_media_release(ev->m);
+        ev->m = NULL;
+     }
+   if (ev->nb_tracks > 0)
+     {
+        libvlc_media_tracks_release(ev->tracks, ev->nb_tracks);
+        ev->nb_tracks = 0;
+        ev->tracks = NULL;
+     }
+   if (ev->nb_chapters > 0)
+     {
+        libvlc_chapter_descriptions_release(ev->chapters, ev->nb_chapters);
+        ev->nb_chapters = 0;
+        ev->chapters = NULL;
+     }
+   for (i = 0; i < META_TRACK_COUNT; ++i)
+     {
+        free(ev->metas[i]);
+        ev->metas[i] = NULL;
+     }
+   if (ev->subtitle_file)
+     {
+        free(ev->subtitle_file);
+        ev->subtitle_file = NULL;
+     }
+   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
+em_play(void *video,
+        double pos EINA_UNUSED)
+{
+   Emotion_LibVLC *ev = video;
+
+   libvlc_media_player_set_pause(ev->mp, false);
+}
+
+static void
+em_stop(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   libvlc_media_player_set_pause(ev->mp, true);
+}
+
+static void
+em_size_get(void *video,
+            int *width,
+            int *height)
+{
+   Emotion_LibVLC *ev = video;
+   libvlc_media_track_t *track;
+
+   if (!width || !height) return;
+   *width = 0;
+   *height = 0;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   track = libvlc_get_current_video_track(ev);
+   if (track)
+     {
+        *width = track->video->i_width;
+        *height = track->video->i_height;
+     }
+}
+
+static double
+em_pos_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 0.0;
+
+   return ev->pos;
+}
+
+static void
+em_pos_set(void *video,
+           double pos)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev))
+     {
+        _emotion_seek_done(ev->obj);
+        return;
+     }
+
+   libvlc_media_player_set_time(ev->mp, pos * 1000);
+
+   ev->seeking = EINA_TRUE;
+}
+
+static double
+em_len_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 0.0;
+
+   return ev->len;
+}
+
+static double
+em_buffer_size_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 0.0;
+
+   return ev->buffer_cache;
+}
+
+static void
+em_fps_num_den_get(Emotion_LibVLC *ev, int *num, int *den)
+{
+   libvlc_media_track_t *track;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   track = libvlc_get_current_video_track(ev);
+   if (track)
+     {
+        if (num)
+          *num = track->video->i_frame_rate_num;
+        if (den)
+          *den = track->video->i_frame_rate_den;
+     }
+}
+
+static int
+em_fps_num_get(void *video)
+{
+   int num = 0;
+
+   em_fps_num_den_get(video, &num, NULL);
+   return num;
+}
+
+static int
+em_fps_den_get(void *video)
+{
+   int den = 0;
+
+   em_fps_num_den_get(video, NULL, &den);
+   return den;
+}
+
+static double
+em_fps_get(void *video)
+{
+   int num = 0, den = 0;
+
+   em_fps_num_den_get(video, &num, &den);
+   return num / (double)den;
+}
+
+static void
+em_vis_set(void *video,
+           Emotion_Vis vis)
+{
+   Emotion_LibVLC *ev = video;
+
+   ev->vis = vis;
+}
+
+static Emotion_Vis
+em_vis_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   return ev->vis;
+}
+
+static Eina_Bool
+em_vis_supported(void *ev EINA_UNUSED, Emotion_Vis vis)
+{
+   /* FIXME */
+   if (vis == EMOTION_VIS_NONE)
+     return EINA_TRUE;
+   else
+     return EINA_FALSE;
+}
+
+static double
+em_ratio_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+   libvlc_media_track_t *track;
+
+   if (!libvlc_mp_is_ready(ev)) return 0.0;
+
+   track = libvlc_get_current_video_track(ev);
+   if (track)
+     {
+        double ratio = track->video->i_sar_num
+          / (double)track->video->i_sar_den;
+        return ratio;
+     }
+   else
+     return 0.0;
+}
+
+static int
+em_video_handled(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 0.0;
+
+   return libvlc_video_get_track_count(ev->mp) > 0;
+}
+
+static int
+em_audio_handled(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 0.0;
+
+   return libvlc_audio_get_track_count(ev->mp) > 0;
+}
+
+static int
+em_seekable(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 0;
+
+   return libvlc_media_player_is_seekable(ev->mp);
+}
+
+static void
+em_frame_done(void *video EINA_UNUSED)
+{
+}
+
+static Emotion_Format
+em_format_get(void *video EINA_UNUSED)
+{
+   return EMOTION_FORMAT_NONE;
+}
+
+static void
+em_video_data_size_get(void *video EINA_UNUSED, int *w EINA_UNUSED, int *h 
EINA_UNUSED)
+{
+}
+
+static int
+em_yuv_rows_get(void *video EINA_UNUSED,
+                int w EINA_UNUSED,
+                int h EINA_UNUSED,
+                unsigned char **yrows EINA_UNUSED,
+                unsigned char **urows EINA_UNUSED,
+                unsigned char **vrows EINA_UNUSED)
+{
+   return 0;
+}
+
+static int
+em_bgra_data_get(void *video EINA_UNUSED, unsigned char **bgra_data 
EINA_UNUSED)
+{
+   return 0;
+}
+
+static void
+em_event_feed(void *video, int event)
+{
+   Emotion_LibVLC *ev = video;
+   unsigned int navigate;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   switch (event)
+     {
+      case EMOTION_EVENT_UP:
+        navigate = libvlc_navigate_up;
+        break;
+
+      case EMOTION_EVENT_DOWN:
+        navigate = libvlc_navigate_down;
+        break;
+
+      case EMOTION_EVENT_LEFT:
+        navigate = libvlc_navigate_left;
+        break;
+
+      case EMOTION_EVENT_RIGHT:
+        navigate = libvlc_navigate_right;
+        break;
+
+      case EMOTION_EVENT_SELECT:
+        navigate = libvlc_navigate_activate;
+        break;
+
+      /* FIXME */
+      default:
+      case EMOTION_EVENT_MENU1:
+      case EMOTION_EVENT_MENU2:
+      case EMOTION_EVENT_MENU3:
+      case EMOTION_EVENT_MENU4:
+      case EMOTION_EVENT_MENU5:
+      case EMOTION_EVENT_MENU6:
+      case EMOTION_EVENT_MENU7:
+      case EMOTION_EVENT_NEXT:
+      case EMOTION_EVENT_PREV:
+      case EMOTION_EVENT_ANGLE_NEXT:
+      case EMOTION_EVENT_ANGLE_PREV:
+      case EMOTION_EVENT_FORCE:
+      case EMOTION_EVENT_0:
+      case EMOTION_EVENT_1:
+      case EMOTION_EVENT_2:
+      case EMOTION_EVENT_3:
+      case EMOTION_EVENT_4:
+      case EMOTION_EVENT_5:
+      case EMOTION_EVENT_6:
+      case EMOTION_EVENT_7:
+      case EMOTION_EVENT_8:
+      case EMOTION_EVENT_9:
+      case EMOTION_EVENT_10:
+        return;
+     }
+   libvlc_media_player_navigate(ev->mp, navigate);
+}
+
+static void
+em_event_mouse_button_feed(void *video EINA_UNUSED, int button EINA_UNUSED, 
int x EINA_UNUSED, int y EINA_UNUSED)
+{
+   /* FIXME */
+}
+
+static void
+em_event_mouse_move_feed(void *video EINA_UNUSED, int x EINA_UNUSED, int y 
EINA_UNUSED)
+{
+   /* FIXME */
+}
+
+static int
+em_video_channel_count(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return -1;
+
+   return libvlc_video_get_track_count(ev->mp);
+}
+
+static void
+em_video_channel_set(void *video,
+                     int channel)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   if (channel < 0)
+     libvlc_video_set_track(ev->mp, -1);
+   else
+     {
+        libvlc_media_track_t *track;
+        track = libvlc_get_track_at_pos(ev, channel, libvlc_track_video);
+        if (track)
+          libvlc_video_set_track(ev->mp, track->i_id);
+     }
+}
+
+static int
+em_video_channel_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+   int id;
+
+   if (!libvlc_mp_is_ready(ev)) return -1;
+
+   id = libvlc_video_get_track(ev->mp);
+
+   return id >= 0 ? libvlc_get_track_pos(ev, id, libvlc_track_video) : -1;
+}
+
+static void
+em_video_subtitle_file_set(void *video,
+                           const char *filepath)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   free(ev->subtitle_file);
+   ev->subtitle_file = filepath ? strdup(filepath) : NULL;
+   libvlc_video_set_subtitle_file(ev->mp, ev->subtitle_file);
+}
+
+static const char *
+em_video_subtitle_file_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   return ev->subtitle_file;
+}
+
+static const char *
+em_video_channel_name_get(void *video,
+                          int channel)
+{
+   Emotion_LibVLC *ev = video;
+   libvlc_media_track_t *track;
+
+   if (!libvlc_mp_is_ready(ev)) return NULL;
+
+   track = libvlc_get_track_at_pos(ev, channel, libvlc_track_video);
+   if (track)
+     return track->psz_description;
+   else
+     return NULL;
+}
+
+static void
+em_video_channel_mute_set(void *video,
+                          int mute)
+{
+   Emotion_LibVLC *ev = video;
+
+   ev->video_mute = mute;
+}
+
+static int
+em_video_channel_mute_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   return ev->video_mute;
+}
+
+static int
+em_channel_count(int vlc_count)
+{
+   /* vlc count the -1 track that deactivate the channel for audio and spu */
+   return vlc_count > 0 ? vlc_count - 1 : vlc_count;
+}
+
+static int
+em_audio_channel_count(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return -1;
+
+   return em_channel_count(libvlc_audio_get_track_count(ev->mp));
+}
+
+static void
+em_audio_channel_set(void *video,
+                     int channel)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   if (channel < 0)
+     libvlc_audio_set_track(ev->mp, -1);
+   else
+     {
+        libvlc_media_track_t *track;
+        track = libvlc_get_track_at_pos(ev, channel, libvlc_track_audio);
+        if (track)
+          libvlc_audio_set_track(ev->mp, track->i_id);
+     }
+}
+
+static int
+em_audio_channel_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+   int id;
+
+   if (!libvlc_mp_is_ready(ev)) return -1;
+
+   id = libvlc_audio_get_track(ev->mp);
+
+   return id >= 0 ? libvlc_get_track_pos(ev, id, libvlc_track_audio) : -1;
+}
+
+static const char *
+em_audio_channel_name_get(void *video,
+                          int channel)
+{
+   Emotion_LibVLC *ev = video;
+   libvlc_media_track_t *track;
+
+   if (!libvlc_mp_is_ready(ev)) return NULL;
+
+   track = libvlc_get_track_at_pos(ev, channel, libvlc_track_audio);
+   if (track)
+     return track->psz_description;
+   else
+     return NULL;
+}
+
+static void
+em_audio_channel_mute_set(void *video,
+                          int mute)
+{
+   Emotion_LibVLC *ev = video;
+
+   ev->audio_mute = mute;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   libvlc_audio_set_mute(ev->mp, mute);
+}
+
+static int
+em_audio_channel_mute_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev))
+     return ev->audio_mute;
+
+   return libvlc_audio_get_mute(ev->mp);
+}
+
+static void
+em_audio_channel_volume_set(void *video,
+                            double vol)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (vol < 0.0)
+     vol = 0.0;
+   else if (vol > 1.0)
+     vol = 1.0;
+   ev->audio_vol = vol * 100;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   libvlc_audio_set_volume(ev->mp, ev->audio_vol);
+}
+
+static double
+em_audio_channel_volume_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev))
+     return ev->audio_vol / 100.0;
+
+   return libvlc_audio_get_volume(ev->mp) / 100.0;
+}
+
+static int
+em_spu_channel_count(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return -1;
+
+   return em_channel_count(libvlc_video_get_spu_count(ev->mp));
+}
+
+static void
+em_spu_channel_set(void *video,
+                   int channel)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   if (channel < 0)
+     libvlc_video_set_spu(ev->mp, -1);
+   else
+     {
+        libvlc_media_track_t *track;
+        track = libvlc_get_track_at_pos(ev, channel, libvlc_track_text);
+        if (track)
+          libvlc_video_set_spu(ev->mp, track->i_id);
+     }
+}
+
+static int
+em_spu_channel_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+   int id;
+
+   if (!libvlc_mp_is_ready(ev)) return -1;
+
+   id = libvlc_video_get_spu(ev->mp);
+
+   return id >= 0 ? libvlc_get_track_pos(ev, id, libvlc_track_text) : -1;
+}
+
+static const char *
+em_spu_channel_name_get(void *video,
+                        int channel)
+{
+   Emotion_LibVLC *ev = video;
+   libvlc_media_track_t *track;
+
+   if (!libvlc_mp_is_ready(ev)) return NULL;
+
+   track = libvlc_get_track_at_pos(ev, channel, libvlc_track_text);
+   if (track)
+     return track->psz_description;
+   else
+     return NULL;
+}
+
+static void
+em_spu_channel_mute_set(void *video EINA_UNUSED, int mute EINA_UNUSED)
+{
+}
+
+static int
+em_spu_channel_mute_get(void *video EINA_UNUSED)
+{
+   return 0;
+}
+
+static int
+em_chapter_count(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 0;
+
+   return libvlc_media_player_get_chapter_count(ev->mp);
+}
+
+static void
+em_chapter_set(void *video, int chapter)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   libvlc_media_player_set_chapter(ev->mp, chapter);
+}
+
+static int
+em_chapter_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 0;
+
+   return libvlc_media_player_get_chapter(ev->mp);
+}
+
+static const char *
+em_chapter_name_get(void *video, int chapter)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return NULL;
+
+   if (ev->nb_chapters == 0)
+     {
+        ev->nb_chapters =
+          libvlc_media_player_get_full_chapter_descriptions(ev->mp,
+                                                            -1,
+                                                            &ev->chapters);
+        if (ev->nb_chapters == 0)
+          ev->nb_chapters = -1;
+     }
+   return chapter < ev->nb_chapters ? ev->chapters[chapter]->psz_name : NULL;
+}
+
+static void
+em_speed_set(void *video, double speed)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return;
+
+   libvlc_media_player_set_rate(ev->mp, speed);
+}
+
+static double
+em_speed_get(void *video)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return 1.0;
+
+   return libvlc_media_player_get_rate(ev->mp);
+}
+
+static int
+em_eject(void *video EINA_UNUSED)
+{
+   return 1;
+}
+
+static const char *
+em_meta_get(void *video, int meta)
+{
+   Emotion_LibVLC *ev = video;
+
+   if (!libvlc_mp_is_ready(ev)) return NULL;
+
+   if (meta <= 0 || meta >= META_TRACK_COUNT)
+     return NULL;
+
+   if (ev->metas[meta])
+     return ev->metas[meta];
+   else
+     {
+        libvlc_meta_t vlc_meta;
+        switch (meta)
+          {
+           case META_TRACK_TITLE:
+             vlc_meta = libvlc_meta_Title;
+             break;
+
+           case META_TRACK_ARTIST:
+             vlc_meta = libvlc_meta_Artist;
+             break;
+
+           case  META_TRACK_ALBUM:
+             vlc_meta = libvlc_meta_Album;
+             break;
+
+           case META_TRACK_YEAR:
+             vlc_meta = libvlc_meta_Date;
+             break;
+
+           case META_TRACK_GENRE:
+             vlc_meta = libvlc_meta_Genre;
+             break;
+
+           case META_TRACK_COMMENT:
+             vlc_meta = libvlc_meta_Description;
+             break;
+
+           case META_TRACK_DISCID:
+             vlc_meta = libvlc_meta_TrackID;
+             break;
+
+           default:
+             return NULL;
+             break;
+          }
+        ev->metas[meta] = libvlc_media_get_meta(ev->m, vlc_meta);
+
+        return ev->metas[meta];
+     }
+}
+
+static const Emotion_Engine em_engine =
+{
+   EMOTION_ENGINE_API_VERSION,
+   EMOTION_ENGINE_PRIORITY_DEFAULT,
+   "libvlc",
+   em_add, /* add */
+   em_del, /* del */
+   em_file_open, /* file_open */
+   em_file_close, /* file_close */
+   em_play, /* play */
+   em_stop, /* stop */
+   em_size_get, /* size_get */
+   em_pos_set, /* pos_set */
+   em_len_get, /* len_get */
+   em_buffer_size_get, /* buffer_size_get */
+   em_fps_num_get, /* fps_num_get */
+   em_fps_den_get, /* fps_den_get */
+   em_fps_get, /* fps_get */
+   em_pos_get, /* pos_get */
+   em_vis_set, /* vis_set */
+   em_vis_get, /* vis_get */
+   em_vis_supported, /* vis_supported */
+   em_ratio_get, /* ratio_get */
+   em_video_handled, /* video_handled */
+   em_audio_handled, /* audio_handled */
+   em_seekable, /* seekable */
+   em_frame_done, /* frame_done */
+   em_format_get, /* format_get */
+   em_video_data_size_get, /* video_data_size_get */
+   em_yuv_rows_get, /* yuv_rows_get */
+   em_bgra_data_get, /* bgra_data_get */
+   em_event_feed, /* event_feed */
+   em_event_mouse_button_feed, /* event_mouse_button_feed */
+   em_event_mouse_move_feed, /* event_mouse_move_feed */
+   em_video_channel_count, /* video_channel_count */
+   em_video_channel_set, /* video_channel_set */
+   em_video_channel_get, /* video_channel_get */
+   em_video_subtitle_file_set, /* video_subtitle_file_set */
+   em_video_subtitle_file_get, /* video_subtitle_file_get */
+   em_video_channel_name_get, /* video_channel_name_get */
+   em_video_channel_mute_set, /* video_channel_mute_set */
+   em_video_channel_mute_get, /* video_channel_mute_get */
+   em_audio_channel_count, /* audio_channel_count */
+   em_audio_channel_set, /* audio_channel_set */
+   em_audio_channel_get, /* audio_channel_get */
+   em_audio_channel_name_get, /* audio_channel_name_get */
+   em_audio_channel_mute_set, /* audio_channel_mute_set */
+   em_audio_channel_mute_get, /* audio_channel_mute_get */
+   em_audio_channel_volume_set, /* audio_channel_volume_set */
+   em_audio_channel_volume_get, /* audio_channel_volume_get */
+   em_spu_channel_count, /* spu_channel_count */
+   em_spu_channel_set, /* spu_channel_set */
+   em_spu_channel_get, /* spu_channel_get */
+   em_spu_channel_name_get, /* spu_channel_name_get */
+   em_spu_channel_mute_set, /* spu_channel_mute_set */
+   em_spu_channel_mute_get, /* spu_channel_mute_get */
+   em_chapter_count, /* chapter_count */
+   em_chapter_set, /* chapter_set */
+   em_chapter_get, /* chapter_get */
+   em_chapter_name_get, /* chapter_name_get */
+   em_speed_set, /* speed_set */
+   em_speed_get, /* speed_get */
+   em_eject, /* eject */
+   em_meta_get, /* meta_get */
+   NULL, /* priority_set */
+   NULL /* priority_get */
+};
+
+void
+libvlc_log(void *data EINA_UNUSED, int level,
+           const libvlc_log_t *ctx EINA_UNUSED,
+           const char *fmt, va_list args)
+{
+   Eina_Log_Level eina_log_level;
+   switch (level)
+     {
+      case LIBVLC_DEBUG:
+        eina_log_level = EINA_LOG_LEVEL_DBG;
+        break;
+
+      case LIBVLC_NOTICE:
+        eina_log_level = EINA_LOG_LEVEL_INFO;
+        break;
+
+      case LIBVLC_WARNING:
+        eina_log_level = EINA_LOG_LEVEL_WARN;
+        break;
+
+      case LIBVLC_ERROR:
+      default:
+        eina_log_level = EINA_LOG_LEVEL_ERR;
+        break;
+     }
+   eina_log_vprint(_emotion_libvlc_log_domain, eina_log_level,
+                   __FILE__, __FUNCTION__, __LINE__, fmt, args);
+}
+
+Eina_Bool
+libvlc_module_init(void)
+{
+   if (libvlc)
+     {
+        return EINA_TRUE;
+     }
+
+   if (getenv("EMOTION_FPS_DEBUG")) debug_fps = EINA_TRUE;
+
+   eina_threads_init();
+   eina_log_threads_enable();
+   _emotion_libvlc_log_domain = eina_log_domain_register
+       ("emotion-libvlc", EINA_COLOR_ORANGE);
+   if (_emotion_libvlc_log_domain < 0)
+     {
+        EINA_LOG_CRIT("Could not register log domain 'emotion-libvlc'");
+        return EINA_FALSE;
+     }
+
+   libvlc = libvlc_new(0, NULL);
+   if (!libvlc)
+     {
+        CRI("could not create libvlc instance");
+        goto error_register;
+     }
+   EINA_LOG_INFO("using libvlc: %s", libvlc_get_version());
+   libvlc_log_set(libvlc, libvlc_log, NULL);
+
+   if (!_emotion_module_register(&em_engine))
+     {
+        ERR("Could not register module %p", &em_engine);
+        goto error_register;
+     }
+
+   return EINA_TRUE;
+
+error_register:
+   if (libvlc)
+     {
+        libvlc_release(libvlc);
+        libvlc = NULL;
+     }
+   eina_log_domain_unregister(_emotion_libvlc_log_domain);
+   _emotion_libvlc_log_domain = -1;
+   return EINA_FALSE;
+}
+
+void
+libvlc_module_shutdown(void)
+{
+   if (!libvlc)
+     {
+        EINA_LOG_ERR("too many libvlc_module_shutdown()");
+        return;
+     }
+
+   _emotion_module_unregister(&em_engine);
+
+   libvlc_release(libvlc);
+   libvlc = NULL;
+
+   eina_log_domain_unregister(_emotion_libvlc_log_domain);
+   _emotion_libvlc_log_domain = -1;
+}
+
+#ifndef EMOTION_STATIC_BUILD_LIBVLC
+
+EINA_MODULE_INIT(libvlc_module_init);
+EINA_MODULE_SHUTDOWN(libvlc_module_shutdown);
+
+#endif

-- 


Reply via email to