Android audio 一 源码路径

Android audio 二 AudioRecord 分析上

Android audio 三 AudioRecord 分析下

Android audio 四 AudioTrack 分析上

Android audio 五 AudioTrack 分析下

Android audio 六 AudioRecord AudiTrack 拾音放音例子

Android 采集音频类 AudioRecord

文件:

frameworks/base/media/java/android/media/AudioRecord.java

frameworks/base/core/jni/android_media_AudioRecord.cpp

frameworks/av/media/libmedia/AudioRecord.cpp

在 APP 里创建一个拾音线程,先要实例化 AudioRecord 对象,下面从实例化对象 AudioRecord 开始分析

private AudioRecord audiorecord = new AudioRecord(......)

AudioRecord 源码如下, 有三个构造函数 AudioRecord :

第一个构造函数实例化 AudioRecord 对象, APP 调用。

第二个 @SystemApi 是系统 API , 这个函数中调用了 native_setup ,实例化本地 AudioRecord 对象。

    // 调用 JNI ,创建本地 audiorecord 实例int initResult = native_setup(new WeakReference<AudioRecord>(this),mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,mAudioFormat, mNativeBufferSizeInBytes,session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
// frameworks/base/media/java/android/media/AudioRecord.java
//---------------------------------------------------------
// Constructor, Finalize
//--------------------
/*** Class constructor.* Though some invalid parameters will result in an {@link IllegalArgumentException} exception,* other errors do not.  Thus you should call {@link #getState()} immediately after construction* to confirm that the object is usable.* @param audioSource the recording source.*   See {@link MediaRecorder.AudioSource} for the recording source definitions.* @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only*   rate that is guaranteed to work on all devices, but other rates such as 22050,*   16000, and 11025 may work on some devices.*   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value*   which is usually the sample rate of the source.*   {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.* @param channelConfig describes the configuration of the audio channels.*   See {@link AudioFormat#CHANNEL_IN_MONO} and*   {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed*   to work on all devices.* @param audioFormat the format in which the audio data is to be returned.*   See {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT},*   and {@link AudioFormat#ENCODING_PCM_FLOAT}.* @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written*   to during the recording. New audio data can be read from this buffer in smaller chunks*   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum*   required buffer size for the successful creation of an AudioRecord instance. Using values*   smaller than getMinBufferSize() will result in an initialization failure.* @throws java.lang.IllegalArgumentException*/
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);
}/*** @hide* Class constructor with {@link AudioAttributes} and {@link AudioFormat}.* @param attributes a non-null {@link AudioAttributes} instance. Use*     {@link AudioAttributes.Builder#setAudioSource(int)} for configuring the audio*     source for this instance.* @param format a non-null {@link AudioFormat} instance describing the format of the data*     that will be recorded through this AudioRecord. See {@link AudioFormat.Builder} for*     configuring the audio format parameters such as encoding, channel mask and sample rate.* @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written*   to during the recording. New audio data can be read from this buffer in smaller chunks*   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum*   required buffer size for the successful creation of an AudioRecord instance. Using values*   smaller than getMinBufferSize() will result in an initialization failure.* @param sessionId ID of audio session the AudioRecord must be attached to, or*   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at construction*   time. See also {@link AudioManager#generateAudioSessionId()} to obtain a session ID before*   construction.* @throws IllegalArgumentException*/
@SystemApi
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int sessionId) throws IllegalArgumentException
{mRecordingState = RECORDSTATE_STOPPED;if(attributes == null){throw new IllegalArgumentException("Illegal null AudioAttributes");}if(format == null){throw new IllegalArgumentException("Illegal null AudioFormat");}// remember which looper is associated with the AudioRecord instanciation// 记住哪个线程与音频记录的安装相关if((mInitializationLooper = Looper.myLooper()) == null){mInitializationLooper = Looper.getMainLooper();}// is this AudioRecord using REMOTE_SUBMIX at full volume?if(attributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX){final AudioAttributes.Builder filteredAttr = new AudioAttributes.Builder();final Iterator<String> tagsIter = attributes.getTags().iterator();while(tagsIter.hasNext()){final String tag = tagsIter.next();if(tag.equalsIgnoreCase(SUBMIX_FIXED_VOLUME)){mIsSubmixFullVolume = true;Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume");}else     // SUBMIX_FIXED_VOLUME: is not to be propagated to the native layers{filteredAttr.addTag(tag);}}filteredAttr.setInternalCapturePreset(attributes.getCapturePreset());mAudioAttributes = filteredAttr.build();}else{mAudioAttributes = attributes;}int rate = format.getSampleRate();if(rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED){rate = 0;}int encoding = AudioFormat.ENCODING_DEFAULT;if((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0){encoding = format.getEncoding();}audioParamCheck(attributes.getCapturePreset(), rate, encoding);if((format.getPropertySetMask()& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0){mChannelIndexMask = format.getChannelIndexMask();mChannelCount = format.getChannelCount();}if((format.getPropertySetMask()& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0){mChannelMask = getChannelMaskFromLegacyConfig(format.getChannelMask(), false);mChannelCount = format.getChannelCount();}else if(mChannelIndexMask == 0){mChannelMask = getChannelMaskFromLegacyConfig(AudioFormat.CHANNEL_IN_DEFAULT, false);mChannelCount =  AudioFormat.channelCountFromInChannelMask(mChannelMask);}audioBuffSizeCheck(bufferSizeInBytes);int[] sampleRate = new int[] {mSampleRate};int[] session = new int[1];session[0] = sessionId;//TODO: update native initialization when information about hardware init failure//      due to capture device already open is available.// 调用 native 方法,创建 audiorecord 实例int initResult = native_setup(new WeakReference<AudioRecord>(this),mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,mAudioFormat, mNativeBufferSizeInBytes,session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);if(initResult != SUCCESS){loge("Error code "+initResult+" when initializing native AudioRecord object.");return; // with mState == STATE_UNINITIALIZED}mSampleRate = sampleRate[0];mSessionId = session[0];mState = STATE_INITIALIZED;
}/*** A constructor which explicitly connects a Native (C++) AudioRecord. For use by* the AudioRecordRoutingProxy subclass.* @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord* (associated with an OpenSL ES recorder). Note: the caller must ensure a correct* value here as no error checking is or can be done.*/
/*package*/ AudioRecord(long nativeRecordInJavaObj)
{mNativeRecorderInJavaObj = 0;mNativeCallbackCookie = 0;mNativeDeviceCallback = 0;// other initialization...if(nativeRecordInJavaObj != 0){deferred_connect(nativeRecordInJavaObj);}else{mState = STATE_UNINITIALIZED;}
}

在实例化 Audio Record 调用 native_setup 方法,进入 native 。

本地方法接口如下列表:

// frameworks/base/core/jni/android_media_AudioRecord.cpp
static const JNINativeMethod gMethods[] = {// name,               signature,  funcPtr{"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},{"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},{"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",(void *)android_media_AudioRecord_setup},{"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},{"native_release",       "()V",    (void *)android_media_AudioRecord_release},{"native_read_in_byte_array","([BIIZ)I",(void *)android_media_AudioRecord_readInArray<jbyteArray>},{"native_read_in_short_array","([SIIZ)I",(void *)android_media_AudioRecord_readInArray<jshortArray>},{"native_read_in_float_array","([FIIZ)I",(void *)android_media_AudioRecord_readInArray<jfloatArray>},{"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",(void *)android_media_AudioRecord_readInDirectBuffer},{"native_get_buffer_size_in_frames","()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},{"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},{"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},{"native_set_pos_update_period","(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},{"native_get_pos_update_period","()I",    (void *)android_media_AudioRecord_get_pos_update_period},{"native_get_min_buff_size","(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},{"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},{"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},{"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},{"native_disableDeviceCallback", "()V",(void *)android_media_AudioRecord_disableDeviceCallback},{"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",(void *)android_media_AudioRecord_get_timestamp},
};

从 JNI 的接口声明映射,知道

native_setup  <-->  android_media_AudioRecord_setup

接下来分析 android_media_AudioRecord_setup

在 android_media_AudioRecord_setup 中先判断是否已经存在 nativeRecordInJavaObj

如果不存在 nativeRecordInJavaObj

lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));

如果存在

lpRecorder = (AudioRecord*)nativeRecordInJavaObj;

把 long 类型指针,转换成 AudioRecord 对象指针,然后 setAudioRecord(env, thiz, lpRecorder);

在 CPP 中一般使用  static_cast 和 reinterpret_cast 模板转换 type-id 类型。

这里使用  (AudioRecord*)  强制指针类型转换。

