AudioTrack分析

934 阅读26分钟

基于Android P版本分析

AudioTrack分析

播放声音可以使用MediaPlayer和AudioTrack,两者都是提供Java API给应用开发者使用。区别在于,MediaPlayer支持多种播放格式,包括mp3、flac、ogg、wav等,而AudioTrack只能播放解码后的PCM数据流;

实际上,MediaPlayer在native层的时候,会创建对应的MediaCodec和AudioTrack,解码后的音频数据本质上也是使用了AudioTrack进行播放。

所以,我们直接分析一下AudioTrack这个类;

AudioTrack API

AudioTrack Java API

首先我们先看一下AudioTrack Java API支持的音频流类型:

StreamTypevalueDescription
STREAM_VOICE_CALL0电话语音
STREAM_SYSTEM1系统声音
STREAM_RING2铃声声音,如来电铃声、闹钟铃声等
STREAM_MUSIC3音乐声音
STREAM_ALARM4警告声音
STREAM_NOTIFICATION5通知音
STREAM_BLUETOOTH_SCO6蓝牙连接语音通话音
STREAM_SYSTEM_ENFORCED7在某些国家实施的系统声音的音频流
STREAM_DTMF8键盘拨号音
STREAM_TTS9文本到语音转换(TTS)的音频流
STREAM_ACCESSIBILITY10可访问声音

这些Stream Type定义在AudioManager.java中,而AudioManager.java又关联了AudioSystem.java。

public class AudioSystem
{
    private static final String TAG = "AudioSystem";
    /* These values must be kept in sync with system/audio.h */
    /*
     * If these are modified, please also update Settings.System.VOLUME_SETTINGS
     * and attrs.xml and AudioManager.java.
     */
    /** Used to identify the default audio stream volume */
    public static final int STREAM_DEFAULT = -1;
    /** Used to identify the volume of audio streams for phone calls */
    public static final int STREAM_VOICE_CALL = 0;
    /** Used to identify the volume of audio streams for system sounds */
    public static final int STREAM_SYSTEM = 1;
    /** Used to identify the volume of audio streams for the phone ring and message alerts */
    public static final int STREAM_RING = 2;
    /** Used to identify the volume of audio streams for music playback */
    public static final int STREAM_MUSIC = 3;
    /** Used to identify the volume of audio streams for alarms */
    public static final int STREAM_ALARM = 4;
    /** Used to identify the volume of audio streams for notifications */
    public static final int STREAM_NOTIFICATION = 5;
    /** Used to identify the volume of audio streams for phone calls when connected on bluetooth */
    public static final int STREAM_BLUETOOTH_SCO = 6;
    /** Used to identify the volume of audio streams for enforced system sounds in certain
     * countries (e.g camera in Japan) */
    public static final int STREAM_SYSTEM_ENFORCED = 7;
    /** Used to identify the volume of audio streams for DTMF tones */
    public static final int STREAM_DTMF = 8;
    /** Used to identify the volume of audio streams exclusively transmitted through the
     *  speaker (TTS) of the device */
    public static final int STREAM_TTS = 9;
    /** Used to identify the volume of audio streams for accessibility prompts */
    public static final int STREAM_ACCESSIBILITY = 10;

AudioSystem中定义了很多StreamType供AudioTrack使用,StreamType类型决定了AudioFlinger使用哪种类型的PlaybackThread进行PCM数据流的播放;

定义上述的流类型,主要是和Android的音频管理策略有关,例如每一种streamType代表这一种类型的音频流数据,在音频流的音量管理中,调节一个类型的音频流音量不会影响其他的音频流音量,同时streamType也可以决定音频流的输出设备,例如streamType为STREAM_BLUETOOTH_SCO时,音频流会通过蓝牙设备输出,比如插着有线耳机期间,音乐声(STREAM_MUSIC)只会输出到有线耳机,而铃声(STREAM_RING)会同时输出到有线耳机和外放;

上述的这些逻辑,其实是属于AudioPolicyService的逻辑,AudioPolicyService用于定义音频管理策略,AudioFlinger用于指定音频管理策略;

AudioTrack中还定义了数据流的传输模式:

Transfer ModevalueDescription
MODE_STATIC0应用进程将回放数据一次性赋给AudioTrack,适用于数据量小、延时要求高的场景
MODE_STREAM1应用进程需要持续调用write()方法将数据写入到FIFO,写数据时有可能遇到阻塞,基本使用所有的音频场景

AudioTrack Native API

与之对应的,在AudioTrack Native层中也定义了对应的API接口和Transfer Type,在MediaPlayer中使用的就是Native层的API定义和TransferType;

AudioTrack Native API音频流类型(定义在/system/media/audio/include/system/audio-base.h):

StreamTypevalueDescription
AUDIO_STREAM_VOICE_CALL0电话语音
AUDIO_STREAM_SYSTEM1系统声音
AUDIO_STREAM_RING2铃声声音,如来电铃声、闹钟铃声等
AUDIO_STREAM_MUSIC3音乐声音
AUDIO_STREAM_ALARM4警告声音
AUDIO_STREAM_NOTIFICATION5通知音
AUDIO_STREAM_BLUETOOTH_SCO6蓝牙连接语音通话音
AUDIO_STREAM_SYSTEM_ENFORCED7强制系统声音
AUDIO_STREAM_DTMF8键盘拨号音
AUDIO_STREAM_TTS9TTS语音
AUDIO_STREAM_ACCESSIBILITY10可访问声音

我们对比Java层的音频流定义,其实发现就是在定义命名上,Native层多了“AUDIO_”的前缀,值还是不变的,和Java层保持一致;

AudioTrack Native 四种数据传输模式:

Transfer TypevalueDescription
TRANSFER_CALLBACK1在AudioTrackThread线程中通过audioCallback回调函数主动从应用进程那里索取数据,ToneGenerator采用这种模式,ToneGenerator就是对应STREAM_DTMF(键盘按键音)
TRANSFER_OBTAIN2应用进程需要调用obtainBuffer()/releaseBuffer()填充数据
TRANSFER_SYNC3应用进程需要持续调用write()方法将数据写入到FIFO,写数据时有可能遇到阻塞,基本使用所有的音频场景,对应MODE_STREAM,其实现逻辑为TRANSFER_OBTAIN的逻辑
TRANSFER_SHARED4应用进程将回放数据一次性赋给AudioTrack,适用于数据量小、延时要求高的场景,对应MODE_STATIC

除了Audio Stream Type和Transfer Type外,Native层还定义了音频输出流的输出标识,我们以常见的几种output Type为例,说明一下:

audio output flagvalueDescription
AUDIO_OUTPUT_FLAG_DIRECT0x1表示音频流直接输出到音频设备,不需要软件混音,一般用于 HDMI 设备声音输出
AUDIO_OUTPUT_FLAG_PRIMARY0x2软件解码、软件混音、采样率转换(SRC)等,表示音频流需要输出到主输出设备,一般用于铃声类声音
AUDIO_OUTPUT_FLAG_FAST0x4表示音频流需要快速输出到音频设备,一般用于按键音、游戏背景音等对时延要求高的场景
AUDIO_OUTPUT_FLAG_DEEP_BUFFER0x8表示音频流输出可以接受较大的时延,一般用于音乐、视频播放等对时延要求不高的场景
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD0x10表示音频流没有经过软件解码,需要输出到硬件解码器,由硬件解码器进行解码

audio_output_flags_t中还定义了多种output flag,这里暂不做过多分析;

我们根据不同的播放场景,使用不同的output flag,如按键音、游戏背景音对输出延时要求高,就需要配置AUDIO_OUTPUT_FLAG_FAST;

audio-base.h中还定义很多其他类型的定义,例如AUDIO_DEVICE_OUT和与之对应的AUDIO_DEVICE_IN,Audio Format、Audio Channel、Audio Source等我们在AudioPolicyService中可能使用到的一些策略配置等;

AudioTrack

我们之前分析了AudioFlinger的架构,然后我们看一下AudioTrack的创建过程以及在创建过程中如何和AudioFlinger建立了连接,用于AudioTrack与AudioFlinger交换数据的匿名共享内存如何分配;

AudioTrack实例创建

int bufferSize = AudioTrack.getMinBufferSize(44100, 2, AudioFormat.ENCODING_PCM_16BIT);
AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder();
attrBuilder.setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
attrBuilder.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC);
mTrack = new AudioTrack.Builder()
        .setAudioAttributes(attrBuilder.build())
        .setAudioFormat(new AudioFormat.Builder()
                .setEncoding(AUDIO_FORMAT)
                .setSampleRate(SAMPLE_RATE)
                .setChannelMask(CHANNEL)
                .build())
        .setTransferMode(AudioTrack.MODE_STREAM)
        .setBufferSizeInBytes(bufferSize)
        .build();

