前面我们知道Writer类中开了一个线程在进行实际的录音。

Writer.addSource()中,有这样一段代码:

    const char *kHeader = isWide ? "#!AMR-WB\n" : "#!AMR\n";
ssize_t n = strlen(kHeader);
if (write(mFd, kHeader, n) != n) {
return ERROR_IO;
}

其实就是在录音之前先将音频的一些参数信息写进去。

在StagefrightRecorder::createAudioSource()中,有一段代码:

  sp<MediaSource> audioEncoder =
OMXCodec::Create(client.interface(), encMeta,
true /* createEncoder */, audioSource);
mAudioSourceNode = audioSource;
return audioEncoder;

这里是创建一个编码器

在OMXCodec::Create()中是这样的,先调用findMatchingCodecs查找匹配的编码器,如果没有匹配的就返回,,如果需要添加编码器,那么就添加一个:

   if (createEncoder) {
sp<MediaSource> softwareCodec =
InstantiateSoftwareEncoder(componentName, source, meta);
if (softwareCodec != NULL) {
ALOGV("Successfully allocated software codec '%s'", componentName);
return softwareCodec;
}
}

因此,添加音频编码器就在这里添加咯!!!

static sp<MediaSource> InstantiateSoftwareEncoder(
const char *name, const sp<MediaSource> &source,
const sp<MetaData> &meta) {
struct FactoryInfo {
const char *name;
sp<MediaSource> (*CreateFunc)(const sp<MediaSource> &, const sp<MetaData> &);
};
static const FactoryInfo kFactoryInfo[] = {
FACTORY_REF(AACEncoder)
};
for (size_t i = 0;
i < sizeof(kFactoryInfo) / sizeof(kFactoryInfo[0]); ++i) {
if (!strcmp(name, kFactoryInfo[i].name)) {
return (*kFactoryInfo[i].CreateFunc)(source, meta);
}
}
return NULL;
}

由这个create中的匹配过程,可以知道,编解码器存放的地方是:struct MediaCodecList中,以一个向量保存

\\frameworks\av\media\libstagefright\MediaCodecList.h

    Vector<CodecInfo> mCodecInfos;
ssize_t MediaCodecList::findCodecByType(
const char *type, bool encoder, size_t startIndex) const {
ssize_t typeIndex = mTypes.indexOfKey(type);
if (typeIndex < 0) {
return -ENOENT;
}
while (startIndex < mCodecInfos.size()) {
const CodecInfo &info = mCodecInfos.itemAt(startIndex);
if (info.mIsEncoder == encoder && (info.mTypes & typeMask)) {
return startIndex;
}
++startIndex;
}
return -ENOENT;
}

对编码器的使用,也就是线程里的 err = mSource->read(&buffer);  由read()函数可以知道音频数据来源是 List<MediaBuffer * > mBuffersReceived这个Buffer:

status_t AudioSource::read(
MediaBuffer **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
*out = NULL;
if (mInitCheck != OK) {
return NO_INIT;
}
while (mStarted && mBuffersReceived.empty()) {
mFrameAvailableCondition.wait(mLock);
}
if (!mStarted) {
return OK;
}
MediaBuffer *buffer = *mBuffersReceived.begin();
mBuffersReceived.erase(mBuffersReceived.begin());
++mNumClientOwnedBuffers;
buffer->setObserver(this);
buffer->add_ref();
// Mute/suppress the recording sound
int64_t timeUs;
CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
int64_t elapsedTimeUs = timeUs - mStartTimeUs;
if (elapsedTimeUs < kAutoRampStartUs) {
memset((uint8_t *) buffer->data(), 0, buffer->range_length());
} else if (elapsedTimeUs < kAutoRampStartUs + kAutoRampDurationUs) {
int32_t autoRampDurationFrames =
(kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL;
int32_t autoRampStartFrames =
(kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL;
int32_t nFrames = mNumFramesReceived - autoRampStartFrames;
rampVolume(nFrames, autoRampDurationFrames,
(uint8_t *) buffer->data(), buffer->range_length());
}
// Track the max recording signal amplitude.
if (mTrackMaxAmplitude) {
trackMaxAmplitude(
(int16_t *) buffer->data(), buffer->range_length() >> 1);
}
*out = buffer;
return OK;
}

而跟踪这个mBuffersReceived,可以知道Android在构造AudioSource实例的时候会指定一个数据源inputSource和回调函数AudioRecordCallbackFunction,在AudioSource中会构造AudioRecord对象,实际录音过程中就是在AudioRecord中通过这个回调函数中将数据一帧一帧填充进去的。

AudioSource::AudioSource(
audio_source_t inputSource, uint32_t sampleRate, uint32_t channelCount)
: mRecord(NULL),
mStarted(false),
mSampleRate(sampleRate),
mPrevSampleTimeUs(0),
mNumFramesReceived(0),
mNumClientOwnedBuffers(0) {
ALOGV("sampleRate: %d, channelCount: %d", sampleRate, channelCount);
CHECK(channelCount == 1 || channelCount == 2);
size_t minFrameCount;
status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
sampleRate,
AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount));
if (status == OK) {
// make sure that the AudioRecord callback never returns more than the maximum
// buffer size
int frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
// make sure that the AudioRecord total buffer size is large enough
int bufCount = 2;
while ((bufCount * frameCount) < minFrameCount) {
bufCount++;
}
mRecord = new AudioRecord(
inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount),
bufCount * frameCount,
<span style="color:#ff0000;">AudioRecordCallbackFunction</span>,
this,
frameCount);
mInitCheck = mRecord->initCheck();
} else {
mInitCheck = status;
}
}

