Android Audio System深入探究之AudioTrack(基于Android9)

文章目录

  • 1. 音频系统概述
  • 2. 放音流程分析
    • 2.1 getMinBufferSize
    • 2.2 AudioTrack
    • 2.3 AudioTrack (Native空间) set分析
      • 2.3.1 Cblk 在AF端初始化
      • 2.3.2 IAudioTrack和AT、AF的关系
      • 2.3.3 共享内存与cblk
    • 2.4 play和write分析
    • 2.5 obtainBuffer 和releaseBuffer
    • 2.6 delete AudioTrack
    • 2.7 stop分析
  • AudioTrack流程总结

基于Android 9

1. 音频系统概述

Audio系统是Android平台重要组成部分,主要包括三方面内容:

  • AudioRecorder和AudioTrack:这两个类属于Audio系统对外提供的API类,通过他们可以完成音频数据的采集和输出任务。
  • AudioFlinger:Audio系统的工作引擎,管理系统中输入输出音频流,并承担音频数据的混音,以及读写Audio硬件等工作以实现数据的输入和输出等功能。
  • AudioPolicyService: 它是Audio系统策略控制中心,具体掌管系统音频设备的选择和切换、音量控制、音频流类型管理等功能。

相关概念缩写如下:

AudioTrack简写为AT, AudioFlinger简称AF, AudioPolicyService简称AP, AudioRecorder简称AR。

2. 放音流程分析

Java层放音API AudioTrack的使用流程如下:

//(1) 根据音频数据来确定所要分配的缓冲区最小size
int bufsize = AudioTrack.getMinBufferSize(8000,//采样率,每秒8000个采样点
                AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
                AudioFormat.ENCODING_PCM_16BIT); //采样精度,一个采样点16bit,2个字节
 //(2)创建AudioTrack               
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 
		8000,//采样率8000
        AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
        AudioFormat.ENCODING_PCM_16BIT,
        bufsize,
        AudioTrack.MODE_STREAM); //数据加载模式,流式
//(3)开始播放                
mAudioTrack.play();

......
//(4) 不断写入音频数据byte[] 到Track中
mAudioTrack.write(audio_data,0,packet.data_len);

//(5) 停止播放和释放资源
track.stop(); //停止播放
track.release(); //释放底层资源

上面的示例代码中涉及两个概念: 数据加载模式、音频流类型。

  1. AudioTrack数据加载模式
    AudioTrack有两种数据加载模式; MODE_STREAMMODE_STACTIC
  • MODE_STREAM: 通过write函数多次循环写入 Audio Data到AudioTrack中。这个过程涉及数据从用户提供的Buffer 拷贝到AudioTrack内部Buffer,有一定的延迟。
  • MODE_STATIC:在play之前,一次性将所有数据写入AudioTrack内部缓冲区。该模式适用于铃声、通知这种内存占用少,延迟较低的场景。
  1. 音频流的类型

在AudioTrack构造函数中使用到了 AudioManager.STREAM_MUSIC这个参数。该参数与Audio系统对音频流管理和分类有关。
常见的音频流类型有以下几种:

  • STREAM_ALARM : 警告音
  • STREAM_MUSIC :音乐声,例如music播放
  • STREAM_RING : 铃声
  • STREAM_SYSTEM :系统声音,例如低电量提示音、锁屏音等
  • STREAM_VOICE_CALL: 通话音

值得注意的是 上面这些类型的划分与音频数据量的多少没有关系。例如MUSIC和RING类型都可以是某首MP3歌曲。同样铃声预览中铃声的流类型也可以设置为MUSIC类型。

音频流类型划分和Audio系统对音频策略管理有关。
具体代码体现如下

源码位置 hardware/libhardware_legacy/AudioPolicyManagerBase.cpp

AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(
        AudioSystem::stream_type stream) {
    // stream to strategy mapping
    switch (stream) {
    case AudioSystem::VOICE_CALL:
    case AudioSystem::BLUETOOTH_SCO:
        return STRATEGY_PHONE;
    case AudioSystem::RING:
    case AudioSystem::ALARM:
        return STRATEGY_SONIFICATION;
    case AudioSystem::NOTIFICATION:
        return STRATEGY_SONIFICATION_RESPECTFUL;
    case AudioSystem::DTMF:
        return STRATEGY_DTMF;
    default:
        ALOGE("unknown stream type");
    case AudioSystem::SYSTEM:
        // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
        // while key clicks are played produces a poor result
    case AudioSystem::TTS:
    case AudioSystem::MUSIC:
        return STRATEGY_MEDIA;
    case AudioSystem::ENFORCED_AUDIBLE:
        return STRATEGY_ENFORCED_AUDIBLE;
    }
}
  1. Buffer分配和Frame概念
    在追踪getMinBufferSize函数调用时,碰到和Frame的概念,那么音频Frame(帧率)的概念是怎么定义的呢?

Frame在ALSA系统中是一个单位,被用来描述一个采样点音频数据量的多少,例如一帧等于多少字节呢?1单位Frame等于1个采样点的字节数 X 声道数(比如PCM 16,双声道的1个Frame等于2x2=4字节)。另外,在目前声卡驱动程序中,内部缓冲区也是采用Frame作为单位来分配和管理的。

总之,getMinBufSize最后会综合考虑采样率、硬件延迟等情况,得出一个最小缓冲区大小。而我们分配的缓冲区大小会是它的整数倍。

2.1 getMinBufferSize

在AudioTrack使用过程,第一步就是调用getMinBufferSize函数获取最小缓冲内存,下面分析下其调用流程:
getMinBufferSize函数是AudioTrack的静态函数,下面看其实现
[–> AudioTrack.java]

static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
		//(1) 根据channelConfig配置获取声音通道
        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);
            }
        }
		//(2) 音频格式合法性判定
        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
        //(3) 采样率合法性判定
        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;
        }
		//(4) 调用JNI方法获取最小BufferSize, 这里之所以要进入native空间是因为minBufferSize的计算还需要确认
		//硬件是否支持这些参数
        int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
        if (size <= 0) {
            loge("getMinBufferSize(): error querying hardware");
            return ERROR;
        }
        else {
            return size;
        }
    }

继续追踪,native_get_min_buff_size方法的实现定义在AudioTrack.java对应的JNI实现类android_media_AudioTrack.cpp

[–> AudioTrack.java]

//JNI方法注册表
static const JNINativeMethod gMethods[] = {
    // name,              signature,     funcPtr
    ......
    {"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;
    //调用AudioTrack的getMinFrameCount方法, 这里是以帧为单位的
    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;
    }
    //将Java层传递过来的audioFormat转成Native的AudioFormat
    const audio_format_t format = audioFormatToNative(audioFormat);
    //判定格式是否是PCM的,不需要做混合和Scale
    if (audio_has_proportional_frames(format)) {
    	//计算每个采样点有多少bytes, 所以最终结果是以byte为单位的
        const size_t bytesPerSample = audio_bytes_per_sample(format);
        return frameCount * channelCount * bytesPerSample;
    } else {
        return frameCount;
    }
}
    

继续,JNI方法android_media_AudioTrack_get_min_buff_size方法会调用Native层的AudioTrack::getMinFrameCount,其实现如下:

// static
status_t AudioTrack::getMinFrameCount(
        size_t* frameCount,
        audio_stream_type_t streamType,
        uint32_t sampleRate)
{
    if (frameCount == NULL) {
        return BAD_VALUE;
    }

    // FIXME handle in server, like createTrack_l(), possible missing info:
    //          audio_io_handle_t output
    //          audio_format_t format
    //          audio_channel_mask_t channelMask
    //          audio_output_flags_t flags (FAST)
    uint32_t afSampleRate;
    status_t status;
    //最终调用AF的sampleRate函数获取, 这个流程中会根据streamtype涉及output设备策略选择(     getOutput(streamType)函数调用, 根据获取的ioHandle查找AudioIoDescriptor)
    //一般返回最高采样率,例如48000Hz
    status = AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);
    if (status != NO_ERROR) {
        ALOGE("Unable to query output sample rate for stream type %d; status %d",
                streamType, status);
        return status;
    }
    size_t afFrameCount;
    //查询硬件内部缓冲区大小,以Frame为单位, 涉及output策略选择待输出的设备
    //上一节章节解释了1 Frame大小 = 一个采样点字节数 x 声道数
    status = AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
    if (status != NO_ERROR) {
        ALOGE("Unable to query output frame count for stream type %d; status %d",
                streamType, status);
        return status;
    }
    uint32_t afLatency;
    查询硬件延迟时间, 涉及output策略选择待输出的设备
    status = AudioSystem::getOutputLatency(&afLatency, streamType);
    if (status != NO_ERROR) {
        ALOGE("Unable to query output latency for stream type %d; status %d",
                streamType, status);
        return status;
    }

    // When called from createTrack, speed is 1.0f (normal speed).
    // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
    //相关实现在AudioSystem::calculateMinFrameCount中完成
    *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("AudioTrack::getMinFrameCount failed for streamType %d, sampleRate %u",
                streamType, sampleRate);
        return BAD_VALUE;
    }
    ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
            *frameCount, afFrameCount, afSampleRate, afLatency);
    return NO_ERROR;
}

OK, 继续看calculateMinFrameCount的实现。
[–>AudioSystem.cpp ]

/* static */ size_t AudioSystem::calculateMinFrameCount(
        uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
        uint32_t sampleRate, float speed /*, uint32_t notificationsPerBufferReq*/)
{
	//minBufCount表示缓冲区的最少个数,以Frame为单位
    // 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);
}

继续 sourceFramesNeededWithTimestretch 函数分析

[–>AudioResamplerPublic.h]

//上层设置的采样率和底层获取的采样率可能不一致,所以需要综合上下层的参数做合适的计算
// Returns the source frames needed to resample to destination frames.  This is not a precise
// value and depends on the resampler (and possibly how it handles rounding internally).
// Nevertheless, this should be an upper bound on the requirements of the resampler.
// If srcSampleRate and dstSampleRate are equal, then it returns destination frames, which
// may not be true if the resampler is asynchronous.
static inline size_t sourceFramesNeeded(
        uint32_t srcSampleRate/*App传递过来的*/, size_t dstFramesRequired/*底层需要的Frames*/, uint32_t dstSampleRate/*底层读取出来的*/) {
    // +1 舍入 - 即使匹配比率也要这样做(重采样器可能使用相位而不是比率)
    // +1 插值所需的额外样本字节
    return srcSampleRate == dstSampleRate ? dstFramesRequired :
            size_t((uint64_t)dstFramesRequired * srcSampleRate / dstSampleRate + 1 + 1);
}

static inline size_t sourceFramesNeededWithTimestretch(
        uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate,
        float speed) {
    // required is the number of input frames the resampler needs
    size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate);
    // to deliver this, the time stretcher requires:
    return required * (double)speed + 1 + 1; // accounting for rounding dependencies
}

2.2 AudioTrack

[–>AudioTrack.java]

AudioTrack有多个构造函数,android 9中已经建议使用Builder模式构造AndioTrack对象。

构造函数如下:

//(1)deprecated
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes, int mode, int sessionId)
//(2)attributes和format都是非空
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
            int mode, int sessionId)
//(3) Builder模式构造
public @NonNull AudioTrack build()

使用Builder模式构造AudioTrack对象

 AudioTrack player = new AudioTrack.Builder()
           .setAudioAttributes(new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_ALARM)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build())
           .setAudioFormat(new AudioFormat.Builder()
                   .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                   .setSampleRate(44100)
                   .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
                   .build())
           .setBufferSizeInBytes(minBuffSize)
           .build();

