Android中多USB攝像頭解決方案——UVCCamera源碼分析(五)

本章我們來分析一下之前我們提過的相機(jī)采集的數(shù)據(jù)究竟是如何繪制到屏幕上的,這里需要幾個(gè)必要的知識(shí)點(diǎn):OpenGL、Android的SurfaceTexture、TextureView。網(wǎng)上可以搜到比較全面的有關(guān)于這些知識(shí)的文章,因此本文將不會(huì)花大篇幅介紹這些知識(shí)。

既然要將相機(jī)的采集,那么我們還是得從開啟預(yù)覽說起,之前文章(http://www.itdecent.cn/p/225734c143ba
)有詳細(xì)分析過startPreview的具體細(xì)節(jié),但是對(duì)startPreview這個(gè)方法的參數(shù)沒有做太多的分析,這里我們將從底層倒過來分析這個(gè)startPreview參數(shù)的來龍去脈。我們先看com.serenegiant.usb.UVCCamera的代碼片段:

/**
     * set preview surface with SurfaceHolder</br>
     * you can use SurfaceHolder came from SurfaceView/GLSurfaceView
     * @param holder
     */
    public synchronized void setPreviewDisplay(final SurfaceHolder holder) {
        nativeSetPreviewDisplay(mNativePtr, holder.getSurface());
    }

    /**
     * set preview surface with SurfaceTexture.
     * this method require API >= 14
     * @param texture
     */
    public synchronized void setPreviewTexture(final SurfaceTexture texture) {  // API >= 11
        final Surface surface = new Surface(texture);   // XXX API >= 14
        nativeSetPreviewDisplay(mNativePtr, surface);
    }

    /**
     * set preview surface with Surface
     * @param surface
     */
    public synchronized void setPreviewDisplay(final Surface surface) {
        nativeSetPreviewDisplay(mNativePtr, surface);
    }

直接說結(jié)論,相機(jī)相當(dāng)于一個(gè)生產(chǎn)者,它會(huì)不斷地產(chǎn)生圖像數(shù)據(jù),與其對(duì)應(yīng)的自然會(huì)需要有一個(gè)消費(fèi)者來接收相機(jī)送過來的數(shù)據(jù),而這個(gè)消費(fèi)者的代表就是Surface。最終這個(gè)Surface將會(huì)通過Android封裝好的SurfaceView或者TextureView繪制到屏幕上。接下來我們?cè)倏催@里傳過來的Surface(或者是Surface的包裝如SurfaceTexture等)是怎么來的。

public void handleStartPreview(final Object surface) {
        ...
            if (surface instanceof SurfaceHolder) {
                mUVCCamera.setPreviewDisplay((SurfaceHolder) surface);
            }
            if (surface instanceof Surface) {
                mUVCCamera.setPreviewDisplay((Surface) surface);
            } else {
                mUVCCamera.setPreviewTexture((SurfaceTexture) surface);
            }
            mUVCCamera.startPreview();
        ...
        }

我們繼續(xù)跟蹤代碼發(fā)現(xiàn)UVCCamera的這幾個(gè)方法調(diào)用者是AbstractUVCCameraHandler中CameraThread的handleStartPreview方法。而這個(gè)方法又是AbstractUVCCameraHandler本身的startPreview方法所調(diào)用的,重點(diǎn)來了,這個(gè)startPreview中的Object surface究竟是從何而來呢。答案就是這個(gè)surface正是由最終渲染承載的TextureView所創(chuàng)建出來的。在UVCCamera中已經(jīng)幫我們封裝好了一個(gè)TextureView——UVCCameraTextureView,UVCCameraTextureView實(shí)現(xiàn)了一個(gè)自定義的方便調(diào)用的接口——CameraViewInterface。

public interface CameraViewInterface extends IAspectRatioView {
    interface Callback {
        void onSurfaceCreated(CameraViewInterface view, Surface surface);

        void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height);

        void onSurfaceDestroy(CameraViewInterface view, Surface surface);
    }

    void onPause();

    void onResume();

    void setCallback(Callback callback);

    SurfaceTexture getSurfaceTexture();

    Surface getSurface();

    boolean hasSurface();

    void setVideoEncoder(final IVideoEncoder encoder);

    Bitmap captureStillImage(int width, int height);
}

而這個(gè)接口中正有一個(gè)獲取Surface的方法——getSurfaceTexture。我們繼續(xù)看UVCCameraTextureView中對(duì)這個(gè)方法的具體實(shí)現(xiàn):

