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
This commit is contained in:
Rafael Antognolli 2011-09-01 19:04:15 +00:00
parent 1cacec0d41
commit a7ae4566b8
17 changed files with 2394 additions and 3 deletions

View File

@ -3,4 +3,4 @@ Vincent Torri <torri@maths.univ-evry.fr>
Nicolas Aguirre <aguirre.nicolas@gmail.com>
Sebastian Dransfeld <sd@tango.flipp.net>
Cedric Bail <cedric.bail@free.fr>
Rafael Antognolli <antognolli@profusion.mobi>

View File

@ -54,6 +54,7 @@ VMAJ=v_maj
AC_SUBST(VMAJ)
want_vlc="no"
want_generic="yes"
case "$host_os" in
mingw* | cegcc*)
want_xine="no"
@ -230,6 +231,7 @@ AC_MSG_RESULT([${have_v4l2}])
EMOTION_CHECK_MODULE([Xine], [${want_xine}])
EMOTION_CHECK_MODULE([Gstreamer], [${want_gstreamer}])
EMOTION_CHECK_MODULE([VLC], [${want_vlc}])
EMOTION_CHECK_MODULE([generic], [${want_generic}])
#disabled vlc
#if test "x${enable_xine}" = "xno" && test "x${enable_gstreamer}" = "xno" && test "x${enable_vlc}" = "xno" ; then
@ -269,7 +271,10 @@ src/lib/Makefile
src/modules/Makefile
src/modules/xine/Makefile
src/modules/gstreamer/Makefile
src/modules/generic/Makefile
src/edje_external/Makefile
src/generic_players/Makefile
src/generic_players/vlc/Makefile
src/bin/Makefile
src/examples/Makefile
doc/Makefile

View File

