forked from enlightenment/efl
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:
parent
1cacec0d41
commit
a7ae4566b8
|
@ -3,4 +3,4 @@ Vincent Torri <torri@maths.univ-evry.fr>
|
||||||
Nicolas Aguirre <aguirre.nicolas@gmail.com>
|
Nicolas Aguirre <aguirre.nicolas@gmail.com>
|
||||||
Sebastian Dransfeld <sd@tango.flipp.net>
|
Sebastian Dransfeld <sd@tango.flipp.net>
|
||||||
Cedric Bail <cedric.bail@free.fr>
|
Cedric Bail <cedric.bail@free.fr>
|
||||||
|
Rafael Antognolli <antognolli@profusion.mobi>
|
||||||
|
|
|
@ -54,6 +54,7 @@ VMAJ=v_maj
|
||||||
AC_SUBST(VMAJ)
|
AC_SUBST(VMAJ)
|
||||||
|
|
||||||
want_vlc="no"
|
want_vlc="no"
|
||||||
|
want_generic="yes"
|
||||||
case "$host_os" in
|
case "$host_os" in
|
||||||
mingw* | cegcc*)
|
mingw* | cegcc*)
|
||||||
want_xine="no"
|
want_xine="no"
|
||||||
|
@ -230,6 +231,7 @@ AC_MSG_RESULT([${have_v4l2}])
|
||||||
EMOTION_CHECK_MODULE([Xine], [${want_xine}])
|
EMOTION_CHECK_MODULE([Xine], [${want_xine}])
|
||||||
EMOTION_CHECK_MODULE([Gstreamer], [${want_gstreamer}])
|
EMOTION_CHECK_MODULE([Gstreamer], [${want_gstreamer}])
|
||||||
EMOTION_CHECK_MODULE([VLC], [${want_vlc}])
|
EMOTION_CHECK_MODULE([VLC], [${want_vlc}])
|
||||||
|
EMOTION_CHECK_MODULE([generic], [${want_generic}])
|
||||||
|
|
||||||
#disabled vlc
|
#disabled vlc
|
||||||
#if test "x${enable_xine}" = "xno" && test "x${enable_gstreamer}" = "xno" && test "x${enable_vlc}" = "xno" ; then
|
#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/Makefile
|
||||||
src/modules/xine/Makefile
|
src/modules/xine/Makefile
|
||||||
src/modules/gstreamer/Makefile
|
src/modules/gstreamer/Makefile
|
||||||
|
src/modules/generic/Makefile
|
||||||
src/edje_external/Makefile
|
src/edje_external/Makefile
|
||||||
|
src/generic_players/Makefile
|
||||||
|
src/generic_players/vlc/Makefile
|
||||||
src/bin/Makefile
|
src/bin/Makefile
|
||||||
src/examples/Makefile
|
src/examples/Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
|
|
|
@ -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]])
|
dnl use: EMOTION_CHECK_MODULE(description, want_module[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||||
AC_DEFUN([EMOTION_CHECK_MODULE],
|
AC_DEFUN([EMOTION_CHECK_MODULE],
|
||||||
[
|
[
|
||||||
|
|
|
@ -6,3 +6,7 @@ SUBDIRS = lib bin modules examples
|
||||||
if ENABLE_EDJE_EXTERNAL
|
if ENABLE_EDJE_EXTERNAL
|
||||||
SUBDIRS += edje_external
|
SUBDIRS += edje_external
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if EMOTION_BUILD_GENERIC
|
||||||
|
SUBDIRS += generic_players
|
||||||
|
endif
|
||||||
|
|
|
@ -16,6 +16,7 @@ LDADD = \
|
||||||
|
|
||||||
SRCS = \
|
SRCS = \
|
||||||
emotion_basic_example.c \
|
emotion_basic_example.c \
|
||||||
|
emotion_generic_example.c \
|
||||||
emotion_signals_example.c
|
emotion_signals_example.c
|
||||||
|
|
||||||
EXTRA_DIST = $(SRCS)
|
EXTRA_DIST = $(SRCS)
|
||||||
|
@ -30,6 +31,7 @@ endif
|
||||||
if EFL_BUILD_EXAMPLES
|
if EFL_BUILD_EXAMPLES
|
||||||
pkglib_PROGRAMS += \
|
pkglib_PROGRAMS += \
|
||||||
emotion_basic_example \
|
emotion_basic_example \
|
||||||
|
emotion_generic_example \
|
||||||
emotion_signals_example
|
emotion_signals_example
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
MAINTAINERCLEANFILES = Makefile.in
|
||||||
|
|
||||||
|
SUBDIRS =
|
||||||
|
|
||||||
|
if EMOTION_BUILD_VLC
|
||||||
|
SUBDIRS += vlc
|
||||||
|
endif
|
|
@ -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@
|
|
@ -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
|
|
@ -41,6 +41,7 @@ enum _Emotion_Format
|
||||||
|
|
||||||
struct _Emotion_Module_Options
|
struct _Emotion_Module_Options
|
||||||
{
|
{
|
||||||
|
const char *player;
|
||||||
Eina_Bool no_video : 1;
|
Eina_Bool no_video : 1;
|
||||||
Eina_Bool no_audio : 1;
|
Eina_Bool no_audio : 1;
|
||||||
};
|
};
|
||||||
|
|
|
@ -124,7 +124,8 @@ static int _log_domain = -1;
|
||||||
static const char *_backend_priority[] = {
|
static const char *_backend_priority[] = {
|
||||||
"gstreamer",
|
"gstreamer",
|
||||||
"xine",
|
"xine",
|
||||||
"vlc"
|
"vlc",
|
||||||
|
"generic"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char SIG_FRAME_DECODE[] = "frame_decode";
|
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);
|
E_SMART_OBJ_GET(sd, obj, E_OBJ_NAME);
|
||||||
if ((!opt) || (!val)) return;
|
if ((!opt) || (!val)) return;
|
||||||
|
|
||||||
|
if (!strcmp(opt, "player"))
|
||||||
|
eina_stringshare_replace(&sd->module_options.player, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
EAPI Eina_Bool
|
EAPI Eina_Bool
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
|
||||||
MAINTAINERCLEANFILES = Makefile.in
|
MAINTAINERCLEANFILES = Makefile.in
|
||||||
|
|
||||||
SUBDIRS = xine gstreamer
|
SUBDIRS = xine gstreamer generic
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue