Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: media/capture/video/chromeos/stream_buffer_manager.cc

Issue 2837273004: media: add video capture device for ARC++ camera HAL v3 (Closed)
Patch Set: set CL dependency Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/capture/video/chromeos/stream_buffer_manager.h"
6
7 #include <sync/sync.h>
8
9 #include "media/capture/video/chromeos/camera_device_context.h"
10 #include "media/capture/video/chromeos/camera_metadata_utils.h"
11 #include "media/capture/video/chromeos/pixel_format_utils.h"
12 #include "mojo/edk/embedder/embedder.h"
13 #include "mojo/edk/embedder/scoped_platform_handle.h"
14
15 namespace media {
16
17 StreamBufferManager::StreamBufferManager(
18 arc::mojom::Camera3CallbackOpsRequest callback_ops_request,
19 std::unique_ptr<StreamCaptureInterface> capture_interface,
20 CameraDeviceContext* device_context,
21 scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner)
22 : callback_ops_(this, std::move(callback_ops_request)),
23 capture_interface_(std::move(capture_interface)),
24 device_context_(device_context),
25 ipc_task_runner_(std::move(ipc_task_runner)),
26 capturing_(false),
27 frame_number_(0),
28 partial_result_count_(1),
29 first_frame_shutter_time_(base::TimeTicks::Now()) {
30 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
31 DCHECK(callback_ops_.is_bound());
32 DCHECK(device_context_);
33 }
34
35 void StreamBufferManager::SetUpStreamAndBuffers(
36 VideoCaptureFormat capture_format,
37 uint32_t partial_result_count,
38 arc::mojom::Camera3StreamPtr stream) {
39 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
40 DCHECK(!stream_context_);
41
42 VLOG(2) << "Stream " << stream->id << " configured: usage=" << stream->usage
43 << " max_buffers=" << stream->max_buffers;
44
45 const size_t kMaximumAllowedBuffers = 15;
46 if (stream->max_buffers > kMaximumAllowedBuffers) {
47 device_context_->SetErrorState(
48 FROM_HERE, std::string("Camera HAL requested ") +
49 std::to_string(stream->max_buffers) +
50 std::string(" buffers which exceeds the allowed maximum "
51 "number of buffers"));
52 return;
53 }
54
55 partial_result_count_ = partial_result_count;
56 stream_context_ = base::MakeUnique<StreamContext>();
57 stream_context_->capture_format = capture_format;
58 stream_context_->stream = std::move(stream);
59
60 // Allocate buffers.
61 size_t num_buffers = stream_context_->stream->max_buffers;
62 stream_context_->buffers.resize(num_buffers);
63 for (size_t j = 0; j < num_buffers; ++j) {
64 const VideoCaptureFormat frame_format(
65 gfx::Size(stream_context_->stream->width,
66 stream_context_->stream->height),
67 0.0, stream_context_->capture_format.pixel_format);
68 auto buffer = base::MakeUnique<base::SharedMemory>();
69 base::SharedMemoryCreateOptions options;
70 options.size = frame_format.ImageAllocationSize();
71 options.share_read_only = false;
72 bool ret = buffer->Create(options);
73 if (!ret) {
74 device_context_->SetErrorState(FROM_HERE,
75 "Failed to create SharedMemory buffer");
76 return;
77 }
78 ret = buffer->Map(buffer->requested_size());
79 if (!ret) {
80 device_context_->SetErrorState(FROM_HERE,
81 "Failed to map SharedMemory buffer");
82 return;
83 }
84 stream_context_->buffers[j] = std::move(buffer);
85 stream_context_->free_buffers.push(j);
86 }
87 VLOG(2) << "Allocated " << stream_context_->stream->max_buffers << " buffers";
88 }
89
90 void StreamBufferManager::StartCapture(arc::mojom::CameraMetadataPtr settings) {
91 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
92 DCHECK(stream_context_);
93 DCHECK(stream_context_->request_settings.is_null());
94
95 capturing_ = true;
96 stream_context_->request_settings = std::move(settings);
97 // We cannot use a loop to register all the free buffers in one shot here
98 // because the camera HAL v3 API specifies that the client cannot call
99 // ProcessCaptureRequest before the previous one returns.
100 RegisterBuffer();
101 }
102
103 void StreamBufferManager::StopCapture() {
104 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
105 capturing_ = false;
106 }
107
108 StreamBufferManager::~StreamBufferManager() {}
109
110 void StreamBufferManager::RegisterBuffer() {
111 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
112 DCHECK(stream_context_);
113
114 if (!capturing_) {
115 return;
116 }
117
118 if (stream_context_->free_buffers.empty()) {
119 return;
120 }
121
122 size_t buffer_id = stream_context_->free_buffers.front();
123 stream_context_->free_buffers.pop();
124 const base::SharedMemory* buffer = stream_context_->buffers[buffer_id].get();
125
126 VideoPixelFormat buffer_format = stream_context_->capture_format.pixel_format;
127 uint32_t drm_format = PixFormatChromiumToDrm(buffer_format);
128 if (!drm_format) {
129 device_context_->SetErrorState(
130 FROM_HERE, std::string("Unsupported video pixel format") +
131 VideoPixelFormatToString(buffer_format));
132 return;
133 }
134 arc::mojom::HalPixelFormat hal_pixel_format = stream_context_->stream->format;
135
136 size_t num_planes = VideoFrame::NumPlanes(buffer_format);
137 std::vector<mojo::ScopedHandle> fds(num_planes);
138 std::vector<uint32_t> strides(num_planes);
139 std::vector<uint32_t> offsets(num_planes);
140 for (size_t i = 0; i < num_planes; ++i) {
141 base::SharedMemoryHandle shm_handle = buffer->handle();
142 // Wrap the platform handle.
143 MojoHandle wrapped_handle;
144 MojoResult result = mojo::edk::CreatePlatformHandleWrapper(
145 mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(
146 base::SharedMemory::DuplicateHandle(shm_handle).GetHandle())),
147 &wrapped_handle);
148 if (result != MOJO_RESULT_OK) {
149 device_context_->SetErrorState(FROM_HERE,
150 "Failed to wrap shared memory handle");
151 return;
152 }
153 fds[i].reset(mojo::Handle(wrapped_handle));
154 strides[i] = VideoFrame::RowBytes(
155 i, buffer_format, stream_context_->capture_format.frame_size.width());
156 if (!i) {
157 offsets[i] = 0;
158 } else {
159 offsets[i] =
160 offsets[i - 1] +
161 VideoFrame::PlaneSize(buffer_format, i - 1,
162 stream_context_->capture_format.frame_size)
163 .GetArea();
164 }
165 }
166 capture_interface_->RegisterBuffer(
167 buffer_id, arc::mojom::Camera3DeviceOps::BufferType::SHM, std::move(fds),
168 drm_format, hal_pixel_format, stream_context_->stream->width,
169 stream_context_->stream->height, std::move(strides), std::move(offsets),
170 base::Bind(&StreamBufferManager::OnRegisteredBuffer, this, buffer_id));
171 VLOG(2) << "Registered buffer " << buffer_id;
172 }
173
174 void StreamBufferManager::OnRegisteredBuffer(size_t buffer_id, int32_t result) {
175 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
176
177 if (!capturing_) {
178 return;
179 }
180 if (result) {
181 device_context_->SetErrorState(FROM_HERE,
182 std::string("Failed to register buffer: ") +
183 std::string(strerror(result)));
184 return;
185 }
186 ProcessCaptureRequest(buffer_id);
187 }
188
189 void StreamBufferManager::ProcessCaptureRequest(size_t buffer_id) {
190 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
191 DCHECK(stream_context_);
192
193 arc::mojom::Camera3StreamBufferPtr buffer =
194 arc::mojom::Camera3StreamBuffer::New();
195 buffer->stream_id = static_cast<uint64_t>(
196 arc::mojom::Camera3RequestTemplate::CAMERA3_TEMPLATE_PREVIEW);
197 buffer->buffer_id = buffer_id;
198 buffer->status = arc::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_OK;
199
200 arc::mojom::Camera3CaptureRequestPtr request =
201 arc::mojom::Camera3CaptureRequest::New();
202 request->frame_number = frame_number_;
203 request->settings = stream_context_->request_settings.Clone();
204 request->output_buffers.push_back(std::move(buffer));
205
206 capture_interface_->ProcessCaptureRequest(
207 std::move(request),
208 base::Bind(&StreamBufferManager::OnProcessedCaptureRequest, this));
209 VLOG(2) << "Requested capture for frame " << frame_number_ << " with buffer "
210 << buffer_id;
211 frame_number_++;
212 // In case |frame_number_| wraps around, we start at 1 to avoid resetting
Pawel Osciak 2017/06/13 08:40:16 If we keep first_frame_shutter_time_ at default 0
jcliang 2017/06/14 04:46:07 Done. Good idea! I didn't realize we can have a "n
213 // |first_frame_shutter_time_|.
214 if (!frame_number_) {
215 frame_number_++;
216 }
217 }
218
219 void StreamBufferManager::OnProcessedCaptureRequest(int32_t result) {
220 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
221
222 if (!capturing_) {
223 return;
224 }
225 if (result) {
226 device_context_->SetErrorState(
227 FROM_HERE, std::string("Process capture request failed") +
228 std::string(strerror(result)));
229 return;
230 }
231 RegisterBuffer();
232 }
233
234 void StreamBufferManager::ProcessCaptureResult(
235 arc::mojom::Camera3CaptureResultPtr result) {
236 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
237
238 if (!capturing_) {
239 return;
240 }
241 uint32_t frame_number = result->frame_number;
242 // A new partial result may be created in either ProcessCaptureResult or
243 // Notify.
244 CaptureResult& partial_result = partial_results_[frame_number];
245 if (partial_results_.size() > stream_context_->stream->max_buffers) {
Pawel Osciak 2017/06/13 08:40:16 Should we check first for >=, and then grow partia
jcliang 2017/06/14 04:46:07 We can't do this since the results may be delivere
Pawel Osciak 2017/06/14 09:07:43 Acknowledged.
246 device_context_->SetErrorState(
247 FROM_HERE,
248 "Received more capture results than the maximum number of buffers");
249 return;
250 }
251 if (result->output_buffers) {
252 if (result->output_buffers->size() != 1) {
253 device_context_->SetErrorState(
254 FROM_HERE,
255 std::string("Incorrect number of output buffers received: ") +
256 std::to_string(result->output_buffers->size()));
257 return;
258 }
259 arc::mojom::Camera3StreamBufferPtr& stream_buffer =
260 result->output_buffers.value()[0];
261 VLOG(2) << "Received capture result for frame " << frame_number
262 << " stream_id: " << stream_buffer->stream_id;
263 // The camera HAL v3 API specifies that only one capture result can carry
264 // the result buffer for any given frame number.
265 if (!partial_result.buffer.is_null()) {
266 device_context_->SetErrorState(
267 FROM_HERE,
268 std::string("Received multiple result buffers for frame ") +
269 std::to_string(frame_number));
270 return;
271 } else {
272 partial_result.buffer = std::move(stream_buffer);
273 // If the buffer is marked as error it is due to either a request or a
274 // buffer error. In either case the content of the buffer must be dropped
275 // and the buffer can be reused. We simply submit the buffer here and
276 // don't wait for any partial results. SubmitCaptureResult() will drop
277 // and resuse the buffer.
278 if (partial_result.buffer->status ==
279 arc::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_ERROR) {
280 SubmitCaptureResult(frame_number);
281 return;
282 }
283 }
284 }
285
286 // |result->partial_result| is set to 0 if the capture result contains only
287 // the result buffer handles and no result metadata.
288 if (result->partial_result) {
289 uint32_t result_id = result->partial_result;
290 if (result_id > partial_result_count_) {
291 device_context_->SetErrorState(
292 FROM_HERE, std::string("Invalid partial_result id: ") +
293 std::to_string(result_id));
294 return;
295 }
296 if (partial_result.partial_metadata_received.find(result_id) !=
297 partial_result.partial_metadata_received.end()) {
298 device_context_->SetErrorState(
299 FROM_HERE, std::string("Received duplicated partial metadata: ") +
300 std::to_string(result_id));
301 return;
302 }
303 partial_result.partial_metadata_received.insert(result_id);
304 MergeMetadata(&partial_result.metadata, result->result);
305 }
306
307 if (partial_result.partial_metadata_received.size() ==
Pawel Osciak 2017/06/13 08:40:16 Perhaps move this if and the same one in Notify()
jcliang 2017/06/14 04:46:07 Done.
308 partial_result_count_ &&
309 !partial_result.buffer.is_null()) {
310 // We can only submit the result buffer after we receive the shutter time.
311 if (partial_result.reference_time != base::TimeTicks()) {
312 SubmitCaptureResult(frame_number);
313 }
314 }
315 }
316
317 void StreamBufferManager::Notify(arc::mojom::Camera3NotifyMsgPtr message) {
318 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
319
320 if (!capturing_) {
321 return;
322 }
323 if (message->type == arc::mojom::Camera3MsgType::CAMERA3_MSG_ERROR) {
324 uint32_t frame_number = message->message->get_error()->frame_number;
325 uint64_t error_stream_id = message->message->get_error()->error_stream_id;
326 arc::mojom::Camera3ErrorMsgCode error_code =
327 message->message->get_error()->error_code;
328 HandleNotifyError(frame_number, error_stream_id, error_code);
329 } else { // arc::mojom::Camera3MsgType::CAMERA3_MSG_SHUTTER
330 uint32_t frame_number = message->message->get_shutter()->frame_number;
331 uint64_t shutter_time = message->message->get_shutter()->timestamp;
332 // A new partial result may be created in either ProcessCaptureResult or
333 // Notify.
334 VLOG(2) << "Received shutter time for frame " << frame_number;
335 if (!shutter_time) {
336 device_context_->SetErrorState(
337 FROM_HERE, std::string("Received invalid shutter time: ") +
338 std::to_string(shutter_time));
339 return;
340 }
341 CaptureResult& partial_result = partial_results_[frame_number];
342 if (partial_results_.size() > stream_context_->stream->max_buffers) {
Pawel Osciak 2017/06/13 08:40:16 Should we check first for >=, and then grow partia
jcliang 2017/06/14 04:46:07 Same as the comments in ProcessCaptureResult.
343 device_context_->SetErrorState(
344 FROM_HERE,
345 "Received more capture results than the maximum number of buffers");
346 return;
347 }
348 // Shutter timestamp is in ns.
349 base::TimeTicks reference_time =
350 base::TimeTicks::FromInternalValue(shutter_time / 1000);
351 partial_result.reference_time = reference_time;
352 if (!frame_number) {
353 // Record the shutter time of the first frame for calculating the
354 // timestamp.
355 first_frame_shutter_time_ = reference_time;
356 }
357 partial_result.timestamp = reference_time - first_frame_shutter_time_;
358 if (partial_result.partial_metadata_received.size() ==
359 partial_result_count_ &&
360 !partial_result.buffer.is_null()) {
361 SubmitCaptureResult(frame_number);
362 }
363 }
364 }
365
366 void StreamBufferManager::HandleNotifyError(
367 uint32_t frame_number,
368 uint64_t error_stream_id,
369 arc::mojom::Camera3ErrorMsgCode error_code) {
370 switch (error_code) {
371 case arc::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_DEVICE:
372 // Fatal error and no more frames will be produced by the device.
373 device_context_->SetErrorState(FROM_HERE, "Fatal device error");
374 break;
375 case arc::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_REQUEST: {
376 // An error has occurred in processing the request; the request
377 // specified by |frame_number| has been dropped by the camera device.
378 // Subsequent requests are unaffected.
379 //
380 // The HAL will call ProcessCaptureResult with the buffers' state set to
381 // STATUS_ERROR. The content of the buffers will be dropped and the
382 // buffers will be reused in SubmitCaptureResult.
383 std::string warning_msg =
384 std::string("An error occurred while processing request for frame ") +
385 std::to_string(frame_number);
386 LOG(WARNING) << warning_msg;
387 device_context_->LogToClient(warning_msg);
388 break;
389 }
390 case arc::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_RESULT: {
Pawel Osciak 2017/06/13 08:40:16 Should we be dropping partial results for other er
jcliang 2017/06/14 04:46:07 For simplicity I've changed it to always drop the
391 // An error has occurred in producing the output metadata buffer for a
392 // result; the output metadata will not be available for the frame
393 // specified by |frame_number|. Subsequent requests are unaffected.
394 std::string warning_msg = std::string(
395 "An error occurred while producing result "
396 "metadata for frame ") +
397 std::to_string(frame_number);
398 LOG(WARNING) << warning_msg;
399 device_context_->LogToClient(warning_msg);
400 CaptureResult& partial_result = partial_results_[frame_number];
401 // The result metadata will not be complete so we don't need to wait for
402 // partial results on frame |frame_number|.
403 partial_result.partial_metadata_received.clear();
404 for (uint32_t i = 0; i < partial_result_count_; ++i) {
405 partial_result.partial_metadata_received.insert(i);
406 }
407 // If the buffer is already returned by the HAL, submit it and we're done.
408 if (!partial_result.buffer.is_null()) {
409 SubmitCaptureResult(frame_number);
410 }
411 break;
412 }
413 case arc::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_BUFFER:
414 // An error has occurred in placing the output buffer into a stream for
415 // a request. |frame_number| specifies the request for which the buffer
416 // was dropped, and |error_stream_id| specifies the stream that dropped
417 // the buffer.
418 //
419 // The HAL will call ProcessCaptureResult with the buffer's state set to
420 // STATUS_ERROR. The content of the buffer will be dropped and the
421 // buffer will be reused in SubmitCaptureResult.
422 device_context_->LogToClient(
423 std::string(
424 "An error occurred while filling output buffer of stream ") +
425 std::to_string(error_stream_id) + std::string(" in frame ") +
426 std::to_string(frame_number));
427 break;
428 default:
429 // To eliminate the warning for not handling CAMERA3_MSG_NUM_ERRORS
430 break;
431 }
432 }
433
434 void StreamBufferManager::SubmitCaptureResult(uint32_t frame_number) {
435 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
436
437 if (partial_results_.begin()->first != frame_number) {
438 device_context_->SetErrorState(
439 FROM_HERE, std::string("Received frame is out-of-order; expect ") +
440 std::to_string(partial_results_.begin()->first) +
441 std::string(" but got ") + std::to_string(frame_number));
442 return;
443 }
444
445 VLOG(2) << "Submit capture result of frame " << frame_number;
446 CaptureResult& partial_result = partial_results_[frame_number];
447 DCHECK(partial_result.buffer);
448 uint32_t buffer_id = partial_result.buffer->buffer_id;
449
450 // Wait on release fence before delivering the result buffer to client.
451 if (partial_result.buffer->release_fence.is_valid()) {
452 const int kSyncWaitTimeoutMs = 1000;
453 mojo::edk::ScopedPlatformHandle fence;
454 MojoResult result = mojo::edk::PassWrappedPlatformHandle(
455 partial_result.buffer->release_fence.release().value(), &fence);
456 if (result != MOJO_RESULT_OK) {
457 device_context_->SetErrorState(FROM_HERE,
458 "Failed to unwrap release fence fd");
459 return;
460 }
461 if (!sync_wait(fence.get().handle, kSyncWaitTimeoutMs)) {
462 device_context_->SetErrorState(FROM_HERE,
463 "Sync wait on release fence timed out");
464 return;
465 }
466 }
467
468 // Deliver the captured data to client and then re-queue the buffer.
469 if (partial_result.buffer->status !=
470 arc::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_ERROR) {
471 DCHECK_EQ(partial_result.partial_metadata_received.size(),
472 partial_result_count_);
473 const base::SharedMemory* shm_buffer =
474 stream_context_->buffers[buffer_id].get();
475 device_context_->SubmitCapturedData(
476 reinterpret_cast<uint8_t*>(shm_buffer->memory()),
477 shm_buffer->mapped_size(), stream_context_->capture_format,
478 partial_result.reference_time, partial_result.timestamp);
479 }
480 stream_context_->free_buffers.push(buffer_id);
481 partial_results_.erase(frame_number);
482 RegisterBuffer();
483 }
484
485 StreamBufferManager::StreamContext::StreamContext() {}
486
487 StreamBufferManager::StreamContext::~StreamContext() {}
488
489 StreamBufferManager::CaptureResult::CaptureResult()
490 : metadata(arc::mojom::CameraMetadata::New()) {}
491
492 StreamBufferManager::CaptureResult::~CaptureResult() {}
493
494 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698