portaudio.c 21 KB
Newer Older
1
/*****************************************************************************
2
 * portaudio.c : portaudio (v19) audio output plugin
3
 *****************************************************************************
4
 * Copyright (C) 2002, 2006 the VideoLAN team
5 6 7
 * $Id$
 *
 * Authors: Frederic Ruget <frederic.ruget@free.fr>
8
 *          Gildas Bazin <gbazin@videolan.org>
9 10 11 12 13
 *
 * This program 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.
zorglub's avatar
zorglub committed
14
 *
15 16 17 18 19 20 21
 * This program 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
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27 28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
35
#include <vlc_aout.h>
36

zorglub's avatar
zorglub committed
37 38

#include <portaudio.h>
39 40 41

#define FRAME_SIZE 1024              /* The size is in samples, not in bytes */

42 43 44 45
#ifdef WIN32
#   define PORTAUDIO_IS_SERIOUSLY_BROKEN 1
#endif

46 47 48
/*****************************************************************************
 * aout_sys_t: portaudio audio output method descriptor
 *****************************************************************************/
49
typedef struct
50
{
51
    audio_output_t *p_aout;
52

53
    vlc_thread_t thread;
54 55
    vlc_cond_t  wait;
    vlc_mutex_t lock_wait;
56
    bool  b_wait;
57 58
    vlc_cond_t  signal;
    vlc_mutex_t lock_signal;
59
    bool  b_signal;
60
    bool  b_error;
61 62 63

} pa_thread_t;

64 65
struct aout_sys_t
{
66
    audio_output_t *p_aout;
67 68 69 70 71 72
    PaStream *p_stream;

    PaDeviceIndex i_devices;
    int i_sample_size;
    PaDeviceIndex i_device_id;
    const PaDeviceInfo *deviceInfo;
73

74
    bool b_chan_reorder;              /* do we need channel reordering */
75 76 77 78
    int pi_chan_table[AOUT_CHAN_MAX];
    uint32_t i_channel_mask;
    uint32_t i_bits_per_sample;
    uint32_t i_channels;
79 80
};

81 82 83
static const uint32_t pi_channels_out[] =
    { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
      AOUT_CHAN_CENTER, AOUT_CHAN_LFE,
84
      AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
85 86
      AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 };

87
#ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
88
static bool b_init = 0;
89
static pa_thread_t *pa_thread;
90
static void* PORTAUDIOThread( void * );
91 92
#endif

93 94 95 96 97
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
static int  Open        ( vlc_object_t * );
static void Close       ( vlc_object_t * );
98
static void Play        ( audio_output_t * );
99

100 101
static int PAOpenDevice( audio_output_t * );
static int PAOpenStream( audio_output_t * );
102 103 104 105 106 107 108

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define DEVICE_TEXT N_("Output device")
#define DEVICE_LONGTEXT N_("Portaudio identifier for the output device")

109 110 111 112 113
vlc_module_begin ()
    set_shortname( "PortAudio" )
    set_description( N_("PORTAUDIO audio output") )
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_AOUT )
114
    add_integer( "portaudio-audio-device", 0,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
115
                 DEVICE_TEXT, DEVICE_LONGTEXT, false )
116 117 118 119
        add_deprecated_alias( "portaudio-device" )   /* deprecated since 0.9.3 */
    set_capability( "audio output", 0 )
    set_callbacks( Open, Close )
vlc_module_end ()
120 121

/* This routine will be called by the PortAudio engine when audio is needed.
122 123 124 125
 * It may called at interrupt level on some machines so don't do anything
 * that could mess up the system like calling malloc() or free().
 */
static int paCallback( const void *inputBuffer, void *outputBuffer,
126 127 128
                       unsigned long framesPerBuffer,
                       const PaStreamCallbackTimeInfo *paDate,
                       PaStreamCallbackFlags statusFlags, void *p_cookie )
129
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
130 131
    VLC_UNUSED( inputBuffer ); VLC_UNUSED( statusFlags );

132
    struct aout_sys_t *p_sys = (struct aout_sys_t*) p_cookie;
133
    audio_output_t   *p_aout = p_sys->p_aout;
134 135
    aout_buffer_t     *p_buffer;
    mtime_t out_date;
136

137 138
    out_date = mdate() + (mtime_t) ( 1000000 *
        ( paDate->outputBufferDacTime - paDate->currentTime ) );
139
    p_buffer = aout_OutputNextBuffer( p_aout, out_date, true );
