Commit cd11cbfe authored by Ming Hu's avatar Ming Hu Committed by Rafaël Carré

Android: add native AudioTrack aout module

Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
Signed-off-by: default avatarRafaël Carré <funman@videolan.org>
parent 04a376f9
......@@ -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)
......
/*****************************************************************************
* 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;
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);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment