Commit 70d6f68f authored by Ricky Liang's avatar Ricky Liang Committed by Commit Bot

Video capture with GpuMemoryBuffer - Handle buffer drops

When the system gets busy, it's possible for the video capture buffer
pool to run out of buffers temporarily.  Instead of raising an exception
we should handle the case gracefully, dropping a camera frame in the
worst case.

Design doc: go/cros-camera:dd:zero-copy

Bug: 982201
Change-Id: I23d2e45b8427bd36ea663410817e4361252823c3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1712844Reviewed-by: default avatarShik Chen <shik@chromium.org>
Commit-Queue: Ricky Liang <jcliang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682158}
parent 42e9bc8a
...@@ -221,6 +221,17 @@ class CameraDeviceDelegateTest : public ::testing::Test { ...@@ -221,6 +221,17 @@ class CameraDeviceDelegateTest : public ::testing::Test {
entry->data.assign(as_int8, as_int8 + entry->count * sizeof(int32_t)); entry->data.assign(as_int8, as_int8 + entry->count * sizeof(int32_t));
static_metadata->entries->push_back(std::move(entry)); static_metadata->entries->push_back(std::move(entry));
entry = cros::mojom::CameraMetadataEntry::New();
entry->index = 3;
entry->tag =
cros::mojom::CameraMetadataTag::ANDROID_REQUEST_PIPELINE_MAX_DEPTH;
entry->type = cros::mojom::EntryType::TYPE_BYTE;
entry->count = 1;
uint8_t pipeline_max_depth = 1;
entry->data.assign(&pipeline_max_depth,
&pipeline_max_depth + entry->count * sizeof(uint8_t));
static_metadata->entries->push_back(std::move(entry));
switch (camera_id) { switch (camera_id) {
case 0: case 0:
camera_info->facing = cros::mojom::CameraFacing::CAMERA_FACING_FRONT; camera_info->facing = cros::mojom::CameraFacing::CAMERA_FACING_FRONT;
......
...@@ -89,6 +89,13 @@ void RequestManager::SetUpStreamsAndBuffers( ...@@ -89,6 +89,13 @@ void RequestManager::SetUpStreamsAndBuffers(
*reinterpret_cast<int32_t*>((*partial_count)->data.data()); *reinterpret_cast<int32_t*>((*partial_count)->data.data());
} }
auto pipeline_depth = GetMetadataEntryAsSpan<uint8_t>(
static_metadata,
cros::mojom::CameraMetadataTag::ANDROID_REQUEST_PIPELINE_MAX_DEPTH);
CHECK_EQ(pipeline_depth.size(), 1u);
pipeline_depth_ = pipeline_depth[0];
preview_buffers_queued_ = 0;
// Set the last received frame number for each stream types to be undefined. // Set the last received frame number for each stream types to be undefined.
for (const auto& stream : streams) { for (const auto& stream : streams) {
StreamType stream_type = StreamIdToStreamType(stream->id); StreamType stream_type = StreamIdToStreamType(stream->id);
...@@ -96,7 +103,7 @@ void RequestManager::SetUpStreamsAndBuffers( ...@@ -96,7 +103,7 @@ void RequestManager::SetUpStreamsAndBuffers(
} }
stream_buffer_manager_->SetUpStreamsAndBuffers( stream_buffer_manager_->SetUpStreamsAndBuffers(
capture_format, std::move(static_metadata), std::move(streams)); capture_format, static_metadata, std::move(streams));
} }
cros::mojom::Camera3StreamPtr RequestManager::GetStreamConfiguration( cros::mojom::Camera3StreamPtr RequestManager::GetStreamConfiguration(
...@@ -294,11 +301,18 @@ void RequestManager::PrepareCaptureRequest() { ...@@ -294,11 +301,18 @@ void RequestManager::PrepareCaptureRequest() {
} }
if (!is_reprocess_request && !is_oneshot_request && !is_preview_request) { if (!is_reprocess_request && !is_oneshot_request && !is_preview_request) {
// We have to keep the pipeline full.
if (preview_buffers_queued_ < pipeline_depth_) {
ipc_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&RequestManager::PrepareCaptureRequest, GetWeakPtr()));
}
return; return;
} }
auto capture_request = request_builder_->BuildRequest( auto capture_request = request_builder_->BuildRequest(
std::move(stream_types), std::move(settings), input_buffer_id); std::move(stream_types), std::move(settings), input_buffer_id);
CHECK_GT(capture_request->output_buffers.size(), 0u);
CaptureResult& pending_result = CaptureResult& pending_result =
pending_results_[capture_request->frame_number]; pending_results_[capture_request->frame_number];
...@@ -318,6 +332,10 @@ void RequestManager::PrepareCaptureRequest() { ...@@ -318,6 +332,10 @@ void RequestManager::PrepareCaptureRequest() {
pending_reprocess_tasks_queue_.pop(); pending_reprocess_tasks_queue_.pop();
} }
if (is_preview_request) {
++preview_buffers_queued_;
}
UpdateCaptureSettings(&capture_request->settings); UpdateCaptureSettings(&capture_request->settings);
capture_interface_->ProcessCaptureRequest( capture_interface_->ProcessCaptureRequest(
std::move(capture_request), std::move(capture_request),
...@@ -375,9 +393,19 @@ bool RequestManager::TryPrepareReprocessRequest( ...@@ -375,9 +393,19 @@ bool RequestManager::TryPrepareReprocessRequest(
bool RequestManager::TryPreparePreviewRequest( bool RequestManager::TryPreparePreviewRequest(
std::set<StreamType>* stream_types, std::set<StreamType>* stream_types,
cros::mojom::CameraMetadataPtr* settings) { cros::mojom::CameraMetadataPtr* settings) {
if (!stream_buffer_manager_->HasFreeBuffers({StreamType::kPreviewOutput})) { if (preview_buffers_queued_ == pipeline_depth_) {
return false; return false;
} }
if (!stream_buffer_manager_->HasFreeBuffers({StreamType::kPreviewOutput})) {
// Try our best to reserve an usable buffer. If the reservation still
// fails, then we'd have to drop the camera frame.
DLOG(WARNING) << "Late request for reserving preview buffer";
stream_buffer_manager_->ReserveBuffer(StreamType::kPreviewOutput);
if (!stream_buffer_manager_->HasFreeBuffers({StreamType::kPreviewOutput})) {
DLOG(WARNING) << "No free buffer for preview stream";
return false;
}
}
stream_types->insert({StreamType::kPreviewOutput}); stream_types->insert({StreamType::kPreviewOutput});
*settings = repeating_request_settings_.Clone(); *settings = repeating_request_settings_.Clone();
...@@ -767,6 +795,11 @@ void RequestManager::SubmitCaptureResult( ...@@ -767,6 +795,11 @@ void RequestManager::SubmitCaptureResult(
stream_buffer_manager_->ReleaseBufferFromCaptureResult(stream_type, stream_buffer_manager_->ReleaseBufferFromCaptureResult(stream_type,
buffer_ipc_id); buffer_ipc_id);
} }
if (stream_type == StreamType::kPreviewOutput) {
--preview_buffers_queued_;
}
pending_result.unsubmitted_buffer_count--; pending_result.unsubmitted_buffer_count--;
if (pending_result.unsubmitted_buffer_count == 0) { if (pending_result.unsubmitted_buffer_count == 0) {
......
...@@ -302,6 +302,15 @@ class CAPTURE_EXPORT RequestManager final ...@@ -302,6 +302,15 @@ class CAPTURE_EXPORT RequestManager final
// shot. // shot.
uint32_t partial_result_count_; uint32_t partial_result_count_;
// The pipeline depth reported in the ANDROID_REQUEST_PIPELINE_MAX_DEPTH
// metadata.
size_t pipeline_depth_;
// The number of preview buffers queued to the camera service. The request
// manager needs to try its best to queue |pipeline_depth_| preview buffers to
// avoid camera frame drops.
size_t preview_buffers_queued_;
// The shutter time of the first frame. We derive the |timestamp| of a // The shutter time of the first frame. We derive the |timestamp| of a
// frame using the difference between the frame's shutter time and // frame using the difference between the frame's shutter time and
// |first_frame_shutter_time_|. // |first_frame_shutter_time_|.
......
...@@ -146,6 +146,17 @@ class RequestManagerTest : public ::testing::Test { ...@@ -146,6 +146,17 @@ class RequestManagerTest : public ::testing::Test {
entry->data.assign(as_int8, as_int8 + entry->count * sizeof(int32_t)); entry->data.assign(as_int8, as_int8 + entry->count * sizeof(int32_t));
static_metadata->entries->push_back(std::move(entry)); static_metadata->entries->push_back(std::move(entry));
entry = cros::mojom::CameraMetadataEntry::New();
entry->index = 2;
entry->tag =
cros::mojom::CameraMetadataTag::ANDROID_REQUEST_PIPELINE_MAX_DEPTH;
entry->type = cros::mojom::EntryType::TYPE_BYTE;
entry->count = 1;
uint8_t pipeline_max_depth = 1;
entry->data.assign(&pipeline_max_depth,
&pipeline_max_depth + entry->count * sizeof(uint8_t));
static_metadata->entries->push_back(std::move(entry));
return static_metadata; return static_metadata;
} }
......
...@@ -175,6 +175,8 @@ void StreamBufferManager::SetUpStreamsAndBuffers( ...@@ -175,6 +175,8 @@ void StreamBufferManager::SetUpStreamsAndBuffers(
++j) { ++j) {
ReserveBuffer(stream_type); ReserveBuffer(stream_type);
} }
CHECK_EQ(stream_context_[stream_type]->free_buffers.size(),
stream_context_[stream_type]->stream->max_buffers);
DVLOG(2) << "Allocated " DVLOG(2) << "Allocated "
<< stream_context_[stream_type]->stream->max_buffers << " buffers"; << stream_context_[stream_type]->stream->max_buffers << " buffers";
} }
...@@ -317,10 +319,7 @@ void StreamBufferManager::ReserveBufferFromPool(StreamType stream_type) { ...@@ -317,10 +319,7 @@ void StreamBufferManager::ReserveBufferFromPool(StreamType stream_type) {
if (!device_context_->ReserveVideoCaptureBufferFromPool( if (!device_context_->ReserveVideoCaptureBufferFromPool(
stream_context->buffer_dimension, stream_context->buffer_dimension,
stream_context->capture_format.pixel_format, &vcd_buffer)) { stream_context->capture_format.pixel_format, &vcd_buffer)) {
device_context_->SetErrorState( DLOG(WARNING) << "Failed to reserve video capture buffer";
media::VideoCaptureError::
kCrosHalV3BufferManagerFailedToCreateGpuMemoryBuffer,
FROM_HERE, "Failed to reserve video capture buffer");
return; return;
} }
auto gmb = gmb_support_->CreateGpuMemoryBufferImplFromHandle( auto gmb = gmb_support_->CreateGpuMemoryBufferImplFromHandle(
......
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