1. 程式人生 > >Android Camera fw學習(四)-recording流程分析

Android Camera fw學習(四)-recording流程分析

應用開發 facet internal server 中一 sca med erase 流程分析

Android Camera fw學習(四)-recording流程分析
備註:備註:本文是Android5.1學習筆記。博文按照軟件啟動流程分析。
  且行且惜,一步一個腳印,這次學習camera Video.雖然標題是recording流程分析,但這裏很多和preview是相似的(包含更新,創建Stream,創建Request),這裏主要分析MediaRecorder對象創建、video幀監聽對象註冊、幀可用事件以及一系列callback流程分析。

一、認識video(mediaRecorder)狀態機

技術分享圖片

Used to record audio and video. The recording control is based on a
simple state machine (see below).狀態機請看上面源碼中給的流程圖。
A common case of using MediaRecorder to record audio works as follows:
1.MediaRecorder recorder = new MediaRecorder();
2.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
3.recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
4.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
5.recorder.setOutputFile(PATH_NAME);
6.recorder.prepare();
7.recorder.start(); // Recording is now started
8….
9.recorder.stop();
10.recorder.reset(); // You can reuse the object by going back to setAudioSource() step
recorder.release(); // Now the object cannot be reused
??Applications may want to register for informational and error
events in order to be informed of some internal update and possible
runtime errors during recording. Registration for such events is
done by setting the appropriate listeners (via calls
(to {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener and/or
{@link #setOnErrorListener(OnErrorListener)}setOnErrorListener).
In order to receive the respective callback associated with these listeners,
applications are required to create MediaRecorder objects on threads with a
Looper running (the main UI thread by default already has a Looper running).

上面是googole工程師加的註釋,最權威的資料。大概意思就是說“使用mediaRecorder記錄音視頻,需要一個簡單的狀態機來控制”。上面的1,2,3…就是在操作時需要準守的步驟。算了吧,翻譯水平有限,重點還是放到camera這邊吧。

二、Camera app如何啟動錄像

//源碼路徑:pdk/apps/TestingCamera/src/com/android/testingcamera/TestingCamera.java
 private void startRecording() {
        log("Starting recording");
        logIndent(1);
        log("Configuring MediaRecoder");
        //這裏會檢查是否打開了錄像功能。這裏我們省略了,直接不如正題
//上面首先創建了一個MediaRecorder的java對象(註意這裏同camera.java類似,java對象中肯定包含了一個mediaRecorder jni本地對象,繼續往下看)
        mRecorder = new MediaRecorder();
        //下面就是設置一些callback.
        mRecorder.setOnErrorListener(mRecordingErrorListener);
        mRecorder.setOnInfoListener(mRecordingInfoListener);
        if (!mRecordHandoffCheckBox.isChecked()) {
    //將當前camera java對象設置給了mediaRecorder java對象。
    //這裏setCamera是jni接口,後面我們貼代碼在分析。
            mRecorder.setCamera(mCamera);
        }
    //將preview surface java對象設置給mediaRecorder java對象,後面貼代碼
    //詳細說明。
        mRecorder.setPreviewDisplay(mPreviewHolder.getSurface());
    //下面2個是設置音頻和視頻的資源。
        mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mRecorder.setProfile(mCamcorderProfiles.get(mCamcorderProfile));
        //從app控件選擇錄像幀大小,並設置給mediaRecorder
        Camera.Size videoRecordSize = mVideoRecordSizes.get(mVideoRecordSize);
        if (videoRecordSize.width > 0 && videoRecordSize.height > 0) {
            mRecorder.setVideoSize(videoRecordSize.width, videoRecordSize.height);
        }
        //從app控件選擇錄像幀率,並設置給mediaRecorder.
        if (mVideoFrameRates.get(mVideoFrameRate) > 0) {
            mRecorder.setVideoFrameRate(mVideoFrameRates.get(mVideoFrameRate));
        }
        File outputFile = getOutputMediaFile(MEDIA_TYPE_VIDEO);
        log("File name:" + outputFile.toString());
        mRecorder.setOutputFile(outputFile.toString());

        boolean ready = false;
        log("Preparing MediaRecorder");
        try {
    //準備一下,請看下面google給的使用mediaRecorder標準流程
            mRecorder.prepare();
            ready = true;
        } catch (Exception e) {//------異常處理省略
        }

        if (ready) {
            try {
                log("Starting MediaRecorder");
                mRecorder.start();//啟動錄像
                mState = CAMERA_RECORD;
                log("Recording active");
                mRecordingFile = outputFile;
            } catch (Exception e) {//-----異常處理省略
        }
//------------
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

可以看到應用啟動錄像功能是是符合狀態機流程的。在應用開發中,也要這樣來做。

  • 1.創建mediaRecorderjava對象,mRecorder = new MediaRecorder();
  • 2.設置camera java對象到mediaRecorder中,mRecorder.setCamera(mCamera);
  • 3.將preview surface對象設置給mediaRecorder,mRecorder.setPreviewDisplay(mPreviewHolder.getSurface());
  • 4.設置音頻源,mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
  • 5.設置視頻源,mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
  • 6.設置錄像幀大小和幀率,以及setOutputFile
  • 8.準備工作,mRecorder.prepare();
  • 9.啟動mdiaRecorder,mRecorder.start();

三、與MediaPlayerService相關的類接口之間的關系簡介

1.mediaRecorder何時與MediaPlayerService發送關系

技術分享圖片

MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL)
{
    ALOGV("constructor");
    const sp<IMediaPlayerService>& service(getMediaPlayerService());
    if (service != NULL) {
        mMediaRecorder = service->createMediaRecorder();
    }
    if (mMediaRecorder != NULL) {
        mCurrentState = MEDIA_RECORDER_IDLE;
    }
    doCleanUp();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  在jni中創建mediaRecorder對象時,其實在構造函數中偷偷的鏈接了mediaPlayerService,這也是Android習慣用的方法。獲取到MediaPlayerService代理對象後,通過匿名binder獲取mediaRecorder代理對象。
frameworks/base/media/java/android/media/MediaRecorder.java

2.mediaPlayerService類和接口之間關系

技術分享圖片

接口簡單介紹-都是通過mediaPlayerService代理對象獲取匿名mediaRecorder和mediaPlayer

接口類型接口說明
virtual sp createMediaRecorder() = 0; 創建mediaRecorder錄視頻服務對象的接口
virtual sp create(const sp& client, int audioSessionId = 0) = 0; 創建mediaPlayer播放音樂服務對象的接口,播放音樂都是通過mediaPlayer對象播放的
virtual status_t decode() = 0; 音頻解碼器

3.MediaRecorder類和接口之間關系

技術分享圖片

mediaRecorder功能就是來錄像的。其中MediaRecorder類中,包含了BpMediaRecorder代理對象引用。MediaRecorderClient本地對象駐留在mediaPlayService中。它的接口比較多,這裏就列出我們今天關註的幾個接口。其它接口查看源碼吧
詳細介紹可以參考源碼:frameworks/av/include/media/IMediaRecorder.h

接口類型接口說明
virtual status_t setCamera(const sp& camera,const sp& proxy) = 0; 這個接口也是非常需要我們關註的,這裏獲取到了啟動錄像操作的本地對象(BnCameraRecordingProxy),並通過匿名binder通信方式,第二個參數就是本地對象.然後在startRecording時將幀監聽對象註冊到camera本地對象中了
virtual status_t setPreviewSurface(const sp& surface) = 0; 將preview預覽surface對象設置給medaiRecorder,因為mediaRecorder也有一個camera本地client,所以這個surface對象最終還是會設置到cameraService用於顯示。而錄像的幀會在CameraService本地創建一個bufferQueue,具體下面會詳細說明
virtual status_t setListener(const sp& listener) = 0; 這裏一看就是設置監聽對象,監聽對象是jni中的JNIMediaRecorderListener對象,該對象可以回調MediaRecorder.java類中的postEventFromNative方法,將時間送到java層。其實MediaRecorder實現了BnMediaRecorderClient接口,即實現notify接口,那麽這裏其實將本地對象傳到MediaRecorder本地的客戶端對象中(本地對象拿到的就是代理對象了),參考代碼片段1
virtual status_t start() = 0; 啟動錄像功能,函數追究下去和Camera關系不大了,這裏就不細說了
1)代碼片段1
源碼路徑:frameworks/base/media/jni/android_media_MediaRecorder.cpp
// create new listener and give it to MediaRecorder
sp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this);
mr->setListener(listener);
  • 1
  • 2
  • 3
  • 4

mediaRecorder jni接口回調java方法,通知上層native事件。

2)代碼片段2
static void android_media_MediaRecorder_setCamera(JNIEnv* env, jobject thiz, jobject camera)
{
// we should not pass a null camera to get_native_camera() call.
//這裏檢查camera是不是空的,顯然不是空的。
    //這個地方需要好好研究一下,其中camera是java層的camera對象(即camera.java)
    //這裏由java對象獲取到camera應用端本地對象。
    sp<Camera> c = get_native_camera(env, camera, NULL);
    if (c == NULL) {
    // get_native_camera will throw an exception in this case
        return;
    }
    //獲取mediaRecorder本地對象
    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
    //下面要特別註意,這裏為什麽傳入的不是Camera對象而是c->remote(),當時琢磨
    //著,camera.cpp也沒實現什麽代理類的接口啊,不過後來在cameraBase類中發現
    //重載了remote()方法,該方法返回ICamera代理對象,呵呵。這樣的話就會在
    //mediaRecorder中創建一個新的ICamera代理對象。並在mediaPlayerService中
    //創建了一個本地的Camera對象。
    //c->getRecordingProxy():獲取camera本地對象實現的Recording本地對象。這裏
    //調用setCamera設置到mediaRecorder本地對象中了(見代碼片段3)
   process_media_recorder_call(env, mr->setCamera(c->remote(), c->getRecordingProxy()),
            "java/lang/RuntimeException", "setCamera failed.");
}
//camera端
sp<ICameraRecordingProxy> Camera::getRecordingProxy() {
    ALOGV("getProxy");
    return new RecordingProxy(this);
}
//看看下面RecordingProxy實現了BnCameraRecordingProxy接口,
//是個本地對象,水落石出了。
class RecordingProxy : public BnCameraRecordingProxy
    {
    public:
        RecordingProxy(const sp<Camera>& camera);

        // ICameraRecordingProxy interface
        virtual status_t startRecording(const sp<ICameraRecordingProxyListener>& listener);
        virtual void stopRecording();
        virtual void releaseRecordingFrame(const sp<IMemory>& mem);
    private:
    //這裏的是mCamera已經不再是之前preview啟動時對應的那個本地Camera對象
    //這是mediaRecorder重新創建的camera本地對象。
        sp<Camera>         mCamera;
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
3)代碼片段3-setCamera本地實現
status_t MediaRecorderClient::setCamera(const sp<ICamera>& camera,
                                        const sp<ICameraRecordingProxy>& proxy)
{
    ALOGV("setCamera");
    Mutex::Autolock lock(mLock);
    if (mRecorder == NULL) {
        ALOGE("recorder is not initialized");
        return NO_INIT;
    }
    return mRecorder->setCamera(camera, proxy);
}
//構造函數中可以看到創建了一個StagefrightRecorder對象,後續的其它操作
//都是通過mRecorder對象實現的
MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid)
{
    ALOGV("Client constructor");
    mPid = pid;
    mRecorder = new StagefrightRecorder;
    mMediaPlayerService = service;
}
//StagefrightRecorder::setCamera實現
struct StagefrightRecorder : public MediaRecorderBase {}
status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera,
                                        const sp<ICameraRecordingProxy> &proxy) {
//省去一些錯誤檢查代碼
    mCamera = camera;
    mCameraProxy = proxy;
    return OK;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

  最終ICamera,ICameraRecordingProxy代理對象都存放到StagefrightRecorder對應的成員變量中,看來豬腳就在這個類中。

4)代碼片段4
status_t CameraSource::isCameraAvailable(
    const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
    int32_t cameraId, const String16& clientName, uid_t clientUid) {

    if (camera == 0) {
        mCamera = Camera::connect(cameraId, clientName, clientUid);
        if (mCamera == 0) return -EBUSY;
        mCameraFlags &= ~FLAGS_HOT_CAMERA;
    } else {
        // We get the proxy from Camera, not ICamera. We need to get the proxy
        // to the remote Camera owned by the application. Here mCamera is a
        // local Camera object created by us. We cannot use the proxy from
        // mCamera here.
        //根據ICamera代理對象重新創建Camera本地對象
        mCamera = Camera::create(camera);
        if (mCamera == 0) return -EBUSY;
        mCameraRecordingProxy = proxy;
        //目前還不清楚是什麽標記,權且理解成支持熱插拔標記
        mCameraFlags |= FLAGS_HOT_CAMERA;
        //代理對象綁定死亡通知對象
        mDeathNotifier = new DeathNotifier();
        // isBinderAlive needs linkToDeath to work.
        mCameraRecordingProxy->asBinder()->linkToDeath(mDeathNotifier);
    }
    mCamera->lock();
    return OK;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

  由上面的類圖之間的關系的,就知道mediaRecorder間接包含了cameaSource對象,這裏為了簡單直接要害代碼。

  • 1.在創建CameraSource對象時,會去檢查一下Camera對象是否可用,可用的話就會根據傳進來的代理對象重新創建Camera本地對象(註意這個時候Camera代理對象在mediaRecorder中)
  • 2.然後保存RecordingProxy代理對象到mCameraRecordingProxy成員中,然後綁定死亡通知對象到RecordingProxy代理對象。
5)代碼片段5
status_t CameraSource::startCameraRecording() {
    ALOGV("startCameraRecording");
    // Reset the identity to the current thread because media server owns the
    // camera and recording is started by the applications. The applications
    // will connect to the camera in ICameraRecordingProxy::startRecording.
    int64_t token = IPCThreadState::self()->clearCallingIdentity();
    status_t err;
    if (mNumInputBuffers > 0) {
        err = mCamera->sendCommand(
            CAMERA_CMD_SET_VIDEO_BUFFER_COUNT, mNumInputBuffers, 0);
    }
    err = OK;
    if (mCameraFlags & FLAGS_HOT_CAMERA) {//前面已經置位FLAGS_HOT_CAMERA,成立
        mCamera->unlock();
        mCamera.clear();
        //通過recording代理對象,直接啟動camera本地端的recording
        if ((err = mCameraRecordingProxy->startRecording(
                new ProxyListener(this))) != OK) {
        }
    } else {
    }
    IPCThreadState::self()->restoreCallingIdentity(token);
    return err;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

  上面代碼需要我們註意的是在啟動startRecording()時,創建的監聽對象new ProxyListener(this),該監聽對象會傳到Camera本地對象中。當幀可用時,用來通知mediaRecorder有幀可以使用了,趕緊編碼吧。

6)代碼片段6-mediaRecorder註冊幀可用監聽對象
class ProxyListener: public BnCameraRecordingProxyListener {
    public:
        ProxyListener(const sp<CameraSource>& source);
        virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
                const sp<IMemory> &data);
    private:
        sp<CameraSource> mSource;
    };
//camera.cpp
status_t Camera::RecordingProxy::startRecording(const sp<ICameraRecordingProxyListener>& listener)
{
    ALOGV("RecordingProxy::startRecording");
    mCamera->setRecordingProxyListener(listener);
    mCamera->reconnect();
    return mCamera->startRecording();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

註冊幀監聽對象就是在啟動Recording時註冊,主要有下面幾步:

  • 1.使用setRecordingProxyListener接口,將監聽對象設置給mRecordingProxyListener 成員。
  • 2.重新和cameraService握手(preview停止時就會斷開鏈接,在切換瞬間就斷開了)
  • 3.使用ICamera代理對象啟動錄像。

四、階段小結

到這裏Camera如何使用medaiRecorder錄像的基本流程已經清楚了,這裏我畫了一個流程圖,大概包含下面9個流程。

技術分享圖片

  • 過程1:上層點擊了錄像功能,或者錄像preview模式下,會創建一個mediaRecorDer Java層對象。
  • 過程2:java層mediaRecorder對象調用native_jni native_setup方法,創建一個native的mediaRecorder對象。創建的過程中連接mediaPlayerService,並通過匿名binder通信方式獲取到一個mediaRecorderClient代理對象,並保存到mediaRecorder對象的成員變量mMediaRecorder中。
  • 過程3:ava層的Camera對象傳給mediaRecorder native層時,可以通過本地方法獲取到Camera本地對象和ICamera代理對象。這裏是獲取ICamera代理對象和RecordingProxy本地對象
  • 過程4:將ICamera代理對象和RecordingProxy本地對象傳給在MedaiService本地端的MediaRecorderClient對象,這時ICamera是重新創建的ICamer代理對象,以及獲取到RecordingProxy代理對象。
  • 過程5:根據過程4獲取到的新的ICamera代理對象和RecordingProxy代理對象,創建新的本地Camera對象Camera2,以及註冊錄像幀監聽對象到Camera2中。
  • 過程6:啟動StartRecording
  • 過程7:當錄像幀可用時,通知駐留在MedaiRecorderClient中的Camera2本地對象收幀,於此同時Camera2又是通過註冊的幀監聽對象告知MediaClientClient對象。MediaClientClient對象拿到幀後進行錄像編碼。
  • 過程8,過程9:通過回調函數,將一些消息發送給應用端。

五、Camera video創建BufferQueue.

status_t StreamingProcessor::updateRecordingStream(const Parameters &params) {
    ATRACE_CALL();
    status_t res;
    Mutex::Autolock m(mMutex);
    sp<CameraDeviceBase> device = mDevice.promote();
    //----------------
    bool newConsumer = false;
    if (mRecordingConsumer == 0) {
        ALOGV("%s: Camera %d: Creating recording consumer with %zu + 1 "
                "consumer-side buffers", __FUNCTION__, mId, mRecordingHeapCount);
        // Create CPU buffer queue endpoint. We need one more buffer here so that we can
        // always acquire and free a buffer when the heap is full; otherwise the consumer
        // will have buffers in flight we‘ll never clear out.
        sp<IGraphicBufferProducer> producer;
        sp<IGraphicBufferConsumer> consumer;
        //創建bufferQueue,同時獲取到生產者和消費者對象。
        BufferQueue::createBufferQueue(&producer, &consumer);
        //註意下面設置buffer的用處是GRALLOC_USAGE_HW_VIDEO_ENCODER,這個會在
        //mediaRecorder中使用到。
        mRecordingConsumer = new BufferItemConsumer(consumer,
                GRALLOC_USAGE_HW_VIDEO_ENCODER,
                mRecordingHeapCount + 1);
        mRecordingConsumer->setFrameAvailableListener(this);
        mRecordingConsumer->setName(String8("Camera2-RecordingConsumer"));
        mRecordingWindow = new Surface(producer);
        newConsumer = true;
        // Allocate memory later, since we don‘t know buffer size until receipt
    }
//更新部分代碼,就不貼出來了----
//註意下面video 錄像buffer的像素格式是CAMERA2_HAL_PIXEL_FORMAT_OPAQUE
    if (mRecordingStreamId == NO_STREAM) {
        mRecordingFrameCount = 0;
        res = device->createStream(mRecordingWindow,
                params.videoWidth, params.videoHeight,
                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mRecordingStreamId);
    }

    return OK;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

主要處理下面幾件事情。

  • 1.由於錄像不需要顯示,這裏創建CameraService BufferQueue本地對象,這個時候獲取到的生產者和消費者都是本地的,只有BufferQueue保存的有IGraphicBufferAlloc代理對象mAllocator,專門用來分配buffer。
  • 2.由於StremingProcess.cpp中實現了FrameAvailableListener監聽接口方法onFrameAvailable()。這裏會通過setFrameAvailableListener方法註冊到BufferQueue中。
  • 3.根據生產者對象創建surface對象,並傳給Camera3Device申請錄像buffer.
  • 4.如果參數有偏差或者之前已經創建過video Stream.這裏會刪除或者更新videoStream.如果壓根沒有創建VideoStream,直接創建VideoStream並根據參數更新流信息。

六、何時錄像幀可用

1.onFrameAvailable()

void StreamingProcessor::onFrameAvailable(const BufferItem& /*item*/) {
    ATRACE_CALL();
    Mutex::Autolock l(mMutex);
    if (!mRecordingFrameAvailable) {
        mRecordingFrameAvailable = true;
        mRecordingFrameAvailableSignal.signal();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

當video buffer進行enqueue操作後,該函數會被調用。函數中可用發現,激活了StreamingProcessor主線程。

2.StreamingProcessor線程loop

bool StreamingProcessor::threadLoop() {
    status_t res;
    {
        Mutex::Autolock l(mMutex);
        while (!mRecordingFrameAvailable) {
        //之前是在這裏掛起的,現在有幀可用就會從這裏喚醒。
            res = mRecordingFrameAvailableSignal.waitRelative(
                mMutex, kWaitDuration);
            if (res == TIMED_OUT) return true;
        }
        mRecordingFrameAvailable = false;
    }
    do {
        res = processRecordingFrame();//進一步處理。
    } while (res == OK);

    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  到這裏發現,原來StreamingProcessor主線程只為錄像服務,previewStream只是使用了它的幾個方法而已。

3.幀可用消息發送給Camera本地對象

status_t StreamingProcessor::processRecordingFrame() {
    ATRACE_CALL();
    status_t res;
    sp<Camera2Heap> recordingHeap;
    size_t heapIdx = 0;
    nsecs_t timestamp;
    sp<Camera2Client> client = mClient.promote();

    BufferItemConsumer::BufferItem imgBuffer;
    //取出buffer消費,就是拿給mediaRecorder編碼
    res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0);
    //----------------------------
    // Call outside locked parameters to allow re-entrancy from notification
    Camera2Client::SharedCameraCallbacks::Lock l(client->mSharedCameraCallbacks);
    if (l.mRemoteCallback != 0) {
    //調用Callback通知Camea本地對象。
        l.mRemoteCallback->dataCallbackTimestamp(timestamp,
                CAMERA_MSG_VIDEO_FRAME,
                recordingHeap->mBuffers[heapIdx]);
    } else {
        ALOGW("%s: Camera %d: Remote callback gone", __FUNCTION__, mId);
    }
    return OK;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

  之前我們已經知道Camera運行時存在類型為ICameraClient的兩個對象,其中一個代理對象保存在CameraService中,本地對象保存的Camera本地對象中。這裏代理對象通知本地對象取幀了。註意這裏消息發送的是“CAMERA_MSG_VIDEO_FRAME”。

4.Camera本地對象轉發消息給mediaRecorder.

void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr)
{
    // If recording proxy listener is registered, forward the frame and return.
    // The other listener (mListener) is ignored because the receiver needs to
    // call releaseRecordingFrame.
    sp<ICameraRecordingProxyListener> proxylistener;
    {
    //這裏mRecordingProxyListener就是mediaRecorder註冊過來的監聽代理對象
        Mutex::Autolock _l(mLock);
        proxylistener = mRecordingProxyListener;
    }
    if (proxylistener != NULL) {
    //這裏就把buffer送到了mediaRecorder中進行編碼
        proxylistener->dataCallbackTimestamp(timestamp, msgType, dataPtr);
        return;
    }
  //---------省略代碼
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

到這裏Camera本地對象就會調用mediaRecorder註冊來的幀監聽對象。前面我們已經做了那麽長的鋪墊,我想應該可以理解了。好了,mediaRecorder有飯吃了。

7.總結

  • 1.一開始我自以為preview和Video使用同一個camera本地對象,看了代碼發現,原來是不同的對象。
  • 2.預覽的BufferQueue是在CameraService中創建的,和surfaceFlinger沒有關系,只是保留了IGraphicBufferAlloc代理對象mAllocator,用於分配buffer.
  • 3.之匿名binder沒有理解透徹,以為只有傳遞本地對象才能使用writeStrongBinder()接口保存binder對象,同時在使用端使用readStrongBinder()就可以獲取到代理對象了。其實也可以傳遞代理對象,只不過代碼會走另外一套邏輯,在kernel中重新創建一個binder_ref索引對象返回給另一端。如下mediaRecorder設置camera時就是傳遞的ICamera代理對象。
    status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy)
    {
        ALOGV("setCamera(%p,%p)", camera.get(), proxy.get());
        Parcel data, reply;
        data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
        //camera->asBinder()是ICamera代理對象
        data.writeStrongBinder(camera->asBinder());
        data.writeStrongBinder(proxy->asBinder());
        remote()->transact(SET_CAMERA, data, &reply);
        return reply.readInt32();
    }

Android Camera fw學習(四)-recording流程分析