Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

output.c

00001 /*****************************************************************************
00002  * output.c : internal management of output streams for the audio output
00003  *****************************************************************************
00004  * Copyright (C) 2002-2004 the VideoLAN team
00005  * $Id: output.c 12676 2005-09-25 16:49:40Z babal $
00006  *
00007  * Authors: Christophe Massiot <[email protected]>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*****************************************************************************
00025  * Preamble
00026  *****************************************************************************/
00027 #include <stdlib.h>                            /* calloc(), malloc(), free() */
00028 #include <string.h>
00029 
00030 #include <vlc/vlc.h>
00031 
00032 #include "audio_output.h"
00033 #include "aout_internal.h"
00034 
00035 /*****************************************************************************
00036  * aout_OutputNew : allocate a new output and rework the filter pipeline
00037  *****************************************************************************
00038  * This function is entered with the mixer lock.
00039  *****************************************************************************/
00040 int aout_OutputNew( aout_instance_t * p_aout,
00041                     audio_sample_format_t * p_format )
00042 {
00043     /* Retrieve user defaults. */
00044     int i_rate = config_GetInt( p_aout, "aout-rate" );
00045     vlc_value_t val, text;
00046     /* kludge to avoid a fpu error when rate is 0... */
00047     if( i_rate == 0 ) i_rate = -1;
00048 
00049     memcpy( &p_aout->output.output, p_format, sizeof(audio_sample_format_t) );
00050     if ( i_rate != -1 )
00051         p_aout->output.output.i_rate = i_rate;
00052     aout_FormatPrepare( &p_aout->output.output );
00053 
00054     vlc_mutex_lock( &p_aout->output_fifo_lock );
00055 
00056     /* Find the best output plug-in. */
00057     p_aout->output.p_module = module_Need( p_aout, "audio output", "$aout", 0);
00058     if ( p_aout->output.p_module == NULL )
00059     {
00060         msg_Err( p_aout, "no suitable aout module" );
00061         vlc_mutex_unlock( &p_aout->output_fifo_lock );
00062         return -1;
00063     }
00064 
00065     if ( var_Type( p_aout, "audio-channels" ) ==
00066              (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) )
00067     {
00068         /* The user may have selected a different channels configuration. */
00069         var_Get( p_aout, "audio-channels", &val );
00070 
00071         if ( val.i_int == AOUT_VAR_CHAN_RSTEREO )
00072         {
00073             p_aout->output.output.i_original_channels |=
00074                                         AOUT_CHAN_REVERSESTEREO;
00075         }
00076         else if ( val.i_int == AOUT_VAR_CHAN_STEREO )
00077         {
00078             p_aout->output.output.i_original_channels =
00079                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
00080         }
00081         else if ( val.i_int == AOUT_VAR_CHAN_LEFT )
00082         {
00083             p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT;
00084         }
00085         else if ( val.i_int == AOUT_VAR_CHAN_RIGHT )
00086         {
00087             p_aout->output.output.i_original_channels = AOUT_CHAN_RIGHT;
00088         }
00089         else if ( val.i_int == AOUT_VAR_CHAN_DOLBYS )
00090         {
00091             p_aout->output.output.i_original_channels
00092                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO;
00093         }
00094     }
00095     else if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER
00096               && (p_aout->output.output.i_original_channels
00097                    & AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) )
00098     {
00099         /* Mono - create the audio-channels variable. */
00100         var_Create( p_aout, "audio-channels",
00101                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
00102         text.psz_string = _("Audio Channels");
00103         var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL );
00104 
00105         val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo");
00106         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
00107         val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left");
00108         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
00109         val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right");
00110         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
00111         if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO )
00112         {
00113             /* Go directly to the left channel. */
00114             p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT;
00115             val.i_int = AOUT_VAR_CHAN_LEFT;
00116             var_Set( p_aout, "audio-channels", val );
00117         }
00118         var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
00119                          NULL );
00120     }
00121     else if ( p_aout->output.output.i_physical_channels ==
00122                (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
00123                 && (p_aout->output.output.i_original_channels &
00124                      (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
00125     {
00126         /* Stereo - create the audio-channels variable. */
00127         var_Create( p_aout, "audio-channels",
00128                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
00129         text.psz_string = _("Audio Channels");
00130         var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL );
00131 
00132         if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
00133         {
00134             val.i_int = AOUT_VAR_CHAN_DOLBYS;
00135             text.psz_string = _("Dolby Surround");
00136         }
00137         else
00138         {
00139             val.i_int = AOUT_VAR_CHAN_STEREO;
00140             text.psz_string = _("Stereo");
00141         }
00142         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
00143         val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left");
00144         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
00145         val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right");
00146         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
00147         val.i_int = AOUT_VAR_CHAN_RSTEREO; text.psz_string=_("Reverse stereo");
00148         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
00149         if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO )
00150         {
00151             /* Go directly to the left channel. */
00152             p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT;
00153             val.i_int = AOUT_VAR_CHAN_LEFT;
00154             var_Set( p_aout, "audio-channels", val );
00155         }
00156         var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
00157                          NULL );
00158     }
00159     val.b_bool = VLC_TRUE;
00160     var_Set( p_aout, "intf-change", val );
00161 
00162     aout_FormatPrepare( &p_aout->output.output );
00163 
00164     /* Prepare FIFO. */
00165     aout_FifoInit( p_aout, &p_aout->output.fifo,
00166                    p_aout->output.output.i_rate );
00167 
00168     vlc_mutex_unlock( &p_aout->output_fifo_lock );
00169 
00170     aout_FormatPrint( p_aout, "output", &p_aout->output.output );
00171 
00172     /* Calculate the resulting mixer output format. */
00173     memcpy( &p_aout->mixer.mixer, &p_aout->output.output,
00174             sizeof(audio_sample_format_t) );
00175     if ( !AOUT_FMT_NON_LINEAR(&p_aout->output.output) )
00176     {
00177         /* Non-S/PDIF mixer only deals with float32 or fixed32. */
00178         p_aout->mixer.mixer.i_format
00179                      = (p_aout->p_libvlc->i_cpu & CPU_CAPABILITY_FPU) ?
00180                         VLC_FOURCC('f','l','3','2') :
00181                         VLC_FOURCC('f','i','3','2');
00182         aout_FormatPrepare( &p_aout->mixer.mixer );
00183     }
00184     else
00185     {
00186         p_aout->mixer.mixer.i_format = p_format->i_format;
00187     }
00188 
00189     aout_FormatPrint( p_aout, "mixer", &p_aout->output.output );
00190 
00191     /* Create filters. */
00192     p_aout->output.i_nb_filters = 0;
00193     if ( aout_FiltersCreatePipeline( p_aout, p_aout->output.pp_filters,
00194                                      &p_aout->output.i_nb_filters,
00195                                      &p_aout->mixer.mixer,
00196                                      &p_aout->output.output ) < 0 )
00197     {
00198         msg_Err( p_aout, "couldn't set an output pipeline" );
00199         module_Unneed( p_aout, p_aout->output.p_module );
00200         return -1;
00201     }
00202 
00203     /* Prepare hints for the buffer allocator. */
00204     p_aout->mixer.output_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
00205     p_aout->mixer.output_alloc.i_bytes_per_sec
00206                         = p_aout->mixer.mixer.i_bytes_per_frame
00207                            * p_aout->mixer.mixer.i_rate
00208                            / p_aout->mixer.mixer.i_frame_length;
00209 
00210     aout_FiltersHintBuffers( p_aout, p_aout->output.pp_filters,
00211                              p_aout->output.i_nb_filters,
00212                              &p_aout->mixer.output_alloc );
00213 
00214     p_aout->output.b_error = 0;
00215     return 0;
00216 }
00217 
00218 /*****************************************************************************
00219  * aout_OutputDelete : delete the output
00220  *****************************************************************************
00221  * This function is entered with the mixer lock.
00222  *****************************************************************************/
00223 void aout_OutputDelete( aout_instance_t * p_aout )
00224 {
00225     if ( p_aout->output.b_error )
00226     {
00227         return;
00228     }
00229 
00230     module_Unneed( p_aout, p_aout->output.p_module );
00231 
00232     aout_FiltersDestroyPipeline( p_aout, p_aout->output.pp_filters,
00233                                  p_aout->output.i_nb_filters );
00234     aout_FifoDestroy( p_aout, &p_aout->output.fifo );
00235 
00236     p_aout->output.b_error = VLC_TRUE;
00237 }
00238 
00239 /*****************************************************************************
00240  * aout_OutputPlay : play a buffer
00241  *****************************************************************************
00242  * This function is entered with the mixer lock.
00243  *****************************************************************************/
00244 void aout_OutputPlay( aout_instance_t * p_aout, aout_buffer_t * p_buffer )
00245 {
00246     aout_FiltersPlay( p_aout, p_aout->output.pp_filters,
00247                       p_aout->output.i_nb_filters,
00248                       &p_buffer );
00249 
00250     if( p_buffer->i_nb_bytes == 0 )
00251     {
00252         aout_BufferFree( p_buffer );
00253         return;
00254     }
00255 
00256     vlc_mutex_lock( &p_aout->output_fifo_lock );
00257     aout_FifoPush( p_aout, &p_aout->output.fifo, p_buffer );
00258     p_aout->output.pf_play( p_aout );
00259     vlc_mutex_unlock( &p_aout->output_fifo_lock );
00260 }
00261 
00262 /*****************************************************************************
00263  * aout_OutputNextBuffer : give the audio output plug-in the right buffer
00264  *****************************************************************************
00265  * If b_can_sleek is 1, the aout core functions won't try to resample
00266  * new buffers to catch up - that is we suppose that the output plug-in can
00267  * compensate it by itself. S/PDIF outputs should always set b_can_sleek = 1.
00268  * This function is entered with no lock at all :-).
00269  *****************************************************************************/
00270 aout_buffer_t * aout_OutputNextBuffer( aout_instance_t * p_aout,
00271                                        mtime_t start_date,
00272                                        vlc_bool_t b_can_sleek )
00273 {
00274     aout_buffer_t * p_buffer;
00275 
00276     vlc_mutex_lock( &p_aout->output_fifo_lock );
00277 
00278     p_buffer = p_aout->output.fifo.p_first;
00279 
00280     /* Drop the audio sample if the audio output is really late.
00281      * In the case of b_can_sleek, we don't use a resampler so we need to be
00282      * a lot more severe. */
00283     while ( p_buffer && p_buffer->start_date <
00284             (b_can_sleek ? start_date : mdate()) - AOUT_PTS_TOLERANCE )
00285     {
00286         msg_Dbg( p_aout, "audio output is too slow ("I64Fd"), "
00287                  "trashing "I64Fd"us", mdate() - p_buffer->start_date,
00288                  p_buffer->end_date - p_buffer->start_date );
00289         p_buffer = p_buffer->p_next;
00290         aout_BufferFree( p_aout->output.fifo.p_first );
00291         p_aout->output.fifo.p_first = p_buffer;
00292     }
00293 
00294     if ( p_buffer == NULL )
00295     {
00296         p_aout->output.fifo.pp_last = &p_aout->output.fifo.p_first;
00297 
00298 #if 0 /* This is bad because the audio output might just be trying to fill
00299        * in it's internal buffers. And anyway, it's up to the audio output
00300        * to deal with this kind of starvation. */
00301 
00302         /* Set date to 0, to allow the mixer to send a new buffer ASAP */
00303         aout_FifoSet( p_aout, &p_aout->output.fifo, 0 );
00304         if ( !p_aout->output.b_starving )
00305             msg_Dbg( p_aout,
00306                  "audio output is starving (no input), playing silence" );
00307         p_aout->output.b_starving = 1;
00308 #endif
00309 
00310         vlc_mutex_unlock( &p_aout->output_fifo_lock );
00311         return NULL;
00312     }
00313 
00314     /* Here we suppose that all buffers have the same duration - this is
00315      * generally true, and anyway if it's wrong it won't be a disaster.
00316      */
00317     if ( p_buffer->start_date > start_date
00318                          + (p_buffer->end_date - p_buffer->start_date) )
00319     /*
00320      *                   + AOUT_PTS_TOLERANCE )
00321      * There is no reason to want that, it just worsen the scheduling of
00322      * an audio sample after an output starvation (ie. on start or on resume)
00323      * --Gibalou
00324      */
00325     {
00326         vlc_mutex_unlock( &p_aout->output_fifo_lock );
00327         if ( !p_aout->output.b_starving )
00328             msg_Dbg( p_aout, "audio output is starving ("I64Fd"), "
00329                      "playing silence", p_buffer->start_date - start_date );
00330         p_aout->output.b_starving = 1;
00331         return NULL;
00332     }
00333 
00334     p_aout->output.b_starving = 0;
00335 
00336     if ( !b_can_sleek &&
00337           ( (p_buffer->start_date - start_date > AOUT_PTS_TOLERANCE)
00338              || (start_date - p_buffer->start_date > AOUT_PTS_TOLERANCE) ) )
00339     {
00340         /* Try to compensate the drift by doing some resampling. */
00341         int i;
00342         mtime_t difference = start_date - p_buffer->start_date;
00343         msg_Warn( p_aout, "output date isn't PTS date, requesting "
00344                   "resampling ("I64Fd")", difference );
00345 
00346         vlc_mutex_lock( &p_aout->input_fifos_lock );
00347         for ( i = 0; i < p_aout->i_nb_inputs; i++ )
00348         {
00349             aout_fifo_t * p_fifo = &p_aout->pp_inputs[i]->fifo;
00350 
00351             aout_FifoMoveDates( p_aout, p_fifo, difference );
00352         }
00353 
00354         aout_FifoMoveDates( p_aout, &p_aout->output.fifo, difference );
00355         vlc_mutex_unlock( &p_aout->input_fifos_lock );
00356     }
00357 
00358     p_aout->output.fifo.p_first = p_buffer->p_next;
00359     if ( p_buffer->p_next == NULL )
00360     {
00361         p_aout->output.fifo.pp_last = &p_aout->output.fifo.p_first;
00362     }
00363 
00364     vlc_mutex_unlock( &p_aout->output_fifo_lock );
00365     return p_buffer;
00366 }

Generated on Tue Dec 20 10:14:59 2005 for vlc-0.8.4a by  doxygen 1.4.2