在AudioTrack.Builder的build()方法中创建了AudioTrack的实例:

private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
                   int mode, int sessionId, boolean offload)
    throws IllegalArgumentException {
    super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
    // mState already == STATE_UNINITIALIZED
​
    …………………………
​
    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*/,
                                  offload);
    …………………………
}

AudioTrack构造方法中的核心逻辑为native层的native_setup()方法;

android_media_AudioTrack_setup

/frameworks/base/core/jni/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, jboolean offload) {
​
    ……………………
​
    sp<AudioTrack> lpTrack = 0;
​
    ……………………
​
    AudioTrackJniStorage* lpJniStorage = NULL;
​
    audio_attributes_t *paa = NULL;
​
    ……………………
​
    // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
    if (nativeAudioTrack == 0) {
        ……………………
​
        int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
        int sampleRateInHertz = sampleRates[0];
        env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
​
        // 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_has_proportional_frames(format)) {
            const size_t bytesPerSample = audio_bytes_per_sample(format);
            frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
        } else {
            frameCount = buffSizeInBytes;
        }
​
        // create the native AudioTrack object
        lpTrack = new AudioTrack();
​
        // read the AudioAttributes values
        // 根据audio_attributes_t结构体的大小,创建相应的大小空间;
        paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
        const jstring jtags =
                (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
        const char* tags = env->GetStringUTFChars(jtags, NULL);
        // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
        strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
        env->ReleaseStringUTFChars(jtags, tags);
        paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
        paa->content_type =
                (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
        paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
​
        ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
                paa->usage, paa->content_type, paa->flags, paa->tags);
​
        // initialize the callback information:
        // this data will be passed with every AudioTrack callback
        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.isOffload = offload;
        lpJniStorage->mCallbackData.busy = false;
​
        audio_offload_info_t offloadInfo;
        if (offload) {
            offloadInfo = AUDIO_INFO_INITIALIZER;
            offloadInfo.format = format;
            offloadInfo.sample_rate = sampleRateInHertz;
            offloadInfo.channel_mask = nativeChannelMask;
            offloadInfo.has_video = false;
            offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
        }
​
        // 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,
                    offload ? &offloadInfo : NULL,
                    -1, -1,                       // default uid, pid values
                    paa);
            break;
​
        case MODE_STATIC:
            // AudioTrack is using shared memory
​
            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,// 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:
            ALOGE("Unknown mode %d", memoryMode);
            goto native_init_failure;
        }
​
        ……………………
    } else {  // end if (nativeAudioTrack == 0)
        ……………………
    }
​
    ……………………
    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());
    if (paa != NULL) {
        // audio attributes were copied in AudioTrack creation
        free(paa);
        paa = NULL;
    }
​
​
    return (jint) AUDIO_JAVA_SUCCESS;
​
    // failures:
native_init_failure:
    if (paa != NULL) {
        free(paa);
    }
    if (nSession != NULL) {
        env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    }
    env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
    env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
    delete lpJniStorage;
    env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
​
    // lpTrack goes out of scope, so reference count drops to zero
    return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}

在JNI中,主要做了几个事件:

  • 创建AudioTrack、AudioTrackJniStorage,AudioTrackJniStorage用于存储PCM数据;
  • 将创建好的AudioTrack对象进行配置,调用set()函数,通过memoryMode类型,即上述过程中描述过的Stream Mode Type,分为MODE_STATIC和MODE_STREAM两种类型,其中分别对应了AudioTrack::TRANSFER_SHARED和AudioTrack::TRANSFER_SYNC;
  • AudioTrack对象创建并配置成功之后,调用setAudioTrack()函数将该对象保存到JNI中,供后续过程中使用;

其中调用set()函数中,默认传入的都是AUDIO_STREAM_DEFAULT类型的streamType,output flag为AUDIO_OUTPUT_FLAG_NONE;

new AudioTrack
AudioTrack::AudioTrack()
    : mStatus(NO_INIT),
      mState(STATE_STOPPED),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mPausedPosition(0),
      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
      mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
    mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
    mAttributes.usage = AUDIO_USAGE_UNKNOWN;
    mAttributes.flags = 0x0;
    strcpy(mAttributes.tags, "");
}

在AudioTrack的构造函数中,首先先对mAttributes中的一些属性进行了初始化,都为默认值;

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<IMemory>& 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,
        audio_port_handle_t selectedDeviceId)
{
    status_t status;
    uint32_t channelCount;
    pid_t callingPid;
    pid_t myPid;
​
    ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
          "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
          streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
          sessionId, transferType, uid, pid);
​
    mThreadCanCallJava = threadCanCallJava;
    mSelectedDeviceId = selectedDeviceId;
    mSessionId = sessionId;
​
    switch (transferType) {
    case TRANSFER_DEFAULT:
        if (sharedBuffer != 0) {
            transferType = TRANSFER_SHARED;
        } else if (cbf == NULL || threadCanCallJava) {
            transferType = TRANSFER_SYNC;
        } else {
            transferType = TRANSFER_CALLBACK;
        }
        break;
    case TRANSFER_CALLBACK:
        if (cbf == NULL || sharedBuffer != 0) {
            ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL || sharedBuffer != 0");
            status = BAD_VALUE;
            goto exit;
        }
        break;
    case TRANSFER_OBTAIN:
    case TRANSFER_SYNC:
        if (sharedBuffer != 0) {
            ALOGE("Transfer type TRANSFER_OBTAIN but sharedBuffer != 0");
            status = BAD_VALUE;
            goto exit;
        }
        break;
    case TRANSFER_SHARED:
        if (sharedBuffer == 0) {
            ALOGE("Transfer type TRANSFER_SHARED but sharedBuffer == 0");
            status = BAD_VALUE;
            goto exit;
        }
        break;
    default:
        ALOGE("Invalid transfer type %d", transferType);
        status = BAD_VALUE;
        goto exit;
    }
    mSharedBuffer = sharedBuffer;
    mTransfer = transferType;
    mDoNotReconnect = doNotReconnect;
​
    ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %zu", sharedBuffer->pointer(),
            sharedBuffer->size());
​
    ALOGV("set() streamType %d frameCount %zu flags %04x", streamType, frameCount, flags);
​
    // invariant that mAudioTrack != 0 is true only after set() returns successfully
    if (mAudioTrack != 0) {
        ALOGE("Track already in use");
        status = INVALID_OPERATION;
        goto exit;
    }
​
    // handle default values first.
    if (streamType == AUDIO_STREAM_DEFAULT) {
        streamType = AUDIO_STREAM_MUSIC;
    }
    if (pAttributes == NULL) {
        if (uint32_t(streamType) >= AUDIO_STREAM_PUBLIC_CNT) {
            ALOGE("Invalid stream type %d", streamType);
            status = BAD_VALUE;
            goto exit;
        }
        mStreamType = streamType;
​
    } else {
        // stream type shouldn't be looked at, this track has audio attributes
        memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
        ALOGV("Building AudioTrack with attributes: usage=%d content=%d flags=0x%x tags=[%s]",
                mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags);
        mStreamType = AUDIO_STREAM_DEFAULT;
        if ((mAttributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
            flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
        }
        if ((mAttributes.flags & AUDIO_FLAG_LOW_LATENCY) != 0) {
            flags = (audio_output_flags_t) (flags | AUDIO_OUTPUT_FLAG_FAST);
        }
        // check deep buffer after flags have been modified above
        if (flags == AUDIO_OUTPUT_FLAG_NONE && (mAttributes.flags & AUDIO_FLAG_DEEP_BUFFER) != 0) {
            flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
        }
    }
​
    // these below should probably come from the audioFlinger too...
    if (format == AUDIO_FORMAT_DEFAULT) {
        format = AUDIO_FORMAT_PCM_16_BIT;
    } else if (format == AUDIO_FORMAT_IEC61937) { // HDMI pass-through?
        mAttributes.flags |= AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO;
    }
​
    // validate parameters
    if (!audio_is_valid_format(format)) {
        ALOGE("Invalid format %#x", format);
        status = BAD_VALUE;
        goto exit;
    }
    mFormat = format;
​
    if (!audio_is_output_channel(channelMask)) {
        ALOGE("Invalid channel mask %#x", channelMask);
        status = BAD_VALUE;
        goto exit;
    }
    mChannelMask = channelMask;
    channelCount = audio_channel_count_from_out_mask(channelMask);
    mChannelCount = channelCount;
​
    // force direct flag if format is not linear PCM
    // or offload was requested
    if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
            || !audio_is_linear_pcm(format)) {
        ALOGV( (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
                    ? "Offload request, forcing to Direct Output"
                    : "Not linear PCM, forcing to Direct Output");
        flags = (audio_output_flags_t)
                // FIXME why can't we allow direct AND fast?
                ((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST);
    }
​
    // force direct flag if HW A/V sync requested
    if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
        flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);
    }