@ -71,6 +71,28 @@ AS_IF([test "x$have_dep" = "xyes"], [$2], [$3])
])
dnl use: EMOTION_CHECK_DEP_GENERIC(want_static[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
AC_DEFUN([EMOTION_CHECK_DEP_GENERIC],
[
requirement=""
PKG_CHECK_MODULES([GENERIC],
[evas >= 0.9.9],
[
have_dep="yes"
],
[have_dep="no"])
if test "x$1" = "xstatic" ; then
requirement_emotion="${requirement} ${requirement_emotion}"
fi
AS_IF([test "x$have_dep" = "xyes"], [$2], [$3])
])
dnl use: EMOTION_CHECK_MODULE(description, want_module[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
AC_DEFUN([EMOTION_CHECK_MODULE],
[

View File

@ -6,3 +6,7 @@ SUBDIRS = lib bin modules examples
if ENABLE_EDJE_EXTERNAL
SUBDIRS += edje_external
endif
if EMOTION_BUILD_GENERIC
SUBDIRS += generic_players
endif

View File

@ -16,6 +16,7 @@ LDADD = \
SRCS = \
emotion_basic_example.c \
emotion_generic_example.c \
emotion_signals_example.c
EXTRA_DIST = $(SRCS)
@ -30,6 +31,7 @@ endif
if EFL_BUILD_EXAMPLES
pkglib_PROGRAMS += \
emotion_basic_example \
emotion_generic_example \
emotion_signals_example
endif

View File

@ -0,0 +1,209 @@
#include <Ecore.h>
#include <Ecore_Evas.h>
#include <Evas.h>
#include <Emotion.h>
#include <stdio.h>
#include <string.h>
#define WIDTH (320)
#define HEIGHT (240)
static Eina_List *filenames = NULL;
static Eina_List *curfile = NULL;
static void
_playback_started_cb(void *data, Evas_Object *o, void *event_info)
{
printf("Emotion object started playback.\n");
}
static Evas_Object *
_create_emotion_object(Evas *e)
{
Evas_Object *em = emotion_object_add(e);
emotion_object_init(em, "generic");
evas_object_smart_callback_add(
em, "playback_started", _playback_started_cb, NULL);
return em;
}
static void
_on_key_down(void *data, Evas *e, Evas_Object *o, void *event_info)
{
Evas_Event_Key_Down *ev = event_info;
Evas_Object *em = data;
if (!strcmp(ev->keyname, "Return"))
{
emotion_object_play_set(em, EINA_TRUE);
}
else if (!strcmp(ev->keyname, "space"))
{
emotion_object_play_set(em, EINA_FALSE);
}
else if (!strcmp(ev->keyname, "Escape"))
{
ecore_main_loop_quit();
}
else if (!strcmp(ev->keyname, "t"))
{
int w, h;
emotion_object_size_get(em, &w, &h);
fprintf(stderr, "example -> size: %dx%d\n", w, h);
}
else if (!strcmp(ev->keyname, "s"))
{
fprintf(stderr, "skipping to position 60\n");
emotion_object_position_set(em, 60);
}
else if (!strcmp(ev->keyname, "1"))
{
fprintf(stderr, "setting speed to 1.0\n");
emotion_object_play_speed_set(em, 1.0);
}
else if (!strcmp(ev->keyname, "2"))
{
fprintf(stderr, "setting speed to 2.0\n");
emotion_object_play_speed_set(em, 2.0);
}
else if (!strcmp(ev->keyname, "n"))
{
const char *file;
curfile = eina_list_next(curfile);
file = eina_list_data_get(curfile);
fprintf(stderr, "playing next file: %s\n", file);
emotion_object_file_set(em, file);
}
else if (!strcmp(ev->keyname, "p"))
{
const char *file;
curfile = eina_list_prev(curfile);
file = eina_list_data_get(curfile);
fprintf(stderr, "playing next file: %s\n", file);
emotion_object_file_set(em, file);
}
else if (!strcmp(ev->keyname, "d"))
{
evas_object_del(em);
}
else
{
fprintf(stderr, "unhandled key: %s\n", ev->keyname);
}
}
static void
_frame_decode_cb(void *data, Evas_Object *o, void *event_info)
{
// fprintf(stderr, "smartcb: frame_decode\n");
}
static void
_length_change_cb(void *data, Evas_Object *o, void *event_info)
{
fprintf(stderr, "smartcb: length_change: %0.3f\n", emotion_object_play_length_get(o));
}
static void
_position_update_cb(void *data, Evas_Object *o, void *event_info)
{
fprintf(stderr, "smartcb: position_update: %0.3f\n", emotion_object_position_get(o));
}
static void
_progress_change_cb(void *data, Evas_Object *o, void *event_info)
{
fprintf(stderr, "smartcb: progress_change: %0.3f, %s\n",
emotion_object_progress_status_get(o),
emotion_object_progress_info_get(o));
}
static void
_frame_resize_cb(void *data, Evas_Object *o, void *event_info)
{
int w, h;
emotion_object_size_get(o, &w, &h);
fprintf(stderr, "smartcb: frame_resize: %dx%d\n", w, h);
}
int
main(int argc, const char *argv[])
{
int err;
Ecore_Evas *ee;
Evas *e;
Evas_Object *bg, *em;
int i;
if (argc < 2)
{
printf("One argument is necessary. Usage:\n");
printf("\t%s <filename>\n", argv[0]);
}
eina_init();
for (i = 1; i < argc; i++)
filenames = eina_list_append(filenames, eina_stringshare_add(argv[i]));
curfile = filenames;
if (!ecore_evas_init())
return EXIT_FAILURE;
/* this will give you a window with an Evas canvas under the first
* engine available */
ee = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL);
if (!ee)
goto error;
ecore_evas_show(ee);
/* the canvas pointer, de facto */
e = ecore_evas_get(ee);
/* adding a background to this example */
bg = evas_object_rectangle_add(e);
evas_object_name_set(bg, "our dear rectangle");
evas_object_color_set(bg, 255, 255, 255, 255); /* white bg */
evas_object_move(bg, 0, 0); /* at canvas' origin */
evas_object_resize(bg, WIDTH, HEIGHT); /* covers full canvas */
evas_object_show(bg);
/* Creating the emotion object */
em = _create_emotion_object(e);
emotion_object_file_set(em, eina_list_data_get(curfile));
evas_object_move(em, 0, 0);
evas_object_resize(em, WIDTH, HEIGHT);
evas_object_show(em);
evas_object_smart_callback_add(em, "frame_decode", _frame_decode_cb, NULL);
evas_object_smart_callback_add(em, "length_change", _length_change_cb, NULL);
evas_object_smart_callback_add(em, "position_update", _position_update_cb, NULL);
evas_object_smart_callback_add(em, "progress_change", _progress_change_cb, NULL);
evas_object_smart_callback_add(em, "frame_resize", _frame_resize_cb, NULL);
evas_object_event_callback_add(bg, EVAS_CALLBACK_KEY_DOWN, _on_key_down, em);
evas_object_focus_set(bg, EINA_TRUE);
emotion_object_play_set(em, EINA_TRUE);
ecore_main_loop_begin();
ecore_evas_free(ee);
ecore_evas_shutdown();
return 0;
error:
fprintf(stderr, "you got to have at least one evas engine built and linked"
" up to ecore-evas for this example to run properly.\n");
EINA_LIST_FREE(filenames, curfile)
eina_stringshare_del(eina_list_data_get(curfile));
ecore_evas_shutdown();
eina_shutdown();
return -1;
}

View File

@ -0,0 +1,8 @@
MAINTAINERCLEANFILES = Makefile.in
SUBDIRS =
if EMOTION_BUILD_VLC
SUBDIRS += vlc
endif

View File

@ -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@

View File

@ -0,0 +1,700 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <poll.h>
#include <vlc/vlc.h>
#include <Emotion_Generic_Plugin.h>
enum _Thread_Events {
EM_THREAD_POSITION_CHANGED,
EM_THREAD_LAST
};
struct _App {
Emotion_Generic_Video_Shared *vs;
Emotion_Generic_Video_Frame vf;
libvlc_instance_t *libvlc;
libvlc_media_t *m;
libvlc_media_player_t *mp;
libvlc_event_manager_t *event_mgr;
libvlc_event_manager_t *mevent_mgr;
char *filename;
char *shmname;
int w, h;
int fd_read;
int fd_write;
int size_sent;
int opening;
int closing;
int playing;
};
static pthread_mutex_t _mutex_fd = PTHREAD_MUTEX_INITIALIZER;
int
_em_read_safe(int fd, void *buf, ssize_t size)
{
ssize_t todo;
char *p;
todo = size;
p = buf;
while (todo > 0)
{
ssize_t r;
r = read(fd, p, todo);
if (r > 0)
{
todo -= r;
p += r;
}
else if (r == 0)
return 0;
else
{
if (errno == EINTR || errno == EAGAIN)
continue;
else
{
fprintf(stderr, "could not read from fd %d: %s",
fd, strerror(errno));
return 0;
}
}
}
return 1;
}
int
_em_write_safe(int fd, const void *buf, ssize_t size)
{
ssize_t todo;
const char *p;
todo = size;
p = buf;
while (todo > 0)
{
ssize_t r;
r = write(fd, p, todo);
if (r > 0)
{
todo -= r;
p += r;
}
else if (r == 0)
return 0;
else
{
if (errno == EINTR || errno == EAGAIN)
continue;
else
{
fprintf(stderr, "could not write to fd %d: %s",
fd, strerror(errno));
return 0;
}
}
}
return 1;
}
static int
_em_str_read(char **str)
{
int size;
int r;
char buf[PATH_MAX];
r = _em_read_safe(STDIN_FILENO, &size, sizeof(size));
if (!r)
{
*str = NULL;
return 0;
}
if (!size)
{
*str = NULL;
return 1;
}
r = _em_read_safe(STDIN_FILENO, buf, size);
if (!r)
{
*str = NULL;
return 0;
}
*str = strdup(buf);
return 1;
}
static int
_em_cmd_read(void)
{
int cmd;
_em_read_safe(STDIN_FILENO, &cmd, sizeof(cmd));
return cmd;
}
static void
_send_cmd_start(int cmd)
{
pthread_mutex_lock(&_mutex_fd);
_em_write_safe(STDOUT_FILENO, &cmd, sizeof(cmd));
}
static void
_send_cmd_finish(void)
{
static const char c = '\n';
_em_write_safe(STDOUT_FILENO, &c, sizeof(c));
pthread_mutex_unlock(&_mutex_fd);
}
static void
_send_cmd(int cmd)
{
_send_cmd_start(cmd);
_send_cmd_finish();
}
static void
_send_cmd_str(const char *str)
{
int len;
len = strlen(str) + 1;
_em_write_safe(STDOUT_FILENO, &len, sizeof(len));
_em_write_safe(STDOUT_FILENO, str, len);
}
#define SEND_CMD_PARAM(i) \
_em_write_safe(STDOUT_FILENO, &(i), sizeof((i)));
static void
_send_resize(int width, int height)
{
_send_cmd_start(EM_RESULT_FRAME_SIZE);
SEND_CMD_PARAM(width);
SEND_CMD_PARAM(height);
_send_cmd_finish();
}
static void
_send_length_changed(const struct libvlc_event_t *ev)
{
float length = ev->u.media_player_length_changed.new_length;
length /= 1000;
fprintf(stderr, "length changed: %0.3f\n", length);
_send_cmd_start(EM_RESULT_LENGTH_CHANGED);
SEND_CMD_PARAM(length);
_send_cmd_finish();
}
static void
_send_time_changed(const struct libvlc_event_t *ev)
{
float new_time = ev->u.media_player_time_changed.new_time;
new_time /= 1000;
_send_cmd_start(EM_RESULT_POSITION_CHANGED);
SEND_CMD_PARAM(new_time);
_send_cmd_finish();
}
static void
_send_seekable_changed(const struct libvlc_event_t *ev)
{
int seekable = ev->u.media_player_seekable_changed.new_seekable;
_send_cmd_start(EM_RESULT_SEEKABLE_CHANGED);
SEND_CMD_PARAM(seekable);
_send_cmd_finish();
}
static void *
_lock(void *data, void **pixels)
{
struct _App *app = data;
if (app->playing)
*pixels = app->vf.frames[app->vs->frame.player];
else
*pixels = NULL;
return NULL; // picture identifier, not needed here
}
static void
_unlock(void *data, void *id, void *const *pixels)
{
struct _App *app = data;
if (!app->playing)
return;
sem_wait(&app->vs->lock);
app->vs->frame.last = app->vs->frame.player;
app->vs->frame.player = app->vs->frame.next;
app->vs->frame.next = app->vs->frame.last;
sem_post(&app->vs->lock);
}
static void
_display(void *data, void *id)
{
struct _App *app = data;
if (!app->playing)
return;
_send_cmd(EM_RESULT_FRAME_NEW);
}
static void *
_tmp_lock(void *data, void **pixels)
{
*pixels = NULL;
return NULL;
}
static void
_tmp_unlock(void *data, void *id, void *const *pixels)
{
}
static void
_tmp_display(void *data, void *id)
{
}
static void
_play(struct _App *app)
{
float pos;
if (!app->mp)
return;
_em_read_safe(STDIN_FILENO, &pos, sizeof(pos));
if (app->playing)
{
libvlc_media_player_set_pause(app->mp, 0);
}
else
{
libvlc_time_t new_time = pos * 1000;
libvlc_media_player_play(app->mp);
libvlc_media_player_set_time(app->mp, new_time);
app->playing = 1;
}
}
static void
_stop(struct _App *app)
{
if (app->mp)
libvlc_media_player_set_pause(app->mp, 1);
}
static void
_send_file_closed(struct _App *app)
{
app->closing = 0;
emotion_generic_shm_free(app->vs);
_send_cmd(EM_RESULT_FILE_CLOSE);
}
static void
_send_file_set(struct _App *app)
{
if (app->opening)
_send_cmd(EM_RESULT_FILE_SET);
if (app->closing)
_send_file_closed(app);
}
static void
_event_cb(const struct libvlc_event_t *ev, void *data)
{
struct _App *app = data;
int thread_event;
switch (ev->type) {
case libvlc_MediaPlayerTimeChanged:
_send_time_changed(ev);
break;
case libvlc_MediaPlayerPositionChanged:
thread_event = EM_THREAD_POSITION_CHANGED;
write(app->fd_write, &thread_event, sizeof(thread_event));
break;
case libvlc_MediaPlayerLengthChanged:
_send_length_changed(ev);
break;
case libvlc_MediaPlayerSeekableChanged:
_send_seekable_changed(ev);
break;
case libvlc_MediaPlayerPlaying:
_send_resize(app->w, app->h);
break;
case libvlc_MediaPlayerStopped:
_send_file_set(app);
break;
case libvlc_MediaPlayerEndReached:
_send_cmd(EM_RESULT_PLAYBACK_STOPPED);
break;
}
}
static void
_file_set(struct _App *app)
{
_em_str_read(&app->filename);
app->m = libvlc_media_new_path(app->libvlc, app->filename);
if (!app->m)
{
fprintf(stderr, "could not open path: \"%s\"\n", app->filename);
return;
}
app->mp = libvlc_media_player_new_from_media(app->m);
if (!app->mp)
{
fprintf(stderr, "could not create new player from media.\n");
return;
}
app->opening = 1;
libvlc_video_set_format(app->mp, "RV32", DEFAULTWIDTH, DEFAULTHEIGHT, DEFAULTWIDTH * 4);
libvlc_video_set_callbacks(app->mp, _tmp_lock, _tmp_unlock, _tmp_display, NULL);
app->event_mgr = libvlc_media_player_event_manager(app->mp);
libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerPositionChanged,
_event_cb, app);
libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerStopped,
_event_cb, app);
app->mevent_mgr = libvlc_media_event_manager(app->m);
libvlc_audio_set_mute(app->mp, 1);
libvlc_media_player_play(app->mp);
}
static void
_position_set(struct _App *app)
{
if (!app->mp)
return;
float position;
_em_read_safe(STDIN_FILENO, &position, sizeof(position));
libvlc_time_t new_time = position * 1000;
libvlc_media_player_set_time(app->mp, new_time);
}
static void
_speed_set(struct _App *app)
{
float rate;
if (!app->mp)
return;
_em_read_safe(STDIN_FILENO, &rate, sizeof(rate));
libvlc_media_player_set_rate(app->mp, rate);
}
static void
_mute_set(struct _App *app)
{
int mute;
if (!app->mp)
return;
_em_read_safe(STDIN_FILENO, &mute, sizeof(mute));
libvlc_audio_set_mute(app->mp, mute);
}
static void
_volume_set(struct _App *app)
{
float volume;
int vol;
if (!app->mp)
return;
_em_read_safe(STDIN_FILENO, &volume, sizeof(volume));
vol = volume * 100;
libvlc_audio_set_volume(app->mp, vol);
}
static void
_audio_track_set(struct _App *app)
{
int track;
_em_read_safe(STDIN_FILENO, &track, sizeof(track));
libvlc_audio_set_track(app->mp, track);
}
static void
_file_set_done(struct _App *app)
{
emotion_generic_shm_get(app->shmname, &app->vs, &app->vf);
app->w = app->vs->width;
app->h = app->vs->height;
libvlc_video_set_format(app->mp, "RV32", app->w, app->h, app->w * 4);
libvlc_video_set_callbacks(app->mp, _lock, _unlock, _display, app);
app->opening = 0;
libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerPlaying,
_event_cb, app);
libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerTimeChanged,
_event_cb, app);
libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerLengthChanged,
_event_cb, app);
libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerSeekableChanged,
_event_cb, app);
libvlc_audio_set_mute(app->mp, 0);
_send_cmd(EM_RESULT_FILE_SET_DONE);
}
static void
_file_close(struct _App *app)
{
app->playing = 0;
if (libvlc_media_player_get_state(app->mp) != libvlc_Playing)
{
_send_file_closed(app);
return;
}
app->closing = 1;
libvlc_media_player_stop(app->mp);
if (app->filename)
free(app->filename);
if (app->mp)
{
libvlc_media_release(app->m);
libvlc_media_player_release(app->mp);
}
}
static void
_process_emotion_commands(struct _App *app)
{
int cmd = _em_cmd_read();
switch (cmd) {
case EM_CMD_FILE_SET:
_file_set(app);
break;
case EM_CMD_FILE_SET_DONE:
_file_set_done(app);
break;
case EM_CMD_FILE_CLOSE:
_file_close(app);
break;
case EM_CMD_PLAY:
_play(app);
break;
case EM_CMD_STOP:
_stop(app);
break;
case EM_CMD_POSITION_SET:
_position_set(app);
break;
case EM_CMD_SPEED_SET:
_speed_set(app);
break;
case EM_CMD_AUDIO_MUTE_SET:
_mute_set(app);
break;
case EM_CMD_VOLUME_SET:
_volume_set(app);
break;
case EM_CMD_AUDIO_TRACK_SET:
_audio_track_set(app);
break;
};
}
static void
_send_track_info(libvlc_media_player_t *mp)
{
int track_count, current;
libvlc_track_description_t *desc;
current = libvlc_audio_get_track(mp);
track_count = libvlc_audio_get_track_count(mp);
desc = libvlc_audio_get_track_description(mp);
_send_cmd_start(EM_RESULT_AUDIO_TRACK_INFO);
SEND_CMD_PARAM(current);
SEND_CMD_PARAM(track_count);
while (desc)
{
int tid = desc->i_id;
const char *name = desc->psz_name;
SEND_CMD_PARAM(tid);
_send_cmd_str(name);
desc = desc->p_next;
}
_send_cmd_finish();
}
static void
_position_changed(struct _App *app)
{
if (!app->opening)
return;
/* sending size info only once */
int r, w, h;
r = libvlc_video_get_size(app->mp, 0, &w, &h);
if (r < 0)
return;
_send_resize(w, h);
/* sending audio track info */
// _send_track_info(app->mp);
libvlc_media_player_stop(app->mp);
}
static void
_process_thread_events(struct _App *app)
{
int event;
size_t size;
size = read(app->fd_read, &event, sizeof(event));
if (size != sizeof(event))
{
fprintf(stderr, "player: problem when reading thread event. size = %zd\n", size);
return;
}
switch (event) {
case EM_THREAD_POSITION_CHANGED:
_position_changed(app);
break;
}
}
int
main(int argc, const char *argv[])
{
struct _App app;
Emotion_Generic_Video_Shared *vs;
struct pollfd fds[2]; // watching on 2 file descriptors
int tpipe[2]; // pipe for comunicating events from threads
char shmname[256];
char cwidth[64], cheight[64], cpitch[64], chroma[64];
char buf[64];
const char *vlc_argv[] =
{
"--quiet",
"--vout",
"vmem",
"--vmem-width",
cwidth,
"--vmem-height",
cheight,
"--vmem-pitch",
cpitch,
"--vmem-chroma",
chroma
};
int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);
snprintf(cwidth, sizeof(cwidth), "%d", DEFAULTWIDTH);
snprintf(cheight, sizeof(cheight), "%d", DEFAULTHEIGHT);
snprintf(cpitch, sizeof(cpitch), "%d", DEFAULTWIDTH * 4);
snprintf(chroma, sizeof(chroma), "RV32");
app.libvlc = libvlc_new(vlc_argc, vlc_argv);
app.mp = NULL;
app.filename = NULL;
app.w = 0;
app.h = 0;
app.size_sent = 0;
app.opening = 0;
app.playing = 0;
app.closing = 0;
if (_em_cmd_read() != EM_CMD_INIT)
{
fprintf(stderr, "player: wrong init command!\n");
return -1;
}
int size;
_em_read_safe(STDIN_FILENO, &size, sizeof(size));
_em_read_safe(STDIN_FILENO, buf, size);
app.shmname = strdup(buf);
_send_cmd(EM_RESULT_INIT);
pipe(tpipe);
app.fd_read = tpipe[0];
app.fd_write = tpipe[1];
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
fds[1].fd = app.fd_read;
fds[1].events = POLLIN;
while (1)
{
int r;
r = poll(fds, 2, 30);
if (r == 0)
continue;
else if (r < 0)
{
fprintf(stderr, "an error ocurred on poll().\n");
break;
}
if (fds[0].revents & POLLIN)
_process_emotion_commands(&app);
if (fds[1].revents & POLLIN)
_process_thread_events(&app);
}
libvlc_release(app.libvlc);
return 0;
}
#undef SEND_CMD_PARAM

