dec.c 13.9 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
static int ReplayGainCallback (vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void *);

44
45
46
/**
 * Creates an audio output
 */
47
48
49
50
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 )
51
{
52
    /* Sanitize audio format */
53
    if( p_format->i_channels > AOUT_CHAN_MAX )
54
55
56
    {
        msg_Err( p_aout, "too many audio channels (%u)",
                 p_format->i_channels );
57
        return -1;
58
    }
59
60
61
    if( p_format->i_channels <= 0 )
    {
        msg_Err( p_aout, "no audio channels" );
62
        return -1;
63
    }
64
65
66
    if( p_format->i_channels != aout_FormatNbChannels( p_format ) )
    {
        msg_Err( p_aout, "incompatible audio channels count with layout mask" );
67
        return -1;
68
    }
69
70
71
72
73

    if( p_format->i_rate > 192000 )
    {
        msg_Err( p_aout, "excessive audio sample frequency (%u)",
                 p_format->i_rate );
74
        return -1;
75
    }
76
77
78
79
    if( p_format->i_rate < 4000 )
    {
        msg_Err( p_aout, "too low audio sample frequency (%u)",
                 p_format->i_rate );
80
        return -1;
81
    }
82

83
    aout_owner_t *owner = aout_owner(p_aout);
84
#ifdef RECYCLE
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    /* Calling decoder is responsible for serializing aout_DecNew() and
     * aout_DecDelete(). So no need to lock to _read_ those properties. */
    if (owner->module != NULL) /* <- output exists */
    {   /* Check if we can recycle the existing output and pipelines */
        if (AOUT_FMTS_IDENTICAL(&owner->input_format, p_format))
            return 0;

        /* TODO? If the new input format is closer to the output format than
         * the old input format was, then the output could be recycled. The
         * input pipeline however would need to be restarted. */

        /* No recycling: delete everything and restart from scratch */
        aout_Shutdown (p_aout);
    }
99
#endif
100
    int ret = 0;
101

102
    /* TODO: reduce lock scope depending on decoder's real need */
103
    aout_lock( p_aout );
104
    assert (owner->module == NULL);
105

106
    /* Create the audio output stream */
107
108
    var_Destroy( p_aout, "audio-device" );
    var_Destroy( p_aout, "audio-channels" );
109

110
    owner->input_format = *p_format;
111
    vlc_atomic_set (&owner->restart, 0);
112
    if( aout_OutputNew( p_aout, p_format ) < 0 )
113
114
    {
        ret = -1;
115
        goto error;
116
    }
117

118
    /* Allocate a software mixer */
119
    assert (owner->volume.mixer == NULL);
120
    owner->volume.mixer = aout_MixerNew (p_aout, owner->mixer_format.i_format);
121

122
123
124
125
126
127
    aout_ReplayGainInit (&owner->gain.data, p_replay_gain);
    var_AddCallback (p_aout, "audio-replay-gain-mode",
                     ReplayGainCallback, owner);
    var_TriggerCallback (p_aout, "audio-replay-gain-mode");

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

131
    assert (owner->input == NULL);
132
133
134
    owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format,
                                  p_request_vout);
    if (owner->input == NULL)
135
136
137
138
    {
        struct audio_mixer *mixer = owner->volume.mixer;

        owner->volume.mixer = NULL;
139
        aout_OutputDelete (p_aout);
140
141
142
143
        aout_unlock (p_aout);
        aout_MixerDelete (mixer);
        return -1;
    }
144
145
error:
    aout_unlock( p_aout );
146
    return ret;
147
148
}

149
150
151
152
/**
 * Stops all plugins involved in the audio output.
 */
void aout_Shutdown (audio_output_t *p_aout)
153
{
154
    aout_owner_t *owner = aout_owner (p_aout);
155
    aout_input_t *input;
156
157
    struct audio_mixer *mixer;

158
    aout_lock( p_aout );
159
    /* Remove the input. */
160
    input = owner->input;
161
162
    if (likely(input != NULL))
        aout_InputDelete (p_aout, input);
163
    owner->input = NULL;
164

165
166
    mixer = owner->volume.mixer;
    owner->volume.mixer = NULL;
167
168
169
170
171

    var_DelCallback (p_aout, "audio-replay-gain-mode",
                     ReplayGainCallback, owner);

    aout_OutputDelete( p_aout );
172
173
    var_Destroy( p_aout, "audio-device" );
    var_Destroy( p_aout, "audio-channels" );
174

175
    aout_unlock( p_aout );
176
177

    aout_MixerDelete (mixer);
178
    free (input);
179
180
}