​
    if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {
        if (audio_has_proportional_frames(format)) {
            mFrameSize = channelCount * audio_bytes_per_sample(format);
        } else {
            mFrameSize = sizeof(uint8_t);
        }
    } else {
        ALOG_ASSERT(audio_has_proportional_frames(format));
        mFrameSize = channelCount * audio_bytes_per_sample(format);
        // createTrack will return an error if PCM format is not supported by server,
        // so no need to check for specific PCM formats here
    }
​
    // sampling rate must be specified for direct outputs
    if (sampleRate == 0 && (flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
        status = BAD_VALUE;
        goto exit;
    }
    mSampleRate = sampleRate;
    mOriginalSampleRate = sampleRate;
    mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
    // 1.0 <= mMaxRequiredSpeed <= AUDIO_TIMESTRETCH_SPEED_MAX
    mMaxRequiredSpeed = min(max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
​
    // Make copy of input parameter offloadInfo so that in the future:
    //  (a) createTrack_l doesn't need it as an input parameter
    //  (b) we can support re-creation of offloaded tracks
    if (offloadInfo != NULL) {
        mOffloadInfoCopy = *offloadInfo;
        mOffloadInfo = &mOffloadInfoCopy;
    } else {
        mOffloadInfo = NULL;
        memset(&mOffloadInfoCopy, 0, sizeof(audio_offload_info_t));
    }
​
    mVolume[AUDIO_INTERLEAVE_LEFT] = 1.0f;
    mVolume[AUDIO_INTERLEAVE_RIGHT] = 1.0f;
    mSendLevel = 0.0f;
    // mFrameCount is initialized in createTrack_l
    mReqFrameCount = frameCount;
    if (notificationFrames >= 0) {
        mNotificationFramesReq = notificationFrames;
        mNotificationsPerBufferReq = 0;
    } else {
        if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
            ALOGE("notificationFrames=%d not permitted for non-fast track",
                    notificationFrames);
            status = BAD_VALUE;
            goto exit;
        }
        if (frameCount > 0) {
            ALOGE("notificationFrames=%d not permitted with non-zero frameCount=%zu",
                    notificationFrames, frameCount);
            status = BAD_VALUE;
            goto exit;
        }
        mNotificationFramesReq = 0;
        const uint32_t minNotificationsPerBuffer = 1;
        const uint32_t maxNotificationsPerBuffer = 8;
        mNotificationsPerBufferReq = min(maxNotificationsPerBuffer,
                max((uint32_t) -notificationFrames, minNotificationsPerBuffer));
        ALOGW_IF(mNotificationsPerBufferReq != (uint32_t) -notificationFrames,
                "notificationFrames=%d clamped to the range -%u to -%u",
                notificationFrames, minNotificationsPerBuffer, maxNotificationsPerBuffer);
    }
    mNotificationFramesAct = 0;
    callingPid = IPCThreadState::self()->getCallingPid();
    myPid = getpid();
    if (uid == AUDIO_UID_INVALID || (callingPid != myPid)) {
        mClientUid = IPCThreadState::self()->getCallingUid();
    } else {
        mClientUid = uid;
    }
    if (pid == -1 || (callingPid != myPid)) {
        mClientPid = callingPid;
    } else {
        mClientPid = pid;
    }
    mAuxEffectId = 0;
    mOrigFlags = mFlags = flags;
    mCbf = cbf;
​
    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 = createTrack_l();
​
    if (status != NO_ERROR) {
        if (mAudioTrackThread != 0) {
            mAudioTrackThread->requestExit();   // see comment in AudioTrack.h
            mAudioTrackThread->requestExitAndWait();
            mAudioTrackThread.clear();
        }
        goto exit;
    }
​
    mUserData = user;
    mLoopCount = 0;
    mLoopStart = 0;
    mLoopEnd = 0;
    mLoopCountNotified = 0;
    mMarkerPosition = 0;
    mMarkerReached = false;
    mNewPosition = 0;
    mUpdatePeriod = 0;
    mPosition = 0;
    mReleased = 0;
    mStartNs = 0;
    mStartFromZeroUs = 0;
    AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
    mSequence = 1;
    mObservedSequence = mSequence;
    mInUnderrun = false;
    mPreviousTimestampValid = false;
    mTimestampStartupGlitchReported = false;
    mRetrogradeMotionReported = false;
    mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID;
    mStartTs.mPosition = 0;
    mUnderrunCountOffset = 0;
    mFramesWritten = 0;
    mFramesWrittenServerOffset = 0;
    mFramesWrittenAtRestore = -1; // -1 is a unique initializer.
    mVolumeHandler = new media::VolumeHandler();
​
exit:
    mStatus = status;
    return status;
}

我们按照set()函数中的执行顺序,看一下该函数中执行了哪些逻辑:

  1. set()函数刚进入,就对transferType进行判断,transferType就是我们刚刚提及到的TRANSFER_SYNC和TRANSFER_SHARED,TRANSFER_CALLBACK和TRANSFER_OBTAIN不会暴露给应用层;

  2. 对streamType进行判断,如果为默认值AUDIO_STREAM_DEFAULT(值为-1),则将streamType修改为AUDIO_STREAM_MUSIC,这里的streamType只是AudioTrack创建时传入的streamType,AudioTrack.cpp中的为mStreamType;

  3. 对传入的AudioAttribute对象进行判空,如果不为空,则将AudioTrack创建时传入的AudioAttribute对象赋值给AudioTrack.cpp中的mAttribute对象,同时将mStreamType赋值为了AUDIO_STREAM_DEFAULT;

  4. 对audioFormat、audio output flag、sampleRate等一些传入的参数进行判断并保存到AudioTrack.cpp中;

  5. 根据传入的cbf(类型为callback_t,在android_media_AudioTrack.cpp中定义)创建对应的AudioTrackThread线程,用于处理audioCallback回调函数(MODE_STREAM 模式时,cbf 为空);

    AudioTrackThread线程是用于AudioTrack(native)与AudioTrack(Java)之间的数据事件通知的;

  6. 调用了createTrack_l()函数,在该函数中获取到AudioServer启动的时候初始化好的AudioFlinger对象,调用了AudioFlinger的createTrack()函数创建对应的PlaybackThread::Track,然后根据Mode_Type(STREAM or STATIC)来创建对应的AudioTrackClientProxy对象;

    status_t AudioTrack::createTrack_l()
    {
        status_t status;
        bool callbackAdded = false;
    ​
        ……………………
    ​
        IAudioFlinger::CreateTrackInput input;
        if (mStreamType != AUDIO_STREAM_DEFAULT) {
            stream_type_to_audio_attributes(mStreamType, &input.attr);
        } else {
            input.attr = mAttributes;
        }
        input.config = AUDIO_CONFIG_INITIALIZER;
        input.config.sample_rate = mSampleRate;
        input.config.channel_mask = mChannelMask;
        input.config.format = mFormat;
        input.config.offload_info = mOffloadInfoCopy;
        input.clientInfo.clientUid = mClientUid;
        input.clientInfo.clientPid = mClientPid;
        input.clientInfo.clientTid = -1;
        if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
            // It is currently meaningless to request SCHED_FIFO for a Java thread.  Even if the
            // application-level code follows all non-blocking design rules, the language runtime
            // doesn't also follow those rules, so the thread will not benefit overall.
            if (mAudioTrackThread != 0 && !mThreadCanCallJava) {
                input.clientInfo.clientTid = mAudioTrackThread->getTid();
            }
        }
        input.sharedBuffer = mSharedBuffer;
        input.notificationsPerBuffer = mNotificationsPerBufferReq;
        input.speed = 1.0;
        if (audio_has_proportional_frames(mFormat) && mSharedBuffer == 0 &&
                (mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
            input.speed  = !isPurePcmData_l() || isOffloadedOrDirect_l() ? 1.0f :
                            max(mMaxRequiredSpeed, mPlaybackRate.mSpeed);
        }
        input.flags = mFlags;
        input.frameCount = mReqFrameCount;
        input.notificationFrameCount = mNotificationFramesReq;
        input.selectedDeviceId = mSelectedDeviceId;
        input.sessionId = mSessionId;
    ​
        IAudioFlinger::CreateTrackOutput output;
    ​
        sp<IAudioTrack> track = audioFlinger->createTrack(input,
                                                          output,
                                                          &status);
    ​
        ……………………
    }
    

    在这其中涉及到了两个类:CreateTrackInput和CreateTrackOutput,都继承自Parcelable,用于跨进程,input中定义的就是audio_attributes、audio_config_t、AudioClient等一些相关信息;output用于接收Track创建成功后的一些信息值;

  7. 最后根据上述过程获取到的一些配置信息,对AudioTrack.cpp中的一些变量进行赋值,即保存应用层传入的信息;

AudioFlinger::createTrack

AudioFlinger::createTrack是AudioTrack的核心逻辑,用于创建AudioTrack对应的PlaybackThread::Track:

sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
                                          CreateTrackOutput& output,
                                          status_t *status)
{
    sp<PlaybackThread::Track> track;
    sp<TrackHandle> trackHandle;
    sp<Client> client;
    status_t lStatus;
    audio_stream_type_t streamType;
    audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
​
    bool updatePid = (input.clientInfo.clientPid == -1);
    const uid_t callingUid = IPCThreadState::self()->getCallingUid();
    uid_t clientUid = input.clientInfo.clientUid;
    if (!isTrustedCallingUid(callingUid)) {
        ALOGW_IF(clientUid != callingUid,
                "%s uid %d tried to pass itself off as %d",
                __FUNCTION__, callingUid, clientUid);
        clientUid = callingUid;
        updatePid = true;
    }
    pid_t clientPid = input.clientInfo.clientPid;
    if (updatePid) {
        const pid_t callingPid = IPCThreadState::self()->getCallingPid();
        ALOGW_IF(clientPid != -1 && clientPid != callingPid,
                 "%s uid %d pid %d tried to pass itself off as pid %d",
                 __func__, callingUid, callingPid, clientPid);
        clientPid = callingPid;
    }
​
    audio_session_t sessionId = input.sessionId;
    if (sessionId == AUDIO_SESSION_ALLOCATE) {
        sessionId = (audio_session_t) newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
    } else if (audio_unique_id_get_use(sessionId) != AUDIO_UNIQUE_ID_USE_SESSION) {
        lStatus = BAD_VALUE;
        goto Exit;
    }
​
    output.sessionId = sessionId;
    output.outputId = AUDIO_IO_HANDLE_NONE;
    output.selectedDeviceId = input.selectedDeviceId;
​
    // outputId的类型为audio_io_handle_t,也就是AudioTrack的唯一标识,对应唯一的output device;
    lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
                                            clientPid, clientUid, &input.config, input.flags,
                                            &output.selectedDeviceId, &portId);
​
    if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
        ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
        goto Exit;
    }
    // client AudioTrack::set already implements AUDIO_STREAM_DEFAULT => AUDIO_STREAM_MUSIC,
    // but if someone uses binder directly they could bypass that and cause us to crash
    if (uint32_t(streamType) >= AUDIO_STREAM_CNT) {
        ALOGE("createTrack() invalid stream type %d", streamType);
        lStatus = BAD_VALUE;
        goto Exit;
    }
​
    // further channel mask checks are performed by createTrack_l() depending on the thread type
    if (!audio_is_output_channel(input.config.channel_mask)) {
        ALOGE("createTrack() invalid channel mask %#x", input.config.channel_mask);
        lStatus = BAD_VALUE;
        goto Exit;
    }