@Override
public SurfaceTexture getSurfaceTexture() {
  return mRenderHandler != null ? mRenderHandler.getPreviewTexture() : null;
}

在UVCCameraTextureView中定義了一個(gè)渲染線程——RenderThread,RenderHandler則是這個(gè)線程中創(chuàng)建的Handler。我們?cè)倏催@個(gè)RenderHandler的getPreviewTexture方法:

public final SurfaceTexture getPreviewTexture() {
    if (DEBUG) Log.v(TAG, "getPreviewTexture:");
        if (mIsActive) {
            synchronized (mThread.mSync) {
            sendEmptyMessage(MSG_CREATE_SURFACE);
            try {
            mThread.mSync.wait();
            } catch (final InterruptedException e) {
            }
        return mThread.mPreviewSurface;
        }
    } else {
        return null;
    }
}

可以看到最終這個(gè)SurfaceTexture是在RenderThread中的,我們發(fā)現(xiàn)在return之前這個(gè)Handler還發(fā)了一個(gè)create_surface的消息。

@Override
public final void handleMessage(final Message msg) {
    if (mThread == null) return;
    switch (msg.what) {
    ···
    case MSG_CREATE_SURFACE:
    mThread.updatePreviewSurface();
    break;
    ···
    }
}
public final void updatePreviewSurface() {
    if (DEBUG) Log.i(TAG, "RenderThread#updatePreviewSurface:");
    synchronized (mSync) {
        if (mPreviewSurface != null) {
            if (DEBUG) Log.d(TAG, "updatePreviewSurface:release mPreviewSurface");
            mPreviewSurface.setOnFrameAvailableListener(null);
            mPreviewSurface.release();
            mPreviewSurface = null;
        }
        mEglSurface.makeCurrent();
        if (mTexId >= 0) {
            mDrawer.deleteTex(mTexId);
        }
       // create texture and SurfaceTexture for input from camera
        mTexId = mDrawer.initTex();
        if (DEBUG) Log.v(TAG, "updatePreviewSurface:tex_id=" + mTexId);
        mPreviewSurface = new SurfaceTexture(mTexId);
        mPreviewSurface.setDefaultBufferSize(mViewWidth, mViewHeight);
        mPreviewSurface.setOnFrameAvailableListener(mHandler);
        // notify to caller thread that previewSurface is ready
        mSync.notifyAll();
    }
}

可以看到最終這個(gè)SurefaceTexture是在這里創(chuàng)建的。我們大概分析一下這段代碼,首先當(dāng)mPreviewSurface不為空時(shí)候先將其release。然后調(diào)用EGLBase.IEglSurface的makeCurrent方法,這個(gè)EGLBase.IEglSurface也是UVCCamera中定義的,它主要封裝了EGL相關(guān)的東西,并且它是根據(jù)UVCCameraTextureView的Surface所創(chuàng)建的:

private final void init() {
    if (DEBUG) Log.v(TAG, "RenderThread#init:");
    // create EGLContext for this thread
    mEgl = EGLBase.createFrom(null, false, false);
    mEglSurface = mEgl.createFromSurface(mSurface);
    mEglSurface.makeCurrent();
    // create drawing object
    mDrawer = new GLDrawer2D(true);
}

這里的mSurface就是UVCCameraTextureView中onSurfaceTextureAvailable回調(diào)所拿到的。

我們繼續(xù)回到mEglSurface.makeCurrent(),Android里(好像是Api 17以后)用到的EGL是1.4版本,而makeCurrent最終調(diào)用的也是EGL14的makeCurrent。這個(gè)方法是用來切換EGL的上下文,只有在該方法調(diào)用之后,我們才可以調(diào)用OpenGL的方法。
接下來是初始化紋理——mDrawer.initTex(),這個(gè)mDrawer也是UVCCamera中定義的類—— GLDrawer2D,它是一個(gè)對(duì)著色器、紋理操作相關(guān)的封裝,有興趣的同學(xué)可以仔細(xì)閱讀一下這個(gè)類。最終這個(gè)方法會(huì)返回一個(gè)紋理id。接下來的代碼就是根據(jù)這個(gè)紋理id創(chuàng)建一塊SurfaceTexture并設(shè)置默認(rèn)框以及對(duì)紋理變化的監(jiān)聽。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容