181
182
183
184
185
186
/**
 * Stops the decoded audio input.
 * @note Due to output recycling, this function is esssentially a stub.
 */
void aout_DecDelete (audio_output_t *aout)
{
187
#ifdef RECYCLE
188
    (void) aout;
189
190
191
#else
    aout_Shutdown (aout);
#endif
192
193
}

194
195
#define AOUT_RESTART_OUTPUT 1
#define AOUT_RESTART_INPUT  2
196
197
198
199
200
201
static void aout_CheckRestart (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

    aout_assert_locked (aout);

202
203
    int restart = vlc_atomic_swap (&owner->restart, 0);
    if (likely(restart == 0))
204
205
        return;

206
    assert (restart & AOUT_RESTART_INPUT);
207
208
209

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

210
211
    if (likely(owner->input != NULL))
        aout_InputDelete (aout, owner->input);
212
    owner->input = NULL;
213

214
215
    /* Reinitializes the output */
    if (restart & AOUT_RESTART_OUTPUT)
216
    {
217
218
219
220
221
222
223
224
        aout_MixerDelete (owner->volume.mixer);
        owner->volume.mixer = NULL;
        aout_OutputDelete (aout);

        if (aout_OutputNew (aout, &owner->input_format))
            return; /* we are officially screwed */
        owner->volume.mixer = aout_MixerNew (aout,
                                             owner->mixer_format.i_format);
225
226
    }

227
228
    owner->input = aout_InputNew (aout, &owner->input_format,
                                  &owner->mixer_format, &request_vout);
229
230
}

231
/**
232
233
 * Marks the audio output for restart, to update any parameter of the output
 * plug-in (e.g. output device or channel mapping).
234
 */
235
void aout_RequestRestart (audio_output_t *aout)
236
237
238
{
    aout_owner_t *owner = aout_owner (aout);

239
240
    /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */
    vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT);
241
242
243
244
245
246
247
248
}

/**
 * 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)
{
249
250
251
    aout_owner_t *owner = aout_owner (aout);

    vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
252
253
}

254
255
256
257
258
259
260
261

/*
 * Buffer management
 */

/*****************************************************************************
 * aout_DecNewBuffer : ask for a new empty buffer
 *****************************************************************************/
262
block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples)
263
{
264
265
266
    /* NOTE: the caller is responsible for serializing input change */
    aout_owner_t *owner = aout_owner (aout);

267
268
    size_t length = samples * owner->input_format.i_bytes_per_frame
                            / owner->input_format.i_frame_length;
269
    block_t *block = block_Alloc( length );
270
271
    if( likely(block != NULL) )
    {
272
        block->i_nb_samples = samples;
273
274
275
        block->i_pts = block->i_length = 0;
    }
    return block;
276
277
278
279
280
}

/*****************************************************************************
 * aout_DecDeleteBuffer : destroy an undecoded buffer
 *****************************************************************************/
281
void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
282
{
283
284
    (void) aout;
    aout_BufferFree (block);
285
286
287
288
289
}

/*****************************************************************************
 * aout_DecPlay : filter & mix the decoded buffer
 *****************************************************************************/
290
int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate)
291
{
292
    aout_owner_t *owner = aout_owner (p_aout);
293
    aout_input_t *input;
294

295
296
    assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE &&
            i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE );
297
    assert( p_buffer->i_pts > 0 );
298

299
    p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000
300
                                / owner->input_format.i_rate;
301

302
    aout_lock( p_aout );
303
304
305
306
    aout_CheckRestart( p_aout );

    input = owner->input;
    if (unlikely(input == NULL)) /* can happen due to restart */
307
    {
308
        aout_unlock( p_aout );
309
310
311
312
        aout_BufferFree( p_buffer );
        return -1;
    }

313
    /* Input */
314
    p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate,
315
                               &owner->sync.date);
316
    if( p_buffer != NULL )
317
    {
318
319
        date_Increment (&owner->sync.date, p_buffer->i_nb_samples);

320
        /* Mixer */
321
322
323
324
325
326
        if (owner->volume.mixer != NULL)
        {
            float amp = owner->volume.multiplier
                      * vlc_atomic_getf (&owner->gain.multiplier);
            aout_MixerRun (owner->volume.mixer, p_buffer, amp);
        }
327
328

        /* Output */
329
330
        aout_OutputPlay( p_aout, p_buffer );
    }
