blob: 018abbb2a70f9dc37c875696e0973b0bd95cf762 [file] [log] [blame]
#include "include/dvr/dvr_api.h"
#include "include/dvr/dvr_buffer_queue.h"
#include <android/native_window.h>
#include <private/dvr/buffer_hub_queue_producer.h>
#include "dvr_internal.h"
#include "dvr_buffer_queue_internal.h"
using namespace android;
using android::dvr::BufferConsumer;
using android::dvr::BufferHubBuffer;
using android::dvr::BufferHubQueueProducer;
using android::dvr::BufferProducer;
using android::dvr::ConsumerQueue;
using android::dvr::ProducerQueue;
extern "C" {
DvrWriteBufferQueue::DvrWriteBufferQueue(
const std::shared_ptr<ProducerQueue>& 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 (producer_queue_->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
ALOGE(
"DvrWriteBufferQueue::GetNativeWindow: The size of buffer metadata "
"(%zu) of the write queue does not match of size of "
"DvrNativeBufferMetadata (%zu).",
producer_queue_->metadata_size(), sizeof(DvrNativeBufferMetadata));
return -EINVAL;
}
if (native_window_ == nullptr) {
// Lazy creation of |native_window|, as not everyone is using
// DvrWriteBufferQueue as an external surface.
sp<IGraphicBufferProducer> gbp =
BufferHubQueueProducer::Create(producer_queue_);
native_window_ = new Surface(gbp, true);
}
*out_window = static_cast<ANativeWindow*>(native_window_.get());
return 0;
}
int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
std::unique_ptr<ConsumerQueue> 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) {
size_t slot;
pdx::LocalHandle fence;
std::shared_ptr<BufferProducer> buffer_producer;
// 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, &fence);
if (!buffer_status) {
ALOGE_IF(buffer_status.error() != ETIMEDOUT,
"DvrWriteBufferQueue::Dequeue: Failed to dequeue buffer: %s",
buffer_status.GetErrorMessage().c_str());
return -buffer_status.error();
}
buffer_producer = buffer_status.take();
if (!buffer_producer)
return -ENOMEM;
if (width_ == buffer_producer->width() &&
height_ == buffer_producer->height() &&
format_ == buffer_producer->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_, buffer_producer->width(),
buffer_producer->height(), buffer_producer->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 = buffer_producer->layer_count();
uint64_t old_usage = buffer_producer->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();
}
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;
}
write_buffer->write_buffer = std::move(buffer_producer);
*out_fence_fd = fence.Release();
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;
}
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 dvrWriteBufferQueueGetExternalSurface(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 dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
DvrWriteBuffer* write_buffer,
int* out_fence_fd) {
if (!write_queue || !write_buffer || !out_fence_fd)
return -EINVAL;
return write_queue->Dequeue(timeout, write_buffer, out_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<ConsumerQueue>& consumer_queue)
: consumer_queue_(consumer_queue) {}
int DvrReadBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
std::unique_ptr<ConsumerQueue> 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::Dequeue(int timeout, DvrReadBuffer* read_buffer,
int* out_fence_fd, void* out_meta,
size_t meta_size_bytes) {
if (meta_size_bytes != consumer_queue_->metadata_size()) {
ALOGE(
"DvrReadBufferQueue::Dequeue: Invalid metadata size, expected (%zu), "
"but actual (%zu).",
consumer_queue_->metadata_size(), meta_size_bytes);
return -EINVAL;
}
size_t slot;
pdx::LocalHandle acquire_fence;
auto buffer_status = consumer_queue_->Dequeue(
timeout, &slot, out_meta, meta_size_bytes, &acquire_fence);
if (!buffer_status) {
ALOGE_IF(buffer_status.error() != ETIMEDOUT,
"dvrReadBufferQueueDequeue: Failed to dequeue buffer: %s",
buffer_status.GetErrorMessage().c_str());
return -buffer_status.error();
}
read_buffer->read_buffer = buffer_status.take();
*out_fence_fd = acquire_fence.Release();
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<BufferHubBuffer>& buffer) {
DvrReadBuffer read_buffer{
std::static_pointer_cast<BufferConsumer>(buffer)};
callback(&read_buffer, 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 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"