Android13 BufferQueueLayer onFirstRef流程分析

BufferQueueLayer是Android系统中的一个图层,用于管理图像缓冲区的队列。它是SurfaceFlinger系统服务的一部分,负责接收应用程序或系统组件提交的图像缓冲区,并将其显示在屏幕上。onFirstRef是BufferQueueLayer类的一个方法,它是在第一次引用BufferQueueLayer对象时被调用的。在这个方法中,可以进行一些初始化操作,例如创建和配置图像缓冲区队列。

在创建BufferQueueLayer同时会创建一套生产者-消费者模型架构,核心是如下几个类:

BufferQueueLayer:创建了BufferQueue、MonitoredProducer、BufferLayerConsumer

BufferQueue:buffer队列,创建BufferQueueCore,BufferQueueProducer

BufferQueueProducer:生产者

BufferQueueConsumer:消费者

MonitoredProducer:生产者的封装

BufferLayerConsumer:消费者的封装

生产者提供图形数据,放入BufferQueue,消费者拿到图形数据进行合成,通常认为生产者为Surface,消费者为SurfaceFlinger,下面我们就分析一下生产者-消费者模型架构的搭建。

我们以BufferQueueLayer的创建为入口分析:

//frameworks/native/services/surfaceflinger/BufferQueueLayer.cpp
void BufferQueueLayer::onFirstRef() {
    BufferLayer::onFirstRef();


    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    //步骤1
    BufferQueue::createBufferQueue(&producer, &consumer, true); //创建BufferQueue
    //步骤2
    mProducer = new MonitoredProducer(producer, mFlinger, this); //创建一个生产者
    {
        // Grab the SF state lock during this since it's the only safe way to access RenderEngine
        Mutex::Autolock lock(mFlinger->mStateLock);
        //步骤3
        mConsumer =
                new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this);  //创建一个消费者
    {
    }
    //步骤4
    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));  //设置缓冲区的类型,会保存到BufferQueueCore中的mConsumerUsageBits变量中
    //步骤5
    mConsumer->setContentsChangedListener(this);  //设置缓冲区内容改变的监听器
    mConsumer->setName(mName);


    // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
    if (!mFlinger->isLayerTripleBufferingDisabled()) {
        mProducer->setMaxDequeuedBufferCount(2);
    }


    if (const auto display = mFlinger->getDefaultDisplayDevice()) {
        updateTransformHint(display);
    }


    if (mFlinger->mLayerExt) {
        mLayerType = mFlinger->mLayerExt->getLayerClass(mName.string());
    }
}

上面这个函数就是创建SurfaceFlinger生产者-消费者模型的核心代码,我们分步骤分析:

BufferQueue createBufferQueue

步骤1:createBufferQueue,从名字看就能知道是创建BufferQueue,并且将生产者producer和消费者consumer的地址传了过去,显然这两个对象也会在createBufferQueue中创建。

//frameworks/native/libs/gui/BufferQueue.cpp
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
        sp<IGraphicBufferConsumer>* outConsumer,
        bool consumerIsSurfaceFlinger) {


    sp<BufferQueueCore> core(new BufferQueueCore()); //创建BufferQueueCore
    
    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); //创建BufferQueueProducer
   
    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); //创建BufferQueueConsumer
    
    *outProducer = producer;
    *outConsumer = consumer;
}

可以看到这个函数中并没有创建BufferQueue,而是创建的BufferQueueCore,可见BufferQueue的核心实现其实是依靠BufferQueueCore的,接着又创建了生产者的具体实现类BufferQueueProducer,消费者的具体实现类BufferQueueConsumer,并且这两个类都持有BufferQueueCore的引用,最后outProducer,outConsumer分别指向创建的生产者-消费者,下面分别进行分析:

new BufferQueueCore

创建BufferQueueCore对象,BufferQueueCore的构造方法如下:

//frameworks/native/libs/gui/BufferQueueCore.cpp
class BufferQueueCore : public virtual RefBase {
BufferQueueCore::BufferQueueCore()
      : mMutex(),
        mIsAbandoned(false),
        mConsumerControlledByApp(false),
        mConsumerName(getUniqueName()),
        mConsumerListener(),
        mConsumerUsageBits(0),
        mConsumerIsProtected(false),
        mConnectedApi(NO_CONNECTED_API),
        mLinkedToDeath(),
        mConnectedProducerListener(),
        mBufferReleasedCbEnabled(false),
        mSlots(),
        mQueue(),
        mFreeSlots(),
        mFreeBuffers(),
        mUnusedSlots(),
        mActiveBuffers(),
        mDequeueCondition(),
        mDequeueBufferCannotBlock(false),
        mQueueBufferCanDrop(false),
        mLegacyBufferDrop(true),
        mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
        mDefaultWidth(1),
        mDefaultHeight(1),
        mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
        mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
        mMaxAcquiredBufferCount(1),
        mMaxDequeuedBufferCount(1),
        mBufferHasBeenQueued(false),
        mFrameCounter(0),
        mTransformHint(0),
        mIsAllocating(false),
        mIsAllocatingCondition(),
        mAllowAllocation(true),
        mBufferAge(0),
        mGenerationNumber(0),
        mAsyncMode(false),
        mSharedBufferMode(false),
        mAutoRefresh(false),
        mSharedBufferSlot(INVALID_BUFFER_SLOT),
        mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
                           HAL_DATASPACE_UNKNOWN),
        mLastQueuedSlot(INVALID_BUFFER_SLOT),
        mUniqueId(getUniqueId()),
        mAutoPrerotation(false),
        mTransformHintInUse(0) {
    int numStartingBuffers = getMaxBufferCountLocked();
    for (int s = 0; s < numStartingBuffers; s++) {
        mFreeSlots.insert(s);
    }
    for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;
            s++) {
        mUnusedSlots.push_front(s);
    }
}
}

new BufferQueueProducer

创建BufferQueueProducer对象,BufferQueueProducer的构造方法如下:

//frameworks/native/libs/gui/BufferQueueProducer.cpp
class BufferQueueProducer : public BnGraphicBufferProducer,
                            private IBinder::DeathRecipient {
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
        bool consumerIsSurfaceFlinger) :
    mCore(core),
    mSlots(core->mSlots),
    mConsumerName(),
    mStickyTransform(0),
    mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),
    mLastQueueBufferFence(Fence::NO_FENCE),
    mLastQueuedTransform(0),
    mCallbackMutex(),
    mNextCallbackTicket(0),
    mCurrentCallbackTicket(0),
    mCallbackCondition(),
    mDequeueTimeout(-1),
    mDequeueWaitingForAllocation(false) {}
}

new BufferQueueConsumer

创建BufferQueueConsumer对象,BufferQueueConsumer的构造方法如下:

//frameworks/native/libs/gui/BufferQueueConsumer.cpp
class BufferQueueConsumer : public BnGraphicBufferConsumer {
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
    mCore(core),
    mSlots(core->mSlots),
    mConsumerName() {}
}

new MonitoredProducer

步骤2:为生产者对象创建一个MonitoredProducer,这个类完全就是生产者的封装类,它里面的所有函数几乎都是通过传递进去的producer来完成的,MonitoredProducer类定义如下:

//frameworks/native/services/surfaceflinger/MonitoredProducer.cpp
class MonitoredProducer : public BnGraphicBufferProducer {
public:
    MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
            const sp<SurfaceFlinger>& flinger,
            const wp<Layer>& layer);
    virtual ~MonitoredProducer();


    // From IGraphicBufferProducer
    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
    virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
    virtual status_t setAsyncMode(bool async);
    virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
                                   PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
                                   FrameEventHistoryDelta* outTimestamps);
    virtual status_t detachBuffer(int slot);
    virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
            sp<Fence>* outFence);
    virtual status_t attachBuffer(int* outSlot,
            const sp<GraphicBuffer>& buffer);
    virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
            QueueBufferOutput* output);
    virtual status_t cancelBuffer(int slot, const sp<Fence>& fence);
    virtual int query(int what, int* value);
    virtual status_t connect(const sp<IProducerListener>& token, int api,
            bool producerControlledByApp, QueueBufferOutput* output);
    virtual status_t disconnect(int api, DisconnectMode mode);
    virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
    virtual void allocateBuffers(uint32_t width, uint32_t height,
            PixelFormat format, uint64_t usage);
    virtual status_t allowAllocation(bool allow);
    virtual status_t setGenerationNumber(uint32_t generationNumber);
    virtual String8 getConsumerName() const override;
    virtual status_t setDequeueTimeout(nsecs_t timeout) override;
    virtual status_t setLegacyBufferDrop(bool drop) override;
    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
            sp<Fence>* outFence, float outTransformMatrix[16]) override;
    virtual IBinder* onAsBinder();
    virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
    virtual status_t setAutoRefresh(bool autoRefresh) override;
    virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
    virtual status_t getUniqueId(uint64_t* outId) const override;
    virtual status_t getConsumerUsage(uint64_t* outUsage) const override;


    // The Layer which created this producer, and on which queued Buffer's will be displayed.
    sp<Layer> getLayer() const;


private:
    sp<IGraphicBufferProducer> mProducer;
    sp<SurfaceFlinger> mFlinger;
    // The Layer which created this producer, and on which queued Buffer's will be displayed.
    wp<Layer> mLayer;
};


}; // namespace android

new BufferLayerConsumer

步骤3:为生产者对象创建一个BufferLayerConsumer,这个类完全就是消费者的封装类,BufferLayerConsumer类定义如下:

//frameworks/native/services/surfaceflinger/BufferLayerConsumer.cpp
class BufferLayerConsumer : public ConsumerBase {
public:
    static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;


    class BufferRejecter {
        friend class BufferLayerConsumer;
        virtual bool reject(const sp<GraphicBuffer>& buf, const BufferItem& item) = 0;


    protected:
        virtual ~BufferRejecter() {}
    };


    struct ContentsChangedListener : public FrameAvailableListener {
        virtual void onSidebandStreamChanged() = 0;
    };


    // BufferLayerConsumer constructs a new BufferLayerConsumer object.  The
    // tex parameter indicates the name of the RenderEngine texture to which
    // images are to be streamed.
    BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine,
                        uint32_t tex, Layer* layer);


    // Sets the contents changed listener. This should be used instead of
    // ConsumerBase::setFrameAvailableListener().
    void setContentsChangedListener(const wp<ContentsChangedListener>& listener);


    // updateTexImage acquires the most recently queued buffer, and sets the
    // image contents of the target texture to it.
    //
    // This call may only be made while RenderEngine is current.
    //
    // This calls doFenceWait to ensure proper synchronization unless native
    // fence is supported.
    //
    // Unlike the GLConsumer version, this version takes a functor that may be
    // used to reject the newly acquired buffer.  It also does not bind the
    // RenderEngine texture until bindTextureImage is called.
    status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
                            bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);


    // See BufferLayerConsumer::bindTextureImageLocked().
    status_t bindTextureImage();


    // setReleaseFence stores a fence that will signal when the current buffer
    // is no longer being read. This fence will be returned to the producer
    // when the current buffer is released by updateTexImage(). Multiple
    // fences can be set for a given buffer; they will be merged into a single
    // union fence.
    void setReleaseFence(const sp<Fence>& fence);


    bool releasePendingBuffer();


    sp<Fence> getPrevFinalReleaseFence() const;


    // See GLConsumer::getTransformMatrix.
    void getTransformMatrix(float mtx[16]);


    // getTimestamp retrieves the timestamp associated with the texture image
    // set by the most recent call to updateTexImage.
    //
    // The timestamp is in nanoseconds, and is monotonically increasing. Its
    // other semantics (zero point, etc) are source-dependent and should be
    // documented by the source.
    int64_t getTimestamp();


    // getDataSpace retrieves the DataSpace associated with the texture image
    // set by the most recent call to updateTexImage.
    ui::Dataspace getCurrentDataSpace();


    // getCurrentHdrMetadata retrieves the HDR metadata associated with the
    // texture image set by the most recent call to updateTexImage.
    const HdrMetadata& getCurrentHdrMetadata() const;


    // getFrameNumber retrieves the frame number associated with the texture
    // image set by the most recent call to updateTexImage.
    //
    // The frame number is an incrementing counter set to 0 at the creation of
    // the BufferQueue associated with this consumer.
    uint64_t getFrameNumber();


    bool getTransformToDisplayInverse() const;


    // must be called from SF main thread
    const Region& getSurfaceDamage() const;


    // Merge the given damage region into the current damage region value.
    void mergeSurfaceDamage(const Region& damage);


    // getCurrentApi retrieves the API which queues the current buffer.
    int getCurrentApi() const;


    // See GLConsumer::setDefaultBufferSize.
    status_t setDefaultBufferSize(uint32_t width, uint32_t height);


    // setFilteringEnabled sets whether the transform matrix should be computed
    // for use with bilinear filtering.
    void setFilteringEnabled(bool enabled);


    // getCurrentBuffer returns the buffer associated with the current image.
    // When outSlot is not nullptr, the current buffer slot index is also
    // returned. Simiarly, when outFence is not nullptr, the current output
    // fence is returned.
    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;


    // getCurrentCrop returns the cropping rectangle of the current buffer.
    Rect getCurrentCrop() const;


    // getCurrentTransform returns the transform of the current buffer.
    uint32_t getCurrentTransform() const;


    // getCurrentScalingMode returns the scaling mode of the current buffer.
    uint32_t getCurrentScalingMode() const;


    // getCurrentFence returns the fence indicating when the current buffer is
    // ready to be read from.
    sp<Fence> getCurrentFence() const;


    // getCurrentFence returns the FenceTime indicating when the current
    // buffer is ready to be read from.
    std::shared_ptr<FenceTime> getCurrentFenceTime() const;


    // setConsumerUsageBits overrides the ConsumerBase method to OR
    // DEFAULT_USAGE_FLAGS to usage.
    status_t setConsumerUsageBits(uint64_t usage);
    void onBufferAvailable(const BufferItem& item) EXCLUDES(mImagesMutex);