140 141 142

    if ( p_buffer != NULL )
    {
143 144 145
        if( p_sys->b_chan_reorder )
        {
            /* Do the channel reordering here */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
146
            aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer,
147 148 149
                                 p_sys->i_channels, p_sys->pi_chan_table,
                                 p_sys->i_bits_per_sample );
        }
150 151
        vlc_memcpy( outputBuffer, p_buffer->p_buffer,
                    framesPerBuffer * p_sys->i_sample_size );
152 153 154 155 156 157
        /* aout_BufferFree may be dangereous here, but then so is
         * aout_OutputNextBuffer (calls aout_BufferFree internally).
         * one solution would be to link the no longer useful buffers
         * in a second fifo (in aout_OutputNextBuffer too) and to
         * wait until we are in Play to do the actual free.
         */
158 159 160
        aout_BufferFree( p_buffer );
    }
    else
161
        /* Audio output buffer shortage -> stop the fill process and wait */
162
    {
163
        vlc_memset( outputBuffer, 0, framesPerBuffer * p_sys->i_sample_size );
164 165 166 167 168 169 170
    }
    return 0;
}

/*****************************************************************************
 * Open: open the audio device
 *****************************************************************************/
171
static int Open( vlc_object_t * p_this )
172
{
173
    audio_output_t *p_aout = (audio_output_t *)p_this;
174 175
    struct aout_sys_t * p_sys;

176
    msg_Dbg( p_aout, "entering Open()");
177 178

    /* Allocate p_sys structure */
179
    p_sys = malloc( sizeof(aout_sys_t) );
180 181 182
    if( p_sys == NULL )
        return VLC_ENOMEM;
    p_sys->p_aout = p_aout;
183
    p_sys->p_stream = 0;
184 185 186
    p_aout->sys = p_sys;
    p_aout->pf_play = Play;
    p_aout->pf_pause = NULL;
187
    p_aout->pf_flush = NULL;
188

189
    /* Retrieve output device id from config */
190
    p_sys->i_device_id = var_CreateGetInteger( p_aout, "portaudio-audio-device" );
191

192 193
#ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
    if( !b_init )
194
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
195 196
        int i_err;

197 198 199 200 201 202 203
        /* Test device */
        if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
        {
            msg_Err( p_aout, "cannot open portaudio device" );
            free( p_sys );
            return VLC_EGENERIC;
        }
204

205 206 207
        /* Close device for now. We'll re-open it later on */
        if( ( i_err = Pa_Terminate() ) != paNoError )
        {
208
            msg_Err( p_aout, "closing the device returned %d", i_err );
209 210
        }

211
        b_init = true;
212 213

        /* Now we need to setup our DirectSound play notification structure */
214
        pa_thread = calloc( 1, sizeof(*pa_thread) );
215
        pa_thread->p_aout = p_aout;
216
        pa_thread->b_error = false;
217
        vlc_mutex_init( &pa_thread->lock_wait );
218
        vlc_cond_init( &pa_thread->wait );
219
        pa_thread->b_wait = false;
220
        vlc_mutex_init( &pa_thread->lock_signal );
221
        vlc_cond_init( &pa_thread->signal );
222
        pa_thread->b_signal = false;
223 224

        /* Create PORTAUDIOThread */
225
        if( vlc_clone( &pa_thread->thread, PORTAUDIOThread, pa_thread,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
226
                               VLC_THREAD_PRIORITY_OUTPUT ) )
227 228 229 230 231 232
        {
            msg_Err( p_aout, "cannot create PORTAUDIO thread" );
            return VLC_EGENERIC;
        }
    }
    else
233
    {
234
        pa_thread->p_aout = p_aout;
235 236 237
        pa_thread->b_wait = false;
        pa_thread->b_signal = false;
        pa_thread->b_error = false;
238
    }
239

240 241
    /* Signal start of stream */
    vlc_mutex_lock( &pa_thread->lock_signal );
242
    pa_thread->b_signal = true;
243 244
    vlc_cond_signal( &pa_thread->signal );
    vlc_mutex_unlock( &pa_thread->lock_signal );
245

246 247
    /* Wait until thread is ready */
    vlc_mutex_lock( &pa_thread->lock_wait );
248
    while( !pa_thread->b_wait )
249 250
        vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait );
    vlc_mutex_unlock( &pa_thread->lock_wait );
251
    pa_thread->b_wait = false;
252

253
    if( pa_thread->b_error )
254
    {
255 256
        msg_Err( p_aout, "PORTAUDIO thread failed" );
        Close( p_this );
257 258
        return VLC_EGENERIC;
    }
259

260 261 262 263 264
    return VLC_SUCCESS;

#else

    if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
265
    {
266 267
        msg_Err( p_aout, "cannot open portaudio device" );
        free( p_sys );
268 269
        return VLC_EGENERIC;
    }
270

271 272 273 274 275
    if( PAOpenStream( p_aout ) != VLC_SUCCESS )
    {
        msg_Err( p_aout, "cannot open portaudio device" );
    }

276
    return VLC_SUCCESS;
277 278

#endif
279 280 281 282 283 284 285
}

/*****************************************************************************
 * Close: close the audio device
 *****************************************************************************/
static void Close ( vlc_object_t *p_this )
{
286
    audio_output_t *p_aout = (audio_output_t *)p_this;
287
    aout_sys_t *p_sys = p_aout->sys;
288 289 290

    msg_Dbg( p_aout, "closing portaudio");

291
#ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
292

293 294
    /* Signal end of stream */
    vlc_mutex_lock( &pa_thread->lock_signal );
295
    pa_thread->b_signal = true;
296 297
    vlc_cond_signal( &pa_thread->signal );
    vlc_mutex_unlock( &pa_thread->lock_signal );
298

299 300
    /* Wait until thread is ready */
    vlc_mutex_lock( &pa_thread->lock_wait );
301
    while( !pa_thread->b_wait )
302 303
        vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait );
    vlc_mutex_unlock( &pa_thread->lock_wait );
304
    pa_thread->b_wait = false;
305

306
#else
307

308
    int i_err = Pa_StopStream( p_sys->p_stream );
309
    if( i_err != paNoError )
310
    {
311 312
        msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err,
                 Pa_GetErrorText( i_err ) );
313
    }
314 315
    i_err = Pa_CloseStream( p_sys->p_stream );
    if( i_err != paNoError )
316
    {
317 318
        msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err,
                 Pa_GetErrorText( i_err ) );
319
    }
320 321 322

    i_err = Pa_Terminate();
    if( i_err != paNoError )
323
    {
324 325 326
        msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
                 Pa_GetErrorText( i_err ) );
    }
327 328 329 330 331

#endif

    msg_Dbg( p_aout, "portaudio closed");
    free( p_sys );
332 333
}

334
static int PAOpenDevice( audio_output_t *p_aout )
335
{
336
    aout_sys_t *p_sys = p_aout->sys;
337 338 339 340 341 342 343 344 345 346 347 348
    const PaDeviceInfo *p_pdi;
    PaError i_err;
    vlc_value_t val, text;
    int i;

    /* Initialize portaudio */
    i_err = Pa_Initialize();
    if( i_err != paNoError )
    {
        msg_Err( p_aout, "Pa_Initialize returned %d : %s",
                 i_err, Pa_GetErrorText( i_err ) );

349 350 351
        return VLC_EGENERIC;
    }

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
    p_sys->i_devices = Pa_GetDeviceCount();
    if( p_sys->i_devices < 0 )
    {
        i_err = p_sys->i_devices;
        msg_Err( p_aout, "Pa_GetDeviceCount returned %d : %s", i_err,
                 Pa_GetErrorText( i_err ) );

        goto error;
    }

    /* Display all devices info */
    msg_Dbg( p_aout, "number of devices = %d", p_sys->i_devices );
    for( i = 0; i < p_sys->i_devices; i++ )
    {
        p_pdi = Pa_GetDeviceInfo( i );
        msg_Dbg( p_aout, "------------------------------------- #%d", i );
        msg_Dbg( p_aout, "Name         = %s", p_pdi->name );
        msg_Dbg( p_aout, "Max Inputs   = %d, Max Outputs = %d",
                  p_pdi->maxInputChannels, p_pdi->maxOutputChannels );
    }
    msg_Dbg( p_aout, "-------------------------------------" );

    msg_Dbg( p_aout, "requested device is #%d", p_sys->i_device_id );
    if( p_sys->i_device_id >= p_sys->i_devices )
    {
        msg_Err( p_aout, "device %d does not exist", p_sys->i_device_id );
        goto error;
    }
    p_sys->deviceInfo = Pa_GetDeviceInfo( p_sys->i_device_id );

    if( p_sys->deviceInfo->maxOutputChannels < 1 )
383
    {
384 385 386 387 388 389 390 391 392 393 394
        msg_Err( p_aout, "no channel available" );
        goto error;
    }

    if( var_Type( p_aout, "audio-device" ) == 0 )
    {
        var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE);
        text.psz_string = _("Audio Device");
        var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );

        if( p_sys->deviceInfo->maxOutputChannels >= 1 )
395
        {
396
            val.i_int = AOUT_VAR_MONO;
397
            text.psz_string = _("Mono");
398 399 400
            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
                        &val, &text );
            msg_Dbg( p_aout, "device supports 1 channel" );
