/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2000-2003 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine 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 * * $Id: xine.c,v 1.5 2004/02/23 04:44:16 rwlin Exp $ */ /* * top-level xine functions */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #if defined (__linux__) #include #elif defined (__FreeBSD__) #include #endif /********** logging **********/ #define LOG_MODULE "xine" #define LOG_VERBOSE /* #define LOG */ #define XINE_ENABLE_EXPERIMENTAL_FEATURES 1 #include "xine_internal.h" #include "plugin_catalog.h" #include "audio_out.h" #include "video_out.h" #include "demuxers/demux.h" #include "buffer.h" #include "spu_decoder.h" #include "input/input_plugin.h" #include "metronom.h" #include "configfile.h" #include "osd.h" #include "xineutils.h" #include "compat.h" #ifdef WIN32 # include # include #endif /* WIN32 */ extern int XINE_SEG_ERROR; extern char *XINE_SEG_ERROR_FILE; //#define LOG_SEG_ERROR sprintf(XINE_SEG_ERROR_FILE,"%s (%d)",__FILE__,__LINE__); #define LOG_SEG_ERROR void xine_handle_stream_end (xine_stream_t *stream, int non_user) { if (stream->status == XINE_STATUS_QUIT) return; stream->status = XINE_STATUS_STOP; /* join thread if needed to fix resource leaks */ xine_demux_stop_thread( stream ); if (non_user) { /* frontends will not be interested in receiving this event * if they have called xine_stop explicitly, so only send * it if stream playback finished because of stream end reached */ xine_event_t event; event.data_length = 0; event.type = XINE_EVENT_UI_PLAYBACK_FINISHED; xine_event_send (stream, &event); } } void extra_info_reset( extra_info_t *extra_info ) { memset( extra_info, 0, sizeof(extra_info_t) ); } void extra_info_merge( extra_info_t *dst, extra_info_t *src ) { if (!src->invalid) { if( src->input_pos ) dst->input_pos = src->input_pos; if( src->input_length ) dst->input_length = src->input_length; if( src->input_time ) dst->input_time = src->input_time; if( src->frame_number ) dst->frame_number = src->frame_number; if( src->seek_count ) dst->seek_count = src->seek_count; if( src->vpts ) dst->vpts = src->vpts; } } static void xine_set_speed_internal (xine_stream_t *stream, int speed) { stream->xine->clock->set_speed (stream->xine->clock, speed); LOG_SEG_ERROR /* see coment on audio_out loop about audio_paused */ if( stream->audio_out ) { stream->audio_out->set_property( stream->audio_out, AO_PROP_PAUSED, (speed != XINE_SPEED_NORMAL) + (speed == XINE_SPEED_PAUSE) ); /* * slow motion / fast forward does not play sound, drop buffered * samples from the sound driver */ if (speed != XINE_SPEED_NORMAL && speed != XINE_SPEED_PAUSE) stream->audio_out->control (stream->audio_out, AO_CTRL_FLUSH_BUFFERS); stream->audio_out->control(stream->audio_out, speed == XINE_SPEED_PAUSE ? AO_CTRL_PLAY_PAUSE : AO_CTRL_PLAY_RESUME); } LOG_SEG_ERROR /* added by rwlin@avamax.com */ if(stream->input_plugin && stream->input_plugin->ioctl) stream->input_plugin->ioctl(stream->input_plugin,DVDIOCTL_XINE_SPEED,(void *)speed); LOG_SEG_ERROR } static void xine_stop_internal (xine_stream_t *stream) { int finished_count_audio = 0; int finished_count_video = 0; lprintf ("status before = %d\n", stream->status); if (stream->status == XINE_STATUS_STOP) { lprintf ("ignored\n"); return; } /* make sure we're not in "paused" state */ xine_set_speed_internal (stream, XINE_SPEED_NORMAL); /* Don't change status if we're quitting */ if (stream->status != XINE_STATUS_QUIT) stream->status = XINE_STATUS_STOP; /* * stop demux */ pthread_mutex_lock (&stream->counter_lock); if (stream->audio_fifo) finished_count_audio = stream->finished_count_audio + 1; else finished_count_audio = 0; finished_count_video = stream->finished_count_video + 1; pthread_mutex_unlock (&stream->counter_lock); lprintf ("stopping demux\n"); if (stream->demux_plugin) { xine_demux_stop_thread( stream ); lprintf ("stop thread done\n"); xine_demux_flush_engine( stream ); lprintf ("flush engine done\n"); /* * wait until engine has really stopped */ #if 0 pthread_mutex_lock (&stream->counter_lock); while ((stream->finished_count_audiofinished_count_videocounter_changed, &stream->counter_lock); } pthread_mutex_unlock (&stream->counter_lock); #endif } lprintf ("demux stopped\n"); lprintf ("done\n"); } void xine_stop (xine_stream_t *stream) { pthread_mutex_lock (&stream->frontend_lock); if (stream->audio_out) stream->audio_out->set_property(stream->audio_out, AO_PROP_DISCARD_BUFFERS, 1); if (stream->video_out) stream->video_out->set_property(stream->video_out, VO_PROP_DISCARD_FRAMES, 1); xine_stop_internal (stream); if (stream->slave && (stream->slave_affection & XINE_MASTER_SLAVE_STOP)) xine_stop(stream->slave); if (stream->video_out) stream->video_out->set_property(stream->video_out, VO_PROP_DISCARD_FRAMES, 0); if (stream->audio_out) stream->audio_out->set_property(stream->audio_out, AO_PROP_DISCARD_BUFFERS, 0); pthread_mutex_unlock (&stream->frontend_lock); } static void xine_close_internal (xine_stream_t *stream) { int i ; if( stream->slave ) { xine_close( stream->slave ); if( stream->slave_is_subtitle ) { xine_dispose(stream->slave); stream->slave = NULL; stream->slave_is_subtitle = 0; } } xine_stop_internal( stream ); lprintf ("disposing demux\n"); if (stream->demux_plugin) { stream->demux_plugin->dispose (stream->demux_plugin); stream->demux_plugin = NULL; } /* * close input plugin */ if (stream->input_plugin) { stream->input_plugin->dispose(stream->input_plugin); stream->input_plugin = NULL; } /* * reset / free meta info */ for (i=0; istream_info[i] = 0; if (stream->meta_info[i]) free (stream->meta_info[i]); stream->meta_info[i] = NULL; } } void xine_close (xine_stream_t *stream) { pthread_mutex_lock (&stream->frontend_lock); xine_close_internal (stream); pthread_mutex_unlock (&stream->frontend_lock); } static int xine_stream_rewire_audio(xine_post_out_t *output, void *data) { xine_stream_t *stream = (xine_stream_t *)output->data; xine_audio_port_t *new_port = (xine_audio_port_t *)data; buf_element_t *buf; if (!data) return 0; pthread_mutex_lock(&stream->next_audio_port_lock); stream->next_audio_port = new_port; if (stream->audio_fifo && (buf = stream->audio_fifo->buffer_pool_try_alloc(stream->audio_fifo))) { /* wake up audio decoder thread */ buf->type = BUF_CONTROL_NOP; stream->audio_fifo->insert(stream->audio_fifo, buf); } /* wait till rewiring is finished */ pthread_cond_wait(&stream->next_audio_port_wired, &stream->next_audio_port_lock); pthread_mutex_unlock(&stream->next_audio_port_lock); return 1; } static int xine_stream_rewire_video(xine_post_out_t *output, void *data) { xine_stream_t *stream = (xine_stream_t *)output->data; xine_video_port_t *new_port = (xine_video_port_t *)data; buf_element_t *buf; if (!data) return 0; pthread_mutex_lock(&stream->next_video_port_lock); stream->next_video_port = new_port; if (stream->video_fifo && (buf = stream->video_fifo->buffer_pool_try_alloc(stream->video_fifo))) { /* wake up video decoder thread */ buf->type = BUF_CONTROL_NOP; stream->video_fifo->insert(stream->video_fifo, buf); } /* wait till rewiring is finished */ pthread_cond_wait(&stream->next_video_port_wired, &stream->next_video_port_lock); pthread_mutex_unlock(&stream->next_video_port_lock); return 1; } xine_stream_t *first_stream=NULL; xine_stream_t *xine_stream_new (xine_t *this, xine_audio_port_t *ao, xine_video_port_t *vo) { xine_stream_t *stream; int i; xprintf (this, XINE_VERBOSITY_DEBUG, "xine_stream_new\n"); /* * create a new stream object */ pthread_mutex_lock (&this->streams_lock); stream = (xine_stream_t *) xine_xmalloc (sizeof (xine_stream_t)) ; stream->current_extra_info = malloc( sizeof( extra_info_t ) ); stream->audio_decoder_extra_info = malloc( sizeof( extra_info_t ) ); stream->video_decoder_extra_info = malloc( sizeof( extra_info_t ) ); extra_info_reset( stream->current_extra_info ); extra_info_reset( stream->video_decoder_extra_info ); extra_info_reset( stream->audio_decoder_extra_info ); stream->xine = this; stream->status = XINE_STATUS_STOP; for (i=0; istream_info[i] = 0; stream->meta_info[i] = NULL; } stream->spu_decoder_plugin = NULL; stream->spu_decoder_streamtype = -1; stream->audio_out = ao; stream->audio_channel_user = -1; stream->audio_channel_auto = -1; stream->audio_decoder_plugin = NULL; stream->audio_decoder_streamtype = -1; stream->spu_channel_auto = -1; stream->spu_channel_letterbox = -1; stream->spu_channel_pan_scan = -1; stream->spu_channel_user = -1; stream->spu_channel = -1; stream->video_out = vo; stream->video_driver = vo->driver; stream->video_channel = 0; stream->video_decoder_plugin = NULL; stream->video_decoder_streamtype = -1; stream->header_count_audio = 0; stream->header_count_video = 0; stream->finished_count_audio = 0; stream->finished_count_video = 0; stream->err = 0; stream->next_audio_port = NULL; stream->next_video_port = NULL; stream->metronom_prebuffer = PREBUFFER_PTS_OFFSET; stream->broadcaster = NULL; /* * initial master/slave */ stream->master = stream; stream->slave = NULL; stream->slave_is_subtitle = 0; /* * init mutexes and conditions */ pthread_mutex_init (&stream->demux_lock, NULL); pthread_mutex_init (&stream->frontend_lock, NULL); pthread_mutex_init (&stream->event_queues_lock, NULL); pthread_mutex_init (&stream->osd_lock, NULL); pthread_mutex_init (&stream->counter_lock, NULL); pthread_cond_init (&stream->counter_changed, NULL); pthread_mutex_init (&stream->first_frame_lock, NULL); pthread_cond_init (&stream->first_frame_reached, NULL); pthread_mutex_init (&stream->current_extra_info_lock, NULL); pthread_mutex_init (&stream->next_video_port_lock, NULL); pthread_mutex_init (&stream->next_audio_port_lock, NULL); pthread_cond_init (&stream->next_video_port_wired, NULL); pthread_cond_init (&stream->next_audio_port_wired, NULL); pthread_mutex_init (&stream->vobu_still_lock, NULL); /* added by rwlin@avamax.com */ /* * event queues */ stream->event_queues = xine_list_new (); /* * create a metronom */ stream->metronom = metronom_init ( (ao != NULL), stream); /* * alloc fifos, init and start decoder threads */ video_decoder_init (stream); audio_decoder_init (stream); /* * osd */ stream->osd_renderer = osd_renderer_init (stream->video_out->get_overlay_manager (stream->video_out), stream->xine->config ); /* * register stream */ if(first_stream==NULL) first_stream=stream; xine_list_append_content (this->streams, stream); pthread_mutex_unlock (&this->streams_lock); stream->video_source.name = "video source"; stream->video_source.type = XINE_POST_DATA_VIDEO; stream->video_source.data = stream; stream->video_source.rewire = xine_stream_rewire_video; stream->audio_source.name = "audio source"; stream->audio_source.type = XINE_POST_DATA_AUDIO; stream->audio_source.data = stream; stream->audio_source.rewire = xine_stream_rewire_audio; return stream; } static void mrl_unescape(char *mrl) { int i, len = strlen(mrl); for (i = 0; i < len; i++) { if ((mrl[i]=='%') && (i<(len-2))) { int c; if (sscanf(&mrl[i + 1], "%02x", &c) == 1) { mrl[i]= (char)c; memmove(mrl + i + 1, mrl + i + 3, len - i - 3); len -= 2; } } } mrl[len] = 0; } static int xine_open_internal (xine_stream_t *stream, const char *mrl) { const char *stream_setup; lprintf ("opening MRL '%s'...\n", mrl); /* * stop engine if necessary */ xine_close_internal (stream); lprintf ("engine should be stopped now\n"); /* * look for a stream_setup in MRL and try finding an input plugin */ stream_setup = mrl; /* look for the next '#' or try the whole MRL, if none is found */ while (*stream_setup && (stream_setup = (strchr(stream_setup, '#') ? strchr(stream_setup, '#') : strlen(mrl) + mrl))) { char *input_source = (char *)malloc(stream_setup - mrl + 1); memcpy(input_source, mrl, stream_setup - mrl); input_source[stream_setup - mrl] = '\0'; /* * find an input plugin */ if ((stream->input_plugin = find_input_plugin (stream, input_source))) { free(input_source); break; } free(input_source); /* if we fail when passing up to the first '#' to the input plugins, * maybe the user stated a (invalid) MRL, with a '#' belonging to the * input source -> look for the next '#' and try again */ if (*stream_setup) stream_setup++; } if (!stream->input_plugin) { xine_log (stream->xine, XINE_LOG_MSG, _("xine: cannot find input plugin for MRL [%s]\n"),mrl); stream->err = XINE_ERROR_NO_INPUT_PLUGIN; return 0; } else { xine_log (stream->xine, XINE_LOG_MSG, "xine: found input plugin : %s\n", stream->input_plugin->input_class->get_description(stream->input_plugin->input_class)); if (stream->input_plugin->input_class->eject_media) stream->eject_class = stream->input_plugin->input_class; stream->meta_info[XINE_META_INFO_INPUT_PLUGIN] = strdup (stream->input_plugin->input_class->get_identifier (stream->input_plugin->input_class)); } if (!stream->input_plugin->open(stream->input_plugin)) { xine_log (stream->xine, XINE_LOG_MSG, _("xine: input plugin cannot open MRL [%s]\n"),mrl); stream->input_plugin->dispose(stream->input_plugin); stream->input_plugin = NULL; stream->err = XINE_ERROR_INPUT_FAILED; return 0; } if (*stream_setup) { while (stream_setup && *stream_setup && *(++stream_setup)) { if (strncasecmp(stream_setup, "demux", 5) == 0) { if (*(stream_setup += 5) == ':') { /* demuxer specified by name */ const char *tmp = ++stream_setup; char *demux_name; stream_setup = strchr(stream_setup, ';'); if (stream_setup) { demux_name = (char *)malloc(stream_setup - tmp + 1); memcpy(demux_name, tmp, stream_setup - tmp); demux_name[stream_setup - tmp] = '\0'; } else { demux_name = (char *)malloc(strlen(tmp) + 1); memcpy(demux_name, tmp, strlen(tmp)); demux_name[strlen(tmp)] = '\0'; } mrl_unescape(demux_name); if (!(stream->demux_plugin = find_demux_plugin_by_name(stream, demux_name, stream->input_plugin))) { xine_log(stream->xine, XINE_LOG_MSG, _("xine: specified demuxer %s failed to start\n"), demux_name); stream->err = XINE_ERROR_NO_DEMUX_PLUGIN; stream->status = XINE_STATUS_STOP; free(demux_name); return 0; } stream->meta_info[XINE_META_INFO_SYSTEMLAYER] = strdup (stream->demux_plugin->demux_class->get_identifier(stream->demux_plugin->demux_class)); free(demux_name); } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } continue; } if (strncasecmp(stream_setup, "save", 4) == 0) { if (*(stream_setup += 4) == ':') { /* filename to save */ const char *tmp = ++stream_setup; char *filename; input_plugin_t *input_saver; stream_setup = strchr(stream_setup, ';'); if (stream_setup) { filename = (char *)malloc(stream_setup - tmp + 1); memcpy(filename, tmp, stream_setup - tmp); filename[stream_setup - tmp] = '\0'; } else { filename = (char *)malloc(strlen(tmp) + 1); memcpy(filename, tmp, strlen(tmp)); filename[strlen(tmp)] = '\0'; } xine_log(stream->xine, XINE_LOG_MSG, _("xine: join rip input plugin\n")); input_saver = rip_plugin_get_instance (stream, filename); if( input_saver ) { stream->input_plugin = input_saver; } else { printf("xine: error opening rip input plugin instance\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } continue; } if (strncasecmp(stream_setup, "lastdemuxprobe", 14) == 0) { if (*(stream_setup += 14) == ':') { /* all demuxers will be probed before the specified one */ const char *tmp = ++stream_setup; char *demux_name; stream_setup = strchr(stream_setup, ';'); if (stream_setup) { demux_name = (char *)malloc(stream_setup - tmp + 1); memcpy(demux_name, tmp, stream_setup - tmp); demux_name[stream_setup - tmp] = '\0'; } else { demux_name = (char *)malloc(strlen(tmp) + 1); memcpy(demux_name, tmp, strlen(tmp)); demux_name[strlen(tmp)] = '\0'; } mrl_unescape(demux_name); if (!(stream->demux_plugin = find_demux_plugin_last_probe(stream, demux_name, stream->input_plugin))) { xine_log(stream->xine, XINE_LOG_MSG, _("xine: last_probed demuxer %s failed to start\n"), demux_name); stream->err = XINE_ERROR_NO_DEMUX_PLUGIN; stream->status = XINE_STATUS_STOP; free(demux_name); return 0; } lprintf ("demux and input plugin found\n"); stream->meta_info[XINE_META_INFO_SYSTEMLAYER] = strdup (stream->demux_plugin->demux_class->get_identifier(stream->demux_plugin->demux_class)); free(demux_name); } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } continue; } if (strncasecmp(stream_setup, "novideo", 7) == 0) { stream_setup += 7; if (*stream_setup == ';' || *stream_setup == '\0') { stream->stream_info[XINE_STREAM_INFO_IGNORE_VIDEO] = 1; } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } xprintf (stream->xine, XINE_VERBOSITY_LOG, "ignoring video\n"); continue; } if (strncasecmp(stream_setup, "noaudio", 7) == 0) { stream_setup += 7; if (*stream_setup == ';' || *stream_setup == '\0') { stream->stream_info[XINE_STREAM_INFO_IGNORE_AUDIO] = 1; } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } xprintf (stream->xine, XINE_VERBOSITY_LOG, "ignoring audio\n"); continue; } if (strncasecmp(stream_setup, "nospu", 5) == 0) { stream_setup += 5; if (*stream_setup == ';' || *stream_setup == '\0') { stream->stream_info[XINE_STREAM_INFO_IGNORE_SPU] = 1; } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } xprintf (stream->xine, XINE_VERBOSITY_LOG, "ignoring subpicture\n"); continue; } if (strncasecmp(stream_setup, "volume", 6) == 0) { if (*(stream_setup += 6) == ':') { const char *tmp = ++stream_setup; char *volume; stream_setup = strchr(stream_setup, ';'); if (stream_setup) { volume = (char *)malloc(stream_setup - tmp + 1); memcpy(volume, tmp, stream_setup - tmp); volume[stream_setup - tmp] = '\0'; } else { volume = (char *)malloc(strlen(tmp) + 1); memcpy(volume, tmp, strlen(tmp)); volume[strlen(tmp)] = '\0'; } mrl_unescape(volume); xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME, atoi(volume)); free(volume); } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } continue; } if (strncasecmp(stream_setup, "compression", 11) == 0) { if (*(stream_setup += 11) == ':') { const char *tmp = ++stream_setup; char *compression; stream_setup = strchr(stream_setup, ';'); if (stream_setup) { compression = (char *)malloc(stream_setup - tmp + 1); memcpy(compression, tmp, stream_setup - tmp); compression[stream_setup - tmp] = '\0'; } else { compression = (char *)malloc(strlen(tmp) + 1); memcpy(compression, tmp, strlen(tmp)); compression[strlen(tmp)] = '\0'; } mrl_unescape(compression); xine_set_param(stream, XINE_PARAM_AUDIO_COMPR_LEVEL, atoi(compression)); free(compression); } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } continue; } if (strncasecmp(stream_setup, "subtitle", 8) == 0) { if (*(stream_setup += 8) == ':') { const char *tmp = ++stream_setup; char *subtitle_mrl; stream_setup = strchr(stream_setup, ';'); if (stream_setup) { subtitle_mrl = (char *)malloc(stream_setup - tmp + 1); memcpy(subtitle_mrl, tmp, stream_setup - tmp); subtitle_mrl[stream_setup - tmp] = '\0'; } else { subtitle_mrl = (char *)malloc(strlen(tmp) + 1); memcpy(subtitle_mrl, tmp, strlen(tmp)); subtitle_mrl[strlen(tmp)] = '\0'; } mrl_unescape(subtitle_mrl); stream->slave = xine_stream_new (stream->xine, NULL, stream->video_out ); stream->slave_affection = XINE_MASTER_SLAVE_PLAY | XINE_MASTER_SLAVE_STOP; if( xine_open( stream->slave, subtitle_mrl ) ) { xprintf (stream->xine, XINE_VERBOSITY_LOG, "subtitle mrl opened '%s'\n", subtitle_mrl); stream->slave->master = stream; stream->slave_is_subtitle = 1; } else { printf("xine: error opening subtitle mrl\n"); xine_dispose( stream->slave ); stream->slave = NULL; } free(subtitle_mrl); } else { printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } continue; } { /* when we got here, the stream setup parameter must be a config entry */ const char *tmp = stream_setup; char *config_entry; if ((stream_setup = strchr(stream_setup, ';'))) { config_entry = (char *)malloc(stream_setup - tmp + 1); memcpy(config_entry, tmp, stream_setup - tmp); config_entry[stream_setup - tmp] = '\0'; } else { config_entry = (char *)malloc(strlen(tmp) + 1); memcpy(config_entry, tmp, strlen(tmp)); config_entry[strlen(tmp)] = '\0'; } mrl_unescape(config_entry); if (!xine_config_change_opt(stream->xine->config, config_entry)) { free(config_entry); printf("xine: error while parsing mrl\n"); stream->err = XINE_ERROR_MALFORMED_MRL; stream->status = XINE_STATUS_STOP; return 0; } free(config_entry); } } } if (!stream->demux_plugin) { /* * find a demux plugin */ if (!(stream->demux_plugin=find_demux_plugin (stream, stream->input_plugin))) { xine_log (stream->xine, XINE_LOG_MSG, _("xine: couldn't find demux for >%s<\n"), mrl); stream->err = XINE_ERROR_NO_DEMUX_PLUGIN; stream->status = XINE_STATUS_STOP; /* force the engine to unregister fifo callbacks */ xine_demux_control_nop(stream, BUF_FLAG_END_STREAM); return 0; } lprintf ("demux and input plugin found\n"); stream->meta_info[XINE_META_INFO_SYSTEMLAYER] = strdup (stream->demux_plugin->demux_class->get_identifier(stream->demux_plugin->demux_class)); } xine_log (stream->xine, XINE_LOG_MSG, "xine: found demuxer plugin: %s\n", stream->demux_plugin->demux_class->get_description(stream->demux_plugin->demux_class)); extra_info_reset( stream->current_extra_info ); extra_info_reset( stream->video_decoder_extra_info ); extra_info_reset( stream->audio_decoder_extra_info ); /* assume handled for now. we will only know for sure after trying * to init decoders (which should happen when headers are sent) */ stream->stream_info[XINE_STREAM_INFO_VIDEO_HANDLED] = 1; stream->stream_info[XINE_STREAM_INFO_AUDIO_HANDLED] = 1; /* * send and decode headers */ stream->demux_plugin->send_headers (stream->demux_plugin); if (stream->demux_plugin->get_status(stream->demux_plugin) != DEMUX_OK) { xine_log (stream->xine, XINE_LOG_MSG, _("xine: demuxer failed to start\n")); stream->demux_plugin->dispose (stream->demux_plugin); stream->demux_plugin = NULL; xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "demux disposed\n"); stream->input_plugin->dispose (stream->input_plugin); stream->input_plugin = NULL; stream->err = XINE_ERROR_NO_DEMUX_PLUGIN; /* remove buffered samples from the sound device driver */ /* why? */ /*if (stream->audio_out) stream->audio_out->control (stream->audio_out, AO_CTRL_FLUSH_BUFFERS); */ stream->status = XINE_STATUS_STOP; xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "return from\n"); return 0; } xine_demux_control_headers_done (stream); lprintf ("done\n"); return 1; } int xine_open (xine_stream_t *stream, const char *mrl) { int ret; pthread_mutex_lock (&stream->frontend_lock); lprintf ("open MRL:%s\n", mrl); ret = xine_open_internal (stream, mrl); pthread_mutex_unlock (&stream->frontend_lock); return ret; } static int xine_play_internal (xine_stream_t *stream, int start_pos, int start_time) { double share ; off_t pos, len; int demux_status; xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "xine_play\n"); if (stream->xine->clock->speed != XINE_SPEED_NORMAL) xine_set_speed_internal (stream, XINE_SPEED_NORMAL); if (!stream->demux_plugin) { xine_log (stream->xine, XINE_LOG_MSG, _("xine_play: no demux available\n")); stream->err = XINE_ERROR_NO_DEMUX_PLUGIN; return 0; } /* hint demuxer thread we want to interrupt it */ stream->demux_action_pending = 1; /* discard audio/video buffers to get engine going and take the lock faster */ if (stream->audio_out) stream->audio_out->set_property(stream->audio_out, AO_PROP_DISCARD_BUFFERS, 1); if (stream->video_out) stream->video_out->set_property(stream->video_out, VO_PROP_DISCARD_FRAMES, 1); pthread_mutex_lock( &stream->demux_lock ); /* demux_lock taken. now demuxer is suspended */ /* * start/seek demux */ if (start_pos) { pthread_mutex_lock( &stream->current_extra_info_lock ); len = stream->current_extra_info->input_length; pthread_mutex_unlock( &stream->current_extra_info_lock ); if ((len == 0) && stream->input_plugin) len = stream->input_plugin->get_length (stream->input_plugin); share = (double) start_pos / 65535; pos = (off_t) (share * len) ; } else pos = 0; /* seek to new position (no data is sent to decoders yet) */ demux_status = stream->demux_plugin->seek (stream->demux_plugin, pos, start_time); stream->demux_action_pending = 0; if (stream->audio_out) stream->audio_out->set_property(stream->audio_out, AO_PROP_DISCARD_BUFFERS, 0); if (stream->video_out) stream->video_out->set_property(stream->video_out, VO_PROP_DISCARD_FRAMES, 0); /* before resuming the demuxer, set first_frame_flag */ pthread_mutex_lock (&stream->first_frame_lock); stream->first_frame_flag = 2; pthread_mutex_unlock (&stream->first_frame_lock); /* before resuming the demuxer, reset current position information */ pthread_mutex_lock( &stream->current_extra_info_lock ); extra_info_reset( stream->current_extra_info ); pthread_mutex_unlock( &stream->current_extra_info_lock ); /* now resume demuxer thread if it is running already */ pthread_mutex_unlock( &stream->demux_lock ); if (demux_status != DEMUX_OK) { xine_log (stream->xine, XINE_LOG_MSG, _("xine_play: demux failed to start\n")); stream->err = XINE_ERROR_DEMUX_FAILED; stream->first_frame_flag = 0; return 0; } else { xine_demux_start_thread( stream ); stream->status = XINE_STATUS_PLAY; } /* Wait until the first frame produced is displayed * see video_out.c */ pthread_mutex_lock (&stream->first_frame_lock); /* FIXME: howto detect if video frames will be produced */ if (stream->first_frame_flag && stream->video_decoder_plugin) { struct timeval tv; struct timespec ts; gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec + 2; ts.tv_nsec = tv.tv_usec * 1000; pthread_cond_timedwait(&stream->first_frame_reached, &stream->first_frame_lock, &ts); } pthread_mutex_unlock (&stream->first_frame_lock); xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "xine_play_internal ...done\n"); return 1; } int xine_play (xine_stream_t *stream, int start_pos, int start_time) { int ret; pthread_mutex_lock (&stream->frontend_lock); ret = xine_play_internal (stream, start_pos, start_time); if( stream->slave && (stream->slave_affection & XINE_MASTER_SLAVE_PLAY) ) xine_play (stream->slave, start_pos, start_time); pthread_mutex_unlock (&stream->frontend_lock); return ret; } int xine_eject (xine_stream_t *stream) { int status; if (!stream->eject_class) return 0; pthread_mutex_lock (&stream->frontend_lock); status = 0; /* only eject, if we are stopped OR a different input plugin is playing */ if (stream->eject_class && stream->eject_class->eject_media && ((stream->status == XINE_STATUS_STOP) || stream->eject_class != stream->input_plugin->input_class)) { status = stream->eject_class->eject_media (stream->eject_class); } pthread_mutex_unlock (&stream->frontend_lock); return status; } void xine_dispose (xine_stream_t *stream) { xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "xine_dispose\n"); stream->status = XINE_STATUS_QUIT; xine_close(stream); if( stream->master != stream ) { stream->master->slave = NULL; } if( stream->slave && stream->slave->master == stream ) { stream->slave->master = NULL; } if(stream->broadcaster) close_broadcaster(stream->broadcaster); xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "shutdown audio\n"); audio_decoder_shutdown (stream); xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "shutdown video\n"); video_decoder_shutdown (stream); stream->osd_renderer->close( stream->osd_renderer ); stream->video_fifo->dispose (stream->video_fifo); pthread_mutex_destroy (&stream->frontend_lock); pthread_mutex_destroy (&stream->counter_lock); pthread_mutex_destroy (&stream->osd_lock); pthread_mutex_destroy (&stream->event_queues_lock); pthread_mutex_destroy (&stream->current_extra_info_lock); pthread_cond_destroy (&stream->counter_changed); pthread_mutex_destroy (&stream->demux_lock); pthread_mutex_destroy (&stream->first_frame_lock); pthread_cond_destroy (&stream->first_frame_reached); pthread_mutex_destroy (&stream->next_video_port_lock); pthread_mutex_destroy (&stream->next_audio_port_lock); pthread_cond_destroy (&stream->next_video_port_wired); pthread_cond_destroy (&stream->next_audio_port_wired); stream->metronom->exit (stream->metronom); free (stream->current_extra_info); free (stream->video_decoder_extra_info); free (stream->audio_decoder_extra_info); free (stream); } void xine_exit (xine_t *this) { int i; xprintf (this, XINE_VERBOSITY_DEBUG, "xine_exit: bye!\n"); for (i = 0; i < XINE_LOG_NUM; i++) this->log_buffers[i]->dispose (this->log_buffers[i]); dispose_plugins (this); if(this->clock) this->clock->exit (this->clock); if(this->config) this->config->dispose(this->config); #if defined(WIN32) WSACleanup(); #endif pthread_mutex_destroy(&this->streams_lock); free (this); } xine_t *xine_new (void) { xine_t *this; int i; #ifdef WIN32 WSADATA Data; int i_err; #endif /* WIN32 */ this = xine_xmalloc (sizeof (xine_t)); if (!this) { printf ("xine: failed to malloc xine_t\n"); abort(); } #ifdef ENABLE_NLS /* * i18n */ bindtextdomain("xine-lib", XINE_LOCALEDIR); #endif /* * config */ this->config = xine_config_init (); /* * log buffers */ for (i = 0; i < XINE_LOG_NUM; i++) this->log_buffers[i] = new_scratch_buffer (25); #ifdef WIN32 /* WinSock Library Init. */ i_err = WSAStartup( MAKEWORD( 1, 1 ), &Data ); if( i_err ) { fprintf( stderr, "error: can't initiate WinSocks, error %i\n", i_err ); } #endif /* WIN32 */ /* * streams_lock */ pthread_mutex_init (&this->streams_lock, NULL); this->verbosity = XINE_VERBOSITY_NONE; return this; } void xine_engine_set_param(xine_t *this, int param, int value) { if(this) { switch(param) { case XINE_ENGINE_PARAM_VERBOSITY: this->verbosity = value; break; default: lprintf("Unknown parameter %d\n", param); break; } } } int xine_engine_get_param(xine_t *this, int param) { if(this) { switch(param) { case XINE_ENGINE_PARAM_VERBOSITY: return this->verbosity; break; default: lprintf("Unknown parameter %d\n", param); break; } } return -1; } void xine_init (xine_t *this) { static char *demux_strategies[] = {"default", "reverse", "content", "extension", NULL}; /* initialize color conversion tables and functions */ init_yuv_conversion(); /* probe for optimized memcpy or config setting */ xine_probe_fast_memcpy (this->config); /* * plugins */ scan_plugins(this); /* * content detection strategy */ this->demux_strategy = this->config->register_enum (this->config, "misc.demux_strategy", 0, demux_strategies, "media format detection strategy", NULL, 10, NULL, NULL); /* * keep track of all opened streams */ this->streams = xine_list_new(); /* * start metronom clock */ this->clock = metronom_clock_init(); this->clock->start_clock (this->clock, 0); } int SPU_ON = 1; /* reference in alphablend.c */ void xine_select_spu_channel (xine_stream_t *stream, int channel) { pthread_mutex_lock (&stream->frontend_lock); stream->spu_channel_user = (channel >= -2 ? channel : -2); switch (stream->spu_channel_user) { case -2: stream->spu_channel = -1; stream->video_out->enable_ovl (stream->video_out, 0); break; case -1: stream->spu_channel = stream->spu_channel_auto; stream->video_out->enable_ovl (stream->video_out, 1); break; default: stream->spu_channel = stream->spu_channel_user; stream->video_out->enable_ovl (stream->video_out, 1); } /* added by rwlin@avamax.com */ //printf("xine_select_spu_channel(): original channel = %d. validity=%x\n",stream->spu_channel,stream->spu_channel_validity); if(stream->spu_channel>=0 && !(stream->spu_channel_validity & ( 1 << stream->spu_channel )) ) { uint32_t mask; int i; //stream->spu_channel = -1; for(i=0,mask=1;i<32;i++,mask<<=1) { if( stream->spu_channel_validity & mask ) { stream->spu_channel=i; break; } } printf("xine_select_spu_channel(): adjusted channel = %d\n",stream->spu_channel); } //printf("set to %d\n",stream->spu_channel); // kevin if (stream->spu_channel==128) { SPU_ON = 0; } else { SPU_ON = 1; } pthread_mutex_unlock (&stream->frontend_lock); } static int xine_get_current_position (xine_stream_t *stream) { off_t len; double share; pthread_mutex_lock (&stream->frontend_lock); if (!stream->input_plugin) { lprintf ("no input source\n"); pthread_mutex_unlock (&stream->frontend_lock); return -1; } if ( (!stream->video_decoder_plugin && !stream->audio_decoder_plugin) ) { if( stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] ) extra_info_merge( stream->current_extra_info, stream->video_decoder_extra_info ); else extra_info_merge( stream->current_extra_info, stream->audio_decoder_extra_info ); } if ( stream->current_extra_info->seek_count != stream->video_seek_count ) { pthread_mutex_unlock (&stream->frontend_lock); return -1; /* position not yet known */ } pthread_mutex_lock( &stream->current_extra_info_lock ); len = stream->current_extra_info->input_length; share = (double) stream->current_extra_info->input_pos; pthread_mutex_unlock( &stream->current_extra_info_lock ); if (len == 0) len = stream->input_plugin->get_length (stream->input_plugin); share /= (double) len; share *= 65536; pthread_mutex_unlock (&stream->frontend_lock); return (int) share; } void xine_get_current_info (xine_stream_t *stream, extra_info_t *extra_info, int size) { pthread_mutex_lock( &stream->current_extra_info_lock ); memcpy( extra_info, stream->current_extra_info, size ); pthread_mutex_unlock( &stream->current_extra_info_lock ); } int xine_get_status (xine_stream_t *stream) { return stream->status; } /* * trick play */ void xine_set_speed (xine_stream_t *stream, int speed) { pthread_mutex_lock (&stream->frontend_lock); if (speed <= XINE_SPEED_PAUSE) speed = XINE_SPEED_PAUSE; else if (speed > XINE_SPEED_FAST_4) speed = XINE_SPEED_FAST_4; xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "set_speed %d\n", speed); xine_set_speed_internal (stream, speed); pthread_mutex_unlock (&stream->frontend_lock); } /* * time measurement / seek */ static int xine_get_stream_length (xine_stream_t *stream) { /* pthread_mutex_lock( &stream->demux_lock ); */ if (stream->demux_plugin) { int len = stream->demux_plugin->get_stream_length (stream->demux_plugin); /* pthread_mutex_unlock( &stream->demux_lock ); */ return len; } /* pthread_mutex_unlock( &stream->demux_lock ); */ return 0; } int xine_get_pos_length (xine_stream_t *stream, int *pos_stream, int *pos_time, int *length_time) { int pos = xine_get_current_position (stream); /* force updating extra_info */ if (pos == -1) return 0; if (pos_stream) *pos_stream = pos; if (pos_time) { pthread_mutex_lock( &stream->current_extra_info_lock ); *pos_time = stream->current_extra_info->input_time; pthread_mutex_unlock( &stream->current_extra_info_lock ); } if (length_time) *length_time = xine_get_stream_length (stream); return 1; } int xine_get_current_frame (xine_stream_t *stream, int *width, int *height, int *ratio_code, int *format, uint8_t *img) { vo_frame_t *frame; frame = stream->video_out->get_last_frame (stream->video_out); if (!frame) return 0; *width = frame->width; *height = frame->height; *ratio_code = frame->ratio; *format = frame->format; if (img){ switch (frame->format) { case XINE_IMGFMT_YV12: memcpy (img, frame->base[0], frame->width*frame->height); memcpy (img+frame->width*frame->height, frame->base[1], frame->width*frame->height/4); memcpy (img+frame->width*frame->height+frame->width*frame->height/4, frame->base[2], frame->width*frame->height/4); break; case XINE_IMGFMT_YUY2: memcpy (img, frame->base[0], frame->width * frame->height * 2); break; default: printf ("xine: error, snapshot function not implemented for format 0x%x\n", frame->format); abort (); } } return 1; } int xine_get_video_frame (xine_stream_t *stream, int timestamp, /* msec */ int *width, int *height, int *ratio_code, int *duration, /* msec */ int *format, uint8_t *img) { int ret; pthread_mutex_lock (&stream->frontend_lock); if (stream->status != XINE_STATUS_STOP) xine_stop_internal (stream); if (stream->demux_plugin->get_video_frame) ret = stream->demux_plugin->get_video_frame (stream->demux_plugin, timestamp, width, height, ratio_code, duration, format, img); else ret = 0; pthread_mutex_unlock (&stream->frontend_lock); return ret; } int xine_get_spu_lang (xine_stream_t *stream, int channel, char *lang) { /* Ask the demuxer first (e.g. TS extracts this information from * the stream) **/ if (stream->demux_plugin) { if (stream->demux_plugin->get_capabilities (stream->demux_plugin) & DEMUX_CAP_SPULANG) { /* pass the channel number to the plugin in the data field */ *((int *)lang) = channel; if (stream->demux_plugin->get_optional_data (stream->demux_plugin, lang, DEMUX_OPTIONAL_DATA_SPULANG) == DEMUX_OPTIONAL_SUCCESS) return 1; } } /* No match, check with input plugin instead (e.g. DVD gets this * info from the IFO). **/ if (stream->input_plugin) { if (stream->input_plugin->get_capabilities (stream->input_plugin) & INPUT_CAP_SPULANG) { /* pass the channel number to the plugin in the data field */ *((int *)lang) = channel; if (stream->input_plugin->get_optional_data (stream->input_plugin, lang, INPUT_OPTIONAL_DATA_SPULANG) == INPUT_OPTIONAL_SUCCESS) return 1; } } return 0; } int xine_get_audio_lang (xine_stream_t *stream, int channel, char *lang) { if (stream->demux_plugin) { if (stream->demux_plugin->get_capabilities (stream->demux_plugin) & DEMUX_CAP_AUDIOLANG) { /* pass the channel number to the plugin in the data field */ *((int *)lang) = channel; if (stream->demux_plugin->get_optional_data (stream->demux_plugin, lang, DEMUX_OPTIONAL_DATA_AUDIOLANG) == DEMUX_OPTIONAL_SUCCESS) return 1; } } if (stream->input_plugin) { if (stream->input_plugin->get_capabilities (stream->input_plugin) & INPUT_CAP_AUDIOLANG) { /* pass the channel number to the plugin in the data field */ *((int *)lang) = channel; if (stream->input_plugin->get_optional_data (stream->input_plugin, lang, INPUT_OPTIONAL_DATA_AUDIOLANG) == INPUT_OPTIONAL_SUCCESS) return 1; } } return 0; } int xine_get_spu_channel (xine_stream_t *stream) { return stream->spu_channel_user; } /* * log functions */ int xine_get_log_section_count (xine_t *this) { return XINE_LOG_NUM; } const char *const *xine_get_log_names (xine_t *this) { static const char *log_sections[XINE_LOG_NUM + 1]; log_sections[XINE_LOG_MSG] = _("messages"); log_sections[XINE_LOG_PLUGIN] = _("plugin"); log_sections[XINE_LOG_NUM] = NULL; return log_sections; } void xine_log (xine_t *this, int buf, const char *format, ...) { va_list argp; va_start (argp, format); this->log_buffers[buf]->scratch_printf (this->log_buffers[buf], format, argp); va_end (argp); if (this->verbosity) { va_start (argp, format); vprintf (format, argp); va_end (argp); } } const char *const *xine_get_log (xine_t *this, int buf) { if(buf >= XINE_LOG_NUM) return NULL; return this->log_buffers[buf]->get_content (this->log_buffers[buf]); } void xine_register_log_cb (xine_t *this, xine_log_cb_t cb, void *user_data) { printf ("xine: xine_register_log_cb: not implemented yet.\n"); abort(); } int xine_get_error (xine_stream_t *stream) { return stream->err; } int xine_trick_mode (xine_stream_t *stream, int mode, int value) { printf ("xine: xine_trick_mode not implemented yet.\n"); abort (); } int xine_stream_master_slave(xine_stream_t *master, xine_stream_t *slave, int affection) { master->slave = slave; master->slave_affection = affection; /* respect transitivity: if our designated master already has a master * of its own, we point to this master's master; if our master is a * standalone stream, its master pointer will point to itself */ slave->master = master->master; return 1; }