diff options
author | Rafael Antognolli <antognolli@gmail.com> | 2011-09-01 19:04:15 +0000 |
---|---|---|
committer | Rafael Antognolli <antognolli@gmail.com> | 2011-09-01 19:04:15 +0000 |
commit | 169abb701f0d5fcef9b60160331357cc75f26947 (patch) | |
tree | 693c5cbf54201da3eccd26ccfb8ee094541ed106 /src/vlc/emotion_generic_vlc.c | |
download | emotion_generic_players-169abb701f0d5fcef9b60160331357cc75f26947.tar.gz |
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
Diffstat (limited to 'src/vlc/emotion_generic_vlc.c')
-rw-r--r-- | src/vlc/emotion_generic_vlc.c | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/src/vlc/emotion_generic_vlc.c b/src/vlc/emotion_generic_vlc.c new file mode 100644 index 0000000..773bbe9 --- /dev/null +++ b/src/vlc/emotion_generic_vlc.c @@ -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 |