dec.c 11.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
#include <vlc_aout.h>
35
#include <vlc_aout_mixer.h>
zorglub's avatar
zorglub committed
36
#include <vlc_input.h>
37
#include <vlc_atomic.h>
zorglub's avatar
zorglub committed
38

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

42
43
44
static int ReplayGainCallback (vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void *);

45
46
47
/**
 * Creates an audio output
 */
48
49
50
51
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 )
52
{
53
    /* Sanitize audio format */
54
55
56
    if( p_format->i_channels != aout_FormatNbChannels( p_format ) )
    {
        msg_Err( p_aout, "incompatible audio channels count with layout mask" );
57
        return -1;
58
    }
59
60
61
62
63

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

73
    aout_owner_t *owner = aout_owner(p_aout);
74
#ifdef RECYCLE
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    /* 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);
    }
89
#endif
90
    int ret = 0;
91

92
    /* TODO: reduce lock scope depending on decoder's real need */
93
    aout_lock( p_aout );
94
    assert (owner->module == NULL);
95

96
    /* Create the audio output stream */
97
98
    var_Destroy( p_aout, "audio-device" );
    var_Destroy( p_aout, "audio-channels" );
99

100
    owner->input_format = *p_format;
101
    vlc_atomic_set (&owner->restart, 0);
102
    if( aout_OutputNew( p_aout, p_format ) < 0 )
103
104
    {
        ret = -1;
105
        goto error;
106
    }
107

108
    /* Allocate a software mixer */
109
    assert (owner->volume.mixer == NULL);
110
    owner->volume.mixer = aout_MixerNew (p_aout, owner->mixer_format.i_format);
111

112
113
114
115
116
117
    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 */
118
119
120
    date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1);
    date_Set (&owner->sync.date, VLC_TS_INVALID);

121
    assert (owner->input == NULL);
122
123
124
    owner->input = aout_InputNew (p_aout, p_format, &owner->mixer_format,
                                  p_request_vout);
    if (owner->input == NULL)
125
126
127
128
    {
        struct audio_mixer *mixer = owner->volume.mixer;

        owner->volume.mixer = NULL;
129
        aout_OutputDelete (p_aout);
130
131
132
133
        aout_unlock (p_aout);
        aout_MixerDelete (mixer);
        return -1;
    }
134
135
error:
    aout_unlock( p_aout );
136
    return ret;
137
138
}

139
140
141
142
/**
 * Stops all plugins involved in the audio output.
 */
void aout_Shutdown (audio_output_t *p_aout)
143
{
144
    aout_owner_t *owner = aout_owner (p_aout);
145
    aout_input_t *input;
146
147
    struct audio_mixer *mixer;

148
    aout_lock( p_aout );
149
    /* Remove the input. */
150
    input = owner->input;
151
152
    if (likely(input != NULL))
        aout_InputDelete (p_aout, input);
153
    owner->input = NULL;
154

155
156
    mixer = owner->volume.mixer;
    owner->volume.mixer = NULL;
157
158
159
160
161

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

    aout_OutputDelete( p_aout );
162
163
    var_Destroy( p_aout, "audio-device" );
    var_Destroy( p_aout, "audio-channels" );
164

165
    aout_unlock( p_aout );
166
167

    aout_MixerDelete (mixer);
168
    free (input);
169
170
}

171
172
173
174
175
176
/**
 * Stops the decoded audio input.
 * @note Due to output recycling, this function is esssentially a stub.
 */
void aout_DecDelete (audio_output_t *aout)
{
177
#ifdef RECYCLE
178
    (void) aout;
179
180
181
#else
    aout_Shutdown (aout);
#endif
182
183
}

184
185
#define AOUT_RESTART_OUTPUT 1
#define AOUT_RESTART_INPUT  2
186
187
188
189
190
191
static void aout_CheckRestart (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

    aout_assert_locked (aout);

192
193
    int restart = vlc_atomic_swap (&owner->restart, 0);
    if (likely(restart == 0))
194
195
        return;

196
    assert (restart & AOUT_RESTART_INPUT);
197
198
199

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

200
201
    if (likely(owner->input != NULL))
        aout_InputDelete (aout, owner->input);
202
    owner->input = NULL;
203

204
205
    /* Reinitializes the output */
    if (restart & AOUT_RESTART_OUTPUT)
206
    {
207
208
209
210
211
212
213
214
        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);
215
216
    }

217
218
    owner->input = aout_InputNew (aout, &owner->input_format,
                                  &owner->mixer_format, &request_vout);
219
220
}

221
/**
222
223
 * Marks the audio output for restart, to update any parameter of the output
 * plug-in (e.g. output device or channel mapping).
224
 */
225
void aout_RequestRestart (audio_output_t *aout)
226
227
228
{
    aout_owner_t *owner = aout_owner (aout);

229
230
    /* DO NOT remove AOUT_RESTART_INPUT. You need to change the atomic ops. */
    vlc_atomic_set (&owner->restart, AOUT_RESTART_OUTPUT|AOUT_RESTART_INPUT);
231
232
233
234
235
236
237
238
}

/**
 * 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)
{
239
240
241
    aout_owner_t *owner = aout_owner (aout);

    vlc_atomic_compare_swap (&owner->restart, 0, AOUT_RESTART_INPUT);
242
243
}

244
245
246
247
248
249
250
251

/*
 * Buffer management
 */

/*****************************************************************************
 * aout_DecNewBuffer : ask for a new empty buffer
 *****************************************************************************/
252
block_t *aout_DecNewBuffer (audio_output_t *aout, size_t samples)
253
{
254
255
256
    /* NOTE: the caller is responsible for serializing input change */
    aout_owner_t *owner = aout_owner (aout);

257
258
    size_t length = samples * owner->input_format.i_bytes_per_frame
                            / owner->input_format.i_frame_length;
259
    block_t *block = block_Alloc( length );
260
261
    if( likely(block != NULL) )
    {
262
        block->i_nb_samples = samples;
263
264
265
        block->i_pts = block->i_length = 0;
    }
    return block;
266
267
268
269
270
}

/*****************************************************************************
 * aout_DecDeleteBuffer : destroy an undecoded buffer
 *****************************************************************************/
271
void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
272
{
273
    (void) aout;
274
    block_Release (block);
275
276
277
278
279
}

/*****************************************************************************
 * aout_DecPlay : filter & mix the decoded buffer
 *****************************************************************************/
280
int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate)
281
{
282
    aout_owner_t *owner = aout_owner (p_aout);
283
    aout_input_t *input;
284

285
286
    assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE &&
            i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE );
287
    assert( p_buffer->i_pts > 0 );
288

289
    p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000
290
                                / owner->input_format.i_rate;
291

292
    aout_lock( p_aout );
293
294
295
296
    aout_CheckRestart( p_aout );

    input = owner->input;
    if (unlikely(input == NULL)) /* can happen due to restart */
297
    {
298
        aout_unlock( p_aout );
299
        block_Release( p_buffer );
300
301
302
        return -1;
    }

303
    /* Input */
304
    p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate,
305
                               &owner->sync.date);
306
    if( p_buffer != NULL )
307
    {
308
309
        date_Increment (&owner->sync.date, p_buffer->i_nb_samples);

310
        /* Mixer */
311
312
        if (owner->volume.mixer != NULL)
        {
313
314
315
            float amp = 0.f;
            if (!owner->volume.mute)
                amp = owner->volume.amp
316
317
318
                      * vlc_atomic_getf (&owner->gain.multiplier);
            aout_MixerRun (owner->volume.mixer, p_buffer, amp);
        }
319
320

        /* Output */
321
322
        aout_OutputPlay( p_aout, p_buffer );
    }
323

324
    aout_unlock( p_aout );
325
326
    return 0;
}
327

328
int aout_DecGetResetLost (audio_output_t *aout)
329
{
330
331
    aout_owner_t *owner = aout_owner (aout);
    aout_input_t *input = owner->input;
332
333
    int val;

334
    aout_lock (aout);
335
336
337
338
339
340
341
    if (likely(input != NULL))
    {
        val = input->i_buffer_lost;
        input->i_buffer_lost = 0;
    }
    else
        val = 0; /* if aout_CheckRestart() failed */
342
    aout_unlock (aout);
343

344
    return val;
345
346
}

347
void aout_DecChangePause (audio_output_t *aout, bool paused, mtime_t date)
Laurent Aimar's avatar
Laurent Aimar committed
348
{
349
    aout_owner_t *owner = aout_owner (aout);
350

351
    aout_lock (aout);
352
353
    /* XXX: Should the date be offset by the pause duration instead? */
    date_Set (&owner->sync.date, VLC_TS_INVALID);
354
355
    aout_OutputPause (aout, paused, date);
    aout_unlock (aout);
Laurent Aimar's avatar
Laurent Aimar committed
356
357
}

358
void aout_DecFlush (audio_output_t *aout)
Laurent Aimar's avatar
Laurent Aimar committed
359
{
360
361
362
    aout_owner_t *owner = aout_owner (aout);

    aout_lock (aout);
363
    date_Set (&owner->sync.date, VLC_TS_INVALID);
364
365
    aout_OutputFlush (aout, false);
    aout_unlock (aout);
Laurent Aimar's avatar
Laurent Aimar committed
366
367
}

368
bool aout_DecIsEmpty (audio_output_t *aout)
369
{
370
    aout_owner_t *owner = aout_owner (aout);
371
372
    mtime_t end_date, now = mdate ();
    bool empty;
373

374
    aout_lock (aout);
375
    end_date = date_Get (&owner->sync.date);
376
377
378
379
380
381
    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);
382
    aout_unlock (aout);
383
    return empty;
384
}
385

386
387
388
389
390
391
392
393
394
395
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;
}