#include "include/dvr/dvr_api.h" #include "include/dvr/dvr_buffer_queue.h" #include #include #include "dvr_internal.h" #include "dvr_buffer_queue_internal.h" using namespace android; using android::dvr::BufferHubBase; using android::dvr::ConsumerBuffer; using android::dvr::ConsumerQueue; using android::dvr::ProducerBuffer; using android::dvr::ProducerQueue; using android::dvr::ProducerQueueConfigBuilder; using android::dvr::UsagePolicy; extern "C" { DvrWriteBufferQueue::DvrWriteBufferQueue( const std::shared_ptr& producer_queue) : producer_queue_(producer_queue), width_(producer_queue->default_width()), height_(producer_queue->default_height()), format_(producer_queue->default_format()) {} int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) { if (native_window_ == nullptr) { // Lazy creation of |native_window|, as not everyone is using // DvrWriteBufferQueue as an external surface. sp gbp = BufferHubProducer::Create(producer_queue_); native_window_ = new Surface(gbp, true); } *out_window = static_cast(native_window_.get()); return 0; } int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) { std::unique_ptr consumer_queue = producer_queue_->CreateConsumerQueue(); if (consumer_queue == nullptr) { ALOGE( "DvrWriteBufferQueue::CreateReadQueue: Failed to create consumer queue " "from producer queue: queue_id=%d.", producer_queue_->id()); return -ENOMEM; } *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue)); return 0; } int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd) { DvrNativeBufferMetadata meta; DvrWriteBuffer* buffer = nullptr; int fence_fd = -1; if (const int ret = GainBuffer(timeout, &buffer, &meta, &fence_fd)) return ret; if (!buffer) return -ENOMEM; write_buffers_[buffer->slot].reset(buffer); write_buffer->write_buffer = std::move(buffer->write_buffer); *out_fence_fd = fence_fd; return 0; } int DvrWriteBufferQueue::GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd) { size_t slot; pdx::LocalHandle release_fence; // Need to retry N+1 times, where N is total number of buffers in the queue. // As in the worst case, we will dequeue all N buffers and reallocate them, on // the {N+1}th dequeue, we are guaranteed to get a buffer with new dimension. size_t max_retries = 1 + producer_queue_->capacity(); size_t retry = 0; for (; retry < max_retries; retry++) { auto buffer_status = producer_queue_->Dequeue(timeout, &slot, out_meta, &release_fence); if (!buffer_status) { ALOGE_IF(buffer_status.error() != ETIMEDOUT, "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer: %s", buffer_status.GetErrorMessage().c_str()); return -buffer_status.error(); } if (write_buffers_[slot] == nullptr) { // Lazy initialization of a write_buffers_ slot. Note that a slot will // only be dynamically allocated once during the entire cycle life of a // queue. write_buffers_[slot] = std::make_unique(); write_buffers_[slot]->slot = slot; } LOG_ALWAYS_FATAL_IF( write_buffers_[slot]->write_buffer, "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot); write_buffers_[slot]->write_buffer = std::move(buffer_status.take()); const auto& producer_buffer = write_buffers_[slot]->write_buffer; if (!producer_buffer) return -ENOMEM; if (width_ == producer_buffer->width() && height_ == producer_buffer->height() && format_ == producer_buffer->format()) { // Producer queue returns a buffer matches the current request. break; } // Needs reallocation. Note that if there are already multiple available // buffers in the queue, the next one returned from |queue_->Dequeue| may // still have the old buffer dimension or format. Retry up to N+1 times or // until we dequeued a buffer with new configuration. ALOGD_IF(TRACE, "DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu " "(w=%u, h=%u, fmt=%u) is different from the buffer returned " "(w=%u, h=%u, fmt=%u). Need re-allocation.", slot, width_, height_, format_, producer_buffer->width(), producer_buffer->height(), producer_buffer->format()); // Currently, we are not storing |layer_count| and |usage| in queue // configuration. Copy those setup from the last buffer dequeued before we // remove it. uint32_t old_layer_count = producer_buffer->layer_count(); uint64_t old_usage = producer_buffer->usage(); // Allocate a new producer buffer with new buffer configs. Note that if // there are already multiple available buffers in the queue, the next one // returned from |queue_->Dequeue| may still have the old buffer dimension // or format. Retry up to BufferHubQueue::kMaxQueueCapacity times or until // we dequeued a buffer with new configuration. auto remove_status = producer_queue_->RemoveBuffer(slot); if (!remove_status) { ALOGE("DvrWriteBufferQueue::Dequeue: Failed to remove buffer: %s", remove_status.GetErrorMessage().c_str()); return -remove_status.error(); } // Make sure that the previously allocated buffer is dereferenced from // write_buffers_ array. write_buffers_[slot]->write_buffer = nullptr; auto allocate_status = producer_queue_->AllocateBuffer( width_, height_, old_layer_count, format_, old_usage); if (!allocate_status) { ALOGE("DvrWriteBufferQueue::Dequeue: Failed to allocate buffer: %s", allocate_status.GetErrorMessage().c_str()); return -allocate_status.error(); } } if (retry >= max_retries) { ALOGE( "DvrWriteBufferQueue::Dequeue: Failed to re-allocate buffer after " "resizing."); return -ENOMEM; } *out_write_buffer = write_buffers_[slot].release(); *out_fence_fd = release_fence.Release(); return 0; } int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer, const DvrNativeBufferMetadata* meta, int ready_fence_fd) { // Some basic sanity checks before we put the buffer back into a slot. size_t slot = static_cast(write_buffer->slot); LOG_FATAL_IF( (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()), "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); if (write_buffers_[slot] != nullptr) { ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot); return -EINVAL; } if (write_buffer->write_buffer == nullptr) { ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer."); return -EINVAL; } if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) { ALOGE( "DvrWriteBufferQueue::PostBuffer: Buffer to be posted does not " "belong to this buffer queue. Posting buffer: id=%d, buffer in " "queue: id=%d", write_buffer->write_buffer->id(), producer_queue_->GetBufferId(slot)); return -EINVAL; } write_buffer->write_buffer->SetQueueIndex(next_post_index_++); pdx::LocalHandle fence(ready_fence_fd); const int ret = write_buffer->write_buffer->PostAsync(meta, fence); if (ret < 0) { ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d", ret); return ret; } // Put the DvrWriteBuffer pointer back into its slot for reuse. write_buffers_[slot].reset(write_buffer); // It's import to reset the write buffer client now. It should stay invalid // until next GainBuffer on the same slot. write_buffers_[slot]->write_buffer = nullptr; return 0; } int DvrWriteBufferQueue::ResizeBuffer(uint32_t width, uint32_t height) { if (width == 0 || height == 0) { ALOGE( "DvrWriteBufferQueue::ResizeBuffer: invalid buffer dimension: w=%u, " "h=%u.", width, height); return -EINVAL; } width_ = width; height_ = height; return 0; } int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size, DvrWriteBufferQueue** out_write_queue) { if (!out_write_queue) return -EINVAL; auto config_builder = ProducerQueueConfigBuilder() .SetDefaultWidth(width) .SetDefaultHeight(height) .SetDefaultFormat(format) .SetMetadataSize(metadata_size); std::unique_ptr producer_queue = ProducerQueue::Create(config_builder.Build(), UsagePolicy{}); if (!producer_queue) { ALOGE("dvrWriteBufferQueueCreate: Failed to create producer queue."); return -ENOMEM; } auto status = producer_queue->AllocateBuffers(width, height, layer_count, format, usage, capacity); if (!status.ok()) { ALOGE("dvrWriteBufferQueueCreate: Failed to allocate buffers."); return -ENOMEM; } *out_write_queue = new DvrWriteBufferQueue(std::move(producer_queue)); return 0; } void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) { delete write_queue; } ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) { if (!write_queue) return -EINVAL; return write_queue->capacity(); } int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) { if (!write_queue) return -EINVAL; return write_queue->id(); } int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue, ANativeWindow** out_window) { if (!write_queue || !out_window) return -EINVAL; return write_queue->GetNativeWindow(out_window); } int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue) { if (!write_queue || !out_read_queue) return -EINVAL; return write_queue->CreateReadQueue(out_read_queue); } int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout, DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd) { if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd) return -EINVAL; return write_queue->GainBuffer(timeout, out_write_buffer, out_meta, out_fence_fd); } int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue, DvrWriteBuffer* write_buffer, const DvrNativeBufferMetadata* meta, int ready_fence_fd) { if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta) return -EINVAL; return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd); } int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue, uint32_t width, uint32_t height) { if (!write_queue) return -EINVAL; return write_queue->ResizeBuffer(width, height); } // ReadBufferQueue DvrReadBufferQueue::DvrReadBufferQueue( const std::shared_ptr& consumer_queue) : consumer_queue_(consumer_queue) {} int DvrReadBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) { std::unique_ptr consumer_queue = consumer_queue_->CreateConsumerQueue(); if (consumer_queue == nullptr) { ALOGE( "DvrReadBufferQueue::CreateReadQueue: Failed to create consumer queue " "from producer queue: queue_id=%d.", consumer_queue_->id()); return -ENOMEM; } *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue)); return 0; } int DvrReadBufferQueue::AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd) { size_t slot; pdx::LocalHandle acquire_fence; auto buffer_status = consumer_queue_->Dequeue(timeout, &slot, out_meta, &acquire_fence); if (!buffer_status) { ALOGE_IF(buffer_status.error() != ETIMEDOUT, "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer: %s", buffer_status.GetErrorMessage().c_str()); return -buffer_status.error(); } if (read_buffers_[slot] == nullptr) { // Lazy initialization of a read_buffers_ slot. Note that a slot will only // be dynamically allocated once during the entire cycle life of a queue. read_buffers_[slot] = std::make_unique(); read_buffers_[slot]->slot = slot; } LOG_FATAL_IF( read_buffers_[slot]->read_buffer, "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot); read_buffers_[slot]->read_buffer = std::move(buffer_status.take()); *out_read_buffer = read_buffers_[slot].release(); *out_fence_fd = acquire_fence.Release(); return 0; } int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer, const DvrNativeBufferMetadata* meta, int release_fence_fd) { // Some basic sanity checks before we put the buffer back into a slot. size_t slot = static_cast(read_buffer->slot); LOG_FATAL_IF( (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()), "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); if (read_buffers_[slot] != nullptr) { ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot); return -EINVAL; } if (read_buffer->read_buffer == nullptr) { ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer."); return -EINVAL; } if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) { if (consumer_queue_->GetBufferId(slot) > 0) { ALOGE( "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not " "belong to this queue (queue_id=%d): attempting to release buffer " "(buffer_id=%d) at slot %d which holds a different buffer " "(buffer_id=%d).", consumer_queue_->id(), read_buffer->read_buffer->id(), static_cast(slot), consumer_queue_->GetBufferId(slot)); } else { ALOGI( "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not " "belong to this queue (queue_id=%d): attempting to release buffer " "(buffer_id=%d) at slot %d which is empty.", consumer_queue_->id(), read_buffer->read_buffer->id(), static_cast(slot)); } } pdx::LocalHandle fence(release_fence_fd); int ret = read_buffer->read_buffer->ReleaseAsync(meta, fence); if (ret < 0) { ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d", ret); return ret; } // Put the DvrReadBuffer pointer back into its slot for reuse. read_buffers_[slot].reset(read_buffer); // It's import to reset the read buffer client now. It should stay invalid // until next AcquireBuffer on the same slot. read_buffers_[slot]->read_buffer = nullptr; return 0; } void DvrReadBufferQueue::SetBufferAvailableCallback( DvrReadBufferQueueBufferAvailableCallback callback, void* context) { if (callback == nullptr) { consumer_queue_->SetBufferAvailableCallback(nullptr); } else { consumer_queue_->SetBufferAvailableCallback( [callback, context]() { callback(context); }); } } void DvrReadBufferQueue::SetBufferRemovedCallback( DvrReadBufferQueueBufferRemovedCallback callback, void* context) { if (callback == nullptr) { consumer_queue_->SetBufferRemovedCallback(nullptr); } else { consumer_queue_->SetBufferRemovedCallback( [callback, context](const std::shared_ptr& buffer) { // When buffer is removed from the queue, the slot is already invalid. auto read_buffer = std::make_unique(); read_buffer->read_buffer = std::static_pointer_cast(buffer); callback(read_buffer.release(), context); }); } } int DvrReadBufferQueue::HandleEvents() { // TODO(jwcai) Probably should change HandleQueueEvents to return Status. consumer_queue_->HandleQueueEvents(); return 0; } void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) { delete read_queue; } ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) { if (!read_queue) return -EINVAL; return read_queue->capacity(); } int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) { if (!read_queue) return -EINVAL; return read_queue->id(); } int dvrReadBufferQueueGetEventFd(DvrReadBufferQueue* read_queue) { if (!read_queue) return -EINVAL; return read_queue->event_fd(); } int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue, DvrReadBufferQueue** out_read_queue) { if (!read_queue || !out_read_queue) return -EINVAL; return read_queue->CreateReadQueue(out_read_queue); } int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd, void* out_meta, size_t meta_size_bytes) { if (!read_queue || !read_buffer || !out_fence_fd) return -EINVAL; if (meta_size_bytes != 0 && !out_meta) return -EINVAL; return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta, meta_size_bytes); } int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout, DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd) { if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd) return -EINVAL; return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta, out_fence_fd); } int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue, DvrReadBuffer* read_buffer, const DvrNativeBufferMetadata* meta, int release_fence_fd) { if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta) return -EINVAL; return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd); } int dvrReadBufferQueueSetBufferAvailableCallback( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferAvailableCallback callback, void* context) { if (!read_queue) return -EINVAL; read_queue->SetBufferAvailableCallback(callback, context); return 0; } int dvrReadBufferQueueSetBufferRemovedCallback( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferRemovedCallback callback, void* context) { if (!read_queue) return -EINVAL; read_queue->SetBufferRemovedCallback(callback, context); return 0; } int dvrReadBufferQueueHandleEvents(DvrReadBufferQueue* read_queue) { if (!read_queue) return -EINVAL; return read_queue->HandleEvents(); } } // extern "C"