dec.c 9.95 KB
Newer Older
1
2
3
/*****************************************************************************
 * dec.c : audio output API towards decoders
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2002-2007 VLC authors and VideoLAN
5
 * $Id$
6
7
8
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
9
10
11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12
 * (at your option) any later version.
13
 *
14
15
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
16
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
19
20
21
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
27
28
29
30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
32
#include <assert.h>

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

38
#include "aout_internal.h"
39
#include "libvlc.h"
40

41
42
43
/**
 * Creates an audio output
 */
44
45
46
47
int aout_DecNew( audio_output_t *p_aout,
                 const audio_sample_format_t *p_format,
                 const audio_replay_gain_t *p_replay_gain,
                 const aout_request_vout_t *p_request_vout )
48
{
49
    /* Sanitize audio format */
50
51
52
    if( p_format->i_channels != aout_FormatNbChannels( p_format ) )
    {
        msg_Err( p_aout, "incompatible audio channels count with layout mask" );
53
        return -1;
54
    }
55
56
57
58
59

    if( p_format->i_rate > 192000 )
    {
        msg_Err( p_aout, "excessive audio sample frequency (%u)",
                 p_format->i_rate );
60
        return -1;
61
    }
62
63
64
65
    if( p_format->i_rate < 4000 )
    {
        msg_Err( p_aout, "too low audio sample frequency (%u)",
                 p_format->i_rate );
66
        return -1;
67
    }
68

69
    aout_owner_t *owner = aout_owner(p_aout);
70
    int ret = 0;
71

72
    /* TODO: reduce lock scope depending on decoder's real need */
73
    aout_lock( p_aout );
74

75
    /* Create the audio output stream */
76
    var_Destroy( p_aout, "audio-device" );
77
    var_Destroy( p_aout, "stereo-mode" );
78

79
    owner->input_format = *p_format;
80
    vlc_atomic_set (&owner->restart, 0);
81
    owner->volume = aout_volume_New (p_aout, p_replay_gain);
82
    if( aout_OutputNew( p_aout, p_format ) < 0 )
83
        goto error;
84
    aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
85
86

    /* Create the audio filtering "input" pipeline */
87
88
89
    date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1);
    date_Set (&owner->sync.date, VLC_TS_INVALID);

90
    assert (owner->input == NULL);
91
92
93
    owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format,
                                  p_request_vout);
    if (owner->input == NULL)
94
    {
95
        aout_OutputDelete (p_aout);
96
error:
97
        aout_volume_Delete (owner->volume);
98
        ret = -1;
99
    }
100
    aout_unlock( p_aout );
101
    return ret;
102
103
}

104
105
106
/**
 * Stops all plugins involved in the audio output.
 */
107
void aout_DecDelete (audio_output_t *p_aout)
108
{
109
    aout_owner_t *owner = aout_owner (p_aout);
110
    aout_input_t *input;
111

112
    aout_lock( p_aout );
113
    /* Remove the input. */
114
    input = owner->input;
115
116
    if (likely(input != NULL))
        aout_InputDelete (p_aout, input);
117
    owner->input = NULL;
118

119
120
121
    aout_OutputDelete( p_aout );
    aout_volume_Delete (owner->volume);

122
    var_Destroy( p_aout, "audio-device" );
123
    var_Destroy( p_aout, "stereo-mode" );
124

125
    aout_unlock( p_aout );
126
    free (input);
127
128
}

129
130
#define AOUT_RESTART_OUTPUT 1
#define AOUT_RESTART_INPUT  2
131
132
133
134
135
136
static void aout_CheckRestart (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

    aout_assert_locked (aout);

137
138
    int restart = vlc_atomic_swap (&owner->restart, 0);
    if (likely(restart == 0))
139
140
        return;

141
    assert (restart & AOUT_RESTART_INPUT);
142
143
144

    const aout_request_vout_t request_vout = owner->input->request_vout;

145
146
    if (likely(owner->input != NULL))
        aout_InputDelete (aout, owner->input);
147
    owner->input = NULL;
148

149
150
    /* Reinitializes the output */
    if (restart & AOUT_RESTART_OUTPUT)
151
    {
152
153
        aout_OutputDelete (aout);
        if (aout_OutputNew (aout, &owner->input_format))
154
155
        {
            aout_volume_Delete (owner->volume);
156
            return; /* we are officially screwed */
157
158
        }
        aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
159
160
    }

161
162
    owner->input = aout_InputNew (aout, &owner->input_format,
                                  &owner->mixer_format, &request_vout);
163
164
}

165
/**
166
167
 * Marks the audio output for restart, to update any parameter of the output
 * plug-in (e.g. output device or channel mapping).
168
 */
169
static void aout_RequestRestart (audio_output_t *aout)
170
171
172
{
    aout_owner_t *owner = aout_owner (aout);

173
174
    /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */
    vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT);
175
176
}

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
int aout_ChannelsRestart (vlc_object_t *obj, const char *varname,
                          vlc_value_t oldval, vlc_value_t newval, void *data)
{
    audio_output_t *aout = (audio_output_t *)obj;
    (void)oldval; (void)newval; (void)data;

    if (!strcmp (varname, "audio-device"))
    {
        /* This is supposed to be a significant change and supposes
         * rebuilding the channel choices. */
        var_Destroy (aout, "stereo-mode");
    }
    aout_RequestRestart (aout);
    return 0;
}

193
194
195
196
197
198
/**
 * This function will safely mark aout input to be restarted as soon as
 * possible to take configuration changes into account
 */
void aout_InputRequestRestart (audio_output_t *aout)
{
199
200
201
    aout_owner_t *owner = aout_owner (aout);

    vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
202
203
}

204
205
206
207
208
209
210
211

/*
 * Buffer management
 */

/*****************************************************************************
 * aout_DecNewBuffer : ask for a new empty buffer
 *****************************************************************************/
212
block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples)
213
{
214
215
216
    /* NOTE: the caller is responsible for serializing input change */
    aout_owner_t *owner = aout_owner (aout);

217
218
    size_t length = samples * owner->input_format.i_bytes_per_frame
                            / owner->input_format.i_frame_length;
219
    block_t *block = block_Alloc( length );
220
221
    if( likely(block != NULL) )
    {
222
        block->i_nb_samples = samples;
223
224
225
        block->i_pts = block->i_length = 0;
    }
    return block;
226
227
228
229
230
}

/*****************************************************************************
 * aout_DecDeleteBuffer : destroy an undecoded buffer
 *****************************************************************************/
231
void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
232
{
233
    (void) aout;
234
    block_Release (block);
235
236
237
238
239
}

/*****************************************************************************
 * aout_DecPlay : filter & mix the decoded buffer
 *****************************************************************************/
240
int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate)
241
{
242
    aout_owner_t *owner = aout_owner (p_aout);
243
    aout_input_t *input;
244

245
246
    assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE &&
            i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE );
247
    assert( p_buffer->i_pts > 0 );
248

249
    p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000
250
                                / owner->input_format.i_rate;
251

252
    aout_lock( p_aout );
253
254
255
256
    aout_CheckRestart( p_aout );

    input = owner->input;
    if (unlikely(input == NULL)) /* can happen due to restart */
257
    {
258
        aout_unlock( p_aout );
259
        block_Release( p_buffer );
260
261
262
        return -1;
    }

263
    /* Input */
264
    p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate,
265
                               &owner->sync.date);
266
    if( p_buffer != NULL )
267
    {
268
269
        date_Increment (&owner->sync.date, p_buffer->i_nb_samples);

270
        /* Mixer */
271
        aout_volume_Amplify (owner->volume, p_buffer);
272
273

        /* Output */
274
275
        aout_OutputPlay( p_aout, p_buffer );
    }
276

277
    aout_unlock( p_aout );
278
279
    return 0;
}
280

281
int aout_DecGetResetLost (audio_output_t *aout)
282
{
283
284
    aout_owner_t *owner = aout_owner (aout);
    aout_input_t *input = owner->input;
285
286
    int val;

287
    aout_lock (aout);
288
289
290
291
292
293
294
    if (likely(input != NULL))
    {
        val = input->i_buffer_lost;
        input->i_buffer_lost = 0;
    }
    else
        val = 0; /* if aout_CheckRestart() failed */
295
    aout_unlock (aout);
296

297
    return val;
298
299
}

300
void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date)
Laurent Aimar's avatar
Laurent Aimar committed
301
{
302
    aout_owner_t *owner = aout_owner (aout);
303

304
    aout_lock (aout);
305
306
    /* XXX: Should the date be offset by the pause duration instead? */
    date_Set (&owner->sync.date, VLC_TS_INVALID);
307
308
    aout_OutputPause (aout, paused, date);
    aout_unlock (aout);
Laurent Aimar's avatar
Laurent Aimar committed
309
310
}

311
void aout_DecFlush (audio_output_t *aout)
Laurent Aimar's avatar
Laurent Aimar committed
312
{
313
314
315
    aout_owner_t *owner = aout_owner (aout);

    aout_lock (aout);
316
    date_Set (&owner->sync.date, VLC_TS_INVALID);
317
318
    aout_OutputFlush (aout, false);
    aout_unlock (aout);
Laurent Aimar's avatar
Laurent Aimar committed
319
320
}

321
bool aout_DecIsEmpty (audio_output_t *aout)
322
{
323
    aout_owner_t *owner = aout_owner (aout);
324
325
    mtime_t end_date, now = mdate ();
    bool empty;
326

327
    aout_lock (aout);
328
    end_date = date_Get (&owner->sync.date);
329
330
331
332
333
334
    empty = end_date == VLC_TS_INVALID || end_date <= now;
    if (empty)
        /* The last PTS has elapsed already. So the underlying audio output
         * buffer should be empty or almost. Thus draining should be fast
         * and will not block the caller too long. */
        aout_OutputFlush (aout, true);
335
    aout_unlock (aout);
336
    return empty;
337
}