Android-浅析-音频系统-AudioTrack-一-基础原理

前言

Linus Benedict Torvalds : RTFSC – Read The Funning Source Code

概括

The AudioTrack class manages and plays a single audio resource for Java applications.

AudioTrack

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
// 根据音频数据的特性来确定所要分配的缓冲区的最小size
int bufsize = AudioTrack.getMinBufferSize(
8000, // 采样率:每秒8K个点
AudioFormat.CHANNEL_CONFIGURATION_STEREO, // 声道数:双声道
AudioFormat.ENCODING_PCM_16BIT // 采样精度:一个采样点16比特,相当于2个字节
);
// 创建AudioTrack
AudioTrack trackplayer = new AudioTrack(
AudioManager.STREAM_MUSIC, //音频流类型
8000,
AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
AudioFormat.ENCODING_PCM_16BIT, bufsize,
AudioTrack.MODE_STREAM //数据加载模式
);
// 开始播放
trackplayer.play();
// 调用write写数据
trackplayer.write(bytes_pkg, 0,bytes_pkg.length); //往track中写数据
// 停止播放和释放资源
trackplayer.stop(); //停止播放
trackplayer.release(); //释放底层资源

getMinBufferSize

Returns the estimated minimum buffer size required for an AudioTrack object to be created in the MODE_STREAM mode.
返回一个从 AudioTrack 对象中获取最小可被创建在 MODE_STREAM 模式下的最小缓冲区大小估计值。

estimated : 估计

The size is an estimate because it does not consider either the route or the sink, since neither is known yet. Note that this size doesn’t guarantee a smooth playback under load, and higher values should be chosen according to the expected frequency at which the buffer will be refilled with additional data to play. For example, if you intend to dynamically set the source sample rate of an AudioTrack to a higher value than the initial source sample rate, be sure to configure the buffer size based on the highest planned sample rate.

在 Android SDK 中getMinBufferSize函数在经过一系列检查后会调用native层的 native_get_min_buff_size 函数,因为在音频录制的时候我们也要检查硬件是否支持我们需要的采样率和采样精度。

java层流程:

  1. 声道检测,是否单声道、双声道、多声道。
  2. 采样率检测,是否在 4000~192000 之间。
  3. 硬件层检测,判断是否支持我们设置的采样率和采样精度,并且返回最小估值。

native层流程:

  1. 查询采样率,一般返回的是所支持的最高采样率,例如44100。(最大采样率)
  2. 查询硬件内部缓冲的大小,以Frame为单位。(帧数)
  3. 查询硬件的延时时间。(延迟量)
  4. 计算最少缓冲区个数:延迟量 / ((1000 * 帧数) / 最大采样率)。
  5. 通过缓冲区数量计算得出最少Frame数。
  6. 计算Buffer大小:PCM格式数据:bufrer是由每帧的字节数,通道数量和采样精度的乘积(frameCount channelCount bytesPerSample);其他格式数据:buffer大小就是Frame大小。

Frame 用来描述数据量的多少的单位,1单位的Frame等于1个采样点的字节数×声道数(例如 16k 采样精度占2字节,一般用MONO两声道,Frame=2*2)。

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
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:
channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
}
// sample rate, note these values are subject to change
// Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed
if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
(sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {
return ERROR_BAD_VALUE;
}
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if (size <= 0) {
return ERROR;
} else {
return size;
}
}
// ----------------------------------------------------------------------------
// returns the minimum required size for the successful creation of a streaming AudioTrack
// returns -1 if there was an error querying the hardware.
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
jint sampleRateInHertz, jint channelCount, jint audioFormat) {
size_t frameCount;
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;
}
const audio_format_t format = audioFormatToNative(audioFormat);
if (audio_is_linear_pcm(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
return frameCount * channelCount * bytesPerSample;
} else {
return frameCount;
}
}
status_t AudioTrack::getMinFrameCount(size_t* frameCount,
audio_stream_type_t streamType,
uint32_t sampleRate)
{
uint32_t afSampleRate;
status_t status;
AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);
size_t afFrameCount;
AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
uint32_t afLatency;
AudioSystem::getOutputLatency(&afLatency, streamType);
// 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);
return NO_ERROR;
}
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
uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
if (minBufCount < 2) {
minBufCount = 2;
}
return minBufCount * sourceFramesNeededWithTimestretch(sampleRate, afFrameCount, afSampleRate, speed);
}

总结:getMinBufSize会综合考虑硬件的情况(例如是否支持采样率,硬件本身的延迟情况等)后,得出一个最小缓冲区的大小。一般我们分配的缓冲大小会是它的整数倍。

创建AudioTrack

Class constructor with AudioAttributes and AudioFormat.

java层流程:

  1. 检查传入的参数是否符合。(具体的判断标准可以看下文代码)
  2. 检查传入的Buffer缓冲区大小是否符合。(因为在上一步获取了最小Buffer,这里一般都没问题)
  3. 通过调用 Native 层构造此类。

native层流程:

  1. 获取framecount、samplerate和channel的信息。
  2. 通过AudioSystem::popCount函数从channel int类型里获取通道数。(popCount用于统计一个整数中有多少位为1,有很多经典的算法)
  3. 将传入进来的Java层参数值转换为native层值。
  4. 通过传入的参数来计算一次framecount值。
  5. 创建一个native层audiotrack对象。
  6. 创建一个audioTrackJniStorage对象。(这个数据将在每个AudioTrack回调中传递)
  7. 将native参数传入创建出来的audiotrack对象。
  8. 将audiotrack对象指针保存到java层的变量中,联通java和native层。

Java层:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Convenience method for the constructor's parameter checks.
// This is where constructor IllegalArgumentException-s are thrown
private void audioParamCheck(int sampleRateInHz, int channelConfig, int channelIndexMask,
int audioFormat, int mode) {
// sample rate, note these values are subject to change
if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
throw ...
}
mSampleRate = sampleRateInHz;
// IEC61937 is based on stereo. We could coerce it to stereo.
// But the application needs to know the stream is stereo so that it is encoded and played correctly. So better to just reject it.
if (audioFormat == AudioFormat.ENCODING_IEC61937
&& channelConfig != AudioFormat.CHANNEL_OUT_STEREO) {
throw ...;
}
// channel config
mChannelConfiguration = channelConfig;
switch (channelConfig) {
case AudioFormat.CHANNEL_OUT_DEFAULT: //AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
case AudioFormat.CHANNEL_OUT_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
mChannelCount = 1;
mChannelMask = AudioFormat.CHANNEL_OUT_MONO;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
mChannelCount = 2;
mChannelMask = AudioFormat.CHANNEL_OUT_STEREO;
break;
default:
if (channelConfig == AudioFormat.CHANNEL_INVALID && channelIndexMask != 0) {
mChannelCount = 0;
break; // channel index configuration only
}
if (!isMultichannelConfigSupported(channelConfig)) {
// input channel configuration features unsupported channels
throw ...;
}
mChannelMask = channelConfig;
mChannelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
}
// check the channel index configuration (if present)
mChannelIndexMask = channelIndexMask;
if (mChannelIndexMask != 0) {
// restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
final int indexMask = (1 << CHANNEL_COUNT_MAX) - 1;
if ((channelIndexMask & ~indexMask) != 0) {
throw ...;
}
int channelIndexCount = Integer.bitCount(channelIndexMask);
if (mChannelCount == 0) {
mChannelCount = channelIndexCount;
} else if (mChannelCount != channelIndexCount) {
throw ...;
}
}
// audio format
if (audioFormat == AudioFormat.ENCODING_DEFAULT) {
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
if (!AudioFormat.isPublicEncoding(audioFormat)) {
throw ...;
}
mAudioFormat = audioFormat;
// audio load mode
if (((mode != MODE_STREAM) && (mode != MODE_STATIC)) ||
((mode != MODE_STREAM) && !AudioFormat.isEncodingLinearPcm(mAudioFormat))) {
throw ...;
}
mDataLoadMode = mode;
}
// Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId)
throws IllegalArgumentException {
super(attributes);
audioParamCheck(rate, channelMask, channelIndexMask, encoding, mode);
audioBuffSizeCheck(bufferSizeInBytes);
int[] sampleRate = new int[] {mSampleRate};
int[] session = new int[1];
session[0] = sessionId;
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
if (initResult != SUCCESS) {
return; // with mState == STATE_UNINITIALIZED
}
}

Native层:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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) {
// Invalid channel representations are caught by !audio_is_output_channel() below.
audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
channelPositionMask, channelIndexMask);
uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
// check the format.
// This function was called from Java, so we compare the format against the Java constants
audio_format_t format = audioFormatToNative(audioFormat);
// compute the frame count
size_t frameCount;
if (audio_is_linear_pcm(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
} else {
frameCount = buffSizeInBytes;
}
jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
int sessionId = nSession[0];
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
// create the native AudioTrack object
sp<AudioTrack> lpTrack = new AudioTrack();
// initialize the callback information:
// this data will be passed with every AudioTrack callback
AudioTrackJniStorage* 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.busy = false;
// initialize the native AudioTrack object
status_t status = NO_ERROR;
switch (memoryMode) {
case MODE_STREAM:
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
0,// shared mem
true,// thread can call Java
sessionId,// audio session ID
AudioTrack::TRANSFER_SYNC,
NULL, // default offloadInfo
-1, -1, // default uid, pid values
paa);
break;
case MODE_STATIC:
// AudioTrack is using shared memory
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:
}
nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
// read the audio session ID back from AudioTrack in case we create a new session
nSession[0] = lpTrack->getSessionId();
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
{ // scope for the lock
Mutex::Autolock l(sLock);
sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
}
// save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field of the Java object (in mNativeTrackInJavaObj)
setAudioTrack(env, thiz, lpTrack);
// save the JNI resources so we can free them later
//ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
// since we had audio attributes, the stream type was derived from them during the
// creation of the native AudioTrack: push the same value to the Java object
env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
// audio attributes were copied in AudioTrack creation
free(paa);
paa = NULL;
return (jint) AUDIO_JAVA_SUCCESS;
}

先记下几个关键点:

  1. AudioTrackJniStorage 对象。
  2. AudioTrack 创建。
  3. AudioTrack 流模式的设置。

AudioTrackJniStorage

AudioTrackJniStorage 是对Android 共享内存机制的一个封装类。里面包含了两个关键变量:MemoryHeapBase 和 MemoryBase。
MemoryHeapBase 是Android 的一套基于Binder机制的对内存操作的类。从BnMemoryHeap派生。服务端(Bnxxx),代理端(Bpxxx)。

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
struct audiotrack_callback_cookie {
jclass audioTrack_class;
jobject audioTrack_ref;
bool busy;
Condition cond;
};
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;
}
~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;
}
};

在这里我们能明白 MemoryHeapBase 是共享内存的核心类,MemoryBase 是在 MemoryHeapBase 基础上包了一层offset的类。在我们new 了一段共享内存后,通过把 MemoryBase 传递给我们要共享内存的代理端来实现共享。

  1. 分配了一块共享内存,使两个进程可以共享这块内存。
  2. 基于Binder通信,使这两个类的进程就可以交互。
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
class MemoryHeapBase : public virtual BnMemoryHeap
{
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(0), mNeedUnmap(false), mOffset(0)
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
if (fd >= 0) {
if (mapfd(fd, size) == NO_ERROR) {
if (flags & READ_ONLY) {
ashmem_set_prot_region(fd, PROT_READ);
}
}
}
}
};
class MemoryBase : public BnMemory
{
public:
MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
virtual ~MemoryBase();
virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
protected:
size_t getSize() const { return mSize; }
ssize_t getOffset() const { return mOffset; }
const sp<IMemoryHeap>& getHeap() const { return mHeap; }
private:
size_t mSize;
ssize_t mOffset;
sp<IMemoryHeap> mHeap;
};

new AudioTrack

将AudioTrack 状态初始为未加载。

1
2
3
4
5
6
7
8
AudioTrack::AudioTrack()
: mStatus(NO_INIT),
...
{
mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
mAttributes.usage = AUDIO_USAGE_UNKNOWN;
mAttributes.flags = 0x0;
}

AudioTrack.set

别看 set 函数很长,其实核心做的事就三件:

  1. 检测跟转换各种参数。
  2. 创建 AudioTrackThread 工作线程。
  3. 调用核心创建函数 createTrack_l。
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
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,
const sp<IMemory>& sharedBuffer,
bool threadCanCallJava,
int sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
int uid,
pid_t pid,
const audio_attributes_t* pAttributes,
bool doNotReconnect)
{
...
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_t status = createTrack_l();
return NO_ERROR;
}

AudioTrackThread

a small internal class to handle the callback.
这个线程是用于AudioTrack(native)与AudioTrack(java)间的数据事件通知的,为上层应用处理事件提供了一个入口。

AudioTrack.createTrack_l

看到核心 createTrack 的时候我们应该明白这几个步骤:

  1. 获取内核 AudioFlinger 对象。
  2. 通过 AudioFlinger 创建 track。
  3. 从拿到的 track 里开始为以后的 write函数做准备。

总结:AudioTrack 其实也是一个壳,核心的工作都是由 AudioFlinger 实现,到此,我们基本明白 AudioTrack 的调用也是一步步函数封装加上共享内存的调用。

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
46
47
48
49
// must be called with mLock held
status_t AudioTrack::createTrack_l()
{
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
sp<IAudioTrack> track = audioFlinger->createTrack(streamType,
mSampleRate,
mFormat,
mChannelMask,
&temp,
&trackFlags,
mSharedBuffer,
output,
tid,
&mSessionId,
mClientUid,
&status);
sp<IMemory> iMem = track->getCblk();
void *iMemPointer = iMem->pointer();
mCblkMemory = iMem;
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
// 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");
return NO_INIT;
}
}
if (mSharedBuffer == 0) {
mStaticProxy.clear();
mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize);
} else {
mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize);
mProxy = mStaticProxy;
}
return NO_ERROR;
}

