Commit c74dcba7 authored by Chih-Yu Huang's avatar Chih-Yu Huang Committed by Commit Bot

media/gpu: make V4L2VEA be able to notify flush done without bitstream buffer.

Originally, we enqueue V4L2 CAPTURE buffers when receiving bitstream
buffers from the client. Also, V4L2 driver might need an extra empty
buffer to notify flush is done. That means, if we don't get an extra
bitstream buffer, we cannot know when the flush is done.

This CL separates the logic of enqueueing/dequeueing V4L2 CAPTURE
buffers and receiving/returning the bitstream buffer. With this CL, we
could get the extra V4L2 CAPTURE buffer even there is no bitstream
buffer.

BUG=chromium:990687, b:141285506
TEST=run VEA unittest on kevin with h264 and vp8 codec

Change-Id: I1d116f0df0d9c4406c84099774c0e7ae5d0dffc4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1824303
Commit-Queue: Chih-Yu Huang <akahuang@chromium.org>
Reviewed-by: default avatarAlexandre Courbot <acourbot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703209}
parent 3f78d6a0
...@@ -109,10 +109,6 @@ V4L2VideoEncodeAccelerator::InputRecord::InputRecord(const InputRecord&) = ...@@ -109,10 +109,6 @@ V4L2VideoEncodeAccelerator::InputRecord::InputRecord(const InputRecord&) =
V4L2VideoEncodeAccelerator::InputRecord::~InputRecord() = default; V4L2VideoEncodeAccelerator::InputRecord::~InputRecord() = default;
V4L2VideoEncodeAccelerator::OutputRecord::OutputRecord() = default;
V4L2VideoEncodeAccelerator::OutputRecord::~OutputRecord() = default;
V4L2VideoEncodeAccelerator::InputFrameInfo::InputFrameInfo() V4L2VideoEncodeAccelerator::InputFrameInfo::InputFrameInfo()
: InputFrameInfo(nullptr, false) {} : InputFrameInfo(nullptr, false) {}
...@@ -767,9 +763,9 @@ void V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask( ...@@ -767,9 +763,9 @@ void V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask(
return; return;
} }
encoder_output_queue_.push_back( bitstream_buffer_pool_.push_back(
std::make_unique<BitstreamBufferRef>(buffer.id(), std::move(shm))); std::make_unique<BitstreamBufferRef>(buffer.id(), std::move(shm)));
Enqueue(); PumpBitstreamBuffers();
if (encoder_state_ == kInitialized) { if (encoder_state_ == kInitialized) {
if (!StartDevicePoll()) if (!StartDevicePoll())
...@@ -841,7 +837,7 @@ void V4L2VideoEncodeAccelerator::ServiceDeviceTask() { ...@@ -841,7 +837,7 @@ void V4L2VideoEncodeAccelerator::ServiceDeviceTask() {
<< output_queue_->FreeBuffersCount() << "+" << output_queue_->FreeBuffersCount() << "+"
<< output_queue_->QueuedBuffersCount() << "/" << output_queue_->QueuedBuffersCount() << "/"
<< output_queue_->AllocatedBuffersCount() << "] => OUT[" << output_queue_->AllocatedBuffersCount() << "] => OUT["
<< encoder_output_queue_.size() << "]"; << bitstream_buffer_pool_.size() << "]";
} }
void V4L2VideoEncodeAccelerator::Enqueue() { void V4L2VideoEncodeAccelerator::Enqueue() {
...@@ -903,8 +899,7 @@ void V4L2VideoEncodeAccelerator::Enqueue() { ...@@ -903,8 +899,7 @@ void V4L2VideoEncodeAccelerator::Enqueue() {
// Enqueue all the outputs we can. // Enqueue all the outputs we can.
const size_t old_outputs_queued = output_queue_->QueuedBuffersCount(); const size_t old_outputs_queued = output_queue_->QueuedBuffersCount();
while (output_queue_->FreeBuffersCount() > 0 && while (output_queue_->FreeBuffersCount() > 0) {
!encoder_output_queue_.empty()) {
if (!EnqueueOutputRecord()) if (!EnqueueOutputRecord())
return; return;
} }
...@@ -959,6 +954,7 @@ void V4L2VideoEncodeAccelerator::Dequeue() { ...@@ -959,6 +954,7 @@ void V4L2VideoEncodeAccelerator::Dequeue() {
// Dequeue completed output (VIDEO_CAPTURE) buffers, and recycle to the // Dequeue completed output (VIDEO_CAPTURE) buffers, and recycle to the
// free list. Notify the client that an output buffer is complete. // free list. Notify the client that an output buffer is complete.
bool buffer_dequeued = false;
while (output_queue_->QueuedBuffersCount() > 0) { while (output_queue_->QueuedBuffersCount() > 0) {
DCHECK(output_queue_->IsStreaming()); DCHECK(output_queue_->IsStreaming());
...@@ -972,30 +968,53 @@ void V4L2VideoEncodeAccelerator::Dequeue() { ...@@ -972,30 +968,53 @@ void V4L2VideoEncodeAccelerator::Dequeue() {
break; break;
} }
V4L2ReadableBufferRef output_buf = std::move(ret.second); output_buffer_queue_.push_back(std::move(ret.second));
OutputRecord& output_record = output_buffer_map_[output_buf->BufferId()]; buffer_dequeued = true;
DCHECK(output_record.buffer_ref); }
int32_t bitstream_buffer_id = output_record.buffer_ref->id;
size_t output_data_size = CopyIntoOutputBuffer( if (buffer_dequeued)
static_cast<const uint8_t*>(output_buf->GetPlaneMapping(0)) + PumpBitstreamBuffers();
output_buf->GetPlaneDataOffset(0), }
base::checked_cast<size_t>(output_buf->GetPlaneBytesUsed(0) -
output_buf->GetPlaneDataOffset(0)), void V4L2VideoEncodeAccelerator::PumpBitstreamBuffers() {
std::move(output_record.buffer_ref)); DVLOGF(4);
DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
DVLOGF(4) << "returning bitstream_buffer_id=" << bitstream_buffer_id
<< ", size=" << output_data_size while (!output_buffer_queue_.empty()) {
<< ", key_frame=" << output_buf->IsKeyframe(); auto output_buf = std::move(output_buffer_queue_.front());
child_task_runner_->PostTask( output_buffer_queue_.pop_front();
FROM_HERE,
base::BindOnce(&Client::BitstreamBufferReady, client_, size_t bitstream_size = base::checked_cast<size_t>(
bitstream_buffer_id, output_buf->GetPlaneBytesUsed(0) - output_buf->GetPlaneDataOffset(0));
BitstreamBufferMetadata( if (bitstream_size > 0) {
output_data_size, output_buf->IsKeyframe(), if (bitstream_buffer_pool_.empty()) {
base::TimeDelta::FromMicroseconds( DVLOGF(4) << "No free bitstream buffer, skip.";
output_buf->GetTimeStamp().tv_usec + output_buffer_queue_.push_front(std::move(output_buf));
output_buf->GetTimeStamp().tv_sec * return;
base::Time::kMicrosecondsPerSecond)))); }
auto buffer_ref = std::move(bitstream_buffer_pool_.back());
auto buffer_id = buffer_ref->id;
bitstream_buffer_pool_.pop_back();
size_t output_data_size = CopyIntoOutputBuffer(
static_cast<const uint8_t*>(output_buf->GetPlaneMapping(0)) +
output_buf->GetPlaneDataOffset(0),
bitstream_size, std::move(buffer_ref));
DVLOGF(4) << "returning buffer_id=" << buffer_id
<< ", size=" << output_data_size
<< ", key_frame=" << output_buf->IsKeyframe();
child_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&Client::BitstreamBufferReady, client_, buffer_id,
BitstreamBufferMetadata(
output_data_size, output_buf->IsKeyframe(),
base::TimeDelta::FromMicroseconds(
output_buf->GetTimeStamp().tv_usec +
output_buf->GetTimeStamp().tv_sec *
base::Time::kMicrosecondsPerSecond))));
}
if ((encoder_state_ == kFlushing) && output_buf->IsLast()) { if ((encoder_state_ == kFlushing) && output_buf->IsLast()) {
// Notify client that flush has finished successfully. The flush callback // Notify client that flush has finished successfully. The flush callback
...@@ -1138,20 +1157,15 @@ bool V4L2VideoEncodeAccelerator::EnqueueOutputRecord() { ...@@ -1138,20 +1157,15 @@ bool V4L2VideoEncodeAccelerator::EnqueueOutputRecord() {
DVLOGF(4); DVLOGF(4);
DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
DCHECK_GT(output_queue_->FreeBuffersCount(), 0u); DCHECK_GT(output_queue_->FreeBuffersCount(), 0u);
DCHECK(!encoder_output_queue_.empty());
TRACE_EVENT0("media,gpu", "V4L2VEA::EnqueueOutputRecord"); TRACE_EVENT0("media,gpu", "V4L2VEA::EnqueueOutputRecord");
// Enqueue an output (VIDEO_CAPTURE) buffer. // Enqueue an output (VIDEO_CAPTURE) buffer.
V4L2WritableBufferRef output_buf = output_queue_->GetFreeBuffer(); V4L2WritableBufferRef output_buf = output_queue_->GetFreeBuffer();
DCHECK(output_buf.IsValid()); DCHECK(output_buf.IsValid());
OutputRecord& output_record = output_buffer_map_[output_buf.BufferId()];
DCHECK(!output_record.buffer_ref);
if (!std::move(output_buf).QueueMMap()) { if (!std::move(output_buf).QueueMMap()) {
VLOGF(1) << "Failed to QueueMMap."; VLOGF(1) << "Failed to QueueMMap.";
return false; return false;
} }
output_record.buffer_ref = std::move(encoder_output_queue_.back());
encoder_output_queue_.pop_back();
return true; return true;
} }
...@@ -1203,10 +1217,7 @@ bool V4L2VideoEncodeAccelerator::StopDevicePoll() { ...@@ -1203,10 +1217,7 @@ bool V4L2VideoEncodeAccelerator::StopDevicePoll() {
input_record.frame = nullptr; input_record.frame = nullptr;
} }
for (auto& output_record : output_buffer_map_) bitstream_buffer_pool_.clear();
output_record.buffer_ref.reset();
encoder_output_queue_.clear();
DVLOGF(3) << "device poll stopped"; DVLOGF(3) << "device poll stopped";
return true; return true;
...@@ -1623,10 +1634,6 @@ bool V4L2VideoEncodeAccelerator::CreateOutputBuffers() { ...@@ -1623,10 +1634,6 @@ bool V4L2VideoEncodeAccelerator::CreateOutputBuffers() {
VLOGF(1) << "Failed to allocate V4L2 output buffers."; VLOGF(1) << "Failed to allocate V4L2 output buffers.";
return false; return false;
} }
DCHECK(output_buffer_map_.empty());
output_buffer_map_ =
std::vector<OutputRecord>(output_queue_->AllocatedBuffersCount());
return true; return true;
} }
...@@ -1651,7 +1658,6 @@ void V4L2VideoEncodeAccelerator::DestroyOutputBuffers() { ...@@ -1651,7 +1658,6 @@ void V4L2VideoEncodeAccelerator::DestroyOutputBuffers() {
DCHECK(!output_queue_->IsStreaming()); DCHECK(!output_queue_->IsStreaming());
output_queue_->DeallocateBuffers(); output_queue_->DeallocateBuffers();
output_buffer_map_.clear();
} }
} // namespace media } // namespace media
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "base/containers/circular_deque.h"
#include "base/containers/queue.h" #include "base/containers/queue.h"
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/macros.h" #include "base/macros.h"
...@@ -75,14 +76,6 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator ...@@ -75,14 +76,6 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator
base::Optional<size_t> ip_output_buffer_index; base::Optional<size_t> ip_output_buffer_index;
}; };
// Record for output buffers.
struct OutputRecord {
OutputRecord();
~OutputRecord();
std::unique_ptr<BitstreamBufferRef> buffer_ref;
};
// Store all the information of input frame passed to Encode(). // Store all the information of input frame passed to Encode().
struct InputFrameInfo { struct InputFrameInfo {
InputFrameInfo(); InputFrameInfo();
...@@ -143,6 +136,9 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator ...@@ -143,6 +136,9 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator
// Device destruction task. // Device destruction task.
void DestroyTask(); void DestroyTask();
// Try to output bitstream buffers.
void PumpBitstreamBuffers();
// Flush all the encoded frames. After all frames is flushed successfully or // Flush all the encoded frames. After all frames is flushed successfully or
// any error occurs, |flush_callback| will be called to notify client. // any error occurs, |flush_callback| will be called to notify client.
void FlushTask(FlushCallback flush_callback); void FlushTask(FlushCallback flush_callback);
...@@ -309,12 +305,14 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator ...@@ -309,12 +305,14 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator
scoped_refptr<V4L2Queue> input_queue_; scoped_refptr<V4L2Queue> input_queue_;
scoped_refptr<V4L2Queue> output_queue_; scoped_refptr<V4L2Queue> output_queue_;
// Mapping of int index to output buffer record.
std::vector<OutputRecord> output_buffer_map_;
// Bitstream buffers ready to be used to return encoded output, as a LIFO // Bitstream buffers ready to be used to return encoded output, as a LIFO
// since we don't care about ordering. // since we don't care about ordering.
std::vector<std::unique_ptr<BitstreamBufferRef>> encoder_output_queue_; std::vector<std::unique_ptr<BitstreamBufferRef>> bitstream_buffer_pool_;
// Queue of encoded bitstream V4L2 buffers. We enqueue the encoded buffers
// from V4L2 devices, and copy the data to the bitstream buffers passed from
// the client via UseOutputBitstreamBuffer().
base::circular_deque<V4L2ReadableBufferRef> output_buffer_queue_;
// The completion callback of the Flush() function. // The completion callback of the Flush() function.
FlushCallback flush_callback_; FlushCallback flush_callback_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment