08.音频系统:第004课_Android音频系统详解:第009节_AudioTrack创建过程_Track和共享内存

在上小节中我们讲解了,在AudioTrack创建过程中,他会选择一个output,一个output对应一个播放设备,他也对应着一个播放线程,该小节我们讲解在这个线程之中,怎么去创建一个Track,应用程序的AudioTrack与播放线程之中的Track是一一对应的,并且我们还会讲解应用程序的AudioTrack与播放线程之中的Track他们之间是怎么传递数据的,也就是说,他们是通过怎样的内存传递数据的。

之前提到过,作为声音的应用程序,他需要给playbackThread线程提供数据,他是怎么提供的呢?APP给AudioTrack提供音频数据有2种方式: 一次性提供(MODE_STATIC)、边播放边提供(MODE_STREAM)

对于简单的提示音,其数据是十分的少的,我们可以把所有的数据,一次性提供给播放线程,那么对于播放音乐,播放网络传递过来的声音,那么我们需要边播放,边提供。

那么有以下几个问题:
1.音频数据保存在buffer中,这个buffer由谁提供呢?APP还是PlaybackTHread。
2.APP提供数据,PlaybackThread消耗数据,如何同步。

我们先来画一个图:
在这里插入图片描述
我们知道在android系统之中,其可能有多个声卡,如上的声卡1,声卡2。每个声卡对应一个output,同时每个output对应一个播放线程PlaybackThread,PlaybackThread中存在一个数组mTracks,其中包含一个或者多个Track,每一个Track都对应应用程序中创建的AudioTrack,正是因为应用程序APP创建了AudioTrack才会导致PlaybackThread中Track的创建(注意:APP与PlaybackThread处于不同的进程)。

那么他们是怎么传递数据的呢?虽然可以使用binder通信,但是其通信的效率并不受很高,其实在声音数据,APP与PlaybackThread声音数据的传递是使用共享内存(即APP与PlaybackThread可以访问同一块内存)。

现在我们先来解决第一个问题,这块共享内存是由谁来创建的,是由APP还是由PlaybackThread呢?我们分为两种情况
1.如果APP提供AudioTrack的数据是一次性提供(MODE_STATIC:一次性,提前提供数据),那么显然这个buffer当然是由应用程序提供。因为应用程序才知道这个buffer有多大。
2.如果应用程序,是一边播放,一边提供数据(MODE_STREAM),那么就由PlaybackThread创建共享内存,因为这样省事(为了简单应用程序的编程)

那么应用程序APP与PlaybackThread如何同步数据呢?
1.如果APP提供AudioTrack的数据是一次性提供(MODE_STATIC:一次性,提前提供数据),APP先构造,PlaybackThread再消费,则无需进行同步。
2.如果应用程序,是一边播放,一边提供数据(MODE_STREAM),使用环形缓冲区进行同步。

在这里插入图片描述
我们回答了以上问题,现在我们需要从源码中,验证以上结果。

先打开测试程序shared_mem_test.cpp文件:

int AudioTrackTest::Test01() {
	/*首先自己分配了buffer*/
	iMem = heap->allocate(BUF_SZ*sizeof(short));
	
	/*设置好数据*/
	p = static_cast<uint8_t*>(iMem->pointer());
    memcpy(p, smpBuf, BUF_SZ*sizeof(short));

	/*创建AudioTrack,其会传入共享内存iMem,iMem如果为NULL则MODE_STREAM模式,共享内存由PlaybackThread创建*/
	sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type,
			   rate,
               AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
               AUDIO_CHANNEL_OUT_MONO,
               iMem);

我们打开AudioTrack.cpp查看AudioTrack的构造函数:

AudioTrack::AudioTrack(
	mStatus = set(streamType, sampleRate, format, channelMask.......

以上的是c++的测试程序,之前我们还提到过java的测试程序,打开MediaAudioTrackTest.java文件,随便查看一个函数如:

public void testSetStereoVolumeMax() throws Exception {
	final int TEST_MODE = AudioTrack.MODE_STREAM;
	 AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, minBuffSize, TEST_MODE);	

可以看到,其指定了TEST_MODE = AudioTrack.MODE_STREAM,即为一边播放,一边提供数据(MODE_STREAM)模式,对于C++中,我们是不需要指定的。
我们查看java中AudioTrack的构造函数:

public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int mode, int sessionId)
	int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);

