Home | History | Annotate | Download | only in libdvr
      1 #include "include/dvr/dvr_api.h"
      2 #include "include/dvr/dvr_buffer_queue.h"
      3 
      4 #include <android/native_window.h>
      5 #include <private/dvr/buffer_hub_queue_producer.h>
      6 
      7 #include "dvr_internal.h"
      8 #include "dvr_buffer_queue_internal.h"
      9 
     10 using namespace android;
     11 using android::dvr::BufferConsumer;
     12 using android::dvr::BufferHubBuffer;
     13 using android::dvr::BufferHubQueueProducer;
     14 using android::dvr::BufferProducer;
     15 using android::dvr::ConsumerQueue;
     16 using android::dvr::ProducerQueue;
     17 using android::dvr::ProducerQueueConfigBuilder;
     18 using android::dvr::UsagePolicy;
     19 
     20 extern "C" {
     21 
     22 DvrWriteBufferQueue::DvrWriteBufferQueue(
     23     const std::shared_ptr<ProducerQueue>& producer_queue)
     24     : producer_queue_(producer_queue),
     25       width_(producer_queue->default_width()),
     26       height_(producer_queue->default_height()),
     27       format_(producer_queue->default_format()) {}
     28 
     29 int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) {
     30   if (native_window_ == nullptr) {
     31     // Lazy creation of |native_window|, as not everyone is using
     32     // DvrWriteBufferQueue as an external surface.
     33     sp<IGraphicBufferProducer> gbp =
     34         BufferHubQueueProducer::Create(producer_queue_);
     35     native_window_ = new Surface(gbp, true);
     36   }
     37 
     38   *out_window = static_cast<ANativeWindow*>(native_window_.get());
     39   return 0;
     40 }
     41 
     42 int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
     43   std::unique_ptr<ConsumerQueue> consumer_queue =
     44       producer_queue_->CreateConsumerQueue();
     45   if (consumer_queue == nullptr) {
     46     ALOGE(
     47         "DvrWriteBufferQueue::CreateReadQueue: Failed to create consumer queue "
     48         "from producer queue: queue_id=%d.", producer_queue_->id());
     49     return -ENOMEM;
     50   }
     51 
     52   *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue));
     53   return 0;
     54 }
     55 
     56 int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer,
     57                                  int* out_fence_fd) {
     58   DvrNativeBufferMetadata meta;
     59   DvrWriteBuffer* buffer = nullptr;
     60   int fence_fd = -1;
     61   if (const int ret = GainBuffer(timeout, &buffer, &meta, &fence_fd))
     62     return ret;
     63   if (!buffer)
     64     return -ENOMEM;
     65 
     66   write_buffers_[buffer->slot].reset(buffer);
     67   write_buffer->write_buffer = std::move(buffer->write_buffer);
     68   *out_fence_fd = fence_fd;
     69   return 0;
     70 }
     71 
     72 int DvrWriteBufferQueue::GainBuffer(int timeout,
     73                                     DvrWriteBuffer** out_write_buffer,
     74                                     DvrNativeBufferMetadata* out_meta,
     75                                     int* out_fence_fd) {
     76   size_t slot;
     77   pdx::LocalHandle release_fence;
     78 
     79   // Need to retry N+1 times, where N is total number of buffers in the queue.
     80   // As in the worst case, we will dequeue all N buffers and reallocate them, on
     81   // the {N+1}th dequeue, we are guaranteed to get a buffer with new dimension.
     82   size_t max_retries = 1 + producer_queue_->capacity();
     83   size_t retry = 0;
     84 
     85   for (; retry < max_retries; retry++) {
     86     auto buffer_status =
     87         producer_queue_->Dequeue(timeout, &slot, out_meta, &release_fence);
     88     if (!buffer_status) {
     89       ALOGE_IF(buffer_status.error() != ETIMEDOUT,
     90                "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer: %s",
     91                buffer_status.GetErrorMessage().c_str());
     92       return -buffer_status.error();
     93     }
     94 
     95     if (write_buffers_[slot] == nullptr) {
     96       // Lazy initialization of a write_buffers_ slot. Note that a slot will
     97       // only be dynamically allocated once during the entire cycle life of a
     98       // queue.
     99       write_buffers_[slot] = std::make_unique<DvrWriteBuffer>();
    100       write_buffers_[slot]->slot = slot;
    101     }
    102 
    103     LOG_ALWAYS_FATAL_IF(
    104         write_buffers_[slot]->write_buffer,
    105         "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot);
    106     write_buffers_[slot]->write_buffer = std::move(buffer_status.take());
    107 
    108     const auto& buffer_producer = write_buffers_[slot]->write_buffer;
    109     if (!buffer_producer)
    110       return -ENOMEM;
    111 
    112     if (width_ == buffer_producer->width() &&
    113         height_ == buffer_producer->height() &&
    114         format_ == buffer_producer->format()) {
    115       // Producer queue returns a buffer matches the current request.
    116       break;
    117     }
    118 
    119     // Needs reallocation. Note that if there are already multiple available
    120     // buffers in the queue, the next one returned from |queue_->Dequeue| may
    121     // still have the old buffer dimension or format. Retry up to N+1 times or
    122     // until we dequeued a buffer with new configuration.
    123     ALOGD_IF(TRACE,
    124              "DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu "
    125              "(w=%u, h=%u, fmt=%u) is different from the buffer returned "
    126              "(w=%u, h=%u, fmt=%u). Need re-allocation.",
    127              slot, width_, height_, format_, buffer_producer->width(),
    128              buffer_producer->height(), buffer_producer->format());
    129 
    130     // Currently, we are not storing |layer_count| and |usage| in queue
    131     // configuration. Copy those setup from the last buffer dequeued before we
    132     // remove it.
    133     uint32_t old_layer_count = buffer_producer->layer_count();
    134     uint64_t old_usage = buffer_producer->usage();
    135 
    136     // Allocate a new producer buffer with new buffer configs. Note that if
    137     // there are already multiple available buffers in the queue, the next one
    138     // returned from |queue_->Dequeue| may still have the old buffer dimension
    139     // or format. Retry up to BufferHubQueue::kMaxQueueCapacity times or until
    140     // we dequeued a buffer with new configuration.
    141     auto remove_status = producer_queue_->RemoveBuffer(slot);
    142     if (!remove_status) {
    143       ALOGE("DvrWriteBufferQueue::Dequeue: Failed to remove buffer: %s",
    144             remove_status.GetErrorMessage().c_str());
    145       return -remove_status.error();
    146     }
    147     // Make sure that the previously allocated buffer is dereferenced from
    148     // write_buffers_ array.
    149     write_buffers_[slot]->write_buffer = nullptr;
    150 
    151     auto allocate_status = producer_queue_->AllocateBuffer(
    152         width_, height_, old_layer_count, format_, old_usage);
    153     if (!allocate_status) {
    154       ALOGE("DvrWriteBufferQueue::Dequeue: Failed to allocate buffer: %s",
    155             allocate_status.GetErrorMessage().c_str());
    156       return -allocate_status.error();
    157     }
    158   }
    159 
    160   if (retry >= max_retries) {
    161     ALOGE(
    162         "DvrWriteBufferQueue::Dequeue: Failed to re-allocate buffer after "
    163         "resizing.");
    164     return -ENOMEM;
    165   }
    166 
    167   *out_write_buffer = write_buffers_[slot].release();
    168   *out_fence_fd = release_fence.Release();
    169 
    170   return 0;
    171 }
    172 
    173 int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer,
    174                                     const DvrNativeBufferMetadata* meta,
    175                                     int ready_fence_fd) {
    176   LOG_FATAL_IF(
    177       (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()),
    178       "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
    179 
    180   // Some basic sanity checks before we put the buffer back into a slot.
    181   size_t slot = static_cast<size_t>(write_buffer->slot);
    182   if (write_buffers_[slot] != nullptr) {
    183     ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot);
    184     return -EINVAL;
    185   }
    186   if (write_buffer->write_buffer == nullptr) {
    187     ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer.");
    188     return -EINVAL;
    189   }
    190   if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) {
    191     ALOGE(
    192         "DvrWriteBufferQueue::PostBuffer: Buffer to be posted does not "
    193         "belong to this buffer queue. Posting buffer: id=%d, buffer in "
    194         "queue: id=%d",
    195         write_buffer->write_buffer->id(), producer_queue_->GetBufferId(slot));
    196     return -EINVAL;
    197   }
    198 
    199   write_buffer->write_buffer->SetQueueIndex(next_post_index_++);
    200   pdx::LocalHandle fence(ready_fence_fd);
    201   const int ret = write_buffer->write_buffer->PostAsync(meta, fence);
    202   if (ret < 0) {
    203     ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d",
    204           ret);
    205     return ret;
    206   }
    207 
    208   // Put the DvrWriteBuffer pointer back into its slot for reuse.
    209   write_buffers_[slot].reset(write_buffer);
    210   // It's import to reset the write buffer client now. It should stay invalid
    211   // until next GainBuffer on the same slot.
    212   write_buffers_[slot]->write_buffer = nullptr;
    213   return 0;
    214 }
    215 
    216 int DvrWriteBufferQueue::ResizeBuffer(uint32_t width, uint32_t height) {
    217   if (width == 0 || height == 0) {
    218     ALOGE(
    219         "DvrWriteBufferQueue::ResizeBuffer: invalid buffer dimension: w=%u, "
    220         "h=%u.",
    221         width, height);
    222     return -EINVAL;
    223   }
    224 
    225   width_ = width;
    226   height_ = height;
    227   return 0;
    228 }
    229 
    230 int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format,
    231                               uint32_t layer_count, uint64_t usage,
    232                               size_t capacity, size_t metadata_size,
    233                               DvrWriteBufferQueue** out_write_queue) {
    234   if (!out_write_queue)
    235     return -EINVAL;
    236 
    237   auto config_builder = ProducerQueueConfigBuilder()
    238                             .SetDefaultWidth(width)
    239                             .SetDefaultHeight(height)
    240                             .SetDefaultFormat(format)
    241                             .SetMetadataSize(metadata_size);
    242   std::unique_ptr<ProducerQueue> producer_queue =
    243       ProducerQueue::Create(config_builder.Build(), UsagePolicy{});
    244   if (!producer_queue) {
    245     ALOGE("dvrWriteBufferQueueCreate: Failed to create producer queue.");
    246     return -ENOMEM;
    247   }
    248 
    249   auto status = producer_queue->AllocateBuffers(width, height, layer_count,
    250                                                 format, usage, capacity);
    251   if (!status.ok()) {
    252     ALOGE("dvrWriteBufferQueueCreate: Failed to allocate buffers.");
    253     return -ENOMEM;
    254   }
    255 
    256   *out_write_queue = new DvrWriteBufferQueue(std::move(producer_queue));
    257   return 0;
    258 }
    259 
    260 void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) {
    261   delete write_queue;
    262 }
    263 
    264 ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) {
    265   if (!write_queue)
    266     return -EINVAL;
    267 
    268   return write_queue->capacity();
    269 }
    270 
    271 int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) {
    272   if (!write_queue)
    273     return -EINVAL;
    274 
    275   return write_queue->id();
    276 }
    277 
    278 int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
    279                                           ANativeWindow** out_window) {
    280   ALOGW(
    281       "dvrWriteBufferQueueGetExternalSurface: This API has been deprecated and "
    282       "renamed to dvrWriteBufferQueueGetANativeWindow.");
    283   return dvrWriteBufferQueueGetANativeWindow(write_queue, out_window);
    284 }
    285 
    286 int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue,
    287                                         ANativeWindow** out_window) {
    288   if (!write_queue || !out_window)
    289     return -EINVAL;
    290 
    291   return write_queue->GetNativeWindow(out_window);
    292 }
    293 
    294 int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
    295                                        DvrReadBufferQueue** out_read_queue) {
    296   if (!write_queue || !out_read_queue)
    297     return -EINVAL;
    298 
    299   return write_queue->CreateReadQueue(out_read_queue);
    300 }
    301 
    302 int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
    303                                DvrWriteBuffer* write_buffer,
    304                                int* out_fence_fd) {
    305   if (!write_queue || !write_buffer || !out_fence_fd)
    306     return -EINVAL;
    307 
    308   return write_queue->Dequeue(timeout, write_buffer, out_fence_fd);
    309 }
    310 
    311 int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout,
    312                                   DvrWriteBuffer** out_write_buffer,
    313                                   DvrNativeBufferMetadata* out_meta,
    314                                   int* out_fence_fd) {
    315   if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd)
    316     return -EINVAL;
    317 
    318   return write_queue->GainBuffer(timeout, out_write_buffer, out_meta,
    319                                  out_fence_fd);
    320 }
    321 
    322 int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue,
    323                                   DvrWriteBuffer* write_buffer,
    324                                   const DvrNativeBufferMetadata* meta,
    325                                   int ready_fence_fd) {
    326   if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta)
    327     return -EINVAL;
    328 
    329   return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd);
    330 }
    331 
    332 int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue,
    333                                     uint32_t width, uint32_t height) {
    334   if (!write_queue)
    335     return -EINVAL;
    336 
    337   return write_queue->ResizeBuffer(width, height);
    338 }
    339 
    340 // ReadBufferQueue
    341 
    342 DvrReadBufferQueue::DvrReadBufferQueue(
    343     const std::shared_ptr<ConsumerQueue>& consumer_queue)
    344     : consumer_queue_(consumer_queue) {}
    345 
    346 int DvrReadBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
    347   std::unique_ptr<ConsumerQueue> consumer_queue =
    348       consumer_queue_->CreateConsumerQueue();
    349   if (consumer_queue == nullptr) {
    350     ALOGE(
    351         "DvrReadBufferQueue::CreateReadQueue: Failed to create consumer queue "
    352         "from producer queue: queue_id=%d.", consumer_queue_->id());
    353     return -ENOMEM;
    354   }
    355 
    356   *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue));
    357   return 0;
    358 }
    359 
    360 int DvrReadBufferQueue::Dequeue(int timeout, DvrReadBuffer* read_buffer,
    361                                 int* out_fence_fd, void* out_meta,
    362                                 size_t meta_size_bytes) {
    363   if (meta_size_bytes != consumer_queue_->metadata_size()) {
    364     ALOGE(
    365         "DvrReadBufferQueue::Dequeue: Invalid metadata size, expected (%zu), "
    366         "but actual (%zu).",
    367         consumer_queue_->metadata_size(), meta_size_bytes);
    368     return -EINVAL;
    369   }
    370 
    371   size_t slot;
    372   pdx::LocalHandle acquire_fence;
    373   auto buffer_status = consumer_queue_->Dequeue(
    374       timeout, &slot, out_meta, meta_size_bytes, &acquire_fence);
    375   if (!buffer_status) {
    376     ALOGE_IF(buffer_status.error() != ETIMEDOUT,
    377              "dvrReadBufferQueueDequeue: Failed to dequeue buffer: %s",
    378              buffer_status.GetErrorMessage().c_str());
    379     return -buffer_status.error();
    380   }
    381 
    382   read_buffer->read_buffer = buffer_status.take();
    383   *out_fence_fd = acquire_fence.Release();
    384 
    385   return 0;
    386 }
    387 
    388 int DvrReadBufferQueue::AcquireBuffer(int timeout,
    389                                       DvrReadBuffer** out_read_buffer,
    390                                       DvrNativeBufferMetadata* out_meta,
    391                                       int* out_fence_fd) {
    392   size_t slot;
    393   pdx::LocalHandle acquire_fence;
    394   auto buffer_status =
    395       consumer_queue_->Dequeue(timeout, &slot, out_meta, &acquire_fence);
    396   if (!buffer_status) {
    397     ALOGE_IF(buffer_status.error() != ETIMEDOUT,
    398              "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer: %s",
    399              buffer_status.GetErrorMessage().c_str());
    400     return -buffer_status.error();
    401   }
    402 
    403   if (read_buffers_[slot] == nullptr) {
    404     // Lazy initialization of a read_buffers_ slot. Note that a slot will only
    405     // be dynamically allocated once during the entire cycle life of a queue.
    406     read_buffers_[slot] = std::make_unique<DvrReadBuffer>();
    407     read_buffers_[slot]->slot = slot;
    408   }
    409 
    410   LOG_FATAL_IF(
    411       read_buffers_[slot]->read_buffer,
    412       "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot);
    413   read_buffers_[slot]->read_buffer = std::move(buffer_status.take());
    414 
    415   *out_read_buffer = read_buffers_[slot].release();
    416   *out_fence_fd = acquire_fence.Release();
    417 
    418   return 0;
    419 }
    420 
    421 int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer,
    422                                       const DvrNativeBufferMetadata* meta,
    423                                       int release_fence_fd) {
    424   LOG_FATAL_IF(
    425       (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()),
    426       "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
    427 
    428   // Some basic sanity checks before we put the buffer back into a slot.
    429   size_t slot = static_cast<size_t>(read_buffer->slot);
    430   if (read_buffers_[slot] != nullptr) {
    431     ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot);
    432     return -EINVAL;
    433   }
    434   if (read_buffer->read_buffer == nullptr) {
    435     ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer.");
    436     return -EINVAL;
    437   }
    438   if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) {
    439     ALOGE(
    440         "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released does not "
    441         "belong to this buffer queue. Releasing buffer: id=%d, buffer in "
    442         "queue: id=%d",
    443         read_buffer->read_buffer->id(), consumer_queue_->GetBufferId(slot));
    444     return -EINVAL;
    445   }
    446 
    447   pdx::LocalHandle fence(release_fence_fd);
    448   int ret = read_buffer->read_buffer->ReleaseAsync(meta, fence);
    449   if (ret < 0) {
    450     ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d",
    451           ret);
    452     return ret;
    453   }
    454 
    455   // Put the DvrReadBuffer pointer back into its slot for reuse.
    456   read_buffers_[slot].reset(read_buffer);
    457   // It's import to reset the read buffer client now. It should stay invalid
    458   // until next AcquireBuffer on the same slot.
    459   read_buffers_[slot]->read_buffer = nullptr;
    460   return 0;
    461 }
    462 
    463 void DvrReadBufferQueue::SetBufferAvailableCallback(
    464     DvrReadBufferQueueBufferAvailableCallback callback, void* context) {
    465   if (callback == nullptr) {
    466     consumer_queue_->SetBufferAvailableCallback(nullptr);
    467   } else {
    468     consumer_queue_->SetBufferAvailableCallback(
    469         [callback, context]() { callback(context); });
    470   }
    471 }
    472 
    473 void DvrReadBufferQueue::SetBufferRemovedCallback(
    474     DvrReadBufferQueueBufferRemovedCallback callback, void* context) {
    475   if (callback == nullptr) {
    476     consumer_queue_->SetBufferRemovedCallback(nullptr);
    477   } else {
    478     consumer_queue_->SetBufferRemovedCallback(
    479         [callback, context](const std::shared_ptr<BufferHubBuffer>& buffer) {
    480           // When buffer is removed from the queue, the slot is already invalid.
    481           auto read_buffer = std::make_unique<DvrReadBuffer>();
    482           read_buffer->read_buffer =
    483               std::static_pointer_cast<BufferConsumer>(buffer);
    484           callback(read_buffer.release(), context);
    485         });
    486   }
    487 }
    488 
    489 int DvrReadBufferQueue::HandleEvents() {
    490   // TODO(jwcai) Probably should change HandleQueueEvents to return Status.
    491   consumer_queue_->HandleQueueEvents();
    492   return 0;
    493 }
    494 
    495 void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) {
    496   delete read_queue;
    497 }
    498 
    499 ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) {
    500   if (!read_queue)
    501     return -EINVAL;
    502 
    503   return read_queue->capacity();
    504 }
    505 
    506 int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) {
    507   if (!read_queue)
    508     return -EINVAL;
    509 
    510   return read_queue->id();
    511 }
    512 
    513 int dvrReadBufferQueueGetEventFd(DvrReadBufferQueue* read_queue) {
    514   if (!read_queue)
    515     return -EINVAL;
    516 
    517   return read_queue->event_fd();
    518 }
    519 
    520 int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
    521                                       DvrReadBufferQueue** out_read_queue) {
    522   if (!read_queue || !out_read_queue)
    523     return -EINVAL;
    524 
    525   return read_queue->CreateReadQueue(out_read_queue);
    526 }
    527 
    528 int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
    529                               DvrReadBuffer* read_buffer, int* out_fence_fd,
    530                               void* out_meta, size_t meta_size_bytes) {
    531   if (!read_queue || !read_buffer || !out_fence_fd)
    532     return -EINVAL;
    533 
    534   if (meta_size_bytes != 0 && !out_meta)
    535     return -EINVAL;
    536 
    537   return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta,
    538                              meta_size_bytes);
    539 }
    540 
    541 int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout,
    542                                     DvrReadBuffer** out_read_buffer,
    543                                     DvrNativeBufferMetadata* out_meta,
    544                                     int* out_fence_fd) {
    545   if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd)
    546     return -EINVAL;
    547 
    548   return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta,
    549                                    out_fence_fd);
    550 }
    551 
    552 int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue,
    553                                     DvrReadBuffer* read_buffer,
    554                                     const DvrNativeBufferMetadata* meta,
    555                                     int release_fence_fd) {
    556   if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta)
    557     return -EINVAL;
    558 
    559   return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd);
    560 }
    561 
    562 int dvrReadBufferQueueSetBufferAvailableCallback(
    563     DvrReadBufferQueue* read_queue,
    564     DvrReadBufferQueueBufferAvailableCallback callback, void* context) {
    565   if (!read_queue)
    566     return -EINVAL;
    567 
    568   read_queue->SetBufferAvailableCallback(callback, context);
    569   return 0;
    570 }
    571 
    572 int dvrReadBufferQueueSetBufferRemovedCallback(
    573     DvrReadBufferQueue* read_queue,
    574     DvrReadBufferQueueBufferRemovedCallback callback, void* context) {
    575   if (!read_queue)
    576     return -EINVAL;
    577 
    578   read_queue->SetBufferRemovedCallback(callback, context);
    579   return 0;
    580 }
    581 
    582 int dvrReadBufferQueueHandleEvents(DvrReadBufferQueue* read_queue) {
    583   if (!read_queue)
    584     return -EINVAL;
    585 
    586   return read_queue->HandleEvents();
    587 }
    588 
    589 }  // extern "C"
    590