| #ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ |
| #define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ |
| |
| #include <gui/BufferQueueDefs.h> |
| |
| #include <pdx/client.h> |
| #include <private/dvr/buffer_hub_client.h> |
| #include <private/dvr/epoll_file_descriptor.h> |
| #include <private/dvr/ring_buffer.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| namespace android { |
| namespace dvr { |
| |
| class ConsumerQueue; |
| |
| // |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are |
| // automatically re-requeued when released by the remote side. |
| class BufferHubQueue : public pdx::Client { |
| public: |
| using LocalChannelHandle = pdx::LocalChannelHandle; |
| template <typename T> |
| using Status = pdx::Status<T>; |
| |
| virtual ~BufferHubQueue() {} |
| void Initialize(); |
| |
| // Create a new consumer queue that is attached to the producer. Returns |
| // a new consumer queue client or nullptr on failure. |
| std::unique_ptr<ConsumerQueue> CreateConsumerQueue(); |
| |
| // Return the number of buffers avaiable for dequeue. |
| size_t count() const { return available_buffers_.GetSize(); } |
| |
| // Return the total number of buffers that the queue is tracking. |
| size_t capacity() const { return capacity_; } |
| |
| // Return the size of metadata structure associated with this BufferBubQueue. |
| size_t metadata_size() const { return meta_size_; } |
| |
| // Return whether the buffer queue is alrady full. |
| bool is_full() const { return available_buffers_.IsFull(); } |
| |
| explicit operator bool() const { return epoll_fd_.IsValid(); } |
| |
| std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const { |
| return buffers_[slot]; |
| } |
| |
| // Enqueue a buffer marks buffer to be available (|Gain|'ed for producer |
| // and |Acquire|'ed for consumer. This is only used for internal bookkeeping. |
| void Enqueue(std::shared_ptr<BufferHubBuffer> buf, size_t slot); |
| |
| // |BufferHubQueue| will keep track of at most this value of buffers. |
| static constexpr size_t kMaxQueueCapacity = |
| android::BufferQueueDefs::NUM_BUFFER_SLOTS; |
| |
| // Special epoll data field indicating that the epoll event refers to the |
| // queue. |
| static constexpr int64_t kEpollQueueEventIndex = -1; |
| |
| // When pass |kNoTimeout| to |Dequeue|, it will block indefinitely without a |
| // timeout. |
| static constexpr int kNoTimeOut = -1; |
| |
| protected: |
| BufferHubQueue(LocalChannelHandle channel, size_t meta_size); |
| BufferHubQueue(const std::string& endpoint_path, size_t meta_size); |
| |
| // Called by ProducerQueue::AddBuffer and ConsumerQueue::AddBuffer only. to |
| // register a buffer for epoll and internal bookkeeping. |
| int AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf, size_t slot); |
| |
| // Called by ProducerQueue::DetachBuffer and ConsumerQueue::DetachBuffer only. |
| // to deregister a buffer for epoll and internal bookkeeping. |
| virtual int DetachBuffer(size_t slot); |
| |
| // Dequeue a buffer from the free queue, blocking until one is available. The |
| // timeout argument specifies the number of milliseconds that |Dequeue()| will |
| // block. Specifying a timeout of -1 causes |Dequeue()| to block indefinitely, |
| // while specifying a timeout equal to zero cause |Dequeue()| to return |
| // immediately, even if no buffers are available. |
| std::shared_ptr<BufferHubBuffer> Dequeue(int timeout, size_t* slot, |
| void* meta); |
| |
| // Wait for buffers to be released and re-add them to the queue. |
| bool WaitForBuffers(int timeout); |
| virtual int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) = 0; |
| |
| // Called when a buffer is allocated remotely. |
| virtual int OnBufferAllocated() = 0; |
| |
| // Data members to handle arbitrary metadata passed through BufferHub. It is |
| // fair to enforce that all buffers in the same queue share the same metadata |
| // type. |meta_size_| is used to store the size of metadata on queue creation; |
| // and |meta_buffer_tmp_| is allocated and resized to |meta_size_| on queue |
| // creation to be later used as temporary space so that we can avoid |
| // additional dynamic memory allocation in each |Enqueue| and |Dequeue| call. |
| size_t meta_size_; |
| |
| // Here we intentionally choose |unique_ptr<uint8_t[]>| over vector<uint8_t> |
| // to disallow dynamic resizing for stability reasons. |
| std::unique_ptr<uint8_t[]> meta_buffer_tmp_; |
| |
| private: |
| static constexpr size_t kMaxEvents = 128; |
| |
| // The |u64| data field of an epoll event is interpreted as int64_t: |
| // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific |
| // element of |buffers_| as a direct index; |
| static bool is_buffer_event_index(int64_t index) { |
| return index >= 0 && |
| index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity); |
| } |
| |
| // When |index| == kEpollQueueEventIndex, it refers to the queue itself. |
| static bool is_queue_event_index(int64_t index) { |
| return index == BufferHubQueue::kEpollQueueEventIndex; |
| } |
| |
| struct BufferInfo { |
| // A logical slot number that is assigned to a buffer at allocation time. |
| // The slot number remains unchanged during the entire life cycle of the |
| // buffer and should not be confused with the enqueue and dequeue order. |
| size_t slot; |
| |
| // A BufferHubBuffer client. |
| std::shared_ptr<BufferHubBuffer> buffer; |
| |
| // Metadata associated with the buffer. |
| std::unique_ptr<uint8_t[]> metadata; |
| |
| BufferInfo() : BufferInfo(-1, 0) {} |
| |
| BufferInfo(size_t slot, size_t metadata_size) |
| : slot(slot), |
| buffer(nullptr), |
| metadata(metadata_size ? new uint8_t[metadata_size] : nullptr) {} |
| |
| BufferInfo(BufferInfo&& other) |
| : slot(other.slot), |
| buffer(std::move(other.buffer)), |
| metadata(std::move(other.metadata)) {} |
| |
| BufferInfo& operator=(BufferInfo&& other) { |
| slot = other.slot; |
| buffer = std::move(other.buffer); |
| metadata = std::move(other.metadata); |
| return *this; |
| } |
| |
| private: |
| BufferInfo(const BufferInfo&) = delete; |
| void operator=(BufferInfo&) = delete; |
| }; |
| |
| // Buffer queue: |
| // |buffers_| tracks all |BufferHubBuffer|s created by this |BufferHubQueue|. |
| std::vector<std::shared_ptr<BufferHubBuffer>> buffers_; |
| |
| // |available_buffers_| uses |dvr::RingBuffer| to implementation queue |
| // sematics. When |Dequeue|, we pop the front element from |
| // |available_buffers_|, and that buffer's reference count will decrease by |
| // one, while another reference in |buffers_| keeps the last reference to |
| // prevent the buffer from being deleted. |
| RingBuffer<BufferInfo> available_buffers_; |
| |
| // Keep track with how many buffers have been added into the queue. |
| size_t capacity_; |
| |
| // Epoll fd used to wait for BufferHub events. |
| EpollFileDescriptor epoll_fd_; |
| |
| BufferHubQueue(const BufferHubQueue&) = delete; |
| void operator=(BufferHubQueue&) = delete; |
| }; |
| |
| class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { |
| public: |
| template <typename Meta> |
| static std::unique_ptr<ProducerQueue> Create() { |
| return BASE::Create(sizeof(Meta)); |
| } |
| |
| // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits |
| // in |usage_clear_mask| will be automatically masked off. Note that |
| // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but |
| // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer |
| // allocation through this producer queue shall not have any of the usage bits |
| // in |usage_deny_set_mask| set. Allocation calls violating this will be |
| // rejected. All buffer allocation through this producer queue must have all |
| // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating |
| // this will be rejected. Note that |usage_deny_set_mask| and |
| // |usage_deny_clear_mask| shall not conflict with each other. Such |
| // configuration will be treated as invalid input on creation. |
| template <typename Meta> |
| static std::unique_ptr<ProducerQueue> Create(int usage_set_mask, |
| int usage_clear_mask, |
| int usage_deny_set_mask, |
| int usage_deny_clear_mask) { |
| return BASE::Create(sizeof(Meta), usage_set_mask, usage_clear_mask, |
| usage_deny_set_mask, usage_deny_clear_mask); |
| } |
| |
| // Import a |ProducerQueue| from a channel handle. |
| template <typename Meta> |
| static std::unique_ptr<ProducerQueue> Import(LocalChannelHandle handle) { |
| return BASE::Create(std::move(handle), sizeof(Meta)); |
| } |
| |
| // Get a buffer producer. Note that the method doesn't check whether the |
| // buffer slot has a valid buffer that has been allocated already. When no |
| // buffer has been imported before it returns |nullptr|; otherwise it returns |
| // a shared pointer to a |BufferProducer|. |
| std::shared_ptr<BufferProducer> GetBuffer(size_t slot) const { |
| return std::static_pointer_cast<BufferProducer>( |
| BufferHubQueue::GetBuffer(slot)); |
| } |
| |
| // Allocate producer buffer to populate the queue. Once allocated, a producer |
| // buffer is automatically enqueue'd into the ProducerQueue and available to |
| // use (i.e. in |Gain|'ed mode). |
| // Returns Zero on success and negative error code when buffer allocation |
| // fails. |
| int AllocateBuffer(int width, int height, int format, int usage, |
| size_t buffer_count, size_t* out_slot); |
| |
| // Add a producer buffer to populate the queue. Once added, a producer buffer |
| // is available to use (i.e. in |Gain|'ed mode). |
| int AddBuffer(const std::shared_ptr<BufferProducer>& buf, size_t slot); |
| |
| // Detach producer buffer from the queue. |
| // Returns Zero on success and negative error code when buffer detach |
| // fails. |
| int DetachBuffer(size_t slot) override; |
| |
| // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, |
| // and caller should call Post() once it's done writing to release the buffer |
| // to the consumer side. |
| std::shared_ptr<BufferProducer> Dequeue(int timeout, size_t* slot); |
| |
| private: |
| friend BASE; |
| |
| // Constructors are automatically exposed through ProducerQueue::Create(...) |
| // static template methods inherited from ClientBase, which take the same |
| // arguments as the constructors. |
| explicit ProducerQueue(size_t meta_size); |
| ProducerQueue(LocalChannelHandle handle, size_t meta_size); |
| ProducerQueue(size_t meta_size, int usage_set_mask, int usage_clear_mask, |
| int usage_deny_set_mask, int usage_deny_clear_mask); |
| |
| int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) override; |
| |
| // Producer buffer is always allocated from the client (i.e. local) side. |
| int OnBufferAllocated() override { return 0; } |
| }; |
| |
| class ConsumerQueue : public pdx::ClientBase<ConsumerQueue, BufferHubQueue> { |
| public: |
| // Get a buffer consumer. Note that the method doesn't check whether the |
| // buffer slot has a valid buffer that has been imported already. When no |
| // buffer has been imported before it returns |nullptr|; otherwise it returns |
| // a shared pointer to a |BufferConsumer|. |
| std::shared_ptr<BufferConsumer> GetBuffer(size_t slot) const { |
| return std::static_pointer_cast<BufferConsumer>( |
| BufferHubQueue::GetBuffer(slot)); |
| } |
| |
| // Import newly created buffers from the service side. |
| // Returns number of buffers successfully imported; or negative error code |
| // when buffer import fails. |
| int ImportBuffers(); |
| |
| // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed |
| // mode, and caller should call Releasse() once it's done writing to release |
| // the buffer to the producer side. |meta| is passed along from BufferHub, |
| // The user of BufferProducer is responsible with making sure that the |
| // Dequeue() is done with the corect metadata type and size with those used |
| // when the buffer is orignally created. |
| template <typename Meta> |
| std::shared_ptr<BufferConsumer> Dequeue(int timeout, size_t* slot, |
| Meta* meta) { |
| return Dequeue(timeout, slot, meta, sizeof(*meta)); |
| } |
| |
| std::shared_ptr<BufferConsumer> Dequeue(int timeout, size_t* slot, void* meta, |
| size_t meta_size); |
| |
| private: |
| friend BASE; |
| |
| ConsumerQueue(LocalChannelHandle handle, size_t meta_size); |
| |
| // Add a consumer buffer to populate the queue. Once added, a consumer buffer |
| // is NOT available to use until the producer side |Post| it. |WaitForBuffers| |
| // will catch the |Post| and |Acquire| the buffer to make it available for |
| // consumer. |
| int AddBuffer(const std::shared_ptr<BufferConsumer>& buf, size_t slot); |
| |
| int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) override; |
| |
| int OnBufferAllocated() override; |
| }; |
| |
| } // namespace dvr |
| } // namespace android |
| |
| #endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ |