AudioTrack的创建
大致流程图
对应代码
总的来说:
- 上层的AudioTrack最后都会对应一个Native层的Track
- AudioTrack会通过binder通信给到AudioFlinger,AudioFlinger会根据属性找到合适的回放线程PlaybackThread
- AudioTrack还会和Track有binder通信,通过对IAudioTrack的实现(个人看法)
JAVA层的AudioTrack.java
# base/media/java/android/media/AudioTrack.java
private AudioTrack(....) throws IllegalArgumentException {
// lyh JNI调用本地方法 native_setup --> android_media_AudioTrack_setup
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
offload, encapsulationMode, tunerConfiguration,
getCurrentOpPackageName());
}
AudioTrack的JNI
# base/core/jni/android_media_AudioTrack.cpp
static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,.....) {
// .... 省略
// lyh native层AudioTrack对象创建
lpTrack = new AudioTrack(opPackageNameStr.c_str());
status_t status = NO_ERROR;
// lyh 两种模式
switch (memoryMode) {
// lyh 写一段播一段
case MODE_STREAM:
// 给AudioTrack赋值
status = lpTrack->set(...各种参数);
break;
// lyh 一次写完播放
case MODE_STATIC:
// lyh 应用端申请共享内存
if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
goto native_init_failure;
}
status = lpTrack->set(....);
break;
}
AudioTrack.cpp
// lyh java层的new AudioTrack --> native_setup --> native的set
status_t AudioTrack::set(audio_stream_type_t streamType,uint32_t sampleRate,audio_format_t format,.....)
{
// lyh 设置音频数据传输类型
switch (transferType) {
....省略
}
// handle default values first.
// lyh 音频流类型设置,程序会设为默认值AUDIO_STREAM_MUSIC
if (streamType == AUDIO_STREAM_DEFAULT) {
streamType = AUDIO_STREAM_MUSIC;
}
// lyh 音频格式设置,采样深度默认为16bit
if (format == AUDIO_FORMAT_DEFAULT) {
format = AUDIO_FORMAT_PCM_16_BIT;
}
// lyh 输出声道合法性检查
if (!audio_is_output_channel(channelMask)) {
status = BAD_VALUE;
goto exit;
}
// lyh 左右声道声音
mVolume[AUDIO_INTERLEAVE_LEFT] = 1.0f;
mVolume[AUDIO_INTERLEAVE_RIGHT] = 1.0f;
// lyh 如果设置了提供音频数据的回调函数,则启动AudioTrackThread线程来提供音频数据
if (cbf != NULL) {
// lyh
/*AudioTrackThread实现两个核心功能:
*1 AudioTrack与AudioFlinger之间 数据传输,AudioFlinger启动了一个线程专门用于接收客户端的
* 音频数据,同时客户端也需要一个线程来“不断”的传送音频数据
*2 用于报告数据的传输状态,AudioTrack中保存了一个callback_t类型的回调函数(即全局变量mCbf)
* 用于事件发生时进行回传
*/
mAudioTrackThread = new AudioTrackThread(*this);
mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
}
// create the IAudioTrack
{
AutoMutex lock(mLock);
// lyh 调用到自己的createTrack_l line-1505
status = createTrack_l();
}
exit:
mStatus = status;
return status;
}
//lyh 构建aduiotrack的流程
status_t AudioTrack::createTrack_l()
{
// lyh 获取AudioFligner
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
// lyh 这里调用了AudioFligner(并不是直接调用,而是通过binder通信,如果你直接进入AudioFligner.cpp看代码会晕)
// lyh track == BnAudioTrack的代理对象 == BpAudioTrack
sp<IAudioTrack> track = audioFlinger->createTrack(input,output,&status);
// lyh BpAudioTrack保存在自己的变量中
mAudioTrack = track;
}
AudioFlinger.cpp
sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
CreateTrackOutput& output,
status_t *status)
{
sp<PlaybackThread::Track> track;
sp<TrackHandle> trackHandle;
// lyh 获取输出设备(这个函数最终会在AudioPolicyManager.cpp中实现,通过mEngine直接获取device)
lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType,
clientPid, clientUid, &input.config, input.flags,
&output.selectedDeviceId, &portId, &secondaryOutputs);
{
Mutex::Autolock _l(mLock);
// lyh 根据output确定回放线程
PlaybackThread *thread = checkPlaybackThread_l(output.outputId);
// lyh 创建Client
client = registerPid(clientPid);
// lyh 一个AudioTrack对应一个Track对象
track = thread->createTrack_l(client, streamType, localAttr, &output.sampleRate,
input.config.format, input.config.channel_mask,
&output.frameCount, &outputcreateTrack_l.notificationFrameCount,
input.notificationsPerBuffer, input.speed,
input.sharedBuffer, sessionId, &output.flags,
callingPid, input.clientInfo.clientTid, clientUid,
&lStatus, portId, input.audioTrackCallback,
input.opPackageName);
}
// return handle to client
// lyh 为该Track创建代理对象TrackHandle
// lyh TrackHandle extend BpTrackHandle
trackHandle = new TrackHandle(track);
// 返回给AudioTrack
return trackHandle;
}
Threads.cpp
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(....)
{
// lyh track的构造
track = new Track(this, client, streamType, attr, sampleRate, format,
channelMask, frameCount,
nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
sessionId, creatorPid, uid, *flags, TrackBase::TYPE_DEFAULT, portId,
SIZE_MAX /*frameCountToBeReady*/, opPackageName);
// lyh 保存
mTracks.add(track);
return track;
}
AudioTrack的Binder通信
不知道你有没有这样一个疑问,从上面的分析可以知道,在这个方法中返回的是audioFlinger->createTrack
返回的是HandleTrack
(extend BnAudioTrack),他是binder通信中的Bn端,而通过网上的资料查到,执行完这个方法,我们会拿到Binder通信的代理端Bp(BpAudioTrack)
有没有令人非常的费解...而出错的地方就是我没有真正的走AudioTrack和AudioFlinger的binder通信流程
# AudioTrack.cpp
status_t AudioTrack::createTrack_l()
{
sp<IAudioTrack> track = audioFlinger->createTrack(input,output,&status);
}
AudioTrack和AudioFlinger
- 想要和AudioFliger通信,我们得拿到Bp代理端,这一步通常用AudioSystem帮我们做到
- 拿到代理对象就可以调用方法,内部会帮我们调用【remote()->transact】
- 通过进程的处理,最后在Bn本地端就会通过【onTransact】接受到对应方法去执行AudioFligner自己的方法了
所以AudioTrack调用audioFlinger->createTrack的真正流程是这样的
class BpAudioFlinger : public BpInterface<IAudioFlinger>
{
// AudioTrack调用的方法!
virtual sp<IAudioTrack> createTrack(const CreateTrackInput& input,
CreateTrackOutput& output,
status_t *status)
{
// lyh AudioTrack拿到AudioFlinger代理对象,调用createTrack,执行到这里等待到onTransact回复reply
// lyh reply有BBinder(Bn端的父类,这里就是HandleTrack,也就是BnAudioTrack)
// lyh 而这里BnAudioFligner会根据CREATE_TRACK去找本地方法,才会调用到AuioFlinger的createTrack
// lyh 然后得到HandleTrack写入reply传递(?这不还是HandleTrack吗)
status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply);
// lyh 这里可以去看看 binder通信中传递binder对象和Parcelable的转换
// lyh reply.readStrongBinder() 返回的是 BBinder 的代理对象 BpBinder
// lyh 也就是HandleTrack(BnAudioTrack)的代理对象BpAudioTrack
track = interface_cast<IAudioTrack>(reply.readStrongBinder());
output.readFromParcel(&reply);
// lyh 再返回给AudioTrack使用
return track;
}
}
AudioTrack和Track
通过上面的分析我们已经拿到BpAudioTrack了,这个对象是用来AudioTrack和Track之间binder通信用的 所以调用和方法就是一条链路的,好像也没什么特别好说的,主要是知道这几个类之间的关系,才比较好找代码
网友评论