protected:
    // abandonLocked overrides the ConsumerBase method to clear
    // mCurrentTextureImage in addition to the ConsumerBase behavior.
    virtual void abandonLocked() EXCLUDES(mImagesMutex);


    // dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
    // specific info in addition to the ConsumerBase behavior.
    virtual void dumpLocked(String8& result, const char* prefix) const;


    // See ConsumerBase::acquireBufferLocked
    virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
                                         uint64_t maxFrameNumber = 0) override
            EXCLUDES(mImagesMutex);


    bool canUseImageCrop(const Rect& crop) const;


    struct PendingRelease {
        PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {}


        bool isPending;
        int currentTexture;
        sp<GraphicBuffer> graphicBuffer;
    };


    // This releases the buffer in the slot referenced by mCurrentTexture,
    // then updates state to refer to the BufferItem, which must be a
    // newly-acquired buffer. If pendingRelease is not null, the parameters
    // which would have been passed to releaseBufferLocked upon the successful
    // completion of the method will instead be returned to the caller, so that
    // it may call releaseBufferLocked itself later.
    status_t updateAndReleaseLocked(const BufferItem& item,
                                    PendingRelease* pendingRelease = nullptr)
            EXCLUDES(mImagesMutex);


    // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
    // If the bind succeeds, this calls doFenceWait.
    status_t bindTextureImageLocked();


