dec.c 14.5 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
    var_Destroy( p_aout, "stereo-mode" );
76

77
    /* Create the audio output stream */
78
    owner->input_format = *p_format;
79
    vlc_atomic_set (&owner->restart, 0);
80
    owner->volume = aout_volume_New (p_aout, p_replay_gain);
81
    if( aout_OutputNew( p_aout, p_format ) < 0 )
82
        goto error;
83
    aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
84
85

    /* Create the audio filtering "input" pipeline */
86
87
    if (aout_FiltersNew (p_aout, p_format, &owner->mixer_format,
                         p_request_vout))
88
    {
89
        aout_OutputDelete (p_aout);
90
error:
91
        aout_volume_Delete (owner->volume);
92
        ret = -1;
93
        goto error;
94
    }
95
96
97

    date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1);
    date_Set (&owner->sync.date, VLC_TS_INVALID);
98
    owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
99
    aout_unlock( p_aout );
100

101
    atomic_init (&owner->buffers_lost, 0);
102

103
    return ret;
104
105
}

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

113
    aout_lock( p_aout );
114
    aout_FiltersDelete (p_aout);
115
116
117
    aout_OutputDelete( p_aout );
    aout_volume_Delete (owner->volume);

118
    var_Destroy( p_aout, "stereo-mode" );
119

120
    aout_unlock( p_aout );
121
122
}

123
124
#define AOUT_RESTART_OUTPUT 1
#define AOUT_RESTART_INPUT  2
125
static int aout_CheckRestart (audio_output_t *aout)
126
127
128
129
130
{
    aout_owner_t *owner = aout_owner (aout);

    aout_assert_locked (aout);

131
132
    int restart = vlc_atomic_swap (&owner->restart, 0);
    if (likely(restart == 0))
133
        return 0;
134

135
    assert (restart & AOUT_RESTART_INPUT);
136

137
    const aout_request_vout_t request_vout = owner->request_vout;
138

139
140
    aout_FiltersDelete (aout);

141
142
    /* Reinitializes the output */
    if (restart & AOUT_RESTART_OUTPUT)
143
    {
144
145
        aout_OutputDelete (aout);
        if (aout_OutputNew (aout, &owner->input_format))
146
            abort (); /* FIXME we are officially screwed */
147
        aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
148
149
    }

150
151
    owner->sync.resamp_type = AOUT_RESAMPLING_NONE;

152
    if (aout_FiltersNew (aout, &owner->input_format, &owner->mixer_format,
153
154
155
156
157
                         &request_vout))
    {
        abort (); /* FIXME */
    }
    return 0;
158
159
}

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

168
169
    /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */
    vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT);
170
171
}

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
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;
}

188
189
190
191
192
193
/**
 * 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)
{
194
195
196
    aout_owner_t *owner = aout_owner (aout);

    vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
197
198
}

199
200
201
202
203
204
205
206

/*
 * Buffer management
 */

/*****************************************************************************
 * aout_DecNewBuffer : ask for a new empty buffer
 *****************************************************************************/
207
block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples)
208
{
209
210
211
    /* NOTE: the caller is responsible for serializing input change */
    aout_owner_t *owner = aout_owner (aout);

212
213
    size_t length = samples * owner->input_format.i_bytes_per_frame
                            / owner->input_format.i_frame_length;
214
    block_t *block = block_Alloc( length );
215
216
    if( likely(block != NULL) )
    {
217
        block->i_nb_samples = samples;
218
219
220
        block->i_pts = block->i_length = 0;
    }
    return block;
221
222
223
224
225
}

/*****************************************************************************
 * aout_DecDeleteBuffer : destroy an undecoded buffer
 *****************************************************************************/
226
void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
227
{
228
    (void) aout;
229
    block_Release (block);
230
231
}

232
233
234
235
236
237
238
239
static void aout_StopResampling (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

    owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
    aout_FiltersAdjustResampling (aout, 0);
}

240
241
242
/*****************************************************************************
 * aout_DecPlay : filter & mix the decoded buffer
 *****************************************************************************/
243
int aout_DecPlay (audio_output_t *aout, block_t *block, int input_rate)
244
{
245
    aout_owner_t *owner = aout_owner (aout);
246

247
248
249
    assert (input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE);
    assert (input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE);
    assert (block->i_pts >= VLC_TS_0);
250

251
252
    block->i_length = CLOCK_FREQ * block->i_nb_samples
                                 / owner->input_format.i_rate;
253

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
    aout_lock (aout);
    if (unlikely(aout_CheckRestart (aout)))
        goto drop; /* Pipeline is unrecoverably broken :-( */

    /* We don't care if someone changes the start date behind our back after
     * this. We'll deal with that when pushing the buffer, and compensate
     * with the next incoming buffer. */
    mtime_t start_date = date_Get (&owner->sync.date);
    const mtime_t now = mdate ();

    if (start_date != VLC_TS_INVALID && start_date < now)
    {   /* The decoder is _very_ late. This can only happen if the user
         * pauses the stream (or if the decoder is buggy, which cannot
         * happen :). */
        msg_Warn (aout, "computed PTS is out of range (%"PRId64"), "
                  "clearing out", now - start_date);
        aout_OutputFlush (aout, false);
        if (owner->sync.resamp_type != AOUT_RESAMPLING_NONE)
            msg_Warn (aout, "timing screwed, stopping resampling");
        aout_StopResampling (aout);
        block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
        start_date = VLC_TS_INVALID;
    }

    if (block->i_pts < now + AOUT_MIN_PREPARE_TIME)
    {   /* The decoder gives us f*cked up PTS. It's its business, but we
         * can't present it anyway, so drop the buffer. */
        msg_Warn (aout, "PTS is out of range (%"PRId64"), dropping buffer",
                  now - block->i_pts);
        aout_StopResampling (aout);
        goto drop;
    }
286

287
288
289
    /* If the audio drift is too big then it's not worth trying to resample
     * the audio. */
    if (start_date == VLC_TS_INVALID)
290
    {
291
292
        start_date = block->i_pts;
        date_Set (&owner->sync.date, start_date);
293
294
    }

295
296
    mtime_t drift = start_date - block->i_pts;
    if (drift < -input_rate * 3 * AOUT_MAX_PTS_ADVANCE / INPUT_RATE_DEFAULT)
297
    {
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
        msg_Warn (aout, "buffer way too early (%"PRId64"), clearing queue",
                  drift);
        aout_OutputFlush (aout, false);
        if (owner->sync.resamp_type != AOUT_RESAMPLING_NONE)
            msg_Warn (aout, "timing screwed, stopping resampling");
        aout_StopResampling (aout);
        block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
        start_date = block->i_pts;
        date_Set (&owner->sync.date, start_date);
        drift = 0;
    }
    else
    if (drift > +input_rate * 3 * AOUT_MAX_PTS_DELAY / INPUT_RATE_DEFAULT)
    {
        msg_Warn (aout, "buffer way too late (%"PRId64"), dropping buffer",
                  drift);
        goto drop;
    }
316

317
318
319
    block = aout_FiltersPlay (aout, block, input_rate);
    if (block == NULL)
    {
320
        atomic_fetch_add(&owner->buffers_lost, 1);
321
322
        goto out;
    }
323

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    /* Adjust the resampler if needed.
     * We first need to calculate the output rate of this resampler. */
    if ((owner->sync.resamp_type == AOUT_RESAMPLING_NONE)
     && (drift < -AOUT_MAX_PTS_ADVANCE || drift > +AOUT_MAX_PTS_DELAY))
    {   /* Can happen in several circumstances :
         * 1. A problem at the input (clock drift)
         * 2. A small pause triggered by the user
         * 3. Some delay in the output stage, causing a loss of lip
         *    synchronization
         * Solution : resample the buffer to avoid a scratch.
         */
        owner->sync.resamp_start_drift = (int)-drift;
        owner->sync.resamp_type = (drift < 0) ? AOUT_RESAMPLING_DOWN
                                             : AOUT_RESAMPLING_UP;
        msg_Warn (aout, (drift < 0)
                  ? "buffer too early (%"PRId64"), down-sampling"
                  : "buffer too late  (%"PRId64"), up-sampling", drift);
    }
    if (owner->sync.resamp_type != AOUT_RESAMPLING_NONE)
    {   /* Resampling has been triggered previously (because of dates
         * mismatch). We want the resampling to happen progressively so
         * it isn't too audible to the listener. */
        const int adjust = (owner->sync.resamp_type == AOUT_RESAMPLING_UP)
            ? +2 : -2;
        /* Check if everything is back to normal, then stop resampling. */
        if (!aout_FiltersAdjustResampling (aout, adjust))
        {
            owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
            msg_Warn (aout, "resampling stopped (drift: %"PRIi64")",
                      block->i_pts - start_date);
        }
        else if (abs ((int)(block->i_pts - start_date))
                                    < abs (owner->sync.resamp_start_drift) / 2)
        {   /* If we reduced the drift from half, then it is time to switch
             * back the resampling direction. */
            if (owner->sync.resamp_type == AOUT_RESAMPLING_UP)
                owner->sync.resamp_type = AOUT_RESAMPLING_DOWN;
            else
                owner->sync.resamp_type = AOUT_RESAMPLING_UP;
            owner->sync.resamp_start_drift = 0;
        }
        else if (owner->sync.resamp_start_drift
              && (abs ((int)(block->i_pts - start_date))
                               > abs (owner->sync.resamp_start_drift) * 3 / 2))
        {   /* If the drift is increasing and not decreasing, than something
             * is bad. We'd better stop the resampling right now. */
            msg_Warn (aout, "timing screwed, stopping resampling");
            aout_StopResampling (aout);
            block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
        }
374
    }
375

376
377
378
379
380
381
382
383
384
385
    block->i_pts = start_date;
    date_Increment (&owner->sync.date, block->i_nb_samples);

    /* Software volume */
    aout_volume_Amplify (owner->volume, block);

    /* Output */
    aout_OutputPlay (aout, block);
out:
    aout_unlock (aout);
386
    return 0;
387
388
drop:
    block_Release (block);
389
    atomic_fetch_add(&owner->buffers_lost, 1);
390
    goto out;
391
}
392