AudioTrack.play

Starts playing an AudioTrack.

java层流程:

  1. 检查当前是否被禁止。是的话就将音量降为0。
  2. 调用native层的播放函数。

native层流程:

  1. 取出之前native层创建的audiotrack对象。
  2. 调用audiotrack对象的start开始播放。

native层:

1
2
3
4
android_media_AudioTrack_start(JNIEnv *env, jobject thiz) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
lpTrack->start();
}

AudioTrack.write

Writes the audio data to the audio sink for playback (streaming mode), or copies audio data for later playback (static buffer mode).

write有两种方式,一种是读流播放:先调用play,再调用write;另一种是读文件:先调用writ再调用play。

java层流程:

  1. 检查当前传入的格式,播放的起始、终点是否合法。
  2. 调用native层的写入函数。

native层流程:

  1. 取出之前native层创建的audiotrack对象。
  2. 调用audiotrack对象的write开始写入播放信息。

这里在调用函数时有一个注意的点:
The format specified in the AudioTrack constructor should be ENCODING_PCM_16BIT to correspond to the data in the array.
write(short[] audioData, int offsetInShorts, int sizeInShorts)

The format specified in the AudioTrack constructor should be ENCODING_PCM_8BIT to correspond to the data in the array. The format can be ENCODING_PCM_16BIT, but this is deprecated.
write(byte[] audioData, int offsetInBytes, int sizeInBytes, int writeMode)

也就是在调用write写入的时候如果pcm是16bit的格式就尽量选择传入short数组格式。

在write Java层有两种方式,一种是读流的方式进行播放,一种是读文件形式。两种形式调用的方法不同,但最终调用native层的方法都是一样的。

native层:

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
46
47
48
49
50
51
52
53
54
55
56
57
// 读流的方式:
static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
T javaAudioData,
jint offsetInSamples, jint sizeInSamples,
jint javaAudioFormat,
jboolean isWriteBlocking) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
// get the pointer for the audio data from the java array
auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
return samplesWritten;
}
// 读文件的形式:
static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
jint javaAudioFormat, jboolean isWriteBlocking) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
ScopedBytesRO bytes(env, javaBytes);
jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
return written;
}
static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
jint offsetInSamples, jint sizeInSamples, bool blocking) {
ssize_t written = 0;
// regular write() or copy the data to the AudioTrack's shared memory?
size_t sizeInBytes = sizeInSamples * sizeof(T);
// 如果是读流的形式 sharedBuffer()为空,读文件则不为空。
if (track->sharedBuffer() == 0) {
written = track->write(data + offsetInSamples, sizeInBytes, blocking);
// for compatibility with earlier behavior of write(), return 0 in this case
if (written == (ssize_t) WOULD_BLOCK) {
written = 0;
}
} else {
// writing to shared memory, check for capacity 首先把数据拷贝到共享内存中。
if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size();
}
memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
written = sizeInBytes;
}
if (written > 0) {
return written / sizeof(T);
}
// for compatibility, error codes pass through unchanged
return written;
}

AudioTrack.stop、AudioTrack.release

Stops playing the audio data. Releases the native AudioTrack resources.

因为release里面就包含了stop。我们直接分析release就好。

java层流程:

  1. 调用stop函数停止播放,stop函数也很粗暴直接调用native层stop。
  2. 调用native层release函数。

native层流程:

  1. stop 首先取出之前native层创建的audiotrack对象,调用stop方法。
  2. release 也首先调用stop,然后获取AudioTrackJniStorage对象,释放AudioTrackJniStorage保存的资源。
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
android_media_AudioTrack_stop(JNIEnv *env, jobject thiz) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
lpTrack->stop();
}
static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) {
sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
lpTrack->stop();
// delete the JNI data
AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
thiz, javaAudioTrackFields.jniData);
// reset the native resources in the Java object so any attempt to access
// them after a call to release fails.
env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
if (pJniStorage) {
Mutex::Autolock l(sLock);
audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
while (lpCookie->busy) {
if (lpCookie->cond.waitRelative(sLock, milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != NO_ERROR) {
break;
}
}
sAudioTrackCallBackCookies.remove(lpCookie);
// delete global refs created in native_setup
env->DeleteGlobalRef(lpCookie->audioTrack_class);
env->DeleteGlobalRef(lpCookie->audioTrack_ref);
delete pJniStorage;
}
}

总结

音频这块要了解的东西确实太多了,针对 接下来的核心层我们要分层次的去了解,大致对于应用层来说了解应该是足够了。