/* * Copyright (C) 2000-2002 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: audio_decoder.c,v 1.82 2002/07/26 14:51:23 mroi Exp $ * * * functions that implement audio decoding */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "xine_internal.h" #include "xineutils.h" /* #define LOG */ void *audio_decoder_loop (void *this_gen) { buf_element_t *buf; xine_t *this = (xine_t *) this_gen; int running = 1; audio_decoder_t *decoder; static int prof_audio_decode = -1; static uint32_t buftype_unknown = 0; if (prof_audio_decode == -1) prof_audio_decode = xine_profiler_allocate_slot ("audio decoder/output"); while (running) { #ifdef LOG printf ("audio_loop: waiting for package...\n"); #endif buf = this->audio_fifo->get (this->audio_fifo); #ifdef LOG printf ("audio_loop: got package pts = %lld, type = %08x\n", buf->pts, buf->type); #endif if (buf->input_pos) this->cur_input_pos = buf->input_pos; if (buf->input_length) this->cur_input_length = buf->input_length; if (buf->input_time) { this->cur_input_time = buf->input_time; pthread_mutex_lock (&this->osd_lock); if( this->curtime_needed_for_osd && !(--this->curtime_needed_for_osd) ) xine_internal_osd (this, ">",90000); pthread_mutex_unlock (&this->osd_lock); } switch (buf->type) { case BUF_CONTROL_START: if (this->cur_audio_decoder_plugin) { this->cur_audio_decoder_plugin->close (this->cur_audio_decoder_plugin); this->cur_audio_decoder_plugin = NULL; this->audio_track_map_entries = 0; this->audio_type = 0; } pthread_mutex_lock (&this->finished_lock); this->audio_finished = 0; pthread_mutex_unlock (&this->finished_lock); this->metronom->handle_audio_discontinuity (this->metronom, DISC_STREAMSTART, 0); break; case BUF_CONTROL_END: if (this->cur_audio_decoder_plugin) { this->cur_audio_decoder_plugin->close (this->cur_audio_decoder_plugin); this->cur_audio_decoder_plugin = NULL; this->audio_track_map_entries = 0; this->audio_type = 0; } pthread_mutex_lock (&this->finished_lock); if (!this->audio_finished ) { this->audio_finished = 1; if (this->video_finished) { if( this->playing_logo ) buf->decoder_flags = 0; this->playing_logo = 0; if( buf->decoder_flags & BUF_FLAG_END_STREAM ) xine_notify_stream_finished (this); } } pthread_mutex_unlock (&this->finished_lock); this->audio_channel_auto = -1; /* future magic - coming soon lrb_flush (this->audio_temp); */ break; case BUF_CONTROL_QUIT: if (this->cur_audio_decoder_plugin) { this->cur_audio_decoder_plugin->close (this->cur_audio_decoder_plugin); this->cur_audio_decoder_plugin = NULL; this->audio_track_map_entries = 0; this->audio_type = 0; } running = 0; break; case BUF_CONTROL_NOP: break; case BUF_CONTROL_RESET_DECODER: if (this->cur_audio_decoder_plugin) this->cur_audio_decoder_plugin->reset (this->cur_audio_decoder_plugin); if (this->audio_out) this->audio_out->control(this->audio_out, AO_CTRL_FLUSH_BUFFERS); break; case BUF_CONTROL_DISCONTINUITY: this->metronom->handle_audio_discontinuity (this->metronom, DISC_RELATIVE, buf->disc_off); break; case BUF_CONTROL_NEWPTS: if (buf->decoder_flags && BUF_FLAG_SEEK) { this->metronom->handle_audio_discontinuity (this->metronom, DISC_STREAMSEEK, buf->disc_off); } else { this->metronom->handle_audio_discontinuity (this->metronom, DISC_ABSOLUTE, buf->disc_off); } break; case BUF_CONTROL_AUDIO_CHANNEL: { printf ("audio_decoder: suggested switching to stream_id %02x\n", buf->decoder_info[0]); this->audio_channel_auto = buf->decoder_info[0] & 0xff; } break; default: xine_profiler_start_count (prof_audio_decode); if ( (buf->type & 0xFF000000) == BUF_AUDIO_BASE ) { uint32_t audio_type = 0; int i,j; /* printf("audio_decoder: buf_type=%08x auto=%08x user=%08x\n", buf->type, this->audio_channel_auto, this->audio_channel_user); */ /* update track map */ i = 0; while ( (iaudio_track_map_entries) && (this->audio_track_map[i]type) ) i++; if ( (i==this->audio_track_map_entries) || (this->audio_track_map[i] != buf->type) ) { j = this->audio_track_map_entries; while (j>i) { this->audio_track_map[j] = this->audio_track_map[j-1]; j--; } this->audio_track_map[i] = buf->type; this->audio_track_map_entries++; } /* find out which audio type to decode */ if (this->audio_channel_user > -2) { if (this->audio_channel_user == -1) { /* auto */ if (this->audio_channel_auto>=0) { if ((buf->type & 0xFF) == this->audio_channel_auto) { audio_type = buf->type; } else audio_type = -1; } else audio_type = this->audio_track_map[0]; } else { if (this->audio_channel_user <= this->audio_track_map_entries) audio_type = this->audio_track_map[this->audio_channel_user]; else audio_type = -1; } /* now, decode this buffer if it's the right audio type */ if (buf->type == audio_type) { int streamtype = (buf->type>>16) & 0xFF; decoder = this->audio_decoder_plugins [streamtype]; /* close old decoder of audio type has changed */ if (audio_type != this->audio_type) { if (this->cur_audio_decoder_plugin) { this->cur_audio_decoder_plugin->close (this->cur_audio_decoder_plugin); this->cur_audio_decoder_plugin = NULL; } if (decoder) { xine_event_t event; xine_log (this, XINE_LOG_FORMAT, "audio_decoder: using audio decoder plugin '%s'\n", decoder->get_identifier()); xine_report_codec( this, XINE_CODEC_AUDIO, 0, buf->type, 1); this->cur_audio_decoder_plugin = decoder; this->cur_audio_decoder_plugin->init (this->cur_audio_decoder_plugin, this->audio_out); this->audio_type = audio_type; event.type = XINE_EVENT_UI_CHANNELS_CHANGED; xine_send_event(this, &event); } } /* finally - decode data */ if (decoder) decoder->decode_data (decoder, buf); else if( buf->type != buftype_unknown ) { xine_log (this, XINE_LOG_MSG, "audio_decoder: no plugin available to handle '%s'\n", buf_audio_name( buf->type ) ); xine_report_codec( this, XINE_CODEC_AUDIO, 0, buf->type, 0); buftype_unknown = buf->type; } } } } else if( buf->type != buftype_unknown ) { xine_log (this, XINE_LOG_MSG, "audio_decoder: unknown buffer type: %08x\n", buf->type ); buftype_unknown = buf->type; } xine_profiler_stop_count (prof_audio_decode); } buf->free_buffer (buf); } pthread_exit(NULL); } void audio_decoder_init (xine_t *this) { pthread_attr_t pth_attrs; struct sched_param pth_params; int err; if (this->audio_out == NULL) { this->audio_finished = 1; this->audio_fifo = NULL; return; } /* The fifo size is based on dvd playback where buffers are filled * with 2k of data. With 230 buffers and a typical audio data rate * of 1.8 Mbit/s (four ac3 streams), the fifo can hold about 2 seconds * of audio, wich should be enough to compensate for drive delays. * We provide buffers of 8k size instead of 2k for demuxers sending * larger chunks. */ this->audio_fifo = fifo_buffer_new (230, 8192); this->audio_channel_user = -1; this->audio_channel_auto = 0; this->audio_track_map_entries = 0; this->audio_type = 0; /* future magic - coming soon this->audio_temp = lrb_new (100, this->audio_fifo); */ pthread_attr_init(&pth_attrs); pthread_attr_getschedparam(&pth_attrs, &pth_params); pth_params.sched_priority = sched_get_priority_min(SCHED_OTHER); pthread_attr_setschedparam(&pth_attrs, &pth_params); pthread_attr_setscope(&pth_attrs, PTHREAD_SCOPE_SYSTEM); if ((err = pthread_create (&this->audio_thread, &pth_attrs, audio_decoder_loop, this)) != 0) { fprintf (stderr, "audio_decoder: can't create new thread (%s)\n", strerror(err)); abort(); } } void audio_decoder_shutdown (xine_t *this) { buf_element_t *buf; void *p; if (this->audio_fifo) { /* this->audio_fifo->clear(this->audio_fifo); */ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); buf->type = BUF_CONTROL_QUIT; this->audio_fifo->put (this->audio_fifo, buf); pthread_join (this->audio_thread, &p); } if(this->audio_out) { this->audio_out->exit (this->audio_out); this->audio_out = NULL; } if (this->audio_fifo) { this->audio_fifo->dispose (this->audio_fifo); this->audio_fifo = NULL; } } int xine_get_audio_channel (xine_t *this) { return this->audio_type & 0xFFFF; } void xine_select_audio_channel (xine_t *this, int channel) { pthread_mutex_lock (&this->xine_lock); if (channel < -2) channel = -2; this->audio_channel_user = channel; pthread_mutex_unlock (&this->xine_lock); } int xine_get_audio_selection (xine_t *this) { return this->audio_channel_user; }