android音频系统(1):AudioTrack

前言:平时开发中经常碰到录音器和音乐播放器,录音器和音乐播放器是音频系统在应用层最直观的体现;

android原生音乐器在播放音乐时用的技术是MeidaPlayer,我一直想知道这个MeidaPlayer是怎么播放音乐的,所以对android的音频系统进行了剖析;

剖析音频系统,肯定是JAVA层和Native层一起分析了;

首先分析的是AudioTrack,它是Audio系统对外提供的API,MeidaPlayer在播放音乐时最终用到的也是它,它是最基本的音频数据输出类;

 

AudioTrack,它是Audio系统对外提供的API,在java层和native层都有对应的类;

1.先来看下在java层的AudioTrack的构造函数:

AudioTrack.java
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
        int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
    this(streamType, sampleRateInHz, channelConfig, audioFormat,
            bufferSizeInBytes, mode, AudioManager.AUDIO_SESSION_ID_GENERATE);
}

我们来分析一下这个构造函数,了解构造函数中参数的实际意义,对了解整个音频系统很重要;

streamType:音频流类型,如AudioManager.STREAM_MUSIC,暂且先将它理解为一个数字就行,它的作用在后面会体现;

sampleRateInHz:采样率,如8000,就表示了每秒8000点,采样率代表了音频的音质,每秒采的点越多,那么数据就越多,数据越多音质就越高;

channelConfig:声道数,如AudioFormat. CHANNEL_ CONFIGURATION_ STEREO,表示双声道,它指的是接收音频的管道有几个,比如人,是靠两只耳朵来接收音频信息的,那这个声道数就是2,有的人也是一只耳朵,那就是1;

audioFormat:音频的编码格式,也可以称为采样精度,精度越高,就代表对这个音频得编码就越细,音频的音质也就越高,如:AudioFormat.ENCODING_PCM_16BIT,表示一个采样点16比特,相当于2个字节;

bufferSizeInBytes:根据音频数据的特性来确定所要分配的缓冲区的最小size,因为流首先是被写在缓冲区里面的;

mode:数据加载模式,如AudioTrack.MODE_STREAM,它只有两种模式,这个后面会解释;

注意:采样率和采样精度越高,音频的音质越好,但是音频所占的内存就越大,这也是为啥网易云音乐中的高音质歌曲在下载时内存有几个G的原因;

 

2.说完了AudioTrack的构造函数,顺便把AudioTrack使用方式来介绍一下吧;

//①创建bufferSizeInBytes缓冲区的大小
int bufsize= AudioTrack. getMinBufferSize( 8000, AudioFormat.CHANNEL_CONFIGURATION_STEREO,AudioFormat. ENCODING_PCM_16BIT);

//②创建AudioTrack
AudioTrack trackplayer= new AudioTrack( AudioManager. STREAM_ MUSIC, AudioFormat. CHANNEL_CONFIGURATION_STEREO,AudioFormat.ENCODING_PCM_16BIT,bufsize,AudioTrack.MODE_STREAM);

//③开始播放
trackplayer. play(); 
......

//④调用write往track中写数据
trackplayer.write(bytes_pkg,0,bytes_pkg.length);
...... 

//⑤停止播放
trackplayer. stop();

//⑥释放底层资源
trackplayer.release();

在创建AudioTrack的时候,我们需要指明音频流类型和数据加载模式,先分析这两个;

①数据加载模式

AudioTrack有两种数据加载模式,MODE_STREAMMODE_STATIC;

MODE_STREAM:日常开发中比较常见的一种模式,这种模式下,通过write一次次把音频数据写到AudioTrack中,也就是先写进去多少,AudioTrack就播放多少,后面再继续写继续播放;这种工作方式由于每次 都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引起延时;

MODE_STATIC:这种数据加载模式,相比于MODE_STREAM就比较简单粗暴了,它在play之前就把所有的数据通过一次write调用传递到AudioTrack的内部缓冲区中,后续就不必再传递数据了,这种模式呢,也有缺点,就是如果一次性写进去的数据太多,会导致系统无法分配足够的内存来存储数据;这种模式适用于像铃声这种内存占用量较小,延时要求比较高的文件;

 

②音频流类型

说起音频,就肯定离不开音频流,因为音频就是以流的形式存在的;不同的音频类型,其实就是音频流不同;

android将系统的声音分为好几种流类型,常见的有:

(1)STREAM_ALARM:警告声;

(2)STREAM_MUSIC:音乐声,例如music;

(3)STREAM_RING:铃声;

(4)STREAM_SYSTEM:系统声音,如低电量提示音,锁屏声音等;

(5)STREAM_VOICE_CALL:通话声

注意:这些类型的划分与音频数据本身并没有关系,如MUSIC和RING类型都可以是某首MP3歌曲,把音频流进行分类,是和Audio系统对音频的管理策略有关,以便于管理;

 

3.在创建AudioTrack的时候,我们需要指明bufferSizeInBytes缓冲区的大小,也就是:

AudioTrack. getMinBufferSize( 8000, AudioFormat.CHANNEL_CONFIGURATION_STEREO,AudioFormat. ENCODING_PCM_16BIT);

我们来分析下这个方法:

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)) {
            return ERROR_BAD_VALUE;
        } else {
            channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
        }
    }

    //目前只支持的音频格式类型,目前只支持PCM8,PCM16等
    if (!AudioFormat.isPublicEncoding(audioFormat)) {
        return ERROR_BAD_VALUE;
    }


    //对编码格式的要求,太高或者太低都不行
    if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
            (sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {
        return ERROR_BAD_VALUE;
    }

    //调用native函数进行计算缓冲区的大小
    int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
    if (size <= 0) {
        return ERROR;
    }
    else {
        return size;
    }
}

点评:上述代码先对音频格式和采样率的合理性进行了判断,然后将缓冲区大小的计算交给了native方法去执行;

有个问题,如果只是个简单计算,为啥在java层不能完成呢?

答案是:还需要确认硬件是否支持上述的音频格式和采样率参数,所以得进入native层去查询;

android_media_AudioTrack.cpp在frameworks\base\core\jni路径下;

继续来看这个代码:native_get_min_buff_size()

android_media_AudioTrack.cpp
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
    jint sampleRateInHertz, jint channelCount, jint audioFormat) {

    //注意我们传进来的参数:sampleRateInHertz为8000,channelCount为2(双声道),audioFormat为AudioFormat. ENCODING_PCM_16BIT

    size_t frameCount;
    //查询硬件内部的缓冲大小,查询结果以Frame为单位,将查出来的Frame的个数保存在frameCount中
    const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
            sampleRateInHertz);
    if (status != NO_ERROR) {
        return -1;
    }
    //返回相应的音频编码格式:这里返回的是AUDIO_FORMAT_PCM_16_BIT
    const audio_format_t format = audioFormatToNative(audioFormat);
    //如果硬件支持该音频编码格式
    if (audio_has_proportional_frames(format)) {
        //根据编码格式来计算每个采样需要多少个字节
        const size_t bytesPerSample = audio_bytes_per_sample(format);
        //得到最小的缓冲大小
        return frameCount * channelCount * bytesPerSample;
    } else {
        return frameCount;
    }
}

点评:帧Frame被用来直观的描述数据量的多少,比如,一帧等于多少字节,在这里,1单位的帧等于1个采样点的字节数x声道数,即2x2=4;

 

4.我们在回到AudioTrack的构造函数,核心代码如下;

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
        int bufferSizeInBytes, int mode, int sessionId)
throws IllegalArgumentException {
    this((new AudioAttributes.Builder())
                .setLegacyStreamType(streamType)
                .build(),
            (new AudioFormat.Builder())
                .setChannelMask(channelConfig)
                .setEncoding(audioFormat)
                .setSampleRate(sampleRateInHz)
                .build(),
            bufferSizeInBytes,
            mode, sessionId);
    deprecateStreamTypeForPlayback(streamType, "AudioTrack", "AudioTrack()");
}

public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
        int mode, int sessionId)
                throws IllegalArgumentException {
    super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
  
    //检查参数是否合法
    audioParamCheck(rate, channelMask, channelIndexMask, encoding, mode);

    //音频流格式,它在上述的audioParamCheck()中已经完成了转换
    mStreamType = AudioSystem.STREAM_DEFAULT;

    //检查缓冲区大小
    audioBuffSizeCheck(bufferSizeInBytes);

    //调用native层的native_setup,进行初始化,我们看下native_setup方法中的几个参数,如mChannelMask,mDataLoadMode等,
    //其实就是我们传递过来的channel和mode,只不过是在检查合法性的时候做了一些修改
    int initResult = native_setup(new WeakReference(this), mAttributes,
            sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
            mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);

    if (mDataLoadMode == MODE_STATIC) {
        //如果当前的数据加载类型是MODE_STATIC
        mState = STATE_NO_STATIC_DATA;
    } else {
        //如果当前的数据加载类型是非MODE_STATIC,那设置当前状态为初始化状态,这个状态很重要
        mState = STATE_INITIALIZED;
    }
}

 

继续来看native_setup()方法,来看下它的核心代码:

代码6: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) {


    //声明Native层的AudioTrack对象
    sp lpTrack = 0;

    //声明Native层的AudioTrackJniStorage对象
    AudioTrackJniStorage* lpJniStorage = NULL;

    if (nativeAudioTrack == 0) {
        //将java层的采样率转化成native层的采样率
        int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
        int sampleRateInHertz = sampleRates[0];
        env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);

        //将java层的声道数转化成native层的声道数
        audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
                channelPositionMask, channelIndexMask);
        uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);

        //检查编码格式,并转化成native层的编码格式
        audio_format_t format = audioFormatToNative(audioFormat);

        //计算以帧为单位的缓冲大小
        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;
        }

        //创建native层的AudioTrack对象
        lpTrack = new AudioTrack();

        //创建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;

        //初始化native层的AudioTrack对象
        status_t status = NO_ERROR;
        switch (memoryMode) {
        case MODE_STREAM:
            //数据加载模式为STREAM模式
            status = lpTrack->set(
                    AUDIO_STREAM_DEFAULT,//指定流类型
                    sampleRateInHertz,
                    format,//采样点精度,即编码格式,一般为PCM16和PCM8
                    nativeChannelMask,
                    frameCount,
                    AUDIO_OUTPUT_FLAG_NONE,
                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
                    0,
                    0,// 共享类型,STREAM模式下为空,实际使用的共享内存有AudioFlinger创建
                    true,
                    sessionId,
                    AudioTrack::TRANSFER_SYNC,
                    NULL,
                    -1, -1,
                    paa);
            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,
                    sampleRateInHertz,
                    format,
                    nativeChannelMask,
                    frameCount,
                    AUDIO_OUTPUT_FLAG_NONE,
                    audioCallback, &(lpJniStorage->mCallbackData),
                    0,
                    lpJniStorage->mMemBase,//STATIC模式,需要传递共享内存
                    true,
                    sessionId,
                    AudioTrack::TRANSFER_SHARED,
                    NULL,
                    -1, -1,
                    paa);
            break;

        default:
            goto native_init_failure;
        }

    }

   
    //把JNI层中new出来的AudioTrack对象指针保存到Java对象的一个变量中,这样就把JNI层的AudioTrack对象和Java层的AudioTrack对象关联起来了,
    //这就是Android的常用技法。
    setAudioTrack(env, thiz, lpTrack);

    //lpJniStorage对象指针也保存到Java对象中。
    env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);

    return (jint) AUDIO_JAVA_SUCCESS;
}

点评:我们看到,

AudioTrack的构造函数,会将自己的状态更改为已经初始化了的状态,并调用native方法,创建在native层的AudioTrack对象,对它进行初始化,并完成JNI层的AudioTrack对象和Java层的AudioTrack对象的关联;

从上述代码我们还可以看到,在不同的数据加载模式下,AudioTrack对象的创建也会不同,在MODE_STATIC模式下进行数据加载,会用到共享内存;

 

5.我们刚分析了AudioTrack的构造函数,接下来分下它的play,write,stop,release方法;

①play()方法

AudioTrack.java
public void play() throws IllegalStateException {
    //如果AudioTrack之前没有被初始化,被报错
    if (mState != STATE_INITIALIZED) {
        throw new IllegalStateException("play() called on uninitialized AudioTrack.");
    }

    final int delay = getStartDelayMs();
    //是否设置播放延迟
    if (delay == 0) {
        //没有延迟
        startImpl();
    } else {
        new Thread() {
            public void run() {
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                baseSetStartDelayMs(0);
                try {
                    startImpl();
                } catch (IllegalStateException e) {
                }
            }
        }.start();
    }
}

private void startImpl() {
    synchronized(mPlayStateLock) {
        baseStart();
        native_start();
        //更改播放状态为:正在播放状态
        mPlayState = PLAYSTATE_PLAYING;
    }
}

 

 

继续进入native方法分析,native_start():

android_media_AudioTrack.cpp
static void android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
    //从java的AudioTrack对象中获取对应的native层的AudioTrack对象指针
    sp lpTrack = getAudioTrack(env, thiz);

    //调用AudioTrack的start方法
    lpTrack->start();
}

native层AudioTrack的start方法后面再来分析;

 

②write()方法

AudioTrack.java
public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
    return write(audioData, offsetInBytes, sizeInBytes, WRITE_BLOCKING);
}


public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
        @WriteMode int writeMode) {

    //调用native的native_write_byte方法
    int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
            writeMode == WRITE_BLOCKING);

    //如果当前的数据加载类型是MODE_STATIC,在write后才会将状态设置为初始化,这跟MODE_STREAM类型是不一样的
    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;
}

继续看native层的native_write_byte方法:

android_media_AudioTrack.cpp
static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
                                                T javaAudioData,
                                                jint offsetInSamples, jint sizeInSamples,
                                                jint javaAudioFormat,
                                                jboolean isWriteBlocking) {
    sp lpTrack = getAudioTrack(env, thiz);
 

    //调用writeToTrack()方法
    jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
            offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);

    envReleaseArrayElements(env, javaAudioData, cAudioData, 0);

    return samplesWritten;
}


static jint writeToTrack(const sp& track, jint audioFormat, const T *data,
                         jint offsetInSamples, jint sizeInSamples, bool blocking) {
    ssize_t written = 0;
    size_t sizeInBytes = sizeInSamples * sizeof(T);
    //track->sharedBuffer() == 0,表示此时的数据加载是STREAM模式,如果不等于0,那就是STATIC模式
    if (track->sharedBuffer() == 0) {
        //STREAM模式,调用write写数据
        written = track->write(data + offsetInSamples, sizeInBytes, blocking);
        if (written == (ssize_t) WOULD_BLOCK) {
            written = 0;
        }
    } else {
        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);
}

 

③stop方法

AudioTrack.java
public void stop()
throws IllegalStateException {
    if (mState != STATE_INITIALIZED) {
        throw new IllegalStateException("stop() called on uninitialized AudioTrack.");
    }

    // stop playing
    synchronized(mPlayStateLock) {
        //调用native方法
        native_stop();
        baseStop();
        //将播放状态置为设置为停止状态
        mPlayState = PLAYSTATE_STOPPED;
        mAvSyncHeader = null;
        mAvSyncBytesRemaining = 0;
    }
}


android_media_AudioTrack.cpp
android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
{
    sp lpTrack = getAudioTrack(env, thiz);
    //调用Native层的stop方法
    lpTrack->stop();
}

Native层的stop方法后面再分析;

 

④release方法

AudioTrack.java
public void release() {
    try {
        //它也会调用stop()
        stop();
    } catch(IllegalStateException ise) {
    }
    baseRelease();
    //调用native方法
    native_release();
    //将状态置为未初始化,下次用到AudioTrack时,需要重新创建AudioTrack,才会将状态置为初始化状态,所以,AudioTrack是一次性使用
    mState = STATE_UNINITIALIZED;
}


android_media_AudioTrack.cpp
static void android_media_AudioTrack_release(JNIEnv *env,  jobject thiz) {
    //释放资源
    sp lpTrack = setAudioTrack(env, thiz, 0);

    // delete the JNI data
    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
        thiz, javaAudioTrackFields.jniData);

    //之前保存在java对象中的指针变量此时要设置为0
    env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
}

点评:java层的AudioTrack分析结束了,java层的AudioTrack只是将工作交给了Native层的AudioTrack;