无论采用哪种构造函数, 最终都会调用下面私有构造函数

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

        if (format == null) {
            throw new IllegalArgumentException("Illegal null AudioFormat");
        }

        // Check if we should enable deep buffer mode
        if (shouldEnablePowerSaving(mAttributes, format, bufferSizeInBytes, mode)) {
            mAttributes = new AudioAttributes.Builder(mAttributes)
                .replaceFlags((mAttributes.getAllFlags()
                        | AudioAttributes.FLAG_DEEP_BUFFER)
                        & ~AudioAttributes.FLAG_LOW_LATENCY)
                .build();
        }

        // remember which looper is associated with the AudioTrack instantiation
        Looper looper;
        if ((looper = Looper.myLooper()) == null) {
            looper = Looper.getMainLooper();
        }

        int rate = format.getSampleRate();
        if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
            rate = 0;
        }

        int channelIndexMask = 0;
        if ((format.getPropertySetMask()
                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0) {
            channelIndexMask = format.getChannelIndexMask();
        }
        int channelMask = 0;
        if ((format.getPropertySetMask()
                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0) {
            channelMask = format.getChannelMask();
        } else if (channelIndexMask == 0) { // if no masks at all, use stereo
            channelMask = AudioFormat.CHANNEL_OUT_FRONT_LEFT
                    | AudioFormat.CHANNEL_OUT_FRONT_RIGHT;
        }
        int encoding = AudioFormat.ENCODING_DEFAULT;
        if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0) {
            encoding = format.getEncoding();
        }
        //检查参数是否合法
        audioParamCheck(rate, channelMask, channelIndexMask, encoding, mode);
        mStreamType = AudioSystem.STREAM_DEFAULT;
		//检查audioBuffer Size是否合法
        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
        //调用了native层的native_setup,使用弱引用传递进去,确保可以被垃圾回收器回收
        int initResult = native_setup(new WeakReference<AudioTrack>(this), 
        		mAttributes, //属性
                sampleRate, //例如8000
                 mChannelMask, //通道掩码,查看相关定义
                 mChannelIndexMask, //声道索引掩码
                 mAudioFormat, //例如AudioFormat.ENCODING_PCM_16BIT
                mNativeBufferSizeInBytes, //等于bufferSizeInBytes
                mDataLoadMode, //DataLoadMode是MODE_STREAM
                 session, //会话Id
                 0 /*nativeTrackInJavaObj*/,
                offload);
        if (initResult != SUCCESS) {
            loge("Error code "+initResult+" when initializing AudioTrack.");
            return; // with mState == STATE_UNINITIALIZED
        }

        mSampleRate = sampleRate[0];
        mSessionId = session[0];

        if ((mAttributes.getFlags() & AudioAttributes.FLAG_HW_AV_SYNC) != 0) {
            int frameSizeInBytes;
            if (AudioFormat.isEncodingLinearFrames(mAudioFormat)) {
                frameSizeInBytes = mChannelCount * AudioFormat.getBytesPerSample(mAudioFormat);
            } else {
                frameSizeInBytes = 1;
            }
            //带有头信息,需要指定偏移量
            mOffset = ((int) Math.ceil(HEADER_V2_SIZE_BYTES / frameSizeInBytes)) * frameSizeInBytes;
        }

        if (mDataLoadMode == MODE_STATIC) {
            mState = STATE_NO_STATIC_DATA;
        } else {
            mState = STATE_INITIALIZED;
        }

        baseRegisterPlayer();
    }

继续跟踪,native_setup对应的JNI函数是android_media_AudioTrack_setup, 那么我们来看看其实现

[–>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) {

    ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
        "nativeAudioTrack=0x%" PRIX64,
        jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
        nativeAudioTrack);

    sp<AudioTrack> lpTrack = 0;

// session Id合法性判定
    if (jSession == NULL) {
        ALOGE("Error creating AudioTrack: invalid session ID pointer");
        return (jint) AUDIO_JAVA_ERROR;
    }

    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    if (nSession == NULL) {
        ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
        return (jint) AUDIO_JAVA_ERROR;
    }
    audio_session_t sessionId = (audio_session_t) nSession[0];
    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    nSession = NULL;

    AudioTrackJniStorage* lpJniStorage = NULL;

    audio_attributes_t *paa = NULL;

    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        ALOGE("Can't find %s when setting up callback.", kClassPathName);
        return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
    }
	//android 9中新增了可以传递nativeAudioTrack的构造函数
    // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
    if (nativeAudioTrack == 0) {
        if (jaa == 0) {
            ALOGE("Error creating AudioTrack: invalid audio attributes");
            return (jint) AUDIO_JAVA_ERROR;
        }
        //采样率合法性判断
        if (jSampleRate == 0) {
            ALOGE("Error creating AudioTrack: invalid sample rates");
            return (jint) AUDIO_JAVA_ERROR;
        }
		//读取采样率
        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);
         //声道合法性判断       
        if (!audio_is_output_channel(nativeChannelMask)) {
            ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
            return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
        }

        uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);

        // 音频格式合法性检查
        // This function was called from Java, so we compare the format against the Java constants
        audio_format_t format = audioFormatToNative(audioFormat);
        if (format == AUDIO_FORMAT_INVALID) {
            ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
            return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
        }

        // 计算帧数
        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;
        }

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

        // 读取 AudioAttributes 的值
        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);
		//(2) 创建AudioTrackJniStorage对象,和数据回调有关
        // 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:
			//(3) STREAM模式
            status = lpTrack->set(
                    AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                    sampleRateInHertz,
                    format,// word length, PCM, 采样精度,一般为PCM16或PCM8
                    nativeChannelMask,
                    frameCount,
                    AUDIO_OUTPUT_FLAG_NONE,
                    //查看`static void audioCallback(int event, void* user, void *info)`
                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
                    0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
                    0,// shared mem, 共享内存,流模式下为空,实际使用的共享内存由AF创建
                    true,// thread can call Java, 内部线程可以调JNI函数
                    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, 如果是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,// 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;
        }

        if (status != NO_ERROR) {
            ALOGE("Error %d initializing AudioTrack", status);
            goto native_init_failure;
        }
    } else {  // end if (nativeAudioTrack == 0)
        ......
}

上面代码列出了三个要点(1)、(2)、(3) , 我们先看AudioTrackJniStorage这个类。

  1. AudioTrackJniStorage分析

AudioTrackJniStorage是一个辅助类,其涉及了共享内存相关知识。共享内存,作为进程间数据传递的一种手段,在AudioTrack和AudioFlinger中被大量使用。

看下AudioTrackJniStorage类的构成

class AudioTrackJniStorage {
    public:
        sp<MemoryHeapBase>         mMemHeap;
        sp<MemoryBase>             mMemBase;
        //保存一些变量,作用不大
        audiotrack_callback_cookie mCallbackData;
        //设备事件回调
        sp<JNIDeviceCallback>      mDeviceCallback;

    AudioTrackJniStorage() {
        mCallbackData.audioTrack_class = 0;
        mCallbackData.audioTrack_ref = 0;
        mCallbackData.isOffload = false;
    }

    ~AudioTrackJniStorage() {
        mMemBase.clear();
        mMemHeap.clear();
    }

    bool allocSharedMem(int sizeInBytes) {
    //共享内存分配
        mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
        if (mMemHeap->getHeapID() < 0) {
            return false;
        }
        mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
        return true;
    }
};

AudioTrackJniStorage类中,主要涉及MemoryHeapBaseMemoryBase, 而在Android中,这两个类是共享内存有关的, 看来AudioTrack流程中会涉及到共享内存的使用,先了解下共享内存,戳下面的链接

Android系统共享内存分析

2.3 AudioTrack (Native空间) set分析

[–> AudioTrack.cpp]

status_t AudioTrack::set(
        audio_stream_type_t streamType, //STREAM_MUSIC
        uint32_t sampleRate, //8000
        audio_format_t format, //PCM_16
        audio_channel_mask_t channelMask,
        size_t frameCount, //由计算获取,这里假设为1024
        audio_output_flags_t flags, //输出flags
        callback_t cbf, //android_media_AudioTrack.cpp 中 `static void audioCallback`
        void* user, // android_media_AudioTrack.cpp:: AudioTrackJniStorage->mCallbackData
        int32_t notificationFrames,
        const sp<IMemory>& sharedBuffer, //共享内存
        bool threadCanCallJava, 
        audio_session_t sessionId,
        transfer_type transferType, //stream 时是TRANSFER_SYNC, static 时TRANSFER_SHARED
        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) {
    ......
    //传输模式为TRANSFER_OBTAIN和TRANSFER_SYNC 不能使用共享内存传递数据
    case TRANSFER_OBTAIN:
    case TRANSFER_SYNC:
        if (sharedBuffer != 0) {
            ALOGE("Transfer type TRANSFER_OBTAIN but sharedBuffer != 0");
            status = BAD_VALUE;
            goto exit;
        }
        break;
        //传输模式为TRANSFER_SHARED时,使用共享内存传输
    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;
    }

    // 默认的STREAM TYPE 为STREAM_MUSIC
   	......
        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;
    //处理notificationFrames
    if (notificationFrames >= 0) {
        mNotificationFramesReq = notificationFrames;
        mNotificationsPerBufferReq = 0;
    } else {
        ......
    }
    mNotificationFramesAct = 0;
    //callingPid 及myPid 的处理
    callingPid = IPCThreadState::self()->getCallingPid();
    myPid = getpid();
   ......
    mAuxEffectId = 0;
    mOrigFlags = mFlags = flags;
    mCbf = cbf;
	//cbf是JNI层传入的回调函数audioCallback, 如果用户设置了回调函数则启动AudioTrackThread
    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();

    ......
    
	// 初始化很多变量
    .......

exit:
    mStatus = status;
    return status;
}

继续, Native AudioTrack 的set 函数中会调用createTrack_l 函数和AF(AudioFlinger)发生关系, 这里是通过Binder来完成交互的

[-->AudioTrack.cpp]

status_t AudioTrack::createTrack_l()
{
    status_t status;
    bool callbackAdded = false;
	//(1)Binder调用获取AudioFlinger 服务的代理对象
    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
    ......

    {
    //低延迟放音相关flag处理
    if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
        ......
    }
	//将上层传递来的参数sampleRate、channel、format及client信息包装进CreateTrackInput作为输入参数
    IAudioFlinger::CreateTrackInput input;
    ......
	//AF从底层获取的音频输出设备相关参数封装
    IAudioFlinger::CreateTrackOutput output;
	//(2) 调用AF的createTrack, 根据传入的CreateTrackInput 参数,创建一个AF端的AT代理对象,类型为IAudioTrack
	//传入参数提醒:Stream模式下shareBuffer为空, 后续AF和AT交互围绕IAudioTrack展开
    sp<IAudioTrack> track = audioFlinger->createTrack(input,
                                                      output,
                                                      &status);

    ......

    mFrameCount = output.frameCount;
    mNotificationFramesAct = (uint32_t)output.notificationFrameCount;
    //输出设备从AF端获取,AF再通过APM(AudioPolicyService)获取输出设备, 毕竟APM管理着音频设备及相关策略
    mRoutedDeviceId = output.selectedDeviceId;
    mSessionId = output.sessionId;

    mSampleRate = output.sampleRate;
    if (mOriginalSampleRate == 0) {
        mOriginalSampleRate = mSampleRate;
    }

    mAfFrameCount = output.afFrameCount;
    mAfSampleRate = output.afSampleRate;
    mAfLatency = output.afLatencyMs;

    mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate;

    // AudioFlinger now owns the reference to the I/O handle,
    // so we are no longer responsible for releasing it.

    // FIXME compare to AudioRecord
    //(3) track是IAudioTrack类型, getCblk最终调用AF端的TrackBase.h
    //TrackBase实现在Tracks.cpp, 成员变量mCblk
    //STREAM模式下,没有在AT端创建共享内存,这块内存实际最终有AF->createTrack,再到
    //PlaybackThread-> createTrack_l中创建
    sp<IMemory> iMem = track->getCblk();
    ......
    void *iMemPointer = iMem->pointer();
   ......
    //(4) Binder死亡通知
    // 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();
	//(5) Client 使用变量cblk 指向服务端传递过来的共享内存地址iMemPointer
	//iMemPointer是共享内存的首地址,类型为void* , static_cast直接将这个void*类型转成audio_track_cblk_t,
	//这表明这块内存的首部存在audio_track_cblk_t对象。
    audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
    mCblk = cblk;
    ......

   
    // Starting address of buffers in shared memory.  If there is a shared buffer, buffers
    // is the value of pointer() for the shared buffer, otherwise buffers points
    // immediately after the control block.  This address is for the mapping within client
    // address space.  AudioFlinger::TrackBase::mBuffer is for the server address space.
    void* buffers;
    if (mSharedBuffer == 0) {
        buffers = cblk + 1;
    } else {
        buffers = mSharedBuffer->pointer();
        if (buffers == NULL) {
            ALOGE("Could not get buffer pointer");
            status = NO_INIT;
            goto exit;
        }
    }

    mAudioTrack->attachAuxEffect(mAuxEffectId);

    // If IAudioTrack is re-created, don't let the requested frameCount
    // decrease.  This can confuse clients that cache frameCount().
    if (mFrameCount > mReqFrameCount) {
        mReqFrameCount = mFrameCount;
    }

    // reset server position to 0 as we have new cblk.
    mServer = 0;

    // update proxy
    if (mSharedBuffer == 0) {
        mStaticProxy.clear();
        mProxy = new AudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
    } else {
        mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
        mProxy = mStaticProxy;
    }

   .......

    }
	......

    mStatus = status;

    // sp track destructor will cause releaseOutput() to be called by AudioFlinger
    return status;
}

2.3.1 Cblk 在AF端初始化

分析 标号(3)处的代码

sp<IMemory> iMem = track->getCblk();

getCblk()调用最终会调用到Bn端

[–>TrackBase.h]

sp<IMemory> getCblk() const { return mCblkMemory; }

那么mCblkMemory 在哪里初始化的呢?
从代码跟踪结果分析 mCblkMemory 是在AF端的Track

[–>Tracks.cpp]

AudioFlinger::PlaybackThread::Track::Track(...):TrackBase(...){
}

Track继承自TrackBasemCblkMemory就是在TrackBase构造函数初始化的

[–>Tracks.cpp]

AudioFlinger::ThreadBase::TrackBase::TrackBase(...):RefBase(){

......
size_t size = sizeof(audio_track_cblk_t);
    if (buffer == NULL && alloc == ALLOC_CBLK) {
        // check overflow when computing allocation size for streaming tracks.
        if (size > SIZE_MAX - bufferSize) {
            android_errorWriteLog(0x534e4554, "34749571");
            return;
        }
        size += bufferSize;
    }
	//前面我们知道放音是为STREAM模式下, 不会使用共享内存,实际这个时候使用的共享内存是由AF创建的
	//由MemoryDealer.cpp完成
    if (client != 0) {
        mCblkMemory = client->heap()->allocate(size);
        if (mCblkMemory == 0 ||
                (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) {
            ALOGE("not enough memory for AudioTrack size=%zu", size);
            client->heap()->dump("AudioTrack");
            mCblkMemory.clear();
            return;
        }
    } else {
        mCblk = (audio_track_cblk_t *) malloc(size);
        if (mCblk == NULL) {
            ALOGE("not enough memory for AudioTrack size=%zu", size);
            return;
        }
    }
	//多种分配方式, 这里是根据track type决定的,我们走的是默认type TYPE_DEFAULT, 所以会走ALLOC_CBLK分支
    // construct the shared structure in-place.
    if (mCblk != NULL) {
    	//(1) 指定内存区域常见cblk对象
        new(mCblk) audio_track_cblk_t();
        switch (alloc) {
        ......
        //我们的分析流程会走这个分支
        case ALLOC_CBLK:
            // stream模式下放音量, clear all buffers
            if (buffer == NULL) {
            	//buffer指向数据空间,它的起始位置是共享内存的首部加上audio_track_cblk_t的大小
                mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
                memset(mBuffer, 0, bufferSize);
            } else {
            	//STATIC模式
                mBuffer = buffer;
#if 0
                mCblk->mFlags = CBLK_FORCEREADY;    // FIXME hack, need to fix the track ready logic
#endif
            }
            break;
        ......
        default:
            LOG_ALWAYS_FATAL("invalid allocation type: %d", (int)alloc);
        }
        mBufferSize = bufferSize;
......

}

重点关注下标号(1) 中语法

new(mCblk) audio_track_cblk_t();

注意其用法,new后面括号里是内存,紧跟其后的是一个类构造函数。
这个语句是C++语言的placement new.其含义是在指定内存中创建一个对象。我们知道,普通new只能在堆上创建对象,堆的地址由系统分配。这里使用placement new将使audio_track_cblk_t创建在共享内存上,从而可以被多进程访问。

2.3.2 IAudioTrack和AT、AF的关系

关系图如下:
Android Audio System深入探究之AudioTrack(基于Android9)_第1张图片

IAudioTrack是AT和AF连接的纽带

2.3.3 共享内存与cblk

audio_track_cblk_t是一个结构体类型,其定义如下

// Important: do not add any virtual methods, including ~
struct audio_track_cblk_t
{
                // Since the control block is always located in shared memory, this constructor
                // is only used for placement new().  It is never used for regular new() or stack.
                            audio_track_cblk_t();
                /*virtual*/ ~audio_track_cblk_t() { }

                friend class Proxy;
                friend class ClientProxy;
                friend class AudioTrackClientProxy;
                friend class AudioRecordClientProxy;
                friend class ServerProxy;
                friend class AudioTrackServerProxy;
                friend class AudioRecordServerProxy;

    // The data members are grouped so that members accessed frequently and in the same context
    // are in the same line of data cache.

                uint32_t    mServer;    // Number of filled frames consumed by server (mIsOut),
                                        // or filled frames provided by server (!mIsOut).
                                        // It is updated asynchronously by server without a barrier.
                                        // The value should be used
                                        // "for entertainment purposes only",
                                        // which means don't make important decisions based on it.

                uint32_t    mPad1;      // unused

    volatile    int32_t     mFutex;     // event flag: down (P) by client,
                                        // up (V) by server or binderDied() or interrupt()
#define CBLK_FUTEX_WAKE 1               // if event flag bit is set, then a deferred wake is pending

private:

                // This field should be a size_t, but since it is located in shared memory we
                // force to 32-bit.  The client and server may have different typedefs for size_t.
                uint32_t    mMinimum;       // server wakes up client if available >= mMinimum

                // Stereo gains for AudioTrack only, not used by AudioRecord.
                gain_minifloat_packed_t mVolumeLR;

                uint32_t    mSampleRate;    // AudioTrack only: client's requested sample rate in Hz
                                            // or 0 == default. Write-only client, read-only server.

                PlaybackRateQueue::Shared mPlaybackRateQueue;

                // client write-only, server read-only
                uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0

                uint16_t    mPad2 __attribute__((__unused__)); // unused

                // server write-only, client read
                ExtendedTimestampQueue::Shared mExtendedTimestampQueue;

                // This is set by AudioTrack.setBufferSizeInFrames().
                // A write will not fill the buffer above this limit.
    volatile    uint32_t   mBufferSizeInFrames;  // effective size of the buffer

public:

    volatile    int32_t     mFlags;         // combinations of CBLK_*

public:
                union {
                    AudioTrackSharedStreaming   mStreaming;
                    AudioTrackSharedStatic      mStatic;
                    int                         mAlign[8];
                } u;

                // Cache line boundary (32 bytes)
};

audio_track_cblk_t中关联了很多友元函数来管理共享内存, 我们的流程主要涉及到AudioTrackClientProxyAudioTrackServerProxy, 这些Proxy类都在AudioTrackShared.cpp中实现。

从上面 Cblk 在AF端初始化分析中我们可大致画出共享内存与CB的关系

下图给出了共享内存与CB的关系
Android Audio System深入探究之AudioTrack(基于Android9)_第2张图片

2.4 play和write分析

AudioTrack初始化完成后,接下来的流程就是play和write相关函数的调用了

//(3)开始播放                
mAudioTrack.play();

//(4) 不断写入音频数据byte[] 到Track中
mAudioTrack.write(audio_data,0,packet.data_len);

流程还是按照调用顺序Java >> JNI >> Native来分析

(1) play()分析

[–> AudioTrack.java]

public void play()
    throws IllegalStateException {
        ......
        //FIXME use lambda to pass startImpl to superclass
        final int delay = getStartDelayMs();
        //不延迟
        if (delay == 0) {
            startImpl();
        } else { //延迟播放
            new Thread() {
                public void run() {
                   .......
                    try {
                        startImpl();
                    } catch (IllegalStateException e) {
                    }
                }
            }.start();
        }
    }

private void startImpl() {
        synchronized(mPlayStateLock) {
        //base类调用, 主要是播放状态更新和音量设置
            baseStart();
            //进入Jni调用
            native_start();
            mPlayState = PLAYSTATE_PLAYING;
        }
    }

Java层调用native_start进入Jni层, Jni层对应的函数为android_media_AudioTrack_start

[–> android_media_AudioTrack.cpp]

static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
	//前面的分析中我们知道, 在AudioTrack初始化时已经创建了Native的AudioTrack对象, 该对象指针被保存在
	//AudioTrack Java对象的nativeTrackInJavaObj变量中
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    ......
    //调用Native的AudioTrack->start 函数启动play播放
    lpTrack->start();
}

go on, JNI层的android_media_AudioTrack_start 会继续调用 Native层的start函数

[–> AudioTrack.cpp]

status_t AudioTrack::start()
{
	//加锁
    AutoMutex lock(mLock);
    ......
    mInUnderrun = true;
	//状态更新
    State previousState = mState;
    if (previousState == STATE_PAUSED_STOPPING) {
        mState = STATE_STOPPING;
    } else {
        mState = STATE_ACTIVE;
    }
    //更新并获取帧的位置
    (void) updateAndGetPosition_l();
	
	......
   //更新mCblk的flags标识
    int32_t flags = android_atomic_and(~(CBLK_STREAM_END_DONE | CBLK_DISABLED), &mCblk->mFlags);

    status_t status = NO_ERROR;
    if (!(flags & CBLK_INVALID)) {
    	//*****调用AudioTrack的远端代理对象IAudioTrack的start函数, 正真实现在AudioTrackHandle这个Binder对象中
    	//mAudioTrack在 Native AudioTrack->set函数中实例化
        status = mAudioTrack->start();
        if (status == DEAD_OBJECT) {
            flags |= CBLK_INVALID;
        }
    }
    if (flags & CBLK_INVALID) {
        status = restoreTrack_l("start");
    }

    // 按需恢复或者暂停AudioTrackThread回调线程
    sp<AudioTrackThread> t = mAudioTrackThread;
    if (status == NO_ERROR) {
        if (t != 0) {
            if (previousState == STATE_STOPPING) {
                mProxy->interrupt();
            } else {
            	//恢复AudioTrackThread线程
                t->resume();
            }
        } else {
           .......
        }

        // Start our local VolumeHandler for restoration purposes.
        mVolumeHandler->setStarted();
    } else {
        ALOGE("start() status %d", status);
        mState = previousState;
        if (t != 0) {
            if (previousState != STATE_STOPPING) {
            //暂停AudioTrackThread线程
                t->pause();
            }
        } else {
           ......
        }
    }

    return status;
}

write函数分析

[–>AudioTrack.cpp]

ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
    if (mTransfer != TRANSFER_SYNC) {
        return INVALID_OPERATION;
    }
	//是否不需要混音量直接输出,相关flags处理, 我们不走
    if (isDirect()) {
       ......
    }
	//参数合法性判定
    if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
       ......
    }

    size_t written = 0;
    Buffer audioBuffer; //Buffer是一个辅助性结构
    while (userSize >= mFrameSize) {
    	//以帧为单位
        audioBuffer.frameCount = userSize / mFrameSize;
        //obtainBuffer从共享内存中得到一块空闲的数据块
        status_t err = obtainBuffer(&audioBuffer,
                blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
        ......
        //空闲数据缓冲大小是audioBuffer.size, 地址在audioBuffer.i8,数据传递通过memcpy完成
        size_t toWrite = audioBuffer.size;
        memcpy(audioBuffer.i8, buffer, toWrite);
        buffer = ((const char *) buffer) + toWrite;
        userSize -= toWrite;
        written += toWrite;
        //releaseBUffer更新写位置,同时触发消费者
        releaseBuffer(&audioBuffer);
    }
    if (written > 0) {
        mFramesWritten += written / mFrameSize;
    }
    return written;
}