View File

@ -41,6 +41,7 @@ enum _Emotion_Format
struct _Emotion_Module_Options
{
const char *player;
Eina_Bool no_video : 1;
Eina_Bool no_audio : 1;
};

View File

@ -124,7 +124,8 @@ static int _log_domain = -1;
static const char *_backend_priority[] = {
"gstreamer",
"xine",
"vlc"
"vlc",
"generic"
};
static const char SIG_FRAME_DECODE[] = "frame_decode";
@ -312,6 +313,9 @@ emotion_object_module_option_set(Evas_Object *obj, const char *opt, const char *
E_SMART_OBJ_GET(sd, obj, E_OBJ_NAME);
if ((!opt) || (!val)) return;
if (!strcmp(opt, "player"))
eina_stringshare_replace(&sd->module_options.player, val);
}
EAPI Eina_Bool

View File

@ -1,4 +1,4 @@
MAINTAINERCLEANFILES = Makefile.in
SUBDIRS = xine gstreamer
SUBDIRS = xine gstreamer generic

View File

@ -0,0 +1,114 @@
#ifndef EMOTION_GENERIC_PLUGIN_H
#define EMOTION_GENERIC_PLUGIN_H
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#define DEFAULTWIDTH 320
#define DEFAULTHEIGHT 240
#define DEFAULTPITCH 4
typedef enum _Emotion_Generic_Cmd Emotion_Generic_Cmd;
typedef enum _Emotion_Generic_Result Emotion_Generic_Result;
typedef struct _Emotion_Generic_Video_Frame Emotion_Generic_Video_Frame;
typedef struct _Emotion_Generic_Video_Shared Emotion_Generic_Video_Shared;
enum _Emotion_Generic_Cmd
{
EM_CMD_INIT, // param: shared memory identifier (string)
EM_CMD_PLAY, // param: position (float)
EM_CMD_STOP, // param: none
EM_CMD_FILE_SET, // param: filename (string)
EM_CMD_FILE_SET_DONE, // param: none
EM_CMD_FILE_CLOSE, // param: none
EM_CMD_POSITION_SET, // param: position (float)
EM_CMD_SPEED_SET, // param: speed (float)
EM_CMD_AUDIO_MUTE_SET, // param: muted (int)
EM_CMD_VOLUME_SET, // param: volume (float)
EM_CMD_AUDIO_TRACK_SET, // param: track id (int)
EM_CMD_LAST
};
enum _Emotion_Generic_Result
{
EM_RESULT_INIT, // param: none
EM_RESULT_FILE_SET, // param: none
EM_RESULT_FILE_SET_DONE, // param: none
EM_RESULT_PLAYBACK_STOPPED, // param: none
EM_RESULT_FILE_CLOSE, // param: none
EM_RESULT_FRAME_NEW, // param: none
EM_RESULT_FRAME_SIZE, // param: int, int (width, height)
EM_RESULT_LENGTH_CHANGED, // param: float
EM_RESULT_POSITION_CHANGED, // param: float
EM_RESULT_SEEKABLE_CHANGED, // param: int
EM_RESULT_AUDIO_TRACK_INFO, // param: current track, track count, track_id, track_name, track_id2, track_name2, ...
// (int, int, int, string, int, string, ...)
EM_RESULT_LAST
};
/* structure for frames 2 buffers to keep integrity */
struct _Emotion_Generic_Video_Frame
{
unsigned char *frames[3];
};
/* structure for frames 2 buffers to keep integrity */
struct _Emotion_Generic_Video_Shared
{
int size;
int width;
int height;
int pitch;
/**
* - "emotion" is the frame from where the Emotion process is reading pixels.
* The player shouldn't touch this frame.
* - "player" is the frame where the slayer process is writing pixels.
* The emotion process shouldn't touch this frame.
* - "last" is the last frame that was rendered by the player. Emotion will
* use this frame the next time it will fetch pixels to Evas.
* - "next" is the unused frame. The player currently using the "player"
* should, after finishing this frame, set "last" to "player", and "player"
* to "next", and finally "next" to "last" so this operation can be done
* many times in case that Emotion does not request pixels fast enough.
*/
struct {
int emotion;
int player;
int last;
int next;
} frame;
sem_t lock;
};
inline void
emotion_generic_shm_get(const char *shmname, Emotion_Generic_Video_Shared **vs, Emotion_Generic_Video_Frame *vf)
{
int shmfd = -1;
int size;
Emotion_Generic_Video_Shared *t_vs;
shmfd = shm_open(shmname, O_RDWR, 0777);
t_vs = mmap(NULL, sizeof(*t_vs), PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
size = t_vs->size;
munmap(t_vs, sizeof(*t_vs));
t_vs = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
vf->frames[0] = (char *)t_vs + sizeof(*t_vs);
vf->frames[1] = (char *)t_vs + sizeof(*t_vs) + t_vs->height * t_vs->width * t_vs->pitch;
vf->frames[2] = (char *)t_vs + sizeof(*t_vs) + 2 * t_vs->height * t_vs->width * t_vs->pitch;
*vs = t_vs;
}
inline void
emotion_generic_shm_free(Emotion_Generic_Video_Shared *vs)
{
munmap(vs, vs->size);
}
#endif // EMOTION_GENERIC_PLUGIN_H

View File

@ -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

View File

@ -0,0 +1,79 @@
Generic - emotion backend
=========================
This generic player backend executes a separate player in another
process. It receives the bytes to be drawn on the emotion object through
a shared memory, and communicates with the player through a pipe, using
the player standard input/output.
The player must communicate with emotion using the defined commands
specified in the Emotion_Generic_Plugin.h. It doesn't need to link
against emotion, just include this file for easier implementation.
How does it work?
=================
When the module is initialized for an emotion object, it starts another process
that runs the specified player. The player command line is specified using:
emotion_object_module_option_set(object, "player", <command>);
A player using libvlc is being provided now, and the generic module internally
checks if the command given was "vlc", in which case it will use this provided
vlc player.
When a file is set to this object, it will send the file name to the player, and
expect an answer that will tell that the player already decoded a bit of the
file, and the video size is already set on the module, so it can allocate a
shared memory with correct size.
The module then allocates the memory, sends a message to the player and expect
an answer. After this last answer, the "open_done" signal is sent and the module
knows that it is ready for playing. Commands sent before the module being ready
are now applied (and play is resumed if necessary).
During this setup stage, info about the file set will be stored in the module,
so commands like meta data get, length get and so will be available to sync
calls like emotion_object_play_length_get();
If the player dies for any reason, a "decode_stop" signal is sent (should change
to something more like an error signal), and if play is called again, it will be
restarted. The playback should start from the same point it was before the
player crashed, if the player supports seek on the current media format).
TODO
====
- Provide better description for commands;
- Explain in details the communication emotion <-> player;
- Make more common functions for players;
- (maybe) add support for named pipes, so we don't rely on standard in/out
for communication;
- Add a detection on the player to know that the emotion process died (so it
can just exit);
- shmname should contain the child pid too;
- better names for commands, maybe add namespace everywhere;
questions
=========
- Using semaphores to lock the critical region between process, and pthread
mutexes for the threads inside the player. Should move to only one type
(semphores or mutexes)?
- There are 2 inline functions insde Emotion_Generic_Plugin.h to make it easier
for the player to get the shared memory correctly. Any problem with this?
Would be good to add more functions/macros to make common tasks like
parsing commands there too?
- Should move players to another project (outside of emotion)?
problems
========
- file_set has some critical time when file is not set yet when we can't call
some functions (I think only another file_set now);
- comunication player -> emotion depends on '\n' to delimitate commands, will
remove this soon (fix this urgently!);
- need to implement missing APIs;

File diff suppressed because it is too large Load Diff

View File

@ -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