audiotrack.c 12.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*****************************************************************************
 * audiotrack.c: Android native AudioTrack audio output module
 *****************************************************************************
 * Copyright © 2012 VLC authors and VideoLAN
 *
 * Authors: Ming Hu <tewilove@gmail.com>
 *
 * 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
 * (at your option) any later version.
 *
 * 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 Lesser General Public License for more details.
 *
 * 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.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_aout.h>

#include <dlfcn.h>
#include <assert.h>

#define SIZE_OF_AUDIOTRACK 256

/* From AudioSystem.h */
#define MUSIC 3

enum pcm_sub_format {
    PCM_SUB_16_BIT          = 0x1, // must be 1 for backward compatibility
    PCM_SUB_8_BIT           = 0x2  // must be 2 for backward compatibility
};

enum audio_format {
    PCM                 = 0x00000000, // must be 0 for backward compatibility
    PCM_16_BIT          = (PCM|PCM_SUB_16_BIT),
    PCM_8_BIT           = (PCM|PCM_SUB_8_BIT)
};

enum audio_channels {
    CHANNEL_OUT_FRONT_LEFT            = 0x4,
    CHANNEL_OUT_FRONT_RIGHT           = 0x8,
    CHANNEL_OUT_FRONT_CENTER          = 0x10,
    CHANNEL_OUT_LOW_FREQUENCY         = 0x20,
    CHANNEL_OUT_BACK_LEFT             = 0x40,
    CHANNEL_OUT_BACK_RIGHT            = 0x80,
    CHANNEL_OUT_FRONT_LEFT_OF_CENTER  = 0x100,
    CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200,
    CHANNEL_OUT_BACK_CENTER           = 0x400,
    CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
    CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT)
};

// _ZN7android11AudioSystem19getOutputFrameCountEPii
typedef int (*AudioSystem_getOutputFrameCount)(int *, int);
// _ZN7android11AudioSystem16getOutputLatencyEPji
typedef int (*AudioSystem_getOutputLatency)(unsigned int *, int);
// _ZN7android11AudioSystem21getOutputSamplingRateEPii
typedef int (*AudioSystem_getOutputSamplingRate)(int *, int);

// _ZN7android10AudioTrack16getMinFrameCountEPiij
typedef int (*AudioTrack_getMinFrameCount)(int *, int, unsigned int);

// _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii
typedef void (*AudioTrack_ctor)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int, int);
// _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i
typedef void (*AudioTrack_ctor_legacy)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int);
// _ZN7android10AudioTrackD1Ev
typedef void (*AudioTrack_dtor)(void *);
// _ZNK7android10AudioTrack9initCheckEv
typedef int (*AudioTrack_initCheck)(void *);
// _ZN7android10AudioTrack5startEv
typedef int (*AudioTrack_start)(void *);
// _ZN7android10AudioTrack4stopEv
typedef int (*AudioTrack_stop)(void *);
// _ZN7android10AudioTrack5writeEPKvj
typedef int (*AudioTrack_write)(void *, void  const*, unsigned int);
// _ZN7android10AudioTrack5flushEv
typedef int (*AudioTrack_flush)(void *);
// _ZN7android10AudioTrack5pauseEv
typedef int (*AudioTrack_pause)(void *);

struct aout_sys_t {
    void *libmedia;
    void *AudioTrack;

    AudioSystem_getOutputFrameCount as_getOutputFrameCount;
    AudioSystem_getOutputLatency as_getOutputLatency;
    AudioSystem_getOutputSamplingRate as_getOutputSamplingRate;

    AudioTrack_getMinFrameCount at_getMinFrameCount;
    AudioTrack_ctor at_ctor;
    AudioTrack_ctor_legacy at_ctor_legacy;
    AudioTrack_dtor at_dtor;
    AudioTrack_initCheck at_initCheck;
    AudioTrack_start at_start;
    AudioTrack_stop at_stop;
    AudioTrack_write at_write;
    AudioTrack_flush at_flush;
    AudioTrack_pause at_pause;
};

static void *InitLibrary(struct aout_sys_t *p_sys);

static int  Open(vlc_object_t *);
static void Close(vlc_object_t *);
static void Play(audio_output_t *, block_t *);
static void Pause (audio_output_t *, bool, mtime_t);

