#ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "emotion_modules.h" #include "emotion_xine.h" int _emotion_xine_log_domain = -1; static int _emotion_init_count = 0; /* internal util calls */ static void *_em_slave (void *par); static void _em_slave_event (void *data, int type, void *arg); static Eina_Bool _em_fd_active (void *data, Ecore_Fd_Handler *fdh); static void _em_event (void *data, const xine_event_t *event); static void _em_module_event (void *data, int type); static Eina_Bool _em_fd_ev_active (void *data, Ecore_Fd_Handler *fdh); //static int _em_timer (void *data); static void *_em_get_pos_len_th(void *par); static void _em_get_pos_len (Emotion_Xine_Video *ev); extern plugin_info_t emotion_xine_plugin_info[]; static void em_frame_done(void *ef); /* this is a slave controller thread for the xine module - libxine loves * to deadlock, internally stall and otherwise have unpredictable behavior * if we use the main process thread for many things - so a lot will be * farmed off to this slave. its job is to handle opening, closing, file * opening, recoder init etc. and all sorts of things can that often block. * anything this thread needs to return, it will return via the event pipe. */ static void * _em_slave(void *par) { Emotion_Xine_Video *ev; void *buf[2]; int len; ev = (Emotion_Xine_Video *)par; while ((len = read(ev->fd_slave_read, buf, sizeof(buf))) > 0) { if (len == sizeof(buf)) { Emotion_Xine_Event *eev; ev = buf[0]; eev = buf[1]; switch (eev->mtype) { case 0: /* noop */ break; case 1: /* init */ { ev->decoder = xine_new(); xine_init(ev->decoder); xine_register_plugins(ev->decoder, emotion_xine_plugin_info); if (1) { xine_cfg_entry_t cf; if (xine_config_lookup_entry(ev->decoder, "input.dvd_use_readahead", &cf)) { cf.num_value = 1; // 0 or 1 xine_config_update_entry(ev->decoder, &cf); } } DBG("OPEN VIDEO PLUGIN..."); if (!ev->opt_no_video) ev->video = xine_open_video_driver(ev->decoder, "emotion", XINE_VISUAL_TYPE_NONE, ev); DBG("RESULT: xine_open_video_driver() = %p", ev->video); // Let xine autodetect the best audio output driver if (!ev->opt_no_audio) ev->audio = xine_open_audio_driver(ev->decoder, NULL, ev); // ev->audio = xine_open_audio_driver(ev->decoder, "oss", ev); // dont use alsa - alsa has oss emulation. // ev->audio = xine_open_audio_driver(ev->decoder, "alsa", ev); // ev->audio = xine_open_audio_driver(ev->decoder, "arts", ev); // ev->audio = xine_open_audio_driver(ev->decoder, "esd", ev); ev->stream = xine_stream_new(ev->decoder, ev->audio, ev->video); ev->queue = xine_event_new_queue(ev->stream); xine_event_create_listener_thread(ev->queue, _em_event, ev); ev->opening = 0; ev->play_ok = 1; _em_module_event(ev, 1); /* event - open done */ } break; case 3: /* shutdown */ { _em_module_event(ev, 3); DBG("shutdown stop"); xine_stop(ev->stream); // pthread_mutex_lock(&(ev->get_pos_len_mutex)); if (!ev->get_pos_thread_deleted) { DBG("closing get_pos thread, %p", ev); pthread_mutex_lock(&(ev->get_pos_len_mutex)); pthread_cond_broadcast(&(ev->get_pos_len_cond)); pthread_mutex_unlock(&(ev->get_pos_len_mutex)); while (ev->get_poslen); } DBG("dispose %p", ev); xine_dispose(ev->stream); DBG("dispose evq %p", ev); xine_event_dispose_queue(ev->queue); DBG("close video drv %p", ev); if (ev->video) xine_close_video_driver(ev->decoder, ev->video); DBG("wait for vo to go"); while (ev->have_vo); DBG("vo gone"); DBG("close audio drv %p", ev); if (ev->audio) xine_close_audio_driver(ev->decoder, ev->audio); DBG("xine exit %p", ev); xine_exit(ev->decoder); DBG("DONE %p", ev); close(ev->fd_write); close(ev->fd_read); close(ev->fd_ev_write); close(ev->fd_ev_read); close(ev->fd_slave_write); close(ev->fd_slave_read); ev->closing = 0; if (eev->xine_event) free(eev->xine_event); free(eev); free(ev); return NULL; } break; case 2: /* file open */ { int pos_stream = 0; int pos_time = 0; int length_time = 0; uint32_t v; char *file; file = eev->xine_event; DBG("OPEN STREAM %s", file); if (xine_open(ev->stream, file)) { if (xine_get_pos_length(ev->stream, &pos_stream, &pos_time, &length_time)) { if (length_time == 0) { ev->pos = (double)pos_stream / 65535; ev->len = 1.0; ev->no_time = 1; } else { ev->pos = 0.0; ev->len = (double)length_time / 1000.0; } } else { ev->pos = 0.0; ev->len = 1.0; } v = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_FRAME_DURATION); if (v > 0) ev->fps = 90000.0 / (double)v; v = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_WIDTH); ev->w = v; v = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_HEIGHT); ev->h = v; v = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_RATIO); ev->ratio = (double)v / 10000.0; ev->just_loaded = 1; ev->get_poslen = 0; xine_set_param(ev->stream, XINE_PARAM_AUDIO_VOLUME, ev->volume * 100); } _em_module_event(ev, 2); /* event - open done */ } break; case 11: /* file close */ { DBG("done %p", ev); em_frame_done(ev); DBG("stop %p", ev); xine_stop(ev->stream); DBG("close %p", ev); xine_close(ev->stream); DBG("close done %p", ev); _em_module_event(ev, 11); } break; case 4: /* play */ { double pos; int pos_stream, pos_time, length_time; pos = *((double *)eev->xine_event); if ((xine_get_param(ev->stream, XINE_PARAM_SPEED) == XINE_SPEED_PAUSE) && (EINA_DBL_EQ(pos, ev->pos)) && (!ev->just_loaded)) { xine_set_param(ev->stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL); } else { if (ev->no_time) xine_play(ev->stream, pos * 65535, 0); else xine_play(ev->stream, 0, pos * 1000); } ev->just_loaded = 0; if (xine_get_pos_length(ev->stream, &pos_stream, &pos_time, &length_time)) { if (length_time == 0) { ev->pos = (double)pos_stream / 65535; ev->len = 1.0; ev->no_time = 1; } else { ev->pos = (double)pos_time / 1000.0; ev->len = (double)length_time / 1000.0; } } _em_module_event(ev, 4); } break; case 5: /* stop */ { xine_set_param(ev->stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); _em_module_event(ev, 5); } break; case 6: /* seek */ { double pos; pos = *((double *)eev->xine_event); if (ev->no_time) xine_play(ev->stream, pos * 65535, 0); else xine_play(ev->stream, 0, pos * 1000); if (!ev->play) xine_set_param(ev->stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); _em_module_event(ev, 6); } break; case 7: /* eject */ { xine_eject(ev->stream); _em_module_event(ev, 7); } break; case 8: /* spu mute */ { xine_set_param(ev->stream, XINE_PARAM_IGNORE_SPU, ev->spu_mute); _em_module_event(ev, 8); } break; case 9: /* channel */ { xine_set_param(ev->stream, XINE_PARAM_SPU_CHANNEL, ev->spu_channel); _em_module_event(ev, 9); } break; case 10: /* vol */ { xine_set_param(ev->stream, XINE_PARAM_AUDIO_VOLUME, ev->volume * 100); _em_module_event(ev, 10); } break; case 12: /* audio mute */ { xine_set_param(ev->stream, XINE_PARAM_AUDIO_MUTE, ev->audio_mute); } break; case 13: /* audio mute */ { xine_set_param(ev->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, ev->audio_channel); } break; case 14: /* audio mute */ { xine_set_param(ev->stream, XINE_PARAM_VIDEO_CHANNEL, ev->video_channel); } break; default: break; } if (eev->xine_event) free(eev->xine_event); free(eev); } } return NULL; } static void _em_slave_event(void *data, int type, void *arg) { void *buf[2]; Emotion_Xine_Event *new_ev; Emotion_Xine_Video *ev; ev = data; new_ev = calloc(1, sizeof(Emotion_Xine_Event)); if (!new_ev) return; new_ev->mtype = type; new_ev->type = -1; new_ev->xine_event = arg; buf[0] = data; buf[1] = new_ev; if (write(ev->fd_slave_write, buf, sizeof(buf)) < 0) perror("write"); } static void * em_add(const Emotion_Engine *api EINA_UNUSED, Evas_Object *obj, const Emotion_Module_Options *opt) { Emotion_Xine_Video *ev; int fds[2]; sigset_t oldset, newset; ev = calloc(1, sizeof(Emotion_Xine_Video)); EINA_SAFETY_ON_NULL_RETURN_VAL(ev, NULL); ev->obj = obj; if (pipe(fds) == 0) { ev->fd_read = fds[0]; ev->fd_write = fds[1]; fcntl(ev->fd_read, F_SETFL, O_NONBLOCK); ev->fd_handler = ecore_main_fd_handler_add(ev->fd_read, ECORE_FD_READ, _em_fd_active, ev, NULL, NULL); ecore_main_fd_handler_active_set(ev->fd_handler, ECORE_FD_READ); } if (pipe(fds) == 0) { ev->fd_ev_read = fds[0]; ev->fd_ev_write = fds[1]; fcntl(ev->fd_ev_read, F_SETFL, O_NONBLOCK); ev->fd_ev_handler = ecore_main_fd_handler_add(ev->fd_ev_read, ECORE_FD_READ, _em_fd_ev_active, ev, NULL, NULL); ecore_main_fd_handler_active_set(ev->fd_ev_handler, ECORE_FD_READ); } if (pipe(fds) == 0) { ev->fd_slave_read = fds[0]; ev->fd_slave_write = fds[1]; if (fcntl(ev->fd_slave_write, F_SETFL, O_NONBLOCK) != 0) ERR("Can't fcntl() slave write fd"); } ev->volume = 0.8; ev->delete_me = 0; ev->get_pos_thread_deleted = 0; ev->opening = 1; ev->play_ok = 0; if (opt) { ev->opt_no_audio = opt->no_audio; ev->opt_no_video = opt->no_video; } pthread_cond_init(&(ev->get_pos_len_cond), NULL); pthread_mutex_init(&(ev->get_pos_len_mutex), NULL); sigemptyset(&newset); sigaddset(&newset, SIGPIPE); sigaddset(&newset, SIGALRM); sigaddset(&newset, SIGCHLD); sigaddset(&newset, SIGUSR1); sigaddset(&newset, SIGUSR2); sigaddset(&newset, SIGHUP); sigaddset(&newset, SIGQUIT); sigaddset(&newset, SIGINT); sigaddset(&newset, SIGTERM); #ifdef SIGPWR sigaddset(&newset, SIGPWR); #endif pthread_sigmask(SIG_BLOCK, &newset, &oldset); pthread_create(&ev->get_pos_len_th, NULL, _em_get_pos_len_th, ev); pthread_create(&ev->slave_th, NULL, _em_slave, ev); pthread_sigmask(SIG_SETMASK, &oldset, NULL); pthread_detach(ev->slave_th); _em_slave_event(ev, 1, NULL); ev->buffer = 1.0; return ev; } static void em_del(void *ef) { Emotion_Xine_Video *ev = ef; ev->closing = 1; ev->delete_me = 1; DBG("del fds %p", ev); ecore_main_fd_handler_del(ev->fd_handler); ev->fd_handler = NULL; ecore_main_fd_handler_del(ev->fd_ev_handler); ev->fd_ev_handler = NULL; if (ev->anim) { ecore_animator_del(ev->anim); ev->anim = NULL; } ev->closing = 1; _em_slave_event(ev, 3, NULL); DBG("done %p", ev); } static Eina_Bool em_file_open(void *ef, const char *file) { Emotion_Xine_Video *ev = ef; _em_slave_event(ev, 2, strdup(file)); return EINA_TRUE; } static void em_file_close(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (!ev) return; _em_slave_event(ev, 11, NULL); } static void em_play(void *ef, double pos) { Emotion_Xine_Video *ev; double *ppos; ev = (Emotion_Xine_Video *)ef; ev->play = 1; ev->play_ok = 0; ppos = malloc(sizeof(double)); *ppos = pos; _em_slave_event(ev, 4, ppos); } static void em_stop(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; ev->play = 0; ev->play_ok = 0; _em_slave_event(ev, 5, NULL); } static void em_size_get(void *ef, int *w, int *h) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (w) *w = ev->w; if (h) *h = ev->h; } static void em_pos_set(void *ef, double pos) { Emotion_Xine_Video *ev; double *ppos; ev = (Emotion_Xine_Video *)ef; ppos = malloc(sizeof(double)); *ppos = pos; _em_slave_event(ev, 6, ppos); } static double em_len_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return ev->len; } static double em_buffer_size_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return ev->buffer; } static int em_fps_num_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return (int)(ev->fps * 10000.0); } static int em_fps_den_get(void *ef EINA_UNUSED) { return 10000; } static double em_fps_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return ev->fps; } static double em_pos_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; _em_get_pos_len(ev); return ev->pos; } static void em_vis_set(void *ef, Emotion_Vis vis) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->vis == vis) return; ev->vis = vis; } static Emotion_Vis em_vis_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return ev->vis; } static Eina_Bool em_vis_supported(void *ef EINA_UNUSED, Emotion_Vis vis EINA_UNUSED) { return EINA_FALSE; } static double em_ratio_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return ev->ratio; } static int em_video_handled(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; return (xine_get_stream_info(ev->stream, XINE_STREAM_INFO_HAS_VIDEO) && xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_HANDLED)); } static int em_audio_handled(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; return (xine_get_stream_info(ev->stream, XINE_STREAM_INFO_HAS_AUDIO) && xine_get_stream_info(ev->stream, XINE_STREAM_INFO_AUDIO_HANDLED)); } static int em_seekable(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; return xine_get_stream_info(ev->stream, XINE_STREAM_INFO_SEEKABLE); } static void em_frame_done(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->cur_frame) { ev->fq--; if (ev->cur_frame->done_func) ev->cur_frame->done_func(ev->cur_frame->done_data); ev->cur_frame = NULL; } } static Emotion_Format em_format_get(void *ef) { Emotion_Xine_Video *ev; volatile Emotion_Xine_Video_Frame *fr; ev = (Emotion_Xine_Video *)ef; fr = ev->cur_frame; if (fr) return fr->format; return EMOTION_FORMAT_YV12; } static void em_video_data_size_get(void *ef, int *w, int *h) { Emotion_Xine_Video *ev; volatile Emotion_Xine_Video_Frame *fr; ev = (Emotion_Xine_Video *)ef; fr = ev->cur_frame; if (!fr) { if (w) *w = 0; if (h) *h = 0; return; } if (w) *w = fr->w; if (h) *h = fr->h; } static int em_yuv_rows_get(void *ef, int w EINA_UNUSED, int h, unsigned char **yrows, unsigned char **urows, unsigned char **vrows) { Emotion_Xine_Video *ev; volatile Emotion_Xine_Video_Frame *fr; ev = (Emotion_Xine_Video *)ef; fr = ev->cur_frame; if (!fr) return 0; if (fr->y) { int i; for (i = 0; i < h; i++) yrows[i] = fr->y + (i * fr->y_stride); for (i = 0; i < (h / 2); i++) urows[i] = fr->u + (i * fr->u_stride); for (i = 0; i < (h / 2); i++) vrows[i] = fr->v + (i * fr->v_stride); return 1; } return 0; } static int em_bgra_data_get(void *ef, unsigned char **bgra_data) { Emotion_Xine_Video *ev; volatile Emotion_Xine_Video_Frame *fr; ev = (Emotion_Xine_Video *)ef; fr = ev->cur_frame; if (!fr) return 0; if (fr->bgra_data) { *bgra_data = fr->bgra_data; return 1; } return 0; } static void em_event_feed(void *ef, int event) { Emotion_Xine_Video *ev; xine_event_t xine_event; ev = (Emotion_Xine_Video *)ef; if ((ev->opening) || (!ev->play_ok)) return; xine_event.data_length = 0; xine_event.data = NULL; xine_event.stream = ev->stream; gettimeofday(&xine_event.tv, NULL); switch (event) { case EMOTION_EVENT_MENU1: xine_event.type = XINE_EVENT_INPUT_MENU1; break; case EMOTION_EVENT_MENU2: xine_event.type = XINE_EVENT_INPUT_MENU2; break; case EMOTION_EVENT_MENU3: xine_event.type = XINE_EVENT_INPUT_MENU3; break; case EMOTION_EVENT_MENU4: xine_event.type = XINE_EVENT_INPUT_MENU4; break; case EMOTION_EVENT_MENU5: xine_event.type = XINE_EVENT_INPUT_MENU5; break; case EMOTION_EVENT_MENU6: xine_event.type = XINE_EVENT_INPUT_MENU6; break; case EMOTION_EVENT_MENU7: xine_event.type = XINE_EVENT_INPUT_MENU7; break; case EMOTION_EVENT_UP: xine_event.type = XINE_EVENT_INPUT_UP; break; case EMOTION_EVENT_DOWN: xine_event.type = XINE_EVENT_INPUT_DOWN; break; case EMOTION_EVENT_LEFT: xine_event.type = XINE_EVENT_INPUT_LEFT; break; case EMOTION_EVENT_RIGHT: xine_event.type = XINE_EVENT_INPUT_RIGHT; break; case EMOTION_EVENT_SELECT: xine_event.type = XINE_EVENT_INPUT_SELECT; break; case EMOTION_EVENT_NEXT: xine_event.type = XINE_EVENT_INPUT_NEXT; break; case EMOTION_EVENT_PREV: xine_event.type = XINE_EVENT_INPUT_PREVIOUS; break; case EMOTION_EVENT_ANGLE_NEXT: xine_event.type = XINE_EVENT_INPUT_ANGLE_NEXT; break; case EMOTION_EVENT_ANGLE_PREV: xine_event.type = XINE_EVENT_INPUT_ANGLE_PREVIOUS; break; case EMOTION_EVENT_FORCE: xine_event.type = XINE_EVENT_INPUT_BUTTON_FORCE; break; case EMOTION_EVENT_0: xine_event.type = XINE_EVENT_INPUT_NUMBER_0; break; case EMOTION_EVENT_1: xine_event.type = XINE_EVENT_INPUT_NUMBER_1; break; case EMOTION_EVENT_2: xine_event.type = XINE_EVENT_INPUT_NUMBER_2; break; case EMOTION_EVENT_3: xine_event.type = XINE_EVENT_INPUT_NUMBER_3; break; case EMOTION_EVENT_4: xine_event.type = XINE_EVENT_INPUT_NUMBER_4; break; case EMOTION_EVENT_5: xine_event.type = XINE_EVENT_INPUT_NUMBER_5; break; case EMOTION_EVENT_6: xine_event.type = XINE_EVENT_INPUT_NUMBER_6; break; case EMOTION_EVENT_7: xine_event.type = XINE_EVENT_INPUT_NUMBER_7; break; case EMOTION_EVENT_8: xine_event.type = XINE_EVENT_INPUT_NUMBER_8; break; case EMOTION_EVENT_9: xine_event.type = XINE_EVENT_INPUT_NUMBER_9; break; case EMOTION_EVENT_10: xine_event.type = XINE_EVENT_INPUT_NUMBER_10_ADD; break; default: return; break; } xine_event_send(ev->stream, &xine_event); } static void em_event_mouse_button_feed(void *ef, int button EINA_UNUSED, int x, int y) { Emotion_Xine_Video *ev; xine_event_t xine_event; xine_input_data_t xine_input; ev = (Emotion_Xine_Video *)ef; if ((ev->opening) || (!ev->play_ok)) return; xine_event.stream = ev->stream; gettimeofday(&xine_event.tv, NULL); xine_event.type = XINE_EVENT_INPUT_MOUSE_BUTTON; xine_input.button = 1; xine_input.x = x; xine_input.y = y; xine_event.data = &xine_input; xine_event.data_length = sizeof(xine_input); xine_event_send(ev->stream, &xine_event); } static void em_event_mouse_move_feed(void *ef, int x, int y) { Emotion_Xine_Video *ev; xine_event_t xine_event; xine_input_data_t xine_input; ev = (Emotion_Xine_Video *)ef; if ((ev->opening) || (!ev->play_ok)) return; xine_event.stream = ev->stream; gettimeofday(&xine_event.tv, NULL); xine_event.type = XINE_EVENT_INPUT_MOUSE_MOVE; xine_input.button = 0; xine_input.x = x; xine_input.y = y; xine_event.data = &xine_input; xine_event.data_length = sizeof(xine_input); xine_event_send(ev->stream, &xine_event); } static int em_video_channel_count(void *ef) { Emotion_Xine_Video *ev; int v; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; v = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_CHANNELS); if ((v < 1) && xine_get_stream_info(ev->stream, XINE_STREAM_INFO_HAS_VIDEO)) return 1; return v; } static void em_video_channel_set(void *ef, int channel) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (channel < 0) channel = 0; ev->video_channel = channel; _em_slave_event(ev, 14, NULL); } static int em_video_channel_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; return xine_get_param(ev->stream, XINE_PARAM_VIDEO_CHANNEL); } static void em_video_subtitle_file_set(void *video EINA_UNUSED, const char *filepath EINA_UNUSED) { DBG("video_subtitle_file_set not implemented for xine yet."); } static const char * em_video_subtitle_file_get(void *video EINA_UNUSED) { DBG("video_subtitle_file_get not implemented for xine yet."); return NULL; } static const char * em_video_channel_name_get(void *ef EINA_UNUSED, int channel EINA_UNUSED) { return NULL; } static void em_video_channel_mute_set(void *ef, int mute) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; ev->video_mute = mute; } static int em_video_channel_mute_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return ev->video_mute; } static int em_audio_channel_count(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; return xine_get_stream_info(ev->stream, XINE_STREAM_INFO_MAX_AUDIO_CHANNEL); } static void em_audio_channel_set(void *ef, int channel) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (channel < -1) channel = -1; ev->audio_channel = channel; _em_slave_event(ev, 13, NULL); } static int em_audio_channel_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; return xine_get_param(ev->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL); } static const char * em_audio_channel_name_get(void *ef, int channel) { Emotion_Xine_Video *ev; static char lang[XINE_LANG_MAX + 1]; ev = (Emotion_Xine_Video *)ef; if (ev->opening) return NULL; lang[0] = 0; if (xine_get_audio_lang(ev->stream, channel, lang)) return lang; return NULL; } static void em_audio_channel_mute_set(void *ef, int mute) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; ev->audio_mute = mute; _em_slave_event(ev, 12, NULL); } static int em_audio_channel_mute_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return ev->audio_mute; } static void em_audio_channel_volume_set(void *ef, double vol) { Emotion_Xine_Video *ev; if (vol < 0.0) vol = 0.0; else if (vol > 1.0) vol = 1.0; ev = (Emotion_Xine_Video *)ef; ev->volume = vol; _em_slave_event(ev, 10, NULL); } static double em_audio_channel_volume_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return ev->volume; ev->volume = xine_get_param(ev->stream, XINE_PARAM_AUDIO_VOLUME) / 100.0; return ev->volume; } static int em_spu_channel_count(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; return xine_get_stream_info(ev->stream, XINE_STREAM_INFO_MAX_SPU_CHANNEL); } static void em_spu_channel_set(void *ef, int channel) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (channel < 0) channel = 0; ev->spu_channel = channel; _em_slave_event(ev, 9, NULL); } static int em_spu_channel_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; return xine_get_param(ev->stream, XINE_PARAM_SPU_CHANNEL); } static const char * em_spu_channel_name_get(void *ef, int channel) { Emotion_Xine_Video *ev; static char lang[XINE_LANG_MAX + 1]; ev = (Emotion_Xine_Video *)ef; if (ev->opening) return NULL; lang[0] = 0; if (xine_get_spu_lang(ev->stream, channel, lang)) return lang; return NULL; } static void em_spu_channel_mute_set(void *ef, int mute) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; ev->spu_mute = mute; _em_slave_event(ev, 8, NULL); } static int em_spu_channel_mute_get(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; return ev->spu_mute; } static int em_chapter_count(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if (ev->opening || (!ev->play_ok)) return 0; if (xine_get_stream_info(ev->stream, XINE_STREAM_INFO_HAS_CHAPTERS)) return 99; return 0; } static void em_chapter_set(void *ef EINA_UNUSED, int chapter EINA_UNUSED) { } static int em_chapter_get(void *ef EINA_UNUSED) { return 0; } static const char * em_chapter_name_get(void *ef EINA_UNUSED, int chapter EINA_UNUSED) { return NULL; } static void em_speed_set(void *ef EINA_UNUSED, double speed EINA_UNUSED) { } static double em_speed_get(void *ef EINA_UNUSED) { return 1.0; } static int em_eject(void *ef) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; _em_slave_event(ev, 7, NULL); return 1; } static const char * em_meta_get(void *ef, int meta) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)ef; if ((ev->opening) || (!ev->play_ok)) return NULL; switch (meta) { case META_TRACK_TITLE: return xine_get_meta_info(ev->stream, XINE_META_INFO_TITLE); break; case META_TRACK_ARTIST: return xine_get_meta_info(ev->stream, XINE_META_INFO_ARTIST); break; case META_TRACK_GENRE: return xine_get_meta_info(ev->stream, XINE_META_INFO_GENRE); break; case META_TRACK_COMMENT: return xine_get_meta_info(ev->stream, XINE_META_INFO_COMMENT); break; case META_TRACK_ALBUM: return xine_get_meta_info(ev->stream, XINE_META_INFO_ALBUM); break; case META_TRACK_YEAR: return xine_get_meta_info(ev->stream, XINE_META_INFO_YEAR); break; case META_TRACK_DISCID: return xine_get_meta_info(ev->stream, XINE_META_INFO_CDINDEX_DISCID); break; default: break; } return NULL; } static Eina_Bool _em_fd_active(void *data EINA_UNUSED, Ecore_Fd_Handler *fdh) { void *buf; int fd, len; Emotion_Xine_Video_Frame *fr; fd = ecore_main_fd_handler_fd_get(fdh); if (fd < 0) return EINA_TRUE; while ((len = read(fd, &buf, sizeof(buf))) > 0) { if (len == sizeof(buf)) { Emotion_Xine_Video *ev; fr = buf; ev = _emotion_video_get(fr->obj); if (ev) { em_frame_done(ev); ev->cur_frame = fr; _em_get_pos_len(ev); if ((xine_get_stream_info(ev->stream, XINE_STREAM_INFO_HAS_VIDEO)) && (xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_HANDLED))) { if (ev->video_mute) em_frame_done(ev); else _emotion_frame_new(fr->obj); } _emotion_frame_resize(fr->obj, fr->w, fr->h, fr->ratio); _emotion_video_pos_update(fr->obj, ev->pos, ev->len); } } } return EINA_TRUE; } static void _em_event(void *data, const xine_event_t *event) { void *buf[2]; Emotion_Xine_Event *new_ev; Emotion_Xine_Video *ev; ev = data; new_ev = calloc(1, sizeof(Emotion_Xine_Event)); if (!new_ev) return; new_ev->mtype = 0; new_ev->type = event->type; if (event->data) { new_ev->xine_event = malloc(event->data_length); if (!new_ev->xine_event) { free(new_ev); return; } memcpy(new_ev->xine_event, event->data, event->data_length); } buf[0] = data; buf[1] = new_ev; if (write(ev->fd_ev_write, buf, sizeof(buf)) < 0) perror("write"); } static void _em_module_event(void *data, int type) { void *buf[2]; Emotion_Xine_Event *new_ev; Emotion_Xine_Video *ev; ev = data; new_ev = calloc(1, sizeof(Emotion_Xine_Event)); if (!new_ev) return; new_ev->mtype = type; new_ev->type = -1; buf[0] = data; buf[1] = new_ev; if (write(ev->fd_ev_write, buf, sizeof(buf)) < 0) perror("write"); } static Eina_Bool _em_audio_only_poller(void *data) { Emotion_Xine_Video *ev; ev = data; _em_get_pos_len(ev); return EINA_TRUE; } static Eina_Bool _em_fd_ev_active(void *data EINA_UNUSED, Ecore_Fd_Handler *fdh) { int fd, len; void *buf[2]; fd = ecore_main_fd_handler_fd_get(fdh); while ((len = read(fd, buf, sizeof(buf))) > 0) { if (len == sizeof(buf)) { Emotion_Xine_Video *ev; Emotion_Xine_Event *eev; ev = buf[0]; eev = buf[1]; if (eev->mtype != 0) { switch (eev->mtype) { case 1: /* init done */ ev->play_ok = 1; break; case 2: /* open done */ ev->play_ok = 1; if (ev->anim) { ecore_animator_del(ev->anim); ev->anim = NULL; } _emotion_open_done(ev->obj); _emotion_frame_resize(ev->obj, ev->w, ev->h, ev->ratio); break; case 3: /* shutdown done */ if (ev->anim) { ecore_animator_del(ev->anim); ev->anim = NULL; } ev->play_ok = 1; break; case 4: /* play done */ ev->play_ok = 1; if (ev->anim) { ecore_animator_del(ev->anim); ev->anim = NULL; } if ((!(xine_get_stream_info(ev->stream, XINE_STREAM_INFO_HAS_VIDEO) && xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_HANDLED))) && (xine_get_stream_info(ev->stream, XINE_STREAM_INFO_HAS_AUDIO) && xine_get_stream_info(ev->stream, XINE_STREAM_INFO_AUDIO_HANDLED))) ev->anim = ecore_evas_animator_add(ev->obj, _em_audio_only_poller, ev); _emotion_playback_started(ev->obj); break; case 5: /* stop done */ if (ev->anim) { ecore_animator_del(ev->anim); ev->anim = NULL; } ev->play_ok = 1; break; case 6: /* seek done */ ev->play_ok = 1; _emotion_seek_done(ev->obj); _em_get_pos_len(ev); _emotion_video_pos_update(ev->obj, ev->pos, ev->len); break; case 7: /* eject done */ if (ev->anim) { ecore_animator_del(ev->anim); ev->anim = NULL; } ev->play_ok = 1; break; case 8: /* spu mute done */ ev->play_ok = 1; break; case 9: /* channel done */ ev->play_ok = 1; break; case 10: /* volume done */ ev->play_ok = 1; break; case 11: /* close done */ if (ev->anim) { ecore_animator_del(ev->anim); ev->anim = NULL; } ev->play_ok = 1; break; case 15: /* get pos done */ if (!EINA_DBL_EQ(ev->last_pos, ev->pos)) { ev->last_pos = ev->pos; _emotion_video_pos_update(ev->obj, ev->pos, ev->len); } break; default: break; } } else { switch (eev->type) { case XINE_EVENT_UI_PLAYBACK_FINISHED: { ev->play = 0; _emotion_decode_stop(ev->obj); _emotion_playback_finished(ev->obj); } break; case XINE_EVENT_UI_CHANNELS_CHANGED: { _emotion_channels_change(ev->obj); } break; case XINE_EVENT_UI_SET_TITLE: { xine_ui_data_t *e; e = (xine_ui_data_t *)eev->xine_event; _emotion_title_set(ev->obj, e->str); } break; case XINE_EVENT_FRAME_FORMAT_CHANGE: break; case XINE_EVENT_UI_MESSAGE: { WRN("UI Message [FIXME: break this out to emotion api]"); // e->type = error type(XINE_MSG_NO_ERROR, XINE_MSG_GENERAL_WARNING, XINE_MSG_UNKNOWN_HOST etc.) // e->messages is a list of messages DOUBLE null terminated } break; case XINE_EVENT_AUDIO_LEVEL: { _emotion_audio_level_change(ev->obj); WRN("Audio Level [FIXME: break this out to emotion api]"); // e->left (0->100) // e->right // e->mute } break; case XINE_EVENT_PROGRESS: { xine_progress_data_t *e; e = (xine_progress_data_t *)eev->xine_event; DBG("PROGRESS: %i", e->percent); ev->buffer = e->percent; _emotion_progress_set(ev->obj, (char *)e->description, (double)e->percent / 100.0); } break; case XINE_EVENT_MRL_REFERENCE_EXT: { xine_mrl_reference_data_ext_t *e; e = (xine_mrl_reference_data_ext_t *)eev->xine_event; _emotion_file_ref_set(ev->obj, e->mrl, e->alternative); } break; case XINE_EVENT_UI_NUM_BUTTONS: { xine_ui_data_t *e; e = (xine_ui_data_t *)eev->xine_event; _emotion_spu_button_num_set(ev->obj, e->num_buttons); } break; case XINE_EVENT_SPU_BUTTON: { xine_spu_button_t *e; e = (xine_spu_button_t *)eev->xine_event; if (e->direction == 1) _emotion_spu_button_set(ev->obj, e->button); else _emotion_spu_button_set(ev->obj, -1); } break; case XINE_EVENT_DROPPED_FRAMES: { xine_dropped_frames_t *e; e = (xine_dropped_frames_t *)eev->xine_event; WRN("Dropped Frames (skipped %i) (discarded %i) [FIXME: break this out to the emotion api]", e->skipped_frames, e->discarded_frames); // e->skipped_frames = % frames skipped * 10 // e->discarded_frames = % frames skipped * 10 } break; default: // DBG("unknown event type %i", eev->type); break; } } if (eev->xine_event) free(eev->xine_event); free(eev); } } return EINA_TRUE; } static void * _em_get_pos_len_th(void *par) { Emotion_Xine_Video *ev; ev = (Emotion_Xine_Video *)par; for (;;) { pthread_mutex_lock(&(ev->get_pos_len_mutex)); pthread_cond_wait(&(ev->get_pos_len_cond), &(ev->get_pos_len_mutex)); pthread_mutex_unlock(&(ev->get_pos_len_mutex)); if (ev->get_poslen) { int pos_stream = 0; int pos_time = 0; int length_time = 0; if (xine_get_pos_length(ev->stream, &pos_stream, &pos_time, &length_time)) { if (length_time == 0) { ev->pos = (double)pos_stream / 65535; ev->len = 1.0; ev->no_time = 1; } else { ev->pos = (double)pos_time / 1000.0; ev->len = (double)length_time / 1000.0; ev->no_time = 0; } } ev->get_poslen = 0; _em_module_event(ev, 15); /* event - getpos done */ //DBG("get pos %3.3f", ev->pos); } if (ev->delete_me) { ev->get_pos_thread_deleted = 1; return NULL; } } return NULL; } static void _em_get_pos_len(Emotion_Xine_Video *ev) { if (!ev->play_ok) return; ev->get_poslen = 1; pthread_mutex_lock(&(ev->get_pos_len_mutex)); pthread_cond_broadcast(&(ev->get_pos_len_cond)); pthread_mutex_unlock(&(ev->get_pos_len_mutex)); } static const Emotion_Engine em_engine = { EMOTION_ENGINE_API_VERSION, EMOTION_ENGINE_PRIORITY_DEFAULT, "xine", em_add, /* add */ em_del, /* del */ 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_buffer_size_get, /* buffer_size_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_subtitle_file_set, /* video_subtitle_file_set */ em_video_subtitle_file_get, /* video_subtitle_file_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, /* priority_set */ NULL, /* priority_get */ NULL /* em_meta_artwork_get */ }; Eina_Bool xine_module_init(void) { if (_emotion_init_count > 0) { _emotion_init_count++; return EINA_TRUE; } eina_threads_init(); eina_log_threads_enable(); _emotion_xine_log_domain = eina_log_domain_register ("emotion-xine", EINA_COLOR_LIGHTCYAN); if (_emotion_xine_log_domain < 0) { EINA_LOG_CRIT("Could not register log domain 'emotion-xine'"); return EINA_FALSE; } if (!_emotion_module_register(&em_engine)) { CRI("Could not register module %p", &em_engine); eina_log_domain_unregister(_emotion_xine_log_domain); _emotion_xine_log_domain = -1; return EINA_FALSE; } _emotion_init_count = 1; return EINA_TRUE; } void xine_module_shutdown(void) { if (_emotion_init_count > 1) { _emotion_init_count--; return; } else if (_emotion_init_count == 0) { EINA_LOG_ERR("too many xine_module_shutdown()"); return; } _emotion_init_count = 0; _emotion_module_unregister(&em_engine); eina_log_domain_unregister(_emotion_xine_log_domain); _emotion_xine_log_domain = -1; } #ifndef EMOTION_STATIC_BUILD_XINE EINA_MODULE_INIT(xine_module_init); EINA_MODULE_SHUTDOWN(xine_module_shutdown); #endif #if 0 void em_debug(Emotion_Xine_Video *ev) { int has_chapters = 0; int max_spu = 0; int max_audio = 0; int video_channels = 0; int video_streams = 0; int video_seekable = 0; char *title; char *comment; char *artist; char *genre; char *album; char *year; char *cdindex_discid; int video_channel = 0; int audio_channel = 0; int spu_channel = 0; int video_ratio = 0; int audio_mode = 0; // return; has_chapters = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_HAS_CHAPTERS); max_spu = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_MAX_SPU_CHANNEL); max_audio = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_MAX_AUDIO_CHANNEL); video_channels = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_CHANNELS); video_streams = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_STREAMS); video_seekable = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_SEEKABLE); title = xine_get_meta_info(ev->stream, XINE_META_INFO_TITLE); comment = xine_get_meta_info(ev->stream, XINE_META_INFO_COMMENT); artist = xine_get_meta_info(ev->stream, XINE_META_INFO_ARTIST); genre = xine_get_meta_info(ev->stream, XINE_META_INFO_GENRE); album = xine_get_meta_info(ev->stream, XINE_META_INFO_ALBUM); year = xine_get_meta_info(ev->stream, XINE_META_INFO_YEAR); cdindex_discid = xine_get_meta_info(ev->stream, XINE_META_INFO_CDINDEX_DISCID); video_channel = xine_get_param(ev->stream, XINE_PARAM_VIDEO_CHANNEL); audio_channel = xine_get_param(ev->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL); spu_channel = xine_get_param(ev->stream, XINE_PARAM_SPU_CHANNEL); video_ratio = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_VIDEO_RATIO); audio_mode = xine_get_stream_info(ev->stream, XINE_STREAM_INFO_AUDIO_MODE); DBG("has_chapters = %i", has_chapters); DBG("max_spu = %i", max_spu); DBG("max_audio = %i", max_audio); DBG("video_channels = %i", video_channels); DBG("video_streams = %i", video_streams); DBG("video_seekable = %i", video_seekable); DBG("title = %s", title); DBG("comment = %s", comment); DBG("artist = %s", artist); DBG("genre = %s", genre); DBG("album = %s", album); DBG("year = %s", year); DBG("cdindex_discid = %s", cdindex_discid); DBG("video_channel = %i", video_channel); DBG("audio_channel = %i", audio_channel); DBG("spu_channels = %i", spu_channel); DBG("video_ratio = %i", video_ratio); DBG("audio_mode = %i", audio_mode); { int i; for (i = 0; i <= max_audio; i++) { char lang[XINE_LANG_MAX + 1]; char buf[128] = "NONE"; lang[0] = 0; if (xine_get_audio_lang(ev->stream, i, lang)) eina_strlcpy(buf, lang, sizeof(buf)); DBG(" AUDIO %i = %s", i, buf); } for (i = 0; i <= max_spu; i++) { char lang[XINE_LANG_MAX + 1]; char buf[128] = "NONE"; lang[0] = 0; if (xine_get_spu_lang(ev->stream, i, lang)) eina_strlcpy(buf, lang, sizeof(buf)); DBG(" SPU %i = %s", i, buf); } } } #endif