Audio系统是Android平台重要组成部分,主要包括三方面内容:
相关概念缩写如下:
AudioTrack简写为AT, AudioFlinger简称AF, AudioPolicyService简称AP, AudioRecorder简称AR。
Java层放音API AudioTrack的使用流程如下:
//(1) 根据音频数据来确定所要分配的缓冲区最小size
int bufsize = AudioTrack.getMinBufferSize(8000,//采样率,每秒8000个采样点
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
AudioFormat.ENCODING_PCM_16BIT); //采样精度,一个采样点16bit,2个字节
//(2)创建AudioTrack
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
8000,//采样率8000
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
AudioFormat.ENCODING_PCM_16BIT,
bufsize,
AudioTrack.MODE_STREAM); //数据加载模式,流式
//(3)开始播放
mAudioTrack.play();
......
//(4) 不断写入音频数据byte[] 到Track中
mAudioTrack.write(audio_data,0,packet.data_len);
//(5) 停止播放和释放资源
track.stop(); //停止播放
track.release(); //释放底层资源
上面的示例代码中涉及两个概念: 数据加载模式、音频流类型。
MODE_STREAM
和 MODE_STACTIC
write
函数多次循环写入 Audio Data到AudioTrack中。这个过程涉及数据从用户提供的Buffer 拷贝到AudioTrack内部Buffer,有一定的延迟。在AudioTrack构造函数中使用到了 AudioManager.STREAM_MUSIC这个参数。该参数与Audio系统对音频流管理和分类有关。
常见的音频流类型有以下几种:
值得注意的是 上面这些类型的划分与音频数据量的多少没有关系。例如MUSIC和RING类型都可以是某首MP3歌曲。同样铃声预览中铃声的流类型也可以设置为MUSIC类型。
音频流类型划分和Audio系统对音频策略管理有关。
具体代码体现如下
源码位置 hardware/libhardware_legacy/AudioPolicyManagerBase.cpp
AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(
AudioSystem::stream_type stream) {
// stream to strategy mapping
switch (stream) {
case AudioSystem::VOICE_CALL:
case AudioSystem::BLUETOOTH_SCO:
return STRATEGY_PHONE;
case AudioSystem::RING:
case AudioSystem::ALARM:
return STRATEGY_SONIFICATION;
case AudioSystem::NOTIFICATION:
return STRATEGY_SONIFICATION_RESPECTFUL;
case AudioSystem::DTMF:
return STRATEGY_DTMF;
default:
ALOGE("unknown stream type");
case AudioSystem::SYSTEM:
// NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
// while key clicks are played produces a poor result
case AudioSystem::TTS:
case AudioSystem::MUSIC:
return STRATEGY_MEDIA;
case AudioSystem::ENFORCED_AUDIBLE:
return STRATEGY_ENFORCED_AUDIBLE;
}
}
getMinBufferSize
函数调用时,碰到和Frame的概念,那么音频Frame(帧率)的概念是怎么定义的呢?Frame在ALSA系统中是一个单位,被用来描述一个采样点音频数据量的多少,例如一帧等于多少字节呢?1单位Frame等于1个采样点的字节数 X 声道数(比如PCM 16,双声道的1个Frame等于2x2=4字节)。另外,在目前声卡驱动程序中,内部缓冲区也是采用Frame作为单位来分配和管理的。
总之,getMinBufSize
最后会综合考虑采样率、硬件延迟等情况,得出一个最小缓冲区大小。而我们分配的缓冲区大小会是它的整数倍。
在AudioTrack使用过程,第一步就是调用getMinBufferSize
函数获取最小缓冲内存,下面分析下其调用流程:
getMinBufferSize
函数是AudioTrack的静态函数,下面看其实现
[–> AudioTrack.java]
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
//(1) 根据channelConfig配置获取声音通道
int channelCount = 0;
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:
//声音通道合法性判定
if (!isMultichannelConfigSupported(channelConfig)) {
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
} else {
channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
}
}
//(2) 音频格式合法性判定
if (!AudioFormat.isPublicEncoding(audioFormat)) {
loge("getMinBufferSize(): Invalid audio format.");
return ERROR_BAD_VALUE;
}
// sample rate, note these values are subject to change
// Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed
//(3) 采样率合法性判定
if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
(sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {
loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");
return ERROR_BAD_VALUE;
}
//(4) 调用JNI方法获取最小BufferSize, 这里之所以要进入native空间是因为minBufferSize的计算还需要确认
//硬件是否支持这些参数
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if (size <= 0) {
loge("getMinBufferSize(): error querying hardware");
return ERROR;
}
else {
return size;
}
}
继续追踪,native_get_min_buff_size
方法的实现定义在AudioTrack.java对应的JNI实现类android_media_AudioTrack.cpp
中
[–> AudioTrack.java]
//JNI方法注册表
static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
......
{"native_get_min_buff_size",
"(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
......
}
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
jint sampleRateInHertz, jint channelCount, jint audioFormat) {
size_t frameCount;
//调用AudioTrack的getMinFrameCount方法, 这里是以帧为单位的
const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
sampleRateInHertz);
if (status != NO_ERROR) {
ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
sampleRateInHertz, status);
return -1;
}
//将Java层传递过来的audioFormat转成Native的AudioFormat
const audio_format_t format = audioFormatToNative(audioFormat);
//判定格式是否是PCM的,不需要做混合和Scale
if (audio_has_proportional_frames(format)) {
//计算每个采样点有多少bytes, 所以最终结果是以byte为单位的
const size_t bytesPerSample = audio_bytes_per_sample(format);
return frameCount * channelCount * bytesPerSample;
} else {
return frameCount;
}
}
继续,JNI方法android_media_AudioTrack_get_min_buff_size
方法会调用Native层的AudioTrack::getMinFrameCount
,其实现如下:
// static
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;
//最终调用AF的sampleRate函数获取, 这个流程中会根据streamtype涉及output设备策略选择( getOutput(streamType)函数调用, 根据获取的ioHandle查找AudioIoDescriptor)
//一般返回最高采样率,例如48000Hz
status = AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);
if (status != NO_ERROR) {
ALOGE("Unable to query output sample rate for stream type %d; status %d",
streamType, status);
return status;
}
size_t afFrameCount;
//查询硬件内部缓冲区大小,以Frame为单位, 涉及output策略选择待输出的设备
//上一节章节解释了1 Frame大小 = 一个采样点字节数 x 声道数
status = AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
if (status != NO_ERROR) {
ALOGE("Unable to query output frame count for stream type %d; status %d",
streamType, status);
return status;
}
uint32_t afLatency;
查询硬件延迟时间, 涉及output策略选择待输出的设备
status = AudioSystem::getOutputLatency(&afLatency, streamType);
if (status != NO_ERROR) {
ALOGE("Unable to query output latency for stream type %d; status %d",
streamType, status);
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).
//相关实现在AudioSystem::calculateMinFrameCount中完成
*frameCount = AudioSystem::calculateMinFrameCount(afLatency, afFrameCount, afSampleRate,
sampleRate, 1.0f /*, 0 notificationsPerBufferReq*/);
// The formula above should always produce a non-zero value under normal circumstances:
// AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
// Return error in the unlikely event that it does not, as that's part of the API contract.
if (*frameCount == 0) {
ALOGE("AudioTrack::getMinFrameCount failed for streamType %d, sampleRate %u",
streamType, sampleRate);
return BAD_VALUE;
}
ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
*frameCount, afFrameCount, afSampleRate, afLatency);
return NO_ERROR;
}
OK, 继续看calculateMinFrameCount
的实现。
[–>AudioSystem.cpp ]
/* static */ size_t AudioSystem::calculateMinFrameCount(
uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
uint32_t sampleRate, float speed /*, uint32_t notificationsPerBufferReq*/)
{
//minBufCount表示缓冲区的最少个数,以Frame为单位
// Ensure that buffer depth covers at least audio hardware latency
uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
if (minBufCount < 2) {
minBufCount = 2;
}
#if 0
// The notificationsPerBufferReq parameter is not yet used for non-fast tracks,
// but keeping the code here to make it easier to add later.
if (minBufCount < notificationsPerBufferReq) {
minBufCount = notificationsPerBufferReq;
}
#endif
ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u "
"sampleRate %u speed %f minBufCount: %u" /*" notificationsPerBufferReq %u"*/,
afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount
/*, notificationsPerBufferReq*/);
//计算最小缓冲大小
return minBufCount * sourceFramesNeededWithTimestretch(
sampleRate, afFrameCount, afSampleRate, speed);
}
继续 sourceFramesNeededWithTimestretch
函数分析
[–>AudioResamplerPublic.h]
//上层设置的采样率和底层获取的采样率可能不一致,所以需要综合上下层的参数做合适的计算
// Returns the source frames needed to resample to destination frames. This is not a precise
// value and depends on the resampler (and possibly how it handles rounding internally).
// Nevertheless, this should be an upper bound on the requirements of the resampler.
// If srcSampleRate and dstSampleRate are equal, then it returns destination frames, which
// may not be true if the resampler is asynchronous.
static inline size_t sourceFramesNeeded(
uint32_t srcSampleRate/*App传递过来的*/, size_t dstFramesRequired/*底层需要的Frames*/, uint32_t dstSampleRate/*底层读取出来的*/) {
// +1 舍入 - 即使匹配比率也要这样做(重采样器可能使用相位而不是比率)
// +1 插值所需的额外样本字节
return srcSampleRate == dstSampleRate ? dstFramesRequired :
size_t((uint64_t)dstFramesRequired * srcSampleRate / dstSampleRate + 1 + 1);
}
static inline size_t sourceFramesNeededWithTimestretch(
uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate,
float speed) {
// required is the number of input frames the resampler needs
size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate);
// to deliver this, the time stretcher requires:
return required * (double)speed + 1 + 1; // accounting for rounding dependencies
}
[–>AudioTrack.java]
AudioTrack有多个构造函数,android 9中已经建议使用Builder模式构造AndioTrack对象。
构造函数如下:
//(1)deprecated
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode, int sessionId)
//(2)attributes和format都是非空
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId)
//(3) Builder模式构造
public @NonNull AudioTrack build()
使用Builder模式构造AudioTrack对象
AudioTrack player = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ALARM)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(44100)
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
.build())
.setBufferSizeInBytes(minBuffSize)
.build();
无论采用哪种构造函数, 最终都会调用下面私有构造函数
private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId, boolean offload)
throws IllegalArgumentException {
super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
// mState already == STATE_UNINITIALIZED
if (format == null) {
throw new IllegalArgumentException("Illegal null AudioFormat");
}
// Check if we should enable deep buffer mode
if (shouldEnablePowerSaving(mAttributes, format, bufferSizeInBytes, mode)) {
mAttributes = new AudioAttributes.Builder(mAttributes)
.replaceFlags((mAttributes.getAllFlags()
| AudioAttributes.FLAG_DEEP_BUFFER)
& ~AudioAttributes.FLAG_LOW_LATENCY)
.build();
}
// remember which looper is associated with the AudioTrack instantiation
Looper looper;
if ((looper = Looper.myLooper()) == null) {
looper = Looper.getMainLooper();
}
int rate = format.getSampleRate();
if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
rate = 0;
}
int channelIndexMask = 0;
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 stereo
channelMask = 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);
mStreamType = AudioSystem.STREAM_DEFAULT;
//检查audioBuffer Size是否合法
audioBuffSizeCheck(bufferSizeInBytes);
mInitializationLooper = looper;
if (sessionId < 0) {
throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
}
int[] sampleRate = new int[] {mSampleRate};
int[] session = new int[1];
session[0] = sessionId;
// native initialization
//调用了native层的native_setup,使用弱引用传递进去,确保可以被垃圾回收器回收
int initResult = native_setup(new WeakReference<AudioTrack>(this),
mAttributes, //属性
sampleRate, //例如8000
mChannelMask, //通道掩码,查看相关定义
mChannelIndexMask, //声道索引掩码
mAudioFormat, //例如AudioFormat.ENCODING_PCM_16BIT
mNativeBufferSizeInBytes, //等于bufferSizeInBytes
mDataLoadMode, //DataLoadMode是MODE_STREAM
session, //会话Id
0 /*nativeTrackInJavaObj*/,
offload);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
}
mSampleRate = sampleRate[0];
mSessionId = session[0];
if ((mAttributes.getFlags() & AudioAttributes.FLAG_HW_AV_SYNC) != 0) {
int frameSizeInBytes;
if (AudioFormat.isEncodingLinearFrames(mAudioFormat)) {
frameSizeInBytes = mChannelCount * AudioFormat.getBytesPerSample(mAudioFormat);
} else {
frameSizeInBytes = 1;
}
//带有头信息,需要指定偏移量
mOffset = ((int) Math.ceil(HEADER_V2_SIZE_BYTES / frameSizeInBytes)) * frameSizeInBytes;
}
if (mDataLoadMode == MODE_STATIC) {
mState = STATE_NO_STATIC_DATA;
} else {
mState = STATE_INITIALIZED;
}
baseRegisterPlayer();
}
继续跟踪,native_setup
对应的JNI函数是android_media_AudioTrack_setup
, 那么我们来看看其实现
[–>android_media_AudioTrack.cpp ]
static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
jlong nativeAudioTrack, jboolean offload) {
ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
"nativeAudioTrack=0x%" PRIX64,
jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
nativeAudioTrack);
sp<AudioTrack> lpTrack = 0;
// session Id合法性判定
if (jSession == NULL) {
ALOGE("Error creating AudioTrack: invalid session ID pointer");
return (jint) AUDIO_JAVA_ERROR;
}
jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
if (nSession == NULL) {
ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
return (jint) AUDIO_JAVA_ERROR;
}
audio_session_t sessionId = (audio_session_t) nSession[0];
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
AudioTrackJniStorage* lpJniStorage = NULL;
audio_attributes_t *paa = NULL;
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find %s when setting up callback.", kClassPathName);
return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}
//android 9中新增了可以传递nativeAudioTrack的构造函数
// if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
if (nativeAudioTrack == 0) {
if (jaa == 0) {
ALOGE("Error creating AudioTrack: invalid audio attributes");
return (jint) AUDIO_JAVA_ERROR;
}
//采样率合法性判断
if (jSampleRate == 0) {
ALOGE("Error creating AudioTrack: invalid sample rates");
return (jint) AUDIO_JAVA_ERROR;
}
//读取采样率
int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
int sampleRateInHertz = sampleRates[0];
env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
// Invalid channel representations are caught by !audio_is_output_channel() below.
audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
channelPositionMask, channelIndexMask);
//声道合法性判断
if (!audio_is_output_channel(nativeChannelMask)) {
ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
}
uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
// 音频格式合法性检查
// This function was called from Java, so we compare the format against the Java constants
audio_format_t format = audioFormatToNative(audioFormat);
if (format == AUDIO_FORMAT_INVALID) {
ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
}
// 计算帧数
size_t frameCount;
if (audio_has_proportional_frames(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
} else {
frameCount = buffSizeInBytes;
}
// (1) 创建 native AudioTrack 对象
lpTrack = new AudioTrack();
// 读取 AudioAttributes 的值
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 it
strncpy(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);
ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
paa->usage, paa->content_type, paa->flags, paa->tags);
//(2) 创建AudioTrackJniStorage对象,和数据回调有关
// initialize the callback information:
// this data will be passed with every AudioTrack callback
lpJniStorage = new AudioTrackJniStorage();
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
// we use a weak reference so the AudioTrack object can be garbage collected.
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
lpJniStorage->mCallbackData.isOffload = offload;
lpJniStorage->mCallbackData.busy = false;
audio_offload_info_t offloadInfo;
if (offload) {
offloadInfo = AUDIO_INFO_INITIALIZER;
offloadInfo.format = format;
offloadInfo.sample_rate = sampleRateInHertz;
offloadInfo.channel_mask = nativeChannelMask;
offloadInfo.has_video = false;
offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
}
// initialize the native AudioTrack object
status_t status = NO_ERROR;
switch (memoryMode) {
case MODE_STREAM:
//(3) STREAM模式
status = lpTrack->set(
AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
sampleRateInHertz,
format,// word length, PCM, 采样精度,一般为PCM16或PCM8
nativeChannelMask,
frameCount,
AUDIO_OUTPUT_FLAG_NONE,
//查看`static void audioCallback(int event, void* user, void *info)`
audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
0,// shared mem, 共享内存,流模式下为空,实际使用的共享内存由AF创建
true,// thread can call Java, 内部线程可以调JNI函数
sessionId,// audio session ID
AudioTrack::TRANSFER_SYNC,
offload ? &offloadInfo : NULL,
-1, -1, // default uid, pid values
paa);
break;
case MODE_STATIC:
// AudioTrack is using shared memory, 如果是static模式,使用共享内存
if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
goto native_init_failure;
}
status = lpTrack->set(
AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
sampleRateInHertz,
format,// word length, PCM
nativeChannelMask,
frameCount,
AUDIO_OUTPUT_FLAG_NONE,
audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
lpJniStorage->mMemBase,// shared mem
true,// thread can call Java
sessionId,// audio session ID
AudioTrack::TRANSFER_SHARED,
NULL, // default offloadInfo
-1, -1, // default uid, pid values
paa);
break;
default:
ALOGE("Unknown mode %d", memoryMode);
goto native_init_failure;
}
if (status != NO_ERROR) {
ALOGE("Error %d initializing AudioTrack", status);
goto native_init_failure;
}
} else { // end if (nativeAudioTrack == 0)
......
}
上面代码列出了三个要点(1)、(2)、(3) , 我们先看AudioTrackJniStorage这个类。
AudioTrackJniStorage
是一个辅助类,其涉及了共享内存相关知识。共享内存,作为进程间数据传递的一种手段,在AudioTrack和AudioFlinger中被大量使用。
看下AudioTrackJniStorage
类的构成
class AudioTrackJniStorage {
public:
sp<MemoryHeapBase> mMemHeap;
sp<MemoryBase> mMemBase;
//保存一些变量,作用不大
audiotrack_callback_cookie mCallbackData;
//设备事件回调
sp<JNIDeviceCallback> mDeviceCallback;
AudioTrackJniStorage() {
mCallbackData.audioTrack_class = 0;
mCallbackData.audioTrack_ref = 0;
mCallbackData.isOffload = false;
}
~AudioTrackJniStorage() {
mMemBase.clear();
mMemHeap.clear();
}
bool allocSharedMem(int sizeInBytes) {
//共享内存分配
mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
if (mMemHeap->getHeapID() < 0) {
return false;
}
mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
return true;
}
};
在AudioTrackJniStorage
类中,主要涉及MemoryHeapBase
和MemoryBase
, 而在Android中,这两个类是共享内存有关的, 看来AudioTrack流程中会涉及到共享内存的使用,先了解下共享内存,戳下面的链接
Android系统共享内存分析
[–> AudioTrack.cpp]
status_t AudioTrack::set(
audio_stream_type_t streamType, //STREAM_MUSIC
uint32_t sampleRate, //8000
audio_format_t format, //PCM_16
audio_channel_mask_t channelMask,
size_t frameCount, //由计算获取,这里假设为1024
audio_output_flags_t flags, //输出flags
callback_t cbf, //android_media_AudioTrack.cpp 中 `static void audioCallback`
void* user, // android_media_AudioTrack.cpp:: AudioTrackJniStorage->mCallbackData
int32_t notificationFrames,
const sp<IMemory>& sharedBuffer, //共享内存
bool threadCanCallJava,
audio_session_t sessionId,
transfer_type transferType, //stream 时是TRANSFER_SYNC, static 时TRANSFER_SHARED
const audio_offload_info_t *offloadInfo,
uid_t uid,
pid_t pid,
const audio_attributes_t* pAttributes,
bool doNotReconnect,
float maxRequiredSpeed,
audio_port_handle_t selectedDeviceId)
{
status_t status;
uint32_t channelCount;
pid_t callingPid;
pid_t myPid;
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);
mThreadCanCallJava = threadCanCallJava;
mSelectedDeviceId = selectedDeviceId;
mSessionId = sessionId;
switch (transferType) {
......
//传输模式为TRANSFER_OBTAIN和TRANSFER_SYNC 不能使用共享内存传递数据
case TRANSFER_OBTAIN:
case TRANSFER_SYNC:
if (sharedBuffer != 0) {
ALOGE("Transfer type TRANSFER_OBTAIN but sharedBuffer != 0");
status = BAD_VALUE;
goto exit;
}
break;
//传输模式为TRANSFER_SHARED时,使用共享内存传输
case TRANSFER_SHARED:
if (sharedBuffer == 0) {
ALOGE("Transfer type TRANSFER_SHARED but sharedBuffer == 0");
status = BAD_VALUE;
goto exit;
}
break;
default:
ALOGE("Invalid transfer type %d", transferType);
status = BAD_VALUE;
goto exit;
}
mSharedBuffer = sharedBuffer;
mTransfer = transferType;
mDoNotReconnect = doNotReconnect;
ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %zu", sharedBuffer->pointer(),
sharedBuffer->size());
ALOGV("set() streamType %d frameCount %zu flags %04x", streamType, frameCount, flags);
// invariant that mAudioTrack != 0 is true only after set() returns successfully
if (mAudioTrack != 0) {
ALOGE("Track already in use");
status = INVALID_OPERATION;
goto exit;
}
// 默认的STREAM TYPE 为STREAM_MUSIC
......
mStreamType = streamType;
} else {
// stream type shouldn't be looked at, this track has audio attributes
memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
ALOGV("Building AudioTrack with attributes: usage=%d content=%d flags=0x%x tags=[%s]",
mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags);
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);
}
if ((mAttributes.flags & AUDIO_FLAG_LOW_LATENCY) != 0) {
flags = (audio_output_flags_t) (flags | AUDIO_OUTPUT_FLAG_FAST);
}
// check deep buffer after flags have been modified above
if (flags == AUDIO_OUTPUT_FLAG_NONE && (mAttributes.flags & AUDIO_FLAG_DEEP_BUFFER) != 0) {
flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
}
}
// these below should probably come from the audioFlinger too...
if (format == AUDIO_FORMAT_DEFAULT) {
format = AUDIO_FORMAT_PCM_16_BIT;
} else if (format == AUDIO_FORMAT_IEC61937) { // HDMI pass-through?
mAttributes.flags |= AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO;
}
// validate parameters
if (!audio_is_valid_format(format)) {
ALOGE("Invalid format %#x", format);
status = BAD_VALUE;
goto exit;
}
mFormat = format;
if (!audio_is_output_channel(channelMask)) {
ALOGE("Invalid channel mask %#x", channelMask);
status = BAD_VALUE;
goto exit;
}
mChannelMask = channelMask;
channelCount = audio_channel_count_from_out_mask(channelMask);
mChannelCount = channelCount;
// force direct flag if format is not linear PCM
// or offload was requested
if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
|| !audio_is_linear_pcm(format)) {
ALOGV( (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
? "Offload request, forcing to Direct Output"
: "Not linear PCM, forcing to Direct Output");
flags = (audio_output_flags_t)
// FIXME why can't we allow direct AND fast?
((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST);
}
// force direct flag if HW A/V sync requested
if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);
}
if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (audio_has_proportional_frames(format)) {
mFrameSize = channelCount * audio_bytes_per_sample(format);
} else {
mFrameSize = sizeof(uint8_t);
}
} else {
ALOG_ASSERT(audio_has_proportional_frames(format));
mFrameSize = channelCount * audio_bytes_per_sample(format);
// createTrack will return an error if PCM format is not supported by server,
// so no need to check for specific PCM formats here
}
// sampling rate must be specified for direct outputs
if (sampleRate == 0 && (flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
status = BAD_VALUE;
goto exit;
}
mSampleRate = sampleRate;
mOriginalSampleRate = sampleRate;
mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// 1.0 <= mMaxRequiredSpeed <= AUDIO_TIMESTRETCH_SPEED_MAX
mMaxRequiredSpeed = min(max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
// 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 tracks
if (offloadInfo != NULL) {
mOffloadInfoCopy = *offloadInfo;
mOffloadInfo = &mOffloadInfoCopy;
} else {
mOffloadInfo = NULL;
memset(&mOffloadInfoCopy, 0, sizeof(audio_offload_info_t));
}
mVolume[AUDIO_INTERLEAVE_LEFT] = 1.0f;
mVolume[AUDIO_INTERLEAVE_RIGHT] = 1.0f;
mSendLevel = 0.0f;
// mFrameCount is initialized in createTrack_l
mReqFrameCount = frameCount;
//处理notificationFrames
if (notificationFrames >= 0) {
mNotificationFramesReq = notificationFrames;
mNotificationsPerBufferReq = 0;
} else {
......
}
mNotificationFramesAct = 0;
//callingPid 及myPid 的处理
callingPid = IPCThreadState::self()->getCallingPid();
myPid = getpid();
......
mAuxEffectId = 0;
mOrigFlags = mFlags = flags;
mCbf = cbf;
//cbf是JNI层传入的回调函数audioCallback, 如果用户设置了回调函数则启动AudioTrackThread
if (cbf != NULL) {
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
// thread begins in paused state, and will not reference us until start()
}
// ######## create the IAudioTrack
status = createTrack_l();
......
// 初始化很多变量
.......
exit:
mStatus = status;
return status;
}
继续, Native AudioTrack 的set
函数中会调用createTrack_l
函数和AF(AudioFlinger)发生关系, 这里是通过Binder来完成交互的
[-->AudioTrack.cpp]
status_t AudioTrack::createTrack_l()
{
status_t status;
bool callbackAdded = false;
//(1)Binder调用获取AudioFlinger 服务的代理对象
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
......
{
//低延迟放音相关flag处理
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
......
}
//将上层传递来的参数sampleRate、channel、format及client信息包装进CreateTrackInput作为输入参数
IAudioFlinger::CreateTrackInput input;
......
//AF从底层获取的音频输出设备相关参数封装
IAudioFlinger::CreateTrackOutput output;
//(2) 调用AF的createTrack, 根据传入的CreateTrackInput 参数,创建一个AF端的AT代理对象,类型为IAudioTrack
//传入参数提醒:Stream模式下shareBuffer为空, 后续AF和AT交互围绕IAudioTrack展开
sp<IAudioTrack> track = audioFlinger->createTrack(input,
output,
&status);
......
mFrameCount = output.frameCount;
mNotificationFramesAct = (uint32_t)output.notificationFrameCount;
//输出设备从AF端获取,AF再通过APM(AudioPolicyService)获取输出设备, 毕竟APM管理着音频设备及相关策略
mRoutedDeviceId = output.selectedDeviceId;
mSessionId = output.sessionId;
mSampleRate = output.sampleRate;
if (mOriginalSampleRate == 0) {
mOriginalSampleRate = mSampleRate;
}
mAfFrameCount = output.afFrameCount;
mAfSampleRate = output.afSampleRate;
mAfLatency = output.afLatencyMs;
mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate;
// AudioFlinger now owns the reference to the I/O handle,
// so we are no longer responsible for releasing it.
// FIXME compare to AudioRecord
//(3) track是IAudioTrack类型, getCblk最终调用AF端的TrackBase.h
//TrackBase实现在Tracks.cpp, 成员变量mCblk
//STREAM模式下,没有在AT端创建共享内存,这块内存实际最终有AF->createTrack,再到
//PlaybackThread-> createTrack_l中创建
sp<IMemory> iMem = track->getCblk();
......
void *iMemPointer = iMem->pointer();
......
//(4) Binder死亡通知
// invariant that mAudioTrack != 0 is true only after set() returns successfully
if (mAudioTrack != 0) {
IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
mDeathNotifier.clear();
}
mAudioTrack = track;
mCblkMemory = iMem;
IPCThreadState::self()->flushCommands();
//(5) Client 使用变量cblk 指向服务端传递过来的共享内存地址iMemPointer
//iMemPointer是共享内存的首地址,类型为void* , static_cast直接将这个void*类型转成audio_track_cblk_t,
//这表明这块内存的首部存在audio_track_cblk_t对象。
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
mCblk = cblk;
......
// Starting address of buffers in shared memory. If there is a shared buffer, buffers
// is the value of pointer() for the shared buffer, otherwise buffers points
// immediately after the control block. This address is for the mapping within client
// address space. AudioFlinger::TrackBase::mBuffer is for the server address space.
void* buffers;
if (mSharedBuffer == 0) {
buffers = cblk + 1;
} else {
buffers = mSharedBuffer->pointer();
if (buffers == NULL) {
ALOGE("Could not get buffer pointer");
status = NO_INIT;
goto exit;
}
}
mAudioTrack->attachAuxEffect(mAuxEffectId);
// If IAudioTrack is re-created, don't let the requested frameCount
// decrease. This can confuse clients that cache frameCount().
if (mFrameCount > mReqFrameCount) {
mReqFrameCount = mFrameCount;
}
// reset server position to 0 as we have new cblk.
mServer = 0;
// update proxy
if (mSharedBuffer == 0) {
mStaticProxy.clear();
mProxy = new AudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
} else {
mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
mProxy = mStaticProxy;
}
.......
}
......
mStatus = status;
// sp track destructor will cause releaseOutput() to be called by AudioFlinger
return status;
}
分析 标号(3)处的代码
sp<IMemory> iMem = track->getCblk();
getCblk()调用最终会调用到Bn端
[–>TrackBase.h]
sp<IMemory> getCblk() const { return mCblkMemory; }
那么mCblkMemory
在哪里初始化的呢?
从代码跟踪结果分析 mCblkMemory
是在AF端的Track
[–>Tracks.cpp]
AudioFlinger::PlaybackThread::Track::Track(...):TrackBase(...){
}
Track
继承自TrackBase
, mCblkMemory
就是在TrackBase构造函数初始化的
[–>Tracks.cpp]
AudioFlinger::ThreadBase::TrackBase::TrackBase(...):RefBase(){
......
size_t size = sizeof(audio_track_cblk_t);
if (buffer == NULL && alloc == ALLOC_CBLK) {
// check overflow when computing allocation size for streaming tracks.
if (size > SIZE_MAX - bufferSize) {
android_errorWriteLog(0x534e4554, "34749571");
return;
}
size += bufferSize;
}
//前面我们知道放音是为STREAM模式下, 不会使用共享内存,实际这个时候使用的共享内存是由AF创建的
//由MemoryDealer.cpp完成
if (client != 0) {
mCblkMemory = client->heap()->allocate(size);
if (mCblkMemory == 0 ||
(mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) {
ALOGE("not enough memory for AudioTrack size=%zu", size);
client->heap()->dump("AudioTrack");
mCblkMemory.clear();
return;
}
} else {
mCblk = (audio_track_cblk_t *) malloc(size);
if (mCblk == NULL) {
ALOGE("not enough memory for AudioTrack size=%zu", size);
return;
}
}
//多种分配方式, 这里是根据track type决定的,我们走的是默认type TYPE_DEFAULT, 所以会走ALLOC_CBLK分支
// construct the shared structure in-place.
if (mCblk != NULL) {
//(1) 指定内存区域常见cblk对象
new(mCblk) audio_track_cblk_t();
switch (alloc) {
......
//我们的分析流程会走这个分支
case ALLOC_CBLK:
// stream模式下放音量, clear all buffers
if (buffer == NULL) {
//buffer指向数据空间,它的起始位置是共享内存的首部加上audio_track_cblk_t的大小
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, bufferSize);
} else {
//STATIC模式
mBuffer = buffer;
#if 0
mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic
#endif
}
break;
......
default:
LOG_ALWAYS_FATAL("invalid allocation type: %d", (int)alloc);
}
mBufferSize = bufferSize;
......
}
重点关注下标号(1) 中语法
new(mCblk) audio_track_cblk_t();
注意其用法,new后面括号里是内存,紧跟其后的是一个类构造函数。
这个语句是C++语言的placement new.其含义是在指定内存中创建一个对象。我们知道,普通new只能在堆上创建对象,堆的地址由系统分配。这里使用placement new将使audio_track_cblk_t创建在共享内存上,从而可以被多进程访问。
IAudioTrack是AT和AF连接的纽带
audio_track_cblk_t
是一个结构体类型,其定义如下
// Important: do not add any virtual methods, including ~
struct audio_track_cblk_t
{
// Since the control block is always located in shared memory, this constructor
// is only used for placement new(). It is never used for regular new() or stack.
audio_track_cblk_t();
/*virtual*/ ~audio_track_cblk_t() { }
friend class Proxy;
friend class ClientProxy;
friend class AudioTrackClientProxy;
friend class AudioRecordClientProxy;
friend class ServerProxy;
friend class AudioTrackServerProxy;
friend class AudioRecordServerProxy;
// The data members are grouped so that members accessed frequently and in the same context
// are in the same line of data cache.
uint32_t mServer; // Number of filled frames consumed by server (mIsOut),
// or filled frames provided by server (!mIsOut).
// It is updated asynchronously by server without a barrier.
// The value should be used
// "for entertainment purposes only",
// which means don't make important decisions based on it.
uint32_t mPad1; // unused
volatile int32_t mFutex; // event flag: down (P) by client,
// up (V) by server or binderDied() or interrupt()
#define CBLK_FUTEX_WAKE 1 // if event flag bit is set, then a deferred wake is pending
private:
// This field should be a size_t, but since it is located in shared memory we
// force to 32-bit. The client and server may have different typedefs for size_t.
uint32_t mMinimum; // server wakes up client if available >= mMinimum
// Stereo gains for AudioTrack only, not used by AudioRecord.
gain_minifloat_packed_t mVolumeLR;
uint32_t mSampleRate; // AudioTrack only: client's requested sample rate in Hz
// or 0 == default. Write-only client, read-only server.
PlaybackRateQueue::Shared mPlaybackRateQueue;
// client write-only, server read-only
uint16_t mSendLevel; // Fixed point U4.12 so 0x1000 means 1.0
uint16_t mPad2 __attribute__((__unused__)); // unused
// server write-only, client read
ExtendedTimestampQueue::Shared mExtendedTimestampQueue;
// This is set by AudioTrack.setBufferSizeInFrames().
// A write will not fill the buffer above this limit.
volatile uint32_t mBufferSizeInFrames; // effective size of the buffer
public:
volatile int32_t mFlags; // combinations of CBLK_*
public:
union {
AudioTrackSharedStreaming mStreaming;
AudioTrackSharedStatic mStatic;
int mAlign[8];
} u;
// Cache line boundary (32 bytes)
};
audio_track_cblk_t
中关联了很多友元函数来管理共享内存, 我们的流程主要涉及到AudioTrackClientProxy
及AudioTrackServerProxy
, 这些Proxy类都在AudioTrackShared.cpp中实现。
从上面 Cblk 在AF端初始化分析中我们可大致画出共享内存与CB的关系
AudioTrack初始化完成后,接下来的流程就是play和write相关函数的调用了
//(3)开始播放
mAudioTrack.play();
//(4) 不断写入音频数据byte[] 到Track中
mAudioTrack.write(audio_data,0,packet.data_len);
流程还是按照调用顺序Java >> JNI >> Native来分析
(1) play()分析
[–> AudioTrack.java]
public void play()
throws IllegalStateException {
......
//FIXME use lambda to pass startImpl to superclass
final int delay = getStartDelayMs();
//不延迟
if (delay == 0) {
startImpl();
} else { //延迟播放
new Thread() {
public void run() {
.......
try {
startImpl();
} catch (IllegalStateException e) {
}
}
}.start();
}
}
private void startImpl() {
synchronized(mPlayStateLock) {
//base类调用, 主要是播放状态更新和音量设置
baseStart();
//进入Jni调用
native_start();
mPlayState = PLAYSTATE_PLAYING;
}
}
Java层调用native_start
进入Jni层, Jni层对应的函数为android_media_AudioTrack_start
[–> android_media_AudioTrack.cpp]
static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
//前面的分析中我们知道, 在AudioTrack初始化时已经创建了Native的AudioTrack对象, 该对象指针被保存在
//AudioTrack Java对象的nativeTrackInJavaObj变量中
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
......
//调用Native的AudioTrack->start 函数启动play播放
lpTrack->start();
}
go on, JNI层的android_media_AudioTrack_start
会继续调用 Native层的start
函数
[–> AudioTrack.cpp]
status_t AudioTrack::start()
{
//加锁
AutoMutex lock(mLock);
......
mInUnderrun = true;
//状态更新
State previousState = mState;
if (previousState == STATE_PAUSED_STOPPING) {
mState = STATE_STOPPING;
} else {
mState = STATE_ACTIVE;
}
//更新并获取帧的位置
(void) updateAndGetPosition_l();
......
//更新mCblk的flags标识
int32_t flags = android_atomic_and(~(CBLK_STREAM_END_DONE | CBLK_DISABLED), &mCblk->mFlags);
status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
//*****调用AudioTrack的远端代理对象IAudioTrack的start函数, 正真实现在AudioTrackHandle这个Binder对象中
//mAudioTrack在 Native AudioTrack->set函数中实例化
status = mAudioTrack->start();
if (status == DEAD_OBJECT) {
flags |= CBLK_INVALID;
}
}
if (flags & CBLK_INVALID) {
status = restoreTrack_l("start");
}
// 按需恢复或者暂停AudioTrackThread回调线程
sp<AudioTrackThread> t = mAudioTrackThread;
if (status == NO_ERROR) {
if (t != 0) {
if (previousState == STATE_STOPPING) {
mProxy->interrupt();
} else {
//恢复AudioTrackThread线程
t->resume();
}
} else {
.......
}
// Start our local VolumeHandler for restoration purposes.
mVolumeHandler->setStarted();
} else {
ALOGE("start() status %d", status);
mState = previousState;
if (t != 0) {
if (previousState != STATE_STOPPING) {
//暂停AudioTrackThread线程
t->pause();
}
} else {
......
}
}
return status;
}
write函数分析
[–>AudioTrack.cpp]
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
if (mTransfer != TRANSFER_SYNC) {
return INVALID_OPERATION;
}
//是否不需要混音量直接输出,相关flags处理, 我们不走
if (isDirect()) {
......
}
//参数合法性判定
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
......
}
size_t written = 0;
Buffer audioBuffer; //Buffer是一个辅助性结构
while (userSize >= mFrameSize) {
//以帧为单位
audioBuffer.frameCount = userSize / mFrameSize;
//obtainBuffer从共享内存中得到一块空闲的数据块
status_t err = obtainBuffer(&audioBuffer,
blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
......
//空闲数据缓冲大小是audioBuffer.size, 地址在audioBuffer.i8,数据传递通过memcpy完成
size_t toWrite = audioBuffer.size;
memcpy(audioBuffer.i8, buffer, toWrite);
buffer = ((const char *) buffer) + toWrite;
userSize -= toWrite;
written += toWrite;
//releaseBUffer更新写位置,同时触发消费者
releaseBuffer(&audioBuffer);
}
if (written > 0) {
mFramesWritten += written / mFrameSize;
}
return written;
}
从write函数分析我们发现数据传递其实就是简单的内存拷贝,而生产者消费者工作的协调,则是通过obtainBuffer与releaseBuffer来完成的。下面分析下这两个函数。
[–> AudioTrack.cpp]
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
struct timespec *elapsed, size_t *nonContig)
{
// previous and new IAudioTrack sequence numbers are used to detect track re-creation
uint32_t oldSequence = 0;
uint32_t newSequence;
Proxy::Buffer buffer;
status_t status = NO_ERROR;
static const int32_t kMaxTries = 5;
int32_t tryCounter = kMaxTries;
do {
// obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to
// keep them from going away if another thread re-creates the track during obtainBuffer()
sp<AudioTrackClientProxy> proxy;
sp<IMemory> iMem;
{ // start of lock scope
AutoMutex lock(mLock);
newSequence = mSequence;
//处理前一次调用obtainBuffer()失败的情况,这种情况有可能是media server挂掉了
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
.......
}
oldSequence = newSequence;
......
// Keep the extra references
proxy = mProxy;
iMem = mCblkMemory;
.......
// Non-blocking if track is stopped or paused
if (mState != STATE_ACTIVE) {
requested = &ClientProxy::kNonBlocking;
}
} // end of lock scope
buffer.mFrameCount = audioBuffer->frameCount;
// FIXME starts the requested timeout and elapsed over from scratch
// (1) proxy是AudioTrackClientProxy类型, 该类实现在AudioTrackShared.cpp中
status = proxy->obtainBuffer(&buffer, requested, elapsed);
} while (((status == DEAD_OBJECT) || (status == NOT_ENOUGH_DATA)) && (tryCounter-- > 0));
audioBuffer->frameCount = buffer.mFrameCount;
audioBuffer->size = buffer.mFrameCount * mFrameSize;
audioBuffer->raw = buffer.mRaw;
if (nonContig != NULL) {
*nonContig = buffer.mNonContig;
}
return status;
}
看下标号(1) 相关调用最终发生在AudioTrackShared: ClientProxy中
[–> AudioTrackShared.cpp]
status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
struct timespec *elapsed)
{
......
//(1) 使用 mCblk来获取可用写入Buffer
audio_track_cblk_t* cblk = mCblk;
.......
for (;;) {
......
// compute number of frames available to write (AudioTrack) or read (AudioRecord)
int32_t front;
int32_t rear;
//我们是放音量会走这个分支
if (mIsOut) {
// The barrier following the read of mFront is probably redundant.
// We're about to perform a conditional branch based on 'filled',
// which will force the processor to observe the read of mFront
// prior to allowing data writes starting at mRaw.
// However, the processor may support speculative execution,
// and be unable to undo speculative writes into shared memory.
// The barrier will prevent such speculative execution.
//原子操作
front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
rear = cblk->u.mStreaming.mRear;
} else {
// On the other hand, this barrier is required.
rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
front = cblk->u.mStreaming.mFront;
}
// write to rear, read from front
ssize_t filled = rear - front;
// pipe should not be overfull
if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
if (mIsOut) {
ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); "
"shutting down", filled, mFrameCount);
mIsShutdown = true;
status = NO_INIT;
goto end;
}
// for input, sync up on overrun
filled = 0;
cblk->u.mStreaming.mFront = rear;
(void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
}
// Don't allow filling pipe beyond the user settable size.
// The calculation for avail can go negative if the buffer size
// is suddenly dropped below the amount already in the buffer.
// So use a signed calculation to prevent a numeric overflow abort.
ssize_t adjustableSize = (ssize_t) getBufferSizeInFrames();
ssize_t avail = (mIsOut) ? adjustableSize - filled : filled;
if (avail < 0) {
avail = 0;
//有可用内存,那么需要确定可以Buffer的地址及大小
} else if (avail > 0) {
// 'avail' may be non-contiguous, so return only the first contiguous chunk
size_t part1;
if (mIsOut) {
rear &= mFrameCountP2 - 1;
part1 = mFrameCountP2 - rear;
} else {
front &= mFrameCountP2 - 1;
part1 = mFrameCountP2 - front;
}
if (part1 > (size_t)avail) {
part1 = avail;
}
if (part1 > buffer->mFrameCount) {
part1 = buffer->mFrameCount;
}
//最终计算获取的可以Buffer
buffer->mFrameCount = part1;
buffer->mRaw = part1 > 0 ?
&((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
buffer->mNonContig = avail - part1;
mUnreleased = part1;
status = NO_ERROR;
break;
}
}
.......
}
上面obtainBuffer的计算过程还是比较复杂的, 但是最终通过CB这个数据结构管理的,而releaseBuffer则是在使用完这块空间后更新写指针的位置。releaseBuffer实现如下
[–> AudioTrack.cpp]
void ClientProxy::releaseBuffer(Buffer* buffer)
{
LOG_ALWAYS_FATAL_IF(buffer == NULL);
size_t stepCount = buffer->mFrameCount;
......
LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount),
"%s: mUnreleased out of range, "
"!(stepCount:%zu <= mUnreleased:%zu <= mFrameCount:%zu), BufferSizeInFrames:%u",
__func__, stepCount, mUnreleased, mFrameCount, getBufferSizeInFrames());
mUnreleased -= stepCount;
audio_track_cblk_t* cblk = mCblk;
// Both of these barriers are required
//放音走这个流程, 写数据实在尾部写入, 有点像环形Buffer处理
if (mIsOut) {
int32_t rear = cblk->u.mStreaming.mRear;
//更新写入位置
android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
} else {
//更新读入位置
int32_t front = cblk->u.mStreaming.mFront;
android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
}
}
AudioTrack生命周期结束时,会调用其析构函数释放资源
AudioTrack::~AudioTrack()
{
// pull together the numbers, before we clean up our structures
mMediaMetrics.gather(this);
if (mStatus == NO_ERROR) {
// Make sure that callback function exits in the case where
// it is looping on buffer full condition in obtainBuffer().
// Otherwise the callback thread will never exit.
stop();
}
/*
* mAudioTrackThread may be created before(call set function),
* and restoreTrack_l may fail if audio route changed when audio device is unavailable now,
* then mStatus will be set error status, so when this audiotrack release, must let
* mAudioTrackThread quit.
*/
if (mAudioTrackThread != 0) {
if(mProxy != NULL)
mProxy->interrupt();
mAudioTrackThread->requestExit(); // see comment in AudioTrack.h
mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
mAudioTrackThread = NULL;
}
/*if (mStatus == NO_ERROR)*/ {
// No lock here: worst case we remove a NULL callback which will be a nop
if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
AudioSystem::removeAudioDeviceCallback(this, mOutput);
mDeviceCallback = NULL;
}
//AF端清理
if(mAudioTrack != NULL) {
IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
mAudioTrack.clear();
mAudioTrack = NULL;
}
//共享内存清理
if(mCblkMemory != NULL) {
mCblkMemory.clear();
mCblkMemory = NULL;
}
if(mSharedBuffer != NULL) {
mSharedBuffer.clear();
mSharedBuffer = NULL;
}
IPCThreadState::self()->flushCommands();
ALOGV("~AudioTrack, releasing session id %d from %d on behalf of %d",
mSessionId, IPCThreadState::self()->getCallingPid(), mClientPid);
//释放相关会话Id
AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
}
}
如果我们在放音时直接退出,没有调用stop方法,则析构函数也会调用,考虑很周到。
直接从Native分析
[–> AudioTrack.cpp]
void AudioTrack::stop()
{
AutoMutex lock(mLock);
if (mState != STATE_ACTIVE && mState != STATE_PAUSED) {
return;
}
if (isOffloaded_l()) {
mState = STATE_STOPPING;
} else {
mState = STATE_STOPPED;
ALOGD_IF(mSharedBuffer == nullptr,
"stop() called with %u frames delivered", mReleased.value());
mReleased = 0;
}
mProxy->stop(); // notify server not to read beyond current client position until start().
mProxy->interrupt();
//mAudioTrack类型是IAudioTrack类型,其stop最终处理在AudioFlinger端
mAudioTrack->stop();
//如果使用静态方式放音,则需要重置position、loopStart、loopEnd及loopCount等信息
if (mSharedBuffer != 0) {
// clear buffer position and loop count.
mStaticProxy->setBufferPositionAndLoop(0 /* position */,
0 /* loopStart */, 0 /* loopEnd */, 0 /* loopCount */);
}
sp<AudioTrackThread> t = mAudioTrackThread;
if (t != 0) {
if (!isOffloaded_l()) {
t->pause();
}
} else {
setpriority(PRIO_PROCESS, 0, mPreviousPriority);
set_sched_policy(0, mPreviousSchedulingGroup);
}
}
stop的工作很简单,就是调用IAudioTrack的stop方法,并要求pause回调线程。
AT通过Binder调用完成与AF的交互流程, 总体流程图如下: