From: Ming Hu Date: Sun, 13 May 2012 20:20:34 +0000 (+0200) Subject: Android: add native AudioTrack aout module X-Git-Tag: 2.1.0-git~2667 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=cd11cbfeadc32261db99df7a671e616ea56f9bcf;p=vlc Android: add native AudioTrack aout module Signed-off-by: Jean-Baptiste Kempf Signed-off-by: Rafaël Carré --- diff --git a/modules/audio_output/Modules.am b/modules/audio_output/Modules.am index a0936f14f2..7d6f718686 100644 --- a/modules/audio_output/Modules.am +++ b/modules/audio_output/Modules.am @@ -3,6 +3,13 @@ SOURCES_auhal = auhal.c packet.c SOURCES_audioqueue = audioqueue.c packet.c SOURCES_opensles_android = opensles_android.c +libandroid_audiotrack_plugin_la_SOURCES = android_audiotrack.c +libandroid_audiotrack_plugin_la_CFLAGS = $(AM_CFLAGS) +libandroid_audiotrack_plugin_la_LIBADD = $(AM_LIBADD) -ldl +if HAVE_ANDROID +libvlc_LTLIBRARIES += libandroid_audiotrack_plugin.la +endif + libadummy_plugin_la_SOURCES = adummy.c libadummy_plugin_la_CFLAGS = $(AM_CFLAGS) libadummy_plugin_la_LIBADD = $(AM_LIBADD) diff --git a/modules/audio_output/audiotrack.c b/modules/audio_output/audiotrack.c new file mode 100644 index 0000000000..183b5c4c55 --- /dev/null +++ b/modules/audio_output/audiotrack.c @@ -0,0 +1,335 @@ +/***************************************************************************** + * audiotrack.c: Android native AudioTrack audio output module + ***************************************************************************** + * Copyright © 2012 VLC authors and VideoLAN + * + * Authors: Ming Hu + * + * 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 +#include +#include + +#include +#include + +#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; + p_aout->format.i_physical_channels = AOUT_CHAN_STEREO; + 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); + } +}