从write函数分析我们发现数据传递其实就是简单的内存拷贝,而生产者消费者工作的协调,则是通过obtainBuffer与releaseBuffer来完成的。下面分析下这两个函数。

2.5 obtainBuffer 和releaseBuffer

[–> AudioTrack.cpp]

status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
        struct timespec *elapsed, size_t *nonContig)
{
    // previous and new IAudioTrack sequence numbers are used to detect track re-creation
    uint32_t oldSequence = 0;
    uint32_t newSequence;

    Proxy::Buffer buffer;
    status_t status = NO_ERROR;

    static const int32_t kMaxTries = 5;
    int32_t tryCounter = kMaxTries;

    do {
        // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to
        // keep them from going away if another thread re-creates the track during obtainBuffer()
        sp<AudioTrackClientProxy> proxy;
        sp<IMemory> iMem;

        {   // start of lock scope
            AutoMutex lock(mLock);

            newSequence = mSequence;
            //处理前一次调用obtainBuffer()失败的情况,这种情况有可能是media server挂掉了
            // did previous obtainBuffer() fail due to media server death or voluntary invalidation?
            if (status == DEAD_OBJECT) {
                .......
            }
            oldSequence = newSequence;

          ......

            // Keep the extra references
            proxy = mProxy;
            iMem = mCblkMemory;

           .......

            // Non-blocking if track is stopped or paused
            if (mState != STATE_ACTIVE) {
                requested = &ClientProxy::kNonBlocking;
            }

        }   // end of lock scope

        buffer.mFrameCount = audioBuffer->frameCount;
        // FIXME starts the requested timeout and elapsed over from scratch
        // (1) proxy是AudioTrackClientProxy类型, 该类实现在AudioTrackShared.cpp中
        status = proxy->obtainBuffer(&buffer, requested, elapsed);
    } while (((status == DEAD_OBJECT) || (status == NOT_ENOUGH_DATA)) && (tryCounter-- > 0));

    audioBuffer->frameCount = buffer.mFrameCount;
    audioBuffer->size = buffer.mFrameCount * mFrameSize;
    audioBuffer->raw = buffer.mRaw;
    if (nonContig != NULL) {
        *nonContig = buffer.mNonContig;
    }
    return status;
}

看下标号(1) 相关调用最终发生在AudioTrackShared: ClientProxy中

[–> AudioTrackShared.cpp]

status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
        struct timespec *elapsed)
{
	......
	//(1) 使用 mCblk来获取可用写入Buffer
	audio_track_cblk_t* cblk = mCblk;
	.......
	for (;;) {
		......
		// compute number of frames available to write (AudioTrack) or read (AudioRecord)
        int32_t front;
        int32_t rear;
        //我们是放音量会走这个分支
        if (mIsOut) {
            // The barrier following the read of mFront is probably redundant.
            // We're about to perform a conditional branch based on 'filled',
            // which will force the processor to observe the read of mFront
            // prior to allowing data writes starting at mRaw.
            // However, the processor may support speculative execution,
            // and be unable to undo speculative writes into shared memory.
            // The barrier will prevent such speculative execution.
            //原子操作
            front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
            rear = cblk->u.mStreaming.mRear;
        } else {
            // On the other hand, this barrier is required.
            rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
            front = cblk->u.mStreaming.mFront;
        }
        // write to rear, read from front
        ssize_t filled = rear - front;
        // pipe should not be overfull
        if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
            if (mIsOut) {
                ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); "
                        "shutting down", filled, mFrameCount);
                mIsShutdown = true;
                status = NO_INIT;
                goto end;
            }
            // for input, sync up on overrun
            filled = 0;
            cblk->u.mStreaming.mFront = rear;
            (void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
        }
        // Don't allow filling pipe beyond the user settable size.
        // The calculation for avail can go negative if the buffer size
        // is suddenly dropped below the amount already in the buffer.
        // So use a signed calculation to prevent a numeric overflow abort.
        ssize_t adjustableSize = (ssize_t) getBufferSizeInFrames();
        ssize_t avail =  (mIsOut) ? adjustableSize - filled : filled;
        if (avail < 0) {
            avail = 0;
		//有可用内存,那么需要确定可以Buffer的地址及大小
        } else if (avail > 0) {
            // 'avail' may be non-contiguous, so return only the first contiguous chunk
            size_t part1;
            if (mIsOut) {
                rear &= mFrameCountP2 - 1;
                part1 = mFrameCountP2 - rear;
            } else {
                front &= mFrameCountP2 - 1;
                part1 = mFrameCountP2 - front;
            }
            if (part1 > (size_t)avail) {
                part1 = avail;
            }
            if (part1 > buffer->mFrameCount) {
                part1 = buffer->mFrameCount;
            }
            //最终计算获取的可以Buffer
            buffer->mFrameCount = part1;
            buffer->mRaw = part1 > 0 ?
                    &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
            buffer->mNonContig = avail - part1;
            mUnreleased = part1;
            status = NO_ERROR;
            break;
        }
	}
	.......
}