看到native_setup,我们就知道,其会调用C++中的函数,我们在源码中进行搜索native_setup,可以找到android_media_AudioTrack.cpp文件,可以找到其对应函数:

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) {
	lpTrack = new AudioTrack();
	case MODE_STREAM:
		status = lpTrack->set()

       case MODE_STATIC:   
       	/*分配共享内存*/   
       	if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
		status = lpTrack->set()

可以看出,如果为MODE_STREAM没事,则APP不会创建共享内存,如果MODE_STATIC(一次性),则会创建共享内存。

其上C++与java的调用过程总结如下图:
在这里插入图片描述

可以知道无论C++的AudioTrack还是javaAudioTrack最终都是创建了一个c++的AudioTrack对象,即c++的构造函数被调用,进而其中的set函数也被调用。

之前我们强调,创建AudioTrack对象,就会导致某个PlaybackThread中创建一个Track对象,这里截图一下之前的时序图:
在这里插入图片描述
其调用关系就不进行重复了。最后得出结论,APP创建一个AudioTrack对象,导致PlaybackThread中new Trac被创建。打开Tracks.cpp:

AudioFlinger::PlaybackThread::Track::Track(
            PlaybackThread *thread,
            const sp<Client>& client,
            audio_stream_type_t streamType,
            uint32_t sampleRate,
            audio_format_t format,
            audio_channel_mask_t channelMask,
            size_t frameCount,
            void *buffer,
            const sp<IMemory>& sharedBuffer,//该为应用程序创建的共享内存
            audio_session_t sessionId,
            int uid,
            audio_output_flags_t flags,
            track_type type)
     :   TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
                  (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer,
                  sessionId, uid, true /*isOut*/,
                  (type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
                  type),

我们可以看到sharedBuffer,其为应用程序创建的共享内存,其也有可能为空,我们来想象一下Track实例化对象中,他会做什么事情,如果应用程序已经构建了sharedBuffer,那么他自己则不会再去构建,如果应用程序没有构建,即传入的sharedBuffer为空,则其会分配sharedBuffer。

从上可以看出,Track由TrackBase派生出来,我们查看一下TrackBase的构造函数,还是在Tracks.cpp文件中:

AudioFlinger::ThreadBase::TrackBase::TrackBase(
	/*如果buffer为空,并且alloc == ALLOC_CBLK*/
	if (buffer == NULL && alloc == ALLOC_CBLK) {
		/*size为一个头部(起到控制作用),如果buffer为NULL,即应用程序没有分配,则大小增加bufferSize*/
		size += bufferSize;
	/*分配内存*/
	mCblkMemory = client->heap()->allocate(size);
	//如果buffer为应用程序传入
    case ALLOC_NONE:
       mBuffer = buffer; //则mBuffer指向应用程序提供的buffer	

在这里插入图片描述
在TrackBase的构造函数被调用之后,会引起Track的构造函数被调用:

AudioFlinger::PlaybackThread::Track::Track(
	if (sharedBuffer == 0) {
        mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
                mFrameSize, !isExternalTrack(), sampleRate);
    } else {
        mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
                mFrameSize);
    }
    mServerProxy = mAudioTrackServerProxy;

从上知道,如果应用程序没有创建sharedBuffer,那么气会创建一个AudioTrackServerProxy,用来管理Buffer,如果提供了sharedBuffer,则会创建StaticAudioTrackServerProxy,用来管理者一个buffer,到底如何管理呢?下小节为大家讲解。

同样,在应用程序那边,AudioTrack肯定也需要对buffer进行管理,打开AudioTrack.cpp:

status_t AudioTrack::set(
	status_t status = createTrack_l();
	/*使用audioFlinger的服务创建Track,会导致播放线程中创建Track*/
	sp<IAudioTrack> track = audioFlinger->createTrack()
	/*如果没有提供buffer,指向PlaybackThread提供的buffer*/
    if (mSharedBuffer == 0) {
        buffers = cblk + 1;
    } else {
    	/*否则指向自己提供的buffer*/
        buffers = mSharedBuffer->pointer();
        if (buffers == NULL) {
            ALOGE("Could not get buffer pointer");
            return NO_INIT;
        }
    }                                                  
	
	/*管理共享内存*/
    // update proxy
    if (mSharedBuffer == 0) {
        mStaticProxy.clear();
        mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize);
    } else {
        mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize);
        mProxy = mStaticProxy;
    }

可以看看,其与Track中的是意义对应的,AudioTrackClientProxy对应AudioTrackServerProxy,StaticAudioTrackClientProxy对应StaticAudioTrackServerProxy。都是对buffer,共享内存的管理。

APP创建得到AudioTrack对象之后,引起PlaybackThread中Track的创建,具体流程如下:
在这里插入图片描述

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南才尽,年少无知!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值