private:
    // Utility class for managing GraphicBuffer references into renderengine
    class Image {
    public:
        Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& engine);
        virtual ~Image();
        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }


    private:
        // mGraphicBuffer is the buffer that was used to create this image.
        sp<GraphicBuffer> mGraphicBuffer;
        // Back-reference into renderengine to initiate cleanup.
        renderengine::RenderEngine& mRE;
        DISALLOW_COPY_AND_ASSIGN(Image);
    };


    // freeBufferLocked frees up the given buffer slot. If the slot has been
    // initialized this will release the reference to the GraphicBuffer in
    // that slot.  Otherwise it has no effect.
    //
    // This method must be called with mMutex locked.
    virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex);


    // IConsumerListener interface
    void onDisconnect() override;
    void onSidebandStreamChanged() override;
    void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                  FrameEventHistoryDelta* outDelta) override;


    // computeCurrentTransformMatrixLocked computes the transform matrix for the
    // current texture.  It uses mCurrentTransform and the current GraphicBuffer
    // to compute this matrix and stores it in mCurrentTransformMatrix.
    // mCurrentTextureImage must not be nullptr.
    void computeCurrentTransformMatrixLocked();


    // doFenceWaitLocked inserts a wait command into the RenderEngine command
    // stream to ensure that it is safe for future RenderEngine commands to
    // access the current texture buffer.
    status_t doFenceWaitLocked() const;


    // getCurrentCropLocked returns the cropping rectangle of the current buffer.
    Rect getCurrentCropLocked() const;


    // The default consumer usage flags that BufferLayerConsumer always sets on its
    // BufferQueue instance; these will be OR:d with any additional flags passed
    // from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always
    // consume buffers as hardware textures.
    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;


    // mCurrentTextureBuffer is the buffer containing the current texture. It's
    // possible that this buffer is not associated with any buffer slot, so we
    // must track it separately in order to support the getCurrentBuffer method.
    std::shared_ptr<Image> mCurrentTextureBuffer;


    // mCurrentCrop is the crop rectangle that applies to the current texture.
    // It gets set each time updateTexImage is called.
    Rect mCurrentCrop;


    // mCurrentTransform is the transform identifier for the current texture. It
    // gets set each time updateTexImage is called.
    uint32_t mCurrentTransform;


    // mCurrentScalingMode is the scaling mode for the current texture. It gets
    // set each time updateTexImage is called.
    uint32_t mCurrentScalingMode;


    // mCurrentFence is the fence received from BufferQueue in updateTexImage.
    sp<Fence> mCurrentFence;


    // The FenceTime wrapper around mCurrentFence.
    std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};


    // mCurrentTransformMatrix is the transform matrix for the current texture.
    // It gets computed by computeTransformMatrix each time updateTexImage is
    // called.
    float mCurrentTransformMatrix[16];


    // mCurrentTimestamp is the timestamp for the current texture. It
    // gets set each time updateTexImage is called.
    int64_t mCurrentTimestamp;


    // mCurrentDataSpace is the dataspace for the current texture. It
    // gets set each time updateTexImage is called.
    ui::Dataspace mCurrentDataSpace;


    // mCurrentHdrMetadata is the HDR metadata for the current texture. It
    // gets set each time updateTexImage is called.
    HdrMetadata mCurrentHdrMetadata;


    // mCurrentFrameNumber is the frame counter for the current texture.
    // It gets set each time updateTexImage is called.
    uint64_t mCurrentFrameNumber;


    // Indicates this buffer must be transformed by the inverse transform of the screen
    // it is displayed onto. This is applied after BufferLayerConsumer::mCurrentTransform.
    // This must be set/read from SurfaceFlinger's main thread.
    bool mCurrentTransformToDisplayInverse;


    // The portion of this surface that has changed since the previous frame
    Region mCurrentSurfaceDamage;


    int mCurrentApi;


    uint32_t mDefaultWidth, mDefaultHeight;


    // mFilteringEnabled indicates whether the transform matrix is computed for
    // use with bilinear filtering. It defaults to true and is changed by
    // setFilteringEnabled().
    bool mFilteringEnabled;


    renderengine::RenderEngine& mRE;


    // mTexName is the name of the RenderEngine texture to which streamed
    // images will be bound when bindTexImage is called. It is set at
    // construction time.
    const uint32_t mTexName;


    // The layer for this BufferLayerConsumer
    const wp<Layer> mLayer;


    wp<ContentsChangedListener> mContentsChangedListener;


    // mCurrentTexture is the buffer slot index of the buffer that is currently
    // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
    // indicating that no buffer slot is currently bound to the texture. Note,
    // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
    // that no buffer is bound to the texture. A call to setBufferCount will
    // reset mCurrentTexture to INVALID_BUFFER_SLOT.
    int mCurrentTexture;


    // Shadow buffer cache for cleaning up renderengine references.
    std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);


    // Separate mutex guarding the shadow buffer cache.
    // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
    // which is contentious enough that we can't just use mMutex.
    mutable std::mutex mImagesMutex;


    // A release that is pending on the receipt of a new release fence from
    // presentDisplay
    PendingRelease mPendingRelease;
}

步骤4:setConsumerUsageBits设置缓冲区的类型,会保存到BufferQueueCore中的mConsumerUsageBits变量中。

BufferLayerConsumer setContentsChangedListener

步骤5:通过setContentsChangedListener给消费者设置一个缓冲区内容变化的监听器:

// frameworks/native/services/surfaceflinger/BufferLayerConsumer.cpp
void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) {
    setFrameAvailableListener(listener);
    Mutex::Autolock lock(mMutex);
    mContentsChangedListener = listener;
}

生产者dequeue一块buffer,应用程序进行绘制,绘制完成后queue此块buffer,此时生产者调用BufferQueueCore的mConsumerListener的onFrameAvailable回调函数,mConsumerListener其实是BufferQueue::ProxyConsumerListener,BufferQueue::ProxyConsumerListener在创建时又接收了ConsumerBase,所以调用到了ConsumerBase的onFrameAvailable中,ConsumerBase这里面又有一个成员变量mFrameAvailableListener,类型为BufferQueueLayer,所以最终是调用到了BufferQueueLayer的具体实现onFrameAvailable中,对这块已经绘制好的buffer进一步处理。

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值