Add generic player backend (with vlc player).
authorantognolli <antognolli>
Thu, 1 Sep 2011 19:04:15 +0000 (19:04 +0000)
committerantognolli <antognolli@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Thu, 1 Sep 2011 19:04:15 +0000 (19:04 +0000)
This generic player backend executes a separate player in another
process. It receives the bytes to be drawn on the emotion object through
a shared memory, and communicates with the player through a pipe, using
the player standard input/output.

The player must communicate with emotion using the defined commands
specified in the Emotion_Generic_Plugin.h. It doesn't need to link
against emotion, just include this file for easier implementation.

This implementation was sponsored by Zodiac Aerospace.

git-svn-id: http://svn.enlightenment.org/svn/e/trunk/emotion@63062 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

17 files changed:
AUTHORS
configure.ac
m4/emotion_check.m4
src/Makefile.am
src/examples/Makefile.am
src/examples/emotion_generic_example.c [new file with mode: 0644]
src/generic_players/Makefile.am [new file with mode: 0644]
src/generic_players/vlc/Makefile.am [new file with mode: 0644]
src/generic_players/vlc/emotion_generic_vlc.c [new file with mode: 0644]
src/lib/emotion_private.h
src/lib/emotion_smart.c
src/modules/Makefile.am
src/modules/generic/Emotion_Generic_Plugin.h [new file with mode: 0644]
src/modules/generic/Makefile.am [new file with mode: 0644]
src/modules/generic/README [new file with mode: 0644]
src/modules/generic/emotion_generic.c [new file with mode: 0644]
src/modules/generic/emotion_generic.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 8ad8032..1ba92ae 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,4 +3,4 @@ Vincent Torri <torri@maths.univ-evry.fr>
 Nicolas Aguirre <aguirre.nicolas@gmail.com>
 Sebastian Dransfeld <sd@tango.flipp.net>
 Cedric Bail <cedric.bail@free.fr>
+Rafael Antognolli <antognolli@profusion.mobi>
index 3015c30..bc0b439 100644 (file)
@@ -54,6 +54,7 @@ VMAJ=v_maj
 AC_SUBST(VMAJ)
 
 want_vlc="no"
+want_generic="yes"
 case "$host_os" in
    mingw* | cegcc*)
       want_xine="no"
@@ -230,6 +231,7 @@ AC_MSG_RESULT([${have_v4l2}])
 EMOTION_CHECK_MODULE([Xine], [${want_xine}])
 EMOTION_CHECK_MODULE([Gstreamer], [${want_gstreamer}])
 EMOTION_CHECK_MODULE([VLC], [${want_vlc}])
+EMOTION_CHECK_MODULE([generic], [${want_generic}])
 
 #disabled vlc
 #if test "x${enable_xine}" = "xno" && test "x${enable_gstreamer}" = "xno" && test "x${enable_vlc}" = "xno" ; then
@@ -269,7 +271,10 @@ src/lib/Makefile
 src/modules/Makefile
 src/modules/xine/Makefile
 src/modules/gstreamer/Makefile
+src/modules/generic/Makefile
 src/edje_external/Makefile
+src/generic_players/Makefile
+src/generic_players/vlc/Makefile
 src/bin/Makefile
 src/examples/Makefile
 doc/Makefile
index ead676a..0281f1d 100644 (file)
@@ -71,6 +71,28 @@ AS_IF([test "x$have_dep" = "xyes"], [$2], [$3])
 
 ])
 
