Android媒体录制框架
Android媒体录制框架
我在此文中只写部分内容,详情参考网上的文章。
在android中录制音频有两种方式,MediaRecorder和AudioRecord。两者的区别如下:
- MediaRecorder
简单方便,不需要理会中间录制过程,结束录制后可以直接得到音频文件进行播放;录制的音频文件是经过压缩的,需要设置编码器;录制的音频文件可以用系统自带的播放器播放。 - audiorecorder
在声音录制过程中,可以处理采集的声音数据,如降噪、合成等。过程为一段一段进行录制然后得到数据分别进行处理。录制的是pcm格式的音频文件,需要用audiotrack来播放,audiotrack更接近底层。
MediaRecorder
MediaRecorder概览图:
MediaRecorder整体框架图:
概要总结:
如上在运行时,整个MediaRecorder大致上可以分为Client和Server两个部分,分别在两个进程中运行,它们之间使用Binder机制实现IPC通信。
MediaPlayerService是多媒体框架中非常重要的一个服务,从框架图中可以看出,MediaRecorder是客户端,MediaPlayerService和MediaRecorderClient是服务器端,MediaPlayerService实现了IMediaPlayerService(接口类)定义的业务逻辑。MediaRecorderClient实现了IMediaRecorder(接口类)定义的业务逻辑,其主要功能包括prepare、start、pause、resume、stop、reset、release等。
C++/JNI层回调事件(JNI层:mr->setListener(listener):将回调接口类MediaRecorderListener设置给MediaRecorder)通知JAVA层是使用JNIMediaRecorderListener::notify(int msg, int ext1, int ext2),该方法通过调用MediaRecoder.java类中private static void postEventFromNative(Object mediarecorder_ref, int what, int arg1, int arg2, Object obj)方法在JNI中的方法句柄,把Native事件回调到Java层,然后使用EventHandler.java类发送事件回到主线层。
主要根据一个基本的录制音视频的调用流程开始分析:
- 创建:new MediaRecorder();
- 设置Camera:mRecorder.setCamera();
- 设置音频源(采集方式):mRecorder.setAudioSource();
- 设置视频源(采集方式):mRecorder.setVideoSource();
- 设置文件的输出格式:mRecorder.setOutputFormat();
- 设置Audio的编码格式(生成对应的编码器):mRecorder.setAudioEncoder();
- 设置Video的编码格式(生成对应的编码器):mRecorder.setVideoEncoder();
- 设置录制的视频编码比特率(每秒编码多少位bit):mRecorder.setVideoEncodingBitRate();
- 设置录制的视频帧率:mRecorder.setVideoFrameRate();
- 设置要捕获的视频的宽度和高度:mRecorder.setVideoSize();
- 设置记录会话的最大持续时间(毫秒):mRecorder.setMaxDuration();
- 设置一个Surface进行预览显示:mRecorder.setPreviewDisplay();
- 设置输出文件路径:mRecorder.setOutputFile();
- 准备录制:mRecorder.prepare();
- 开始录制:mRecorder.start();
- 暂停或恢复录制:mRecorder.pause()/resume();
- 停止录制:mRecorder.stop();
- 重置Recorder:mRecorder.reset();
- 释放Recorder资源:mRecorder.release();
StagefrightRecorder
如图所示,MediaRecorder在运行时,可以分成Client和Server两个部分,它们分别在两个进程中运行,它们之间使用Binder机制实现IPC通讯。
手机启动时会启动进程/system/bin/mediaserver。该进程会把media相关服务注册到ServiceManager中,如MediaPlayerService。
应用层创建MediaRecorder实例:mMediaRecorder = new MediaRecorder();调用SDK 中MediaRecorder.java,通过JNI方式调用到framework层android_media_MediaRecorder.cpp。
继而调用mediarecorder.cpp的构造函数,它首先会从ServiceManager中获得MediaPlayerService服务,然后通过服务来创建recorder。这个recorder就是录音的真实实例。
通过getMediaPlayerService得到的service其实是BpMediaPlayerService,它和mediaserver进程中的BnMediaPlayerService是相对应的,共同负责进程间binder通信。BpMediaPlayerService中的createMediaRecorder其实是通过binder机制将CREATE_MEDIA_RECORDER消息发送出去。
在BnMediaPlayerService中,通过onTransact()来处理接收到的消息,并返回结果。当接收消息中的code为CREATE_MEDIA_RECORDER时,调用MediaPlayerService中的createMediaRecorder函数。在该函数中创建了一个MediaRecorderClient的实例,也就是说MediaPlayerService会为每个client应用进程创建一个相应的MediaRecorderClient的实例,来提供服务。
如此MediaRecorder.cpp就得到了一个recorder的实例,对它来说这个实例和本地的其他类的实例没什么用法上的区别,但其实这个实例是运行在另外一个进程中。实现这种假象的就是binder机制。在MediaRecorderClient的构造函数中,才会真正的创建StagefrightRecorder的具体实例,即真正的录制对象,使用的StageFright多媒体框架。在android 4.0以后只有StagefrightRecorder一个录制框架。在2.2、2.3中还存在另外一个录制对象PVMediaRecorder,使用的是OpenCore框架实现录音或录像。
- 音视频处理过程图:
- 音视频Puller处理过程图:
录像流程
这里直接是网上的资料(内容竟然还是蛮多的):
Android基础总结: Camera2详解之一 API学习
极客笔记 Android Camera2 API(里面有系列讲解)
使用Camera2 替代过时的Camera API(里面有谷歌文档链接)
android Camera2 API使用详解(不用看,和下一篇类似)
Android:Camera2开发详解(上):实现预览、拍照、保存照片等功能(不用看)
马小藤的简书:
Android Camera简单整理(一)-Camera Android架构(基于Q)
Android Camera简单整理(二)-Qcom HAL3 Camx架构学习
Android Camera简单整理(三)-Mtk Camera MtkCam3架构学习
Android Camera简单整理(四)-Android Camera性能Debug经验
这是从谷歌官方文档翻译而来:
android.hardware.camera2包为连接到Android设备的各个相机设备提供了一个接口。它取代了不推荐使用的Camera类。
此包将相机设备建模为管道,它接收用于捕获单个帧的输入请求,根据请求捕获单个图像,然后输出一个捕获结果元数据包,以及一组用于请求的输出图像缓冲区。请求按顺序处理,多个请求可以同时进行。由于摄像头设备是具有多个阶段的管道,因此需要在运行中处理多个请求才能在大多数Android设备上保持全帧率。
要枚举、查询和打开可用的摄像头设备,请获取一个CameraManager实例。
一个CameraDevices提供一组静态属性信息,描述硬件设备以及设备的可用设置和输出参数。此信息通过CameraCharacteristics对象提供,并可通过getCameraCharacteristics(String)提供。
要从相机设备捕获或流式传输图像,应用程序必须首先创建一个带有一组输出Surfaces的camera capture session以使用相机设备,这用createCaptureSession(SessionConfiguration)来完成。每个Surface都必须预先配置appropriate size and format(如果适用)以匹配相机设备可用的尺寸和格式。目标Surface(包括SurfaceView、SurfaceTexture)可以从各种类中获得,例如:Surface(SurfaceTexture)、MediaCodec、MediaRecorder、Allocation和ImageReader。
通常,相机预览图像被发送到SurfaceView或TextureView(通过其SurfaceTexture)。可以使用JPEG和RAW_SENSOR格式的ImageReader为DngCreator捕获JPEG图像或RAW缓冲区。在RenderScript、OpenGL ES中或直接在托管代码或本机代码中,应用程序驱动的相机数据处理最好分别通过YUV类型、SurfaceTexture和ImageReader的YUV_420_888格式的分配来完成。
然后,应用程序需要构建一个CaptureRequest,它定义了相机设备捕获单个图像所需的所有捕获参数。该请求还列出了哪些配置的输出Surfaces应该作为此捕获的目标。CameraDevice有一个factory method用于为给定用例创建一个request builder,它针对运行应用程序的Android设备进行了优化。
设置请求后,可以将其交给活动捕获会话以供一次性使用capture或无休止地repeating使用。这两种方法还有一个变体,它接受请求列表以用作突发捕获/重复突发。重复请求的优先级低于当前捕获,因此在有重复请求时通过capture()提交的请求将在当前重复(突发)捕获的任何新实例开始捕获之前被捕获。
处理请求后,相机设备将生成一个TotalCaptureResult对象,其中包含有关相机设备在捕获时的状态以及使用的最终设置的信息。如果需要舍去或解决矛盾的参数,这些可能与请求有所不同。相机设备还将向请求中包含的每个输出Surfaces发送一帧图像数据。这些是相对于输出CaptureResult异步生成的,有时会晚得多。
这是谷歌关于相机的文档:AOSP开发相机
录音流程
这是AudioRecorder的架构和使用流程图:
也是先放网上的资料:
AudioRecord API Reference Document
Android录音AudioRecord,AudioTrack学习
这是从谷歌官方文档翻译而来:
AudioRecord类管理Java应用程序的音频资源,以记录来自平台音频输入硬件的音频。这是通过从AudioRecord对象“拉”(读取)数据来实现的。应用程序负责使用以下三种方法之一read(byte[], int, int),read(short[], int, int)或read(java.nio.ByteBuffer, int)及时轮询AudioRecord对象。选择使用哪种方法将基于AudioRecord用户最方便的音频数据存储格式。
在创建时,AudioRecord对象初始化其关联的音频缓冲区,它将用新的音频数据填充。这个缓冲区的大小,在构造过程中指定,决定了AudioRecord在“溢出”尚未读取的数据之前可以记录多长时间。应该从音频硬件中以小于总记录缓冲区大小的块读取数据。
创建AudioRecord实例的应用程序需要Manifest.permission.RECORD_AUDIO权限。否则Builder将在build()中抛出UnsupportedOperationException,并且构造函数将返回一个STATE_UNINITIALIZED状态的实例 。
利用AudioRecord实现Android录音的流程为:
- 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过。getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。
- 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。
- 调用startRecording函数,开始录音。
- 创建一个数据流,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中数据导入数据流,生成PCM格式文件
- 录音结束时,关闭数据流, 停止录音。
代码实现如下:
1 | |
AudioBitrate客制化
各种各样的图
- PersistentSurface及GraphicBufferSource实现的BufferQueue框架:
- 写文件的过程及重要类:
- MediaRecorder init:
- MediaRecorder prepare:
- MediaCodecSource 即对应videoEncoder和audioEncoder的初始化:
- MediaRecorder start:
- MediaRecorder stop:
- MediaRecorder init flow:
- PersistentSurface init and setInputSurface flow:
- PersistentSurface prepare flow:
- MediaRecorder start flow:
- MediaRecorder Buffer Callback from OMX to MediaRecorder:
- MediaRecorder stop flow: