]> git.sesse.net Git - vlc/blob - modules/gui/qnx/aout.c
Remove legacy alloca checks
[vlc] / modules / gui / qnx / aout.c
1 /*****************************************************************************
2  * aout.c : QNX audio output
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <errno.h>                                                 /* ENOMEM */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35
36 #include <vlc_aout.h>
37
38 #include <sys/asoundlib.h>
39
40 struct aout_sys_t
41 {
42     snd_pcm_t  * p_pcm_handle;
43     int          i_card;
44     int          i_device;
45
46     uint8_t *    p_silent_buffer;
47 };
48
49 #define DEFAULT_FRAME_SIZE 2048
50
51 /*****************************************************************************
52  * Local prototypes
53  *****************************************************************************/
54 int            OpenAudio    ( vlc_object_t *p_this );
55 void           CloseAudio   ( vlc_object_t *p_this );
56 static int     GetBufInfo       ( aout_instance_t * );
57 static void    Play             ( aout_instance_t * );
58 static void*   QNXaoutThread    ( vlc_object_t * );
59
60 /*****************************************************************************
61  * Open : creates a handle and opens an alsa device
62  *****************************************************************************
63  * This function opens an alsa device, through the alsa API
64  *****************************************************************************/
65 int OpenAudio( vlc_object_t *p_this )
66 {
67     aout_instance_t *p_aout = (aout_instance_t *)p_this;
68     int i_ret;
69     int i_bytes_per_sample;
70     int i_nb_channels;
71     snd_pcm_channel_info_t pi;
72     snd_pcm_channel_params_t pp;
73     aout_instance_t *p_aout = (aout_instance_t *)p_this;
74
75     /* allocate structure */
76     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
77     if( p_aout->output.p_sys == NULL )
78         return -1;
79
80     /* open audio device */
81     if( ( i_ret = snd_pcm_open_preferred( &p_aout->output.p_sys->p_pcm_handle,
82                                           &p_aout->output.p_sys->i_card,
83                                           &p_aout->output.p_sys->i_device,
84                                           SND_PCM_OPEN_PLAYBACK ) ) < 0 )
85     {
86         msg_Err( p_aout, "unable to open audio device (%s)",
87                          snd_strerror( i_ret ) );
88         free( p_aout->output.p_sys );
89         return -1;
90     }
91
92     /* disable mmap */
93     if( ( i_ret = snd_pcm_plugin_set_disable( p_aout->output.p_sys->p_pcm_handle,
94                                               PLUGIN_DISABLE_MMAP ) ) < 0 )
95     {
96         msg_Err( p_aout, "unable to disable mmap (%s)", snd_strerror(i_ret) );
97         CloseAudio( p_this );
98         free( p_aout->output.p_sys );
99         return -1;
100     }
101
102     p_aout->output.p_sys->p_silent_buffer = malloc( DEFAULT_FRAME_SIZE * 4 );
103     p_aout->output.pf_play = Play;
104     aout_VolumeSoftInit( p_aout );
105
106     memset( &pi, 0, sizeof(pi) );
107     memset( &pp, 0, sizeof(pp) );
108
109     pi.channel = SND_PCM_CHANNEL_PLAYBACK;
110     if( ( i_ret = snd_pcm_plugin_info( p_aout->output.p_sys->p_pcm_handle,
111                                        &pi ) ) < 0 )
112     {
113         msg_Err( p_aout, "unable to get plugin info (%s)",
114                          snd_strerror( i_ret ) );
115         CloseAudio( p_this );
116         free( p_aout->output.p_sys );
117         return -1;
118     }
119
120     pp.mode       = SND_PCM_MODE_BLOCK;
121     pp.channel    = SND_PCM_CHANNEL_PLAYBACK;
122     pp.start_mode = SND_PCM_START_FULL;
123     pp.stop_mode  = SND_PCM_STOP_STOP;
124
125     pp.buf.block.frags_max   = 3;
126     pp.buf.block.frags_min   = 1;
127
128     pp.format.interleave     = 1;
129     pp.format.rate           = p_aout->output.output.i_rate;
130
131     i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
132     if ( i_nb_channels > 2 )
133     {
134         /* I don't know if QNX supports more than two channels. */
135         i_nb_channels = 2;
136         p_aout->output.output.i_channels = AOUT_CHAN_STEREO;
137     }
138     pp.format.voices         = i_nb_channels;
139
140     p_aout->output.output.i_format = VLC_CODEC_S16N;
141     p_aout->output.i_nb_samples = DEFAULT_FRAME_SIZE;
142     pp.format.format = SND_PCM_SFMT_S16;
143     i_bytes_per_sample = 2;
144
145     pp.buf.block.frag_size = p_aout->output.i_nb_samples *
146                             p_aout->output.output.i_channels *
147                             i_bytes_per_sample;
148
149     /* set parameters */
150     if( ( i_ret = snd_pcm_plugin_params( p_aout->output.p_sys->p_pcm_handle,
151                                          &pp ) ) < 0 )
152     {
153         msg_Err( p_aout, "unable to set parameters (%s)", snd_strerror(i_ret) );
154         CloseAudio( p_this );
155         free( p_aout->output.p_sys );
156         return -1;
157     }
158
159     /* prepare channel */
160     if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle,
161                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
162     {
163         msg_Err( p_aout, "unable to prepare channel (%s)",
164                          snd_strerror( i_ret ) );
165         CloseAudio( p_this );
166         free( p_aout->output.p_sys );
167         return -1;
168     }
169
170     /* Create audio thread and wait for its readiness. */
171     if( vlc_thread_create( p_aout, "aout", QNXaoutThread,
172                            VLC_THREAD_PRIORITY_OUTPUT ) )
173     {
174         msg_Err( p_aout, "cannot create QNX audio thread (%m)" );
175         CloseAudio( p_this );
176         free( p_aout->output.p_sys );
177         return -1;
178     }
179
180     return( 0 );
181 }
182
183 /*****************************************************************************
184  * GetBufInfo: buffer status query
185  *****************************************************************************
186  * This function returns the number of used byte in the queue.
187  * It also deals with errors : indeed if the device comes to run out
188  * of data to play, it switches to the "underrun" status. It has to
189  * be flushed and re-prepared
190  *****************************************************************************/
191 static int GetBufInfo( aout_instance_t *p_aout )
192 {
193     int i_ret;
194     snd_pcm_channel_status_t status;
195
196     /* get current pcm status */
197     memset( &status, 0, sizeof(status) );
198     if( ( i_ret = snd_pcm_plugin_status( p_aout->output.p_sys->p_pcm_handle,
199                                          &status ) ) < 0 )
200     {
201         msg_Err( p_aout, "unable to get device status (%s)",
202                          snd_strerror( i_ret ) );
203         return( -1 );
204     }
205
206     /* check for underrun */
207     switch( status.status )
208     {
209         case SND_PCM_STATUS_READY:
210         case SND_PCM_STATUS_UNDERRUN:
211             if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle,
212                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
213             {
214                 msg_Err( p_aout, "unable to prepare channel (%s)",
215                                  snd_strerror( i_ret ) );
216             }
217             break;
218     }
219
220     return( status.count );
221 }
222
223 /*****************************************************************************
224  * Play : plays a sample
225  *****************************************************************************
226  * Plays a sample using the snd_pcm_write function from the alsa API
227  *****************************************************************************/
228 static void Play( aout_instance_t *p_aout )
229 {
230 }
231
232 /*****************************************************************************
233  * CloseAudio: close the audio device
234  *****************************************************************************/
235 void CloseAudio ( vlc_object_t *p_this )
236 {
237     aout_instance_t *p_aout = (aout_instance_t *)p_this;
238     int i_ret;
239
240     vlc_object_kill( p_aout );
241     vlc_thread_join( p_aout );
242
243     if( ( i_ret = snd_pcm_close( p_aout->output.p_sys->p_pcm_handle ) ) < 0 )
244     {
245         msg_Err( p_aout, "unable to close audio device (%s)",
246                          snd_strerror( i_ret ) );
247     }
248
249     free( p_aout->output.p_sys->p_silent_buffer );
250     free( p_aout->output.p_sys );
251 }
252
253
254 /*****************************************************************************
255  * QNXaoutThread: asynchronous thread used to DMA the data to the device
256  *****************************************************************************/
257 static void* QNXaoutThread( vlc_object_t *p_this )
258 {
259     aout_instance_t * p_aout = (aout_instance_t*)p_this;
260     struct aout_sys_t * p_sys = p_aout->output.p_sys;
261     int canc = vlc_savecancel ();
262
263     while ( vlc_object_alive (p_aout) )
264     {
265         aout_buffer_t * p_buffer;
266         int i_tmp, i_size;
267         uint8_t * p_bytes;
268
269         if ( p_aout->output.output.i_format != VLC_CODEC_SPDIFL )
270         {
271             mtime_t next_date = 0;
272
273             /* Get the presentation date of the next write() operation. It
274              * is equal to the current date + duration of buffered samples.
275              * Order is important here, since GetBufInfo is believed to take
276              * more time than mdate(). */
277             next_date = (mtime_t)GetBufInfo( p_aout ) * 1000000
278                       / p_aout->output.output.i_bytes_per_frame
279                       / p_aout->output.output.i_rate
280                       * p_aout->output.output.i_frame_length;
281             next_date += mdate();
282
283             p_buffer = aout_OutputNextBuffer( p_aout, next_date, false );
284         }
285         else
286         {
287             p_buffer = aout_OutputNextBuffer( p_aout, 0, true );
288         }
289
290         if ( p_buffer != NULL )
291         {
292             p_bytes = p_buffer->p_buffer;
293             i_size = p_buffer->i_buffer;
294         }
295         else
296         {
297             i_size = DEFAULT_FRAME_SIZE / p_aout->output.output.i_frame_length
298                       * p_aout->output.output.i_bytes_per_frame;
299             p_bytes = p_aout->output.p_sys->p_silent_buffer;
300             memset( p_bytes, 0, i_size );
301         }
302
303         i_tmp = snd_pcm_plugin_write( p_aout->output.p_sys->p_pcm_handle,
304                                         (void *) p_bytes,
305                                         (size_t) i_size );
306
307         if( i_tmp < 0 )
308         {
309             msg_Err( p_aout, "write failed (%m)" );
310         }
311
312         if ( p_buffer != NULL )
313         {
314             aout_BufferFree( p_buffer );
315         }
316     }
317
318     vlc_restorecancel (canc);
319     return NULL;
320 }
321