aout.c
上传用户:riyaled888
上传日期:2009-03-27
资源大小:7338k
文件大小:11k
源码类别:

多媒体

开发平台:

MultiPlatform

  1. /*****************************************************************************
  2.  * aout.c : QNX audio output
  3.  *****************************************************************************
  4.  * Copyright (C) 2000, 2001 VideoLAN
  5.  *
  6.  * Authors: Henri Fallon <henri@videolan.org>
  7.  *          Jon Lech Johansen <jon-vl@nanocrew.net>
  8.  *          Pascal Levesque <pascal.levesque@mindready.com>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  23.  *****************************************************************************/
  24. /*****************************************************************************
  25.  * Preamble
  26.  *****************************************************************************/
  27. #include <errno.h>                                                 /* ENOMEM */
  28. #include <string.h>                                            /* strerror() */
  29. #include <stdlib.h>                            /* calloc(), malloc(), free() */
  30. #include <vlc/vlc.h>
  31. #ifdef HAVE_ALLOCA_H
  32. #   include <alloca.h>
  33. #endif
  34. #include <vlc/aout.h>
  35. #include "aout_internal.h"
  36. #include <sys/asoundlib.h>
  37. struct aout_sys_t
  38. {
  39.     snd_pcm_t  * p_pcm_handle;
  40.     int          i_card;
  41.     int          i_device;
  42.     byte_t *     p_silent_buffer;
  43. };
  44. #define DEFAULT_FRAME_SIZE 2048
  45. /*****************************************************************************
  46.  * Local prototypes
  47.  *****************************************************************************/
  48. int            E_(OpenAudio)    ( vlc_object_t *p_this );
  49. void           E_(CloseAudio)   ( vlc_object_t *p_this );
  50. static int     GetBufInfo       ( aout_instance_t * );
  51. static void    Play             ( aout_instance_t * );
  52. static int     QNXaoutThread    ( aout_instance_t * );
  53. /*****************************************************************************
  54.  * Open : creates a handle and opens an alsa device
  55.  *****************************************************************************
  56.  * This function opens an alsa device, through the alsa API
  57.  *****************************************************************************/
  58. int E_(OpenAudio)( vlc_object_t *p_this )
  59. {
  60.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  61.     int i_ret;
  62.     int i_bytes_per_sample;
  63.     int i_nb_channels;
  64.     snd_pcm_channel_info_t pi;
  65.     snd_pcm_channel_params_t pp;
  66.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  67.     /* allocate structure */
  68.     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
  69.     if( p_aout->output.p_sys == NULL )
  70.     {
  71.         msg_Err( p_aout, "out of memory" );
  72.         return -1;
  73.     }
  74.     /* open audio device */
  75.     if( ( i_ret = snd_pcm_open_preferred( &p_aout->output.p_sys->p_pcm_handle,
  76.                                           &p_aout->output.p_sys->i_card,
  77.                                           &p_aout->output.p_sys->i_device,
  78.                                           SND_PCM_OPEN_PLAYBACK ) ) < 0 )
  79.     {
  80.         msg_Err( p_aout, "unable to open audio device (%s)",
  81.                          snd_strerror( i_ret ) );
  82.         free( p_aout->output.p_sys );
  83.         return -1;
  84.     }
  85.     /* disable mmap */
  86.     if( ( i_ret = snd_pcm_plugin_set_disable( p_aout->output.p_sys->p_pcm_handle,
  87.                                               PLUGIN_DISABLE_MMAP ) ) < 0 )
  88.     {
  89.         msg_Err( p_aout, "unable to disable mmap (%s)", snd_strerror(i_ret) );
  90.         E_(CloseAudio)( p_this );
  91.         free( p_aout->output.p_sys );
  92.         return -1;
  93.     }
  94.     p_aout->output.p_sys->p_silent_buffer = malloc( DEFAULT_FRAME_SIZE * 4 );
  95.     p_aout->output.pf_play = Play;
  96.     aout_VolumeSoftInit( p_aout );
  97.     memset( &pi, 0, sizeof(pi) );
  98.     memset( &pp, 0, sizeof(pp) );
  99.     pi.channel = SND_PCM_CHANNEL_PLAYBACK;
  100.     if( ( i_ret = snd_pcm_plugin_info( p_aout->output.p_sys->p_pcm_handle,
  101.                                        &pi ) ) < 0 )
  102.     {
  103.         msg_Err( p_aout, "unable to get plugin info (%s)",
  104.                          snd_strerror( i_ret ) );
  105.         E_(CloseAudio)( p_this );
  106.         free( p_aout->output.p_sys );
  107.         return -1;
  108.     }
  109.     pp.mode       = SND_PCM_MODE_BLOCK;
  110.     pp.channel    = SND_PCM_CHANNEL_PLAYBACK;
  111.     pp.start_mode = SND_PCM_START_FULL;
  112.     pp.stop_mode  = SND_PCM_STOP_STOP;
  113.     pp.buf.block.frags_max   = 3;
  114.     pp.buf.block.frags_min   = 1;
  115.     pp.format.interleave     = 1;
  116.     pp.format.rate           = p_aout->output.output.i_rate;
  117.     i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
  118.     if ( i_nb_channels > 2 )
  119.     {
  120.         /* I don't know if QNX supports more than two channels. */
  121.         i_nb_channels = 2;
  122.         p_aout->output.output.i_channels = AOUT_CHAN_STEREO;
  123.     }
  124.     pp.format.voices         = i_nb_channels;
  125.     p_aout->output.output.i_format = AOUT_FMT_S16_NE;
  126.     p_aout->output.i_nb_samples = DEFAULT_FRAME_SIZE;
  127.     pp.format.format = SND_PCM_SFMT_S16;
  128.     i_bytes_per_sample = 2;
  129.     pp.buf.block.frag_size = p_aout->output.i_nb_samples *
  130.                             p_aout->output.output.i_channels *
  131.                             i_bytes_per_sample;
  132.     /* set parameters */
  133.     if( ( i_ret = snd_pcm_plugin_params( p_aout->output.p_sys->p_pcm_handle,
  134.                                          &pp ) ) < 0 )
  135.     {
  136.         msg_Err( p_aout, "unable to set parameters (%s)", snd_strerror(i_ret) );
  137.         E_(CloseAudio)( p_this );
  138.         free( p_aout->output.p_sys );
  139.         return -1;
  140.     }
  141.     /* prepare channel */
  142.     if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle,
  143.                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
  144.     {
  145.         msg_Err( p_aout, "unable to prepare channel (%s)",
  146.                          snd_strerror( i_ret ) );
  147.         E_(CloseAudio)( p_this );
  148.         free( p_aout->output.p_sys );
  149.         return -1;
  150.     }
  151.     /* Create audio thread and wait for its readiness. */
  152.     if( vlc_thread_create( p_aout, "aout", QNXaoutThread,
  153.                            VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
  154.     {
  155.         msg_Err( p_aout, "cannot create QNX audio thread (%s)", strerror(errno) );
  156.         E_(CloseAudio)( p_this );
  157.         free( p_aout->output.p_sys );
  158.         return -1;
  159.     }
  160.     return( 0 );
  161. }
  162. /*****************************************************************************
  163.  * GetBufInfo: buffer status query
  164.  *****************************************************************************
  165.  * This function returns the number of used byte in the queue.
  166.  * It also deals with errors : indeed if the device comes to run out
  167.  * of data to play, it switches to the "underrun" status. It has to
  168.  * be flushed and re-prepared
  169.  *****************************************************************************/
  170. static int GetBufInfo( aout_instance_t *p_aout )
  171. {
  172.     int i_ret;
  173.     snd_pcm_channel_status_t status;
  174.     /* get current pcm status */
  175.     memset( &status, 0, sizeof(status) );
  176.     if( ( i_ret = snd_pcm_plugin_status( p_aout->output.p_sys->p_pcm_handle,
  177.                                          &status ) ) < 0 )
  178.     {
  179.         msg_Err( p_aout, "unable to get device status (%s)",
  180.                          snd_strerror( i_ret ) );
  181.         return( -1 );
  182.     }
  183.     /* check for underrun */
  184.     switch( status.status )
  185.     {
  186.         case SND_PCM_STATUS_READY:
  187.         case SND_PCM_STATUS_UNDERRUN:
  188.             if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle,
  189.                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
  190.             {
  191.                 msg_Err( p_aout, "unable to prepare channel (%s)",
  192.                                  snd_strerror( i_ret ) );
  193.             }
  194.             break;
  195.     }
  196.     return( status.count );
  197. }
  198. /*****************************************************************************
  199.  * Play : plays a sample
  200.  *****************************************************************************
  201.  * Plays a sample using the snd_pcm_write function from the alsa API
  202.  *****************************************************************************/
  203. static void Play( aout_instance_t *p_aout )
  204. {
  205. }
  206. /*****************************************************************************
  207.  * CloseAudio: close the audio device
  208.  *****************************************************************************/
  209. void E_(CloseAudio) ( vlc_object_t *p_this )
  210. {
  211.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  212.     int i_ret;
  213.     p_aout->b_die = 1;
  214.     vlc_thread_join( p_aout );
  215.     if( ( i_ret = snd_pcm_close( p_aout->output.p_sys->p_pcm_handle ) ) < 0 )
  216.     {
  217.         msg_Err( p_aout, "unable to close audio device (%s)",
  218.                          snd_strerror( i_ret ) );
  219.     }
  220.     free( p_aout->output.p_sys->p_silent_buffer );
  221.     free( p_aout->output.p_sys );
  222. }
  223. /*****************************************************************************
  224.  * QNXaoutThread: asynchronous thread used to DMA the data to the device
  225.  *****************************************************************************/
  226. static int QNXaoutThread( aout_instance_t * p_aout )
  227. {
  228.     struct aout_sys_t * p_sys = p_aout->output.p_sys;
  229.     while ( !p_aout->b_die )
  230.     {
  231.         aout_buffer_t * p_buffer;
  232.         int i_tmp, i_size;
  233.         byte_t * p_bytes;
  234.         if ( p_aout->output.output.i_format != VLC_FOURCC('s','p','d','i') )
  235.         {
  236.             mtime_t next_date = 0;
  237.             /* Get the presentation date of the next write() operation. It
  238.              * is equal to the current date + duration of buffered samples.
  239.              * Order is important here, since GetBufInfo is believed to take
  240.              * more time than mdate(). */
  241.             next_date = (mtime_t)GetBufInfo( p_aout ) * 1000000
  242.                       / p_aout->output.output.i_bytes_per_frame
  243.                       / p_aout->output.output.i_rate
  244.                       * p_aout->output.output.i_frame_length;
  245.             next_date += mdate();
  246.             p_buffer = aout_OutputNextBuffer( p_aout, next_date, VLC_FALSE );
  247.         }
  248.         else
  249.         {
  250.             p_buffer = aout_OutputNextBuffer( p_aout, 0, VLC_TRUE );
  251.         }
  252.         if ( p_buffer != NULL )
  253.         {
  254.             p_bytes = p_buffer->p_buffer;
  255.             i_size = p_buffer->i_nb_bytes;
  256.         }
  257.         else
  258.         {
  259.             i_size = DEFAULT_FRAME_SIZE / p_aout->output.output.i_frame_length
  260.                       * p_aout->output.output.i_bytes_per_frame;
  261.             p_bytes = p_aout->output.p_sys->p_silent_buffer;
  262.             memset( p_bytes, 0, i_size );
  263.         }
  264.         i_tmp = snd_pcm_plugin_write( p_aout->output.p_sys->p_pcm_handle,
  265.                                         (void *) p_bytes,
  266.                                         (size_t) i_size );
  267.         if( i_tmp < 0 )
  268.         {
  269.             msg_Err( p_aout, "write failed (%s)", strerror(errno) );
  270.         }
  271.         if ( p_buffer != NULL )
  272.         {
  273.             aout_BufferFree( p_buffer );
  274.         }
  275.     }
  276.     return 0;
  277. }