您的位置:首页 > 移动开发 > Android开发

Android MediaRecorder系统结构

2016-04-15 06:01 232 查看
前面有分析过Camera的实现,现在来看看MediaRecorder的实现,这里我不会太去关注它的分层结构,我更关注它的逻辑!

APP层 /path/to/aosp/frameworks/base/media/java/android/media/MediaRecorder.java

JNI层 /path/to/aosp/frameworks/base/media/jni/android_media_MediaRecorder.cpp

调用NATIVE层的MediaRecorder(这里是BnMediaRecorderClient)

header /path/to/aosp/frameworks/av/include/media/mediarecorder.h

implementation
/path/to/aosp/frameworks/av/media/libmedia/mediarecorder.cpp

?
getMediaPlayerService()这个方法位于/path/to/aosp/frameworks/av/include/media/IMediaDeathNotifier.h

获取到MediaPlayerService(这个是BpMediaPlayerService)之后

调用IMediaPlayerService当中的

?
创建MediaRecorderClient(这里是BnMediaRecorder)

但是通过binder拿到的是BpMediaRecorder

因为有如下的interface_cast过程

?
而MediaRecorderClient当中又会创建StagefrightRecorder(MediaRecorderBase),它位于

/path/to/aosp/frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp

目前我们可以认为在APP/JNI/NATIVE这边是在一个进程当中,在MediaPlayerService当中的MediaRecorderClient/StagefrightRecorder是在另外一个进程当中,他们之间通过binder通信,而且Bp和Bn我们也都有拿到,后面我们将不再仔细区分Bp和Bn。

客户端这边

BnMediaRecorderClient

BpMediaRecorder

BpMediaPlayerService

服务端这边

BpMediaRecorderClient(如果需要通知客户端的话,它可以获得这个Bp)

BnMediaRecorder

BnMediaPlayerService

这有张图(点过去看原始大图)





我们以开始录影为例子,比如start()

在这里就兵分两路,一个CameraSource,一个MPEG4Writer(sp mWriter)

这两个class都位于/path/to/aosp/frameworks/av/media/libstagefright/当中

?
?
?
?
?
这里和OMXCodec关联起来

有一个叫media_codecs.xml的配置文件来表明设备支持哪些codec

我们录制MPEG 4的时候还会有声音,所以后面还有个setupAudioEncoder,具体的方法就不展开了,总之就是把声音也作为一个Track加入到MPEG4Writer当中去。

这里插个题外话,Google说把setupAudioEncoder放到后面是为了避免开始录影的那一个提示声音也被录制进去,但是实际发现它这样做还是会有bug,在一些设备上还是会把那声录制进去,这个遇到的都是靠APP自己来播放声音来绕过这个问题的。

另外MPEG4Writer当中有个

start(MetaData*)

启动两个方法

a) startWriterThread

启动一个thread去写

?
b) startTracks

?
然后调用每个Track的start方法

?
通过status_t MPEG4Writer::Track::threadEntry()

是新启动另外一个thread,它里面会通过一个循环来不断读取CameraSource(read)里面的数据,CameraSource里面的数据当然是从driver返回过来的(可以参见CameraSourceListener,CameraSource用一个叫做mFrameReceived的List专门存放从driver过来的数据,如果收到数据会调用mFrameAvailableCondition.signal,若还没有开始录影,这个时候收到的数据是被丢弃的,当然MediaWriter先启动的是CameraSource的start方法,再启动写Track),然后写到文件当中。

注意:准确来说这里MPEG4Writer读取的是OMXCodec里的数据,因为数据先到CameraSource,codec负责编码之后,MPEG4Writer才负责写到文件当中!关于数据在CameraSource/OMXCodec/MPEG4Writer之间是怎么传递的,可以参见http://guoh.org/lifelog/2013/06/interaction-between-stagefright-and-codec/当中讲Buffer的传输过程。

回头再来看,Stagefright做了什么事情?我更觉得它只是一个粘合剂(glue)的用处,它工作在MediaPlayerService这一层,把MediaSource,MediaWriter,Codec以及上层的MediaRecorder绑定在一起,这应该就是它最大的作用,Google用它来替换Opencore也是符合其一贯的工程派作风(相比复杂的学术派而言,虽然Google很多东西也很复杂,但是它一般都是以尽量简单的方式来解决问题)。

让大家觉得有点不习惯的是,它把MediaRecorder放在MediaPlayerService当中,这两个看起来是对立的事情,或者某一天它们会改名字,或者是两者分开,不知道~~

当然这只是个简单的大体介绍,Codec相关的后面争取专门来分析一下!

有些细节的东西在这里没有列出,需要的话会把一些注意点列出来:

1. 时光流逝录影

CameraSource对应的就是CameraSourceTimeLapse

具体做法就是在

dataCallbackTimestamp

当中有skipCurrentFrame

当然它是用些变量来记录和计算

mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate) // 两个frame之间的间隔时间

记录上一个frame的(mLastTimeLapseFrameRealTimestampUs) // 上一个frame发生的时间

然后通过frame rate计算出两个frame之间的相距离时间,中间的都透过releaseOneRecordingFrame来drop掉

也就是说driver返回的东西都不变,只是在SW这层我们自己来处理掉

关于Time-lapse相关的可以参阅

https://en.wikipedia.org/wiki/Time-lapse_photography

2. 录影当中需要用到Camera的话是通过ICameraRecordingProxy,即Camera当中的RecordingProxy(这是一个BnCameraRecordingProxy)

当透过binder,将ICameraRecordingProxy传到服务端进程之后,它就变成了Bp,如下:

?
在CameraSource当中会这样去使用

?
疑问点:

CameraSource当中这个

List > mFramesBeingEncoded;

有什么用?

每编码完一个frame,CameraSource就会将其保存起来,Buffer被release的时候,会反过来release掉这些frame(s),这种做法是为了效率么?为什么不编码完一个frame就将其release掉?

另外不得不再感叹下Google经常的delete this;行为,精妙,但是看起来反常!

原文地址: http://guoh.org/lifelog/2013/06/android-mediarecorder-architecture/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: