Android10 音频系统之AudioTrack

        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对象中的指针变量此时都要设置为零,然后删除引用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值