如果想知道录音设备,比如MIC中的数据是怎么通过回调函数写到Buffer中,那就要继续跟踪传到AudioRecord中的回调函数AudioRecordCallbackFunction了。

分析AudioRecord可以知道,在创建AudioRecord的时候会创建一个线程

 if (cbf != NULL) {
mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava);
mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
}

调用回调的这个过程就是在这个AudioRecordThread线程中操作的。

bool AudioRecord::AudioRecordThread::threadLoop()
{
{
AutoMutex _l(mMyLock);
if (mPaused) {
mMyCond.wait(mMyLock);
// caller will check for exitPending()
return true;
}
}
if (!mReceiver.processAudioBuffer(this)) {
pause();
}
return true;
}

一次次在processAudioBuffer(this)中调用回调函数(也就是AudioRecord.mCbf)。

 mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);

前面都是初始化构建过程,现在来看下AudioRecord::start,我们知道其中会调用IAudioRecord类型对象mAudioRecord的start函数。

status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession)
{
status_t ret = NO_ERROR;
sp<AudioRecordThread> t = mAudioRecordThread;
ALOGV("start, sync event %d trigger session %d", event, triggerSession);
AutoMutex lock(mLock);
// acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
// while we are accessing the cblk
sp<IAudioRecord> audioRecord = mAudioRecord;
sp<IMemory> iMem = mCblkMemory;
audio_track_cblk_t* cblk = mCblk;
if (!mActive) {
mActive = true;
cblk->lock.lock();
if (!(cblk->flags & CBLK_INVALID)) {
cblk->lock.unlock();
ALOGV("mAudioRecord->start()");
ret = mAudioRecord->start(event, triggerSession);
cblk->lock.lock();
if (ret == DEAD_OBJECT) {
android_atomic_or(CBLK_INVALID, &cblk->flags);
}
}
if (cblk->flags & CBLK_INVALID) {
audio_track_cblk_t* temp = cblk;
ret = restoreRecord_l(temp);
cblk = temp;
}
cblk->lock.unlock();
if (ret == NO_ERROR) {
mNewPosition = cblk->user + mUpdatePeriod;
cblk->bufferTimeoutMs = (event == AudioSystem::SYNC_EVENT_NONE) ? MAX_RUN_TIMEOUT_MS :
AudioSystem::kSyncRecordStartTimeOutMs;
cblk->waitTimeMs = 0;
if (t != 0) {
t->resume();
} else {
mPreviousPriority = getpriority(PRIO_PROCESS, 0);
get_sched_policy(0, &mPreviousSchedulingGroup);
androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
}
} else {
mActive = false;
}
}
return ret;
}

分析围绕IAudioRecord接口的类图


可以知道,最终调用的是AudioFinger中RecordHandler这个类的start()函数。

status_t AudioFlinger::RecordHandle::start(int /*AudioSystem::sync_event_t*/ event,
int triggerSession) {
ALOGV("RecordHandle::start()");
return mRecordTrack->start((AudioSystem::sync_event_t)event, triggerSession);
}

后面再分析AudioFlinger怎么操作硬件。

这篇博文提到了怎么在AudioFlinger中处理音频多路输出的情况:

http://blog.csdn.net/hgl868/article/details/6888502

Android 语音遥控器的整体分析-底层实现机制分析相关推荐

  1. Android 语音遥控器的整体分析

    今天蓝牙遥控器的导入终于完成了,分别梳理和记录一下部分语音和蓝牙相关的知识,首先是主机端上层语音部分: 一.应用层使用MediaRecorder的过程(应用层) 1.创建一个MediaRecorder ...

  2. Android 语音遥控器的整体分析-HAL层的AudioFlinger

    上篇说到语音部分最后会通过AudioFlinger来操作HAL层. 一.首先我们看下硬件接口层的接口(奇怪为什么只有Audio的hardwareinterface): (1)hardware\libh ...

  3. Android 语音遥控器的整体分析-主机端语音解码的添加

    前面几篇大致介绍了HAL层的实现方式.这里要介绍下如何在Android主机端的HAL层语音解码的添加. 一.首先需要了解libhardware.so(\libhardware\hardware.c) ...

  4. 2022-2028全球与中国语音遥控器市场现状及未来发展趋势

    2021年全球语音遥控器市场销售额达到了 亿美元,预计2028年将达到 亿美元,年复合增长率(CAGR)为 %(2022-2028).地区层面来看,中国市场在过去几年变化较快,2021年市场规模为 百 ...

  5. 智能会议系统(34)---Android语音通话实现方案及相关技术介绍

    Android语音通话实现方案及相关技术介绍 Android语音通话实现方案及相关技术介绍 语音通话 Step1语音采集和输出 Step2编解码方式 Step3网络传输 Step4去噪声消回音 语音通 ...

  6. Android系统(62)---Alarm的机制

    Android中Alarm的机制 本次给大家分析的是Android中Alarm的机制所用源码为最新的Android4.4.4.首先简单介绍如何使用Alarm并给出其工作原理,接着分析Alarm和Tim ...

  7. Android语音通话实现方案及相关技术介绍

    Android语音通话实现方案及相关技术介绍 Android语音通话实现方案及相关技术介绍 语音通话 Step1语音采集和输出 Step2编解码方式 Step3网络传输 Step4去噪声消回音 语音通 ...

  8. Android 语音播放Media Player

    原文地址: https://developer.android.com/guide/topics/media/mediaplayer.html#viacontentresolver 语音播放 因为实习 ...

  9. android 语音播放

    android 语音播放 MediaPlayer可以播放本地或者网络的音频,流程如下: Uri myUri = ....; // initialize Uri here MediaPlayer med ...

最新文章

  1. bp神经网络应用实例_人工智能BP神经网络学习神器——AISPACE
  2. 如何删除Cookie?
  3. HTTP1.0,HTTP1.1,HTTPS和HTTP2.0的区别
  4. springboot @ConfigurationProperties注入属性流程
  5. JavaScript分支结构(判断结构)使用教程
  6. Angular @NgModule providers里multi等于true在源代码里如何体现的
  7. JAVA中在某游戏系统有猫狗猪_算法面试题之猫狗队列(java)
  8. equation在c语言中是什么意思,MathType出现此对象创建于Equation中的问题怎么办
  9. JMeter循环控制器循环次数使用变量控制注意事项
  10. 鸿蒙历程和路标图,鸿蒙2.0来了?华为开发者大会时间确认:Mate40会不会首发?...
  11. 百度文库文章提取器(下)
  12. Mac_苹果电脑设置眼睛保护色
  13. 英文名字的昵称(亲切的叫法)
  14. iPhoneSE3定价或跌穿3K,苹果不给安卓手机活路了?
  15. HDUOJ 2059 龟兔赛跑——
  16. windows使用模拟器
  17. “咱们吃鸡吧”的背后
  18. CentOS 7 网络配置
  19. 博途PLC和CODESYS平台下FB编程应用(如何实例化多个FB)
  20. Leetcode刷题笔记之445. 两数相加Ⅱ

热门文章

  1. ArrayList 排序
  2. 15个可以做签名的高权重论坛
  3. 【屏幕录制软件集锦】自己收藏的一些认为比较好的软件
  4. 色阶的中间调调节原理——兼论色阶和曲线的联系
  5. java80/20法则_那些很熟悉但又不知怎么用的设计法则(1):80/20法则
  6. linebreak_operator-linebreak
  7. html5 等比压缩图片,HTML5实现input:file上传压缩,等比压缩图片、base64和文件互相转换...
  8. glc四驱软件测试,【图】独家官方答复 奔驰GLC四驱情况新进展_汽车之家
  9. 特斯拉是l3还是l2_自动驾驶L2、L3级,到底是什么?竟引得BBA联手开发
  10. 虹科方案 | 虹科Vdoo安全平台:CVE-2020-25860 - 在 RAUC 嵌入式固件更新框架中发现的重大漏洞