接上篇 播放器创建
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<AMessage> &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<AMessage> &format) { //对于video,会去通知播放器videosize发生变化。 if (!mIsAudio) { sp<AMessage> 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<MediaCodecBuffer> buffer; mCodec->getOutputBuffer(index, &buffer); //如果需要修正音视频同步,会直接通过这个消息处理kWhatRenderBuffer。当视频早于音频很长时间,会等待音频,然后在渲染,当视频晚于音频很长时间,会 直接seek到音频点,当视频晚于音频时间较短,会通过丢视频帧来实现同步。 sp<AMessage> 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<AMessage> &msg) { //针对视频,要去请求vsync信号,首先是获取surfaceflinger的句柄,对应的是 ISurfaceComposer类型的实例,具体 是在VideoFrameScheduler::updateVsync()中实现的,然后获取vsync的周期。 //sp<ISurfaceComposer> mComposer; //String16 name("SurfaceFlinger"); //sp<IServiceManager> sm = defaultServiceManager(); //mComposer = interface_cast<ISurfaceComposer>(sm->checkService(name)); if (mHasVideo) { mVideoScheduler = new VideoFrameScheduler(); mVideoScheduler->init(); } //解码后的buffer,将其保存到QueueEntry对象中。 sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(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<MediaCodecBuffer> firstAudioBuffer = (*mAudioQueue.begin()).mBuffer; sp<MediaCodecBuffer> 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<AMessage> &msg) { sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(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<AMessage> &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<AudioTrack> 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<AMessage> &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<AudioTrack> 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<IMemory>& 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<IAudioFlinger>& 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<IAudioTrack> 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<audio_io_handle_t> 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<IAudioTrack>track = audioFlinger→createTrack(…);
frameworks/av/services/audioflinger/AudioFlinger.cpp sp<IAudioTrack> 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<IMemory>& 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<PlaybackThread::Track> track; sp<TrackHandle> trackHandle; sp<Client> 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<IMemory> mCblkMemory;所指向的内存块。这块缓冲区是使用了匿名共享机制,所以可以跨进程共享内存。
Frameworks/av/services/audioflinger/TrackBase.h
sp<IMemory> mCblkMemory;
audio_track_cblk_t*mCblk;
所以在AudioTrack.cpp中的createTrack_l,创建完
sp<IAudioTrack>track = audioFlinger→createTrack(…),通过
sp<IMemory>iMem = track->getCblk();获取的内存空间就是指TrackBase.h中的mCblkMemory。
这块缓冲区的具体处理,可以仔细研究下。
最后调用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<AudioTrackThread> t = mAudioTrackThread; if (status == NO_ERROR) { t->resume(); }else{ t->pause(); } }
PlaybackThread和AudioTrackThread操作的共享缓冲区,这就实现了两个进程间数据的传递,完成播放。
两个线程的具体执行过程,可以仔细研究下。