From a7ae4566b8f911676009a04c4411e3aff87b2abc Mon Sep 17 00:00:00 2001 From: Rafael Antognolli Date: Thu, 1 Sep 2011 19:04:15 +0000 Subject: [PATCH] Add generic player backend (with vlc player). 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. SVN revision: 63062 --- legacy/emotion/AUTHORS | 2 +- legacy/emotion/configure.ac | 5 + legacy/emotion/m4/emotion_check.m4 | 22 + legacy/emotion/src/Makefile.am | 4 + legacy/emotion/src/examples/Makefile.am | 2 + .../src/examples/emotion_generic_example.c | 209 +++ .../emotion/src/generic_players/Makefile.am | 8 + .../src/generic_players/vlc/Makefile.am | 19 + .../generic_players/vlc/emotion_generic_vlc.c | 700 ++++++++++ legacy/emotion/src/lib/emotion_private.h | 1 + legacy/emotion/src/lib/emotion_smart.c | 6 +- legacy/emotion/src/modules/Makefile.am | 2 +- .../modules/generic/Emotion_Generic_Plugin.h | 114 ++ .../emotion/src/modules/generic/Makefile.am | 31 + legacy/emotion/src/modules/generic/README | 79 ++ .../src/modules/generic/emotion_generic.c | 1123 +++++++++++++++++ .../src/modules/generic/emotion_generic.h | 70 + 17 files changed, 2394 insertions(+), 3 deletions(-) create mode 100644 legacy/emotion/src/examples/emotion_generic_example.c create mode 100644 legacy/emotion/src/generic_players/Makefile.am create mode 100644 legacy/emotion/src/generic_players/vlc/Makefile.am create mode 100644 legacy/emotion/src/generic_players/vlc/emotion_generic_vlc.c create mode 100644 legacy/emotion/src/modules/generic/Emotion_Generic_Plugin.h create mode 100644 legacy/emotion/src/modules/generic/Makefile.am create mode 100644 legacy/emotion/src/modules/generic/README create mode 100644 legacy/emotion/src/modules/generic/emotion_generic.c create mode 100644 legacy/emotion/src/modules/generic/emotion_generic.h diff --git a/legacy/emotion/AUTHORS b/legacy/emotion/AUTHORS index 8ad8032b18..1ba92aea0c 100644 --- a/legacy/emotion/AUTHORS +++ b/legacy/emotion/AUTHORS @@ -3,4 +3,4 @@ Vincent Torri Nicolas Aguirre Sebastian Dransfeld Cedric Bail - +Rafael Antognolli diff --git a/legacy/emotion/configure.ac b/legacy/emotion/configure.ac index 3015c308fb..bc0b439d73 100644 --- a/legacy/emotion/configure.ac +++ b/legacy/emotion/configure.ac @@ -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 diff --git a/legacy/emotion/m4/emotion_check.m4 b/legacy/emotion/m4/emotion_check.m4 index ead676a602..0281f1d80b 100644 --- a/legacy/emotion/m4/emotion_check.m4 +++ b/legacy/emotion/m4/emotion_check.m4 @@ -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], [ diff --git a/legacy/emotion/src/Makefile.am b/legacy/emotion/src/Makefile.am index 8146837361..4082c3a67d 100644 --- a/legacy/emotion/src/Makefile.am +++ b/legacy/emotion/src/Makefile.am @@ -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 diff --git a/legacy/emotion/src/examples/Makefile.am b/legacy/emotion/src/examples/Makefile.am index 18b5921177..a19a1ae2c3 100644 --- a/legacy/emotion/src/examples/Makefile.am +++ b/legacy/emotion/src/examples/Makefile.am @@ -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/legacy/emotion/src/examples/emotion_generic_example.c b/legacy/emotion/src/examples/emotion_generic_example.c new file mode 100644 index 0000000000..2eb8eef244 --- /dev/null +++ b/legacy/emotion/src/examples/emotion_generic_example.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include +#include + +#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 \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/legacy/emotion/src/generic_players/Makefile.am b/legacy/emotion/src/generic_players/Makefile.am new file mode 100644 index 0000000000..ad31accc35 --- /dev/null +++ b/legacy/emotion/src/generic_players/Makefile.am @@ -0,0 +1,8 @@ + +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = + +if EMOTION_BUILD_VLC +SUBDIRS += vlc +endif diff --git a/legacy/emotion/src/generic_players/vlc/Makefile.am b/legacy/emotion/src/generic_players/vlc/Makefile.am new file mode 100644 index 0000000000..4089b11e24 --- /dev/null +++ b/legacy/emotion/src/generic_players/vlc/Makefile.am @@ -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/legacy/emotion/src/generic_players/vlc/emotion_generic_vlc.c b/legacy/emotion/src/generic_players/vlc/emotion_generic_vlc.c new file mode 100644 index 0000000000..773bbe9b7a --- /dev/null +++ b/legacy/emotion/src/generic_players/vlc/emotion_generic_vlc.c @@ -0,0 +1,700 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 diff --git a/legacy/emotion/src/lib/emotion_private.h b/legacy/emotion/src/lib/emotion_private.h index 65ea4c7ed4..0961fed962 100644 --- a/legacy/emotion/src/lib/emotion_private.h +++ b/legacy/emotion/src/lib/emotion_private.h @@ -41,6 +41,7 @@ enum _Emotion_Format struct _Emotion_Module_Options { + const char *player; Eina_Bool no_video : 1; Eina_Bool no_audio : 1; }; diff --git a/legacy/emotion/src/lib/emotion_smart.c b/legacy/emotion/src/lib/emotion_smart.c index 87f58d1775..33ed128aea 100644 --- a/legacy/emotion/src/lib/emotion_smart.c +++ b/legacy/emotion/src/lib/emotion_smart.c @@ -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 diff --git a/legacy/emotion/src/modules/Makefile.am b/legacy/emotion/src/modules/Makefile.am index 1af8c8c7e8..faaa146341 100644 --- a/legacy/emotion/src/modules/Makefile.am +++ b/legacy/emotion/src/modules/Makefile.am @@ -1,4 +1,4 @@ MAINTAINERCLEANFILES = Makefile.in -SUBDIRS = xine gstreamer +SUBDIRS = xine gstreamer generic diff --git a/legacy/emotion/src/modules/generic/Emotion_Generic_Plugin.h b/legacy/emotion/src/modules/generic/Emotion_Generic_Plugin.h new file mode 100644 index 0000000000..b85f647731 --- /dev/null +++ b/legacy/emotion/src/modules/generic/Emotion_Generic_Plugin.h @@ -0,0 +1,114 @@ +#ifndef EMOTION_GENERIC_PLUGIN_H +#define EMOTION_GENERIC_PLUGIN_H + +#include +#include +#include +#include +#include +#include + +#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/legacy/emotion/src/modules/generic/Makefile.am b/legacy/emotion/src/modules/generic/Makefile.am new file mode 100644 index 0000000000..0e6455972f --- /dev/null +++ b/legacy/emotion/src/modules/generic/Makefile.am @@ -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/legacy/emotion/src/modules/generic/README b/legacy/emotion/src/modules/generic/README new file mode 100644 index 0000000000..9d4d7035ba --- /dev/null +++ b/legacy/emotion/src/modules/generic/README @@ -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", ); + +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/legacy/emotion/src/modules/generic/emotion_generic.c b/legacy/emotion/src/modules/generic/emotion_generic.c new file mode 100644 index 0000000000..ef6b9eb8bf --- /dev/null +++ b/legacy/emotion/src/modules/generic/emotion_generic.c @@ -0,0 +1,1123 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#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/legacy/emotion/src/modules/generic/emotion_generic.h b/legacy/emotion/src/modules/generic/emotion_generic.h new file mode 100644 index 0000000000..e068d58ccc --- /dev/null +++ b/legacy/emotion/src/modules/generic/emotion_generic.h @@ -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 +