/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Manage the producer and consumer buffers needed by FrameProviders. */ #pragma once #include #include #include #include #include #include #include namespace android { namespace webcam { enum BufferType { V4L2 = 0, }; struct HardwareBufferDesc { uint8_t* yData = nullptr; uint32_t yDataLength = 0; uint32_t yRowStride = 0; uint8_t* uData = nullptr; uint32_t uDataLength = 0; uint32_t uRowStride = 0; uint8_t* vData = nullptr; uint32_t vDataLength = 0; uint32_t vRowStride = 0; uint32_t uvPixelStride = 0; uint32_t bufferId = 0; }; class BufferManager; // Base class for Buffer, for future types apart from V4L2 // Thin wrapper over struct v4l2_buffer. The client should not free the memory obtained from // getMem(). All Buffers are created by BufferManager through BufferCreatorAndDestroyer which is // transport specific. // TODO(b/267794640): Change Buffer to have either a std::shared_ptr to BufferManager or have // BufferManager be a singleton. BufferManager should implement // counting 'handout' buffers before freeing. BufferManager should explicitly // check that there are no Buffers alive before freeing the handed out buffers. // This will also ensure that the lifetime of BufferManager >= lifetime of // Buffer. class Buffer { public: Buffer() = default; virtual BufferType getBufferType() = 0; [[nodiscard]] virtual void* getMem() = 0; [[nodiscard]] virtual size_t getLength() const = 0; virtual void setBytesUsed(uint32_t bytesUsed) = 0; virtual ~Buffer() = default; void setTimestamp(uint64_t ts) { mTimestamp = ts; } [[nodiscard]] uint64_t getTimestamp() const { return mTimestamp; } [[nodiscard]] virtual uint32_t getIndex() const = 0; friend class BufferManager; private: uint64_t mTimestamp = 0; }; class V4L2Buffer : public Buffer { public: V4L2Buffer(void* mem, const struct v4l2_buffer* buffer) : mMem(mem), mBuffer(*buffer) {} V4L2Buffer() { memset(&mBuffer, 0, sizeof(mBuffer)); } ~V4L2Buffer() override = default; BufferType getBufferType() override { return BufferType::V4L2; } // Memory will be freed by BufferManager. Do not free. [[nodiscard]] void* getMem() override { return mMem; } [[nodiscard]] size_t getLength() const override { return mBuffer.length; } void setBytesUsed(uint32_t bytesUsed) override { mBuffer.bytesused = bytesUsed; } struct v4l2_buffer* getV4L2Buffer() { return &mBuffer; } [[nodiscard]] uint32_t getIndex() const override { return mBuffer.index; } private: void* mMem = nullptr; struct v4l2_buffer mBuffer {}; }; class BufferProducer { public: // Returns a free buffer if it is available. This method does not wait for a free // buffer. Buffer is owned by BufferProducer. Caller should not manage the lifetime of the // object and should ensure that all buffer references are dropped when BufferProducer goes out // of scope. virtual Buffer* getFreeBufferIfAvailable() = 0; // Queues a filled buffer to the BufferManager. virtual Status queueFilledBuffer(Buffer* buffer) = 0; // Cancels a queued Buffer virtual Status cancelBuffer(Buffer* buffer) = 0; virtual ~BufferProducer() = default; }; class BufferConsumer { public: // Gets a filled buffer from BufferManager (waits if one is not available) and returns the // consumer side buffer for the BufferManager to give away to the producer to use. // Buffer is owned by BufferConsumer. Caller should not manage the lifetime of the object. virtual Buffer* getFilledBufferAndSwap() = 0; virtual ~BufferConsumer() = default; }; class BufferCreatorAndDestroyer { public: virtual ~BufferCreatorAndDestroyer() = default; virtual Status allocateAndMapBuffers(std::shared_ptr* consumerBuffer, std::vector>* producerBuffer) = 0; virtual void destroyBuffers(std::shared_ptr& consumerBuffer, std::vector>& producerBuffers) = 0; }; class BufferManager : public BufferConsumer, public BufferProducer { // There are 2 types of buffers : the consumer side buffer and the producer side buffers. // The consumer side needs only 1. // The producer side is typically some component which fills in frames such as a FrameProvider // class instance. // The consumer side is typically some component that fetches filled buffers and sends them over // some transport method to the host. public: // TODO(b/267794640): Look into better memory management // BufferCreatorAndDestroyer is owned by the caller, it must be active throughout the lifetime // of BufferManager explicit BufferManager(BufferCreatorAndDestroyer* crD); ~BufferManager() override; [[nodiscard]] bool isInited() const { return mInited; } Buffer* getFreeBufferIfAvailable() override; Status queueFilledBuffer(Buffer* buffer) override; Status cancelBuffer(Buffer* buffer) override; Buffer* getFilledBufferAndSwap() override; private: enum BufferState { IN_USE = 0, FILLED = 1, FREE = 2, }; struct BufferItem { BufferItem() : buffer(nullptr), state(BufferState::FREE) {} BufferItem(std::shared_ptr& buf, BufferState st) : buffer(buf), state(st) {} std::shared_ptr buffer; BufferState state = BufferState::FREE; }; // Checks if a filled buffer has been made available by the producer and gets the vector index, // of the latest filled buffer. Cancels buffers older than the one referenced by index. bool filledProducerBufferAvailableLocked(uint32_t* index); bool changeProducerBufferStateLocked(Buffer* buffer, BufferState newState); bool mInited = false; BufferCreatorAndDestroyer* mCrD = nullptr; std::mutex mBufferLock; // guards all operations relating to Buffer and BufferItems std::condition_variable mProducerBufferFilled; // guarded by mBufferLock BufferItem mConsumerBufferItem; // guarded by mBufferLock // Using a simple vector here as we don't expect to have more than 3-4 buffers in circulation // We have multiple producer buffers so that skews between other producers being used by the // producer side - eg: buffer from camera doesn't get blocked from getting encoded while // consumer is still consuming the consumer side buffer (this could happen if we had only 1 // producer buffer and 1 consumer buffer and there's a skew between camera frame production and // consumer (UVC gadget driver etc) frame consumption. std::vector mProducerBufferItems; // guarded by mBufferLock }; } // namespace webcam } // namespace android