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 深入系统完全讲解(25)

Android APP使用系统签名

Android蓝牙输入/输出流:发送~1400字节,接收1008

如何使用套接字编程在两个 android 手机之间创建实时音频流..?

android文件资源解析

将RTSP流保存到android中的mp4文件