331

332
    aout_unlock( p_aout );
333
334
    return 0;
}
335

336
int aout_DecGetResetLost (audio_output_t *aout)
337
{
338
339
    aout_owner_t *owner = aout_owner (aout);
    aout_input_t *input = owner->input;
340
341
    int val;

342
    aout_lock (aout);
343
344
345
346
347
348
349
    if (likely(input != NULL))
    {
        val = input->i_buffer_lost;
        input->i_buffer_lost = 0;
    }
    else
        val = 0; /* if aout_CheckRestart() failed */
350
    aout_unlock (aout);
351

352
    return val;
353
354
}

355
void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date)
Laurent Aimar's avatar
Laurent Aimar committed
356
{
357
    aout_owner_t *owner = aout_owner (aout);
358

359
    aout_lock (aout);
360
361
    /* XXX: Should the date be offset by the pause duration instead? */
    date_Set (&owner->sync.date, VLC_TS_INVALID);
362
363
    aout_OutputPause (aout, paused, date);
    aout_unlock (aout);
Laurent Aimar's avatar
Laurent Aimar committed
364
365
}

366
void aout_DecFlush (audio_output_t *aout)
Laurent Aimar's avatar
Laurent Aimar committed
367
{
368
369
370
    aout_owner_t *owner = aout_owner (aout);

    aout_lock (aout);
371
    date_Set (&owner->sync.date, VLC_TS_INVALID);
372
373
    aout_OutputFlush (aout, false);
    aout_unlock (aout);
Laurent Aimar's avatar
Laurent Aimar committed
374
375
}

376
bool aout_DecIsEmpty (audio_output_t *aout)
377
{
378
    aout_owner_t *owner = aout_owner (aout);
379
380
    mtime_t end_date, now = mdate ();
    bool empty;
381

382
    aout_lock (aout);
383
    end_date = date_Get (&owner->sync.date);
384
385
386
387
388
389
    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);
390
    aout_unlock (aout);
391
    return empty;
392
}
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435

/**
 * Notifies the audio input of the drift from the requested audio
 * playback timestamp (@ref block_t.i_pts) to the anticipated playback time
 * as reported by the audio output hardware.
 * Depending on the drift amplitude, the input core may ignore the drift
 * trigger upsampling or downsampling, or even discard samples.
 * Future VLC versions may instead adjust the input decoding speed.
 *
 * The audio output plugin is responsible for estimating the ideal current
 * playback time defined as follows:
 *  ideal time = buffer timestamp - (output latency + pending buffer duration)
 *
 * Practically, this is the PTS (block_t.i_pts) of the current buffer minus
 * the latency reported by the output programming interface.
 * Computing the estimated drift directly would probably be more intuitive.
 * However the use of an absolute time value does not introduce extra
 * measurement errors due to the CPU scheduling jitter and clock resolution.
 * Furthermore, the ideal while it is an abstract value, is easy for most
 * audio output plugins to compute.
 * The following definition is equivalent but depends on the clock time:
 *  ideal time = real time + drift

 * @note If aout_LatencyReport() is never called, the core will assume that
 * there is no drift.
 *
 * @param ideal estimated ideal time as defined above.
 */
void aout_TimeReport (audio_output_t *aout, mtime_t ideal)
{
    mtime_t delta = mdate() - ideal /* = -drift */;

    aout_assert_locked (aout);
    if (delta < -AOUT_MAX_PTS_ADVANCE || +AOUT_MAX_PTS_DELAY < delta)
    {
        aout_owner_t *owner = aout_owner (aout);

        msg_Warn (aout, "not synchronized (%"PRId64" us), resampling",
                  delta);
        if (date_Get (&owner->sync.date) != VLC_TS_INVALID)
            date_Move (&owner->sync.date, delta);
    }
}
436
437
438
439
440
441
442
443
444
445
446

static int ReplayGainCallback (vlc_object_t *obj, char const *var,
                               vlc_value_t oldval, vlc_value_t val, void *data)
{
    aout_owner_t *owner = data;
    float multiplier = aout_ReplayGainSelect (obj, val.psz_string,
                                              &owner->gain.data);
    vlc_atomic_setf (&owner->gain.multiplier, multiplier);
    VLC_UNUSED(var); VLC_UNUSED(oldval);
    return VLC_SUCCESS;
}