/*
	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 <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdarg.h>
#include <signal.h>
#if defined (__linux__)
#include <endian.h>
#elif defined (__FreeBSD__)
#include <machine/endian.h>
#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 <fcntl.h>
#   include <winsock.h>
#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_audio<finished_count_audio) || 
           (stream->finished_count_video<finished_count_video)) {
      
      lprintf ("waiting for finisheds.\n");
      pthread_cond_wait (&stream->counter_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; i<XINE_STREAM_INFO_MAX; i++) {
    stream->stream_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; i<XINE_STREAM_INFO_MAX; i++) {
    stream->stream_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;
}
