Android R MediaRecoder创建流程
Posted 虫师魁拔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android R MediaRecoder创建流程相关的知识,希望对你有一定的参考价值。
应用层一般录音时,采用 MediaRecoder 代码如下
MediaRecorder mediaRecorder = new MediaRecorder();
// 设置音频来源 MIC == 麦克
mediaRecorder.setAudiosource(MediaRecorder.AudioSource.MIC);
// 设置默认音频输出格式 .amr 格式
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
// 设置默认音频编码方式 .amr 编码
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
// 设置保存文件路径
mediaRecorder.setOutputFile(file);
try {
mediaRecorder.prepare();
// 开始录制
mediaRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
1、接下来分析源码创建流程
查看 MediaRecorder 中源码,这几个都是 native 方法,直接找它对应的 JNI 函数,按照android命名规则很好找,在 frameworks/base/media/jni/ 目录下,按照类名查找,这里对应的就是 android_media_MediaRecorder.cpp
函数调用的流程差不多,我们以 prepare() 为例:(为啥选择 prepare,因为它很重要 很重要 很重要)
MediaRecorder.java 中最终执行的
private native void _prepare() throws IllegalStateException, IOException;
android_media_MediaRecorder.cpp 中根据 JNINativeMethod 定义的数组查找 _prepare 对应的函数为:
android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)
这里会去获取 sp<MediaRecorder> mr,再执行 mr->prepare(),接着看 MediaRecorder.cpp 中,又是 mMediaRecorder->prepare() ,mMediaRecorder 是 sp<IMediaRecorder> 的。继续看 IMediaRecorder.cpp 中,IMediaRecorder 是一个接口类,并且定义了 如下两个主要类,
class BpMediaRecorder: public BpInterface<IMediaRecorder>
{
... ...
status_t prepare()
{
ALOGV("prepare");
Parcel data, reply;
data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
remote()->transact(PREPARE, data, &reply);
return reply.readInt32();
}
... ...
}
... ...
status_t BnMediaRecorder::onTransact(
uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags)
{
... ...
switch (code) {
case PREPARE: {
ALOGV("PREPARE");
CHECK_INTERFACE(IMediaRecorder, data, reply);
reply->writeInt32(prepare());
return NO_ERROR;
} break;
... ...
default:
return BBinder::onTransact(code, data, reply, flags);
}
代码中 remote() 是关键,首先要知道 remote() 是返回一个 IBinder 类,远端代理,这里简单理解下。transact 就会走到下面 onTransact ,然后执行远程服务端 prepare() 函数。
服务即 system/bin/mediaserver 这个了,系统在 init.rc 中注册了 mediaserver ,会在开机时启动这个服务,接下来就是去这个服务中处理。找到上面 BnMediaRecorder 类的继承者 MediaRecorderClient,MediaRecorderClient.cpp 中继续执行 prepare() ,再走到 StagefrightRecorder.cpp 中执行 prepare()
status_t StagefrightRecorder::prepareInternal() {
ALOGV("prepare");
if (mOutputFd < 0) {
ALOGE("Output file descriptor is invalid");
return INVALID_OPERATION;
}
// Get UID and PID here for permission checking
mClientUid = IPCThreadState::self()->getCallingUid();
mClientPid = IPCThreadState::self()->getCallingPid();
status_t status = OK;
switch (mOutputFormat) {
case OUTPUT_FORMAT_DEFAULT:
case OUTPUT_FORMAT_THREE_GPP:
case OUTPUT_FORMAT_MPEG_4:
case OUTPUT_FORMAT_WEBM:
status = setupMPEG4orWEBMRecording();
break;
case OUTPUT_FORMAT_AMR_NB:
case OUTPUT_FORMAT_AMR_WB:
status = setupAMRRecording();
break;
case OUTPUT_FORMAT_AAC_ADIF:
case OUTPUT_FORMAT_AAC_ADTS:
status = setupAACRecording();
break;
... ...
return status;
}
status_t StagefrightRecorder::prepare() {
ALOGV("prepare");
Mutex::Autolock autolock(mLock);
if (mVideoSource == VIDEO_SOURCE_SURFACE) {
return prepareInternal();
}
return OK;
}
最终走到 prepareInternal() ,然后根据之前应用设置
mMediaRecorder.setAudioSource mMediaRecorder.setOutputFormat
这几个来做一个检查,例如 OUTPUT_FORMAT_AAC_ADTS 这种类型,执行 setupAACRecording() 检查
status_t StagefrightRecorder::setupAACRecording() {
// FIXME:
// Add support for OUTPUT_FORMAT_AAC_ADIF
CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_AAC_ADTS);
CHECK(mAudioEncoder == AUDIO_ENCODER_AAC ||
mAudioEncoder == AUDIO_ENCODER_HE_AAC ||
mAudioEncoder == AUDIO_ENCODER_AAC_ELD);
CHECK(mAudioSource != AUDIO_SOURCE_CNT);
mWriter = new AACWriter(mOutputFd);
return setupRawAudioRecording();
}
这里敲黑板了,CHECK 不同于 ASSERT,但是作用也差不太多,检测失败会直接使服务GG,所以一定要注意应用设置参数时候别瞎来,不匹配的格式 prepare 不过。当然应用不会闪退,挂的是mediaserver,录音文件肯定是没了,logcat中会有如下信息提示:
DEBUG : Abort message: 'frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp:1244 CHECK(mAudioEncoder == AUDIO_ENCODER_AAC || mAudioEncoder == AUDIO_ENCODER_HE_AAC || mAudioEncoder == AUDIO_ENCODER_AAC_ELD) failed.'
DEBUG : r0 00000000 r1 00000ba1 r2 00000006 r3 ea2fba18
DEBUG : r4 ea2fba2c r5 ea2fba10 r6 0000049f r7 0000016b
DEBUG : r8 ea2fba18 r9 ea2fba28 r10 ea2fba48 r11 ea2fba38
DEBUG : ip 00000ba1 sp ea2fb9e8 lr ef307eed pc ef307f00
DEBUG : backtrace:
DEBUG : #00 pc 00038f00 /apex/com.android.runtime/lib/bionic/libc.so (abort+172) (BuildId: 3f6c9092328e50137ce743f77ee900af)
DEBUG : #01 pc 00004c03 /system/lib/liblog.so (__android_log_default_aborter+6) (BuildId: cd46e7f1d132c868d445d550f408d5ac)
DEBUG : #02 pc 00005323 /system/lib/liblog.so (__android_log_assert+174) (BuildId: cd46e7f1d132c868d445d550f408d5ac)
到这里就设置完了,最后 start() 会调用 MediaWriter.h 中函数,这里只有声明,没看到实现的,应该是直接调用so库里面内容了,后续再看
参考文档:
android 中CHECK宏分析
Binder机制(四)通过demo研究Binder上层接口设计
以上是关于Android R MediaRecoder创建流程的主要内容,如果未能解决你的问题,请参考以下文章
Android蓝牙输入/输出流:发送~1400字节,接收1008