]> git.sesse.net Git - vlc/blob - modules/audio_output/audiotrack.c
audiotrack: set i_original_channels correctly
[vlc] / modules / audio_output / audiotrack.c
1 /*****************************************************************************
2  * audiotrack.c: Android native AudioTrack audio output module
3  *****************************************************************************
4  * Copyright © 2012 VLC authors and VideoLAN
5  *
6  * Authors: Ming Hu <tewilove@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <vlc_common.h>
28 #include <vlc_plugin.h>
29 #include <vlc_aout.h>
30
31 #include <dlfcn.h>
32 #include <assert.h>
33
34 #define SIZE_OF_AUDIOTRACK 256
35
36 /* From AudioSystem.h */
37 #define MUSIC 3
38
39 enum pcm_sub_format {
40     PCM_SUB_16_BIT          = 0x1, // must be 1 for backward compatibility
41     PCM_SUB_8_BIT           = 0x2  // must be 2 for backward compatibility
42 };
43
44 enum audio_format {
45     PCM                 = 0x00000000, // must be 0 for backward compatibility
46     PCM_16_BIT          = (PCM|PCM_SUB_16_BIT),
47     PCM_8_BIT           = (PCM|PCM_SUB_8_BIT)
48 };
49
50 enum audio_channels {
51     CHANNEL_OUT_FRONT_LEFT            = 0x4,
52     CHANNEL_OUT_FRONT_RIGHT           = 0x8,
53     CHANNEL_OUT_FRONT_CENTER          = 0x10,
54     CHANNEL_OUT_LOW_FREQUENCY         = 0x20,
55     CHANNEL_OUT_BACK_LEFT             = 0x40,
56     CHANNEL_OUT_BACK_RIGHT            = 0x80,
57     CHANNEL_OUT_FRONT_LEFT_OF_CENTER  = 0x100,
58     CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200,
59     CHANNEL_OUT_BACK_CENTER           = 0x400,
60     CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
61     CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT)
62 };
63
64 // _ZN7android11AudioSystem19getOutputFrameCountEPii
65 typedef int (*AudioSystem_getOutputFrameCount)(int *, int);
66 // _ZN7android11AudioSystem16getOutputLatencyEPji
67 typedef int (*AudioSystem_getOutputLatency)(unsigned int *, int);
68 // _ZN7android11AudioSystem21getOutputSamplingRateEPii
69 typedef int (*AudioSystem_getOutputSamplingRate)(int *, int);
70
71 // _ZN7android10AudioTrack16getMinFrameCountEPiij
72 typedef int (*AudioTrack_getMinFrameCount)(int *, int, unsigned int);
73
74 // _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii
75 typedef void (*AudioTrack_ctor)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int, int);
76 // _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i
77 typedef void (*AudioTrack_ctor_legacy)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int);
78 // _ZN7android10AudioTrackD1Ev
79 typedef void (*AudioTrack_dtor)(void *);
80 // _ZNK7android10AudioTrack9initCheckEv
81 typedef int (*AudioTrack_initCheck)(void *);
82 // _ZN7android10AudioTrack5startEv
83 typedef int (*AudioTrack_start)(void *);
84 // _ZN7android10AudioTrack4stopEv
85 typedef int (*AudioTrack_stop)(void *);
86 // _ZN7android10AudioTrack5writeEPKvj
87 typedef int (*AudioTrack_write)(void *, void  const*, unsigned int);
88 // _ZN7android10AudioTrack5flushEv
89 typedef int (*AudioTrack_flush)(void *);
90 // _ZN7android10AudioTrack5pauseEv
91 typedef int (*AudioTrack_pause)(void *);
92
93 struct aout_sys_t {
94     void *libmedia;
95     void *AudioTrack;
96
97     AudioSystem_getOutputFrameCount as_getOutputFrameCount;
98     AudioSystem_getOutputLatency as_getOutputLatency;
99     AudioSystem_getOutputSamplingRate as_getOutputSamplingRate;
100
101     AudioTrack_getMinFrameCount at_getMinFrameCount;
102     AudioTrack_ctor at_ctor;
103     AudioTrack_ctor_legacy at_ctor_legacy;
104     AudioTrack_dtor at_dtor;
105     AudioTrack_initCheck at_initCheck;
106     AudioTrack_start at_start;
107     AudioTrack_stop at_stop;
108     AudioTrack_write at_write;
109     AudioTrack_flush at_flush;
110     AudioTrack_pause at_pause;
111 };
112
113 static void *InitLibrary(struct aout_sys_t *p_sys);
114
115 static int  Open(vlc_object_t *);
116 static void Close(vlc_object_t *);
117 static void Play(audio_output_t *, block_t *);
118 static void Pause (audio_output_t *, bool, mtime_t);
119
120 vlc_module_begin ()
121     set_shortname("AudioTrack")
122     set_description(N_("Android AudioTrack audio output"))
123     set_capability("audio output", 225)
124     set_category(CAT_AUDIO)
125     set_subcategory(SUBCAT_AUDIO_AOUT)
126     add_shortcut("android")
127     set_callbacks(Open, Close)
128 vlc_module_end ()
129
130 static void *InitLibrary(struct aout_sys_t *p_sys)
131 {
132     /* DL Open libmedia */
133     void *p_library;
134     p_library = dlopen("libmedia.so", RTLD_NOW|RTLD_LOCAL);
135     if (!p_library)
136         return NULL;
137
138     /* Register symbols */
139     p_sys->as_getOutputFrameCount = (AudioSystem_getOutputFrameCount)(dlsym(p_library, "_ZN7android11AudioSystem19getOutputFrameCountEPii"));
140     p_sys->as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPji"));
141     if(p_sys->as_getOutputLatency == NULL) {
142         /* 4.1 Jellybean prototype */
143         p_sys->as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"));
144     }
145     p_sys->as_getOutputSamplingRate = (AudioSystem_getOutputSamplingRate)(dlsym(p_library, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"));
146     p_sys->at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPiij"));
147     if(p_sys->at_getMinFrameCount == NULL) {
148         /* 4.1 Jellybean prototype */
149         p_sys->at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj"));
150     }
151     p_sys->at_ctor = (AudioTrack_ctor)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii"));
152     p_sys->at_ctor_legacy = (AudioTrack_ctor_legacy)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i"));
153     p_sys->at_dtor = (AudioTrack_dtor)(dlsym(p_library, "_ZN7android10AudioTrackD1Ev"));
154     p_sys->at_initCheck = (AudioTrack_initCheck)(dlsym(p_library, "_ZNK7android10AudioTrack9initCheckEv"));
155     p_sys->at_start = (AudioTrack_start)(dlsym(p_library, "_ZN7android10AudioTrack5startEv"));
156     p_sys->at_stop = (AudioTrack_stop)(dlsym(p_library, "_ZN7android10AudioTrack4stopEv"));
157     p_sys->at_write = (AudioTrack_write)(dlsym(p_library, "_ZN7android10AudioTrack5writeEPKvj"));
158     p_sys->at_flush = (AudioTrack_flush)(dlsym(p_library, "_ZN7android10AudioTrack5flushEv"));
159     p_sys->at_pause = (AudioTrack_pause)(dlsym(p_library, "_ZN7android10AudioTrack5pauseEv"));
160
161     /* We need the first 3 or the last 1 */
162     if (!((p_sys->as_getOutputFrameCount && p_sys->as_getOutputLatency && p_sys->as_getOutputSamplingRate)
163         || p_sys->at_getMinFrameCount)) {
164         dlclose(p_library);
165         return NULL;
166     }
167
168     // We need all the other Symbols
169     if (!((p_sys->at_ctor || p_sys->at_ctor_legacy) && p_sys->at_dtor && p_sys->at_initCheck &&
170            p_sys->at_start && p_sys->at_stop && p_sys->at_write && p_sys->at_flush)) {
171         dlclose(p_library);
172         return NULL;
173     }
174     return p_library;
175 }
176
177 static int Open(vlc_object_t *p_this)
178 {
179     struct aout_sys_t *p_sys;
180     audio_output_t *p_aout = (audio_output_t*)(p_this);
181
182     int status, size;
183     int afSampleRate, afFrameCount, afLatency, minBufCount, minFrameCount;
184     int stream_type, channel, rate, format;
185
186     p_sys = (struct aout_sys_t*) malloc(sizeof(aout_sys_t));
187     if (!p_sys)
188         return VLC_ENOMEM;
189
190     p_sys->libmedia = InitLibrary(p_sys);
191     if (!p_sys->libmedia) {
192         msg_Err(p_aout, "Could not initialize libmedia.so!");
193         free(p_sys);
194         return VLC_EGENERIC;
195     }
196
197     /* 4000 <= frequency <= 48000 */
198     rate = p_aout->format.i_rate;
199     if (rate < 4000)
200         rate = 4000;
201     if (rate > 48000)
202         rate = 48000;
203
204     stream_type = MUSIC;
205
206     /* We can only accept U8 and S16L */
207     if (p_aout->format.i_format != VLC_CODEC_U8 && p_aout->format.i_format != VLC_CODEC_S16L)
208         p_aout->format.i_format = VLC_CODEC_S16L;
209     format = (p_aout->format.i_format == VLC_CODEC_S16L) ? PCM_16_BIT : PCM_8_BIT;
210
211     /* TODO: android supports more channels */
212     p_aout->format.i_original_channels = p_aout->format.i_physical_channels;
213     switch(aout_FormatNbChannels(&p_aout->format))
214     {
215     case 1:
216         channel = CHANNEL_OUT_MONO;
217         p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
218         break;
219     case 2:
220     default:
221         channel = CHANNEL_OUT_STEREO;
222         p_aout->format.i_physical_channels = AOUT_CHANS_STEREO;
223         break;
224     }
225
226     /* Get the minimum buffer value */
227     if (!p_sys->at_getMinFrameCount) {
228         status = p_sys->as_getOutputSamplingRate(&afSampleRate, stream_type);
229         status ^= p_sys->as_getOutputFrameCount(&afFrameCount, stream_type);
230         status ^= p_sys->as_getOutputLatency((uint32_t*)(&afLatency), stream_type);
231         if (status != 0) {
232             msg_Err(p_aout, "Could not query the AudioStream parameters");
233             dlclose(p_sys->libmedia);
234             free(p_sys);
235             return VLC_EGENERIC;
236         }
237         minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
238         if (minBufCount < 2)
239             minBufCount = 2;
240         minFrameCount = (afFrameCount * rate * minBufCount) / afSampleRate;
241     }
242     else {
243         status = p_sys->at_getMinFrameCount(&minFrameCount, stream_type, rate);
244         if (status != 0) {
245             msg_Err(p_aout, "Could not query the AudioTrack parameters");
246             dlclose(p_sys->libmedia);
247             free(p_sys);
248             return VLC_EGENERIC;
249         }
250     }
251
252     size = minFrameCount * (channel == CHANNEL_OUT_STEREO ? 2 : 1) * 4;
253
254     /* Sizeof(AudioTrack) == 0x58 (not sure) on 2.2.1, this should be enough */
255     p_sys->AudioTrack = malloc(SIZE_OF_AUDIOTRACK);
256     if (!p_sys->AudioTrack) {
257         dlclose(p_sys->libmedia);
258         free(p_sys);
259         return VLC_ENOMEM;
260     }
261
262     *((uint32_t *) ((uint32_t)p_sys->AudioTrack + SIZE_OF_AUDIOTRACK - 4)) = 0xbaadbaad;
263     // Higher than android 2.2
264     if (p_sys->at_ctor)
265         p_sys->at_ctor(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0, 0);
266     // Higher than android 1.6
267     else if (p_sys->at_ctor_legacy)
268         p_sys->at_ctor_legacy(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0);
269
270     assert( (*((uint32_t *) ((uint32_t)p_sys->AudioTrack + SIZE_OF_AUDIOTRACK - 4)) == 0xbaadbaad) );
271
272     /* And Init */
273     status = p_sys->at_initCheck(p_sys->AudioTrack);
274
275     /* android 1.6 uses channel count instead of stream_type */
276     if (status != 0) {
277         channel = (channel == CHANNEL_OUT_STEREO) ? 2 : 1;
278         p_sys->at_ctor_legacy(p_sys->AudioTrack, stream_type, rate, format, channel, size, 0, NULL, NULL, 0);
279         status = p_sys->at_initCheck(p_sys->AudioTrack);
280     }
281     if (status != 0) {
282         msg_Err(p_aout, "Cannot create AudioTrack!");
283         dlclose(p_sys->libmedia);
284         free(p_sys->AudioTrack);
285         free(p_sys);
286         return VLC_EGENERIC;
287     }
288
289     p_aout->sys = p_sys;
290     p_aout->pf_play = Play;
291     p_aout->pf_pause = Pause;
292
293     p_sys->at_start(p_sys->AudioTrack);
294
295     p_aout->format.i_rate = rate;
296
297     return VLC_SUCCESS;
298 }
299
300 static void Close(vlc_object_t *p_this)
301 {
302     audio_output_t *p_aout = (audio_output_t*)p_this;
303     aout_sys_t *p_sys = p_aout->sys;
304
305     p_sys->at_stop(p_sys->AudioTrack);
306     p_sys->at_flush(p_sys->AudioTrack);
307     p_sys->at_dtor(p_sys->AudioTrack);
308     free(p_sys->AudioTrack);
309     dlclose(p_sys->libmedia);
310     free(p_sys);
311 }
312
313 /* FIXME: lipsync */
314 static void Play(audio_output_t *p_aout, block_t *p_buffer)
315 {
316     aout_sys_t *p_sys = p_aout->sys;
317
318     size_t length = 0;
319     while (length < p_buffer->i_buffer) {
320         length += p_sys->at_write(p_sys->AudioTrack, (char*)(p_buffer->p_buffer) + length, p_buffer->i_buffer - length);
321     }
322
323     block_Release( p_buffer );
324 }
325
326 static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
327 {
328     VLC_UNUSED(date);
329
330     aout_sys_t *p_sys = p_aout->sys;
331
332     if (pause) {
333         p_sys->at_pause(p_sys->AudioTrack);
334     } else {
335         p_sys->at_start(p_sys->AudioTrack);
336     }
337 }