Nicolas Aguirre <aguirre.nicolas@gmail.com>
Sebastian Dransfeld <sd@tango.flipp.net>
Cedric Bail <cedric.bail@free.fr>
-
+Rafael Antognolli <antognolli@profusion.mobi>
AC_SUBST(VMAJ)
want_vlc="no"
+want_generic="yes"
case "$host_os" in
mingw* | cegcc*)
want_xine="no"
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
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
])
+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],
[
if ENABLE_EDJE_EXTERNAL
SUBDIRS += edje_external
endif
+
+if EMOTION_BUILD_GENERIC
+SUBDIRS += generic_players
+endif
SRCS = \
emotion_basic_example.c \
+ emotion_generic_example.c \
emotion_signals_example.c
EXTRA_DIST = $(SRCS)
if EFL_BUILD_EXAMPLES
pkglib_PROGRAMS += \
emotion_basic_example \
+ emotion_generic_example \
emotion_signals_example
endif
--- /dev/null
+#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;
+}
--- /dev/null
+
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS =
+
+if EMOTION_BUILD_VLC
+SUBDIRS += vlc
+endif
--- /dev/null
+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@
--- /dev/null
+#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
struct _Emotion_Module_Options
{
+ const char *player;
Eina_Bool no_video : 1;
Eina_Bool no_audio : 1;
};
static const char *_backend_priority[] = {
"gstreamer",
"xine",
- "vlc"
+ "vlc",
+ "generic"
};
static const char SIG_FRAME_DECODE[] = "frame_decode";
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
MAINTAINERCLEANFILES = Makefile.in
-SUBDIRS = xine gstreamer
+SUBDIRS = xine gstreamer generic
--- /dev/null
+#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
--- /dev/null
+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
--- /dev/null
+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;
+
--- /dev/null
+#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
+
--- /dev/null
+#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
+