+dnl use: EMOTION_CHECK_DEP_GENERIC(want_static[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+
+AC_DEFUN([EMOTION_CHECK_DEP_GENERIC],
+[
+
+requirement=""
+
+PKG_CHECK_MODULES([GENERIC],
+   [evas >= 0.9.9],
+   [
+    have_dep="yes"
+   ],
+   [have_dep="no"])
+
+if test "x$1" = "xstatic" ; then
+   requirement_emotion="${requirement} ${requirement_emotion}"
+fi
+
+AS_IF([test "x$have_dep" = "xyes"], [$2], [$3])
+
+])
+
 dnl use: EMOTION_CHECK_MODULE(description, want_module[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
 AC_DEFUN([EMOTION_CHECK_MODULE],
 [
index 8146837..4082c3a 100644 (file)
@@ -6,3 +6,7 @@ SUBDIRS = lib bin modules examples
 if ENABLE_EDJE_EXTERNAL
 SUBDIRS += edje_external
 endif
+
+if EMOTION_BUILD_GENERIC
+SUBDIRS += generic_players
+endif
index 18b5921..a19a1ae 100644 (file)
@@ -16,6 +16,7 @@ LDADD = \
 
 SRCS = \
        emotion_basic_example.c \
+       emotion_generic_example.c \
        emotion_signals_example.c
 
 EXTRA_DIST = $(SRCS)
@@ -30,6 +31,7 @@ endif
 if EFL_BUILD_EXAMPLES
 pkglib_PROGRAMS += \
        emotion_basic_example \
+       emotion_generic_example \
        emotion_signals_example
 endif
 
diff --git a/src/examples/emotion_generic_example.c b/src/examples/emotion_generic_example.c
new file mode 100644 (file)
index 0000000..2eb8eef
--- /dev/null
@@ -0,0 +1,209 @@
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <Evas.h>
+#include <Emotion.h>
+#include <stdio.h>
+#include <string.h>
+
+#define WIDTH  (320)
+#define HEIGHT (240)
+
+static Eina_List *filenames = NULL;
+static Eina_List *curfile = NULL;
+
+static void
+_playback_started_cb(void *data, Evas_Object *o, void *event_info)
+{
+    printf("Emotion object started playback.\n");
+}
+
+static Evas_Object *
+_create_emotion_object(Evas *e)
+{
+   Evas_Object *em = emotion_object_add(e);
+
+   emotion_object_init(em, "generic");
+
+   evas_object_smart_callback_add(
+       em, "playback_started", _playback_started_cb, NULL);
+
+   return em;
+}
+
+static void
+_on_key_down(void *data, Evas *e, Evas_Object *o, void *event_info)
+{
+   Evas_Event_Key_Down *ev = event_info;
+   Evas_Object *em = data;
+
+   if (!strcmp(ev->keyname, "Return"))
+     {
+       emotion_object_play_set(em, EINA_TRUE);
+     }
+   else if (!strcmp(ev->keyname, "space"))
+     {
+       emotion_object_play_set(em, EINA_FALSE);
+     }
+   else if (!strcmp(ev->keyname, "Escape"))
+     {
+       ecore_main_loop_quit();
+     }
+   else if (!strcmp(ev->keyname, "t"))
+     {
+       int w, h;
+       emotion_object_size_get(em, &w, &h);
+       fprintf(stderr, "example -> size: %dx%d\n", w, h);
+     }
+   else if (!strcmp(ev->keyname, "s"))
+     {
+       fprintf(stderr, "skipping to position 60\n");
+       emotion_object_position_set(em, 60);
+     }
+   else if (!strcmp(ev->keyname, "1"))
+     {
+       fprintf(stderr, "setting speed to 1.0\n");
+       emotion_object_play_speed_set(em, 1.0);
+     }
+   else if (!strcmp(ev->keyname, "2"))
+     {
+       fprintf(stderr, "setting speed to 2.0\n");
+       emotion_object_play_speed_set(em, 2.0);
+     }
+   else if (!strcmp(ev->keyname, "n"))
+     {
+       const char *file;
+       curfile = eina_list_next(curfile);
+       file = eina_list_data_get(curfile);
+       fprintf(stderr, "playing next file: %s\n", file);
+       emotion_object_file_set(em, file);
+     }
+   else if (!strcmp(ev->keyname, "p"))
+     {
+       const char *file;
+       curfile = eina_list_prev(curfile);
+       file = eina_list_data_get(curfile);
+       fprintf(stderr, "playing next file: %s\n", file);
+       emotion_object_file_set(em, file);
+     }
+   else if (!strcmp(ev->keyname, "d"))
+     {
+       evas_object_del(em);
+     }
+   else
+     {
+       fprintf(stderr, "unhandled key: %s\n", ev->keyname);
+     }
+}
+
+static void
+_frame_decode_cb(void *data, Evas_Object *o, void *event_info)
+{
+   // fprintf(stderr, "smartcb: frame_decode\n");
+}
+
+static void
+_length_change_cb(void *data, Evas_Object *o, void *event_info)
+{
+   fprintf(stderr, "smartcb: length_change: %0.3f\n", emotion_object_play_length_get(o));
+}
+
+static void
+_position_update_cb(void *data, Evas_Object *o, void *event_info)
+{
+   fprintf(stderr, "smartcb: position_update: %0.3f\n", emotion_object_position_get(o));
+}
+
+static void
+_progress_change_cb(void *data, Evas_Object *o, void *event_info)
+{
+   fprintf(stderr, "smartcb: progress_change: %0.3f, %s\n",
+          emotion_object_progress_status_get(o),
+          emotion_object_progress_info_get(o));
+}
+
+static void
+_frame_resize_cb(void *data, Evas_Object *o, void *event_info)
+{
+   int w, h;
+   emotion_object_size_get(o, &w, &h);
+   fprintf(stderr, "smartcb: frame_resize: %dx%d\n", w, h);
+}
+
+int
+main(int argc, const char *argv[])
+{
+   int err;
+   Ecore_Evas *ee;
+   Evas *e;
+   Evas_Object *bg, *em;
+   int i;
+
+   if (argc < 2)
+     {
+       printf("One argument is necessary. Usage:\n");
+       printf("\t%s <filename>\n", argv[0]);
+     }
+
+   eina_init();
+   for (i = 1; i < argc; i++)
+     filenames = eina_list_append(filenames, eina_stringshare_add(argv[i]));
+
+   curfile = filenames;
+
+   if (!ecore_evas_init())
+     return EXIT_FAILURE;
+
+   /* this will give you a window with an Evas canvas under the first
+    * engine available */
+   ee = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL);
+   if (!ee)
+     goto error;
+
+   ecore_evas_show(ee);
+
+   /* the canvas pointer, de facto */
+   e = ecore_evas_get(ee);
+
+   /* adding a background to this example */
+   bg = evas_object_rectangle_add(e);
+   evas_object_name_set(bg, "our dear rectangle");
+   evas_object_color_set(bg, 255, 255, 255, 255); /* white bg */
+   evas_object_move(bg, 0, 0); /* at canvas' origin */
+   evas_object_resize(bg, WIDTH, HEIGHT); /* covers full canvas */
+   evas_object_show(bg);
+
+   /* Creating the emotion object */
+   em = _create_emotion_object(e);
+   emotion_object_file_set(em, eina_list_data_get(curfile));
+   evas_object_move(em, 0, 0);
+   evas_object_resize(em, WIDTH, HEIGHT);
+   evas_object_show(em);
+
+   evas_object_smart_callback_add(em, "frame_decode", _frame_decode_cb, NULL);
+   evas_object_smart_callback_add(em, "length_change", _length_change_cb, NULL);
+   evas_object_smart_callback_add(em, "position_update", _position_update_cb, NULL);
+   evas_object_smart_callback_add(em, "progress_change", _progress_change_cb, NULL);
+   evas_object_smart_callback_add(em, "frame_resize", _frame_resize_cb, NULL);
+
+   evas_object_event_callback_add(bg, EVAS_CALLBACK_KEY_DOWN, _on_key_down, em);
+   evas_object_focus_set(bg, EINA_TRUE);
+
+   emotion_object_play_set(em, EINA_TRUE);
+
+   ecore_main_loop_begin();
+
+   ecore_evas_free(ee);
+   ecore_evas_shutdown();
+   return 0;
+
+error:
+   fprintf(stderr, "you got to have at least one evas engine built and linked"
+                   " up to ecore-evas for this example to run properly.\n");
+
+   EINA_LIST_FREE(filenames, curfile)
+      eina_stringshare_del(eina_list_data_get(curfile));
+
+   ecore_evas_shutdown();
+   eina_shutdown();
+   return -1;
+}
diff --git a/src/generic_players/Makefile.am b/src/generic_players/Makefile.am
new file mode 100644 (file)
index 0000000..ad31acc
--- /dev/null
@@ -0,0 +1,8 @@
+
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS =
+
+if EMOTION_BUILD_VLC
+SUBDIRS += vlc
+endif
diff --git a/src/generic_players/vlc/Makefile.am b/src/generic_players/vlc/Makefile.am
new file mode 100644 (file)
index 0000000..4089b11
--- /dev/null
@@ -0,0 +1,19 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I$(top_srcdir) \
+-I$(top_srcdir)/src/lib \
+-I$(top_srcdir)/src/modules \
+-I$(top_srcdir)/src/modules/generic \
+-DPACKAGE_BIN_DIR=\"$(bindir)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+@VLC_CFLAGS@
+
+pkgdir = $(libdir)/emotion/generic_players/$(MODULE_ARCH)
+
+bin_PROGRAMS = em_generic_vlc
+
+em_generic_vlc_SOURCES = emotion_generic_vlc.c
+em_generic_vlc_DEPENDENCIES = $(top_srcdir)/src/modules/generic/Emotion_Generic_Plugin.h
+em_generic_vlc_LDADD = @VLC_LIBS@
diff --git a/src/generic_players/vlc/emotion_generic_vlc.c b/src/generic_players/vlc/emotion_generic_vlc.c
new file mode 100644 (file)
index 0000000..773bbe9
--- /dev/null
@@ -0,0 +1,700 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <poll.h>
+
+#include <vlc/vlc.h>
+#include <Emotion_Generic_Plugin.h>
+
+enum _Thread_Events {
+     EM_THREAD_POSITION_CHANGED,
+     EM_THREAD_LAST
+};
+
+struct _App {
+     Emotion_Generic_Video_Shared *vs;
+     Emotion_Generic_Video_Frame vf;
+     libvlc_instance_t *libvlc;
+     libvlc_media_t *m;
+     libvlc_media_player_t *mp;
+     libvlc_event_manager_t *event_mgr;
+     libvlc_event_manager_t *mevent_mgr;
+     char *filename;
+     char *shmname;
+     int w, h;
+     int fd_read;
+     int fd_write;
+     int size_sent;
+     int opening;
+     int closing;
+     int playing;
+};
+
+static pthread_mutex_t _mutex_fd = PTHREAD_MUTEX_INITIALIZER;
+
+int
+_em_read_safe(int fd, void *buf, ssize_t size)
+{
+   ssize_t todo;
+   char *p;
+
+   todo = size;
+   p = buf;
+
+   while (todo > 0)
+     {
+        ssize_t r;
+
+        r = read(fd, p, todo);
+        if (r > 0)
+          {
+             todo -= r;
+             p += r;
+          }
+        else if (r == 0)
+          return 0;
+        else
+          {
+             if (errno == EINTR || errno == EAGAIN)
+               continue;
+             else
+               {
+                 fprintf(stderr, "could not read from fd %d: %s",
+                         fd, strerror(errno));
+                  return 0;
+               }
+          }
+     }
+
+   return 1;
+}
+
+int
+_em_write_safe(int fd, const void *buf, ssize_t size)
+{
+   ssize_t todo;
+   const char *p;
+
+   todo = size;
+   p = buf;
+
+   while (todo > 0)
+     {
+        ssize_t r;
+
+        r = write(fd, p, todo);
+        if (r > 0)
+          {
+             todo -= r;
+             p += r;
+          }
+        else if (r == 0)
+          return 0;
+        else
+          {
+             if (errno == EINTR || errno == EAGAIN)
+               continue;
+             else
+               {
+                 fprintf(stderr, "could not write to fd %d: %s",
+                         fd, strerror(errno));
+                 return 0;
+               }
+          }
+     }
+
+   return 1;
+}
+
+static int
+_em_str_read(char **str)
+{
+   int size;
+   int r;
+   char buf[PATH_MAX];
+
+   r = _em_read_safe(STDIN_FILENO, &size, sizeof(size));
+   if (!r)
+     {
+        *str = NULL;
+        return 0;
+     }
+
+   if (!size)
+     {
+        *str = NULL;
+        return 1;
+     }
+
+   r = _em_read_safe(STDIN_FILENO, buf, size);
+   if (!r)
+     {
+        *str = NULL;
+        return 0;
+     }
+
+   *str = strdup(buf);
+   return 1;
+}
+
+static int
+_em_cmd_read(void)
+{
+   int cmd;
+   _em_read_safe(STDIN_FILENO, &cmd, sizeof(cmd));
+
+   return cmd;
+}
+
+static void
+_send_cmd_start(int cmd)
+{
+   pthread_mutex_lock(&_mutex_fd);
+   _em_write_safe(STDOUT_FILENO, &cmd, sizeof(cmd));
+}
+
+static void
+_send_cmd_finish(void)
+{
+   static const char c = '\n';
+   _em_write_safe(STDOUT_FILENO, &c, sizeof(c));
+   pthread_mutex_unlock(&_mutex_fd);
+}
+
+static void
+_send_cmd(int cmd)
+{
+   _send_cmd_start(cmd);
+   _send_cmd_finish();
+}
+
+static void
+_send_cmd_str(const char *str)
+{
+   int len;
+   len = strlen(str) + 1;
+   _em_write_safe(STDOUT_FILENO, &len, sizeof(len));
+   _em_write_safe(STDOUT_FILENO, str, len);
+}
+
+#define SEND_CMD_PARAM(i) \
+   _em_write_safe(STDOUT_FILENO, &(i), sizeof((i)));
+
+static void
+_send_resize(int width, int height)
+{
+   _send_cmd_start(EM_RESULT_FRAME_SIZE);
+   SEND_CMD_PARAM(width);
+   SEND_CMD_PARAM(height);
+   _send_cmd_finish();
+}
+
+static void
+_send_length_changed(const struct libvlc_event_t *ev)
+{
+   float length = ev->u.media_player_length_changed.new_length;
+   length /= 1000;
+
+   fprintf(stderr, "length changed: %0.3f\n", length);
+   _send_cmd_start(EM_RESULT_LENGTH_CHANGED);
+   SEND_CMD_PARAM(length);
+   _send_cmd_finish();
+}
+
+static void
+_send_time_changed(const struct libvlc_event_t *ev)
+{
+   float new_time = ev->u.media_player_time_changed.new_time;
+   new_time /= 1000;
+   _send_cmd_start(EM_RESULT_POSITION_CHANGED);
+   SEND_CMD_PARAM(new_time);
+   _send_cmd_finish();
+}
+
+static void
+_send_seekable_changed(const struct libvlc_event_t *ev)
+{
+   int seekable = ev->u.media_player_seekable_changed.new_seekable;
+   _send_cmd_start(EM_RESULT_SEEKABLE_CHANGED);
+   SEND_CMD_PARAM(seekable);
+   _send_cmd_finish();
+}
+
+static void *
+_lock(void *data, void **pixels)
+{
+   struct _App *app = data;
+
+   if (app->playing)
+     *pixels = app->vf.frames[app->vs->frame.player];
+   else
+     *pixels = NULL;
+
+   return NULL; // picture identifier, not needed here
+}
+
+static void
+_unlock(void *data, void *id, void *const *pixels)
+{
+   struct _App *app = data;
+
+   if (!app->playing)
+     return;
+
+   sem_wait(&app->vs->lock);
+   app->vs->frame.last = app->vs->frame.player;
+   app->vs->frame.player = app->vs->frame.next;
+   app->vs->frame.next = app->vs->frame.last;
+
+   sem_post(&app->vs->lock);
+}
+
+static void
+_display(void *data, void *id)
+{
+   struct _App *app = data;
+   if (!app->playing)
+     return;
+
+   _send_cmd(EM_RESULT_FRAME_NEW);
+}
+
+static void *
+_tmp_lock(void *data, void **pixels)
+{
+   *pixels = NULL;
+   return NULL;
+}
+
+static void
+_tmp_unlock(void *data, void *id, void *const *pixels)
+{
+}
+
+static void
+_tmp_display(void *data, void *id)
+{
+}
+
+static void
+_play(struct _App *app)
+{
+   float pos;
+
+   if (!app->mp)
+     return;
+
+   _em_read_safe(STDIN_FILENO, &pos, sizeof(pos));
+
+   if (app->playing)
+     {
+       libvlc_media_player_set_pause(app->mp, 0);
+     }
+   else
+     {
+       libvlc_time_t new_time = pos * 1000;
+       libvlc_media_player_play(app->mp);
+       libvlc_media_player_set_time(app->mp, new_time);
+       app->playing = 1;
+     }
+}
+
+static void
+_stop(struct _App *app)
+{
+   if (app->mp)
+     libvlc_media_player_set_pause(app->mp, 1);
+}
+
+static void
+_send_file_closed(struct _App *app)
+{
+   app->closing = 0;
+   emotion_generic_shm_free(app->vs);
+   _send_cmd(EM_RESULT_FILE_CLOSE);
+}
+
+static void
+_send_file_set(struct _App *app)
+{
+   if (app->opening)
+      _send_cmd(EM_RESULT_FILE_SET);
+
+   if (app->closing)
+     _send_file_closed(app);
+}
+
+static void
+_event_cb(const struct libvlc_event_t *ev, void *data)
+{
+   struct _App *app = data;
+   int thread_event;
+
+   switch (ev->type) {
+      case libvlc_MediaPlayerTimeChanged:
+        _send_time_changed(ev);
+        break;
+      case libvlc_MediaPlayerPositionChanged:
+        thread_event = EM_THREAD_POSITION_CHANGED;
+        write(app->fd_write, &thread_event, sizeof(thread_event));
+        break;
+      case libvlc_MediaPlayerLengthChanged:
+        _send_length_changed(ev);
+        break;
+      case libvlc_MediaPlayerSeekableChanged:
+        _send_seekable_changed(ev);
+        break;
+      case libvlc_MediaPlayerPlaying:
+        _send_resize(app->w, app->h);
+        break;
+      case libvlc_MediaPlayerStopped:
+        _send_file_set(app);
+        break;
+      case libvlc_MediaPlayerEndReached:
+        _send_cmd(EM_RESULT_PLAYBACK_STOPPED);
+        break;
+   }
+}
+
+static void
+_file_set(struct _App *app)
+{
+   _em_str_read(&app->filename);
+
+   app->m = libvlc_media_new_path(app->libvlc, app->filename);
+   if (!app->m)
+     {
+       fprintf(stderr, "could not open path: \"%s\"\n", app->filename);
+       return;
+     }
+   app->mp = libvlc_media_player_new_from_media(app->m);
+
+   if (!app->mp)
+     {
+       fprintf(stderr, "could not create new player from media.\n");
+       return;
+     }
+
+   app->opening = 1;
+   libvlc_video_set_format(app->mp, "RV32", DEFAULTWIDTH, DEFAULTHEIGHT, DEFAULTWIDTH * 4);
+   libvlc_video_set_callbacks(app->mp, _tmp_lock, _tmp_unlock, _tmp_display, NULL);
+   app->event_mgr = libvlc_media_player_event_manager(app->mp);
+   libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerPositionChanged,
+                      _event_cb, app);
+   libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerStopped,
+                      _event_cb, app);
+
+   app->mevent_mgr = libvlc_media_event_manager(app->m);
+
+   libvlc_audio_set_mute(app->mp, 1);
+   libvlc_media_player_play(app->mp);
+}
+
+static void
+_position_set(struct _App *app)
+{
+   if (!app->mp)
+     return;
+
+   float position;
+   _em_read_safe(STDIN_FILENO, &position, sizeof(position));
+
+   libvlc_time_t new_time = position * 1000;
+   libvlc_media_player_set_time(app->mp, new_time);
+}
+
+static void
+_speed_set(struct _App *app)
+{
+   float rate;
+
+   if (!app->mp)
+     return;
+
+   _em_read_safe(STDIN_FILENO, &rate, sizeof(rate));
+
+   libvlc_media_player_set_rate(app->mp, rate);
+}
+
+static void
+_mute_set(struct _App *app)
+{
+   int mute;
+
+   if (!app->mp)
+     return;
+
+   _em_read_safe(STDIN_FILENO, &mute, sizeof(mute));
+
+   libvlc_audio_set_mute(app->mp, mute);
+}
+
+static void
+_volume_set(struct _App *app)
+{
+   float volume;
+   int vol;
+
+   if (!app->mp)
+     return;
+
+   _em_read_safe(STDIN_FILENO, &volume, sizeof(volume));
+   vol = volume * 100;
+
+   libvlc_audio_set_volume(app->mp, vol);
+}
+
+static void
+_audio_track_set(struct _App *app)
+{
+   int track;
+
+   _em_read_safe(STDIN_FILENO, &track, sizeof(track));
+
+   libvlc_audio_set_track(app->mp, track);
+}
+
+static void
+_file_set_done(struct _App *app)
+{
+   emotion_generic_shm_get(app->shmname, &app->vs, &app->vf);
+   app->w = app->vs->width;
+   app->h = app->vs->height;
+   libvlc_video_set_format(app->mp, "RV32", app->w, app->h, app->w * 4);
+   libvlc_video_set_callbacks(app->mp, _lock, _unlock, _display, app);
+   app->opening = 0;
+
+
+   libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerPlaying,
+                      _event_cb, app);
+   libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerTimeChanged,
+                      _event_cb, app);
+   libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerLengthChanged,
+                      _event_cb, app);
+   libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerSeekableChanged,
+                      _event_cb, app);
+
+   libvlc_audio_set_mute(app->mp, 0);
+   _send_cmd(EM_RESULT_FILE_SET_DONE);
+}
+
+static void
+_file_close(struct _App *app)
+{
+   app->playing = 0;
+   if (libvlc_media_player_get_state(app->mp) != libvlc_Playing)
+     {
+       _send_file_closed(app);
+       return;
+     }
+
+   app->closing = 1;
+   libvlc_media_player_stop(app->mp);
+   if (app->filename)
+     free(app->filename);
+   if (app->mp)
+     {
+        libvlc_media_release(app->m);
+        libvlc_media_player_release(app->mp);
+     }
+}
+
+static void
+_process_emotion_commands(struct _App *app)
+{
+   int cmd = _em_cmd_read();
+   switch (cmd) {
+      case EM_CMD_FILE_SET:
+        _file_set(app);
+        break;
+      case EM_CMD_FILE_SET_DONE:
+        _file_set_done(app);
+        break;
+      case EM_CMD_FILE_CLOSE:
+        _file_close(app);
+        break;
+      case EM_CMD_PLAY:
+        _play(app);
+        break;
+      case EM_CMD_STOP:
+        _stop(app);
+        break;
+      case EM_CMD_POSITION_SET:
+        _position_set(app);
+        break;
+      case EM_CMD_SPEED_SET:
+        _speed_set(app);
+        break;
+      case EM_CMD_AUDIO_MUTE_SET:
+        _mute_set(app);
+        break;
+      case EM_CMD_VOLUME_SET:
+        _volume_set(app);
+        break;
+      case EM_CMD_AUDIO_TRACK_SET:
+        _audio_track_set(app);
+        break;
+   };
+}
+
+static void
+_send_track_info(libvlc_media_player_t *mp)
+{
+   int track_count, current;
+   libvlc_track_description_t *desc;
+
+   current = libvlc_audio_get_track(mp);
+   track_count = libvlc_audio_get_track_count(mp);
+   desc = libvlc_audio_get_track_description(mp);
+
+   _send_cmd_start(EM_RESULT_AUDIO_TRACK_INFO);
+   SEND_CMD_PARAM(current);
+   SEND_CMD_PARAM(track_count);
+   while (desc)
+     {
+       int tid = desc->i_id;
+       const char *name = desc->psz_name;
+       SEND_CMD_PARAM(tid);
+       _send_cmd_str(name);
+       desc = desc->p_next;
+     }
+   _send_cmd_finish();
+}
+
+static void
+_position_changed(struct _App *app)
+{
+   if (!app->opening)
+     return;
+
+   /* sending size info only once */
+   int r, w, h;
+   r = libvlc_video_get_size(app->mp, 0, &w, &h);
+   if (r < 0)
+     return;
+   _send_resize(w, h);
+
+   /* sending audio track info */
+   // _send_track_info(app->mp);
+
+   libvlc_media_player_stop(app->mp);
+}
+
+static void
+_process_thread_events(struct _App *app)
+{
+   int event;
+   size_t size;
+
+   size = read(app->fd_read, &event, sizeof(event));
+   if (size != sizeof(event))
+     {
+       fprintf(stderr, "player: problem when reading thread event. size = %zd\n", size);
+       return;
+     }
+
+   switch (event) {
+      case EM_THREAD_POSITION_CHANGED:
+        _position_changed(app);
+        break;
+   }
+}
+
+int
+main(int argc, const char *argv[])
+{
+   struct _App app;
+   Emotion_Generic_Video_Shared *vs;
+   struct pollfd fds[2]; // watching on 2 file descriptors
+   int tpipe[2]; // pipe for comunicating events from threads
+   char shmname[256];
+   char cwidth[64], cheight[64], cpitch[64], chroma[64];
+   char buf[64];
+   const char *vlc_argv[] =
+     {
+        "--quiet",
+       "--vout",
+       "vmem",
+       "--vmem-width",
+       cwidth,
+       "--vmem-height",
+       cheight,
+       "--vmem-pitch",
+       cpitch,
+       "--vmem-chroma",
+       chroma
+     };
+
+   int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);
+   snprintf(cwidth, sizeof(cwidth), "%d", DEFAULTWIDTH);
+   snprintf(cheight, sizeof(cheight), "%d", DEFAULTHEIGHT);
+   snprintf(cpitch, sizeof(cpitch), "%d", DEFAULTWIDTH * 4);
+   snprintf(chroma, sizeof(chroma), "RV32");
+
+   app.libvlc = libvlc_new(vlc_argc, vlc_argv);
+   app.mp = NULL;
+   app.filename = NULL;
+   app.w = 0;
+   app.h = 0;
+   app.size_sent = 0;
+   app.opening = 0;
+   app.playing = 0;
+   app.closing = 0;
+
+   if (_em_cmd_read() != EM_CMD_INIT)
+     {
+       fprintf(stderr, "player: wrong init command!\n");
+       return -1;
+     }
+
+   int size;
+   _em_read_safe(STDIN_FILENO, &size, sizeof(size));
+   _em_read_safe(STDIN_FILENO, buf, size);
+   app.shmname = strdup(buf);
+
+   _send_cmd(EM_RESULT_INIT);
+
+   pipe(tpipe);
+   app.fd_read = tpipe[0];
+   app.fd_write = tpipe[1];
+   fds[0].fd = STDIN_FILENO;
+   fds[0].events = POLLIN;
+   fds[1].fd = app.fd_read;
+   fds[1].events = POLLIN;
+
+   while (1)
+     {
+       int r;
+
+       r = poll(fds, 2, 30);
+       if (r == 0)
+         continue;
+       else if (r < 0)
+         {
+            fprintf(stderr, "an error ocurred on poll().\n");
+            break;
+         }
+
+       if (fds[0].revents & POLLIN)
+         _process_emotion_commands(&app);
+       if (fds[1].revents & POLLIN)
+         _process_thread_events(&app);
+     }
+
+   libvlc_release(app.libvlc);
+
+
+   return 0;
+}
+#undef SEND_CMD_PARAM
index 65ea4c7..0961fed 100644 (file)
@@ -41,6 +41,7 @@ enum _Emotion_Format
 
 struct _Emotion_Module_Options
 {
+   const char *player;
    Eina_Bool no_video : 1;
    Eina_Bool no_audio : 1;
 };