​
    // further format checks are performed by createTrack_l() depending on the thread type
    if (!audio_is_valid_format(input.config.format)) {
        ALOGE("createTrack() invalid format %#x", input.config.format);
        lStatus = BAD_VALUE;
        goto Exit;
    }
​
    {
        Mutex::Autolock _l(mLock);
        PlaybackThread *thread = checkPlaybackThread_l(output.outputId);
        if (thread == NULL) {
            ALOGE("no playback thread found for output handle %d", output.outputId);
            lStatus = BAD_VALUE;
            goto Exit;
        }
​
        client = registerPid(clientPid);
​
        PlaybackThread *effectThread = NULL;
        // check if an effect chain with the same session ID is present on another
        // output thread and move it here.
        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
            sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
            if (mPlaybackThreads.keyAt(i) != output.outputId) {
                uint32_t sessions = t->hasAudioSession(sessionId);
                if (sessions & ThreadBase::EFFECT_SESSION) {
                    effectThread = t.get();
                    break;
                }
            }
        }
        ALOGV("createTrack() sessionId: %d", sessionId);
​
        output.sampleRate = input.config.sample_rate;
        output.frameCount = input.frameCount;
        output.notificationFrameCount = input.notificationFrameCount;
        output.flags = input.flags;
​
        track = thread->createTrack_l(client, streamType, input.attr, &output.sampleRate,
                                      input.config.format, input.config.channel_mask,
                                      &output.frameCount, &output.notificationFrameCount,
                                      input.notificationsPerBuffer, input.speed,
                                      input.sharedBuffer, sessionId, &output.flags,
                                      input.clientInfo.clientTid, clientUid, &lStatus, portId);
        LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
        // we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
​
        output.afFrameCount = thread->frameCount();
        output.afSampleRate = thread->sampleRate();
        output.afLatencyMs = thread->latency();
​
        // move effect chain to this output thread if an effect on same session was waiting
        // for a track to be created
        if (lStatus == NO_ERROR && effectThread != NULL) {
            // no risk of deadlock because AudioFlinger::mLock is held
            Mutex::Autolock _dl(thread->mLock);
            Mutex::Autolock _sl(effectThread->mLock);
            moveEffectChain_l(sessionId, effectThread, thread, true);
        }
​
        // Look for sync events awaiting for a session to be used.
        for (size_t i = 0; i < mPendingSyncEvents.size(); i++) {
            if (mPendingSyncEvents[i]->triggerSession() == sessionId) {
                if (thread->isValidSyncEvent(mPendingSyncEvents[i])) {
                    if (lStatus == NO_ERROR) {
                        (void) track->setSyncEvent(mPendingSyncEvents[i]);
                    } else {
                        mPendingSyncEvents[i]->cancel();
                    }
                    mPendingSyncEvents.removeAt(i);
                    i--;
                }
            }
        }
​
        setAudioHwSyncForSession_l(thread, sessionId);
    }
​
    if (lStatus != NO_ERROR) {
        // remove local strong reference to Client before deleting the Track so that the
        // Client destructor is called by the TrackBase destructor with mClientLock held
        // Don't hold mClientLock when releasing the reference on the track as the
        // destructor will acquire it.
        {
            Mutex::Autolock _cl(mClientLock);
            client.clear();
        }
        track.clear();
        goto Exit;
    }
​
    // return handle to client
    trackHandle = new TrackHandle(track);
​
Exit:
    if (lStatus != NO_ERROR && output.outputId != AUDIO_IO_HANDLE_NONE) {
        AudioSystem::releaseOutput(output.outputId, streamType, sessionId);
    }
    *status = lStatus;
    return trackHandle;
}
  1. 首先先定义一个变量,例如track、trackHandle、client、streamType等;

  2. 根据CreateTrackInput对象input获取一下基本的状态信息;

  3. 根据input信息(streamType、flags等参数)调用AudioSystem::getOutputForAttr()函数,经过一系列的调用,进入到AudioPolicyManager::getOutputForDevice()函数中:

    • 如果输出标识置了AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD或AUDIO_OUTPUT_FLAG_DIRECT,那么最终调用AudioFlinger::openOutput()打开输出标识对应的输出流设备并创建相应的PlaybackThread,保存该PlaybackThread对应的audio_io_handle_t给AudioTrack;
    • 如果输出标识是其他类型,那么根据策略选择一个输出流设备和PlaybackThread,并保存该PlaybackThread对应的audio_io_handle_t给AudioTrack;因为在系统启动时,就已经打开primary_out、low_latency、deep_buffer这三种输出流设备,并创建对应的PlaybackThread了;
  4. 对streamType进行判断,这一块的streamType其实就是应用层定义的streamType,一直通过Attribute对象传到了AudioPolicyManager的getOutputForDevice()函数中,在该函数中通过调用stream_type_to_audio_attributes()函数利用attr.usage确定了对应的streamType类型;

  5. 紧接着调用了checkPlaybackThread_l()函数,根据在getOutputForAttr()函数中获取到的audio_io_handle_t类型的outputId,从mPlaybackThreads集合中查找对应的PlaybackThread对象thread;

  6. PlaybackThread对象查找成功之后,紧接着就是根据audio config信息,包括attr、sampleRate、format、flag等一些信息通过thread->createTrack()函数创建对应的Track对象;

  7. Track对象创建成功之后,将Track的一些状态信息保存到output(类型为CreateTrackOutput)中;

  8. 创建TrackHandle对象,其中封装了track;

FIFO控制块

参考:audio_track_cblk_t

Track创建成功之后,就涉及到了audio_track_cblk_t这个概念,audio_track_cblk_t这个结构是FIFO实现的关键,该结构是在createTrack的时候,由AudioFlinger申请相应的内存,然后通过IMemory接口返回AudioTrack,通过track->getCblk()函数获取到IMemory对象;

……………………
sp<IMemory> iMem = track->getCblk();
if (iMem == 0) {
    ALOGE("Could not get control block");
    status = NO_INIT;
    goto exit;
}
void *iMemPointer = iMem->pointer();
if (iMemPointer == NULL) {
    ALOGE("Could not get control block pointer");
    status = NO_INIT;
    goto exit;
}
// invariant that mAudioTrack != 0 is true only after set() returns successfully
if (mAudioTrack != 0) {
    IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
    mDeathNotifier.clear();
}
mAudioTrack = track;
mCblkMemory = iMem;
IPCThreadState::self()->flushCommands();
​
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
mCblk = cblk;
……………………

这样AudioTrack和AudioFlinger管理着同一个audio_track_cblk_t,通过它实现了环形FIFO,AudioTrack向FIFO中写入音频数据,AudioFlinger从FIFO中读取音频数据,经Mixer后送给AudioHardware进行播放;

audio_track_cblk_t的主要数据成员:

  • user:AudioTrack当前的写位置的偏移
  • userBase:AudioTrack写偏移的基准位置,结合user的值方可确定真实FIFO地址指针
  • server:AudioFlinger当前的读位置的偏移
  • serverBase:AudioFlinger读偏移的基准位置,结合server的值方可确定真实的FIFO地址指针
  • frameCount:FIFO的大小,以音频数据的帧为单位,16bit的音频每帧的大小是2字节
  • buffers:指向FIFO的起始地址
  • out:音频流的方向,对于AudioTrack,out=1,对于AudioRecord,out=0

audio_track_cblk_t的主要成员函数:

  • framesAvailable_l()和framesAvailable():用于获取FIFO中可写的空闲空间的大小,只是加锁和不加锁的区别
  • framesReady():用于获取FIFO中可读取的空间大小
AudioTrackClientProxy

audio_track_cblk_t创建成功之后,就利用该对象创建了AudioTrackClientProxy对象,AudioTrack将使用它从FIFO上取得可用空间的位置;

我们这里描述一下其余几个相关的Proxy:

Proxy TypeDescription
AudioTrackClientProxyMODE_STREAM 模式下,生产者 AudioTrack 使用它在 FIFO 中找到可用空间的位置
AudioTrackServerProxyMODE_STREAM 模式下,消费者 AudioFlinger::PlaybackThread 使用它在 FIFO 中找到可读数据的位置
StaticAudioTrackClientProxyMODE_STATIC 模式下,生产者 AudioTrack 使用它在 FIFO 中找到可用空间的位置
StaticAudioTrackServerProxyMODE_STATIC 模式下,消费者 AudioFlinger::PlaybackThread 使用它在 FIFO 中找到可读数据的位置
总结

AudioTrack 由此建立了和 AudioFlinger 的全部联系工作:

  • 通过 IAudioTrack 接口可以控制该音轨的状态,例如 start、stop、pause
  • 持续写入数据到 FIFO 上,实现音频连续播放
  • 通过 audio_io_handle_t,可以找到它对应的 PlaybackThread,从而查询该 PlaybackThread 的相关信息,如所设置的采样率、格式等等

Track对象仅仅负责音频相关业务,对外并没有提供夸进程的Binder调用接口,因此须要将通信业务托付给另外一个对象来完毕。这就是TrackHandle存在的意义,TrackHandle负责代理Track的通信业务。它是Track与AudioTrack之间的跨进程通道;

TrackHandle架构.png

构造 1 个 AudioTrack 实例时,AudioFlinger 会有 1 个 PlaybackThread 实例、1 个 Track 实例、1 个 TrackHandle 实例、1 个 AudioTrackServerProxy 实例、1 块 FIFO 与之对应;

AudioTrack数据读写

AudioTrack实例构造后,应用程序接着可以写入音频数据了。

AudioTrack和AudioFlinger之间的关系是生产者和消费者的关系,AudioTrack从文件中读取PCM数据写入到FIFO环形缓冲区中,AudioFlinger监听FIFO环形缓冲区,从中读取AudioTrack写入的PCM数据。AudioTrack 和 AudioFlinger 是不同的进程,AudioFlinger 同时也在不停地读取数据,所以 FIFO 可用空间是在不停变化的;

  • AudioTrack:AudioTrack在FIFO中找到一块可用的空间,把用户传入的音频数据写入到这块可用空间上,然后更新写位置(对于AudioFlinger来说,意味着FIFO上有更多的可读数据)。如果用户传入的数据量比可用空间要大,那么要把用户传入的数据拆分多次写入到FIFO中;
  • AudioFlinger:AudioFlinger 在 FIFO 中找到一块可读数据块,把可读数据拷贝到目的缓冲区上,然后更新读位置(对于 AudioTrack 来说,意味着 FIFO 上有更多的可用空间了);如果FIFO 上可读数据量比预期的要小,那么要进行多次的读取,才能积累到预期的数据量;

上面的过程中,如果 AudioTrack 总能及时生产数据,并且 AudioFlinger 总能及时消耗掉这些数据,那么整个过程将是非常和谐的;但系统可能会发生异常,出现如下的状态:

  • Block:

    • 状态:AudioFlinger长时间不读取FIFO上的可读数据,使得AudioTrack长时间获取不到可用空间,无法写入数据;
    • 原因:这种情况的根本原因大多是底层驱动发生阻塞异常,导致AudioFlinger无法继续写数据到硬件设备中;
  • Underrun:

    • 状态:AudioTrack 写入数据的速度跟不上 AudioFlinger 读取数据的速度,使得 AudioFlinger 不能及时获取到预期的数据量,反映到现实的后果就是声音断续;
    • 原因:这种情况的根本原因大多是应用程序不能及时写入数据或者缓冲区分配过小;
    • AudioFlinger针对这点做了容错处理:当发现underrun时,先陷入短时间的睡眠,不急着读取数据,让应用程序准备更多的数据;