401
        }
402
        if( p_sys->deviceInfo->maxOutputChannels >= 2 )
403
        {
404
            val.i_int = AOUT_VAR_STEREO;
405
            text.psz_string = _("Stereo");
406 407 408 409
            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
                        &val, &text );
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT,
                        &val, NULL );
410
            var_Set( p_aout, "audio-device", val );
411
            msg_Dbg( p_aout, "device supports 2 channels" );
412
        }
413
        if( p_sys->deviceInfo->maxOutputChannels >= 4 )
414
        {
415
            val.i_int = AOUT_VAR_2F2R;
416
            text.psz_string = _("2 Front 2 Rear");
417 418 419
            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
                        &val, &text );
            msg_Dbg( p_aout, "device supports 4 channels" );
420
        }
421
        if( p_sys->deviceInfo->maxOutputChannels >= 5 )
422
        {
423
            val.i_int = AOUT_VAR_3F2R;
424
            text.psz_string = _("3 Front 2 Rear");
425 426 427
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
            msg_Dbg( p_aout, "device supports 5 channels" );
428
        }
429
        if( p_sys->deviceInfo->maxOutputChannels >= 6 )
430
        {
431
            val.i_int = AOUT_VAR_5_1;
432
            text.psz_string = _("5.1");
433 434 435
            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
                        &val, &text );
            msg_Dbg( p_aout, "device supports 5.1 channels" );
436 437
        }

438
        var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
439
        var_TriggerCallback( p_aout, "intf-change" );
440 441
    }

442
    /* Audio format is paFloat32 (always supported by portaudio v19) */
443
    p_aout->format.i_format = VLC_CODEC_FL32;
444

445
    return VLC_SUCCESS;
446 447 448 449 450 451 452

 error:
    if( ( i_err = Pa_Terminate() ) != paNoError )
    {
        msg_Err( p_aout, "Pa_Terminate returned %d", i_err );
    }
    return VLC_EGENERIC;
453 454
}

