Android上使用AudioTrack可以实现播放PCM。本文深入分析了Android的AudioTrack.cpp源码,详细解释了从AudioTrack.java到AudioTrack.cpp的工作流程。主要内容包括获取缓冲区最小值、AudioTrack对象的创建、音频数据的写入以及停止和释放方法

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

1. 获取最小缓存
xref: /frameworks/base/media/java/android/media/AudioTrack.java
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:
if (!isMultichannelConfigSupported(channelConfig)) {
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
} else {
channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
}
}
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
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;
}
// 调用native层方法
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if (size <= 0) {
loge("getMinBufferSize(): error querying hardware");
return ERROR;
}
else {
return size;
}
}
确定输出通道是双声道还是单声道,调用native层的native_get_min_buff_size方法计算最小缓存
xref: /frameworks/base/core/jni/android_media_AudioTrack.cpp
static const JNINativeMethod gMethods[] = {
{"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;
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_has_proportional_frames(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
//min_buf_size = 所需帧的最小个数 * 声道数 * 一个采样点的字节数
return frameCount * channelCount * bytesPerSample;
} else {
return frameCount;
}
}
getMinFrameCount这个函数用于确定至少需要多少Frame才能保证音频的正常播放。
1Frame = 一个采样点的字节数 x 声道数(比如PCM16,双声道的1个Frame等于2×2=4字节)
xref: /frameworks/av/media/libaudioclient/AudioTrack.cpp
status_t AudioTrack::getMinFrameCount(
size_t* frameCount,
audio_stream_type_t streamType,
uint32_t sampleRate)
{
if (frameCount == NULL) {
return BAD_VALUE;
}
// 查询硬件设备的采样率
uint32_t afSampleRate;
status_t status;
status = AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);
if (status != NO_ERROR) {
ALOGE("%s(): Unable to query output sample rate for stream type %d; status %d",
__func__, streamType, status);
return status;
}
// 查询硬件内部缓冲的大小
size_t afFrameCount;
status = AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
if (status != NO_ERROR) {
ALOGE("%s(): Unable to query output frame count for stream type %d; status %d",
__func__, streamType, status);
return status;
}
// 查询硬件的延时时间
uint32_t afLatency;
status = AudioSystem::getOutputLatency(&afLatency, streamType);
if (status != NO_ERROR) {
ALOGE("%s(): Unable to query output latency for stream type %d; status %d",
__func__, streamType, status);
return status;
}
// 计算一个最低帧数
*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("%s(): failed for streamType %d, sampleRate %u",
__func__, streamType, sampleRate);
return BAD_VALUE;
}
ALOGV("%s(): getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
__func__, *frameCount, afFrameCount, afSampleRate, afLatency);
return NO_ERROR;
}
查询硬件设备的采样率、硬件内部缓冲的大小、硬件的延时时间然后计算一个最低帧数。
xref: /frameworks/av/media/libaudioclient/AudioSystem.cpp
/* static */ size_t AudioSystem::calculateMinFrameCount(
uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
uint32_t sampleRate, float speed /*, uint32_t notificationsPerBufferReq*/)
{
// 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);
}
2. AudioTrack的创建
xref: /frameworks/base/media/java/android/media/AudioTrack.java
private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId, boolean offload)
throws IllegalArgumentException {
........................................................................
// 检查参数的合法性
audioParamCheck(rate, channelMask, channelIndexMask, encoding, mode);
mOffloaded = offload;
mStreamType = AudioSystem.STREAM_DEFAULT;
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
// 调用natvie层的native_setup方法
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, //传入的这个值是8000
mChannelMask, //传入的这个值是2
mChannelIndexMask,
mAudioFormat, //传入的这个值是AudioFormat.ENCODING_PCM_16BIT
mNativeBufferSizeInBytes, //传入的这个值是bufferSizeInBytes
mDataLoadMode, //传入的这个值是MODE_STREAM
session, 0 /*nativeTrackInJavaObj*/,
offload);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
}
........................................................................
}
检查参数的合法性,调用native层的native_setup方法
xref: /frameworks/base/core/jni/android_media_AudioTrack.cpp
static const JNINativeMethod gMethods[] = {
{"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
(void *)android_media_AudioTrack_setup},
};
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) {
sp<AudioTrack> lpTrack = 0;
......................................................................................
// if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
if (nativeAudioTrack == 0) {
......................................................................................
// create the native AudioTrack object
lpTrack = new AudioTrack();
......................................................................................
// 对两种模式(MODE_STREAM和MODE_STATIC)进行区分
switch (memoryMode) {
case MODE_STREAM:
// 设置参数,这里的app不分配内存而是在后面的playbackthread中分配内存
status = lpTrack->set(
AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
sampleRateInHertz,
format,// 采样点的精度,一般为PCM16或者PCM8
nativeChannelMask,
offload ? 0 : frameCount,
offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,
audioCallback, //该回调函数定义在android_media_AudioTrack.cpp中
&(lpJniStorage->mCallbackData),//callback, callback data (user)
0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
0,/ 共享内存,STREAM模式下为空。实际使用的共享内存由AF创建
true,// thread can call Java
sessionId,// audio session ID
offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,
offload ? &offloadInfo : NULL,
-1, -1, // default uid, pid values
paa.get());
break;
case MODE_STATIC:
// 如果是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,// STATIC模式下,需要传递该共享内存
true,// thread can call Java
sessionId,// audio session ID
AudioTrack::TRANSFER_SHARED,
NULL, // default offloadInfo
-1, -1, // default uid, pid values
paa.get());
break;
............................................................................................
}
// 把JNI层中new出来的AudioTrack对象指针保存到Java对象的一个变量中
setAudioTrack(env, thiz, lpTrack);
// save the JNI resources so we can free them later
// lpJniStorage对象指针也保存到Java对象中
env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
return (jint) AUDIO_JAVA_SUCCESS;
............................................................................................
}
3. 开始播放
xref: /frameworks/base/media/java/android/media/AudioTrack.java
public void play()
throws IllegalStateException {
.........................................................................................
final int delay = getStartDelayMs();
if (delay == 0) {
startImpl();
} else {
new Thread() {
public void run() {
.........................................................................................
try {
startImpl();
} catch (IllegalStateException e) {
// fail silently for a state exception when it is happening after
// a delayed start, as the player state could have changed between the
// call to start() and the execution of startImpl()
}
}
}.start();
}
}
.........................................................................................
private void startImpl() {
synchronized(mPlayStateLock) {
baseStart();
// 调用native层的start方法
native_start();
.........................................................................................
}
}
调用native层的start方法
xref: /frameworks/base/core/jni/android_media_AudioTrack.cpp
static const JNINativeMethod gMethods[] = {
{"native_start", "()V", (void *)android_media_AudioTrack_start},
};
.....................................................................................
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for start()");
return;
}
lpTrack->start();
}
调用AudioTrack的start方法
xref: /frameworks/av/media/libaudioclient/AudioTrack.cpp
status_t AudioTrack::start()
{
// 如果已经是start状态则返回
if (mState == STATE_ACTIVE) {
return INVALID_OPERATION;
}
mInUnderrun = true;
// 保存上一次的状态
State previousState = mState;
if (previousState == STATE_PAUSED_STOPPING) {
mState = STATE_STOPPING;
} else {
mState = STATE_ACTIVE;
}
.................................................................................
// 如果上一状态是停止状态,表明需要重新把position设置为0,从头播放
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mPosition = 0;
mPreviousTimestampValid = false;
mTimestampStartupGlitchReported = false;
mTimestampRetrogradePositionReported = false;
mTimestampRetrogradeTimeReported = false;
mTimestampStallReported = false;
mTimestampStaleTimeReported = false;
mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID;
if (!isOffloadedOrDirect_l()
&& mStartEts.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) {
// Server side has consumed something, but is it finished consuming?
// It is possible since flush and stop are asynchronous that the server
// is still active at this point.
ALOGV("%s(%d): server read:%lld cumulative flushed:%lld client written:%lld",
__func__, mPortId,
(long long)(mFramesWrittenServerOffset
+ mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]),
(long long)mStartEts.mFlushed,
(long long)mFramesWritten);
// mStartEts is already adjusted by mFramesWrittenServerOffset, so we delta adjust.
mFramesWrittenServerOffset -= mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER];
}
mFramesWritten = 0;
mProxy->clearTimestamp(); // need new server push for valid timestamp
mMarkerReached = false;
// For offloaded tracks, we don't know if the hardware counters are really zero here,
// since the flush is asynchronous and stop may not fully drain.
// We save the time when the track is started to later verify whether
// the counters are realistic (i.e. start from zero after this time).
mStartFromZeroUs = mStartNs / 1000;
// force refresh of remaining frames by processAudioBuffer() as last
// write before stop could be partial.
mRefreshRemaining = true;
// for static track, clear the old flags when starting from stopped state
if (mSharedBuffer != 0) {
android_atomic_and(
~(CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END),
&mCblk->mFlags);
}
}
// 新的播放位置
mNewPosition = mPosition + mUpdatePeriod;
// 获取share buffer的flag
int32_t flags = android_atomic_and(~(CBLK_STREAM_END_DONE | CBLK_DISABLED), &mCblk->mFlags);
status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
// 如果share buffer可用,则调用track的start方法
status = mAudioTrack->start();
if (status == DEAD_OBJECT) {
flags |= CBLK_INVALID;
}
}
if (flags & CBLK_INVALID) {
status = restoreTrack_l("start");
}
// resume or pause the callback thread as needed.
sp<AudioTrackThread> t = mAudioTrackThread;
if (status == NO_ERROR) {
if (t != 0) {
if (previousState == STATE_STOPPING) {
// 中断
mProxy->interrupt();
} else {
// 恢复播放
t->resume();
}
} else {
// 获取当前线程的优先级
mPreviousPriority = getpriority(PRIO_PROCESS, 0);
get_sched_policy(0, &mPreviousSchedulingGroup);
// 设置线程优先级为ANDROID_PRIORITY_AUDIO
androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
}
// Start our local VolumeHandler for restoration purposes.
mVolumeHandler->setStarted();
} else {
ALOGE("%s(%d): status %d", __func__, mPortId, status);
mState = previousState;
if (t != 0) {
if (previousState != STATE_STOPPING) {
t->pause();
}
} else {
setpriority(PRIO_PROCESS, 0, mPreviousPriority);
set_sched_policy(0, mPreviousSchedulingGroup);
}
}
return status;
}
4. 调用write写数据
xref: /frameworks/base/media/java/android/media/AudioTrack.java
public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
@WriteMode int writeMode) {
.........................................................................................
final int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat,
writeMode == WRITE_BLOCKING);
if ((mDataLoadMode == MODE_STATIC)
&& (mState == STATE_NO_STATIC_DATA)
&& (ret > 0)) {
// benign race with respect to other APIs that read mState
mState = STATE_INITIALIZED;
}
return ret;
}
我们的用例中采用的是PCM16数据。它对应的JNI层函数native_write_short。
xref: /frameworks/base/core/jni/android_media_AudioTrack.cpp
static const JNINativeMethod gMethods[] = {
{"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
};
...................................................................................................
static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
T javaAudioData,
jint offsetInSamples, jint sizeInSamples,
jint javaAudioFormat,
jboolean isWriteBlocking) {
//ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
// offsetInSamples, sizeInSamples);
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
........................................................................................
// 无论是PCM16还是PCM8数据,最终都会调用writeToTrack函数
jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
return samplesWritten;
}
无论是PCM16还是PCM8数据,最终都会调用writeToTrack函数
static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
jint offsetInSamples, jint sizeInSamples, bool blocking) {
// give the data to the native AudioTrack object (the data starts at the offset)
ssize_t written = 0;
// regular write() or copy the data to the AudioTrack's shared memory?
size_t sizeInBytes = sizeInSamples * sizeof(T);
if (track->sharedBuffer() == 0) { // STREAM模式
// 调用write函数写数据
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 {// STATIC模式
// writing to shared memory, check for capacity
if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size();
}
// 在STATIC模式下,直接把数据memcpy到共享内存,但是这种模式下要先调用write然后再play
memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
written = sizeInBytes;
}
if (written >= 0) {
return written / sizeof(T);
}
return interpretWriteSizeError(written);
}
AudioTrack有两种数据加载模式:MODE_STREAM和MODE_STATIC,它们对应着两种完全不同的使用场景。
MODE_STREAM:在这种模式下,通过write一次次把音频数据写到AudioTrack中。这和平时通过write系统调用往文件中写数据类似,但这种工作方式每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引入延时。为解决这一问题,AudioTrack就引入了第二种模式。
MODE_STATIC:这种模式下,在play之前只需要把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用量较小,延时要求较高的文件。但它也有一个缺点,就是一次write的数据不能太多,否则系统无法分配足够的内存来存储全部数据。
5.调用release释放资源
xref: /frameworks/base/media/java/android/media/AudioTrack.java
public void release() {
synchronized (mStreamEventCbLock){
endStreamEventHandling();
}
// even though native_release() stops the native AudioTrack, we need to stop
// AudioTrack subclasses too.
try {
stop();
} catch(IllegalStateException ise) {
// don't raise an exception, we're releasing the resources.
}
baseRelease();
native_release();
synchronized (mPlayStateLock) {
mState = STATE_UNINITIALIZED;
mPlayState = PLAYSTATE_STOPPED;
mPlayStateLock.notify();
}
}
先调用stop方法停止播放,在调用native_release方法释放资源
public void stop()
throws IllegalStateException {
if (mState != STATE_INITIALIZED) {
throw new IllegalStateException("stop() called on uninitialized AudioTrack.");
}
// stop playing
synchronized(mPlayStateLock) {
native_stop();
baseStop();
if (mOffloaded && mPlayState != PLAYSTATE_PAUSED_STOPPING) {
mPlayState = PLAYSTATE_STOPPING;
} else {
mPlayState = PLAYSTATE_STOPPED;
mOffloadEosPending = false;
mAvSyncHeader = null;
mAvSyncBytesRemaining = 0;
mPlayStateLock.notify();
}
}
}
调用native_stop方法
static const JNINativeMethod gMethods[] = {
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
};
................................................................................
android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
{
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for stop()");
return;
}
// 停止播放
lpTrack->stop();
}
停止播放,接下来调用release方法释放资源
static const JNINativeMethod gMethods[] = {
{"native_release", "()V", (void *)android_media_AudioTrack_release},
};
....................................................................................
static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) {
sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
if (lpTrack == NULL) {
return;
}
//ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
// delete the JNI data
AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
thiz, javaAudioTrackFields.jniData);
// 之前保存在Java对象中的指针变量此时都要设置为零
env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
if (pJniStorage) {
Mutex::Autolock l(sLock);
audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
//ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
while (lpCookie->busy) {
if (lpCookie->cond.waitRelative(sLock,
milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
NO_ERROR) {
break;
}
}
sAudioTrackCallBackCookies.remove(lpCookie);
// 删除引用
env->DeleteGlobalRef(lpCookie->audioTrack_class);
env->DeleteGlobalRef(lpCookie->audioTrack_ref);
delete pJniStorage;
}
}
之前保存在Java对象中的指针变量此时都要设置为零,然后删除引用
1819

被折叠的 条评论
为什么被折叠?



