音视频录制状态机:start→ Initial -→Initialized → DataSource Configured → Prepared-→Recording
录制过程从上到下涉及很多模块:
1),app层,描述各种录制参数;
2)framework层,主要指java层framework,如MeidaRecorder,在收到app层的录制需求后,协调camera,micphone等模块完成录制请求,同时回调实时录制状态。
3)库层,如cameraservice,audiofligner,surfacefligner,Mediaplayerservice,stageFright等,其中mediaplayerservice是底层多媒体库向上的接口,也就说底层的多媒体库可能因为平台或者升级原因不断改变,但是只要mediaplayerservice中的接口不变,上层的框架及应用就不会受影响。
4)stagefright,是多媒体库的核心库。
5)Hal或者驱动层,处理音视频的来源,存储,及硬件的编解码等。
1,从Camera中录像的工作流程开始分析:
mMediaRecorder = new MediaRecorder();
Camera camera = mCameraDevice.getCamera();
mMediaRecorder.setCamera(camera);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(mProfile);
mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
mMediaRecorder.setOutputFile(mVideoFilename);
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
mMediaRecorder.prepare();
mMediaRecorder.start();
下面具体看下MediaRecorder怎么完成这个功能函数的调用的。以录制预览的窗口为例。
Frameworks/base/media/java/android/media/MediaRecorder.java
public void setPreviewDisplay(Surface sv) {
//只是做了个记录,
mSurface = sv;
}
什么时候会使用到setPreviewDisplay设置的surface呢?从MediaRecorder的构造函数开始看:
public class MediaRecorder{
//先加载jni库,然后调用native层的实现native_init,
static {
System.loadLibrary("media_jni");
native_init();
}
}
frameworks/base/media/jni/android_media_MediaRecorder.cpp
static void android_media_MediaRecorder_native_init(JNIEnv *env){
//这个函数的作用,是把mediarecorder.java中的部分变量保存在 fields中,如前面设置的surface被保存在了fields.surface 。
clazz = env→FindClass("android/media/MediaRecorder");
// mNativeContext用来保存本地的meidaRecorder.cpp实例
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
}
mediaRecorder.java的构造函数,直接调用了native_setup。
frameworks/base/media/jni/android_media_MediaRecorder.cpp
static void android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jstring packageName, jstring opPackageName){
//创建native层实例MediaRecorder,并设置一个listenter,用于回调录制状态。
sp<MediaRecorder> mr = new MediaRecorder(String16(opPackageNameStr.c_str()));
sp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this);
mr->setListener(listener);
//把创建mediaRecorder.cpp实例,保存到fields.context中。
setMediaRecorder(env, thiz, mr);
}
2,按照状态机,在start前,要调用prepared方法。java层的其实没做什么事情,直接看本地层实现:
frameworks/base/media/jni/android_media_MediaRecorder.cpp
static void android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz){
//获取mediaRecorder.cpp实例,获取之前保存的java层的surface,进一步获取nativesurface。
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
jobject surface = env->GetObjectField(thiz, fields.surface);
const sp<Surface> native_surface = get_surface(env, surface);
//为mediaplayer.cpp设置previewSurface,
if (process_media_recorder_call(env, mr->setPreviewSurface(
native_surface->getIGraphicBufferProducer()), "java/lang/RuntimeException",
"setPreviewSurface failed.")) {}
//调用mediarecorder.cpp的prepare。
process_media_recorder_call(env, mr->prepare(), "java/io/IOException", "prepare failed.");
}
mediaRecorder.cpp的构造函数中创建了sp<IMediaRecorder> mMediaRecorder这是是app端跟MediaPlayerService之间通信的桥梁,跨进程的。
Frameworks/av/media/libmeida/MediaRecorder.cpp
MediaRecorder::MediaRecorder(const String16& opPackageName) : mSurfaceMediaSource(NULL){
//通过MediaPlayerService的客户端实例,创建了 IMediaRecorder,然后把录制状态置为Idle。
const sp<IMediaPlayerService> service(getMediaPlayerService());
mMediaRecorder = service->createMediaRecorder(opPackageName);
mCurrentState = MEDIA_RECORDER_IDLE;
}
接着看MediaPlayerService中的实现,IMediaRecorder对应在MPS中的server实现是MediaRecorderClient。应用端录制的各种请求都会通过MediaRecorderClient转向MediaPlayerService。
Frameworks/av/media/libmediaplayerservice/ MediaPlayerService.cpp
sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 &opPackageName){
pid_t pid = IPCThreadState::self()->getCallingPid();
sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);
wp<MediaRecorderClient> w = recorder;
Mutex::Autolock lock(mLock);
mMediaRecorderClients.add(w);
ALOGV("Create new media recorder client from pid %d", pid);
return recorder;
}
MediaRecorderClient的构造中,会创建实际的音视频录制实例StagefrightRecorder。
Frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp
MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid,
const String16& opPackageName){
mRecorder = AVMediaServiceFactory::get()->createStagefrightRecorder(opPackageName);
mMediaPlayerService = service;
}
rameworks/av/media/libavextension/mediaplayerservice/AVMediaServiceFactory.cpp
StagefrightRecorder *AVMediaServiceFactory::createStagefrightRecorder(
const String16 &opPackageName) {
return new StagefrightRecorder(opPackageName);
}
3,StagefrightRecorder的构造函数只是做了变量的初始化,很多功能的实现是在其prepare,start调用时开始的,主要功能包括:创建音视频编解码器,设置音视频源source,MediaWriter。
在Camera录像设置属性的函数调用,最终都会调用到StagefrightRecorder.cpp的相应方法。
在prepare()中会调用prepareInternal()来创建音视频编解码器。
Frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::prepareInternal() {
//根据输出文件的格式,创建相应的编解码器。
switch (mOutputFormat) {
case OUTPUT_FORMAT_DEFAULT:
case OUTPUT_FORMAT_THREE_GPP:
case OUTPUT_FORMAT_MPEG_4:
case OUTPUT_FORMAT_WEBM:
status = setupMPEG4orWEBMRecording();
break;
}
}
针对视频录制:
status_t StagefrightRecorder::setupMPEG4orWEBMRecording() {
sp<MediaWriter> writer;
sp<MPEG4Writer> mp4writer;
//根据输出文件类型,创建相应的mediaWriter,MediaWrite的作用是把视频track,音频track写入到chunks中,然后打包写入到文件容器中。
if (mOutputFormat == OUTPUT_FORMAT_WEBM) {
writer = new WebmWriter(mOutputFd);
} else {
//实际创建的是MPEG4Writer实例。其中的 mOutputFd是通过setOutputFile()设置的保存文件的描述符。
writer = mp4writer = AVFactory::get()->CreateMPEG4Writer(mOutputFd);
}
//创建视频资源,相应的视频编码器。
sp<MediaSource> mediaSource;
err = setupMediaSource(&mediaSource);
sp<MediaCodecSource> encoder;
err = setupVideoEncoder(mediaSource, &encoder);
writer->addSource(encoder);
mVideoEncoderSource = encoder;
}
针对音频的数据源source,音频编码器的创建跟视频是类似的。
在设置音视频source时,通过checkVideoEncoderCapabilities检查应用端设置的参数是否合法。或者设置默认的参数。
status_t StagefrightRecorder::checkVideoEncoderCapabilities() {
if (!mCaptureFpsEnable) {
// Dont clip for time lapse capture as encoder will have enough
// time to encode because of slow capture rate of time lapse.
clipVideoBitRate();
clipVideoFrameRate();
clipVideoFrameWidth();
clipVideoFrameHeight();
setDefaultProfileIfNecessary();
}
return OK;
}
4,创建视频编码器及数据源source。
创建视频数据源,视频数据通常来自camera,这个过程就是打开camera设备的过程。
StagefrightRecorder.cpp
status_t StagefrightRecorder::setupMediaSource(sp<MediaSource> *mediaSource) {
status_t err = setupCameraSource(&cameraSource);
*mediaSource = cameraSource;
}
status_t StagefrightRecorder::setupCameraSource(
sp<CameraSource> *cameraSource) {
//先做参数检查。
if ((err = checkVideoEncoderCapabilities()) != OK) { return err; }
//创建CameraSource实例。参数mCamera代表具体的Camera实例,录像过程中MediaRecorder不能直接访问到Camera,
具体可以通过 mCameraProxy(ICameraRecordingProxy.cpp)使用Camera,比如start/stop Recording,Release recording frames;
另外还有一个ICameraRecordingProxyListener接口,在Recording中具体的录像实例(stageFrightRecorder)将通过这个接口来接收视频帧,
具体就是其方法dataCallbackTimestamp。
*cameraSource = AVFactory::get()->CreateCameraSourceFromCamera(mCamera, mCameraProxy,
mCameraId, mClientName, mClientUid,
mClientPid,videoSize, mFrameRate,mPreviewSurface);
//获取Camera发送buffer的模式,实际也就是MetadataBufferType类型,如果是Metadata Mode或者buffer queue模式,
发送过来的output buffers中是包含元数据metadata的。
mMetaDataStoredInVideoBuffers = (*cameraSource)->metaDataStoredInVideoBuffers();
}
详细看下上面函数中两个关键点,一个是camerasource的使用,一个是metadatabuffers type。
(1),整理下用Camera录制的过程:
录像机应用打开Camera开始预览;
应用通过MediaRecorder::setCamera()传递ICamera,ICameraRecordingProxy到MediaRecorder中;
然后MediaRecorder在其start()中,使用ICamera来setup相机,setup之后,具体的录像实例(stagefrightRecorder)就跟cameraService断开连接了;
接着,stagefrightRecorder调用ICameraRecordingProxy::startRecording(),同时传递ICameraRecordingProxyListener给应用,应用重新连接回cameraservice,开始recording;
这时候应用拥有属于自己的camera,同一个时刻cameraservice只允许有一个client,因为每个录像实例都可能用它拥有的camera来做些事情,比如变焦,所以mediarecorder是不能直接访问到camera的,通常都是通过ICameraRecordingProxy方式;
mediarecorder通过ICameraRecordingProxyListener接收视频帧,通过ICameraRecordingProxy::releaseRecordingFrame释放视频帧,通过ICameraRecordingProxy::stopRecording()停止录像。
调用顺序如下:
* The call sequences are as follows:
* 1. The app: Camera.unlock().
* 2. The app: MediaRecorder.setCamera().
* 3. Start recording
* (1) The app: MediaRecorder.start().
* (2) The recorder: ICamera.unlock() and ICamera.disconnect().
* (3) The recorder: ICameraRecordingProxy.startRecording().
* (4) The app: ICamera.reconnect().
* (5) The app: ICamera.startRecording().
* 4. During recording
* (1) The recorder: receive frames from ICameraRecordingProxyListener.dataCallbackTimestamp()
* (2) The recorder: release frames by ICameraRecordingProxy.releaseRecordingFrame().
* 5. Stop recording
* (1) The app: MediaRecorder.stop()
* (2) The recorder: ICameraRecordingProxy.stopRecording().
* (3) The app: ICamera.stopRecording().
(2),MetadataBufferType 定义了通过stagefright媒体录制框架,可以传递给视频编码组件进行编码的metadatabuffers的类型。metadatabuffers在媒体框架层是怎么工作的,可以参考frameworks/native/include/media/hardware/HardwareAPI.h。
元数据缓冲区的创建者和视频编码器共享存储在这些缓冲区中的数据,以及怎么样利用信息来定位到实际的像素数据作为视频编码的源输入,还有其他的信息是不是必须的,这是视频编码组件来做的事情。stagefright录制框架不需要知道这些元数据缓冲区的具体信息,它只去接收每个单独的medadatabuffer做为元输入,然后复制一份medadatabuffer,通过openMaxapi传给视频编码组件。
元数据缓冲区的创建者必须确保每一个metadaabuffer的前4bytes指定了它的buffertype,其余部分包含实际的元数据信息。视频编码组件接收到一个metadatabuffer时,将根据buffer的前4bytes来找出metadatabuffer的类型,然后执行适合这个buffer类型的操作。
接着看视频数据源source,这里的MediaSource就是接下来创建的camerasource实例,直接看camerasource。
创建camerasource后,直接调用了其init方法,初始化camerasource,以便提供视频输入流。
Frameworks/av/media/libstagefright/CameraSource.cpp
status_t CameraSource::init(
const sp<hardware::ICamera>& camera,
const sp<ICameraRecordingProxy>& proxy,
int32_t cameraId,
const String16& clientName,
uid_t clientUid,
pid_t clientPid,
Size videoSize,
int32_t frameRate,
bool storeMetaDataInVideoBuffers) {
err = initWithCameraAccess(camera, proxy, cameraId, clientName, clientUid, clientPid,
videoSize, frameRate,
storeMetaDataInVideoBuffers);
}
status_t CameraSource::initWithCameraAccess(
const sp<hardware::ICamera>& camera,
const sp<ICameraRecordingProxy>& proxy,
int32_t cameraId, const String16& clientName,
uid_t clientUid, pid_t clientPid, Size videoSize, int32_t frameRate,
bool storeMetaDataInVideoBuffers) {
//这里将打开caemra,连接到camera设备。
if ((err = isCameraAvailable(camera, proxy, cameraId,
clientName, clientUid, clientPid)) != OK) { return err; }
//使用要求的视频帧大小,帧率来设置camera。
if ((err = configureCamera(¶ms, videoSize.width, videoSize.height,
frameRate))) { return err; }
//检查大小,帧率是否配置成功。
if ((err = checkVideoSize(newCameraParams,
videoSize.width, videoSize.height)) != OK) { return err; }
if ((err = checkFrameRate(newCameraParams, frameRate)) != OK) { return err; }
//camera传递数据到video buffers的模式, mVideoBufferMode这个值也是stagefrightRecorder中metadata buffer type的来源。
storeMetaDataInVideoBuffers是默认参数并且值是true,这表示默认video buffers中是存储实际的数据的。不同的video buffer mode,
实际存储到video buffers中的数据会不一样,使用的接口也有区别,具体可以参考frameworks/av/include/camera/android/hardware/ICamera.h。
mVideoBufferMode = hardware::Icamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV;
if (storeMetaDataInVideoBuffers) {
if (OK == mCamera->setVideoBufferMode(
hardware::ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE)) {
mVideoBufferMode = hardware::ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE;
} else if (OK == mCamera->setVideoBufferMode(
hardware::ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA)) {
mVideoBufferMode
=hardware::Icamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA;}
}
}
分步看下怎么连接到camera设备的,以及camera的配置。
Frameworks/av/media/libstagefright/CameraSource.cpp
status_t CameraSource::isCameraAvailable(
const sp<hardware::ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
int32_t cameraId, const String16& clientName, uid_t clientUid, pid_t clientPid) {
//因为调用了setCamera,所以这里的camera不为0,会走下面的else部分,并且 proxy也不为null,这个proxy指的是RecordingProxy,
它是Camera.cpp的内部类。其实java层的setCamera()是没有携带proxy参数的,RecordingProxy是在setCamera的jni实现中添加的。
接下来的操作是创建一个camera客户端,执行connect方法。
if (camera == 0) {
mCamera = Camera::connect(cameraId, clientName, clientUid, clientPid);
if (mCamera == 0) return -EBUSY;
mCameraFlags &= ~FLAGS_HOT_CAMERA;
} else {
//从现有的远程camera构造一个camera client,也即是mCamera ,作为这个远端Camera的代理,类型是Camera.cpp,不是ICamera。
这个代理mCamera 是提供给应用的,是一个本地的Camera对象。
mCamera = Camera::create(camera);
if (mCamera == 0) return -EBUSY;
//把应用端的Recording proxy记录到 mCameraRecordingProxy。
mCameraRecordingProxy = proxy;
mCameraFlags |= FLAGS_HOT_CAMERA;
}
//camera实例同时只能被一个进程访问,所以这里将其lock,但是Since API level 14,camera是在mediarecorder.start时,自动锁定的,
实际在recording开始和停止时,是不需要调用lock,unlock的,这是api的注释。
mCamera->lock();
}
camera实例的创建
构造sp<Camera>c = new Camera(-1);同时调用其父类CameraBase的构造函数。
接着是if(camera->connect(c) == NO_ERROR){...}连接Camera设备,这里的camera是Icamera类型的实例,这个camera实例的来源是在android_media_MediaRecorder.cpp中的android_media_MediaRecorder_setCamera函数中,也就是c→remote()的返回值,其中c是Camera.cpp的指针变量,remote()方法实际Camera.cpp的父类CameraBase.cpp中的方法。connect()方法,实际调用的CameraService::Client中的connect方法,这个CameraService::Client继承了BnCamera,间接继承了ICamera接口。根据camera设备api的版本,CameraService::Client有不同的实现类,以CAMERA_DEVICE_API_VERSION_3_0以上的版本为例,实际创建的Camera2Client对象。所以前面的connect()方法最终会调用Camera2Client.cpp中的connect()方法。
Camera2Client.cpp
status_t Camera2Client::connect(const sp<hardware::ICameraClient>& client) {
//连接的实际作用是把 client记录在 mRemoteCallback, mSharedCameraCallbacks变量中,以备后面回调使用。
这里的参数client也就是CameraSource.cpp中的变量sp<Camera> mCamera;Camera继承了BnCameraClient,也就间接继承了ICameraClient类。
mRemoteCallback = client;
mSharedCameraCallbacks = client;
}
从ICameraClient类中的函数可以看出,这个实例对象可以用于数据回调,通知回调。
IcameraClient.h
class ICameraClient: public android::IInterface {
virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
virtual void dataCallback(int32_t msgType, const sp<IMemory>& data,
virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& data) = 0;
…...
};
到这里,做个小结,针对CameraSource.cpp中跟camera有关的实例:
一,sp<Camera> mCamera;代表了一个打开的camera实例,在视频录制这个场景中,它的主要作用是lock,unlock一个camera设备,参数设置setParameters,setVideoBufferMode,setPreviewTarget等,以及通过sendCommand()发送命令给cameradriver。
二,sp<ICameraRecordingProxy> mCameraRecordingProxy;这个实例实际的实现端是Camera.cpp的内部类Camera::RecordingProxy,在调用setCamera()的过程实例化Camera::RecordingProxy对象,一路传递到CameraSource.cpp中,在mCameraFlags被设置为FLAGS_HOT_CAMERA时,将使用mCameraRecordingProxy开始录像,停止录像,释放视频帧等。如果没有设置为FLAGS_HOT_CAMERA,mCameraFlags默认值是FLAGS_SET_CAMERA,这种场景下使用 mCamera实例来做mCameraRecordingProxy做的那些事情。
三,监听类,ICameraRecordingProxyListener.cpp,具体实现类是CameraSource的内部类CameraSource::ProxyListener,在开始录像时,通过
mCameraRecordingProxy->startRecording(newProxyListener(this))传递ProxyListener实例,用于接收视频buffers,如果是用mCamera->startRecording();开始录像,需要先设置相应的listener,mCamera->setListener(newCameraSourceListener(this))。
续:https://blog.youkuaiyun.com/lin20044140410/article/details/80058141