Android 音频源码分析——AndroidRecord录音(一)

Android 音频源码分析——AndroidRecord录音(一)
Android 音频源码分析——AndroidRecord录音(二)
Android 音频源码分析——AndroidRecord音频数据传输流程
Android 音频源码分析——audioserver启动

基于Android 源码版本:9.0

  • java代码路径:frameworks/base/media/java/android/media/
  • jni代码路径:frameworks/base/core/jni/
  • C++代码路径:frameworks/av/media/libaudioclient/
      frameworks/av/media/libaudiohal/
      frameworks/av/media/audioserver/
      frameworks/av/sevices/audioflinger/
      frameworks/av/sevices/audiopolicy/
  • hal层:hardware/interfaces/audio/
     hardware/libhardware/modules/audio/
  • system:system/media/audio/include/system/

由于涉及源码比较多,所以只贴出部分,中间由删除许多,有兴趣的可以自行看源码。

1.简介

使⽤AudioRecord的录音流程,分为以下⼏步

  1. 获取 创建AudioRecord 所需的buffer size 大小;
  2. 根据⾳频设备和AudioRecord参数,创建AudioRecord
  3. 调⽤AudioRecord.startRecording开始录音。
  4. 读取录制的音频数据AudioRecord.read(data, 0, bufferSize)。
  5. 停止录音,并释放;

主要使用的API

//静态方法
AudioRecord.getMinBufferSize(sampleRate, channel, audioFormat)
//创建AudioRecord对象
new AudioRecord(MediaRecorder.AudioSource.MIC,
						sampleRate,
						channel,
						AudioFormat.ENCODING_PCM_16BIT,
						bufferSize
				);
audiorecord.startRecording();
audiorecord.read(data, 0, bufferSize);
audiorecord.stop();
audiorecord.release();

2.getMinBufferSize

AudioRecord构造器中需要传递一个buffersize,该值需要通过AudioRecord的静态函数getMinBufferSize获取。我们先看下getMinBufferSize
getMinBufferSize:静态方法,用来获取AudioRecord对象所需的最小缓冲区大小(字节)。

//参数分别是 采样率、声道、音频格式
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
   
    int channelCount = 0;
    switch (channelConfig) {
   
   		//省略,这里单生道转换为1, 双声道转换为2;
    }
	//调用native方法。
    int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
    if (size == 0) {
   
        return ERROR_BAD_VALUE;
    }
    else if (size == -1) {
   
        return ERROR;
    }
    else {
   
        return size;
    }
}

接着看jni代码,android_media_AudioRecord.cpp

// 返回成功创建AudioRecord实例所需的最小大小。
//如果不支持参数组合,则返回0。
//如果查询buffer size出错,则返回-1。
static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
    jint sampleRateInHertz, jint channelCount, jint audioFormat) {
   
    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
          sampleRateInHertz, channelCount, audioFormat);
    size_t frameCount = 0;
    audio_format_t format = audioFormatToNative(audioFormat);//java 对象转C++对象
    //调用C++ AudioRecord类的getMinFrameCount方法
    status_t result = AudioRecord::getMinFrameCount(&frameCount,
            sampleRateInHertz,
            format,
            audio_channel_in_mask_from_count(channelCount));
    if (result == BAD_VALUE) {
   
        return 0;
    }
    if (result != NO_ERROR) {
   
        return -1;
    }
    return frameCount * channelCount * audio_bytes_per_sample(format);
}

C++ AudioRecord.cpp

status_t AudioRecord::getMinFrameCount(
        size_t* frameCount,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask)
{
   
    if (frameCount == NULL) {
   
        return BAD_VALUE;
    }
    size_t size;
    status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);
    if (status != NO_ERROR) {
   
        return status;
    }
    //检测size是否符合规则
    // We double the size of input buffer for ping pong use of record buffer.
    // Assumes audio_is_linear_pcm(format)
    if ((*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) *
            audio_bytes_per_sample(format))) == 0) {
   
        return BAD_VALUE;
    }
    return NO_ERROR;
}

AudioSystem 通过binder方式,调用AudioFlinger getInputBufferSize函数。
AudioSystem.cpp

status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
        audio_channel_mask_t channelMask, size_t* buffSize)
{
   
    const sp<AudioFlingerClient> afc = getAudioFlingerClient();
    if (afc == 0) {
   
        return NO_INIT;
    }
    return afc->getInputBufferSize(sampleRate, format, channelMask, buffSize);
}

AudioFlinger 运行在audioserver系统进程中,与hal层进行交互。
具体流程:

AudioFlinger::getInputBufferSize
->>DeviceHalInterface getInputBufferSize
->>DeviceHalHidl::getInputBufferSize
IDevice.hal getInputBufferSize(AudioConfig config)
generates (Result retval, uint64_t bufferSize);
->>Device.impl.h Device::getInputBufferSize
->>audio_hw.c adev_get_input_buffer_size

3.初始化AudioRecord

流程:AudioRecord.java ->android.media.AudioRecord.cpp->AudioRecord.cpp->AudioSystem.cpp->IAudioFlinger.cpp->AudioFlinger.cpp

