androidaudio架构
㈠ android.media.AsyncPlayer这个类应该怎么用
代码结构:
Open Core 的代码在Android 代码的 External/Opencore 目录中 。这个目录是OpenCore
的根目录,其中包含的子目录如下所示 :
android :这里面是一个上层的库,它实现了一个为Android 使用的音视频采集,播放的接口,和DRM 数字版权管理的接口实现。
baselibs :包含数据结构和线程安全等内容的底层库
codecs_v2 :音视频的编解码器,基于 OpenMAX 实现
engines :核心部分 ,多媒体 引擎的实现
extern_libs_v2 :包含了 khronos 的 OpenMAX 的头文件
fileformats :文件格式的解析( parser )工具
nodes :提供一些PVMF 的NODE ,主要是编解码和文件解析方面的。
oscl :操作系统兼容库
pvmi : 输入输出控制的抽象接口
protocols :主要是与网络相关的 RTSP 、 RTP 、 HTTP 等协议 的相关内容
pvcommon : pvcommon 库文件的 Android.mk 文件,没有源文件。
pvplayer : pvplayer 库文件的 Android.mk 文件,没有源文件。
pvauthor : pvauthor 库文件的 Android.mk 文件,没有源文件。
tools_v2 :编译工具以及一些可注册的模块。
本文主要介绍Android MediaPlayer的架构,主要由OpenCore 里的PV Player来实现的。
1.概述
Android的MediaPlayer包含了Audio和Video的播放功能,Music和Video两个应用程序都是调用MediaPlayer实现的。
代码主要分布在以下的目录中:
java程序的路径:
packages/apps/Music/src/com/android/music/
JAVA类的路径:
frameworks/base/media/java/android/media/MediaPlayer.java
JAVA本地调用部分(JNI):
frameworks/base/media/jni/android_media_MediaPlayer.cpp
编译为 libmedia_jni.so
头文件:
frameworks/base/include/media/
多媒体库:
frameworks/base/media/libmedia/
编译为 libmedia.so
多媒体服务:
frameworks/base/media/libmediaplayerservice/
编译为 libmediaplayerservice.so
具体实现:
external/opencore/
编译为 libopencoreplayer.so
libopencoreplayer.so是主要的实现部分,其他的库基本上都是在其上建立的封装和为建立进程间通讯的机制。
2.框架
运行的时候,大致可以分成Client和Server两个部分,分别在两个进程中运行,使用Binder机制实现IPC通讯。从框架结构上来看,IMediaPlayerService.h、IMediaPlayerClient.h和MediaPlayer.h三个类定义了MeidaPlayer的接口和架构,MediaPlayerService.cpp和mediaplayer.cpp两个文件用于MeidaPlayer架构的实现,MeidaPlayer的具体功能在PVPlayer(库libopencoreplayer.so)中的实现。
2.1 IMediaPlayerClient.h
描述一个MediaPlayer客户端的接口
class IMediaPlayerClient: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerClient);
virtual void notify(int msg, int ext1, int ext2) = 0;
};
class BnMediaPlayerClient: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在定义中,IMediaPlayerClient类继承IInterface,并定义了一个MediaPlayer客户端的接口,BnMediaPlayerClient继承了BnInterface,这是为基于Android的基础类Binder机制实现在进程通讯而构建的。事实上,根据BnInterface类模版的定义,BnInterface类相当于双继承了BnInterface和ImediaPlayerClient,这是Android一种常用的定义方式。
2.2 mediaplayer.h
对外的接口类,它最主要是定义了一个MediaPlayer类:
class MediaPlayer : public BnMediaPlayerClient
{
public:
MediaPlayer();
~MediaPlayer();
void onFirstRef();
void disconnect();
status_t setDataSource(const char *url);
status_t setDataSource(int fd, int64_t offset, int64_t length);
status_t setVideoSurface(const sp& surface);
status_t setListener(const sp& listener);
status_t prepare();
status_t prepareAsync();
status_t start();
status_t stop();
status_t pause();
bool isPlaying();
status_t getVideoWidth(int *w);
status_t getVideoHeight(int *h);
status_t seekTo(int msec);
status_t getCurrentPosition(int *msec);
status_t getDuration(int *msec);
status_t reset();
status_t setAudioStreamType(int type);
status_t setLooping(int loop);
status_t setVolume(float leftVolume, float rightVolume);
void notify(int msg, int ext1, int ext2);
static sp decode(const char* url, uint32_t *pSampleRate, int*
pNumChannels);
static sp decode(int fd, int64_t offset, int64_t length, uint32_t
*pSampleRate, int* pNumChannels);
//……
}
从接口中可以看出MediaPlayer类刚好实现了一个MediaPlayer的基本操作,例如播放(start)、停止(stop)、暂停(pause)等。
另外的一个类DeathNotifier在MediaPlayer类中定义,它继承了IBinder类中的DeathRecipient类:
class DeathNotifier: public IBinder:: DeathRecipient
{
public:
DeathNotifier() {}
virtual ~DeathNotifier();
virtual void binderDied(const wp& who);
};
事实上,MediaPlayer类正是间接地继承了IBinder,而MediaPlayer:: DeathNotifier类继承了IBinder::
DeathRecipient,这都是为了实现进程间通讯而构建的。
2.3 IMediaPlayer.h
主要的的内容是一个实现MediaPlayer功能的接口:
class IMediaPlayer: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayer);
virtual void disconnect() = 0;
virtual status_t setVideoSurface(const sp& surface) = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
virtual status_t pause() = 0;
virtual status_t isPlaying(bool* state) = 0;
virtual status_t getVideoSize(int* w, int* h) = 0;
virtual status_t seekTo(int msec) = 0;
virtual status_t getCurrentPosition(int* msec) = 0;
virtual status_t getDuration(int* msec) = 0;
virtual status_t reset() = 0;
virtual status_t setAudioStreamType(int type) = 0;
virtual status_t setLooping(int loop) = 0;
virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
};
class BnMediaPlayer: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在IMediaPlayer类中,主要定义MediaPlayer的功能接口,这个类必须被继承才能够使用。值得注意的是,这些接口和MediaPlayer类的接口有些类似,但是它们并没有直接的关系。事实上,在MediaPlayer类的各种实现中,一般都会通过调用IMediaPlayer类的实现类来完成。
2.4 头文件IMediaPlayerService.h
描述一个MediaPlayer的服务,定义方式如下所示:
class IMediaPlayerService: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerService);
virtual sp create(pid_t pid, const
sp& client, const char* url) = 0;
virtual sp create(pid_t pid, const
sp& client, int fd, int64_t offset, int64_t length) =
0;
virtual sp decode(const char* url, uint32_t *pSampleRate, int*
pNumChannels) = 0;
virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t
*pSampleRate, int* pNumChannels) = 0;
};
class BnMediaPlayerService: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
由于有纯虚函数,IMediaPlayerService
以及BnMediaPlayerService必须被继承实现才能够使用,在IMediaPlayerService定义的create和decode等接口,事实上是必须被继承者实现的内容。注意,create的返回值的类型是sp,这个IMediaPlayer正是提供实现功能的接口。
3 实现
3.1 App
在packages/apps/Music/src/com/android/music/里的MediaPlaybackService.java文件中,包含了对MediaPlayer的调用。
在MediaPlaybackService.java中包含对包的引用:
import android.media.MediaPlayer;
在MediaPlaybackService类的内部,定义了MultiPlayer类:
private class MultiPlayer {
private MediaPlayer mMediaPlayer = new MediaPlayer();
}
MultiPlayer类中使用了MediaPlayer类,其中有一些对这个MediaPlayer的调用,调用的过程如下所示:
mMediaPlayer.reset();
mMediaPlayer.setDataSource(path);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
reset,setDataSource和setAudioStreamType等接口就是通过JAVA本地调用(JNI)来实现的。
3.2 Jni
在frameworks/base/media/jni/android_media_MediaPlayer.cpp中实现,其中android_media_MediaPlayer_reset函数的实现如下所示:
static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
{
sp mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
}
先获取一个MediaPlayer指针,通过对它的调用来实现实际的功能。
register_android_media_MediaPlayer用于将gMethods注册为的类"android/media/MediaPlayer",其实现如下所示。
static int register_android_media_MediaPlayer(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
// ......
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
"android/media/MediaPlayer"对应JAVA的类android.media.MediaPlayer。
3.3 libmedia.so
frameworks/base/media/libmedia/mediaplayer.cpp文件实现mediaplayer.h提供的接口,其中一个重要的片段如下所示:
const sp& MediaPlayer::getMediaPlayerService()
{
Mutex::Autolock _l(mServiceLock);
if (mMediaPlayerService.get() == 0) {
sp sm = defaultServiceManager();
sp binder;
do {
binder = sm->getService(String16("media.player"));
if (binder != 0)
break;
LOGW("MediaPlayerService not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
if (mDeathNotifier == NULL) {
mDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(mDeathNotifier);
mMediaPlayerService = interface_cast(binder);
}
LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");
return mMediaPlayerService;
}
其中最重要的一点是binder =
sm->getService(String16("media.player"));这个调用用来得到一个名称为"media.player"的服务,这个调用返回值的类型为IBinder,根据实现将其转换成类型IMediaPlayerService使用。
一个具体的函数setDataSource如下所示:
status_t MediaPlayer::setDataSource(const char *url)
{
LOGV("setDataSource(%s)", url);
status_t err = UNKNOWN_ERROR;
if (url != NULL) {
const sp& service(getMediaPlayerService());
if (service != 0) {
sp player(service->create(getpid(), this, url));
err = setDataSource(player);
}
}
return err;
}
㈡ 有没有用过android sdk里面的AudioEffect设置音效的
在Android2.3中增加了对音频混响的支持,这些API包含在android.media.audiofx包中。
一、概述
AudioEffect是android audio framework(android 音频框架)提供的音频效果控制的基类。开发者不能直接使用此类,应该使用它的派生类。下面列出它的派生类。
Equalizer
Virtualizer
BassBoost
PresetReverb
EnvironmentalReverb
当创建AudioEffect时,如果音频效果应用到一个具体的AudioTrack和MediaPlayer的实例,应用程序必须指定该实例的音频session ID,如果要应用Global音频输出混响的效果必须制定Session 0。
要创建音频输出混响(音频 Session 0)要求要有 MODIFY_AUDIO_SETTINGS权限。
如果要创建的效果在audio framework不存在,那么直接创建该效果,如果已经存在那么直接使用此效果。如果优先级高的对象要在低级别的对象使用该效果时,那么控制将转移到优先级高的对象上,否则继续停留在此对象上。在这种情况下,新的申请将被监听器通知。
㈢ android audio 怎么切换到dmic
android audio 切换到dmic 的办法
tinyalsa访问设备节点应该是/dev/snd/pcmCxDxp, /dev/snd/pcmCxDxc和/dev/snd/controlCx,画图时没有确认造成笔误这个框图大致把soundrecorder从app到framework到HAL的类图画了出来
2.有些子类父类继承关系没有表示出来,从soundrecorder到AudioRecord基本就是一条线下来
3.我也没有详细去看代码,stagefright这一块很庞大,实现了多种mime格式的编解码
音频数据生成文件保存在sd卡中应该是在MPEG4Writer这里完成的,这个没有细看
我们重点看下AudioSystem,AudioPolicyService,AudioFlinger和AudioHardware这块。
4.以open_record()和get_input()这两个方法为例我们看下这几个类之间的调用关系
从AudioRecord.cpp开始,文件位置:frameworksasemedialibmediaAudioRecord.cpp
㈣ Android Audio简述
audio是Android系统是比较重要的一个模块,本人也涉足时间不是很长,经验还是很少的,只是把自己在工作中所遇到的问题记录。
Audio 即音频, 也就是控制着手机中的各种声音的输出,比如说,音乐的播放,音量大小,按键音,插入耳机,声音从耳机播放,连接蓝牙,声音从蓝牙耳机中播放。还有一些HiFi播放, offload播放,高清播放。
常见的问题如下:
POP音,漏音,声音卡顿,耳机无法识别,还有一些音频通路等问题,还有一些稳定性的问题如:ANR、CRASH、tombstone。还有一些安全漏洞的问题(主要是核心库那里)。
回归正题
-------华丽的分割线-------
audio分为 应用层,fwk层,native fwk, hal层。
常见的文件有MediaPlayer.java、 AudioSystem.java、 AudioService.java、AudioManager.java 文件路径(framework/base/media)
AudioFlinger.cpp、AudioTrack.cpp、 AudioPolicyManager.cpp、 Threads.cpp、Tracks.cpp、 Engine.cpp、 Audiosystem.cpp 文件路径(framework/av)
audio_hw.c 文件路径(vendor/)
java文件我在这里就不过多的说了,没啥好讲的,主要说一下c++文件吧。
AudioFlinger 是音频策略的执行者, AudioPolicyManager是制作音频策略的,AudioTrack是负责播放从上层传过来的PCM数据,简单的说就是负责播放的。
audio_hw 是每个HAL层的文件,每个手机厂商自己定制的文件。当然Google也有。
一般呢,我们处理音频相关的问题呢,有一些特定的套路,需要AP 侧的log, 有时还需要kernel 的log, 当然最主要的是需要音频数据。也就是出现问题时,的声音数据,让我们可以快速的定位出现问题的位置。
简单的说下播放一首歌曲的流程:(以Android O 为例)
上层创建一个MediaPlayer对象,然后调用NuPlayer框架(播放器),NuPlayer先将当前歌曲的文件信息读取,采样率,比特率,等之类的东西。然后开始调用audio decoder (音频解码器) 将解码出来的PCM数据传给AudioTrack, Audiotrack 会创建一个Track,(每一个播放都会创建一个属于自己的track,不用了就销毁,最多可以同时创建32个),经过AudioFlinger重采样之后,送到HAL层,HAL层在经过一些混音,降噪之类的处理,将声音送到Codec,然后送给硬件输出,进行播放。
这个是一个大概的播放流程,如果我们在播放过程中遇到了一些问题,比如说是fwk层的问题,我们就在AudioTrack与AudioFlinger之间寻问题的原因。
比如说,播放无声,我们需要看AudioPolicyManager中的一些策略是不是将当前的track给mute了。或者是一些其他的原因等。
这里只是简单的介绍下Audio,Audio算是一个较为复杂的模块,还需要好好的研究。
㈤ Android Audio System 之一:AudioTrack如何与AudioFlinger交换
引子Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中 进行播放,目前Android的Froyo版本设定了同时最多可以创建32个音频流,也就是说,Mixer最多会同时处理32个AudioTrack的数 据流。如何使用AudioTrackAudioTrack的主要代码位于 frameworks/base/media/libmedia/audiotrack.cpp中。现在先通过一个例子来了解一下如何使用 AudioTrack,ToneGenerator是android中产生电话拨号音和其他音调波形的一个实现,我们就以它为例子:ToneGenerator的初始化函数:bool ToneGenerator::initAudioTrack() { // Open audio track in mono, PCM 16bit//, default sampling rate, default buffer size mpAudioTrack = new AudioTrack(); mpAudioTrack->set(mStreamType, 0, AudioSystem::PCM_16_BIT, AudioSystem::CHANNEL_OUT_MONO, 0, 0, audioCallback, this, 0, 0, mThreadCanCallJava); if (mpAudioTrack->initCheck() != NO_ERROR) { LOGE("AudioTrack->initCheck failed"); goto initAudioTrack_exit; } mpAudioTrack->setVolume(mVolume, mVolume); mState = TONE_INIT; ...... } 可见,创建步骤很简单,先new一个AudioTrack的实例,然后调用set成员函数完成参数的设置并注册到AudioFlinger中,然后可以调 用其他诸如设置音量等函数进一步设置音频参数。其中,一个重要的参数是audioCallback,audioCallback是一个回调函数,负责响应 AudioTrack的通知,例如填充数据、循环播放、播放位置触发等等。回调函数的写法通常像这样:void ToneGenerator::audioCallback(int event, void* user, void *info) { if (event != AudioTrack::EVENT_MORE_DATA) return; AudioTrack::Buffer *buffer = static_cast<AudioTrack::Buffer *>(info); ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user); short *lpOut = buffer->i16; unsigned int lNumSmp = buffer->size/sizeof(short); const ToneDescriptor *lpToneDesc = lpToneGen->mpToneDesc; if (buffer->size == 0) return; // Clear output buffer: WaveGenerator accumulates into lpOut buffer memset(lpOut, 0, buffer->size); ...... // 以下是产生音调数据的代码,略.... } 该函数首先判断事件的类型是否是EVENT_MORE_DATA,如果是,则后续的代码会填充相应的音频数据后返回,当然你可以处理其他事件,以下是可用的事件类型:enum event_type { EVENT_MORE_DATA = 0,// Request to write more data to PCM buffer. EVENT_UNDERRUN = 1,// PCM buffer underrun occured. EVENT_LOOP_END = 2,// Sample loop end was reached; playback restarted from loop start if loop count was not 0. EVENT_MARKER = 3,// Playback head is at the specified marker position (See setMarkerPosition()). EVENT_NEW_POS = 4,// Playback head is at a new position (See setPositionUpdatePeriod()). EVENT_BUFFER_END = 5// Playback head is at the end of the buffer. }; 开始播放:mpAudioTrack->start(); 停止播放:mpAudioTrack->stop(); 只要简单地调用成员函数start()和stop()即可。AudioTrack和AudioFlinger的通信机制通常,AudioTrack和AudioFlinger并不在同一个进程中,它们通过android中的binder机制建立联系。AudioFlinger是android中的一个service,在android启动时就已经被加载。下面这张图展示了他们两个的关系:图一AudioTrack和AudioFlinger的关系我们可以这样理解这张图的含义:audio_track_cblk_t实现了一个环形FIFO;AudioTrack是FIFO的数据生产者;AudioFlinger是FIFO的数据消费者。建立联系的过程下面的序列图展示了AudioTrack和AudioFlinger建立联系的过程:图二AudioTrack和AudioFlinger建立联系解释一下过程:Framework或者Java层通过JNI,new AudioTrack();根据StreamType等参数,通过一系列的调用getOutput();如有必要,AudioFlinger根据StreamType打开不同硬件设备;AudioFlinger为该输出设备创建混音线程: MixerThread(),并把该线程的id作为getOutput()的返回值返回给AudioTrack;AudioTrack通过binder机制调用AudioFlinger的createTrack();AudioFlinger注册该AudioTrack到MixerThread中;AudioFlinger创建一个用于控制的TrackHandle,并以IAudioTrack这一接口作为createTrack()的返回值;AudioTrack通过IAudioTrack接口,得到在AudioFlinger中创建的FIFO(audio_track_cblk_t);AudioTrack创建自己的监控线程:AudioTrackThread;自此,AudioTrack建立了和AudioFlinger的全部联系工作,接下来,AudioTrack可以:通过IAudioTrack接口控制该音轨的状态,例如start,stop,pause等等;通过对FIFO的写入,实现连续的音频播放;监控线程监控事件的发生,并通过audioCallback回调函数与用户程序进行交互;FIFO的管理 audio_track_cblk_taudio_track_cblk_t这个结构是FIFO实现的关键,该结构是在createTrack的时候,由AudioFlinger申请相 应的内存,然后通过IMemory接口返回AudioTrack的,这样AudioTrack和AudioFlinger管理着同一个 audio_track_cblk_t,通过它实现了环形FIFO,AudioTrack向FIFO中写入音频数据,AudioFlinger从FIFO 中读取音频数据,经Mixer后送给AudioHardware进行播放。audio_track_cblk_t的主要数据成员: user -- AudioTrack当前的写位置的偏移 userBase -- AudioTrack写偏移的基准位置,结合user的值方可确定真实的FIFO地址指针 server -- AudioFlinger当前的读位置的偏移 serverBase -- AudioFlinger读偏移的基准位置,结合server的值方可确定真实的FIFO地址指针 frameCount -- FIFO的大小,以音频数据的帧为单位,16bit的音频每帧的大小是2字节 buffers -- 指向FIFO的起始地址 out -- 音频流的方向,对于AudioTrack,out=1,对于AudioRecord,out=0audio_track_cblk_t的主要成员函数:framesAvailable_l()和framesAvailable()用于获取FIFO中可写的空闲空间的大小,只是加锁和不加锁的区别。uint32_t audio_track_cblk_t::framesAvailable_l() { uint32_t u = this->user; uint32_t s = this->server; if (out) { uint32_t limit = (s < loopStart) ? s : loopStart; return limit + frameCount - u; } else { return frameCount + u - s; } } framesReady()用于获取FIFO中可读取的空间大小。uint32_t audio_track_cblk_t::framesReady() { uint32_t u = this->user; uint32_t s = this->server; if (out) { if (u < loopEnd) { return u - s; } else { Mutex::Autolock _l(lock); if (loopCount >= 0) { return (loopEnd - loopStart)*loopCount + u - s; } else { return UINT_MAX; } } } else { return s - u; } } 我们看看下面的示意图: _____________________________________________ ^ ^ ^ ^ buffer_start server(s) user(u) buffer_end 很明显,frameReady = u - s,frameAvalible = frameCount - frameReady = frameCount - u + s 可能有人会问,应为这是一个环形的buffer,一旦user越过了buffer_end以后,应该会发生下面的情况: _____________________________________________ ^ ^ ^ ^ buffer_start user(u) server(s) buffer_end这时候u在s的前面,用上面的公式计算就会错误,但是android使用了一些技巧,保证了上述公式一直成立。我们先看完下面三个函数的代码再分析:uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) { uint32_t u = this->user; u += frameCount; ...... if (u >= userBase + this->frameCount) { userBase += this->frameCount; } this->user = u; ...... return u; } bool audio_track_cblk_t::stepServer(uint32_t frameCount) { // the code below simulates lock-with-timeout // we MUST do this to protect the AudioFlinger server // as this lock is shared with the client. status_t err; err = lock.tryLock(); if (err == -EBUSY) { // just wait a bit usleep(1000); err = lock.tryLock(); } if (err != NO_ERROR) { // probably, the client just died. return false; } uint32_t s = this->server; s += frameCount; // 省略部分代码 // ...... if (s >= serverBase + this->frameCount) { serverBase += this->frameCount; } this->server = s; cv.signal(); lock.unlock(); return true; } void* audio_track_cblk_t::buffer(uint32_t offset) const { return (int8_t *)this->buffers + (offset - userBase) * this->frameSize; } stepUser()和stepServer的作用是调整当前偏移的位置,可以看到,他们仅仅是把成员变量user或server的值加上需要移动 的数量,user和server的值并不考虑FIFO的边界问题,随着数据的不停写入和读出,user和server的值不断增加,只要处理得 当,user总是出现在server的后面,因此frameAvalible()和frameReady()中的算法才会一直成立。根据这种算 法,user和server的值都可能大于FIFO的大小:framCount,那么,如何确定真正的写指针的位置呢?这里需要用到userBase这一 成员变量,在stepUser()中,每当user的值越过(userBase+frameCount),userBase就会增加 frameCount,这样,映射到FIFO中的偏移总是可以通过(user-userBase)获得。因此,获得当前FIFO的写地址指针可以通过成员 函数buffer()返回:p = mClbk->buffer(mclbk->user);在AudioTrack中,封装了两个函数:obtainBuffer()和releaseBuffer()操作 FIFO,obtainBuffer()获得当前可写的数量和写指针的位置,releaseBuffer()则在写入数据后被调用,它其实就是简单地调用 stepUser()来调整偏移的位置。IMemory接口在createTrack的过程中,AudioFlinger会根据传入的frameCount参数,申请一块内存,AudioTrack可以通过 IAudioTrack接口的getCblk()函数获得指向该内存块的IMemory接口,然后AudioTrack通过该IMemory接口的 pointer()函数获得指向该内存块的指针,这块内存的开始部分就是audio_track_cblk_t结构,紧接着是大小为frameSize的 FIFO内存。IMemory->pointer() ---->|_______________________________________________________ |__audio_track_cblk_t__|_______buffer of FIFO(size==frameCount)____|看看AudioTrack的createTrack()的代码就明白了:sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), streamType, sampleRate, format, channelCount, frameCount, ((uint16_t)flags) << 16, sharedBuffer, output, &status); // 得到IMemory接口 sp<IMemory> cblk = track->getCblk(); mAudioTrack.clear(); mAudioTrack = track; mCblkMemory.clear(); mCblkMemory = cblk; // 得到audio_track_cblk_t结构 mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); // 该FIFO用于输出 mCblk->out = 1; // Update buffer size in case it has been limited by AudioFlinger ring track creation mFrameCount = mCblk->frameCount; if (sharedBuffer == 0) { // 给FIFO的起始地址赋值 mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); } else { .......... } (DroidPhone)
㈥ 安卓系统为什么音质不好
Android 基于Linux,我们先来了解一下Linux的特点。Linux使用ALSA作为其音频架构,其全称Advanced Linux Sound Architecture,即高级Linux声音架构的意思,在2.6核心之后,ALSA成为了Linux系统默认的音频子架构。取代了之前的OSS[Open Sound System,开放式声音系统]。
ALSA并不太好理解,它首先是一个驱动库,包含了大量的声卡设备的开源驱动,并提供了核心层API与ALSA库通信,而ALSA库则是应用程序访问和操控音频硬件的中间层,这个中间层有标准接口,开发者可以无须考虑硬件差异性进行开发,它对提升开发效率是大有帮助的。ALSA可以向下兼容OSS,因为OSS已经被淘汰,其兼容的工作模式不再讨论。
这个体系被继承到了Android当中。在Android2.2[含2,2]之前,系统文件夹中能找到一个LibAudioALSA.so的文件,这就是ALSA库文件,其他应用程序调用它,与声卡设备进行指令和数据通信。Android音频架构与Linux的并无本质区别。
在桌面版本的Linux当中,为了兼容各类声卡,Linux也设置了一个SRC[Sample Rate Converter,采样频率转换]的环节,当当前采样率低于48kHz时强制SRC到48kHz输出。这个SRC环节位于ALSA的插件模块中的混音器部分。Android针对这个进行了改进。
什么是SRC?SRC即Sample Rate Converter,中文意思为采样频率转换。它被声卡爱好者所关注,大部分发烧友视SRC为音质杀手。
Android增加了一个AudioFinger,这个可以简单的理解为Android的ALSA音频子系统的标准化的插件模块,它包含了AudioMixer[混音器]、AudioResampler[重采样]等子模块,AudioResampler即我们理解的SRC,Android换了一个新名称而已。针对SRC,Android做了改进,但改进并不是以去除SRC为目的,而是修改了默认的输出频率,Android的SRC目标采样率为44.1kHz,非该值的采样率都将SRC处理。例如播放48kHz采样率的信号,输出的最终是44.1kHz,这对音质将产生负面影响。
ALSA是一个针对Linux 桌面版本设计的音频架构,它实际上是不适合智能终端设备的,起码里面大量的开源驱动代码是可以去除的,对与Android来说,这些都是废代码。从Android2.3起,启用了一个新的音频架构。它放弃了一直使用的ALSA架构,因此系统文件夹中,也不再有LibAudioALSA.so这个文件。
Android2.3起,架构已经做了修改,在针对内部代码进行了优化,去除了冗余代码,理论上让系统能变得更加高效,可以将新架构理解为一个精简的或者为智能终端设备定制的ALSA架构。遗憾的是,它同样存在SRC严重劣化的问题,通过测试可以证明。
Android 3.0专门为平板电脑设计,影音体验变得更加重要了,是不是新系统在音质方面会有新的的进步呢,测试结果依然是令人失望的。
Android系统将采样率同一为44.1kHz输出,这造成了诸多限制,它将无法实现96kHz、192kHz高清音频节目的良好回放,大量视频节目源自DVD或者蓝光盘,其采用率多为48kHz,Android设备在回放这些视频节目时,音质也将大打折扣。
理论上软件SRC可以通过更换算法来实现音质提升,但却不太现实,智能终端所采用的CPU多为ARM,ARM芯片的浮点运算力有限,而SRC需要大量的浮点运算的资源,即便有了高质量的SRC算法,其运算也是以牺牲设备性能和耗电量为代价的,实用性差。
从Android的音频架构及流程分析,可以认为,播放44.1kHz采样率的音乐节目时,不会引发SRC,音质因此可以获得保证,理论上确实如此。但它同样存在问题,不管是之前的ALSA架构还是Android2.3之后改良的架构,其驱动库都位于核心层,也就意味着音频设备厂商、用户无法象PC平台那样安装驱动来改善音质。实际测试也表明,Android设备音质普遍偏差,Soomal有大量测试可以证明。
我们再把目光投向iOS,iOS非常封闭,我们甚至无法获知其架构的具体构成,但iOS设备不存在硬件设备多样性的问题,因此要实现更好音质也会更加简单。iOS可以实现针对性的开发和改良,以实现更好的音质。实际情况也是如此,目前为止,还没有一款Android设备的音质可以媲美任意一款iOS设备,这种差距,我们认为不是来自硬件,而是操作系统。
Android音频架构的局限性也使得其难以成为优质的影音平台,如果你希望设计一款基于Android的高清影音播放器,那么首先需要做的不是设计硬件,而是去修改现有架构的不足,或者干脆设计一个专用的架构来取代Android的通用架构。从源代码分析,Android和原生的Linux底层能支持各种采样率,开源也使得其具有改造基础,因此,在技术实力强劲的公司手里,Android也可以乌鸡变凤凰。
㈦ Android audio_policy_configuration.xml
前言
Android的audioserver 进程启动时,会创建AudioPolicyManager,在构造函数中,首先会去解析audio_policy_configuration.xml文件。
audio音频数据从一个源走到一个目的都是需要根据配置文件audio_policy_configuration.xml来决定,所以理解configuration配置文件中各个标签项转化为c++实体类的及各成员至关重要。
audio_policy_configuration.xml为音频audio的设备、流以及路由等配置文件,里面写明了audio音频部分有哪些设备、哪些流以及它们支持的编码、格式以及通道存储布局等等
audio_policy_configuration.xml中 的<moles>对应每一个audio hal 的so,mole中列出的mixPorts,devicePorts和routes解析之后完整的描述了音频的路由规则
mole name
支持“primary” (用于车载使用场景), "A2DP", "remote_submix"和"USB"。模块名称和相应音频驱动程序应编译到 audio.primary.$(variant).so 中
devicePorts
包含可从此模块访问的所有输入和输出设备(包括永久连接的设备和可移除设备)的设备描述符列表。设备的output和input,不是像mixport那样以role来分,而是以type中有关键字“IN”和“OUT”来区分
mixPorts
包含由音频 HAL 提供的所有输出流和输入流的列表。每个 mixPort 实例都可被视为传输到 Android AudioService 的物理音频流,stream配置了自己的格式、采样率以及mask,并且分为输出、输入流。一个mixPort标签可能有多个profile属性,也就是支持很多编码格式属性
routes
定义输入和输出设备之间或音频流和设备之间可能存在的连接的列表。route是把deviceport和mixport连接起来的路由,数据由一个stream输出到另一个device,或者从一个device输出到另一个stream。
㈧ 如何在Android平台上使用USB Audio设备
需求:USB Headset插上去后,声音要从本地CODEC切换到USB Headset输出/输入。 上网搜了有关USB Audio Hotplug的东西,比较适用的资源如下:1、Hotplugging USB audio devices (Howto) 题目看起来很吻合我们的问题,事实上并没有多少参考价值。其中脚本 /etc/hotplug/usb/extigy或许可以捕捉到USB Audio设备的热插拔事件,应该可以进一步验证和利用,留意这点。 2、Example to map USB Ports to ALSA card numbers and add each sound card to a combined, single interface device 这是利用udev来获取USB热插拔事件,虽然Android没有udev,但例子程序对热插拔事件字符串的处理值得参考。 3、USB mic on Linux 其实我们工作的第一步:验证USB Headset是否可以回放录音。 3.1、插上USB Headset,可以看到alsa的确加载了USB Audio,如下: ~ # cat /proc/asound/cards 0 [WMTSOC ]: HWDAC - WMT_SOC WMT_SOC (HWDAC) 1 [default ]: USB-Audio - C-Media USB Headphone Set 3.2、参考了这个链接,写了如下的配置文件/etc/asond.conf: pcm.!default {type asymplayback.pcm {type plugslave.pcm hw:1,0}capture.pcm {type plugslave.pcm hw:1,0}} 重启后,声音就从Headset出来了。 hw:1,0对应card1即USB-Audio - C-Media USB Headphone Set4、Linux下USB设备热插拔 到此,需要考虑在Android平台切换USB Audio的实现问题了。有几个途径:1/ hotplug/usb;2/ udev;3/ netlink。这里就是netlink的实现方式,链接里有个证实可用的例子程序,目前可能需要做热插拔事件字符串的处理。 难点:Android音频设备的切换底层入口是alsa_default.cpp,目前看来需要在asound.conf定义好local CODEC和USB Audio的plug;还需要修改 alsa_default.cpp,最主要Android要知道USBAudio插上时打开 USB Audio的plug, USB Audio拔下时打开local CODEC的plug。这样一想,修改的幅度还是蛮大的。而且未能确定如果在播放的过程中,切换音频设备是否有影响?如果alsa允许只是配置好asound.conf达到同样的目的,那就好办了,可惜目前找不到这方面的资料,应该没有这个便利了。 进展:2011/9/19:按照以上难点分析,大致完成了整个Android框架层的代码和ALSA配置文件,基本实现了USB Audio热插拔时的音频设备切换。但有个很大的问题:在播放时切换音频设备会导致AudioFlinger服务crash(之前做2G通话时也遇到这个问题,用其他办法规避了)。看来在切换音频设备时,应该停止播放;等切换完成后,再恢复播放。
㈨ audioserver是手机自带的吗
audioserver是手机自带的,通过本文可了解Android系统的音频架构,基本组件及功能,大概了解常用的播放模式,音频流传输路径,低延迟音频的一些能力。
㈩ Android 中怎么实现微信内置浏览器audio的自动播放
参考下面方法
加入stalled事件处理,发生stalled则重新audio.load() ; audio.play(); 或者保证audio.load()后,在canplaythrogh事件(或者readyState大于2后)进行audio.play()