视音频录制流程

音视频录制状态机: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))。
 

视音频录制流程(二)

前面的过程漏了一点,就是camera设备的打开过程,也就是mCamera=Camera.open(CAMERA_ID),分析的recorder.setCamera(mCamera);中的参数就是open的返回值。open()的过程还是很长的,下面只关注跟cameraService,ICameraClient相关一小部分,以便跟前面的connect中的描述可以串起来。

连接camera设备的过程,是先获取cameraservice的句柄,然后执行cameraservice的connect方法。这个过程会创建一个cameraclient实例CameraDeviceClient,并作为出参回传给调用者,也就是device指向指针的指针。

Frameworks/av/service/camera/libcameraservice/cameraservice.cpp
Status CameraService::connect(
        const sp<ICameraClient>& cameraClient,
        int cameraId,
        const String16& clientPackageName,
        int clientUid,
        int clientPid,
        /*out*/
        sp<ICamera>* device) {
    sp<Client> client = nullptr;
    ret = connectHelper<ICameraClient,Client>(cameraClient, id,
            CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, clientPid, API_1,
            /*legacyMode*/ false, /*shimUpdateOnly*/ false,
            /*out*/client);
    *device = client;
}
cameraclient的创建会根据hal版本和api版本,来确定具体的实例。

CameraService.cpp
Status CameraService::makeClient(const sp<CameraService>& cameraService,
        const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
        int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
        int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
        /*out*/sp<BasicClient>* client) {
//默认配置下创建的是 CameraClient
*client = new  CameraClient(cameraService, tmp, packageName, cameraIdToInt(cameraId),
                        facing, clientPid, clientUid, getpid(), legacyMode);
//对于hal 3,api1,创建的 Camera2Client
*client = new Camera2Client(cameraService, tmp, packageName, cameraIdToInt(cameraId),
                        facing, clientPid, clientUid, servicePid, legacyMode);
//对于hal 3,api2,创建的 CameraDeviceClient
*client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
                        facing, clientPid, clientUid, servicePid);
}
 

接着执行cameraclient的初始化,这个过程中会打开camera硬件设备。


 

设置camerasource的最后一步,camera的配置。

status_t CameraSource::configureCamera(
        CameraParameters* params,        int32_t width, int32_t height,        int32_t frameRate) {
//这个过程,主要是用要求的视频大小,帧率来配置camera,如果没有请求视频宽高,帧率,将跳过,使用camera setting中的默认值。
    if (width != -1 && height != -1) {…}
    if (frameRate != -1) {….}
}
这段分析都是从prepare()开始,到这里视频source已经准备好了。


 

5,继续prepare(),看下视频编码器的创建。

StagefrightRecorder.cpp
status_t StagefrightRecorder::setupVideoEncoder(
        const sp<MediaSource> &cameraSource,
        sp<MediaCodecSource> *source) {
//根据视频源camerasource,创建视频编码器。
sp<MediaCodecSource> encoder = MediaCodecSource::Create(
            mLooper, format, cameraSource, mPersistentSurface, flags);
//保存bufferproducer,用于buffer的申请。
    if (cameraSource == NULL) {
        mGraphicBufferProducer = encoder->getGraphicBufferProducer();
    }
}
 

MediaCodecSource::Create()创建一个MediaCodecSource实例,给一些变量赋值,然后初始化init()-->initEncoder()。

MediaCodecSource.cpp
status_t MediaCodecSource::initEncoder() {
    Vector<AString> matchingCodecs;
//根据设置的mime类型(MEDIA_MIMETYPE_VIDEO_MPEG4或者MEDIA_MIMETYPE_VIDEO_AVC),查找匹配的编码器。
    MediaCodecList::findMatchingCodecs(
        outputMIME.c_str(), true /* encoder */,
    ((mFlags & FLAG_PREFER_SOFTWARE_CODEC) ? MediaCodecList::kPreferSoftwareCodecs : 0),
        &matchingCodecs);
//创建解码组件,执行其init(),configure()。
    mEncoder = MediaCodec::CreateByComponentName(mCodecLooper, matchingCodecs[ix]);
//kWhatEncoderActivity 消息的处理,通过feedEncoderInputBuffers填充编码输入buffer。
    mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
    mEncoder→setCallback(mEncoderActivityNotify);
//注意configure()的最后一个参数 MediaCodec::CONFIGURE_FLAG_ENCODE表示接下来要创建的是编码组件。
    err = mEncoder->configure(  mOutputFormat,   NULL /* nativeWindow */,
                    NULL /* crypto */,   MediaCodec::CONFIGURE_FLAG_ENCODE);
//start(),表示编码组件初始化已经成功完成,可以准备编码工作了,也就是将其OMX_CommandStateSet状态置为OMX_StateIdle。
最终调用的是OMXNodeInstance.cpp中方法sendCommand()。
    err = mEncoder->start();
}
//这个方法会列出所有匹配mime格式的编码器,即是其中的参数matches,同时也会考虑优先软解,还是强制硬解。

void MediaCodecList::findMatchingCodecs(
        const char *mime, bool encoder, uint32_t flags,
        Vector<AString> *matches, Vector<AString> *owners) {
    const sp<IMediaCodecList> list = getInstance();
    const sp<MediaCodecInfo> info = list→getCodecInfo(matchIndex);
    AString componentName = info→getCodecName();
    matches->push(componentName);
}
MediaCodec.cpp
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
//这里返回的实际是ACodec实例。
    mCodec = GetCodecBase(name, nameIsType);
    mCodec->setCallback(
            std::unique_ptr<CodecBase::CodecCallback>(
                    new CodecCallback(new AMessage(kWhatCodecNotify, this))));
    mBufferChannel = mCodec->getBufferChannel();
    mBufferChannel->setCallback(
            std::unique_ptr<CodecBase::BufferCallback>(
                    new BufferCallback(new AMessage(kWhatCodecNotify, this))));
//在 kWhatInit消息的处理中,分配编解码组件。具体调用是mCodec->initiateAllocateComponent(format);
    sp<AMessage> msg = new AMessage(kWhatInit, this);
}
 

MediaCodec.cpp
status_t MediaCodec::configure(        const sp<AMessage> &format,        const sp<Surface> &surface,            
const sp<ICrypto> &crypto,        const sp<IDescrambler> &descrambler,        uint32_t flags) {
//配置,启动编码组件,具体调用mCodec->initiateConfigureComponent(format);
    sp<AMessage> msg = new AMessage(kWhatConfigure, this);
}
 

Acodec.cpp
//这个函数,编解码组件配置都会执行,比较长,
status_t ACodec::configureCodec(        const char *mime, const sp<AMessage> &msg) {
    if (video) {
//确定是否使用软件渲染,是解码需要的。
        bool usingSwRenderer = false;
        if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) {
            usingSwRenderer = true;
            (void)setPortMode(kPortIndexOutput, IOMX::kPortModePresetByteBuffer);
        }else if (haveNativeWindow && !storingMetadataInDecodedBuffers()) {
            err = setPortMode(kPortIndexOutput, IOMX::kPortModePresetANWBuffer);
        }
//创建编码或者解码器。
        if (encoder) {
            err = setupVideoEncoder(mime, msg, outputFormat, inputFormat);
        }else {
                    err = setupVideoDecoder(mime, msg, haveNativeWindow, usingSwRenderer, outputFormat);
            }
    }
…...
}
 

再回到StagefrightRecorder.cpp中,前面编码组件的构建是从setupVideoEncoder()开始的,

StagefrightRecorder.cpp
status_t StagefrightRecorder::setupMPEG4orWEBMRecording() {
    sp<MediaWriter> writer;
        sp<MediaSource> mediaSource;
        err = setupMediaSource(&mediaSource);
        sp<MediaCodecSource> encoder;
        err = setupVideoEncoder(mediaSource, &encoder);
//前面设置视频source(camerasource),设置编码器都执行完了,接着是创建视频track,writer是Mpeg4Writer类型实例,
Mpeg4Writer的作用就是分别把视频track和音频track写入到chunck中,然后打包写入到文件容器。
    writer->addSource(encoder);
}
MPEG4Writer.cpp
status_t MPEG4Writer::addSource(const sp<IMediaSource> &source) {
    const char *mime;
    source->getFormat()->findCString(kKeyMIMEType, &mime);
    bool isAudio = !strncasecmp(mime, "audio/", 6);
//source是视频encorder,所以创建的视频track,如果是setupAudioEncoder()中调用addSource,传入的source是音频encorder,创建的就是音频track。
    Track *track = new Track(this, source, 1 + mTracks.size());
    mTracks.push_back(track);
}
 

prepare()执行完,接下来就是start()方法的调用。

StagefrightRecorder.cpp
status_t StagefrightRecorder::start() {
    switch (mOutputFormat) {
                case OUTPUT_FORMAT_DEFAULT:
            case OUTPUT_FORMAT_THREE_GPP:
            case OUTPUT_FORMAT_MPEG_4:
            case OUTPUT_FORMAT_WEBM:{
            sp<MetaData> meta = new MetaData;
            setupMPEG4orWEBMMetaData(&meta);
//调用mepg4writer的start()方法。
            status = mWriter->start(meta.get());
        }
    }
}
 

对文件的读写,前提是先了解这个文件格式。

mpeg4文件格式解析,参考:https://blog.csdn.net/chenchong_219/article/details/44263691

MPEG4Writer.cpp
status_t MPEG4Writer::start(MetaData *param) {
//先写入文件格式。
    writeFtypBox(param);
//为这个视频track创建一个线程,并从ThreadWrapper()函数开始运行,线程实际执行的是MPEG4Writer::threadFunc()函数,执行mp4文件的chunks的写入。
     status_t err = startWriterThread();
//将会调用前面创建的视频track中的start()方法。
    err = startTracks(param);
}
 

重点看下Mpeg4Writer::Track的start()方法,媒体元数据的输入输出都在这里完成。

MPEG4Writer.cpp
status_t MPEG4Writer::Track::start(MetaData *params) {
    sp<MetaData> meta = new MetaData;
//mSource是前面StagefrightRecorder::setupVideoEncoder()的返回值MediaCodecSource实例。
    status_t err = mSource->start(meta.get());
    pthread_create(&mThread, &attr, ThreadWrapper, this);
}
 

从MediaCodecSource.cpp的start()方法,间接通过,MediaCodecSource::Puller的start(),调用了CameraSource.cpp的start()方法。
CameraSource.cpp
status_t CameraSource::start(MetaData *meta) {
    if ((err = startCameraRecording()) == OK) {
        mStarted = true;
    }
}
CameraSource.cpp
status_t CameraSource::startCameraRecording() {
//创建存储的buffers的内存块,作为本地视频元数据。
    createVideoBufferMemoryHeap(sizeof(VideoNativeHandleMetadata), kDefaultVideoBufferCount);
//启动camera录制,同时设置监听ProxyListener ,通过其回调dataCallbackTimestamp获取元数据。
    if ((err = mCameraRecordingProxy->startRecording(new ProxyListener(this))) != OK) {...}
}
 

void CameraSource::dataCallbackTimestamp(int64_t timestampUs,
        int32_t msgType __unused, const sp<IMemory> &data) {
//camera返回的数据放在列表 mFramesReceived,然后通过mFrameAvailableCondition 唤醒一个线程。
    mFramesReceived.push_back(data);
    mFrameTimes.push_back(timeUs);
    mFrameAvailableCondition.signal();
}
 

mFrameAvailableCondition.signal();唤醒了那个线程呢?前面在MPEG4Writer::Track::start()创建了线程ThreadWrapper,这个线程运行后,通过mSource->read(&buffer)来读取camerasource中的数据,当列表无数据可读时,这个线程就会在read时进入wait,然后等camera返回的数据填充到列表mFramesReceived时,在唤醒,继续读取。这样camera返回的数据就到了MPEG4Writer::Track中。

ThreadWrapper直接调用了threadEntry。

status_t MPEG4Writer::Track::threadEntry() {
//循环执行,通过camerasource的read拿到元数据。
    while (!mDone && (err = mSource->read(&buffer)) == OK) {
//执行编码。
        f (mIsMPEG4) {                    copyCodecSpecificData((const uint8_t *)buffer->data() +
             buffer->range_offset(),                            buffer->range_length());
        }
//中间略去了很多代码,这里是根据timestampUs ,mChunkSamples 生成chunk。 先是调用MPEG4Writer::Track的bufferChunk()方法,
生成一个chunk后,再调用MPEG4Writer的bufferChunk()方法,同时把生成的chunk作为参数传递到MPEG4Writer中,
同时在MPEG4Writer::bufferChunk()中通过mChunkReadyCondition.signal()唤醒了另一个处于wait的线程。
        mChunkSamples.push_back(copy);
        bufferChunk(timestampUs);
    }
}
mChunkReadyCondition.signal()唤醒的是MPEG4Writer中线程ThreadWrapper,也就是MPEG4Writer中的threadFunc()方法。

void MPEG4Writer::threadFunc() {
    while (!mDone) {
//循环执行,读取MPEG4Writer::Track的bufferChunk()方法生成的chunk,
        while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
            mChunkReadyCondition.wait(mLock);
        }
//将chunk写入文件容器。
        writeChunkToFile(&chunk);
        writeAllChunks();
    }
}
到这里完成了视音频录制的。音频的处理类似。
MediaWriter是把音视频字幕track打包到容器中,播放时的逆过程是通过MediaExtractor来提取分离出容器中的音视频track。
--------------------- 
作者:lin-0410 
来源:CSDN 
原文:https://blog.csdn.net/lin20044140410/article/details/80058141 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/xuhui_7810/article/details/89497175