AudioTrack写入
byte[] buffer = new byte[4096];
while (true) { // 模拟判断条件,这个由应用层定义
    mTrack.write(buffer, 0, 4096);
    if (false) { // 模拟判断条件,这个由应用层定义
        mTrack.stop();
        mTrack.release();
    }
}
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) {
​
    ……………………
​
    int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
                                writeMode == WRITE_BLOCKING);
​
    ……………………
​
    return ret;
}
template <typename T>
static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
                                                T javaAudioData,
                                                jint offsetInSamples, jint sizeInSamples,
                                                jint javaAudioFormat,
                                                jboolean isWriteBlocking) {
    ……………………
​
    jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
            offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
​
    ……………………
}
​
template <typename T>
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) {
        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;
        }
    } 
    ……………………
    return interpretWriteSizeError(written);
}

在writeToTrack()函数中,调用了AudioTrack的write()函数:

ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
    if (mTransfer != TRANSFER_SYNC) {
        return INVALID_OPERATION;
    }
​
    ……………………
​
    size_t written = 0;
    Buffer audioBuffer;
​
    while (userSize >= mFrameSize) {
        // 用户传入的数据帧数 frameCount = userSize / frameSize
        // userSize就是应用层传入的byte数组的大小,对应上面的描述,userSize = 4096
        // frameCount求得的值就是userSize对应的帧数
        audioBuffer.frameCount = userSize / mFrameSize;
​
        // obtainBuffer() 从 FIFO 上得到一块可用区间
        status_t err = obtainBuffer(&audioBuffer,
                blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
        ……………………
        size_t toWrite = audioBuffer.size;
        memcpy(audioBuffer.i8, buffer, toWrite);
        buffer = ((const char *) buffer) + toWrite;
        userSize -= toWrite;
        written += toWrite;
    }
​
    if (written > 0) {
        mFramesWritten += written / mFrameSize;
    }
    return written;
}

其中的核心:obtainBuffer()函数,从FIFO环形缓冲区中得到一块可用的空间给audioBuffer,然后调用memcpy()函数将传入的buffer中的audio数据拷贝给audioBuffer,即写入了FIFO缓存区中;

AudioFlinger读取

我们在之前的Threads.cpp中的AudioFlinger::PlaybackThread::threadLoop()函数中,其中包括了准备Track、音频混音、音频写入这几个主要的逻辑。

其中我们主要关注一下上述的2个流程:混音和写入,我们以DirectOutputThread为例,看看这2个流程中,具体做了哪些操作:

threadLoop_mix
void AudioFlinger::DirectOutputThread::threadLoop_mix()
{
    // mFrameCount是硬件设备(PCM设备)处理单个数据块的帧数
    // 上层必须积累了足够多(mFrameCount)的数据,采写入到PCM设备,所以mFrameCount也就是AudioFlinger预期的数据量
    size_t frameCount = mFrameCount;
    // 目的缓冲区,threadLoop_write()会把mSinkBuffer上的数据写到PCM设备
    int8_t *curBuf = (int8_t *)mSinkBuffer;
    // output audio to hardware
    // FIFO 上可读的数据量可能要比预期的要小,因此可能需要多次读取才能积累足够的数据量
    // 注意:AudioTrack 和 AudioFlinger 是不同的进程,AudioTrack 同时也在不停地生产数据
    //   所以 FIFO 可读的数据量是在不停变化的
    while (frameCount) {
        AudioBufferProvider::Buffer buffer;
        buffer.frameCount = frameCount;
        // getNextBuffer() 从 FIFO 上获取可读数据块
        status_t status = mActiveTrack->getNextBuffer(&buffer);
        if (status != NO_ERROR || buffer.raw == NULL) {
            // no need to pad with 0 for compressed audio
            if (audio_has_proportional_frames(mFormat)) {
                memset(curBuf, 0, frameCount * mFrameSize);
            }
            break;
        }
        // memcpy() 把 FIFO 可读数据拷贝到 mSinkBuffer 目的缓冲区
        memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
        frameCount -= buffer.frameCount;
        curBuf += buffer.frameCount * mFrameSize;
        // releaseBuffer() 更新 FIFO 读位置
        // 对于 AudioTrack 来说,意味着 FIFO 上有更多的可用空间
        mActiveTrack->releaseBuffer(&buffer);
    }
    mCurrentWriteLength = curBuf - (int8_t *)mSinkBuffer;
    mSleepTimeUs = 0;
    mStandbyTimeNs = systemTime() + mStandbyDelayNs;
    mActiveTrack.clear();
}
threadLoop_write
// shared by MIXER and DIRECT, overridden by DUPLICATING
ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
{
    LOG_HIST_TS();
    mInWrite = true;
    ssize_t bytesWritten;
    const size_t offset = mCurrentWriteLength - mBytesRemaining;
​
    // If an NBAIO sink is present, use it to write the normal mixer's submix
    if (mNormalSink != 0) {
​
        ……………………
    // otherwise use the HAL / AudioStreamOut directly
    } else {
        // Direct output and offload threads
​
        if (mUseAsyncWrite) {
            ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
            mWriteAckSequence += 2;
            mWriteAckSequence |= 1;
            ALOG_ASSERT(mCallbackThread != 0);
            mCallbackThread->setWriteBlocked(mWriteAckSequence);
        }
        // FIXME We should have an implementation of timestamps for direct output threads.
        // They are used e.g for multichannel PCM playback over HDMI.
        bytesWritten = mOutput->write((char *)mSinkBuffer + offset, mBytesRemaining);
​
        if (mUseAsyncWrite &&
                ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
            // do not wait for async callback in case of error of full write
            mWriteAckSequence &= ~1;
            ALOG_ASSERT(mCallbackThread != 0);
            mCallbackThread->setWriteBlocked(mWriteAckSequence);
        }
    }
​
    mNumWrites++;
    mInWrite = false;
    mStandby = false;
    return bytesWritten;
}

该函数中,将上一步配置好的mSinkBuffer传入到write()函数中,PCM数据就传入到了mOutput输出流设备中了;