455
static int PAOpenStream( audio_output_t *p_aout )
456
{
457
    aout_sys_t *p_sys = p_aout->sys;
458 459 460 461
    const PaHostErrorInfo* paLastHostErrorInfo = Pa_GetLastHostErrorInfo();
    PaStreamParameters paStreamParameters;
    vlc_value_t val;
    int i_channels, i_err;
462
    uint32_t i_channel_mask;
463

464 465 466 467
    if( var_Get( p_aout, "audio-device", &val ) < 0 )
    {
        return VLC_EGENERIC;
    }
468

469 470
    if( val.i_int == AOUT_VAR_5_1 )
    {
471
        p_aout->format.i_physical_channels
472 473 474 475 476
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
              | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
              | AOUT_CHAN_LFE;
    }
    else if( val.i_int == AOUT_VAR_3F2R )
477
    {
478
        p_aout->format.i_physical_channels
479 480
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
            | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
481
    }
482
    else if( val.i_int == AOUT_VAR_2F2R )
483
    {
484
        p_aout->format.i_physical_channels
485 486
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
            | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
487
    }
488
    else if( val.i_int == AOUT_VAR_MONO )
489
    {
490
        p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
491 492 493
    }
    else
    {
494
        p_aout->format.i_physical_channels
495
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
496 497
    }

498
    i_channels = aout_FormatNbChannels( &p_aout->format );
499
    msg_Dbg( p_aout, "nb_channels requested = %d", i_channels );
500
    i_channel_mask = p_aout->format.i_physical_channels;
501 502 503

    /* Calculate the frame size in bytes */
    p_sys->i_sample_size = 4 * i_channels;
504 505
    p_aout->i_nb_samples = FRAME_SIZE;
    aout_FormatPrepare( &p_aout->format );
506 507
    aout_VolumeSoftInit( p_aout );

508
    /* Check for channel reordering */
509 510 511
    p_aout->sys->i_channel_mask = i_channel_mask;
    p_aout->sys->i_bits_per_sample = 32; /* forced to paFloat32 */
    p_aout->sys->i_channels = i_channels;
512

513
    p_aout->sys->b_chan_reorder =
514
        aout_CheckChannelReorder( NULL, pi_channels_out,
515
                                  i_channel_mask, i_channels,
516
                                  p_aout->sys->pi_chan_table );
517

518
    if( p_aout->sys->b_chan_reorder )
519 520 521 522
    {
        msg_Dbg( p_aout, "channel reordering needed" );
    }

523 524 525 526 527 528 529 530
    paStreamParameters.device = p_sys->i_device_id;
    paStreamParameters.channelCount = i_channels;
    paStreamParameters.sampleFormat = paFloat32;
    paStreamParameters.suggestedLatency =
        p_sys->deviceInfo->defaultLowOutputLatency;
    paStreamParameters.hostApiSpecificStreamInfo = NULL;

    i_err = Pa_OpenStream( &p_sys->p_stream, NULL /* no input */,
531
                &paStreamParameters, (double)p_aout->format.i_rate,
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
                FRAME_SIZE, paClipOff, paCallback, p_sys );
    if( i_err != paNoError )
    {
        msg_Err( p_aout, "Pa_OpenStream returns %d : %s", i_err,
                 Pa_GetErrorText( i_err ) );
        if( i_err == paUnanticipatedHostError )
        {
            msg_Err( p_aout, "type %d code %ld : %s",
                     paLastHostErrorInfo->hostApiType,
                     paLastHostErrorInfo->errorCode,
                     paLastHostErrorInfo->errorText );
        }
        p_sys->p_stream = 0;
        return VLC_EGENERIC;
    }

    i_err = Pa_StartStream( p_sys->p_stream );
    if( i_err != paNoError )
    {
        msg_Err( p_aout, "Pa_StartStream() failed" );
        Pa_CloseStream( p_sys->p_stream );
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
557
}
558 559 560 561

/*****************************************************************************
 * Play: play sound
 *****************************************************************************/
562
static void Play( audio_output_t * p_aout )
563
{
564
    VLC_UNUSED( p_aout );
565 566 567 568 569 570 571
}

#ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
/*****************************************************************************
 * PORTAUDIOThread: all interactions with libportaudio.a are handled
 * in this single thread.  Otherwise libportaudio.a is _not_ happy :-(
 *****************************************************************************/
572
static void* PORTAUDIOThread( void *data )
573
{
574
    pa_thread_t *pa_thread = (pa_thread_t*)data;
575
    audio_output_t *p_aout;
576 577
    aout_sys_t *p_sys;
    int i_err;
578
    int canc = vlc_savecancel ();
579

580
    for( ;; )
581 582 583
    {
        /* Wait for start of stream */
        vlc_mutex_lock( &pa_thread->lock_signal );
584
        while( !pa_thread->b_signal )
585 586
            vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal );
        vlc_mutex_unlock( &pa_thread->lock_signal );
587
        pa_thread->b_signal = false;
588 589

        p_aout = pa_thread->p_aout;
590
        p_sys = p_aout->sys;
591 592 593 594

        if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
        {
            msg_Err( p_aout, "cannot open portaudio device" );
595
            pa_thread->b_error = true;
596 597 598 599 600
        }

        if( !pa_thread->b_error && PAOpenStream( p_aout ) != VLC_SUCCESS )
        {
            msg_Err( p_aout, "cannot open portaudio device" );
601
            pa_thread->b_error = true;
602 603 604 605 606 607 608 609 610 611 612

            i_err = Pa_Terminate();
            if( i_err != paNoError )
            {
                msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
                         Pa_GetErrorText( i_err ) );
            }
        }

        /* Tell the main thread that we are ready */
        vlc_mutex_lock( &pa_thread->lock_wait );
613
        pa_thread->b_wait = true;
614 615 616 617 618
        vlc_cond_signal( &pa_thread->wait );
        vlc_mutex_unlock( &pa_thread->lock_wait );

        /* Wait for end of stream */
        vlc_mutex_lock( &pa_thread->lock_signal );
619
        while( !pa_thread->b_signal )
620 621
            vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal );
        vlc_mutex_unlock( &pa_thread->lock_signal );
622
        pa_thread->b_signal = false;
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646

        if( pa_thread->b_error ) continue;

        i_err = Pa_StopStream( p_sys->p_stream );
        if( i_err != paNoError )
        {
            msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err,
                     Pa_GetErrorText( i_err ) );
        }
        i_err = Pa_CloseStream( p_sys->p_stream );
        if( i_err != paNoError )
        {
            msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err,
                     Pa_GetErrorText( i_err ) );
        }
        i_err = Pa_Terminate();
        if( i_err != paNoError )
        {
            msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
                     Pa_GetErrorText( i_err ) );
        }

        /* Tell the main thread that we are ready */
        vlc_mutex_lock( &pa_thread->lock_wait );
647
        pa_thread->b_wait = true;
648 649 650
        vlc_cond_signal( &pa_thread->wait );
        vlc_mutex_unlock( &pa_thread->lock_wait );
    }
651
    vlc_restorecancel (canc);
652
    return NULL;
653 654
}
#endif