// ----------------------------------------------------------------------------
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)
{......audio_attributes_t *paa = NULL;sp<AudioRecord> lpRecorder = 0;audiorecord_callback_cookie *lpCallbackData = NULL;jclass clazz = env->GetObjectClass(thiz);if(clazz == NULL){ALOGE("Can't find %s when setting up callback.", kClassPathName);return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;}// if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one.if(nativeRecordInJavaObj == 0){......// create an uninitialized AudioRecord objectlpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));// read the AudioAttributes valuespaa = (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 itstrncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);env->ReleaseStringUTFChars(jtags, tags);paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;if(paa->flags & AUDIO_FLAG_HW_HOTWORD){flags = AUDIO_INPUT_FLAG_HW_HOTWORD;}// create the callback information:// this data will be passed with every AudioRecord callbacklpCallbackData = new audiorecord_callback_cookie;lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);// we use a weak reference so the AudioRecord object can be garbage collected.lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);lpCallbackData->busy = false;const status_t status = lpRecorder->set(paa->source,sampleRateInHertz,format,        // word length, PCMlocalChanMask,frameCount,recorderCallback,// callback_tlpCallbackData,// void* user0,             // notificationFrames,true,          // threadCanCallJavasessionId,AudioRecord::TRANSFER_DEFAULT,flags,-1, -1,        // default uid, pidpaa);if(status != NO_ERROR){ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",status);goto native_init_failure;}}else     // end if nativeRecordInJavaObj == 0){lpRecorder = (AudioRecord*)nativeRecordInJavaObj;// create the callback information:// this data will be passed with every AudioRecord callbacklpCallbackData = new audiorecord_callback_cookie;lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);// we use a weak reference so the AudioRecord object can be garbage collected.lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);lpCallbackData->busy = false;}......// save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field// of the Java objectsetAudioRecord(env, thiz, lpRecorder);// save our newly created callback information in the "nativeCallbackCookie" field// of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);return (jint) AUDIO_JAVA_SUCCESS;// failure:native_init_failure:env->DeleteGlobalRef(lpCallbackData->audioRecord_class);env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);delete lpCallbackData;env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);// lpRecorder goes out of scope, so reference count drops to zeroreturn (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
}

接下来分析 setAudioRecord 函数,该函数返回 AudioRecord 对象的指针

// frameworks/base/core/jni/android_media_AudioRecord.cpp
// 结构体中的成员很重要, 把 AudioRecord 对象, 回调函数 和回调的音频数据保存到属性 jfielID,
// 并保存到 java 层,提高 JAVA <--> CPP 相互调用的效率。
struct audio_record_fields_t {// these fields provide access from C++ to the...jmethodID postNativeEventInJava; //... event post callback methodjfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord objectjfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback datajfieldID  nativeDeviceCallback;    // provides access to the JNIDeviceCallback instance
};static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
{Mutex::Autolock l(sLock);sp<AudioRecord> old =(AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);if (ar.get()) {ar->incStrong((void*)setAudioRecord);}if (old != 0) {old->decStrong((void*)setAudioRecord);}env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());return old;
}

分析了 stepup ,接下来分析 start 和 stop 函数,发现在 JNI 接口里调用本地 AudioRecord 对象。

主要的拾音业务逻辑在 AudioRecord.cpp 中。下一节分析 AudioRecord.cpp 。

// frameworks/base/core/jni/android_media_AudioRecord.cpp
// ----------------------------------------------------------------------------
static jint
android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
{sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);if (lpRecorder == NULL ) {jniThrowException(env, "java/lang/IllegalStateException", NULL);return (jint) AUDIO_JAVA_ERROR;}return nativeToJavaStatus(lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
}// ----------------------------------------------------------------------------
static void
android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
{sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);if (lpRecorder == NULL ) {jniThrowException(env, "java/lang/IllegalStateException", NULL);return;}lpRecorder->stop();//ALOGV("Called lpRecorder->stop()");
}

Android audio 二 AudioRecord 分析上相关推荐

  1. android audio 音量设置分析

    From audiod 中经常遇到的场景是音量调整与输出设备的切换,下面两篇文章 针对这两个场景分别分析一下 1.第一种情况,如果是多路(new多个AudioTrack线程)mix混音的情况,就是Mi ...

  2. android p ify 三星,Enjarify - Android逆向(二)

    Enjarify - Android逆向(二) 首先奉上enjarify的Github地址,小伙伴们可以clone到本地使用哦 Enjarify介绍 上一节我们说了,在开发Android应用时,And ...

  3. Android录音下————AudioRecord源码分析

    Android录音下----AudioRecord源码分析 文章目录 Android录音下----AudioRecord源码分析 一.概述 1.主要分析点 2.储备知识 二.getMinBufferS ...

  4. Android audio音频流数据异常问题分析

    一.背景 在 Android 系统的开发过程当中,音频异常问题通常有如下几类,无声,调节不了声音,爆音,声音卡顿,声音效果异常(忽大忽小,低音缺失等)等.尤其声音效果这部分问题通常从日志上信息量较少, ...

  5. Android Audio代码分析(2): AudioPoilicyService 启动

    policy: 设备的选择 https://www.cxyzjd.com/article/VNanyesheshou/115659838 Android 音频源码分析--AudioTrack设备选择_ ...

  6. Android中图片压缩分析(上)

    此文章首发:https://mp.weixin.qq.com/s/QZ-XTsO7WnNvpnbr3DWQmg 一.前言 在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种: ...

  7. Android Audio代码分析8 - AudioHardwareALSA::openOutputStream函数

    发现以前写的东西,对调用函数的展开放在了函数的前面,导致不方便找到原来代码及设置的函数参数. 以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些. 闲言少叙,跳入代码. 前两天 ...

  8. 可能是最详细的Android图片压缩原理分析(二)—— 鲁班压缩算法解析

    本篇文章已授权微信公众号guolin_blog(郭霖)独家发布 稀土掘金链接 前言 通过上一篇,我们了解了一些关于图片压缩的基础知识,这篇文章我们主要讲解一下鲁班压缩的算法逻辑,很多博客都是从Gith ...

  9. 【SemiDrive源码分析】【MailBox核间通信】45 - Android侧 RPMSG_IPCC_RPC驱动分析(上) 之 RPMSG设备 与 RPMSG驱动 匹配过程分析

    [SemiDrive源码分析][MailBox核间通信]45 - Android侧 RPMSG_IPCC_RPC驱动分析(上) 之 RPMSG设备 与 RPMSG驱动 匹配过程分析 一. IPCC_R ...

  10. Android Audio音量设置原理流程分析

    Android Audio音量设置原理流程分析 简介 本篇文章主要介绍Android音量设置从App应用层到framework层执行流程,以及相关的细节和原理分析,建议在阅读此文章前去看博主的混音理论 ...

最新文章

  1. 虞旦盛老师 的《数学分析续》课件
  2. 腾讯数据库专家雷海林分享智能运维架构
  3. BAT机器学习面试1000题系列(第1~10题)
  4. 【好文链接】环形队列、串口数据处理
  5. Vue笔记:使用 axios 中 this 指向问题
  6. 简单的时间间隔调度任务
  7. log4j配置文件详解------自学
  8. Mysql-多表查询
  9. 2022年中国数字孪生城市市场分析:孪生城市产业经济全域协作
  10. 计算机二级excel设置宏,Excel2013中为宏指定快捷键的方法
  11. 打印机的系统是linux吗,linux下打印机的配置和使用
  12. 手指在屏幕上滑动,红色的小球始终跟随手指移动。
  13. Docker 生产环境之安全性 - 适用于 Docker 的 AppArmor 安全配置文件
  14. python数据统计分析
  15. 前端有哪些好的学习网站?
  16. 极简linux版本,4MLinux 26.0发布,这是一个极简版本
  17. Python爬虫及其它函数知识读记及简单用法,持续更新中...
  18. php编码规范(未完待续)
  19. [句型] 二十六、特殊疑问句 3
  20. 小春日记 - 基金初识

热门文章

  1. RTKLIB专题学习(十二)—支持的信号ID/观测类型及读取优先级
  2. Spark优化——推测执行机制
  3. java日期计算天数_用Java计算两个日期之间的天数
  4. 好奇怪呀后面加什么标点_说后面加什么标点符号
  5. 对大一c语言学习的感想
  6. Python攻城师的成长——ORM(choices字段)、AJAX
  7. dubbokeeper-moniter部署指南
  8. 路由器工作原理与配置
  9. 基于AD9854的DDS信号发生器设计
  10. 汇编语言 查看存储器中的数据