上面obtainBuffer的计算过程还是比较复杂的, 但是最终通过CB这个数据结构管理的,而releaseBuffer则是在使用完这块空间后更新写指针的位置。releaseBuffer实现如下

[–> AudioTrack.cpp]

void ClientProxy::releaseBuffer(Buffer* buffer)
{
    LOG_ALWAYS_FATAL_IF(buffer == NULL);
    size_t stepCount = buffer->mFrameCount;
    ......
    LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount),
            "%s: mUnreleased out of range, "
            "!(stepCount:%zu <= mUnreleased:%zu <= mFrameCount:%zu), BufferSizeInFrames:%u",
            __func__, stepCount, mUnreleased, mFrameCount, getBufferSizeInFrames());
    mUnreleased -= stepCount;
    audio_track_cblk_t* cblk = mCblk;
    // Both of these barriers are required
    //放音走这个流程, 写数据实在尾部写入, 有点像环形Buffer处理
    if (mIsOut) {
        int32_t rear = cblk->u.mStreaming.mRear;
        //更新写入位置
        android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
    } else {
    	//更新读入位置
        int32_t front = cblk->u.mStreaming.mFront;
        android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
    }
}

2.6 delete AudioTrack

AudioTrack生命周期结束时,会调用其析构函数释放资源

AudioTrack::~AudioTrack()
{
    // pull together the numbers, before we clean up our structures
    mMediaMetrics.gather(this);

    if (mStatus == NO_ERROR) {
        // Make sure that callback function exits in the case where
        // it is looping on buffer full condition in obtainBuffer().
        // Otherwise the callback thread will never exit.
        stop();
    }

    /*
     * mAudioTrackThread may be created before(call set function),
     * and restoreTrack_l may fail if audio route changed when audio device is unavailable now,
     * then mStatus will be set error status, so when this audiotrack release, must let
     * mAudioTrackThread quit.
     */
    if (mAudioTrackThread != 0) {
        if(mProxy != NULL)
            mProxy->interrupt();
        mAudioTrackThread->requestExit();   // see comment in AudioTrack.h
        mAudioTrackThread->requestExitAndWait();
        mAudioTrackThread.clear();
        mAudioTrackThread = NULL;
    }

   /*if (mStatus == NO_ERROR)*/ {
        // No lock here: worst case we remove a NULL callback which will be a nop
        if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
            AudioSystem::removeAudioDeviceCallback(this, mOutput);
            mDeviceCallback = NULL;
        }
		//AF端清理
        if(mAudioTrack != NULL) {
            IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
            mAudioTrack.clear();
            mAudioTrack = NULL;
        }
		//共享内存清理
        if(mCblkMemory != NULL) {
            mCblkMemory.clear();
            mCblkMemory = NULL;
        }

        if(mSharedBuffer != NULL) {
            mSharedBuffer.clear();
            mSharedBuffer = NULL;
        }
        IPCThreadState::self()->flushCommands();
        ALOGV("~AudioTrack, releasing session id %d from %d on behalf of %d",
                mSessionId, IPCThreadState::self()->getCallingPid(), mClientPid);
                //释放相关会话Id
        AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
    }
}

如果我们在放音时直接退出,没有调用stop方法,则析构函数也会调用,考虑很周到。

2.7 stop分析

直接从Native分析

[–> AudioTrack.cpp]

void AudioTrack::stop()
{
    AutoMutex lock(mLock);
    if (mState != STATE_ACTIVE && mState != STATE_PAUSED) {
        return;
    }

    if (isOffloaded_l()) {
        mState = STATE_STOPPING;
    } else {
        mState = STATE_STOPPED;
        ALOGD_IF(mSharedBuffer == nullptr,
                "stop() called with %u frames delivered", mReleased.value());
        mReleased = 0;
    }

    mProxy->stop(); // notify server not to read beyond current client position until start().
    mProxy->interrupt();
    //mAudioTrack类型是IAudioTrack类型,其stop最终处理在AudioFlinger端
    mAudioTrack->stop();
	//如果使用静态方式放音,则需要重置position、loopStart、loopEnd及loopCount等信息
    if (mSharedBuffer != 0) {
        // clear buffer position and loop count.
        mStaticProxy->setBufferPositionAndLoop(0 /* position */,
                0 /* loopStart */, 0 /* loopEnd */, 0 /* loopCount */);
    }

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

stop的工作很简单,就是调用IAudioTrack的stop方法,并要求pause回调线程。

AudioTrack流程总结

AT通过Binder调用完成与AF的交互流程, 总体流程图如下:

Android Audio System深入探究之AudioTrack(基于Android9)_第3张图片
AF端相关类都是通过内部类实现,这样做可以比较清楚地理清相关类关系。

你可能感兴趣的:(Android源码剖析,android系统分析,AudioTrack,AudioFlinger)