AudioRecord构建器

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
        int bufferSizeInBytes)
throws IllegalArgumentException {
   
    this((new AudioAttributes.Builder())
                .setInternalCapturePreset(audioSource)
                .build(),
            (new AudioFormat.Builder())
                .setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,
                                    true/*allow legacy configurations*/))
                .setEncoding(audioFormat)
                .setSampleRate(sampleRateInHz)
                .build(),
            bufferSizeInBytes,
            AudioManager.AUDIO_SESSION_ID_GENERATE);
}

创建AudioAttributes、AudioFormat对象,并调用AudioRecord另一个构建器。
AudioManager.AUDIO_SESSION_ID_GENERATE表示一个特殊的音频会话ID,用于指示未知的音频会话ID,并且framework应生成一个新值。

@SystemApi
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
        int sessionId) throws IllegalArgumentException {
   
    mRecordingState = RECORDSTATE_STOPPED;
	//..省略部分代码, 检查attributes, format
	
	//sampleRate channel等转换
    。。。。。
	//检查buffer size
    audioBuffSizeCheck(bufferSizeInBytes);

    int[] sampleRate = new int[] {
   mSampleRate};
    int[] session = new int[1];
    session[0] = sessionId;
    //调用natvie方法,初始化设备
    //TODO: update native initialization when information about hardware init failure
    //      due to capture device already open is available.
    int initResult = native_setup( new WeakReference<AudioRecord>(this),
            mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
            mAudioFormat, mNativeBufferSizeInBytes,
            session, getCurrentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
    if (initResult != SUCCESS) {
   
        loge("Error code "+initResult+" when initializing native AudioRecord object.");
        return; // with mState == STATE_UNINITIALIZED
    }
    //初始化成功,获取底层返回的 samplerate,及sessionid。
    mSampleRate = sampleRate[0];
    mSessionId = session[0];
    mState = STATE_INITIALIZED;
}

native_setup 对应jni中的android_media_AudioRecord_setup函数,

android_media_AudioRecord_setup

主要包含:

  • 一些参数的转换、检查;AudioAttributes sampleRate channelIndexMask fromat
  • 创建C++ 层AudioRecord对象
  • lpRecorder->set 设置参数
  • 更新sessionid, sampleRate到java层
  • 将C++ 层对象保存在 java 某些字段中
static jint
android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
        jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
        jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
        jlong nativeRecordInJavaObj)
{
   
   	//.........
	//获取channel session
    audio_attributes_t *paa = NULL;
    sp<AudioRecord> lpRecorder = 0;
    audiorecord_callback_cookie *lpCallbackData = NULL;
    
    // 判断是否需要创建C++ 层AudioRecord
    if (nativeRecordInJavaObj == 0) {
   
    	//.........
		//检查 AudioAttributes sampleRate channelIndexMask fromat等
        
        size_t bytesPerSample = audio_bytes_per_sample(format);

        if (buffSizeInBytes == 0) {
   
             ALOGE("Error creating AudioRecord: frameCount is 0.");
            return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
        }
        size_t frameSize = channelCount * bytesPerSample;
        size_t frameCount = buffSizeInBytes / frameSize;

        // 创建C++ 层AudioRecord对象;
        lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));

        // create the callback information:
        // this data will be passed with every AudioRecord callback
        lpCallbackData = new audiorecord_callback_cookie;
        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
        // 我们使用弱引用,以便可以对AudioRecord对象进行垃圾回收。
        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
        lpCallbackData->busy = false;
		//AudioRecord设置参数
        const status_t status = lpRecorder-><
  • 6
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
Android系统源码是按照功能进行分类的,主要分为系统代码、工具、文档、开发环境、虚拟机、配置脚本和编译脚本等类别。其中,系统代码是Android系统的核心部分,包含了各个功能模块的实现代码。工具包括了用于Android系统开发和调试的工具,例如adb、emulator等。文档部分包含了Android系统的开发文档和相关说明。开发环境是Android系统的开发所需的各种环境、库和工具。虚拟机是用于运行Android应用程序的Dalvik虚拟机。配置脚本和编译脚本是用于配置和编译Android系统的脚本文件。 Android系统采用的是一个从BSD继承而来的标准的系统函数库bionic。它是一个轻量级的C库,专门为Android系统进行了优化和定制。在源码根目录下有bionic文件夹,它包含了bionic库的源代码和相关文件。 Android4.3程序库的类型非常多,功能也非常强大。其中一些常用且重要的系统程序库包括: - libcore:Android系统的核心库,提供了Java核心类库的实现,包括集合、IO、网络等功能。 - libandroid_runtime:Android运行时库,提供了Android应用程序运行所需的功能,例如应用程序的启动和管理、进程间通信等。 - libui:Android系统的用户界面库,提供了绘制窗口、图形渲染等功能。 - libsqlite:SQLite数据库库,提供了数据库的管理和操作功能。 - libmedia:媒体库,提供了音频和视频的播放和录制功能。 以上是Android系统源码分析的一些基本信息。如果你有更具体的问题,可以告诉我,我会尽力帮助你。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值