/* XMPS - X Movie Player System * Copyright (C) 1999 Damien Chavarria * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public Licensse as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * libxmps.h : global types and definitions. * */ /***************************************************************************** * INCLUDES * *****************************************************************************/ #include "libxmps.h" unsigned int ids = 0; short int xmps_video_conversion_matrix[6][6] = { /* src | dest -> */ /* RGB8 */ /* RGB15 */ /* RGB16 */ /* RGB24 */ /* RGB32 */ /* YUV12 */ /* XMPS_VIDEO_RGB8 */ { 1, 0, 1, 1, 1, 1 }, /* XMPS_VIDEO_RGB15 */ { 0, 1, 1, 0, 0, 0 }, /* XMPS_VIDEO_RGB16 */ { 0, 0, 1, 0, 0, 0 }, /* XMPS_VIDEO_RGB24 */ { 0, 0, 1, 1, 0, 1 }, /* XMPS_VIDEO_RGB32 */ { 0, 0, 0, 0, 1, 0 }, /* XMPS_VIDEO_YUV12 */ { 0, 0, 0, 0, 0, 1 } }; /* * Util functions * */ float xmps_time_diff(struct timeval *start_time) { struct timeval new_time; int sec, usec; gettimeofday(&new_time, 0); sec = new_time.tv_sec - start_time->tv_sec; usec = new_time.tv_usec - start_time->tv_usec; if (usec < 0) { usec = usec + 1000000; sec = sec - 1; } return sec + usec/1000000.0; } /***************************************************************************** * LIBRARY_FUNCTIONS * *****************************************************************************/ xmps_session_t *xmps_init(int argc, char **argv, xmps_config_file_t *config_file) { xmps_session_t *xmps_session; libxmps_color_init(); xmps_session = (xmps_session_t *) malloc(sizeof(xmps_session_t)); xmps_session->plugin_center = (xmps_plugin_center_t *) malloc(sizeof(xmps_plugin_center_t)); xmps_session->plugin_graph = (xmps_plugin_graph_t *) malloc(sizeof(xmps_plugin_graph_t)); /* * inititalize plugin * center object * */ if(config_file == NULL) { xmps_session->plugin_center->config_file = xmps_config_file_new(); } else { xmps_session->plugin_center->config_file = config_file; } xmps_session->plugin_center->gui_plugins_list = NULL; xmps_session->plugin_center->media_plugins_list = NULL; xmps_session->plugin_center->system_plugins_list = NULL; xmps_session->plugin_center->video_decoder_plugins_list = NULL; xmps_session->plugin_center->audio_decoder_plugins_list = NULL; xmps_session->plugin_center->video_renderer_plugins_list = NULL; xmps_session->plugin_center->audio_renderer_plugins_list = NULL; xmps_session->plugin_center->video_filter_plugins_list = NULL; xmps_session->plugin_center->audio_filter_plugins_list = NULL; xmps_session->plugin_center->video_addon_plugins_list = NULL; /* * intitialize the * plugin graph * */ xmps_session->plugin_graph->nodes = NULL; xmps_session->plugin_graph->links = NULL; xmps_session->user_data = NULL; /* * and retrun the new * session object * */ return xmps_session; } /* * internal : gives * unique plugin id; * */ unsigned int xmps_get_plugin_id() { return ids++; } unsigned int xmps_load_plugin(xmps_session_t *xmps_session, char *plugin_path) { void *h; void *(*gpi) (void *cfgfile); /* * check for consistency * */ if(xmps_session == NULL) { XMPS_DEBUG("null session info!"); return 0; } #ifdef RTLD_NOW if ((h = dlopen(plugin_path, RTLD_LAZY)) != NULL) #else if ((h = dlopen(plugin_path, 0)) != NULL) #endif { if ((gpi = dlsym(h, "get_gui_info")) != NULL) { xmps_gui_plugin_t *g; g = (xmps_gui_plugin_t *) gpi(xmps_session->plugin_center->config_file); g->id = xmps_get_plugin_id(); XMPS_DEBUG("registering gui : %s", g->name); xmps_session->plugin_center->gui_plugins_list = g_list_prepend(xmps_session->plugin_center->gui_plugins_list, (xmps_gui_plugin_t *) g); } if ((gpi = dlsym(h, "get_media_info")) != NULL) { xmps_media_plugin_t *m; m = (xmps_media_plugin_t *) gpi(xmps_session->plugin_center->config_file); m->id = xmps_get_plugin_id(); XMPS_DEBUG("registering media : %s", m->name); xmps_session->plugin_center->media_plugins_list = g_list_prepend(xmps_session->plugin_center->media_plugins_list, (xmps_media_plugin_t *) m); } if ((gpi = dlsym(h, "get_system_info")) != NULL) { xmps_system_plugin_t *s; s = (xmps_system_plugin_t *) gpi(xmps_session->plugin_center->config_file); s->id = xmps_get_plugin_id(); XMPS_DEBUG("registering system : %s", s->name); xmps_session->plugin_center->system_plugins_list = g_list_prepend(xmps_session->plugin_center->system_plugins_list, (xmps_system_plugin_t *) s); } if ((gpi = dlsym(h, "get_video_decoder_info")) != NULL) { xmps_video_decoder_plugin_t *v; v = (xmps_video_decoder_plugin_t *) gpi(xmps_session->plugin_center->config_file); v->id = xmps_get_plugin_id(); XMPS_DEBUG("registering video codec : %s", v->name); xmps_session->plugin_center->video_decoder_plugins_list = g_list_prepend(xmps_session->plugin_center->video_decoder_plugins_list, (xmps_video_decoder_plugin_t *) v); } if ((gpi = dlsym(h, "get_video_renderer_info")) != NULL) { xmps_video_renderer_plugin_t *r; r = (xmps_video_renderer_plugin_t *) gpi(xmps_session->plugin_center->config_file); r->id = xmps_get_plugin_id(); XMPS_DEBUG("registering video renderer : %s", r->name); xmps_session->plugin_center->video_renderer_plugins_list = g_list_prepend(xmps_session->plugin_center->video_renderer_plugins_list, (xmps_video_renderer_plugin_t *) r); } if ((gpi = dlsym(h, "get_audio_decoder_info")) != NULL) { xmps_audio_decoder_plugin_t *a; a = (xmps_audio_decoder_plugin_t *) gpi(xmps_session->plugin_center->config_file); a->id = xmps_get_plugin_id(); XMPS_DEBUG("registering audio decoder : %s", a->name); xmps_session->plugin_center->audio_decoder_plugins_list = g_list_prepend(xmps_session->plugin_center->audio_decoder_plugins_list, (xmps_audio_decoder_plugin_t *) a); } if ((gpi = dlsym(h, "get_audio_renderer_info")) != NULL) { xmps_audio_renderer_plugin_t *r; r = (xmps_audio_renderer_plugin_t *) gpi(xmps_session->plugin_center->config_file); r->id = xmps_get_plugin_id(); XMPS_DEBUG("registering audio renderer : %s", r->name); xmps_session->plugin_center->audio_renderer_plugins_list = g_list_prepend(xmps_session->plugin_center->audio_renderer_plugins_list, (xmps_audio_renderer_plugin_t *) r); } if ((gpi = dlsym(h, "get_video_addon_info")) != NULL) { xmps_video_addon_plugin_t *ad; ad = (xmps_video_addon_plugin_t *) gpi(xmps_session->plugin_center->config_file); ad->id = xmps_get_plugin_id(); XMPS_DEBUG("registering video addon : %s", ad->name); xmps_session->plugin_center->video_addon_plugins_list = g_list_prepend(xmps_session->plugin_center->video_addon_plugins_list, (xmps_video_addon_plugin_t *) ad); } return 1; } else { XMPS_DEBUG("error in loading plugin : %s", dlerror()); return 0; } return 0; } /* * private : scans a directory * */ int xmps_plugin_scan_dir(xmps_session_t *xmps_session, char *dirname) { char *filename; char *ext; DIR *dir; struct dirent *ent; struct stat statbuf; if(xmps_session == NULL) { XMPS_DEBUG("null session info!"); return 0; } /* * open the directory * */ dir = opendir(dirname); if (dir) { XMPS_DEBUG("scanning %s", dirname); while ((ent = readdir(dir)) != NULL) { filename = (char *) malloc(strlen(dirname) + strlen(ent->d_name) + 2); sprintf(filename, "%s/%s", dirname, ent->d_name); if (!stat(filename, &statbuf)) { if (S_ISREG(statbuf.st_mode)) { if ((ext = strrchr(ent->d_name, '.')) != NULL) { if (!strcmp(ext, ".so")) { xmps_load_plugin(xmps_session, filename); } else { g_free(filename); } } else { g_free(filename); } } else { g_free(filename); } } else { g_free(filename); } } } else { return 0; } return 1; } unsigned int xmps_load_all(xmps_session_t *xmps_session) { if(xmps_session == NULL || xmps_session->plugin_center == NULL) { XMPS_DEBUG("xrong parameters!"); return 0; } xmps_plugin_scan_dir(xmps_session, GUI_PLUGIN_DIR); xmps_plugin_scan_dir(xmps_session, MEDIA_PLUGIN_DIR); xmps_plugin_scan_dir(xmps_session, SYSTEM_PLUGIN_DIR); xmps_plugin_scan_dir(xmps_session, VIDEO_CODEC_PLUGIN_DIR); xmps_plugin_scan_dir(xmps_session, VIDEO_RENDERER_PLUGIN_DIR); xmps_plugin_scan_dir(xmps_session, AUDIO_CODEC_PLUGIN_DIR); xmps_plugin_scan_dir(xmps_session, AUDIO_RENDERER_PLUGIN_DIR); xmps_plugin_scan_dir(xmps_session, VIDEO_ADDON_PLUGIN_DIR); return 1; } xmps_plugin_types_t xmps_get_plugin_type_from_id(xmps_session_t *session, unsigned int id) { GList *node; if(session == NULL || session->plugin_center == NULL) { return XMPS_PLUGIN_NONE; } for(node = session->plugin_center->gui_plugins_list; node != NULL; node = g_list_next(node)) { xmps_gui_plugin_t *gui; gui = (xmps_gui_plugin_t *) node->data; if(gui != NULL && gui->id == id) { return XMPS_PLUGIN_GUI; } } for(node = session->plugin_center->media_plugins_list; node != NULL; node = g_list_next(node)) { xmps_media_plugin_t *media; media = (xmps_media_plugin_t *) node->data; if(media != NULL && media->id == id) { return XMPS_PLUGIN_MEDIA; } } for(node = session->plugin_center->system_plugins_list; node != NULL; node = g_list_next(node)) { xmps_system_plugin_t *system; system = (xmps_system_plugin_t *) node->data; if(system != NULL && system->id == id) { return XMPS_PLUGIN_SYSTEM; } } for(node = session->plugin_center->video_decoder_plugins_list; node != NULL; node = g_list_next(node)) { xmps_video_decoder_plugin_t *video_decoder; video_decoder = (xmps_video_decoder_plugin_t *) node->data; if(video_decoder != NULL && video_decoder->id == id) { return XMPS_PLUGIN_VIDEO_DECODER; } } for(node = session->plugin_center->video_renderer_plugins_list; node != NULL; node = g_list_next(node)) { xmps_video_renderer_plugin_t *video_renderer; video_renderer = (xmps_video_renderer_plugin_t *) node->data; if(video_renderer != NULL && video_renderer->id == id) { return XMPS_PLUGIN_VIDEO_RENDERER; } } for(node = session->plugin_center->audio_decoder_plugins_list; node != NULL; node = g_list_next(node)) { xmps_audio_decoder_plugin_t *audio_decoder; audio_decoder = (xmps_audio_decoder_plugin_t *) node->data; if(audio_decoder != NULL && audio_decoder->id == id) { return XMPS_PLUGIN_AUDIO_DECODER; } } for(node = session->plugin_center->audio_renderer_plugins_list; node != NULL; node = g_list_next(node)) { xmps_audio_renderer_plugin_t *audio_renderer; audio_renderer = (xmps_audio_renderer_plugin_t *) node->data; if(audio_renderer != NULL && audio_renderer->id == id) { return XMPS_PLUGIN_AUDIO_RENDERER; } } return XMPS_PLUGIN_NONE; } void *xmps_get_plugin_from_id(xmps_session_t *session, unsigned int id) { GList *node; if(session == NULL || session->plugin_center == NULL) { return NULL; } for(node = session->plugin_center->gui_plugins_list; node != NULL; node = g_list_next(node)) { xmps_gui_plugin_t *gui; gui = (xmps_gui_plugin_t *) node->data; if(gui != NULL && gui->id == id) { return (void *) gui; } } for(node = session->plugin_center->media_plugins_list; node != NULL; node = g_list_next(node)) { xmps_media_plugin_t *media; media = (xmps_media_plugin_t *) node->data; if(media != NULL && media->id == id) { return (void *) media; } } for(node = session->plugin_center->system_plugins_list; node != NULL; node = g_list_next(node)) { xmps_system_plugin_t *system; system = (xmps_system_plugin_t *) node->data; if(system != NULL && system->id == id) { return (void *) system; } } for(node = session->plugin_center->video_decoder_plugins_list; node != NULL; node = g_list_next(node)) { xmps_video_decoder_plugin_t *video_decoder; video_decoder = (xmps_video_decoder_plugin_t *) node->data; if(video_decoder != NULL && video_decoder->id == id) { return (void *) video_decoder; } } for(node = session->plugin_center->video_renderer_plugins_list; node != NULL; node = g_list_next(node)) { xmps_video_renderer_plugin_t *video_renderer; video_renderer = (xmps_video_renderer_plugin_t *) node->data; if(video_renderer != NULL && video_renderer->id == id) { return (void *) video_renderer; } } for(node = session->plugin_center->audio_decoder_plugins_list; node != NULL; node = g_list_next(node)) { xmps_audio_decoder_plugin_t *audio_decoder; audio_decoder = (xmps_audio_decoder_plugin_t *) node->data; if(audio_decoder != NULL && audio_decoder->id == id) { return (void *) audio_decoder; } } for(node = session->plugin_center->audio_renderer_plugins_list; node != NULL; node = g_list_next(node)) { xmps_audio_renderer_plugin_t *audio_renderer; audio_renderer = (xmps_audio_renderer_plugin_t *) node->data; if(audio_renderer != NULL && audio_renderer->id == id) { return (void *) audio_renderer; } } return NULL; } unsigned int xmps_playback_init(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL) { return 0; } playback = (xmps_playback_t *) malloc(sizeof(xmps_playback_t)); playback->playing = 0; playback->media = NULL; playback->system = NULL; playback->video_decoder = NULL; playback->audio_decoder = NULL; playback->video_renderer = NULL; playback->audio_renderer = NULL; playback->video_info = NULL; playback->audio_info = NULL; playback->audio_buffer = NULL; playback->video_buffer = NULL; playback->video_surface = NULL; playback->conversion_surface = NULL; playback->audio_setup_ok = 0; playback->video_setup_ok = 0; playback->media_name = NULL; xmps_session->user_data = (void *) playback; return 1; } unsigned int xmps_playback_setup_video(xmps_session_t *xmps_session, unsigned int use_it, unsigned int x_decal, unsigned int y_decal, unsigned int windowid, char *video_renderer_label) { GList *renderer_node; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } ((xmps_playback_t *) xmps_session->user_data)->use_video = use_it; ((xmps_playback_t *) xmps_session->user_data)->video_renderer = NULL; XMPS_DEBUG("video setup"); /* * we need to find the * wanted renderer */ for(renderer_node = xmps_session->plugin_center->video_renderer_plugins_list; renderer_node != NULL; renderer_node = g_list_next(renderer_node)) { if(renderer_node->data != NULL) { xmps_video_renderer_plugin_t *video_renderer; video_renderer = (xmps_video_renderer_plugin_t *) renderer_node->data; XMPS_DEBUG("trying %s renderer", video_renderer->name); if( strstr(video_renderer->name, video_renderer_label) != NULL ) { XMPS_DEBUG("selecting %s renderer", video_renderer->name); ((xmps_playback_t *) xmps_session->user_data)->video_renderer = video_renderer; } } } if(((xmps_playback_t *) xmps_session->user_data)->video_renderer == NULL) { return 0; } ((xmps_playback_t *) xmps_session->user_data)->video_info = (xmps_video_info_t *) malloc(sizeof(xmps_video_info_t)); ((xmps_playback_t *) xmps_session->user_data)->video_info->windowid = windowid; ((xmps_playback_t *) xmps_session->user_data)->video_info->x = x_decal; ((xmps_playback_t *) xmps_session->user_data)->video_info->y = y_decal; return 1; } unsigned int xmps_playback_setup_audio(xmps_session_t *xmps_session, unsigned int use_it, char *audio_renderer_label) { GList *renderer_node; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } ((xmps_playback_t *) xmps_session->user_data)->use_audio = use_it; ((xmps_playback_t *) xmps_session->user_data)->audio_info = (xmps_audio_info_t *) malloc(sizeof(xmps_audio_info_t)); XMPS_DEBUG("audio setup"); /* * we need to find the * wanted renderer * */ for(renderer_node = xmps_session->plugin_center->audio_renderer_plugins_list; renderer_node != NULL; renderer_node = g_list_next(renderer_node)) { if(renderer_node->data != NULL) { xmps_audio_renderer_plugin_t *audio_renderer; audio_renderer = (xmps_audio_renderer_plugin_t *) renderer_node->data; XMPS_DEBUG("trying %s renderer", audio_renderer->name); if( strstr(audio_renderer->name, audio_renderer_label) != NULL ) { XMPS_DEBUG("selecting %s renderer", audio_renderer->name); ((xmps_playback_t *) xmps_session->user_data)->audio_renderer = audio_renderer; } } } return 1; } void xmps_playback_audio_callback(void *user_data, void *buffer, int length) { xmps_playback_t *playback; if(user_data == NULL) { return; } playback = (xmps_playback_t *) user_data; if(playback->audio_decoder == NULL) { return; } if(playback->playing) { if(!playback->audio_decoder->decompress(playback->audio_decoder, NULL, buffer, length)) { memset(buffer, 0, length); return; } playback->audio_bytes += length; } } unsigned int xmps_playback_connect_audio(xmps_session_t *xmps_session, xmps_audio_decoder_plugin_t *audio_decoder, xmps_audio_renderer_plugin_t *audio_renderer) { GList *decoder_formats; GList *renderer_formats; xmps_audio_info_t *decoder_info; if(xmps_session == NULL || audio_decoder == NULL || audio_renderer == NULL) { return 0; } XMPS_DEBUG("begining audio adaptation"); for(decoder_formats = (GList *) audio_decoder->get(audio_decoder, XMPS_FLAG_AUDIO_FORMAT_LIST, NULL); decoder_formats != NULL; decoder_formats = g_list_next(decoder_formats)) { for(renderer_formats = (GList *) audio_renderer->get(audio_renderer, XMPS_FLAG_AUDIO_FORMAT_LIST, NULL); renderer_formats != NULL; renderer_formats = g_list_next(renderer_formats)) { xmps_audio_format_t *decoder_format = (xmps_audio_format_t *) decoder_formats->data; xmps_audio_format_t *renderer_format = (xmps_audio_format_t *) renderer_formats->data; if(decoder_format->type == renderer_format->type) { /* * we found a compatibility * */ audio_decoder->set(audio_decoder, XMPS_FLAG_AUDIO_FORMAT, (void *) decoder_format); audio_renderer->set(audio_renderer, XMPS_FLAG_AUDIO_FORMAT, (void *) renderer_format); decoder_info = (xmps_audio_info_t *) audio_decoder->get(audio_decoder, XMPS_FLAG_AUDIO_INFO, NULL); if(decoder_info != NULL) { if(audio_renderer->set(audio_renderer, XMPS_FLAG_AUDIO_INFO, (void *) decoder_info) == 1) { /* * yep! we got it, now set * callback information */ ((xmps_playback_t *) xmps_session->user_data)->audio_info = decoder_info; audio_renderer->set(audio_renderer, XMPS_FLAG_AUDIO_CALLBACK, (void *) xmps_playback_audio_callback); audio_renderer->set(audio_renderer, XMPS_FLAG_USER_DATA, (void *) xmps_session->user_data); ((xmps_playback_t *) xmps_session->user_data)->audio_setup_ok = 1; return 1; } } } } } return 0; } unsigned int xmps_playback_connect_video(xmps_session_t *xmps_session, xmps_video_decoder_plugin_t *video_decoder, xmps_video_renderer_plugin_t *video_renderer) { GList *decoder_formats; GList *renderer_formats; xmps_video_info_t *decoder_info; xmps_playback_t *playback; if(xmps_session == NULL || video_decoder == NULL || video_renderer == NULL) { return 0; } XMPS_DEBUG("begining video adaptation"); playback = (xmps_playback_t *) xmps_session->user_data; /* * first pass to try and find * a common format * */ for(decoder_formats = (GList *) video_decoder->get(video_decoder, XMPS_FLAG_VIDEO_FORMAT_LIST, NULL); decoder_formats != NULL; decoder_formats = g_list_next(decoder_formats)) { for(renderer_formats = (GList *) video_renderer->get(video_renderer, XMPS_FLAG_VIDEO_FORMAT_LIST, NULL); renderer_formats != NULL; renderer_formats = g_list_next(renderer_formats)) { xmps_video_format_t *decoder_format = (xmps_video_format_t *) decoder_formats->data; xmps_video_format_t *renderer_format = (xmps_video_format_t *) renderer_formats->data; if(decoder_format->type == renderer_format->type) { /* * we found a compatibility * */ XMPS_DEBUG("found a compatible format, %d", decoder_format->type); video_decoder->set(video_decoder, XMPS_FLAG_VIDEO_FORMAT, (void *) decoder_format); video_renderer->set(video_renderer, XMPS_FLAG_VIDEO_FORMAT, (void *) renderer_format); decoder_info = (xmps_video_info_t *) video_decoder->get(video_decoder, XMPS_FLAG_VIDEO_INFO, NULL); if(decoder_info != NULL) { ((xmps_playback_t *) xmps_session->user_data)->video_info->width = decoder_info->width; ((xmps_playback_t *) xmps_session->user_data)->video_info->height = decoder_info->height; ((xmps_playback_t *) xmps_session->user_data)->video_info->format.type = renderer_format->type; ((xmps_playback_t *) xmps_session->user_data)->video_info->format.bpp = renderer_format->bpp; XMPS_DEBUG("video adaptation : %dx%d window id is %d", ((xmps_playback_t *) xmps_session->user_data)->video_info->width, ((xmps_playback_t *) xmps_session->user_data)->video_info->height, ((xmps_playback_t *) xmps_session->user_data)->video_info->windowid); if(video_renderer->set(video_renderer, XMPS_FLAG_VIDEO_INFO, (void *) ((xmps_playback_t *) xmps_session->user_data)->video_info) == 1) { /* * yep! we got it, now get * information about buffers */ ((xmps_playback_t *) xmps_session->user_data)->video_surface = video_renderer->get(video_renderer, XMPS_FLAG_VIDEO_BUFFER, NULL); ((xmps_playback_t *) xmps_session->user_data)->conversion_surface = NULL; ((xmps_playback_t *) xmps_session->user_data)->video_setup_ok = 1; return 1; } } } } } /* * if we're here we * found nothing so we try * finding a conversion * */ for(decoder_formats = (GList *) video_decoder->get(video_decoder, XMPS_FLAG_VIDEO_FORMAT_LIST, NULL); decoder_formats != NULL; decoder_formats = g_list_next(decoder_formats)) { for(renderer_formats = (GList *) video_renderer->get(video_renderer, XMPS_FLAG_VIDEO_FORMAT_LIST, NULL); renderer_formats != NULL; renderer_formats = g_list_next(renderer_formats)) { xmps_video_format_t *decoder_format = (xmps_video_format_t *) decoder_formats->data; xmps_video_format_t *renderer_format = (xmps_video_format_t *) renderer_formats->data; XMPS_DEBUG("trying from %d to %d", decoder_format->type, renderer_format->type); if( xmps_video_conversion_matrix[decoder_format->type][renderer_format->type] == 1 ) { /* * we found something! * */ XMPS_DEBUG("found conversion from %d to %d", decoder_format->type, renderer_format->type); video_decoder->set(video_decoder, XMPS_FLAG_VIDEO_FORMAT, (void *) decoder_format); video_renderer->set(video_renderer, XMPS_FLAG_VIDEO_FORMAT, (void *) renderer_format); decoder_info = (xmps_video_info_t *) video_decoder->get(video_decoder, XMPS_FLAG_VIDEO_INFO, NULL); ((xmps_playback_t *) xmps_session->user_data)->video_info->width = decoder_info->width; ((xmps_playback_t *) xmps_session->user_data)->video_info->height = decoder_info->height; ((xmps_playback_t *) xmps_session->user_data)->video_info->palette = decoder_info->palette; ((xmps_playback_t *) xmps_session->user_data)->video_info->format.type = renderer_format->type; ((xmps_playback_t *) xmps_session->user_data)->video_info->format.bpp = renderer_format->bpp; if(video_renderer->set(video_renderer, XMPS_FLAG_VIDEO_INFO, (void *) ((xmps_playback_t *) xmps_session->user_data)->video_info) == 1) { /* * yep! we got it, now get * information about buffers */ ((xmps_playback_t *) xmps_session->user_data)->video_surface = video_renderer->get(video_renderer, XMPS_FLAG_VIDEO_BUFFER, NULL); ((xmps_playback_t *) xmps_session->user_data)->conversion_surface = malloc(decoder_info->width*decoder_info->height*decoder_info->format.bpp/8); playback->video_decoder_format = decoder_format; playback->video_renderer_format = renderer_format; ((xmps_playback_t *) xmps_session->user_data)->video_setup_ok = 1; return 1; } } } } XMPS_DEBUG("could not connect video decoder to video renderer"); return 0; } unsigned int xmps_playback_open(xmps_session_t *xmps_session, char *video_path) { unsigned int setup_ok; GList *media_node; GList *system_node; GList *codec_node; GList *media_output; GList *system_output; GList *addon_node; xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->plugin_center == NULL) { XMPS_DEBUG("open, wrong params!"); return 0; } if(xmps_session->user_data == NULL) { XMPS_DEBUG("you must call xmps_playback_init() first!"); return 0; } playback = (xmps_playback_t *) xmps_session->user_data; setup_ok = 0; /* * find appropriate media */ for( media_node = xmps_session->plugin_center->media_plugins_list; media_node != NULL; media_node = g_list_next(media_node) ) { if(media_node->data != NULL) { xmps_media_plugin_t *media; media = (xmps_media_plugin_t *) media_node->data; if(media->open(media, video_path) == 1) { XMPS_DEBUG("openning media with %s Media", media->name); /* * we found it, store it * into the plugin graph * */ xmps_plugin_graph_add_node(xmps_session->plugin_graph, XMPS_PLUGIN_MEDIA, (void *) media); playback->media = media; playback->media_name = video_path; /* * now search for * a system layer * */ for(system_node = xmps_session->plugin_center->system_plugins_list; system_node != NULL; system_node = g_list_next(system_node) ) { if(system_node->data != NULL) { xmps_system_plugin_t *system; system = (xmps_system_plugin_t *) system_node->data; XMPS_DEBUG("selecting %s system", system->name); if(system->open(system) == 1) { /* * we search a raw output */ for(media_output = (GList *) media->get(media, XMPS_FLAG_OUTPUT_LIST, NULL); media_output != NULL; media_output = g_list_next(media_output)) { if(media_output->data != NULL) { xmps_data_t *data_stream; data_stream = (xmps_data_t *) media_output->data; XMPS_DEBUG("found an output data stream of type %d", data_stream->type); if(system->set(system, XMPS_FLAG_INPUT, data_stream) == 1) { unsigned int setup_enough = 0; /* * we found a system layer * we put it into the graph * */ XMPS_DEBUG("openning media stream with %s System", system->name); xmps_plugin_graph_add_node(xmps_session->plugin_graph, XMPS_PLUGIN_SYSTEM, (void *) system); xmps_plugin_graph_add_link(xmps_session->plugin_graph, (void *) media, (void *) system); playback->system = system; /* * now ask for output data * and search for decoders * */ for(system_output = (GList *) system->get(system, XMPS_FLAG_OUTPUT_LIST, NULL); system_output != NULL; system_output = g_list_next(system_output)) { if(system_output->data != NULL) { xmps_data_t *data_stream; data_stream = (xmps_data_t *) system_output->data; XMPS_DEBUG("found an output data stream of type %d", data_stream->type); switch(data_stream->type) { case XMPS_DATA_RAW: /* * stream type is not known * we need to try all decoders * */ for(codec_node = xmps_session->plugin_center->video_decoder_plugins_list; codec_node != NULL; codec_node = g_list_next(codec_node)) { if(codec_node->data != NULL) { xmps_video_decoder_plugin_t *video_decoder; video_decoder = (xmps_video_decoder_plugin_t *) codec_node->data; if(video_decoder->open(video_decoder) == 1) { XMPS_DEBUG("selecting %s Video Decoder", video_decoder->name); if(video_decoder->set(video_decoder, XMPS_FLAG_INPUT, (void *) data_stream) == 1) { /* * we found a video decoder * for this data stream * */ XMPS_DEBUG("using %s Video Codec to play the stream", video_decoder->name); xmps_plugin_graph_add_node(xmps_session->plugin_graph, XMPS_PLUGIN_VIDEO_DECODER, (void *) video_decoder); xmps_plugin_graph_add_link(xmps_session->plugin_graph, (void *) system, (void *) video_decoder); playback->video_decoder = video_decoder; /* * now we need to adapt that * video stream to a renderer * */ if(((xmps_playback_t *) xmps_session->user_data)->video_renderer == NULL) { XMPS_DEBUG("you must select a video renderer first!"); return 0; } if( xmps_playback_connect_video(xmps_session, video_decoder, ((xmps_playback_t *) xmps_session->user_data)->video_renderer) == 1 ) { playback->video_data = data_stream; setup_enough = 1; break; } else { return 0; } } } } } /* * if we didnt' found anything * we try audio decoders * */ if(!setup_enough) { for(codec_node = xmps_session->plugin_center->audio_decoder_plugins_list; codec_node != NULL; codec_node = g_list_next(codec_node)) { if(codec_node->data != NULL) { xmps_audio_decoder_plugin_t *audio_decoder; audio_decoder = (xmps_audio_decoder_plugin_t *) codec_node->data; if(audio_decoder->open(audio_decoder) == 1) { XMPS_DEBUG("selecting %s Audio Decoder", audio_decoder->name); if(audio_decoder->set(audio_decoder, XMPS_FLAG_INPUT, (void *) data_stream) == 1) { /* * we found a audio decoder * for this data stream * */ XMPS_DEBUG("using %s Audio Codec to play the stream", audio_decoder->name); xmps_plugin_graph_add_node(xmps_session->plugin_graph, XMPS_PLUGIN_AUDIO_DECODER, (void *) audio_decoder); xmps_plugin_graph_add_link(xmps_session->plugin_graph, (void *) system, (void *) audio_decoder); playback->audio_decoder = audio_decoder; /* * now we need to adapt that * audio stream to a renderer * */ if(((xmps_playback_t *) xmps_session->user_data)->audio_renderer == NULL) { XMPS_DEBUG("you must select a audio renderer first!"); return 0; } if( xmps_playback_connect_audio(xmps_session, audio_decoder, ((xmps_playback_t *) xmps_session->user_data)->audio_renderer) == 1 ) { playback->audio_data = data_stream; setup_enough = 1; break; } } } } } } break; case XMPS_DATA_VIDEO_COMP: /* * stream is compressed video * so we search a video decoder * */ for(codec_node = xmps_session->plugin_center->video_decoder_plugins_list; codec_node != NULL; codec_node = g_list_next(codec_node)) { if(codec_node->data != NULL) { xmps_video_decoder_plugin_t *video_decoder; video_decoder = (xmps_video_decoder_plugin_t *) codec_node->data; if(video_decoder->open(video_decoder) == 1) { XMPS_DEBUG("selecting %s Video Decoder", video_decoder->name); if(video_decoder->set(video_decoder, XMPS_FLAG_INPUT, (void *) data_stream) == 1) { /* * we found a video decoder * for this data stream * */ XMPS_DEBUG("using %s Video Codec to play the stream", video_decoder->name); xmps_plugin_graph_add_node(xmps_session->plugin_graph, XMPS_PLUGIN_VIDEO_DECODER, (void *) video_decoder); xmps_plugin_graph_add_link(xmps_session->plugin_graph, (void *) system, (void *) video_decoder); playback->video_decoder = video_decoder; /* * now we need to adapt that * video stream to a renderer * */ if(((xmps_playback_t *) xmps_session->user_data)->video_renderer == NULL) { XMPS_DEBUG("you must select a video renderer first!"); return 0; } if( xmps_playback_connect_video(xmps_session, video_decoder, ((xmps_playback_t *) xmps_session->user_data)->video_renderer) == 1 ) { playback->video_data = data_stream; setup_enough = 1; break; } } } } } break; case XMPS_DATA_AUDIO_COMP: /* * stream is compressed audio * so we search an audio decoder * */ for(codec_node = xmps_session->plugin_center->audio_decoder_plugins_list; codec_node != NULL; codec_node = g_list_next(codec_node)) { if(codec_node->data != NULL) { xmps_audio_decoder_plugin_t *audio_decoder; audio_decoder = (xmps_audio_decoder_plugin_t *) codec_node->data; if(audio_decoder->open(audio_decoder) == 1) { XMPS_DEBUG("selecting %s Audio Decoder", audio_decoder->name); if(audio_decoder->set(audio_decoder, XMPS_FLAG_INPUT, (void *) data_stream) == 1) { /* * we found a audio decoder * for this data stream * */ XMPS_DEBUG("using %s Audio Codec to play the stream", audio_decoder->name); xmps_plugin_graph_add_node(xmps_session->plugin_graph, XMPS_PLUGIN_AUDIO_DECODER, (void *) audio_decoder); xmps_plugin_graph_add_link(xmps_session->plugin_graph, (void *) system, (void *) audio_decoder); playback->audio_decoder = audio_decoder; /* * now we need to adapt that * audio stream to a renderer * */ if(((xmps_playback_t *) xmps_session->user_data)->audio_renderer == NULL) { XMPS_DEBUG("you must select a audio renderer first!"); return 0; } if( xmps_playback_connect_audio(xmps_session, audio_decoder, ((xmps_playback_t *) xmps_session->user_data)->audio_renderer) == 1 ) { playback->audio_data = data_stream; setup_enough = 1; break; } } } } } break; case XMPS_DATA_SUBTITLES: { XMPS_DEBUG("found a subtitles stream"); for(addon_node = xmps_session->plugin_center->video_addon_plugins_list; addon_node != NULL; addon_node = g_list_next(addon_node)) { if(addon_node->data != NULL) { xmps_video_addon_plugin_t *addon; addon = (xmps_video_addon_plugin_t *) addon_node->data; XMPS_DEBUG("Selecting %s for subtitles", addon->name); if(addon->set(addon, XMPS_FLAG_INPUT, (void *) data_stream) == 1) { /* * we found a subtitler */ XMPS_DEBUG("using %s as subtitler", addon->name); playback->subtitles = addon; } } } } break; default: break; } } } if(setup_enough == 1) { return 1; } } } } } } } return 0; } } } return 0; } void *xmps_video_only_thread(void *data) { xmps_playback_t *playback; unsigned int *buffer_size; void *video_buffer = NULL; struct timeval start_time; long long audio_time = 0; long long video_time = 0; int diff; XMPS_DEBUG("start playing!"); playback = (xmps_playback_t *) data; if(playback == NULL) { XMPS_DEBUG("oops!"); return NULL; } gettimeofday(&start_time, 0); while(playback->playing) { buffer_size = (unsigned int *) playback->system->get(playback->system, XMPS_FLAG_VIDEO_BUFFER_SIZE, NULL); if(video_buffer == NULL ) { video_buffer = malloc(100000); } playback->system->read(playback->system, playback->video_data->id, video_buffer, *buffer_size); if(playback->conversion_surface == NULL) { if (!playback->video_decoder->decompress(playback->video_decoder, video_buffer, playback->video_surface, *buffer_size)) { playback->playing = 0; } playback->current_frame++; } else { if (!playback->video_decoder->decompress(playback->video_decoder, video_buffer, playback->conversion_surface, *buffer_size)) { playback->playing = 0; } playback->current_frame++; xmps_video_conversion(playback->video_decoder_format, playback->video_renderer_format, playback->video_info, playback->conversion_surface, playback->video_surface); } audio_time = 1000 * xmps_time_diff(&start_time); video_time = playback->current_frame * 1000 / (*((double *) playback->system->get(playback->system, XMPS_FLAG_FRAMERATE, NULL))); diff = video_time - audio_time; if (diff > 0) { usleep(diff*1000/2); } playback->video_renderer->draw(playback->video_renderer, playback->video_surface); } return NULL; } void *xmps_video_synched_on_audio_thread(void *data) { xmps_playback_t *playback; unsigned int *buffer_size; void *video_buffer = NULL; double audio_time = 0; double video_time = 0; int diff; XMPS_DEBUG("start playing synched!"); playback = (xmps_playback_t *) data; if(playback == NULL) { XMPS_DEBUG("oops! problem"); return NULL; } while(playback->playing) { buffer_size = (unsigned int *) playback->system->get(playback->system, XMPS_FLAG_VIDEO_BUFFER_SIZE, NULL); if(video_buffer == NULL ) { video_buffer = malloc(100000); } if( *buffer_size && !playback->system->read(playback->system, playback->video_data->id, video_buffer, *buffer_size)) { playback->playing = 0; } if(playback->conversion_surface == NULL) { if(!playback->video_decoder->decompress(playback->video_decoder, video_buffer, playback->video_surface, *buffer_size)) { playback->playing = 0; } playback->current_frame++; } else { if(playback->conversion_surface == NULL || buffer_size == NULL) { XMPS_DEBUG("null surface"); } if(!playback->video_decoder->decompress(playback->video_decoder, video_buffer, playback->conversion_surface, *buffer_size)) { playback->playing = 0; } playback->current_frame++; xmps_video_conversion(playback->video_decoder_format, playback->video_renderer_format, playback->video_info, playback->conversion_surface, playback->video_surface); } /* * do synchro here; * */ if(playback->audio_info != NULL && playback->video_info != NULL) { audio_time = ((unsigned long long) playback->audio_bytes / (playback->audio_info->sample_size) )*1000/playback->audio_info->frequency; video_time = playback->current_frame * 1000 / (*((double *) playback->system->get(playback->system, XMPS_FLAG_FRAMERATE, NULL))); diff = video_time - audio_time; if(diff > 50) { usleep(35000); } if(diff < - 150) { buffer_size = (unsigned int *) playback->system->get(playback->system, XMPS_FLAG_VIDEO_BUFFER_SIZE, NULL); playback->system->read(playback->system, playback->video_data->id, video_buffer, *buffer_size); playback->video_decoder->set(playback->video_decoder, XMPS_FLAG_DROPFRAME, (void *) video_buffer); playback->current_frame++; } if(diff < - 230) { buffer_size = (unsigned int *) playback->system->get(playback->system, XMPS_FLAG_VIDEO_BUFFER_SIZE, NULL); playback->system->read(playback->system, playback->video_data->id, video_buffer, *buffer_size); playback->video_decoder->set(playback->video_decoder, XMPS_FLAG_DROPFRAME, (void *) video_buffer); playback->current_frame++; } } /* * apply subtitles if any */ if(playback->subtitles != NULL) { playback->subtitles->apply(playback->subtitles, playback->video_surface, playback->video_info, video_time); } playback->video_renderer->draw(playback->video_renderer, playback->video_surface); } return NULL; } unsigned int xmps_playback_play(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; /* * check for audio && video * */ if(playback->video_setup_ok && playback->audio_setup_ok) { /* * creating video synched thread * */ playback->playing = 1; pthread_create(&playback->main_thread, NULL, xmps_video_synched_on_audio_thread, (void *) playback); playback->audio_bytes = 0; playback->current_frame = 0; playback->audio_renderer->start(playback->audio_renderer); return 1; } /* * only video? * */ if(playback->video_setup_ok) { /* * creating video only thread * */ playback->playing = 1; playback->current_frame = 0; pthread_create(&playback->main_thread, NULL, xmps_video_only_thread, (void *) playback); return 1; } /* * only audio? * */ if(playback->audio_setup_ok && playback->audio_renderer != NULL) { playback->audio_bytes = 0; playback->audio_renderer->start(playback->audio_renderer); playback->playing = 1; } return 1; } unsigned int xmps_playback_pause(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; if(playback->playing) { XMPS_DEBUG("pausing!"); playback->playing = 0; if(playback->video_setup_ok && playback->use_video) { XMPS_DEBUG("waiting for video thread"); pthread_join(playback->main_thread, NULL); } } else { playback->playing = 1; XMPS_DEBUG("restarting playback"); if(playback->video_setup_ok && playback->audio_setup_ok) { /* * creating video synched thread * */ XMPS_DEBUG("restarting video thread"); memset(&playback->main_thread, 0, sizeof(pthread_t)); pthread_create(&playback->main_thread, NULL, xmps_video_synched_on_audio_thread, (void *) playback); XMPS_DEBUG("video thread created!"); return 1; } if(playback->video_setup_ok) { /* * creating video only thread * */ pthread_create(&playback->main_thread, NULL, xmps_video_only_thread, (void *) playback); return 1; } } return 1; } unsigned int xmps_playback_seek(xmps_session_t *xmps_session, unsigned short percent) { xmps_playback_t *playback; unsigned int has_to_replay = 0; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; if(playback->playing) { has_to_replay = 1; xmps_playback_pause(xmps_session); } if(playback->system != NULL) { playback->system->seek(playback->system, 0, percent, XMPS_SEEK_PERCENT); /* * reset synchro * */ playback->audio_bytes = 0; playback->current_frame = 0; } if(playback->audio_setup_ok && playback->use_audio && playback->audio_decoder != NULL) { playback->audio_decoder->set(playback->audio_decoder, XMPS_FLAG_EMPTY_BUFFERS, NULL); } if(has_to_replay) { xmps_playback_pause(xmps_session); } /* * mute for renderer's BUFFER_TIME * * */ return 1; } unsigned int xmps_playback_stop(xmps_session_t *xmps_session) { xmps_playback_t *playback; unsigned int volume = 0; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; if(playback->playing) { if(playback->video_setup_ok && playback->use_video) { playback->playing = 0; pthread_join(playback->main_thread, NULL); } if(playback->audio_renderer != NULL) { playback->playing = 1; playback->audio_renderer->set(playback->audio_renderer, XMPS_FLAG_AUDIO_VOLUME, &volume); /* * wait "buffering" time * that's about 0.5 sec. */ usleep(200000); playback->playing = 0; } if(playback->audio_setup_ok && playback->use_audio && playback->audio_decoder != NULL) { playback->audio_decoder->set(playback->audio_decoder, XMPS_FLAG_EMPTY_BUFFERS, NULL); } } xmps_playback_seek(xmps_session, 0); playback->audio_bytes = 0; playback->current_frame = 0; volume = 100; if(playback->audio_renderer != NULL) { playback->audio_renderer->set(playback->audio_renderer, XMPS_FLAG_AUDIO_VOLUME, &volume); } return 1; } unsigned int xmps_playback_set_volume(xmps_session_t *xmps_session, unsigned int volume) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; if(playback->audio_renderer != NULL) { playback->audio_renderer->set(playback->audio_renderer, XMPS_FLAG_AUDIO_VOLUME, &volume); } return 1; } xmps_video_info_t *xmps_playback_get_video_info(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; return playback->video_info; } xmps_audio_info_t *xmps_playback_get_audio_info(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; return playback->audio_info; } xmps_time_t *xmps_playback_get_time_info(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return NULL; } playback = (xmps_playback_t *) xmps_session->user_data; if(playback->system == NULL) { return NULL; } return (xmps_time_t *) playback->system->get(playback->system, XMPS_FLAG_TIME_INFO, NULL); } unsigned int xmps_playback_video_resize(xmps_session_t *xmps_session, unsigned int new_width, unsigned int new_height) { xmps_playback_t *playback; xmps_video_info_t info; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; if(playback->video_renderer == NULL) { return 0; } info.width = new_width; info.height = new_height; xmps_playback_pause(xmps_session); playback->video_renderer->set(playback->video_renderer, XMPS_FLAG_VIDEO_RESIZE, &info); playback->video_surface = playback->video_renderer->get(playback->video_renderer, XMPS_FLAG_VIDEO_BUFFER, NULL); xmps_playback_pause(xmps_session); return 1; } unsigned int xmps_playback_set_loop(xmps_session_t *session, unsigned int on) { return 1; } unsigned int xmps_playback_video_fullscreen(xmps_session_t *xmps_session, unsigned int on) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; if(playback->video_renderer == NULL) { return 0; } xmps_playback_pause(xmps_session); if(playback->video_renderer != NULL) { if(on) { XMPS_DEBUG("setting fullscreen"); playback->video_renderer->set(playback->video_renderer, XMPS_FLAG_FULLSCREEN_SET, NULL); XMPS_DEBUG("fullscreen done"); playback->video_surface = playback->video_renderer->get(playback->video_renderer, XMPS_FLAG_VIDEO_BUFFER, NULL); } else { XMPS_DEBUG("quiting fullscreen mode!"); playback->video_renderer->set(playback->video_renderer, XMPS_FLAG_FULLSCREEN_UNSET, NULL); XMPS_DEBUG("fullscreen done"); playback->video_surface = playback->video_renderer->get(playback->video_renderer, XMPS_FLAG_VIDEO_BUFFER, NULL); } } xmps_playback_pause(xmps_session); return 1; } unsigned int xmps_playback_is_playing(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; return (playback->playing == 1); } unsigned int xmps_playback_has_audio(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; return (playback->audio_setup_ok == 1); } unsigned int xmps_playback_has_video(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; return (playback->video_setup_ok == 1); } unsigned int xmps_playback_close(xmps_session_t *xmps_session) { xmps_playback_t *playback; if(xmps_session == NULL || xmps_session->user_data == NULL) { return 0; } playback = (xmps_playback_t *) xmps_session->user_data; if(playback->playing) { xmps_playback_stop(xmps_session); } if(playback->media != NULL) { playback->media->close(playback->media); } if(playback->system != NULL) { playback->system->close(playback->system); } if(playback->video_renderer != NULL) { playback->video_renderer->close(playback->video_renderer); } if(playback->video_decoder != NULL) { playback->video_decoder->close(playback->video_decoder); } if(playback->audio_renderer != NULL) { playback->audio_renderer->close(playback->audio_renderer); } if(playback->audio_decoder != NULL) { playback->audio_decoder->close(playback->audio_decoder); } if(playback->subtitles != NULL) { playback->subtitles->close(playback->subtitles); } if(playback->audio_renderer != NULL) { playback->audio_renderer->close(playback->audio_renderer); } if(playback->video_renderer != NULL) { playback->video_renderer->close(playback->video_renderer); } playback->audio_data = NULL; playback->video_data = NULL; playback->video_info = NULL; playback->audio_info = NULL; playback->media = NULL; playback->system = NULL; playback->video_decoder = NULL; playback->audio_decoder = NULL; playback->subtitles = NULL; playback->video_surface = NULL; playback->conversion_surface = NULL; playback->video_setup_ok = 0; playback->audio_setup_ok = 0; playback->playing = 0; return 1; } void xmps_quit() { }