接上篇 播放器创建
6,下面开始看数据是怎么送到播放设备的。
解码后的数据处理,除了CB_OUTPUT_AVAILABLE,还可能有一个CB_OUTPUT_FORMAT_CHANGED,对应的消息处理都是在NuPlayer的Decoder中。
04-0209:13:33.703 V/NuPlayerDecoder( 544): [audio] kWhatCodecNotify: cbID= 4, paused = 0
04-0209:13:33.707 V/NuPlayerDecoder( 544): [audio] kWhatCodecNotify: cbID= 2, paused = 0
04-0209:13:33.707 V/NuPlayerDecoder( 544): [audio] kWhatCodecNotify: cbID= 1, paused = 0
除了第一个log出现次数很少外,后面两个log会看到交替出现。
NuPlayerDecoder.cpp
void NuPlayer::Decoder::onMessageReceived(const sp &msg) {
case MediaCodec::CB_OUTPUT_AVAILABLE:
handleAnOutputBuffer(index, offset, size, timeUs, flags);
case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
handleOutputFormatChange(format);
}
具体分析这两个消息的处理:
NuPlayerDecoder.cpp
void NuPlayer::Decoder::handleOutputFormatChange(const sp &format) {
//对于video,会去通知播放器videosize发生变化。
if (!mIsAudio) {
sp notify = mNotify→dup();
notify->setInt32("what", kWhatVideoSizeChanged);
}else if (mRenderer != NULL) {
//对于audio,会打开它的sink端,通过调用mAudioSink->open()方法。
mRenderer->changeAudioFormat(format, false /* offloadOnly */, hasVideo,
flags, mSource->isStreaming(), reply);
}
}
重点看handleAnOutputBuffer的处理。
NuPlayerDecoder.cpp
bool NuPlayer::Decoder::handleAnOutputBuffer(
size_t index, size_t offset, size_t size, int64_t timeUs, int32_t flags) {
//获取解码后的buffer。
sp buffer;
mCodec->getOutputBuffer(index, &buffer);
//如果需要修正音视频同步,会直接通过这个消息处理kWhatRenderBuffer。当视频早于音频很长时间,会等待音频,然后在渲染,当视频晚于音频很长时间,会
直接seek到音频点,当视频晚于音频时间较短,会通过丢视频帧来实现同步。
sp reply = new AMessage(kWhatRenderBuffer, this);
//queuebuffer间接调用了 onQueueBuffer。
mRenderer->queueBuffer(mIsAudio, buffer, reply);
//设置流结束标志。
if (eos && !isDiscontinuityPending()) {
mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
}
}
NuPlayerRenderer.cpp
void NuPlayer::Renderer::onQueueBuffer(const sp &msg) {
//针对视频,要去请求vsync信号,首先是获取surfaceflinger的句柄,对应的是 ISurfaceComposer类型的实例,具体
是在VideoFrameScheduler::updateVsync()中实现的,然后获取vsync的周期。
//sp mComposer;
//String16 name("SurfaceFlinger");
//sp sm = defaultServiceManager();
//mComposer = interface_cast(sm->checkService(name));
if (mHasVideo) {
mVideoScheduler = new VideoFrameScheduler();
mVideoScheduler->init();
}
//解码后的buffer,将其保存到QueueEntry对象中。
sp buffer = static_cast(obj.get());
QueueEntry entry;
entry.mBuffer = buffer;
//step1,把需要渲染的buffer分别保存到一个列表中 mAudioQueue, mVideoQueue,然后就开始同步渲染。
if (audio) {
Mutex::Autolock autoLock(mLock);
mAudioQueue.push_back(entry);
postDrainAudioQueue_l();
} else {
mVideoQueue.push_back(entry);
postDrainVideoQueue();
}
//先取出音视频的第一针,如果音频比视频早0.1secs,就drop一些音频。
sp firstAudioBuffer = (*mAudioQueue.begin()).mBuffer;
sp firstVideoBuffer = (*mVideoQueue.begin()).mBuffer;
int64_t firstAudioTimeUs;
int64_t firstVideoTimeUs;
CHECK(firstAudioBuffer->meta()
->findInt64("timeUs", &firstAudioTimeUs));
CHECK(firstVideoBuffer->meta()
->findInt64("timeUs", &firstVideoTimeUs));
int64_t diff = firstVideoTimeUs – firstAudioTimeUs;
if (diff > 100000ll) {
// Audio data starts More than 0.1 secs before video.
// Drop some audio.
(*mAudioQueue.begin()).mNotifyConsumed->post();
mAudioQueue.erase(mAudioQueue.begin());
VTRACE_INT("drop-audio", 1);
VTRACE_ASYNC_END("render-audio", (int)firstAudioTimeUs);
return;
}
//step2,开始同步渲染,跟step1中的调用类似,只有在音视频不同步时才需要执行这个调用,让音视频保持同步,正常渲染执行step1就够了。
syncQueuesDone_l();
}
//先看对视频的渲染,我的理解,这里对video的渲染只是处理跟音频的同步,实际的把buffer数据入队到BufferQueue的操作,在Acodec那边的onOutputBufferDrained的处理中已经执行过了。
NuPlayerRenderer.cpp
void NuPlayer::Renderer::postDrainVideoQueue() {
QueueEntry &entry = *mVideoQueue.begin();
}
回看下Acodec.cpp对kWhatOutputBufferDrained消息的处理函数onOutputBufferDrained()。
Acodec.cpp
void ACodec::BaseState::onOutputBufferDrained(const sp &msg) {
sp buffer = static_cast(obj.get());
BufferInfo *info = mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
info->mData = buffer;
err = mCodec->mNativeWindow->queueBuffer(
mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd);
}
先把解码后的数据保存到BufferInfo中,然后执行了ANativeWindow的queueBuffer操作,ANativeWindow的具体表现就是一个surface,显示渲染的过程是surface先通过GraphicBufferProducer来申请一个buffer,就是dequeueBuffer,然后往这个buffer填充数据,把填充好数据的buffer通过queuebuffer入队到BufferQueueCore中,这个过程会有onFrameAvailable的回调,会通知GraphicBufferConsumer来消费这个buffer,这个GraphicBufferConsumer实际是surfaceflinger派出管理buffer的类,所以最终消费buffer的还是surfaceflinger。
显示部分可以参考surfaceflinger相关博客。
下面看音频的处理。postDrainAudioQueue_l()调用触发了kWhatDrainAudioQueue消息。
NuPlayerRenderer.cpp
void NuPlayer::Renderer::onMessageReceived(const sp &msg) {
case kWhatDrainAudioQueue:{
// onDrainAudioQueue()这个是判断audiosink是否准备就绪,audiosink可以认为是解码后音频数据的接收端,它是MediaPlayerBase:: AudioSink内部类,
声明在frameworks/av/include/media/MediaPlayerInterface.h中,作为audio output的抽象层,具体实现AudioSink的类就
是class AudioOutput : public MediaPlayerBase::AudioSink。AudioOutput定义在MediaPlayerService.cpp中,AudioOutput中持
有的sp mTrack,将会把解码后的数据送到AudioFligner的回放线程,进一步送到回放的音频设备。也就是说AudioSink中的方法最终都是通过
调用AudioTrack的相应方法来完成功能实现的。
if (onDrainAudioQueue()) {
//计算接收端需要多长时间来回放,然后执行下次循环。
int64_t delayUs =
mAudioSink->msecsPerFrame() * numFramesPendingPlayout * 1000ll;
postDrainAudioQueue_l(delayUs);
}
}
}
数据传到AudioTrack是在onDrainAudioQueue中执行的。
NuPlayerRenderer.cpp
bool NuPlayer::Renderer::onDrainAudioQueue() {
//通过getPosition()判断是否存在AudioTrack实例,可能AudioTrack还没创建,比如一个可能的原因:这是个剩余的Audio。
if (mAudioSink->getPosition(&numFramesPlayed) != OK) {
//如果getPosition失败,渲染将不能重新安排drain,除非有有新的样本入队。
drainAudioQueueUntilLastEOS();
return false;
}
//循环把mAudioQueue中的数据写入AudioTrack,还记得前面在处理handleAnOutputBuffer时,通过NuPlayer::Renderer::onQueueBuffer把解码
后的数据push到了mAudioQueue中。
while (!mAudioQueue.empty()) {
QueueEntry *entry = &*mAudioQueue.begin();
size_t copy = entry->mBuffer->size() - entry->mOffset;
ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset,
copy, false /* blocking */);
}
}
到这里数据已经写入到AudioSink端,什么时候开始往音频设备输送呢?前面提到kWhatChangeAudioFormat这个消息的处理会调用NuPlayer::Renderer::onChangeAudioFormat(),间接调用了onOpenAudioSink()。
NuPlayerRenderer.cpp
status_t NuPlayer::Renderer::onOpenAudioSink(
const sp &format, bool offloadOnly, bool hasVideo,
uint32_t flags, bool isStreaming) {
//这里先是通过AudioOutPut的open方法创建一个AudioTrack,然后调用其start方法,开始循环播放声音。
err = mAudioSink->open(
sampleRate, numChannels,
(audio_channel_mask_t)channelMask,
audioFormat, 0 /* bufferCount - unused */,
&NuPlayer::Renderer::AudioSinkCallback, this,
(audio_output_flags_t)offloadFlags, &offloadInfo);
err = mAudioSink->start();
}
下面就是开始分析AudioTrack是怎样把数据送到播放设备的。这里的播放设备指喇叭,耳机等。
Frameworks/av/media/libmediaplayerservice.cpp
status_t MediaPlayerService::AudioOutput::open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format, int bufferCount, AudioCallback cb, void *cookie,
audio_output_flags_t flags, const audio_offload_info_t *offloadInfo,
bool doNotReconnect, uint32_t suggestedFrameCount){
//创建一个新的AudioTrack,一个播放实例对应一个AudioTrack。
sp t;
t = new AudioTrack(
mStreamType, sampleRate,
format, channelMask,
frameCount, flags,
CallbackWrapper, newcbd,
0, // notification frames
mSessionId, AudioTrack::TRANSFER_CALLBACK,
offloadInfo, mUid,
mPid, mAttributes, doNotReconnect);
//从音频数据中获取流类型。
mStreamType = t->streamType();
//设置音量。
t->setVolume(mLeftVolume, mRightVolume);
}
继续把AudioTrack的构造函数分析完,然后在回头看AudioTrack的write、start函数。
AudioTrack的构造函数,除了做变量的初始化外,重要的是调用了set()方法。
04-0209:13:49.643 V/AudioTrack( 544): set(): streamType 3, sampleRate48000, format 0x1, channelMask 0x3, frameCount 24960, flags #0,notificationFrames 0, sessionId 169, transferType 0, uid 10049, pid8530
从上面的log可以了解下音频数据的一些参数。
Frameworks/av/media/libaudioclient/AudioTrack.cpp
status_t AudioTrack::set(
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
audio_output_flags_t flags,
callback_t cbf,
void* user,
int32_t notificationFrames,
const sp& sharedBuffer,
bool threadCanCallJava,
audio_session_t sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
uid_t uid,
pid_t pid,
const audio_attributes_t* pAttributes,
bool doNotReconnect,
float maxRequiredSpeed){
// 默认的流类型是music。这些默认值是在从音频数据得不到相应参数时用的。
if (streamType == AUDIO_STREAM_DEFAULT) {
streamType = AUDIO_STREAM_MUSIC;
}
//默认的采样大小16bit,
if (format == AUDIO_FORMAT_DEFAULT) {
format = AUDIO_FORMAT_PCM_16_BIT;
}
//左右声道默认都是最大音量。
mVolume[AUDIO_INTERLEAVE_LEFT] = 1.0f;
mVolume[AUDIO_INTERLEAVE_RIGHT] = 1.0f;
//创建了AudioTrackThread,作为生产者不断往通道中填充数据,audioflinger端的playbackThread作为消费者获取数据送到音频设备,完成播放。
if (cbf != NULL) {
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
}
//这个函数一方面会根据音频的路由策略,获取跟当前播放类型匹配的输出通道,另一面创建一个IAudioTrack,作为AudioTrack.cpp
跟AudioPolicyService.cpp跨进程通信的桥梁。
status_t status = createTrack_l();
}
重点关注音频通道的建立。
AudioTrack.cpp
status_t AudioTrack::createTrack_l(){
//获取AudioFlinger的服务句柄,实际是AudioFlingerClient实例,是一个Binder客户端,作为跨进程沟通的桥梁。
const sp& audioFlinger = AudioSystem::get_audio_flinger();
//step1,这里根据音频策略,获取输出通道,理论上可以直接使用AudioPolicyService相关的服务,这里AudioSystem只是做个封装中转,或者说这样降低了
使用者(AudioTrack)和底层服务(AudioPolicyService,AudioFlinger)之间的耦合,不管音频系统底层的库怎么升级,只要AudioSystem,AudioService向上
的接口不变,它的使用者(AudioTrack)就不需要做修改。
status = AudioSystem::getOutputForAttr(attr, &output,
mSessionId, &streamType, mClientUid,
&config,
mFlags, &mRoutedDeviceId, &mPortId);
//step2,创建跟AudioFlinger跨进程通信的桥梁IAduioTrack。
sp track = audioFlinger->createTrack(streamType,
mSampleRate,
mFormat,
mChannelMask,
&temp,
&flags,
mSharedBuffer,
output,
mClientPid,
tid,
&mSessionId,
mClientUid,
&status,
mPortId);
}
详细看下音频通道的获取getOutputForAttr,直接看AudioPolicyService中实现,中间过程无非是获取AudioPolicyService的服务句柄,然后调用其方法,其他AudioPolicyService中的功能调用也是这个套路。
值得注意的是AudioPolicyService中的一些功能的实现函数在AudioPolicyInterfaceImpl.cpp中,而不是在AudioPolicyService.cpp中。
AudioPolicyInterfaceImpl.cpp
status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId){
//直接调用AudioPolicymanager中的方法,AudioPolicyManager是音频策略的基础类,又继承自AudioPolicyInterface,AudioPolicyManager实现的
是所有平台通用的音频策略行为,具体平台会以提供共享库的形式来指定具体的音频策略。
return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
config, flags, selectedDeviceId, portId);
}
frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId){
//根据参数,对流类型做个归类,不同的流类型可能对应的路由策略是一样的。
*stream = streamTypefromAttributesInt(&attributes);
//step1,根据音频参数获取相应的路由策略,这个出现了一个新的对象mEngine(Engine.cpp类型),初始化在AudioPolicyManager.cpp的构造函数中,
解析完audio plicy的配置文件(audio_policy_configuration.xml 或者audio_policy.conf)后,会实例化一个Engine对象,代表了具体平台的音频策略。
routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
//step2,根据音频策略,匹配音频设备,
audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
//step3,为匹配好的音频设备,选择合适的输出通道output.
*output = getOutputForDevice(device, session, *stream,
config->sample_rate, config->format, config->channel_mask,
flags, &config->offload_info);
}
//step1,具体路由策略的选择,实现在Engine中:
frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp
routing_strategy Engine::getStrategyForUsage(audio_usage_t usage){
// usage to strategy mapping
switch (usage) {
case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
return STRATEGY_ACCESSIBILITY;
case AUDIO_USAGE_MEDIA:
case AUDIO_USAGE_GAME:
case AUDIO_USAGE_ASSISTANT:
case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
return STRATEGY_MEDIA;
case AUDIO_USAGE_VOICE_COMMUNICATION:
return STRATEGY_PHONE;
case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
return STRATEGY_DTMF;
case AUDIO_USAGE_ALARM:
case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
return STRATEGY_SONIFICATION;
case AUDIO_USAGE_NOTIFICATION:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
case AUDIO_USAGE_NOTIFICATION_EVENT:
return STRATEGY_SONIFICATION_RESPECTFUL;
case AUDIO_USAGE_UNKNOWN:
default:
return STRATEGY_MEDIA;
}
}
//step2,根据音频策略,匹配音频设备,实现在Engine中,
frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp
audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy,
DeviceVector availableOutputDevices,
DeviceVector availableInputDevices,
const SwAudioOutputCollection &outputs) const{
switch (strategy) {
case STRATEGY_MEDIA: {
uint32_t device2 = AUDIO_DEVICE_NONE;
if (isInCall() && (device == AUDIO_DEVICE_NONE)) {
// when in call, get the device for Phone strategy
device = getDeviceForStrategy(STRATEGY_PHONE);
break;
}
if (strategy != STRATEGY_SONIFICATION) {
// no sonification on remote submix (e.g. WFD)
if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
String8("0")) != 0) {
device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
}
}
if (isInCall() && (strategy == STRATEGY_MEDIA)) {
device = getDeviceForStrategyInt(
STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
break;
}
if ((device2 == AUDIO_DEVICE_NONE) &&
(mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
(outputs.isA2dpOnPrimary() || (outputs.getA2dpOutput() != 0))) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
}
}
if ((device2 == AUDIO_DEVICE_NONE) &&
(mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] == AUDIO_POLICY_FORCE_SPEAKER)) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
}
}
ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
return device;
}
这个函数很长,只粘贴了部分,针对media的路由实现,匹配过程从上到下是有优先级的,只有前一步找不到合适的设备,才往下一步找。
//step3,为匹配好的音频设备,选择合适的输出通道output.
AudioPolicyManager.cpp
audio_io_handle_t AudioPolicyManager::getOutputForDevice(
audio_devices_t device,
audio_session_t session,
audio_stream_type_t stream,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
const audio_offload_info_t *offloadInfo){
//由这个判断,普通的音视频回放不是directoutput,所以走的else部分。DirectOutput这个音频输出属性,是指不进行软件混音直接交给hal进行处理。
Primary通常是软件解码,软件混音,采样率转换等都要执行的。
profile = getProfileForDirectOutput(device,…);
if (profile != 0) {}else{
//找到支持这个device的所有的output,添加到outputs中,也就是函数的返回值。每个output支持多个音频设备,不同的output支持的音频设备也可能不同,
所以可能会有多个支持同一device的output。这个查找的过程是从 mOutputs映射中找支持device的output,那 mOutputs中这些output是什么时候添加进去的呢?
前面说过AudioPolicyManager的构造函数中会加载具体平台描述音频设备的配置文件,每一种音频设备接口都有一个对应的库文件,解析完配置文件后,
会有AudioFlinger加载这些设备接口,添加到一个mAudioHwDevs映射中,接着的操作会调用AudioFligner的openOutput接口打开output通道,
一个设备接口可能包含多个output,打开output的过程中,会创建音频输出流AudioStreamOut,然后创建跟这个输出流,
输出通道output对应的回放线程PlayBackThread,这个回放线程就是往输出通道中填东西的,并把回放线程添加一个映射中mPlaybackThreads,
其中的key就是output,后续可以根据这个output找到它对应的回放线程。这个过程中打开的output保存在了mOutputs这个映射中,也是这里查找的来源,
这个映射的key 是:audio_io_handle_t output。
SortedVector outputs = getOutputsForDevice(device, mOutputs);
//找到的输出通可能不止一个,选择一个最优的,选择的标准是其中的参数flags,看那个通道的mFlags跟它最接近,这个参数中的flags哪里来的呢?往上回溯,
不难发现是AudioTrack的set函数中传进来的,但是在音频回放的这个环境下,set中的参数并不是最初的源头,最初的源头是nuplayerDecoder.cpp
中的handleOutputFormatChange方法中,当然NuPlayerDecoder中的这个flag在经过NuPlayerRenderer,MediaPlayerService::AudioOutput::open
到AudioTrack的过程中会有修正,所以nuplayerDecoder中的值不是selectOutput 中最终看到的。
output = selectOutput(outputs, flags, format);
}
}
AudioTrack.cpp的set函数的参数可以通过log了解:
ALOGV("set():streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount%zu, " "flags #%x, notificationFrames %d, sessionId %d,transferType %d, uid %d, pid %d",
streamType,sampleRate, format, channelMask, frameCount, flags,notificationFrames,
sessionId,transferType, uid, pid);
V/AudioTrack(7381): set(): streamType -1, sampleRate 44100, format 0x1,channelMask 0x3, frameCount 14144, flags #0, notificationFrames 0,sessionId 0, transferType 3, uid -1, pid -1
到这里音频通道output就打通了,这段分析的源头是从AudioTrack的构造函数开始的。
其中还有一个关键环节:创建IAudioTrack这个跨进程的Binder句柄。就是在AudioTrack的createTrack_l中会调用sp
frameworks/av/services/audioflinger/AudioFlinger.cpp
sp AudioFlinger::createTrack(
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *frameCount,
audio_output_flags_t *flags,
const sp& sharedBuffer,
audio_io_handle_t output,
pid_t pid,
pid_t tid,
audio_session_t *sessionId,
int clientUid,
status_t *status,
audio_port_handle_t portId){
sp track;
sp trackHandle;
sp client;
//在AudioFligner::openoutput时,创建了回放线程,并以output作为key,保存在mPlaybackThreads这个映射中,这里根据ouput来找到匹配
的playbackThread。
PlaybackThread *thread = checkPlaybackThread_l(output);
//在找到的playbackthread中,创建一个playbackthread::track对象,这个track对象构建时,相应的缓冲区就分配了。
track = thread->createTrack_l(client, streamType, sampleRate, format,
channelMask, frameCount, sharedBuffer, lSessionId, flags, tid,
clientUid, &lStatus, portId);
//将playbackthread::track作为参数,创建的 TrackHandle会返回到AudioTrack.cpp端,也就是IAudioTrack的实例,作为跨进程通信的桥梁。
AudioTrack需要使用AudioFligner的服务都是通过IAudioTrack实例来完成的,比如获取数据内存块就是调用了track->getCblk()。
trackHandle = new TrackHandle(track);
return trackHandle;
}
简单做个总结,回放过程一些概念的关系:
一个playbackthread 对应一个回放设备,如果有两个回放设备,就会有两个playbackthread回放线程;
同一个stream流类型对应的回放设备是一样的,所以同一个stream流类型对应的playbackthread回放线程也是一样的;
一个AudioTrack对应一个AudioTrackThread线程,负责生成数据,对应的playbackthread负责消费数据;
一个AudioTrack对应一个播放实例;
一个AudioTrack有其对应合适的Audio Interface;
一个Audio Interface 有相应的output输出通道;
一个output输出通道有其适合的Audio Device音频设备;
一个stream流类型有对应的strategy路由策略;
一个strategy路由策略有相应的Audio device设备;
可能有多个output输出通道对应一个AudioDevice音频设备,所以会根据stream流类型,及device,可以选择出合适的output通道,
也就是符合这个stream流类型,又支持这个device的output通道;
把AudioTrack的构造函数分析完后,在回头看AudioTrack的write、start函数。
NuPlayerDecoder解码后的数据,通过调用AudioTrack的write函数传过来。
Frameworks/av/media/libaudioclient/AudioTrack.cpp
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking){
Buffer audioBuffer;
while (userSize >= mFrameSize) {
status_t err = obtainBuffer(&audioBuffer,
blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
memcpy(audioBuffer.i8, buffer, toWrite);
}
}
这个函数的关键是调用obtainBuffer申请一块缓冲区,然后把数据copy进去。
获取的这块缓冲区实际是跟playbackThread使用的缓冲区共享的。具体来源要从AudioTrack的set函数说起,set函数中会调用createTrack_l方法,这个过程中会调用AudioFlinger的createTrack来生成一个跨进程通信的桥梁IAudioTrack,这个IAudioTrack在服务端的实现实际是AudioFlinger::PlaybackThread::Track,缓冲区内存的申请是在AudioFlinger::PlaybackThread::Track的父类TrackBase的构造函数中申请的,也即是sp
Frameworks/av/services/audioflinger/TrackBase.h
sp
audio_track_cblk_t*mCblk;
所以在AudioTrack.cpp中的createTrack_l,创建完
sp
sp
这块缓冲区的具体处理,可以仔细研究下。
最后调用start方法开启正式的播放。
前面把播放链路打通了,start方法就是让它运转起来。
Frameworks/av/media/libaudioclient/AudioTrack.cpp
status_t AudioTrack::start(){
//前面介绍过 mAudioTrack是IAudioTrack,跟AudioFligner端通信的桥梁,让他start,实际是调用AudioFlinger::TrackHandle::TrackHandle中
的start,然后调用AudioFlinger::PlaybackThread::Track中的start,最终让 PlaybackThread消费者线程运转起来。
if (!(flags & CBLK_INVALID)) {
status = mAudioTrack->start();
}
//接着让 AudioTrackThread这个生产者线程运转,
sp t = mAudioTrackThread;
if (status == NO_ERROR) {
t->resume();
}else{
t->pause();
}
}
PlaybackThread和AudioTrackThread操作的共享缓冲区,这就实现了两个进程间数据的传递,完成播放。
两个线程的具体执行过程,可以仔细研究下。