393
int aout_DecGetResetLost (audio_output_t *aout)
394
{
395
    aout_owner_t *owner = aout_owner (aout);
396
    return atomic_exchange(&owner->buffers_lost, 0);
397
398
}

399
void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date)
Laurent Aimar's avatar
Laurent Aimar committed
400
{
401
    aout_owner_t *owner = aout_owner (aout);
402

403
    aout_lock (aout);
404
405
    /* XXX: Should the date be offset by the pause duration instead? */
    date_Set (&owner->sync.date, VLC_TS_INVALID);
406
407
    aout_OutputPause (aout, paused, date);
    aout_unlock (aout);
Laurent Aimar's avatar
Laurent Aimar committed
408
409
}

410
void aout_DecFlush (audio_output_t *aout)
Laurent Aimar's avatar
Laurent Aimar committed
411
{
412
413
414
    aout_owner_t *owner = aout_owner (aout);

    aout_lock (aout);
415
    date_Set (&owner->sync.date, VLC_TS_INVALID);
416
417
    aout_OutputFlush (aout, false);
    aout_unlock (aout);
Laurent Aimar's avatar
Laurent Aimar committed
418
419
}

420
bool aout_DecIsEmpty (audio_output_t *aout)
421
{
422
    aout_owner_t *owner = aout_owner (aout);
423
424
    mtime_t end_date, now = mdate ();
    bool empty;
425

426
    aout_lock (aout);
427
    end_date = date_Get (&owner->sync.date);
428
429
430
431
432
433
    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);
434
    aout_unlock (aout);
435
    return empty;
436
}