音频的回放流程-音频回放链路的创建(解码后的数据送到回放设备)

接上篇 播放器创建


6,下面开始看数据是怎么送到播放设备的。

解码后的数据处理,除了CB_OUTPUT_AVAILABLE,还可能有一个CB_OUTPUT_FORMAT_CHANGED,对应的消息处理都是在NuPlayerDecoder中。

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.cppkWhatOutputBufferDrained消息的处理函数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中,然后执行了ANativeWindowqueueBuffer操作,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的构造函数分析完,然后在回头看AudioTrackwritestart函数。

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.cppset函数的参数可以通过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句柄。就是在AudioTrackcreateTrack_l中会调用sptrack = audioFlinger→createTrack(…);


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使用的缓冲区共享的。具体来源要从AudioTrackset函数说起,set函数中会调用createTrack_l方法,这个过程中会调用AudioFlingercreateTrack来生成一个跨进程通信的桥梁IAudioTrack,这个IAudioTrack在服务端的实现实际是AudioFlinger::PlaybackThread::Track,缓冲区内存的申请是在AudioFlinger::PlaybackThread::Track的父类TrackBase的构造函数中申请的,也即是sp mCblkMemory;所指向的内存块。这块缓冲区是使用了匿名共享机制,所以可以跨进程共享内存。

Frameworks/av/services/audioflinger/TrackBase.h

sp mCblkMemory;

audio_track_cblk_t*mCblk;


所以在AudioTrack.cpp中的createTrack_l,创建完

sptrack = audioFlinger→createTrack(…),通过

spiMem = 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 t = mAudioTrackThread;
	if (status == NO_ERROR) {
		t->resume();
	}else{
		t->pause();
	}
}

PlaybackThreadAudioTrackThread操作的共享缓冲区,这就实现了两个进程间数据的传递,完成播放。

两个线程的具体执行过程,可以仔细研究下。



你可能感兴趣的:(音频的回放流程-音频回放链路的创建(解码后的数据送到回放设备))