首页 > Android, Multimedia > Android源码分析:Audio的播放

Android源码分析:Audio的播放

2011-11-14 18:24 星期一    浏览: 4,253    绿 发表评论 阅读评论

Audio系统

Audio部分主要包括Audio的管理(AudioSystem)、AudioPCM输出音轨(AudioTrack),AudioPCM录制输入源(AuidoRecord)、Audio音效(AudioEffect)和Audio可视化效果(Visualizer)。基于Android的模块化和分层设计思想,它们与前述的PlaybackRecordingCamera等部分有着相似的架构。首先都是应用程序调用对应的Java API,然后到JNI层,再到的C++类(可称它们为C++ API),再通过Binder跨进程调用到mediaserver进程中的Service。当然反过来,Server侧也可以跨进程调用到应用程序侧,多为用于通知消息事件。

Audio的管理诸如设置音量以及获取音频的一些参数信息,都可以调用Java层的android.media.AudioManager来进行,AudioManager再调用android.media.AudioSystem,它最终调用到AudioFlinger这个service。跨进程调用的API接口类是IAudioFlinger,提供了众多API。在文件frameworks/base/media/libmedia/IAudioFlinger.cpp中定义的AudioFlinger接口所支持的API对应的枚举类型如下:

enum {
CREATE_TRACK = IBinder::FIRST_CALL_TRANSACTION, //
创建输出音轨
OPEN_RECORD, //
打开录音PCM输入源
SAMPLE_RATE, //
获取采样率
CHANNEL_COUNT,//
获取声道数
FORMAT,//
获取音频格式
FRAME_COUNT,//
获取帧数量,音频的帧包含一个采样时刻各个声道上的数据
LATENCY,//
获取延时时间
SET_MASTER_VOLUME,//
设置主音量控制
SET_MASTER_MUTE,//
设置主音量静音
MASTER_VOLUME,//
获取主音量控制
MASTER_MUTE,//
获取主音量是否静音
SET_STREAM_VOLUME, //
设置流音量
SET_STREAM_MUTE, //
设置流静音
STREAM_VOLUME,//
获取流音量
STREAM_MUTE,//
获取流是否静音
SET_MODE, //
设置音频模式
SET_MIC_MUTE,//
设置MIC静音
GET_MIC_MUTE,//
获取MIC是否静音
IS_STREAM_ACTIVE,//
流是否还有效
SET_PARAMETERS,//
设置某些参数
GET_PARAMETERS,//
获取参数值
REGISTER_CLIENT,//
注册客户端
GET_INPUTBUFFERSIZE,//
获取输入buffer大小
OPEN_OUTPUT,//
打开一个音频输出
OPEN_DUPLICATE_OUTPUT,//
打开并复制音频输出,意味着有两个输出
CLOSE_OUTPUT,//
关闭输出
SUSPEND_OUTPUT,//
暂停输出
RESTORE_OUTPUT,//
恢复输出
OPEN_INPUT,//
打开输入
CLOSE_INPUT,//
关闭输入
SET_STREAM_OUTPUT,//
设置流输出
SET_VOICE_VOLUME,//
设置声音音量
GET_RENDER_POSITION,
GET_INPUT_FRAMES_LOST,
NEW_AUDIO_SESSION_ID,
LOAD_EFFECT_LIBRARY,
UNLOAD_EFFECT_LIBRARY,
QUERY_NUM_EFFECTS,
QUERY_EFFECT,
GET_EFFECT_DESCRIPTOR,
CREATE_EFFECT,
MOVE_EFFECTS,
READ_INPUT
};

其类继承关系图如下,JNI层使用AudioSystemAudioSystem提供了大量静态函数以及静态成员变量。它通过get_audio_flinger()来获取静态变量sp<IAudioFlinger> gAudioFlinger这个AudioFlinger在应用程序侧的代理对象,然后通过它跨进程调用到AudioFlinger

 

 

下图是其调用流程图:

 

 

 

 

调用API

Apps

 

 

 

android.media.AudioManager

 

 

 

 

 

AudioSystem
AudioSystem.cpp

 

C/C++

android.media..AudioService

 

 

 

 

Java

android.media.AudioSystem

 

 

 

 

 

JNI
android_media_AudioSystem.cpp

 

C/C++

 

 

 

 

 

 

 

 

 

 

IAudioFlinger
IAudioFlinger.cpp

 

 

 

 

 

 

 

 

 

Binder IPC

 

 

 

 

 

 

 

 

AudioSystem

C++

 

 

AudioPCM播放--AudioTrack