注意:需要留意下java层AudioTrack中的几个状态,其中mState状态只在AudioTrack被new出来的时候才会被置为STATE_INITIALIZED,而且还要保证此时的数据编码格式为MODE_STREAM,mState状态只有被置为STATE_INITIALIZED后,write,play,stop方法才可以调用,并且在release方法后,mState状态会被置为STATE_UNINITIALIZED,下次要想继续用AudioTrack的话,必须要重新创建它;如果数据编码格式为MODE_STATIC,AudioTrack在调用了write()方法后才会将mState状态置为STATE_INITIALIZED,所以,MODE_STATIC模式下,只能先write,后play,顺序不能乱;

 

6.Native层的AudioTrack分析

通过上面的分析我们知道,在java层new AudioTrack的时候,会创建出在native层的AudioTrack对象,并且调用该对象的set方法进行初始化,native层的AudioTrack在frameworks\av\media\libaudioclient目录下,我们来分析下Native层的AudioTrack;

AudioTrack的无参构造器:

AudioTrack.cpp
AudioTrack::AudioTrack()
    : mStatus(NO_INIT),//把初始状态设置为NO_INIT
      mState(STATE_STOPPED),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mPausedPosition(0),
      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
      mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
      mPortId(AUDIO_PORT_HANDLE_NONE)
{
    mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
    mAttributes.usage = AUDIO_USAGE_UNKNOWN;
    mAttributes.flags = 0x0;
    strcpy(mAttributes.tags, "");
}

AudioTrack的初始化,即set方法:

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,
        int32_t notificationFrames,
        const sp& sharedBuffer,
        bool threadCanCallJava,
        audio_session_t sessionId,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        uid_t uid,
        pid_t pid,
        const audio_attributes_t* pAttributes,
        bool doNotReconnect,
        float maxRequiredSpeed)
{

..................................................
    //cbf是JNI层传入的回调函数audioCallback,如果用户设置了回调函数,则启动一个线程
    if (cbf != NULL) {
        mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
        mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
    }
....................................
    //调用createTrack_l
    status_t status = createTrack_l();
    ....................................
    return NO_ERROR;
}

set()方法很长,大部分语句是用来赋值的,核心代码是 status_t status = createTrack_l();

我们直接来看这个方法:

status_t AudioTrack::createTrack_l()
{
    //得到AudioFlinger的Binder代理端BpAudioFlinger;
    const sp& audioFlinger = AudioSystem::get_audio_flinger();


    //向audioFlinger发送createTrack请求,返回IAudioTrack对象,后续AudioFlinger和AudioTrack的交互就是围绕着IAudioTrack进行
    sp track = audioFlinger->createTrack(streamType,
                                                      mSampleRate,
                                                      mFormat,
                                                      mChannelMask,
                                                      &temp,
                                                      &flags,
                                                      mSharedBuffer,
                                                      output,
                                                      mClientPid,
                                                      tid,
                                                      &mSessionId,
                                                      mClientUid,
                                                      &status,
                                                      mPortId);
    //在STREAM模式下,没有在AudioTrack端创建共享内存,但AudioTrack和AudioFlinger的数据交互是通过共享内存完成的,这块共享内存最终由AudioFlinger创建;
    //下面这个方法会取出AudioFlinger创建的共享内存
    sp iMem = track->getCblk();

    //IMemory的Pointer在此处将返回共享内存的首地址,类型为void *
    void *iMemPointer = iMem->pointer();
    
    //static_cast直接把这个void *类型转换成audio_track_cblk_t,表明这块内存的首部中存在audio_track_cblk_t这个对象
    audio_track_cblk_t* cblk = static_cast(iMemPointer);
    mCblk = cblk;

    void* buffers;
    if (mSharedBuffer == 0) {
        //buffers指向数据空间,它的起始位置是共享内存的首部加上audio_track_cblk_t的大小
        buffers = cblk + 1;
    } else {
        //STATIC模式下的处理
        buffers = mSharedBuffer->pointer();
    }

    return NO_ERROR;
    }

点评:createTrack_l()方法也很长,核心代码是:audioFlinger->createTrack(),返回一个IAudioTrack对象;从上面的代码我们知道,IAudioTrack中有一块共享内存,其头部是一个audio_track_cblk_t对象,在该对象之后才是数据缓冲;

关于这个audio_track_cblk_t对象,它的声明在在AudioTrackShared.h中,定义在AudioTrack.cpp中,它的逻辑很复杂,这里就不详细分析,就说下它的作用:它是用来协调和管理AudioTrack和AudioFlinger二者数据生产和消费的步伐,举个例子,音频输入设备采用环形缓冲的方式管理,当生产者没有及时提供数据时,输出设备就会循环使用缓冲中的数据,这样就会听到一段重复的声音,所以,这里需要用到同步,这就是audio_track_cblk_t对象的作用;

 

7.在上面的代码中,我们看到有这么一句:

mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);

AudioTrackThread有什么用呢?

这个线程与外界数据的输入方式有关系,AudioTrack支持两种数据输入方式:

Push方式:用户主动调用write写数据,这相当于数据被push到AudioTrack;

Pull方式:在JNI层的代码中可以发现,在构造AudioTrack时,传入了一个回调函数audioCallback,pull方式就是AudioTrackThread调用这个回调函数主动从用户那里pull数据,它的具体代码有机会在分析;

 

8.write方法在AudioTrack.cpp中的实现

write函数涉及Audio系统中最重要的问题,即数据是如何传输的;

我们直接来看代码:

ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
    //Buffer是一个辅助性的结构
    Buffer audioBuffer;

    while (userSize >= mFrameSize) {
        //以帧为单位
        audioBuffer.frameCount = userSize / mFrameSize;

        //从共享内存中得到一块空闲的数据块
        status_t err = obtainBuffer(&audioBuffer,
                blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
       
        //空闲数据缓冲区的大小是audioBuffer.size,地址在
        size_t toWrite = audioBuffer.size;
        //地址在audioBuffer.i8中,数据传递通过memcpy来完成
        memcpy(audioBuffer.i8, buffer, toWrite);
        buffer = ((const char *) buffer) + toWrite;
        userSize -= toWrite;
        written += toWrite;
        //更新写位置,同时会触发消费者
        releaseBuffer(&audioBuffer);
    }

    if (written > 0) {
        mFramesWritten += written / mFrameSize;
    }

    return written;
}

点评:通过write函数,发现数据的传递其实是很简单的memcpy,但生产者和消费者协调,则是通过obtainBuffer和releaseBuffer来完成的;

obtainBuffer和releaseBuffer的代码我就不粘贴了,就说下他们的功能:

obtainBuffer的功能,就是从CB管理的数据缓冲中得到一块可写空间,如果没有的话,那就等,而releaseBuffer,则是在使用完这块空间后更新写指针的位置;

 

9.stop方法在AudioTrack.cpp中的实现

void AudioTrack::stop()
{
    //mAudioTrack是IAudioTrack类型,那其stop的最终处理也在AudioFlinger端
  mAudioTrack->stop();

    sp t = mAudioTrackThread;
    if (t != 0) {
        if (!isOffloaded_l()) {
            //请求停止AudioTrackThread
            t->pause();
        }
    } else {
        setpriority(PRIO_PROCESS, 0, mPreviousPriority);
        set_sched_policy(0, mPreviousSchedulingGroup);
    }
}

点评:stop的工作比较简单,就是调用IAudioTrack的stop,并且还要求退出回调线程。

 

10.播放音频在AudioTrack.cpp中的实现

status_t AudioTrack::start()
{
    status = mAudioTrack->start();
}

我们看到,播放操作还是交给了IAudioTrack,也就是AudioFlinger端;

 

10.我们分析完了AudioTrack,来总结一下吧:

①java层的AudioTrack只是上层的api,我们对java层AudioTrack的api调用,实际都会传递到native层的AudioTrack去执行;

②native层的AudioTrack在初始化的时候,会跨进程的调用AudioFlinger的createTrack方法,并返回一个IAudioTrack的对象,native层的AudioTrack以及AudioFlinger借此对象进行交互;
③得到IAudioTrack对象以后,native层AudioTrack的start(),stop等方法的最终操作会传递给AudioFlinger去执行;

④在native层的AudioTrack中将数据write进内存,AudioFlinger通过内存共享与AudioTrack共享同一块资源,从而完成数据的传递;

 

你可能感兴趣的:(音频系统)