index 87f58d1..33ed128 100644 (file)
@@ -124,7 +124,8 @@ static int _log_domain = -1;
 static const char *_backend_priority[] = {
   "gstreamer",
   "xine",
-  "vlc"
+  "vlc",
+  "generic"
 };
 
 static const char SIG_FRAME_DECODE[] = "frame_decode";
@@ -312,6 +313,9 @@ emotion_object_module_option_set(Evas_Object *obj, const char *opt, const char *
 
    E_SMART_OBJ_GET(sd, obj, E_OBJ_NAME);
    if ((!opt) || (!val)) return;
+
+   if (!strcmp(opt, "player"))
+     eina_stringshare_replace(&sd->module_options.player, val);
 }
 
 EAPI Eina_Bool
index 1af8c8c..faaa146 100644 (file)
@@ -1,4 +1,4 @@
 
 MAINTAINERCLEANFILES = Makefile.in
 
-SUBDIRS = xine gstreamer
+SUBDIRS = xine gstreamer generic
diff --git a/src/modules/generic/Emotion_Generic_Plugin.h b/src/modules/generic/Emotion_Generic_Plugin.h
new file mode 100644 (file)
index 0000000..b85f647
--- /dev/null
@@ -0,0 +1,114 @@
+#ifndef EMOTION_GENERIC_PLUGIN_H
+#define EMOTION_GENERIC_PLUGIN_H
+
+#include <semaphore.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DEFAULTWIDTH           320
+#define DEFAULTHEIGHT          240
+#define DEFAULTPITCH           4
+
+typedef enum _Emotion_Generic_Cmd Emotion_Generic_Cmd;
+typedef enum _Emotion_Generic_Result Emotion_Generic_Result;
+typedef struct _Emotion_Generic_Video_Frame Emotion_Generic_Video_Frame;
+typedef struct _Emotion_Generic_Video_Shared Emotion_Generic_Video_Shared;
+
+enum _Emotion_Generic_Cmd
+{
+   EM_CMD_INIT, // param: shared memory identifier (string)
+   EM_CMD_PLAY, // param: position (float)
+   EM_CMD_STOP, // param: none
+   EM_CMD_FILE_SET, // param: filename (string)
+   EM_CMD_FILE_SET_DONE, // param: none
+   EM_CMD_FILE_CLOSE, // param: none
+   EM_CMD_POSITION_SET, // param: position (float)
+   EM_CMD_SPEED_SET, // param: speed (float)
+   EM_CMD_AUDIO_MUTE_SET, // param: muted (int)
+   EM_CMD_VOLUME_SET, // param: volume (float)
+   EM_CMD_AUDIO_TRACK_SET, // param: track id (int)
+   EM_CMD_LAST
+};
+
+enum _Emotion_Generic_Result
+{
+   EM_RESULT_INIT, // param: none
+   EM_RESULT_FILE_SET, // param: none
+   EM_RESULT_FILE_SET_DONE, // param: none
+   EM_RESULT_PLAYBACK_STOPPED, // param: none
+   EM_RESULT_FILE_CLOSE, // param: none
+   EM_RESULT_FRAME_NEW, // param: none
+   EM_RESULT_FRAME_SIZE, // param: int, int (width, height)
+   EM_RESULT_LENGTH_CHANGED, // param: float
+   EM_RESULT_POSITION_CHANGED, // param: float
+   EM_RESULT_SEEKABLE_CHANGED, // param: int
+   EM_RESULT_AUDIO_TRACK_INFO, // param: current track, track count, track_id, track_name, track_id2, track_name2, ...
+                              // (int, int, int, string, int, string, ...)
+   EM_RESULT_LAST
+};
+
+/* structure for frames 2 buffers to keep integrity */
+struct _Emotion_Generic_Video_Frame
+{
+   unsigned char *frames[3];
+};
+
+/* structure for frames 2 buffers to keep integrity */
+struct _Emotion_Generic_Video_Shared
+{
+   int size;
+   int width;
+   int height;
+   int pitch;
+   /**
+    * - "emotion" is the frame from where the Emotion process is reading pixels.
+    *   The player shouldn't touch this frame.
+    * - "player" is the frame where the slayer process is writing pixels.
+    *   The emotion process shouldn't touch this frame.
+    * - "last" is the last frame that was rendered by the player. Emotion will
+    *   use this frame the next time it will fetch pixels to Evas.
+    * - "next" is the unused frame. The player currently using the "player"
+    *   should, after finishing this frame, set "last" to "player", and "player"
+    *   to "next", and finally "next" to "last" so this operation can be done
+    *   many times in case that Emotion does not request pixels fast enough.
+    */
+   struct {
+       int emotion;
+       int player;
+       int last;
+       int next;
+   } frame;
+   sem_t lock;
+};
+
+inline void
+emotion_generic_shm_get(const char *shmname, Emotion_Generic_Video_Shared **vs, Emotion_Generic_Video_Frame *vf)
+{
+   int shmfd = -1;
+   int size;
+   Emotion_Generic_Video_Shared *t_vs;
+
+   shmfd = shm_open(shmname, O_RDWR, 0777);
+
+   t_vs = mmap(NULL, sizeof(*t_vs), PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
+   size = t_vs->size;
+   munmap(t_vs, sizeof(*t_vs));
+   t_vs = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
+
+   vf->frames[0] = (char *)t_vs + sizeof(*t_vs);
+   vf->frames[1] = (char *)t_vs + sizeof(*t_vs) + t_vs->height * t_vs->width * t_vs->pitch;
+   vf->frames[2] = (char *)t_vs + sizeof(*t_vs) + 2 * t_vs->height * t_vs->width * t_vs->pitch;
+
+   *vs = t_vs;
+}
+
+inline void
+emotion_generic_shm_free(Emotion_Generic_Video_Shared *vs)
+{
+   munmap(vs, vs->size);
+}
+
+#endif // EMOTION_GENERIC_PLUGIN_H
diff --git a/src/modules/generic/Makefile.am b/src/modules/generic/Makefile.am
new file mode 100644 (file)
index 0000000..0e64559
--- /dev/null
@@ -0,0 +1,31 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I$(top_srcdir) \
+-I$(top_srcdir)/src/lib \
+-I$(top_srcdir)/src/modules \
+-DPACKAGE_BIN_DIR=\"$(bindir)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+@EMOTION_CFLAGS@ \
+@EMOTION_CPPFLAGS@ \
+@EFL_EMOTION_BUILD@
+
+if EMOTION_BUILD_GENERIC
+if !EMOTION_STATIC_BUILD_GENERIC
+
+pkgdir = $(libdir)/emotion
+
+pkg_LTLIBRARIES = em_generic.la
+includes_HEADERS = Emotion_Generic_Plugin.h
+includesdir = $(includedir)/emotion-@VMAJ@
+noinst_HEADERS = emotion_generic.h
+
+em_generic_la_SOURCES = emotion_generic.c
+em_generic_la_LIBADD = $(top_builddir)/src/lib/libemotion.la
+em_generic_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version
+em_generic_la_LIBTOOLFLAGS = --tag=disable-static
+em_generic_la_DEPENDENCIES = emotion_generic.h Emotion_Generic_Plugin.h
+
+endif
+endif
diff --git a/src/modules/generic/README b/src/modules/generic/README
new file mode 100644 (file)
index 0000000..9d4d703
--- /dev/null
@@ -0,0 +1,79 @@
+Generic - emotion backend
+=========================
+
+This generic player backend executes a separate player in another
+process. It receives the bytes to be drawn on the emotion object through
+a shared memory, and communicates with the player through a pipe, using
+the player standard input/output.
+
+The player must communicate with emotion using the defined commands
+specified in the Emotion_Generic_Plugin.h. It doesn't need to link
+against emotion, just include this file for easier implementation.
+
+
+How does it work?
+=================
+
+When the module is initialized for an emotion object, it starts another process
+that runs the specified player. The player command line is specified using:
+
+   emotion_object_module_option_set(object, "player", <command>);
+
+A player using libvlc is being provided now, and the generic module internally
+checks if the command given was "vlc", in which case it will use this provided
+vlc player.
+
+When a file is set to this object, it will send the file name to the player, and
+expect an answer that will tell that the player already decoded a bit of the
+file, and the video size is already set on the module, so it can allocate a
+shared memory with correct size.
+
+The module then allocates the memory, sends a message to the player and expect
+an answer. After this last answer, the "open_done" signal is sent and the module
+knows that it is ready for playing. Commands sent before the module being ready
+are now applied (and play is resumed if necessary).
+
+During this setup stage, info about the file set will be stored in the module,
+so commands like meta data get, length get and so will be available to sync
+calls like emotion_object_play_length_get();
+
+If the player dies for any reason, a "decode_stop" signal is sent (should change
+to something more like an error signal), and if play is called again, it will be
+restarted. The playback should start from the same point it was before the
+player crashed, if the player supports seek on the current media format).
+
+TODO
+====
+
+ - Provide better description for commands;
+ - Explain in details the communication emotion <-> player;
+ - Make more common functions for players;
+ - (maybe) add support for named pipes, so we don't rely on standard in/out
+ for communication;
+ - Add a detection on the player to know that the emotion process died (so it
+ can just exit);
+ - shmname should contain the child pid too;
+ - better names for commands, maybe add namespace everywhere;
+
+
+questions
+=========
+
+ - Using semaphores to lock the critical region between process, and pthread
+   mutexes for the threads inside the player. Should move to only one type
+   (semphores or mutexes)?
+ - There are 2 inline functions insde Emotion_Generic_Plugin.h to make it easier
+   for the player to get the shared memory correctly. Any problem with this?
+   Would be good to add more functions/macros to make common tasks like
+   parsing commands there too?
+ - Should move players to another project (outside of emotion)?
+
+
+problems
+========
+ - file_set has some critical time when file is not set yet when we can't call
+ some functions (I think only another file_set now);
+ - comunication player -> emotion depends on '\n' to delimitate commands, will
+ remove this soon (fix this urgently!);
+ - need to implement missing APIs;
+
diff --git a/src/modules/generic/emotion_generic.c b/src/modules/generic/emotion_generic.c
new file mode 100644 (file)
index 0000000..ef6b9eb
--- /dev/null
@@ -0,0 +1,1123 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <Eina.h>
+#include <Evas.h>
+
+#include "Emotion.h"
+#include "emotion_private.h"
+#include "emotion_generic.h"
+
+struct _default_players {
+   const char *name;
+   const char *cmdline;
+};
+
+int _emotion_generic_log_domain = -1;
+
+static struct _default_players players[] = {
+       { "vlc", "em_generic_vlc" },
+       { NULL, NULL }
+};
+
+static const char *
+_get_player(const char *name)
+{
+   int i;
+
+   if (!name)
+     return players[0].cmdline;
+
+   for (i = 0; players[i].name; i++)
+     {
+       if (!strcmp(players[i].name, name))
+         return players[i].cmdline;
+     }
+
+   return name;
+}
+
+static void
+_player_send_cmd(Emotion_Generic_Video *ev, int cmd)
+{
+   if (cmd >= EM_CMD_LAST)
+     {
+       ERR("invalid command to player.");
+       return;
+     }
+   ecore_exe_send(ev->player.exe, &cmd, sizeof(cmd));
+}
+
+static void
+_player_send_int(Emotion_Generic_Video *ev, int number)
+{
+   ecore_exe_send(ev->player.exe, &number, sizeof(number));
+}
+
+static void
+_player_send_float(Emotion_Generic_Video *ev, float number)
+{
+   ecore_exe_send(ev->player.exe, &number, sizeof(number));
+}
+
+static void
+_player_send_str(Emotion_Generic_Video *ev, const char *str, Eina_Bool stringshared)
+{
+   int len;
+
+   if (stringshared)
+     len = eina_stringshare_strlen(str) + 1;
+   else
+     len = strlen(str) + 1;
+   ecore_exe_send(ev->player.exe, &len, sizeof(len));
+   ecore_exe_send(ev->player.exe, str, len);
+}
+
+static Eina_Bool
+_create_shm_data(Emotion_Generic_Video *ev, const char *shmname)
+{
+   int shmfd;
+   int npages;
+   size_t size;
+   Emotion_Generic_Video_Shared *vs;
+
+   shmfd = shm_open(shmname, O_CREAT | O_RDWR | O_TRUNC, 0777);
+   size = 3 * (ev->w * ev->h * DEFAULTPITCH) + sizeof(*vs);
+
+   npages = (int)(size / getpagesize()) + 1;
+   size = npages * getpagesize();
+   char *buf = malloc(size);
+
+   if (ftruncate(shmfd, size))
+     {
+       ERR("error when allocating shared memory (size = %zd): "
+           "%s", size, strerror(errno));
+       return EINA_FALSE;
+     }
+   vs = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
+   if (vs == MAP_FAILED)
+     {
+       ERR("error when mapping shared memory.\n");
+       return EINA_FALSE;
+     }
+
+   memcmp(vs, buf, size);
+
+   vs->size = size;
+   vs->width = ev->w;
+   vs->height = ev->h;
+   vs->pitch = DEFAULTPITCH;
+   vs->frame.emotion = 0;
+   vs->frame.player = 1;
+   vs->frame.last = 2;
+   vs->frame.next = 2;
+   sem_init(&vs->lock, 1, 1);
+   ev->frame.frames[0] = (char *)vs + sizeof(*vs);
+   ev->frame.frames[1] = (char *)vs + sizeof(*vs) + vs->height * vs->width * vs->pitch;
+   ev->frame.frames[2] = (char *)vs + sizeof(*vs) + 2 * vs->height * vs->width * vs->pitch;
+
+   if (ev->shared)
+     munmap(ev->shared, ev->shared->size);
+   ev->shared = vs;
+
+   return EINA_TRUE;
+}
+
+static void
+_player_new_frame(Emotion_Generic_Video *ev)
+{
+   if (!ev->drop++)
+     _emotion_frame_new(ev->obj);
+}
+
+static void
+_player_file_set_done(Emotion_Generic_Video *ev)
+{
+   if (!_create_shm_data(ev, ev->shmname))
+     {
+       ERR("could not create shared memory.");
+       return;
+     }
+   _player_send_cmd(ev, EM_CMD_FILE_SET_DONE);
+}
+
+static void
+_file_open(Emotion_Generic_Video *ev)
+{
+   INF("Opening file: %s", ev->filename);
+   ev->w = DEFAULTWIDTH;
+   ev->h = DEFAULTHEIGHT;
+   ev->ratio = (double)DEFAULTWIDTH / DEFAULTHEIGHT;
+   ev->speed = 1.0;
+   ev->len = 0;
+   ev->drop = 0;
+
+   if (!ev->ready)
+     return;
+   _player_send_cmd(ev, EM_CMD_FILE_SET);
+   _player_send_str(ev, ev->filename, EINA_TRUE);
+}
+
+static void
+_player_ready(Emotion_Generic_Video *ev)
+{
+   INF("received: player ready.");
+
+   ev->initializing = EINA_FALSE;
+   ev->ready = EINA_TRUE;
+
+   if (!ev->filename)
+     return;
+
+   _file_open(ev);
+}
+
+#define RCV_CMD_PARAM(src, param) \
+   memcpy(&(param), (src), sizeof((param))); \
+   (src) = (char *)(src) + sizeof((param));
+
+#define RCV_CMD_STR(src, buf, len) \
+   RCV_CMD_PARAM((src), (len)); \
+   memcpy((buf), (src), (len)); \
+   (src) = (char *)(src) + len;
+
+static int
+_player_int_read(Emotion_Generic_Video *ev, void **data)
+{
+   int number;
+   memcpy(&number, *data, sizeof(number));
+   *data = (char *)(*data) + sizeof(number);
+
+   return number;
+}
+
+static void
+_player_frame_resize(Emotion_Generic_Video *ev, void *line)
+{
+   int w, h;
+   RCV_CMD_PARAM(line, w);
+   RCV_CMD_PARAM(line, h);
+
+   INF("received frame resize: %dx%d", w, h);
+   ev->w = w;
+   ev->h = h;
+   ev->ratio = (float)w / h;
+
+   if (ev->opening)
+     return;
+
+   _emotion_frame_resize(ev->obj, ev->w, ev->h, ev->ratio);
+}
+
+static void
+_player_length_changed(Emotion_Generic_Video *ev, void *line)
+{
+   float length;
+   RCV_CMD_PARAM(line, length);
+
+   INF("received length changed: %0.3f", length);
+
+   ev->len = length;
+   _emotion_video_pos_update(ev->obj, ev->pos, ev->len);
+}
+
+static void
+_player_position_changed(Emotion_Generic_Video *ev, void *line)
+{
+   float position;
+   RCV_CMD_PARAM(line, position);
+
+   INF("received position changed: %0.3f", position);
+
+   ev->pos = position;
+   _emotion_video_pos_update(ev->obj, ev->pos, ev->len);
+
+   if (ev->len == 0)
+     return;
+
+   float progress = ev->pos / ev->len;
+   char buf[16];
+   snprintf(buf, sizeof(buf), "%0.1f%%", progress * 100);
+
+   _emotion_progress_set(ev->obj, buf, progress);
+}
+
+static void
+_player_seekable_changed(Emotion_Generic_Video *ev, void *line)
+{
+   int seekable;
+   RCV_CMD_PARAM(line, seekable);
+
+   INF("received seekable changed: %d", seekable);
+
+   seekable = !!seekable;
+
+   ev->seekable = seekable;
+}
+
+static void
+_player_volume(Emotion_Generic_Video *ev, void *line)
+{
+   float vol, oldvol;
+   RCV_CMD_PARAM(line, vol);
+
+   INF("received volume: %0.3f", vol);
+
+   oldvol = ev->volume;
+   ev->volume = vol;
+   if (vol != oldvol && !ev->opening)
+     _emotion_audio_level_change(ev->obj);
+}
+
+static void
+_player_audio_mute(Emotion_Generic_Video *ev, void *line)
+{
+   int mute;
+   RCV_CMD_PARAM(line, mute);
+
+   INF("received audio mute: %d", mute);
+
+   ev->audio_mute = !!mute;
+}
+
+static void
+_audio_channels_free(Emotion_Generic_Video *ev)
+{
+   int i;
+   for (i = 0; i < ev->audio_channels_count; i++)
+     eina_stringshare_del(ev->audio_channels[i].name);
+   free(ev->audio_channels);
+   ev->audio_channels_count = 0;
+}
+
+static void
+_player_audio_tracks_info(Emotion_Generic_Video *ev, void *line)
+{
+   int track_current, tracks_count;
+   int i;
+
+   if (ev->audio_channels_count)
+     _audio_channels_free(ev);
+
+   RCV_CMD_PARAM(line, track_current);
+   RCV_CMD_PARAM(line, tracks_count);
+   INF("video with %d audio tracks (current = %d):", tracks_count, track_current);
+   ev->audio_channels = calloc(
+      tracks_count, sizeof(Emotion_Generic_Audio_Channel));
+   ev->audio_channels_count = tracks_count;
+   ev->audio_channel_current = track_current;
+   for (i = 0; i < tracks_count; i++)
+     {
+       int tid, len;
+       char buf[PATH_MAX];
+       RCV_CMD_PARAM(line, tid);
+       RCV_CMD_STR(line, buf, len);
+       ev->audio_channels[i].id = tid;
+       ev->audio_channels[i].name = eina_stringshare_add_length(buf, len);
+       INF("\t%d: %s", tid, buf);
+     }
+}
+
+static void
+_player_file_closed(Emotion_Generic_Video *ev)
+{
+   INF("Closed previous file.");
+   sem_destroy(&ev->shared->lock);
+
+   ev->closing = EINA_FALSE;
+
+   if (ev->opening)
+     _file_open(ev);
+}
+
+static void
+_player_open_done(Emotion_Generic_Video *ev)
+{
+   ev->opening = EINA_FALSE;
+   shm_unlink(ev->shmname);
+   _emotion_open_done(ev->obj);
+
+   if (ev->play)
+     {
+       _player_send_cmd(ev, EM_CMD_PLAY);
+       _player_send_float(ev, ev->pos);
+     }
+
+   INF("Open done");
+}
+
+static void
+_player_read_cmd(Emotion_Generic_Video *ev, void *line, int size)
+{
+   int type;
+   RCV_CMD_PARAM(line, type);
+
+   switch (type) {
+      case EM_RESULT_INIT:
+        _player_ready(ev);
+        break;
+      case EM_RESULT_FRAME_NEW:
+        _player_new_frame(ev);
+        break;
+      case EM_RESULT_FILE_SET:
+        _player_file_set_done(ev);
+        break;
+      case EM_RESULT_FILE_SET_DONE:
+        _player_open_done(ev);
+        break;
+      case EM_RESULT_FILE_CLOSE:
+        _player_file_closed(ev);
+        break;
+      case EM_RESULT_PLAYBACK_STOPPED:
+        _emotion_playback_finished(ev->obj);
+        break;
+      case EM_RESULT_FRAME_SIZE:
+        _player_frame_resize(ev, line);
+        break;
+      case EM_RESULT_LENGTH_CHANGED:
+        _player_length_changed(ev, line);
+        break;
+      case EM_RESULT_POSITION_CHANGED:
+        _player_position_changed(ev, line);
+        break;
+      case EM_RESULT_SEEKABLE_CHANGED:
+        _player_seekable_changed(ev, line);
+        break;
+      case EM_RESULT_AUDIO_TRACK_INFO:
+        _player_audio_tracks_info(ev, line);
+        break;
+      default:
+        WRN("received wrong command: %d", type);
+   };
+}
+
+#undef RCV_CMD_PARAM
+
+static Eina_Bool
+_player_data_cb(void *data, int type __UNUSED__, void *event)
+{
+   Ecore_Exe_Event_Data *ev = event;
+   Emotion_Generic_Video *evideo = data;
+   int psize;
+   char *pdata;
+   int i;
+
+   if (ev->exe != evideo->player.exe)
+     {
+        ERR("slave != ev->exe");
+       return ECORE_CALLBACK_DONE;
+     }
+
+   if (ev->size < 4)
+     {
+       ERR("invalid command: missing bytes.");
+       return ECORE_CALLBACK_DONE;
+     }
+
+   for (i = 0; ev->lines[i].line; i++)
+     _player_read_cmd(evideo, ev->lines[i].line, ev->lines[i].size);
+
+   return ECORE_CALLBACK_DONE;
+}
+
+static Eina_Bool
+_player_add_cb(void *data, int type __UNUSED__, void *event)
+{
+   Ecore_Exe_Event_Add *event_add = event;
+   Ecore_Exe *player = event_add->exe;
+   Emotion_Generic_Video *ev = data;
+
+   if (ev->player.exe != player)
+     {
+       ERR("ev->player != player.");
+       return ECORE_CALLBACK_DONE;
+     }
+
+   _player_send_cmd(ev, EM_CMD_INIT);
+   _player_send_str(ev, ev->shmname, EINA_TRUE);
+
+   return ECORE_CALLBACK_DONE;
+}
+
+static Eina_Bool
+_player_del_cb(void *data, int type __UNUSED__, void *event)
+{
+   Ecore_Exe_Event_Del *event_del = event;
+   Ecore_Exe *player = event_del->exe;
+   Emotion_Generic_Video *ev = data;
+   ERR("player died.");
+
+   ev->player.exe = NULL;
+   ev->ready = EINA_FALSE;
+   _emotion_decode_stop(ev->obj);
+
+   return ECORE_CALLBACK_DONE;
+}
+
+static Eina_Bool
+_fork_and_exec(Evas_Object *obj, Emotion_Generic_Video *ev)
+{
+   char shmname[256];
+   struct timeval tv;
+
+   gettimeofday(&tv, NULL);
+   snprintf(shmname, sizeof(shmname), "/em-generic-shm_%d_%d",
+           (int)tv.tv_sec, (int)tv.tv_usec);
+
+   ev->shmname = eina_stringshare_add(shmname);
+
+   ev->player_add = ecore_event_handler_add(
+      ECORE_EXE_EVENT_ADD, _player_add_cb, ev);
+   ev->player_del = ecore_event_handler_add(
+      ECORE_EXE_EVENT_DEL, _player_del_cb, ev);
+   ev->player_data = ecore_event_handler_add(
+      ECORE_EXE_EVENT_DATA, _player_data_cb, ev);
+
+   ev->player.exe = ecore_exe_pipe_run(
+      ev->cmdline,
+      ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_WRITE |
+      ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_NOT_LEADER,
+      ev);
+
+   if (!ev->player.exe)
+     {
+        ERR("could not start player.");
+        return EINA_FALSE;
+     }
+
+   ev->initializing = EINA_TRUE;
+
+   return EINA_TRUE;
+}
+
+static unsigned char
+em_init(Evas_Object *obj, void **emotion_video, Emotion_Module_Options *opt)
+{
+   Emotion_Generic_Video *ev;
+
+   if (!emotion_video) return 0;
+
+   ev = (Emotion_Generic_Video *)calloc(1, sizeof(*ev));
+   if (!ev) return 0;
+
+   ev->speed = 1.0;
+   ev->volume = 0.5;
+   ev->audio_mute = EINA_FALSE;
+
+   ev->obj = obj;
+   ev->cmdline = eina_stringshare_add(_get_player(opt->player));
+   *emotion_video = ev;
+
+   return _fork_and_exec(obj, ev);
+}
+
+static int
+em_shutdown(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+
+   if (!ev) return 0;
+
+   if (ev->player.exe)
+     {
+       ecore_exe_terminate(ev->player.exe);
+       ecore_exe_free(ev->player.exe);
+       ev->player.exe = NULL;
+     }
+
+   if (ev->shared)
+     munmap(ev->shared, ev->shared->size);
+
+   _audio_channels_free(ev);
+
+   eina_stringshare_del(ev->cmdline);
+   eina_stringshare_del(ev->shmname);
+
+   ecore_event_handler_del(ev->player_add);
+   ecore_event_handler_del(ev->player_data);
+   ecore_event_handler_del(ev->player_del);
+
+   return 1;
+}
+
+static unsigned char
+em_file_open(const char *file, Evas_Object *obj, void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   INF("file set: %s", file);
+   if (!ev) return 0;
+
+   ev->pos = 0;
+   ev->opening = EINA_TRUE;
+
+   eina_stringshare_replace(&ev->filename, file);
+
+   if (!ev->closing)
+     _file_open(ev);
+
+   return 1;
+}
+
+static void
+em_file_close(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+
+   if (!ev) return;
+   INF("file close: %s", ev->filename);
+
+   if (!ev->filename)
+     return;
+
+   _player_send_cmd(ev, EM_CMD_FILE_CLOSE);
+   ev->closing = EINA_TRUE;
+}
+
+static Emotion_Format
+em_format_get(void *ef)
+{
+   return EMOTION_FORMAT_BGRA;
+}
+
+static void
+em_video_data_size_get(void *data, int *w, int *h)
+{
+   Emotion_Generic_Video *ev = data;
+
+   if (!ev) return;
+   if (w) *w = ev->w;
+   if (h) *h = ev->h;
+}
+
+static void
+em_play(void *data, double pos)
+{
+   Emotion_Generic_Video *ev = data;
+
+   if (!ev)
+     return;
+
+   ev->play = EINA_TRUE;
+   INF("play: %0.3f", pos);
+
+   if (ev->initializing || ev->opening)
+     return;
+
+   if (ev->ready)
+     {
+       _player_send_cmd(ev, EM_CMD_PLAY);
+       _player_send_float(ev, ev->pos);
+       return;
+     }
+
+   ev->player.exe = ecore_exe_pipe_run(
+      ev->cmdline,
+      ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_WRITE |
+      ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_NOT_LEADER,
+      ev);
+
+   if (!ev->player.exe)
+     ERR("could not start player.");
+}
+
+static void
+em_stop(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+
+   if (!ev)
+     return;
+
+   ev->play = EINA_FALSE;
+
+   if (!ev->ready)
+     return;
+
+   _player_send_cmd(ev, EM_CMD_STOP);
+   _emotion_decode_stop(ev->obj);
+}
+
+static void
+em_size_get(void *data, int *w, int *h)
+{
+   Emotion_Generic_Video *ev = data;
+   if(w) *w = ev->w;
+   if(h) *h = ev->h;
+}
+
+static void
+em_pos_set(void *data, double pos)
+{
+   Emotion_Generic_Video *ev = data;
+   float position = pos;
+   _player_send_cmd(ev, EM_CMD_POSITION_SET);
+   _player_send_float(ev, position);
+   _emotion_seek_done(ev->obj);
+}
+
+static double
+em_len_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->len;
+}
+
+static int
+em_fps_num_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return (int)(ev->fps * 1000.0);
+}
+
+static int
+em_fps_den_get(void *ef)
+{
+   return 1000;
+}
+
+static double
+em_fps_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->fps;
+}
+
+static double
+em_pos_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->pos;
+}
+
+static void
+em_vis_set(void *ef, Emotion_Vis vis)
+{
+}
+
+static Emotion_Vis
+em_vis_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->vis;
+}
+
+static Eina_Bool
+em_vis_supported(void *ef, Emotion_Vis vis)
+{
+   return EINA_FALSE;
+}
+
+static double
+em_ratio_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->ratio;
+}
+
+static int em_video_handled(void *ef)
+{
+   fprintf(stderr, "video handled!\n");
+   return 1;
+}
+
+static int em_audio_handled(void *ef)
+{
+   fprintf(stderr, "audio handled!\n");
+   return 1;
+}
+
+static int em_seekable(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->seekable;
+}
+
+static void em_frame_done(void *ef)
+{
+}
+
+static int
+em_yuv_rows_get(void *data, int w, int h, unsigned char **yrows, unsigned char **urows, unsigned char **vrows)
+{
+   Emotion_Generic_Video *ev;
+   volatile Emotion_Generic_Video_Shared *vs;
+   return 0;
+}
+
+static int
+em_bgra_data_get(void *data, unsigned char **bgra_data)
+{
+   Emotion_Generic_Video *ev = data;
+
+   if (!ev || ev->opening || ev->closing)
+     return 0;
+
+   // lock frame here
+   sem_wait(&ev->shared->lock);
+
+   // send current frame to emotion
+   if (ev->shared->frame.emotion != ev->shared->frame.last)
+     {
+       ev->shared->frame.next = ev->shared->frame.emotion;
+       ev->shared->frame.emotion = ev->shared->frame.last;
+     }
+   *bgra_data = ev->frame.frames[ev->shared->frame.emotion];
+
+   // unlock frame here
+   sem_post(&ev->shared->lock);
+   ev->drop = 0;
+
+   return 1;
+}
+
+static void
+em_event_feed(void *ef, int event)
+{
+}
+
+static void
+em_event_mouse_button_feed(void *ef, int button, int x, int y)
+{
+}
+
+static void
+em_event_mouse_move_feed(void *ef, int x, int y)
+{
+}
+
+static int
+em_video_channel_count(void *ef)
+{
+   int ret  = 0;
+   return ret;
+}
+
+static void
+em_video_channel_set(void *ef, int channel)
+{
+}
+
+static int
+em_video_channel_get(void *ef)
+{
+   return 1;
+}
+
+static const char *
+em_video_channel_name_get(void *ef, int channel)
+{
+   return NULL;
+}
+
+static void
+em_video_channel_mute_set(void *ef, int mute)
+{
+}
+
+static int
+em_video_channel_mute_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->video_mute;
+}
+
+static int
+em_audio_channel_count(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->audio_channels_count;
+}
+
+static void
+em_audio_channel_set(void *data, int channel)
+{
+   Emotion_Generic_Video *ev = data;
+   int i;
+
+   for (i = 0; i < ev->audio_channels_count; i++)
+     {
+       if (ev->audio_channels[i].id == channel)
+         {
+            _player_send_cmd(ev, EM_CMD_AUDIO_TRACK_SET);
+            _player_send_int(ev, channel);
+            break;
+         }
+     }
+}
+
+static int
+em_audio_channel_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->audio_channel_current;
+}
+
+static const char *
+em_audio_channel_name_get(void *data, int channel)
+{
+   Emotion_Generic_Video *ev = data;
+   int i;
+
+   for (i = 0; i < ev->audio_channels_count; i++)
+     {
+       if (ev->audio_channels[i].id == channel)
+         return ev->audio_channels[i].name;
+     }
+
+   return NULL;
+}
+
+static void
+em_audio_channel_mute_set(void *data, int mute)
+{
+   Emotion_Generic_Video *ev = data;
+   _player_send_cmd(ev, EM_CMD_AUDIO_MUTE_SET);
+   _player_send_int(ev, mute);
+   ev->audio_mute = !!mute;
+}
+
+static int
+em_audio_channel_mute_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->audio_mute;
+}
+
+static void
+em_audio_channel_volume_set(void *data, double vol)
+{
+   Emotion_Generic_Video *ev = data;
+   float fvol;
+
+   if (vol > 1.0) vol = 1.0;
+   if (vol < 0.0) vol = 0.0;
+
+   fvol = vol;
+   _player_send_cmd(ev, EM_CMD_VOLUME_SET);
+   _player_send_float(ev, fvol);
+
+   ev->volume = vol;
+}
+
+static double
+em_audio_channel_volume_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return ev->volume;
+}
+
+static int
+em_spu_channel_count(void *ef)
+{
+   return 0;
+}
+
+static void
+em_spu_channel_set(void *ef, int channel)
+{
+}
+
+static int
+em_spu_channel_get(void *ef)
+{
+   int num = 0;
+   return num;
+}
+
+static const char *
+em_spu_channel_name_get(void *ef, int channel)
+{
+   return NULL;
+}
+
+static void
+em_spu_channel_mute_set(void *ef, int mute)
+{
+   return;
+}
+
+static int
+em_spu_channel_mute_get(void *ef)
+{
+   return 0;
+}
+
+static int
+em_chapter_count(void *ef)
+{
+   int num = 0;
+   return num;
+}
+
+static void
+em_chapter_set(void *ef, int chapter)
+{
+}
+
+static int
+em_chapter_get(void *ef)
+{
+   int num = 0;
+   return num;
+}
+
+static const char *
+em_chapter_name_get(void *ef, int chapter)
+{
+   return NULL;
+}
+
+static void
+em_speed_set(void *data, double speed)
+{
+   Emotion_Generic_Video *ev = data;
+   float rate = speed;
+
+   _player_send_cmd(ev, EM_CMD_SPEED_SET);
+   _player_send_float(ev, rate);
+
+   ev->speed = rate;
+}
+
+static double
+em_speed_get(void *data)
+{
+   Emotion_Generic_Video *ev = data;
+   return (double)ev->speed;
+}
+
+static int
+em_eject(void *ef)
+{
+   return 1;
+}
+
+static const char *
+em_meta_get(void *ef, int meta)
+{
+   char * meta_data = NULL;
+   return meta_data;
+}
+
+static Emotion_Video_Module em_module =
+{
+   em_init, /* init */
+   em_shutdown, /* shutdown */
+   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_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_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 /* handle */
+};
+
+static Eina_Bool
+module_open(Evas_Object *obj, const Emotion_Video_Module **module, void **video, Emotion_Module_Options *opt)
+{
+   if (!module)        {
+       return EINA_FALSE;
+   }
+
+   if (_emotion_generic_log_domain < 0)
+     {
+        eina_threads_init();
+        eina_log_threads_enable();
+        _emotion_generic_log_domain = eina_log_domain_register
+          ("emotion-generic", EINA_COLOR_LIGHTCYAN);
+        if (_emotion_generic_log_domain < 0)
+          {
+             EINA_LOG_CRIT("Could not register log domain 'emotion-generic'");
+             return EINA_FALSE;
+          }
+     }
+
+
+   if (!em_module.init(obj, video, opt))       {
+       return EINA_FALSE;
+   }
+
+   *module = &em_module;
+
+   return EINA_TRUE;
+}
+
+static void module_close(Emotion_Video_Module *module, void *video)
+{
+       em_module.shutdown(video);
+}
+
+
+Eina_Bool
+generic_module_init(void)
+{
+   return _emotion_module_register("generic", module_open, module_close);
+}
+
+void
+generic_module_shutdown(void)
+{
+   _emotion_module_unregister("generic");
+}
+
+#ifndef EMOTION_STATIC_BUILD_GENERIC
+
+EINA_MODULE_INIT(generic_module_init);
+EINA_MODULE_SHUTDOWN(generic_module_shutdown);
+
+#endif
+
diff --git a/src/modules/generic/emotion_generic.h b/src/modules/generic/emotion_generic.h
new file mode 100644 (file)
index 0000000..e068d58
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef EMOTION_GENERIC_H
+#define EMOTION_GENERIC_H
+
+#include "Emotion_Generic_Plugin.h"
+
+/* default values */
+
+typedef struct _Emotion_Generic_Video       Emotion_Generic_Video;
+typedef struct _Emotion_Generic_Player       Emotion_Generic_Player;
+typedef struct _Emotion_Generic_Audio_Channel Emotion_Generic_Audio_Channel;
+
+struct _Emotion_Generic_Player
+{
+   Ecore_Exe *exe;
+};
+
+struct _Emotion_Generic_Audio_Channel
+{
+   int id;
+   const char *name;
+};
+
+/* emotion/generic main structure */
+struct _Emotion_Generic_Video
+{
+   const char               *cmdline;
+   const char               *shmname;
+
+   Emotion_Generic_Player    player;
+   Ecore_Event_Handler      *player_add, *player_del, *player_data;
+   int                      drop;
+
+   const char               *filename;
+   volatile double          len;
+   volatile double           pos;
+   double                    fps;
+   double                    ratio;
+   int                       w, h;
+   Evas_Object               *obj;
+   Emotion_Generic_Video_Shared *shared;
+   Emotion_Generic_Video_Frame frame;
+   volatile int              spu_channel;
+   volatile int              video_channel;
+   volatile int              fq;
+   int                      volume;
+   float                    speed;
+   Emotion_Vis               vis;
+   Eina_Bool                initializing : 1;
+   Eina_Bool                ready : 1;
+   Eina_Bool                 play : 1;
+   Eina_Bool                 video_mute : 1;
+   Eina_Bool                 audio_mute : 1;
+   Eina_Bool                 spu_mute : 1;
+   Eina_Bool                seekable : 1;
+   volatile Eina_Bool        opening : 1;
+   volatile Eina_Bool        closing : 1;
+   int                      audio_channels_count;
+   int                      audio_channel_current;
+   struct _Emotion_Generic_Audio_Channel *audio_channels;
+};
+
+extern int _emotion_generic_log_domain;
+#define DBG(...) EINA_LOG_DOM_DBG(_emotion_generic_log_domain, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_emotion_generic_log_domain, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_emotion_generic_log_domain, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_emotion_generic_log_domain, __VA_ARGS__)
+#define CRITICAL(...) EINA_LOG_DOM_CRIT(_emotion_generic_log_domain, __VA_ARGS__)
+
+#endif
+