Java层可以使用APIandroid.media.AudiIoTrack将来自Java层的PCM数据送往底层的音频硬件进行播放,支持StreamStatic模式,参见官方API文档。它为上层Java应用程序处理PCM数据提供了一个入口点,但一般很少在Java层直接处理PCM数据,更多是在Java层某些调用(如指明要做什么)之后,直接由底层的native代码进行处理,比如指定数据源让底层的native代码形成一个多媒体数据处理管道,然后底层的多媒体框架会自动进行相关处理。所以一般会很少用到android.media.AudiIoTrack这个API类。若是开发C/C++应用程序,则使用C++AudiIoTrack作为API

调用过程是通过JNI层,再到C++AudioTrack,再到AudioFlinger,最终通过播放线程的Track送往音频播放硬件进行声音的播放。它也支持对播放的控制,如开始/暂停/停止等。

它有着类似于调用流程图:

 

 

 

 

 

 

调用API

Apps

 

 

 

JNI
android_media_AudioTrack.cpp

 

Binder IPC

 

 

 

 

android.media.AudiIoTrack

 

 

 

Java

 

 

 

 

 

C/C++

 

 

 

 

BnInterface<IAudioTrack>

 

 

 

 

AudioTrack
AudioTrack.cpp

 

 

 

 

BnAudioTrack

 

 

 

 

 

IAudioTrack
IAudioTrack.cpp

 

 

 

AudioFlinger::TrackHandle

 

 

 

 

PlaybackThread::Track

 

 

 

 

 

 

 

 

 

 

write函数将缓冲区buffer中的userSize大小的写入audioflinger的缓冲区中。对于8位的PCM数据,则转换为16位;若本来就是16位的数据,直接使用memcpy拷贝过去。

要写入的缓冲区通过obtainBuffer获取,写入结束之后使用releaseBuffer

 

audio_track_cblk_tstepUser表示写入frameCount(函数参数)个桢

 

 

在创建C++AudioTrack的对象时,往往可以指定一个回调函数,用于:

 

 

 

 

当指定了回调函数后,AudioTrack会创建一个AudioTrackThread线程。该线程执行AudioTrackprocessAudioBuffer函数,用于判断某些事件是否发生。当这些事件发生时,就调用创建AudioTrack时指定的回调函数。这些事件包括:

EVENT_UNDERRUN:表示出现Underrun状态,即数据已播放完,新数据还没到来

EVENT_BUFFER_END:播放已处在缓冲区尾部

EVENT_LOOP_END:已经播放指定的循环次数,调用回调函数

EVENT_MARKER

EVENT_NEW_POS: 在新位置开始播放

EVENT_MORE_DATA:用于请求更多数据

 

 

 

 

 

 

 

 

 

 

 

 

 

Java层的android.media.AudiIoTrack在使用JNI层创建AudiroTrack对象时,指定的回调函数是:

static void audioCallback(int event, void* user, void *info) {
if (event == AudioTrack::EVENT_MORE_DATA) {
// set size to 0 to signal we’re not using the callback to write more data
AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;
pBuff->size = 0;
} else if (event == AudioTrack::EVENT_MARKER) {
audiotrack_callback_cookie *callbackInfo =
audiotrack_callback_cookie *)user;

JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user && env) {
env->CallStaticVoidMethod(
callbackInfo->audioTrack_class,
javaAudioTrackFields.postNativeEventInJava,
callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
} else if (event == AudioTrack::EVENT_NEW_POS) {
audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user && env) {
env->CallStaticVoidMethod(
callbackInfo->audioTrack_class,
javaAudioTrackFields.postNativeEventInJava,
callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
}
}

回调函数根据不同的event类型调用Java代码,也就是说把消息事件往Java层传递。因它只是对EVENT_MARKEREVENT_NEW_POS两种消息调用Java代码,因此Java层只能接收到这两种事件,也就是说只能对这两种事件做出某些反应。android.media.AudiIoTrack中的postEventFromNative函数:

private static void postEventFromNative(Object audiotrack_ref,
int what, int arg1, int arg2, Object obj) {
//logd(“Event posted from the native side: event=”+ what + ” args=”+ arg1+” “+arg2);

AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();

if (track == null) {
return;
}

if (track.mEventHandlerDelegate != null) {
Message m = track.mEventHandlerDelegate.getHandler().obtainMessage(what, arg1, arg2, obj);
track.mEventHandlerDelegate.getHandler().sendMessage(m);
}
}

可见postEventFromNative函数将消息发送到Java的线程队列上,由线程的handler进行处理。线程的handler处理函数见NativeEventHandlerDelegatehandleMessage函数,下面是其代码片段:

switch(msg.what) {
case NATIVE_EVENT_MARKER:
if (listener != null) {
listener.onMarkerReached(mAudioTrack);
}
break;
case NATIVE_EVENT_NEW_POS:
if (listener != null) {
listener.onPeriodicNotification(mAudioTrack);
}
break;
default:
Log.e(TAG, “[ android.media.AudioTrack.NativeEventHandler ] ” +
“Unknown event type: ” + msg.what);
break;
}

可见,对这些event事件的处理是调用listener的函数,也就是说,Java层监听处理的两种消息事件EVENT_MARKEREVENT_NEW_POS,由重载实现的监听器去处理。

 

 

 

 

 

 

sp<IAudioTrack> AudioFlinger::createTrack(
pid_t pid,
int streamType,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,

const sp<IMemory>& sharedBuffer,
int output,
int *sessionId,
status_t *status)
{
sp<PlaybackThread::Track> track;
sp<TrackHandle> trackHandle;
sp<Client> client;
wp<Client> wclient;
status_t lStatus;
int lSessionId;
if (streamType >= AudioSystem::NUM_STREAM_TYPES) {
LOGE(“invalid stream type”);
lStatus = BAD_VALUE;
goto Exit;//
流类型检查,若不合法,跳转退出
}
{
Mutex::Autolock _l(mLock);
PlaybackThread *thread = checkPlaybackThread_l(output);//
根据ouput编号,在自身维护的列表中查找对应的播放线程
PlaybackThread *effectThread = NULL;
if (thread == NULL) {//
没找到对应的播放线程,则跳转退出
LOGE(“unknown output thread”);
lStatus = BAD_VALUE;
goto Exit;
}
wclient = mClients.valueFor(pid);//
根据客户端应用程序进程号查抄对应的client信息
if (wclient != NULL) {
client = wclient.promote();//
找到,则提升为对应的强指针
} else {
client = new Client(this, pid);//
没找到,则创建一个client
mClients.add(pid, client);//
将新创建的client添加到列表中
}

LOGV(“createTrack() sessionId: %d”, (sessionId == NULL) ? -2 : *sessionId);
if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
if (mPlaybackThreads.keyAt(i) != output) {
// prevent same audio session on different output threads
uint32_t sessions = t->hasAudioSession(*sessionId);
if (sessions & PlaybackThread::TRACK_SESSION) {
lStatus = BAD_VALUE;
goto Exit;//若是音轨线程,跳转退出,TODOwhy
}
// check if an effect with same session ID is waiting for a track to be created
if (sessions & PlaybackThread::EFFECT_SESSION) {
effectThread = t.get();//
得到音效线程
}
}
}
lSessionId = *sessionId;
} else {
// if no audio session id is provided, create one here
lSessionId = nextUniqueId();//
得到一个线程ID
if (sessionId != NULL) {
*sessionId = lSessionId;
}
}
LOGV(“createTrack() lSessionId: %d”, lSessionId);
track = thread->createTrack_l(client, streamType, sampleRate, format,channelCount, frameCount, sharedBuffer,lSessionId, &lStatus);

// move effect chain to this output thread if an effect on same session was waiting

// for a track to be created

if (lStatus == NO_ERROR && effectThread != NULL) {

Mutex::Autolock _dl(thread->mLock);

Mutex::Autolock _sl(effectThread->mLock);

moveEffectChain_l(lSessionId, effectThread, thread, true);

}

}

if (lStatus == NO_ERROR) {

trackHandle = new TrackHandle(track);

} else {

// remove local strong reference to Client before deleting the Track so that the Client

// destructor is called by the TrackBase destructor with mLock held

client.clear();

track.clear();

}

Exit:

if(status) {

*status = lStatus;

}

return trackHandle;

}

 

AudioFlinger的播放线程

播放线程中可以有多个track,见其成员变量:

SortedVector< sp<Track> > mTracks;

也可以有多个音效:

Vector< sp<EffectChain> > mEffectChains;

这些track或音效用用整型的mSessionId标识,它们分别归结为两类:

enum effect_state {
EFFECT_SESSION = 0×1, // the audio session corresponds to at least one effect

TRACK_SESSION = 0×2 // the audio session corresponds to at least one track
};

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文链接地址: http://blog.redwolf-soft.com/?p=978

原创文章,版权©红狼博客所有, 转载随意,但请注明出处。

    分享到:

相关文章:

  • 无相关文章
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.
订阅评论
  欢迎参与讨论,请在这里发表您的看法、交流您的观点。