vlc_module_begin ()
    set_shortname("AudioTrack")
    set_description(N_("Android AudioTrack audio output"))
    set_capability("audio output", 225)
    set_category(CAT_AUDIO)
    set_subcategory(SUBCAT_AUDIO_AOUT)
    add_shortcut("android")
    set_callbacks(Open, Close)
vlc_module_end ()

static void *InitLibrary(struct aout_sys_t *p_sys)
{
    /* DL Open libmedia */
    void *p_library;
    p_library = dlopen("libmedia.so", RTLD_NOW|RTLD_LOCAL);
    if (!p_library)
        return NULL;

    /* Register symbols */
    p_sys->as_getOutputFrameCount = (AudioSystem_getOutputFrameCount)(dlsym(p_library, "_ZN7android11AudioSystem19getOutputFrameCountEPii"));
    p_sys->as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPji"));
    if(p_sys->as_getOutputLatency == NULL) {
        /* 4.1 Jellybean prototype */
        p_sys->as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"));
    }
    p_sys->as_getOutputSamplingRate = (AudioSystem_getOutputSamplingRate)(dlsym(p_library, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"));
    p_sys->at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPiij"));
    if(p_sys->at_getMinFrameCount == NULL) {
        /* 4.1 Jellybean prototype */
        p_sys->at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj"));
    }
    p_sys->at_ctor = (AudioTrack_ctor)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii"));
    p_sys->at_ctor_legacy = (AudioTrack_ctor_legacy)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i"));
    p_sys->at_dtor = (AudioTrack_dtor)(dlsym(p_library, "_ZN7android10AudioTrackD1Ev"));
    p_sys->at_initCheck = (AudioTrack_initCheck)(dlsym(p_library, "_ZNK7android10AudioTrack9initCheckEv"));
    p_sys->at_start = (AudioTrack_start)(dlsym(p_library, "_ZN7android10AudioTrack5startEv"));
    p_sys->at_stop = (AudioTrack_stop)(dlsym(p_library, "_ZN7android10AudioTrack4stopEv"));
    p_sys->at_write = (AudioTrack_write)(dlsym(p_library, "_ZN7android10AudioTrack5writeEPKvj"));
    p_sys->at_flush = (AudioTrack_flush)(dlsym(p_library, "_ZN7android10AudioTrack5flushEv"));
    p_sys->at_pause = (AudioTrack_pause)(dlsym(p_library, "_ZN7android10AudioTrack5pauseEv"));

    /* We need the first 3 or the last 1 */
    if (!((p_sys->as_getOutputFrameCount && p_sys->as_getOutputLatency && p_sys->as_getOutputSamplingRate)
        || p_sys->at_getMinFrameCount)) {
        dlclose(p_library);
        return NULL;
    }

    // We need all the other Symbols
    if (!((p_sys->at_ctor || p_sys->at_ctor_legacy) && p_sys->at_dtor && p_sys->at_initCheck &&
           p_sys->at_start && p_sys->at_stop && p_sys->at_write && p_sys->at_flush)) {
        dlclose(p_library);
        return NULL;
    }
    return p_library;
}

static int Open(vlc_object_t *p_this)
{
    struct aout_sys_t *p_sys;
    audio_output_t *p_aout = (audio_output_t*)(p_this);

    int status, size;
    int afSampleRate, afFrameCount, afLatency, minBufCount, minFrameCount;
    int stream_type, channel, rate, format;

    p_sys = (struct aout_sys_t*) malloc(sizeof(aout_sys_t));
    if (!p_sys)
        return VLC_ENOMEM;

    p_sys->libmedia = InitLibrary(p_sys);
    if (!p_sys->libmedia) {
        msg_Err(p_aout, "Could not initialize libmedia.so!");
        free(p_sys);
        return VLC_EGENERIC;
    }

    /* 4000 <= frequency <= 48000 */
    rate = p_aout->format.i_rate;
    if (rate < 4000)
        rate = 4000;
    if (rate > 48000)
        rate = 48000;

    stream_type = MUSIC;

    /* We can only accept U8 and S16L */
    if (p_aout->format.i_format != VLC_CODEC_U8 && p_aout->format.i_format != VLC_CODEC_S16L)
        p_aout->format.i_format = VLC_CODEC_S16L;
    format = (p_aout->format.i_format == VLC_CODEC_S16L) ? PCM_16_BIT : PCM_8_BIT;

    /* TODO: android supports more channels */
    p_aout->format.i_original_channels = aout_FormatNbChannels(&p_aout->format);
    switch(p_aout->format.i_original_channels)
    {
    case 1:
        channel = CHANNEL_OUT_MONO;
        p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
        break;
    case 2:
    default:
        channel = CHANNEL_OUT_STEREO;
222
        p_aout->format.i_physical_channels = AOUT_CHANS_STEREO;
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
        break;
    }

    /* Get the minimum buffer value */
    if (!p_sys->at_getMinFrameCount) {
        status = p_sys->as_getOutputSamplingRate(&afSampleRate, stream_type);
        status ^= p_sys->as_getOutputFrameCount(&afFrameCount, stream_type);
        status ^= p_sys->as_getOutputLatency((uint32_t*)(&afLatency), stream_type);
        if (status != 0) {
            msg_Err(p_aout, "Could not query the AudioStream parameters");
            dlclose(p_sys->libmedia);
            free(p_sys);
            return VLC_EGENERIC;
        }
        minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
        if (minBufCount < 2)
            minBufCount = 2;
        minFrameCount = (afFrameCount * rate * minBufCount) / afSampleRate;
    }
    else {
        status = p_sys->at_getMinFrameCount(&minFrameCount, stream_type, rate);
        if (status != 0) {
            msg_Err(p_aout, "Could not query the AudioTrack parameters");
            dlclose(p_sys->libmedia);
            free(p_sys);
            return VLC_EGENERIC;
        }
    }

    size = minFrameCount * (channel == CHANNEL_OUT_STEREO ? 2 : 1) * 4;

    /* Sizeof(AudioTrack) == 0x58 (not sure) on 2.2.1, this should be enough */
    p_sys->AudioTrack = malloc(SIZE_OF_AUDIOTRACK);
    if (!p_sys->AudioTrack) {
        dlclose(p_sys->libmedia);
        free(p_sys);
        return VLC_ENOMEM;
    }

    *((uint32_t *) ((uint32_t)p_sys->AudioTrack + SIZE_OF_AUDIOTRACK - 4)) = 0xbaadbaad;
    // Higher than android 2.2
    if (p_sys->at_ctor)
        p_sys->at_ctor(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0, 0);
    // Higher than android 1.6
    else if (p_sys->at_ctor_legacy)
        p_sys->at_ctor_legacy(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0);

    assert( (*((uint32_t *) ((uint32_t)p_sys->AudioTrack + SIZE_OF_AUDIOTRACK - 4)) == 0xbaadbaad) );

    /* And Init */
    status = p_sys->at_initCheck(p_sys->AudioTrack);

    /* android 1.6 uses channel count instead of stream_type */
    if (status != 0) {
        channel = (channel == CHANNEL_OUT_STEREO) ? 2 : 1;
        p_sys->at_ctor_legacy(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0);
        status = p_sys->at_initCheck(p_sys->AudioTrack);
    }
    if (status != 0) {
        msg_Err(p_aout, "Cannot create AudioTrack!");
        dlclose(p_sys->libmedia);
        free(p_sys->AudioTrack);
        free(p_sys);
        return VLC_EGENERIC;
    }

    p_aout->sys = p_sys;
    p_aout->pf_play = Play;
    p_aout->pf_pause = Pause;

    p_sys->at_start(p_sys->AudioTrack);

    p_aout->format.i_rate = rate;

    return VLC_SUCCESS;
}

static void Close(vlc_object_t *p_this)
{
    audio_output_t *p_aout = (audio_output_t*)p_this;
    aout_sys_t *p_sys = p_aout->sys;

    p_sys->at_stop(p_sys->AudioTrack);
    p_sys->at_flush(p_sys->AudioTrack);
    p_sys->at_dtor(p_sys->AudioTrack);
    free(p_sys->AudioTrack);
    dlclose(p_sys->libmedia);
    free(p_sys);
}

/* FIXME: lipsync */
static void Play(audio_output_t *p_aout, block_t *p_buffer)
{
    aout_sys_t *p_sys = p_aout->sys;

    size_t length = 0;
    while (length < p_buffer->i_buffer) {
        length += p_sys->at_write(p_sys->AudioTrack, (char*)(p_buffer->p_buffer) + length, p_buffer->i_buffer - length);
    }

    block_Release( p_buffer );
}

static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
{
    aout_sys_t *p_sys = p_aout->sys;

    if (pause) {
        p_sys->at_pause(p_sys->AudioTrack);
    } else {
        p_sys->at_start(p_sys->AudioTrack);
    }
}