ANativeWindow 和 Surface

纵然是瞬间 提交于 2019-11-27 08:35:55

Android播放视频从解码到显示实质也是BufferQueue的生产消费的过程,如下图所示:

其中生产者是Surface,消费者是SurfaceFlinger。

本文主要针对Surface进行分析,理清ANativeWindow 和 Surface之间的关系。

ANativeWindow的定义如下:

// 代码位置 frameworks/native/libs/nativewindow/include/system/window.h

...

struct ANativeWindow
{
    ...    
    
    int     (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
                struct ANativeWindowBuffer** buffer);
    ...   
    
    int     (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
                struct ANativeWindowBuffer* buffer);
    ...
    
    int     (*query)(const struct ANativeWindow* window,
                int what, int* value);
    ...
    
    int     (*perform)(struct ANativeWindow* window,
                int operation, ... );
    ...
    
    int     (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
                struct ANativeWindowBuffer* buffer);
    ...

    int     (*dequeueBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer** buffer, int* fenceFd);
    ...
    
    int     (*queueBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer* buffer, int fenceFd);    
    ...
    
    int     (*cancelBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer* buffer, int fenceFd);  
}  

...

static inline int native_window_set_buffer_count(
        struct ANativeWindow* window,
        size_t bufferCount)
{
    return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
}

...

static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
        struct ANativeWindowBuffer** anb) {
    return anw->dequeueBuffer_DEPRECATED(anw, anb);
}

...

Surface的定义如下:

// frameworks/native/libs/gui/include/gui/Surface.h

...

class Surface                      
    : public ANativeObjectBase<ANativeWindow, Surface, RefBase>
{
public:

...

可见Surface是ANativeWindow的子类

再看如下代码:

// frameworks/native/libs/gui/Surface.cpp

...

Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
      : mGraphicBufferProducer(bufferProducer),
        mCrop(Rect::EMPTY_RECT),
        mBufferAge(0),
        mGenerationNumber(0),
        mSharedBufferMode(false),
        mAutoRefresh(false),
        mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
        mSharedBufferHasBeenQueued(false),
        mQueriedSupportedTimestamps(false),
        mFrameTimestampsSupportsPresent(false),
        mEnableFrameTimestamps(false),
        mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) {
    // Initialize the ANativeWindow function pointers.
    ANativeWindow::setSwapInterval  = hook_setSwapInterval;
    ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
    ANativeWindow::cancelBuffer     = hook_cancelBuffer;
    ANativeWindow::queueBuffer      = hook_queueBuffer;
    ANativeWindow::query            = hook_query;
    ANativeWindow::perform          = hook_perform;

    ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
    ANativeWindow::cancelBuffer_DEPRECATED  = hook_cancelBuffer_DEPRECATED;
    ANativeWindow::lockBuffer_DEPRECATED    = hook_lockBuffer_DEPRECATED;
    ANativeWindow::queueBuffer_DEPRECATED   = hook_queueBuffer_DEPRECATED;

...

Surface构造时,会对ANativeWindow定义的那些函数进行初始化,hook_xxx表示钩子函数,说明ANativeWindow真正的实现是在Surface里面。

以MediaCodec为例分析一下申请解码输出buffer到送显示的过程,这两个过程也是生产者dequeue(申请buffer)和queue(送显示)的过程。

Dequeue

// frameworks/av/media/libstagefright/ACodec.cpp

...

status_t ACodec::allocateOutputBuffersFromNativeWindow() {
    // This method only handles the non-metadata mode (or simulating legacy
    // mode with metadata, which is transparent to ACodec).
    CHECK(!storingMetadataInDecodedBuffers());

    OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
    status_t err = configureOutputBuffersFromNativeWindow(
            &bufferCount, &bufferSize, &minUndequeuedBuffers, true /* preregister */);
    if (err != 0)
        return err;
    mNumUndequeuedBuffers = minUndequeuedBuffers;

    static_cast<Surface*>(mNativeWindow.get())
            ->getIGraphicBufferProducer()->allowAllocation(true);

    ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
         "output port",
         mComponentName.c_str(), bufferCount, bufferSize);

    // Dequeue buffers and send them to OMX
    for (OMX_U32 i = 0; i < bufferCount; i++) {
        ANativeWindowBuffer *buf;
        int fenceFd;
        err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
        if (err != 0) {
            ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
            break;
        }
        sp<GraphicBuffer> graphicBuffer(GraphicBuffer::from(buf));
        BufferInfo info;
        info.mStatus = BufferInfo::OWNED_BY_US;
        info.mFenceFd = fenceFd;
        info.mIsReadFence = false;
        info.mRenderInfo = NULL;
        info.mGraphicBuffer = graphicBuffer;
        info.mNewGraphicBuffer = false;

        // TODO: We shouln't need to create MediaCodecBuffer. In metadata mode
        //       OMX doesn't use the shared memory buffer, but some code still
        //       access info.mData. Create an ABuffer as a placeholder.
        info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));
        info.mCodecData = info.mData;
...
// frameworks/native/libs/gui/Surface.cpp

...

int Surface::hook_dequeueBuffer(ANativeWindow* window,                                                                                            
        ANativeWindowBuffer** buffer, int* fenceFd) {
    Surface* c = getSelf(window);
    return c->dequeueBuffer(buffer, fenceFd);
}

...

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    ATRACE_CALL();
    ALOGV("Surface::dequeueBuffer");

    uint32_t reqWidth;
    uint32_t reqHeight;
    PixelFormat reqFormat;
    uint64_t reqUsage;
    bool enableFrameTimestamps;

    {
        Mutex::Autolock lock(mMutex);
        if (mReportRemovedBuffers) {
            mRemovedBuffers.clear();
        }
    
        reqWidth = mReqWidth ? mReqWidth : mUserWidth;
        reqHeight = mReqHeight ? mReqHeight : mUserHeight;
    
        reqFormat = mReqFormat;
        reqUsage = mReqUsage;
...

        FrameEventHistoryDelta frameTimestamps;
        status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth,        
                                                                reqHeight, reqFormat, 
                                                                reqUsage, &mBufferAge,
                                                                enableFrameTimestamps ? 
                                                                &frameTimestamps: 
                                                                nullptr);
...

ACodec中allocateOutputBuffersFromNativeWindow调用mNativeWindow->dequeueBuffer,通过Surface的hook_dequeueBuffer最终调用到Surface的dequeueBuffer,最后mGraphicBufferProducer->dequeueBuffer。这个mGraphicBufferProducer的具体实现就是一个BufferQueue,到此可以知道解码申请输出缓存的时候是通过Surface从BufferQueue中dequeue具体数目的匿名共享buffer进行解码显示轮转。

Queue

// frameworks/av/media/libstagefright/ACodec.cpp

...

void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
    IOMX::buffer_id bufferID;      
    CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
    sp<RefBase> obj;               
    CHECK(msg->findObject("buffer", &obj));
    sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
    int32_t discarded = 0;         
    msg->findInt32("discarded", &discarded);

...

    info->mData = buffer;
    int32_t render;
    if (mCodec->mNativeWindow != NULL
            && msg->findInt32("render", &render) && render != 0
            && !discarded && buffer->size() != 0) {
        ATRACE_NAME("render");
        // The client wants this buffer to be rendered.

        android_native_rect_t crop;
        if (buffer->format()->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)) {
            // NOTE: native window uses extended right-bottom coordinate
            ++crop.right;
            ++crop.bottom;
            if (memcmp(&crop, &mCodec->mLastNativeWindowCrop, sizeof(crop)) != 0) {
                mCodec->mLastNativeWindowCrop = crop;
                status_t err = native_window_set_crop(mCodec->mNativeWindow.get(), &crop);
                ALOGW_IF(err != NO_ERROR, "failed to set crop: %d", err);
            }
        }

...

        info->checkReadFence("onOutputBufferDrained before queueBuffer");
        err = mCodec->mNativeWindow->queueBuffer(
                    mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd);

...
// frameworks/native/libs/gui/Surface.cpp

...

int Surface::hook_queueBuffer(ANativeWindow* window,                                                                                              
        ANativeWindowBuffer* buffer, int fenceFd) {
    Surface* c = getSelf(window);
    return c->queueBuffer(buffer, fenceFd);
}

...

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
    ATRACE_CALL();
    ALOGV("Surface::queueBuffer");
    Mutex::Autolock lock(mMutex);
    int64_t timestamp;
    bool isAutoTimestamp = false;

...

    nsecs_t now = systemTime();
    status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
    mLastQueueDuration = systemTime() - now;

...

ACodec的onOutputBufferDrained调用mNativeWindow->queueBuffer,通过Surface的hook_queueBuffer最终调用到Surface的queueBuffer,最后调用mGraphicBufferProducer->queueBuffer完成向BufferQueue送显示帧的过程。

 

以上代码均来源于Android Pie工程,通过这几段代码期望能大体了解Android视频解码到现实的基本流程

小结:

  1. ANativeWindow是android的本地窗口,Surface是ANativeWindow的子类,也是ANativeWindow的具体实现。
  2. IGraphicBufferProducer的具体实现实质就是BufferQueue,创建Surface的时候作为传参。
  3.  解码和显示共享内存的方式可以节省内存,减小解码到显示消耗时间。

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!