简述
这部分,我们主要讲解两个函数getMinBufferSize和AudioTrack。
正文
一、getMinBufferSize函数解析
直接帖整段的代码,不打算省略了。可能很占空间,但是我们可以把每个变量都看一遍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {int channelCount = 0;// 传入的声道配置。CHANNEL_OUT_MONO、CHANNEL_CONFIGURATION_MONO是一样的主要是为了兼容以前。// CHANNEL_OUT_MONO单声道 CHANNEL_OUT_STEREO 双声道。switch(channelConfig) {case AudioFormat.CHANNEL_OUT_MONO:case AudioFormat.CHANNEL_CONFIGURATION_MONO:channelCount = 1;break;case AudioFormat.CHANNEL_OUT_STEREO:case AudioFormat.CHANNEL_CONFIGURATION_STEREO:channelCount = 2;break;default:// 判断是否支持全方位的声道数,总共是8个,支持的话,需要支持前左、前右、后左、后右、左边、右边if (!isMultichannelConfigSupported(channelConfig)) {loge("getMinBufferSize(): Invalid channel configuration.");return ERROR_BAD_VALUE;} else {// 如果支持,就计算出它的声道数。channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);}}// 检查下format的格式是否正确。if (!AudioFormat.isPublicEncoding(audioFormat)) {loge("getMinBufferSize(): Invalid audio format.");return ERROR_BAD_VALUE;}// sample rate, note these values are subject to change// 确保采样率在我们规定的范围,如果自行扩展采样率的话,就需要修改这边的静态变量。if ( (sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX) ) {loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");return ERROR_BAD_VALUE;}// 接下来就直接调用native的方法了。int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);if (size <= 0) {loge("getMinBufferSize(): error querying hardware");return ERROR;}else {return size;}
}
|
上面的函数做了几件事:
- 将上层的channelconfig转变为声道的数量
- 判断audioformat是否是我们定义的
- 调用native的min buffer size
接着来看native的代码。
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,jint sampleRateInHertz, jint channelCount, jint audioFormat) {size_t frameCount;// 通过AudioTrack去获取需要的最少的帧数。const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,sampleRateInHertz);if (status != NO_ERROR) {return -1;}// 将上层的audioFormat转换成native的格式 java层是一个递增索引,在native是位活的形式const audio_format_t format = audioFormatToNative(audioFormat);// 判断是否是线性的pcm数据流,因为只有线性数据流我们才能计算出帧对应多少字节。if (audio_is_linear_pcm(format)) {// 获取每一帧占多少个字节const size_t bytesPerSample = audio_bytes_per_sample(format);// 帧数 * 每帧字节数 * 通道数 就是我们所要得到的buffer的字节数了。return frameCount * channelCount * bytesPerSample;} else {return frameCount;}
}
上面的代码牵扯到一个帧到字节的计算方式。
buffersize = 帧数 x 每帧占用字节数 x 通道数
format这个参数,我们可以理解为采样精度,每帧占用字节数如下图:
现在还有frameCount这个变量是未知的,我们来看下函数AudioTrack::getMinFrameCount
status_t AudioTrack::getMinFrameCount(size_t* frameCount,audio_stream_type_t streamType,uint32_t sampleRate)
{if (frameCount == NULL) {return BAD_VALUE;}// FIXME handle in server, like createTrack_l(), possible missing info:// audio_io_handle_t output// audio_format_t format// audio_channel_mask_t channelMask// audio_output_flags_t flags (FAST)uint32_t afSampleRate;status_t status;// 获取streamtype下,对应output的采样率status = AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);if (status != NO_ERROR) {return status;}size_t afFrameCount;// 获取streamtype下,对应output的hal层的buffer size对应的帧数,采样率为当时output默认。status = AudioSystem::getOutputFrameCount(&afFrameCount, streamType);if (status != NO_ERROR) {return status;}uint32_t afLatency;// 获取streamtype下,对应output的hal层latency的大小,单位是msstatus = AudioSystem::getOutputLatency(&afLatency, streamType);if (status != NO_ERROR) {return status;}// When called from createTrack, speed is 1.0f (normal speed).// This is rechecked again on setting playback rate (TODO: on setting sample rate, too).// 开始计算所需要的最少帧数*frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);if (*frameCount == 0) {return BAD_VALUE;}return NO_ERROR;
}
该函数的逻辑,主要是去获取output的一些信息,采样率、latency、底层buffer对应的帧数,然后计算所需要的最少帧数。
static size_t calculateMinFrameCount(uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,uint32_t sampleRate, float speed)
{// Ensure that buffer depth covers at least audio hardware latency// 确保buffer大小最少能够覆盖硬件的延时// 算法就是latency 除以 底层buffer大小在当前采样率下播放完所需花费的时间,这个就是多少倍了。uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);// 这个倍数不能小于2,最少必须等于2.就是说,如果latencyms比buffer还短,那就只需要两倍buffer的msif (minBufCount < 2) {minBufCount = 2;}// sourceFramesNeededWithTimestretch这个函数里面包括了SRC的计算,和spee的的部分我们就不贴了。return minBufCount * sourceFramesNeededWithTimestretch(sampleRate, afFrameCount, afSampleRate, speed);
}
上面的计算过程就不再细看了,算法:
size_t((uint64_t)dstFramesRequired x srcSampleRate / dstSampleRate + 1 + 1)
我们继续看函数AudioSystem::getOutputSamplingRate.
code snippet widget
该函数的逻辑:
- 获取output,这个output对应到AudioFlinger中的某个output thread,每个thread都会含有一个hal的output。
- 通过这个output去获取hal层当前设置的samplingRate。
我们一起来看下getOutput函数,由于这个函数会一路调用到AudioPolicyManager中,所以我这边直接就贴AudioPolicy部分的代码。
这部分牵扯到了AudioPolicy部分。每一个output都对应到audio_policy.conf中的一个port节点。
audio_io_handle_t AudioPolicyManager::getOutput(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)
{// 通过stream type去获取路由策略routing_strategy strategy = getStrategy(stream);// 通过策略去选择输出的deviceaudio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);// 通过device来选择输出的outputreturn getOutputForDevice(device, AUDIO_SESSION_ALLOCATE,stream, samplingRate,format, channelMask,flags, offloadInfo);
}audio_io_handle_t AudioPolicyManager::getOutputForDevice(audio_devices_t device,audio_session_t session __unused,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)
{audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;uint32_t latency = 0;status_t status;// open a direct output if required by specified parameters//force direct flag if offload flag is set: offloading implies a direct output stream// and all common behaviors are driven by checking only the direct flag// this should normally be set appropriately in the policy configuration file// 如果flag中指定AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD、AUDIO_OUTPUT_FLAG_HW_AV_SYNC则会// 强制成AUDIO_OUTPUT_FLAG_DIRECT,因为offloading就意味着direct output,后面只需要判断该flag// 就可以了。if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);}if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);}// 只允许stream type为music的可以使用deep bufferif (stream != AUDIO_STREAM_MUSIC) {flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);} else if (/* stream == AUDIO_STREAM_MUSIC && */flags == AUDIO_OUTPUT_FLAG_NONE &&property_get_bool("audio.deep_buffer.media", false /* default_value */)) {// use DEEP_BUFFER as default output for music stream typeflags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_DEEP_BUFFER;}if (stream == AUDIO_STREAM_TTS) {flags = AUDIO_OUTPUT_FLAG_TTS;}sp<IOProfile> profile;// 如果明显是使用mixed output或者没有明确要求的使用direct output,则直接调到non_direct_outputif (((flags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) &&audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE &&audio_channel_count_from_out_mask(channelMask) <= 2) {goto non_direct_output;}// Do not allow offloading if one non offloadable effect is enabled. This prevents from// creating an offloaded track and tearing it down immediately after start when audioflinger// detects there is an active non offloadable effect.// FIXME: We should check the audio session here but we do not have it in this context.// This may prevent offloading in rare situations where effects are left active by apps// in the background.// 必须保证mEffects中的效果,所有的使能的效果并且属于media必须support offload effect,或者没有// 效果才可以offloading。// isNonOffloadableEffectEnabled该函数的实现是,只有effect使能并且属于media策略// 并且不support offload effect的时候才返回true。默认基本上都是返回false的。if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||!mEffects.isNonOffloadableEffectEnabled()) {profile = getProfileForDirectOutput(device,samplingRate,format,channelMask,(audio_output_flags_t)flags);}if (profile != 0) {sp<SwAudioOutputDescriptor> outputDesc = NULL;for (size_t i = 0; i < mOutputs.size(); i++) {sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);if (!desc->isDuplicated() && (profile == desc->mProfile)) {outputDesc = desc;// 如果找到互相匹配的direct output则直接复用,引用计数+1if ((samplingRate == outputDesc->mSamplingRate) &&(format == outputDesc->mFormat) &&(channelMask == outputDesc->mChannelMask)) {outputDesc->mDirectOpenCount++;return mOutputs.keyAt(i);}}}// 如果有找到对应的direct output,但是参数不一样,则先关闭处理,如果当前该output// 有音频流在输出的话,会怎么样呢??if (outputDesc != NULL) {closeOutput(outputDesc->mIoHandle);}// 如果选中的profile是一个offloaded,但是没有指定特殊参数,则使用默认值audio_offload_info_t defaultOffloadInfo = AUDIO_INFO_INITIALIZER;if ((profile->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) && !offloadInfo) {flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);defaultOffloadInfo.sample_rate = samplingRate;defaultOffloadInfo.channel_mask = channelMask;defaultOffloadInfo.format = format;defaultOffloadInfo.stream_type = stream;defaultOffloadInfo.bit_rate = 0;defaultOffloadInfo.duration_us = -1;defaultOffloadInfo.has_video = true; // conservativedefaultOffloadInfo.is_streaming = true; // likelyoffloadInfo = &defaultOffloadInfo;}// 重新构造对象outputDesc = new SwAudioOutputDescriptor(profile, mpClientInterface);// 设置这个output当前选择的输出设备outputDesc->mDevice = device;// output latency设置为0outputDesc->mLatency = 0;outputDesc->mFlags = (audio_output_flags_t)(outputDesc->mFlags | flags);audio_config_t config = AUDIO_CONFIG_INITIALIZER;config.sample_rate = samplingRate;config.channel_mask = channelMask;config.format = format;if (offloadInfo != NULL) {config.offload_info = *offloadInfo;}// 这是要通知到AudioFlinger那边去打开output,并且让output thread跑起来status = mpClientInterface->openOutput(profile->getModuleHandle(),&output,&config,&outputDesc->mDevice,String8(""),&outputDesc->mLatency,outputDesc->mFlags);// 只能接受符合要求参数的outputif (status != NO_ERROR ||(samplingRate != 0 && samplingRate != config.sample_rate) ||(format != AUDIO_FORMAT_DEFAULT && format != config.format) ||(channelMask != 0 && channelMask != config.channel_mask)) {if (output != AUDIO_IO_HANDLE_NONE) {mpClientInterface->closeOutput(output);}// 如果direct output没有被打开,并且符合下面条件,则回到mixer output去。if (audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE) {goto non_direct_output;}return AUDIO_IO_HANDLE_NONE;}outputDesc->mSamplingRate = config.sample_rate;outputDesc->mChannelMask = config.channel_mask;outputDesc->mFormat = config.format;outputDesc->mRefCount[stream] = 0;outputDesc->mStopTime[stream] = 0;outputDesc->mDirectOpenCount = 1;// 未将该output添加进来之前,先获取一次output,对于打开的output中,优先offload、// deep buffer,(通过flag判断)最后选择打开列表中的第一个。audio_io_handle_t srcOutput = getOutputForEffect();// 将这个output添加到AudioPolicy的mOutputs中来。addOutput(output, outputDesc);audio_io_handle_t dstOutput = getOutputForEffect();// output发生迁移,全局效果跟着迁移。if (dstOutput == output) {mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, srcOutput, dstOutput);}mPreviousOutputs = mOutputs;mpClientInterface->onAudioPortListUpdate();return output;}non_direct_output:// 这边不用去管channel mask,因为mixer具有downmix的能力// 打开一个非direct output// 对于非direct output,只支持pcmif (audio_is_linear_pcm(format)) {// get which output is suitable for the specified stream. The actual// routing change will happen when startOutput() will be called// 根据指定的stream type获取哪一些output是合适的。发生routing是在startOutput函数。SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);// 到这个阶段,我们可以去掉DIRECT flag,因为没有direct output会被发现了。flags = (audio_output_flags_t)(flags & ~AUDIO_OUTPUT_FLAG_DIRECT);// 获取output,优先级:flag一致越多的 > primary output > 数组中第一个outputoutput = selectOutput(outputs, flags, format);}return output;
}
函数的主要逻辑是:
- 通过stream type获取路由策略
- 通过路由策略获取输出设备
- 通过输出设备获取输出的output 到这边就跟AudioFlinger中的output thread挂钩了。
getOutputForDevice这个函数的主要逻辑是:
- 根据传入的flag进行判断选择什么样的output。
- 如果是direct的output,则需要参数完全一致,否则关闭,重新配置打开这个direct output,打开失败回到non direct output来。
- 如果是non direct output的话,会按照一定优先级来选择
这边留下一个疑问,如果一个offload的output当前正在playback,那上面的close会触发什么,或者带来什么改变?
我们一开始只是关注获取output,结果却看了如此多的代码。。。output值其实是AudioFlinger那边一个thread的索引,通过这个值,我们就可以从AudioFlinger中拿到这个对象。
我们回到函数AudioSystem::getOutputSamplingRate,接着看里面的getSamplingRate部分。
status_t AudioSystem::getSamplingRate(audio_io_handle_t output,uint32_t* samplingRate)
{// 获取到AudioFlinger的binder。const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();if (af == 0) return PERMISSION_DENIED;// 看本地是否有存储该output对应的对象,如果有则直接获取,如果没有则通过AudioFlinger去获取sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output);if (outputDesc == 0) {*samplingRate = af->sampleRate(output);} else {*samplingRate = outputDesc->mSamplingRate;}if (*samplingRate == 0) {return BAD_VALUE;}return NO_ERROR;
}
// 我们来看下这个获取flinger的函数,因为它会注册当前的信息到AudioFlinger,所以显得较为重要。
const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
{sp<IAudioFlinger> af;sp<AudioFlingerClient> afc;{Mutex::Autolock _l(gLock);// 全局变量gAudioFlinger如果为0,说明这个进程第一次访问AudioFlinger,才需要去获取跟注册。if (gAudioFlinger == 0) {// 获取service的逻辑sp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder;do {binder = sm->getService(String16("media.audio_flinger"));if (binder != 0)break;ALOGW("AudioFlinger not published, waiting...");usleep(500000); // 0.5 s} while (true);// 如果全局gAudioFlingerClient为null 说明尚未注册到AudioFlinger中,则创建一个。if (gAudioFlingerClient == NULL) {gAudioFlingerClient = new AudioFlingerClient();} else {// 否则看下是否有这个回调函数,有的话则回调。if (gAudioErrorCallback) {gAudioErrorCallback(NO_ERROR);}}// AudioFlinger挂掉的话会通知gAudioFlingerClient去调用自己的binderDied方法binder->linkToDeath(gAudioFlingerClient);// 将该binder对象存储起来gAudioFlinger = interface_cast<IAudioFlinger>(binder);// 将client对象存储起来afc = gAudioFlingerClient;}af = gAudioFlinger;}// 如果afc不为空,则注册到AudioFlinger中去。AudioFlinger中会根据pid进行绑定。// AudioFlinger那边会给thread发送open output的信息,这样thread会拼装一个iodesc回来给到// AudioSystem,由这边来进行维护output的信息。if (afc != 0) {af->registerClient(afc);}return af;
}
到这边我们就知道怎么获取到的samplingRate了,它的值是存储在output thread中的,你可能还是想知道这个samplingRate是从哪里来的,前面我们其实也看到了open output的一些代码了,那边我们是有传递samplingRate进去的,也就是说,这个samplingRate的值基本上就是第一个打开这个output的时候设置的,除非后面有通过setparameter进行重新设置,否则这个值就是这样了。它也必须跟audio_policy.conf中一致的。
到这边我们就回过头来看函数AudioTrack::getMinFrameCount
我们已经有output了,并且获取到了samplingRate了,那接下来就是framecount和latency,而这两个值都是hal层的数据。并不是我们open output的时候传递进去的,不过获取的逻辑基本上跟上面获取samplingRate是一致的。
这样我们就获取到所有我们想要的数据了。就可以计算出MinBufferSize
由于我们是通过getMinBufferSize一路调下来的,它传递给底层的参数实在太有限了,所以基本上这个获取到的是primary的相关信息。我们可以看到在AudioTrack中调用getMinFrameCount也就只有采样率会保存下来,但是并不会用来跟AudioPolicy和AudioFlinger打交道。所以获取到的buffersize是跟实际有点差别的。但是你又不得不遵循就是了。
二、AudioTrack的创建流程
我们从java这边的构造函数开始。
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int mode, int sessionId)throws IllegalArgumentException {// mState already == STATE_UNINITIALIZED// 例行检查if (attributes == null) {throw new IllegalArgumentException("Illegal null AudioAttributes");}if (format == null) {throw new IllegalArgumentException("Illegal null AudioFormat");}// 记录下和audiotrack实例关联的looper。Looper looper;if ((looper = Looper.myLooper()) == null) {looper = Looper.getMainLooper();}int rate = 0;// 看下format参数中是否带有采样率,如果有,则直接从format对象中获取,否则从获取primary output的if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0){rate = format.getSampleRate();} else {rate = AudioSystem.getPrimaryOutputSamplingRate();if (rate <= 0) {rate = 44100;}}int channelIndexMask = 0;// 同上,看下是否包含channel的信息。if ((format.getPropertySetMask()& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0) {channelIndexMask = format.getChannelIndexMask();}int channelMask = 0;if ((format.getPropertySetMask()& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0) {channelMask = format.getChannelMask();} else if (channelIndexMask == 0) { // if no masks at all, use stereochannelMask = AudioFormat.CHANNEL_OUT_FRONT_LEFT| AudioFormat.CHANNEL_OUT_FRONT_RIGHT;}int encoding = AudioFormat.ENCODING_DEFAULT;if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0) {encoding = format.getEncoding();}// 检测参数是否正确audioParamCheck(rate, channelMask, channelIndexMask, encoding, mode);// 采用AudioAttributes的形式,则该变量会被设置为defaultmStreamType = AudioSystem.STREAM_DEFAULT;// 很粗略的判断了下我们申请的buffersize是否合法。audioBuffSizeCheck(bufferSizeInBytes);mInitializationLooper = looper;IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);mAppOps = IAppOpsService.Stub.asInterface(b);mAttributes = new AudioAttributes.Builder(attributes).build();// sessionId和音效挂钩部分if (sessionId < 0) {throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);}int[] session = new int[1];session[0] = sessionId;// 调用native的函数进行初始化 数据加载模式被传到jni了int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,mSampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,mNativeBufferSizeInBytes, mDataLoadMode, session);if (initResult != SUCCESS) {return; // with mState == STATE_UNINITIALIZED}mSessionId = session[0];// 数据加载模式 针对已经native出来之后状态的设置。非static的,设置成STATE_INITIALIZEDif (mDataLoadMode == MODE_STATIC) {mState = STATE_NO_STATIC_DATA;} else {mState = STATE_INITIALIZED;}
}
上面基本上大部分都在核对参数是否合理,然后调用native的函数进行初始化。
接下来我们来看下native的初始化函数:
static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,jobject jaa,jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {// jaa是AudioAttributes对象,这个对象不允许为空if (jaa == 0) {return (jint) AUDIO_JAVA_ERROR;}// Invalid channel representations are caught by !audio_is_output_channel() below.// 将java的channelmask映射成native的ChannelMask。audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(channelPositionMask, channelIndexMask);if (!audio_is_output_channel(nativeChannelMask)) {return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;}// 换算出channelCountuint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);// 检查formataudio_format_t format = audioFormatToNative(audioFormat);if (format == AUDIO_FORMAT_INVALID) {return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;}// 计算frame countsize_t frameCount;if (audio_is_linear_pcm(format)) {const size_t bytesPerSample = audio_bytes_per_sample(format);frameCount = buffSizeInBytes / (channelCount * bytesPerSample);} else {frameCount = buffSizeInBytes;}jclass clazz = env->GetObjectClass(thiz);if (clazz == NULL) {return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;}// session数组不能为空if (jSession == NULL) {return (jint) AUDIO_JAVA_ERROR;}// session中不能一个对象都没有。jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);if (nSession == NULL) {return (jint) AUDIO_JAVA_ERROR;}int sessionId = nSession[0];env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);nSession = NULL;// 创建native版本的audiotracksp<AudioTrack> lpTrack = new AudioTrack();audio_attributes_t *paa = NULL;// 初始化native的attribute。接下来基本上是从java的attribute搬到native的attribute中来。paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));const jstring jtags =(jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);const char* tags = env->GetStringUTFChars(jtags, NULL);// copying array size -1, char array for tags was calloc'd, no need to NULL-terminate itstrncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);env->ReleaseStringUTFChars(jtags, tags);paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);paa->content_type =(audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);// initialize the callback information:// this data will be passed with every AudioTrack callback// 初始化callback信息,这个数据将被传递通过audiotrack的callbackAudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);// 这边使用弱引用,这样audiotrack才能被垃圾回收给回收。lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);lpJniStorage->mCallbackData.busy = false;// 初始化native的audiotrackstatus_t status = NO_ERROR;switch (memoryMode) {// 加载模式是streamcase MODE_STREAM:status = lpTrack->set(AUDIO_STREAM_DEFAULT,// stream type, 更多的信息会通过paa来传递。sampleRateInHertz,format,// word length, PCMnativeChannelMask,frameCount,AUDIO_OUTPUT_FLAG_NONE,audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)0,// notificationFrames == 0 因为没有使用EVENT_MORE_DATA来给audiotrack填充数据0,// shared memtrue,// thread can call JavasessionId,// audio session IDAudioTrack::TRANSFER_SYNC,NULL, // default offloadInfo-1, -1, // default uid, pid valuespaa);break;// 加载模式为staticcase MODE_STATIC:// AudioTrack使用shared memory// 这部分 我们后面在另作分析if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {goto native_init_failure;}status = lpTrack->set(AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)sampleRateInHertz,format,// word length, PCMnativeChannelMask,frameCount,AUDIO_OUTPUT_FLAG_NONE,audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTracklpJniStorage->mMemBase,// shared memtrue,// thread can call JavasessionId,// audio session IDAudioTrack::TRANSFER_SHARED,NULL, // default offloadInfo-1, -1, // default uid, pid valuespaa);break;default:goto native_init_failure;}if (status != NO_ERROR) {goto native_init_failure;}nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);if (nSession == NULL) {goto native_init_failure;}// 从audiotrack那边读取sessionid,防止我们创建了个新的sessionnSession[0] = lpTrack->getSessionId();env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);nSession = NULL;{ // scope for the lockMutex::Autolock l(sLock);// 将这次的lpJniStorage->mCallbackData添加到全局的sAudioTrackCallBackCookies数组中sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);}// 将native的audiotrack赋值被java的audiotrack类中的变量mNativeTrackInJavaObjsetAudioTrack(env, thiz, lpTrack);// save the JNI resources so we can free them later// 保存jni的资源,这样我们后面好释放他们。env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);// 既然我们已经有了audio attributes,stream type是来自于它们,我们将这个值保存回java。env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());// audio attributes 已经没用了 释放掉。free(paa);paa = NULL;return (jint) AUDIO_JAVA_SUCCESS;// 创建失败的释放流程。
native_init_failure:if (paa != NULL) {free(paa);}if (nSession != NULL) {env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);}env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);delete lpJniStorage;env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);// lpTrack goes out of scope, so reference count drops to zeroreturn (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}
该函数主要做了几件事:
- 类型检测、转换,计算framecount,callback信息的记录
- 创建native的audiotrack
- 设置audiotrack
接下来我们来看audiotrack的设置函数:AudioTrack::set
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,uint32_t notificationFrames, // 这个为0const sp<IMemory>& sharedBuffer, // 这个为0bool threadCanCallJava,int sessionId,transfer_type transferType,//TRANSFER_SYNC const audio_offload_info_t *offloadInfo,int uid,pid_t pid,const audio_attributes_t* pAttributes,bool doNotReconnect)
{switch (transferType) {case TRANSFER_DEFAULT:if (sharedBuffer != 0) {transferType = TRANSFER_SHARED;} else if (cbf == NULL || threadCanCallJava) {transferType = TRANSFER_SYNC;} else {transferType = TRANSFER_CALLBACK;}break;case TRANSFER_CALLBACK:if (cbf == NULL || sharedBuffer != 0) {return BAD_VALUE;}break;case TRANSFER_OBTAIN:case TRANSFER_SYNC:// 要求sharedBuffer必须为0if (sharedBuffer != 0) {return BAD_VALUE;}break;case TRANSFER_SHARED:if (sharedBuffer == 0) {return BAD_VALUE;}break;default:return BAD_VALUE;}mSharedBuffer = sharedBuffer;mTransfer = transferType;mDoNotReconnect = doNotReconnect;// mAudioTrack != 0为true,只有在成功跑完set之后才会成立。if (mAudioTrack != 0) {return INVALID_OPERATION;}// handle default values first.if (streamType == AUDIO_STREAM_DEFAULT) {streamType = AUDIO_STREAM_MUSIC;}if (pAttributes == NULL) {if (uint32_t(streamType) >= AUDIO_STREAM_PUBLIC_CNT) {return BAD_VALUE;}mStreamType = streamType;} else {// 拷贝属性,并且设置mStreamType为默认值,stream type不用去看,因为有audio attributememcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));mStreamType = AUDIO_STREAM_DEFAULT;if ((mAttributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);}}// these below should probably come from the audioFlinger too...// 下面的这些应该可能来自AudioFlinger。if (format == AUDIO_FORMAT_DEFAULT) {format = AUDIO_FORMAT_PCM_16_BIT;}// validate parametersif (!audio_is_valid_format(format)) {return BAD_VALUE;}mFormat = format;if (!audio_is_output_channel(channelMask)) {return BAD_VALUE;}mChannelMask = channelMask;uint32_t channelCount = audio_channel_count_from_out_mask(channelMask);mChannelCount = channelCount;// 如果format不是一个线性PCM或者要求是offload的话,则强制打上direct flag。if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)|| !audio_is_linear_pcm(format)) {flags = (audio_output_flags_t)// fast跟direct不能共存。fast在mixer线程,direct在direct线程((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST);}// 如果要求音视频同步,也强制打上direct flagif ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);}if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {// 如果是含有direct flag,并且是线性pcm的话,我们需要根据fomat和channelCount来计算帧大小// 否则,如果不是线性pcm的话,那帧大小设置为1个字节,8位。if (audio_is_linear_pcm(format)) {mFrameSize = channelCount * audio_bytes_per_sample(format);} else {mFrameSize = sizeof(uint8_t);}} else {// 非direct的话,那计算就按照线性PCM的方式计算。ALOG_ASSERT(audio_is_linear_pcm(format));mFrameSize = channelCount * audio_bytes_per_sample(format);// 如果PCM format不被AudioFlinger所支持的话,则会在createTrack中返回一个错误,// 所以我们这边不需要特意去检测format。}// 对于带有direct flag的track,需要指定samplingRate。if (sampleRate == 0 && (flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {return BAD_VALUE;}mSampleRate = sampleRate;mOriginalSampleRate = sampleRate;mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;// Make copy of input parameter offloadInfo so that in the future:// (a) createTrack_l doesn't need it as an input parameter// (b) we can support re-creation of offloaded tracksif (offloadInfo != NULL) {mOffloadInfoCopy = *offloadInfo;mOffloadInfo = &mOffloadInfoCopy;} else {mOffloadInfo = NULL;}mVolume[AUDIO_INTERLEAVE_LEFT] = 1.0f;mVolume[AUDIO_INTERLEAVE_RIGHT] = 1.0f;mSendLevel = 0.0f;// mFrameCount在createTrack_l中被初始化mReqFrameCount = frameCount;mNotificationFramesReq = notificationFrames;mNotificationFramesAct = 0;// sessionId如果为0,则表示需要系统帮忙分配。if (sessionId == AUDIO_SESSION_ALLOCATE) {mSessionId = AudioSystem::newAudioUniqueId();} else {mSessionId = sessionId;}int callingpid = IPCThreadState::self()->getCallingPid();int mypid = getpid();if (uid == -1 || (callingpid != mypid)) {mClientUid = IPCThreadState::self()->getCallingUid();} else {mClientUid = uid;}if (pid == -1 || (callingpid != mypid)) {mClientPid = callingpid;} else {mClientPid = pid;}mAuxEffectId = 0;mFlags = flags;mCbf = cbf;// java调用下来的,这边cbf是不为null的。所以java创建的audiotrack都是会创建AudioTrackThread的if (cbf != NULL) {mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);// 线程一开始会被paused状态所暂停,直到我们调用了start函数}// 创建binder IAudioTrackstatus_t status = createTrack_l();// 如果出现问题,则销毁mAudioTrackThreadif (status != NO_ERROR) {if (mAudioTrackThread != 0) {mAudioTrackThread->requestExit(); // see comment in AudioTrack.hmAudioTrackThread->requestExitAndWait();mAudioTrackThread.clear();}return status;}mStatus = NO_ERROR;mState = STATE_STOPPED;mUserData = user;mLoopCount = 0;mLoopStart = 0;mLoopEnd = 0;mLoopCountNotified = 0;mMarkerPosition = 0;mMarkerReached = false;mNewPosition = 0;mUpdatePeriod = 0;mPosition = 0;mReleased = 0;mStartUs = 0;// 这个函数会在AudioFlinger中创建AudioSessionRef对象,并且进行保存。AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);mSequence = 1;mObservedSequence = mSequence;mInUnderrun = false;mPreviousTimestampValid = false;mTimestampStartupGlitchReported = false;mRetrogradeMotionReported = false;return NO_ERROR;
}
上面的函数主要干了几件事:
- 对参数的转换和检验,主要是对flag的特殊处理,并且保存了一些数据
- audiotrack的回调线程的创建和运行
- 创建IAudioTrack,这是个binder对象,这个对象是audiotrack在AudioFlinger的代理
- acquireAudioSessionId的调用,会在AudioFlinger中对sessionid进行记录。
我们来看下关键性函数:createTrack_l
code snippet widget
上面函数主要干了几件事:
- 通过创建的audiotrack的attribute来获取对应的output thread。
- 针对fast track进行过滤,并且构造track的flag
- 接下来让AudioFlinger去创建IAudioTrack对象,由于server端也需要检测track类型,所以返回后需要更新audiotrack这边的flag的信息。
- 通知proxy什么时候可以通知audiotrack填充数据。
AudioPolicyManager::getOutputForAttr()这个函数的实现跟我们上面加过的getOutput差不多,只是这边传递attribute来获取,并且这次获取的,跟我们本次申请的是一致的。
我们来看AudioFlinger的createTrack的实现
sp<IAudioTrack> AudioFlinger::createTrack(audio_stream_type_t streamType,uint32_t sampleRate,audio_format_t format,audio_channel_mask_t channelMask,size_t *frameCount,IAudioFlinger::track_flags_t *flags,const sp<IMemory>& sharedBuffer,audio_io_handle_t output,// 这个是output的索引pid_t tid,int *sessionId,int clientUid,status_t *status)
{sp<PlaybackThread::Track> track;sp<TrackHandle> trackHandle;sp<Client> client;status_t lStatus;int lSessionId;// client AudioTrack::set already implements AUDIO_STREAM_DEFAULT => AUDIO_STREAM_MUSIC,// but if someone uses binder directly they could bypass that and cause us to crashif (uint32_t(streamType) >= AUDIO_STREAM_CNT) {lStatus = BAD_VALUE;goto Exit;}// further sample rate checks are performed by createTrack_l() depending on the thread typeif (sampleRate == 0) {lStatus = BAD_VALUE;goto Exit;}// further channel mask checks are performed by createTrack_l() depending on the thread typeif (!audio_is_output_channel(channelMask)) {lStatus = BAD_VALUE;goto Exit;}// further format checks are performed by createTrack_l() depending on the thread typeif (!audio_is_valid_format(format)) {lStatus = BAD_VALUE;goto Exit;}if (sharedBuffer != 0 && sharedBuffer->pointer() == NULL) {lStatus = BAD_VALUE;goto Exit;}{Mutex::Autolock _l(mLock);// 根据output索引获取到output thread对象PlaybackThread *thread = checkPlaybackThread_l(output);if (thread == NULL) {lStatus = BAD_VALUE;goto Exit;}// 这边又注册了一次client。pid_t pid = IPCThreadState::self()->getCallingPid();client = registerPid(pid);PlaybackThread *effectThread = NULL;if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) {lSessionId = *sessionId;// 检测是否有一个相同session id的效果链在另外一个output thread,有的话则移过来。for (size_t i = 0; i < mPlaybackThreads.size(); i++) {sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);if (mPlaybackThreads.keyAt(i) != output) {uint32_t sessions = t->hasAudioSession(lSessionId);if (sessions & PlaybackThread::EFFECT_SESSION) {effectThread = t.get();break;}}}} else {// sessionid没有指定,则给自动分配一个lSessionId = nextUniqueId();if (sessionId != NULL) {*sessionId = lSessionId;}}// 让对应的output thread去创建tracktrack = thread->createTrack_l(client, streamType, sampleRate, format,channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));// we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless// move effect chain to this output thread if an effect on same session was waiting// for a track to be created// 如果track被创建成功,并且存在effectThread的话,我们就将效果给移过来。if (lStatus == NO_ERROR && effectThread != NULL) {// no risk of deadlock because AudioFlinger::mLock is heldMutex::Autolock _dl(thread->mLock);Mutex::Autolock _sl(effectThread->mLock);moveEffectChain_l(lSessionId, effectThread, thread, true);}// Look for sync events awaiting for a session to be used.// 这部分不太清楚作用,后面再看看for (size_t i = 0; i < mPendingSyncEvents.size(); i++) {if (mPendingSyncEvents[i]->triggerSession() == lSessionId) {if (thread->isValidSyncEvent(mPendingSyncEvents[i])) {if (lStatus == NO_ERROR) {(void) track->setSyncEvent(mPendingSyncEvents[i]);} else {mPendingSyncEvents[i]->cancel();}mPendingSyncEvents.removeAt(i);i--;}}}setAudioHwSyncForSession_l(thread, (audio_session_t)lSessionId);}if (lStatus != NO_ERROR) {// remove local strong reference to Client before deleting the Track so that the// Client destructor is called by the TrackBase destructor with mClientLock held// Don't hold mClientLock when releasing the reference on the track as the// destructor will acquire it.{Mutex::Autolock _cl(mClientLock);client.clear();}track.clear();goto Exit;}// return handle to client// 封装一个binder对象给audiotrack.trackHandle = new TrackHandle(track);Exit:*status = lStatus;return trackHandle;
}
上面函数主要也是对一些参数的检测,以及确认是否有效果需要转移过来,如果有则转移.
这边又对pid注册了一个client在AudioFlinger。
但是我们主体逻辑还是在output thread去createTrack_l的。
code snippet widget
上面函数主要做了几件事:
- 确认fast是否符合server端的要求
- 重新计算framecount
- output的类型判断是否符合要求
- 创建track的实例
- 效果方面的设置
- 如果是fast的话,则申请调度权限
我们重点是来看Track的创建了。
这个我们要分两部分来看,先看Track的基类TrackBase的构造函数:
AudioFlinger::ThreadBase::TrackBase::TrackBase(ThreadBase *thread,const sp<Client>& client,// 这个是AudioFlinger那边给构造的clientuint32_t sampleRate,audio_format_t format,audio_channel_mask_t channelMask,size_t frameCount,void *buffer,int sessionId,int clientUid,IAudioFlinger::track_flags_t flags,bool isOut,alloc_type alloc,track_type type): RefBase(),mThread(thread),mClient(client),mCblk(NULL),// mBuffermState(IDLE),//Track的初始状态为IDLEmSampleRate(sampleRate),mFormat(format),mChannelMask(channelMask),mChannelCount(isOut ?audio_channel_count_from_out_mask(channelMask) :audio_channel_count_from_in_mask(channelMask)),// mFrameSize,如果是线性pcm的话,则用通道数*采样精度。mFrameSize(audio_is_linear_pcm(format) ?mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),mFrameCount(frameCount),mSessionId(sessionId),mFlags(flags),mIsOut(isOut),mServerProxy(NULL),mId(android_atomic_inc(&nextTrackId)),mTerminated(false),mType(type),mThreadIoHandle(thread->id())
{// if the caller is us, trust the specified uidif (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {int newclientUid = IPCThreadState::self()->getCallingUid();if (clientUid != -1 && clientUid != newclientUid) {ALOGW("uid %d tried to pass itself off as %d", newclientUid, clientUid);}clientUid = newclientUid;}// clientUid contains the uid of the app that is responsible for this track, so we can blame// battery usage on it.// clientUid包含app的uid,是这个track的owner,所以对于耗电量我们可以来找它。mUid = clientUid;size_t size = sizeof(audio_track_cblk_t);// 如果buffer是为空,则roundup(frameCount),否则就自带的// roundup(frameCount)就是的多一位2进制位。thread创建track的时候,传递进来的buffer为NULLsize_t bufferSize = (buffer == NULL ? roundup(frameCount) : frameCount) * mFrameSize;if (buffer == NULL && alloc == ALLOC_CBLK) {size += bufferSize;}if (client != 0) {// 这边采用到client进行空间的分配,后面针对这点在分析下mCblkMemory = client->heap()->allocate(size);if (mCblkMemory == 0 ||// 注意这边是有mCblk的赋值操作。(mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) {client->heap()->dump("AudioTrack");mCblkMemory.clear();return;}} else {// 如果client为0的话,则走这个分支,这种分配空间方式,可以免去调用结构体的构造函数。mCblk = (audio_track_cblk_t *) new uint8_t[size];// assume mCblk != NULL}// construct the shared structure in-place.if (mCblk != NULL) {// 在指定的空间,调用audio_track_cblk_t的构造函数。new(mCblk) audio_track_cblk_t();switch (alloc) {case ALLOC_READONLY: {const sp<MemoryDealer> roHeap(thread->readOnlyHeap());if (roHeap == 0 ||(mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||(mBuffer = mBufferMemory->pointer()) == NULL) {ALOGE("not enough memory for read-only buffer size=%zu", bufferSize);if (roHeap != 0) {roHeap->dump("buffer");}mCblkMemory.clear();mBufferMemory.clear();return;}memset(mBuffer, 0, bufferSize);} break;case ALLOC_PIPE:mBufferMemory = thread->pipeMemory();// mBuffer is the virtual address as seen from current process (mediaserver),// and should normally be coming from mBufferMemory->pointer().// However in this case the TrackBase does not reference the buffer directly.// It should references the buffer via the pipe.// Therefore, to detect incorrect usage of the buffer, we set mBuffer to NULL.mBuffer = NULL;break;case ALLOC_CBLK:// clear all buffersif (buffer == NULL) {mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);memset(mBuffer, 0, bufferSize);} else {mBuffer = buffer;
#if 0mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic
#endif}break;case ALLOC_LOCAL:mBuffer = calloc(1, bufferSize);break;case ALLOC_NONE:mBuffer = buffer;break;}#ifdef TEE_SINKif (mTeeSinkTrackEnabled) {NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount, mFormat);if (Format_isValid(pipeFormat)) {Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat);size_t numCounterOffers = 0;const NBAIO_Format offers[1] = {pipeFormat};ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);ALOG_ASSERT(index == 0);PipeReader *pipeReader = new PipeReader(*pipe);numCounterOffers = 0;index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);ALOG_ASSERT(index == 0);mTeeSink = pipe;mTeeSource = pipeReader;}}
#endif}
}
这个函数主要是分配了buffer,并且初始化的cblk这个重要的管理结构体。需要注意的是,buffer的大小,并不是framecount*framesize,如果传进来的buffer是null的话,是有进行roundup的操作的,否则是原值(比如说sharebuffer的情况)。
接下来要看track的构造函数了:
AudioFlinger::PlaybackThread::Track::Track(PlaybackThread *thread,const sp<Client>& client,audio_stream_type_t streamType,uint32_t sampleRate,audio_format_t format,audio_channel_mask_t channelMask,size_t frameCount,void *buffer,const sp<IMemory>& sharedBuffer,int sessionId,int uid,IAudioFlinger::track_flags_t flags,track_type type): TrackBase(thread, client, sampleRate, format, channelMask, frameCount,(sharedBuffer != 0) ? sharedBuffer->pointer() : buffer,sessionId, uid, flags, true /*isOut*/,// 分配空间的方式,如果类型是patch,则选第一种方案,否则选择第二种方案// ALLOC_CBLK,第一种方案还看buffer是否为NULL,为空的话就选择ALLOC_LOCAL,// 否则为ALLOC_NONE(type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,type),mFillingUpStatus(FS_INVALID),// mRetryCount initialized later when neededmSharedBuffer(sharedBuffer),mStreamType(streamType),mName(-1), // see note belowmMainBuffer(thread->mixBuffer()),// 如果有效果链的话,会被替换,目前还是指向线程的mixBuffermAuxBuffer(NULL),mAuxEffectId(0), mHasVolumeController(false),mPresentationCompleteFrames(0),mFastIndex(-1),mCachedVolume(1.0),mIsInvalid(false),mAudioTrackServerProxy(NULL),mResumeToStopping(false),mFlushHwPending(false)
{// client == 0 意味着必须 sharedBuffer == 0// 但是目前位置client是不为0的,所以sharebuffer就随意了。ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(),sharedBuffer->size());if (mCblk == NULL) {return;}if (sharedBuffer == 0) {// 创建AudioTrack server端的代理,流模式的。mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize, !isExternalTrack(), sampleRate);} else {// 创建静态的AudioTrack server端的代理。mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize);}mServerProxy = mAudioTrackServerProxy;mName = thread->getTrackName_l(channelMask, format, sessionId);if (mName < 0) {ALOGE("no more track names available");return;}// only allocate a fast track index if we were able to allocate a normal track nameif (flags & IAudioFlinger::TRACK_FAST) {// 设置mFramesReadyIsCalledByMultipleThreads变量为truemAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();ALOG_ASSERT(thread->mFastTrackAvailMask != 0);// 获取从左往右第一个1的indexint i = __builtin_ctz(thread->mFastTrackAvailMask);ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks);// 既然fast track是稀缺资源,我们在fast track被激活前就给分配了,这样会把后面更重要的fast// track给拒之门外。// 动态的创建将会是更好的方案。 所以亟待解决。。。mFastIndex = i;thread->mFastTrackAvailMask &= ~(1 << i);}
}
这边完成创建了server端的proxy了,刚才我们在看audiotrack那边的创建的时候也创建了自己的代理。
总结创建流
把所有的代码贴出来,主要是为了看到整个创建的细节,并不是为了单纯完成对流程的梳理,创建流程可以看下图:
补充
这边我们再补充下AudioTrack那边创建的线程现在的状态:
AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava): Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL),mIgnoreNextPausedInt(false)
{
}bool AudioTrack::AudioTrackThread::threadLoop()
{{AutoMutex _l(mMyLock);// 从构造函数中,我们看到了mPaused一开始被设置为true,所以线程刚跑起来是在这边等待的。if (mPaused) {mMyCond.wait(mMyLock);// caller will check for exitPending()return true;}if (mIgnoreNextPausedInt) {mIgnoreNextPausedInt = false;mPausedInt = false;}if (mPausedInt) {if (mPausedNs > 0) {(void) mMyCond.waitRelative(mMyLock, mPausedNs);} else {mMyCond.wait(mMyLock);}mPausedInt = false;return true;}}if (exitPending()) {return false;}nsecs_t ns = mReceiver.processAudioBuffer();switch (ns) {case 0:return true;case NS_INACTIVE:pauseInternal();return true;case NS_NEVER:return false;case NS_WHENEVER:// Event driven: call wake() when callback notifications conditions change.ns = INT64_MAX;// fall throughdefault:LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %" PRId64, ns);pauseInternal(ns);return true;}
}
由于就算不是采用callback的形式,但是一旦是从java层创建的audiotrack都是必然会产生该线程的,并且会进入run的状态,但是由于一开始是会被pause住的,pause的取消是在后面audiotrack的start函数被触发。
我们来看jni帮java提供的回调函数的实现吧:
static void audioCallback(int event, void* user, void *info) {audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;{Mutex::Autolock l(sLock);if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {return;}callbackInfo->busy = true;}switch (event) {case AudioTrack::EVENT_MARKER: {JNIEnv *env = AndroidRuntime::getJNIEnv();if (user != NULL && env != NULL) {env->CallStaticVoidMethod(callbackInfo->audioTrack_class,javaAudioTrackFields.postNativeEventInJava,callbackInfo->audioTrack_ref, event, 0,0, NULL);if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();}}} break;case AudioTrack::EVENT_NEW_POS: {JNIEnv *env = AndroidRuntime::getJNIEnv();if (user != NULL && env != NULL) {env->CallStaticVoidMethod(callbackInfo->audioTrack_class,javaAudioTrackFields.postNativeEventInJava,callbackInfo->audioTrack_ref, event, 0,0, NULL);if (env->ExceptionCheck()) {env->ExceptionDescribe();env->ExceptionClear();}}} break;}{Mutex::Autolock l(sLock);callbackInfo->busy = false;callbackInfo->cond.broadcast();}
}
可以看出来只是提供了两个事件的回调,一个是EVENT_MARKER一个是EVENT_NEW_POS一个是设置警戒线,一个是设置提示的帧的位置。而触发回调audiotrack的processAudioBuffer中,那我们可以猜想,java下来的audiotrack的话,该线程是不需要跑的,除非需要知道marker和new pos,否则可以一直进入sleep状态。
那如果上层设置了,但是我们并不是采用callback的形式提供数据,那线程每次执行一次的processAudioBuffer就会触发一次数据的更新,这个时候就可以反馈marker和new pos了。