1 // Copyright 2018 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 "content/browser/media/capture/frame_sink_video_capture_device.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/containers/flat_map.h"
12 #include "base/memory/read_only_shared_memory_region.h"
13 #include "base/memory/shared_memory_mapping.h"
14 #include "base/task/post_task.h"
15 #include "content/public/browser/browser_task_traits.h"
16 #include "content/public/test/browser_task_environment.h"
17 #include "content/public/test/test_utils.h"
18 #include "media/base/video_frame.h"
19 #include "media/capture/video/video_frame_receiver.h"
20 #include "media/capture/video_capture_types.h"
21 #include "mojo/public/cpp/bindings/pending_receiver.h"
22 #include "mojo/public/cpp/bindings/pending_remote.h"
23 #include "mojo/public/cpp/bindings/receiver.h"
24 #include "mojo/public/cpp/bindings/remote.h"
25 #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "ui/gfx/geometry/size.h"
29 #include "ui/gfx/native_widget_types.h"
30
31 using testing::_;
32 using testing::ByRef;
33 using testing::Eq;
34 using testing::Expectation;
35 using testing::Ge;
36 using testing::NiceMock;
37 using testing::NotNull;
38 using testing::SaveArg;
39 using testing::Sequence;
40 using testing::StrNe;
41
42 namespace content {
43 namespace {
44
45 // Threading notes: Throughout these tests, the UI thread (the main test
46 // thread) represents the executor of all external-to-device operations. This
47 // means that it represents everything that runs on the UI thread in the browser
48 // process, plus anything that would run in the VIZ process. The IO thread is
49 // used as the "device thread" for content::FrameSinkVideoCaptureDevice.
50 #define DCHECK_ON_DEVICE_THREAD() DCHECK_CURRENTLY_ON(BrowserThread::IO)
51 #define DCHECK_NOT_ON_DEVICE_THREAD() DCHECK_CURRENTLY_ON(BrowserThread::UI)
52
53 // Convenience macro to block the test procedure and run all pending UI tasks.
54 #define RUN_UI_TASKS() base::RunLoop().RunUntilIdle()
55
56 // Convenience macro to post a task to run on the device thread.
57 #define POST_DEVICE_TASK(closure) \
58 base::PostTask(FROM_HERE, {BrowserThread::IO}, closure)
59
60 // Convenience macro to block the test procedure until all pending tasks have
61 // run on the device thread.
62 #define WAIT_FOR_DEVICE_TASKS() \
63 task_environment_.RunIOThreadUntilIdle(); \
64 RUN_UI_TASKS()
65
66 // Capture parameters.
67 constexpr gfx::Size kResolution = gfx::Size(320, 180);
68 constexpr int kMaxFrameRate = 25; // It evenly divides 1 million usec.
69 constexpr base::TimeDelta kMinCapturePeriod = base::TimeDelta::FromMicroseconds(
70 base::Time::kMicrosecondsPerSecond / kMaxFrameRate);
71 constexpr media::VideoPixelFormat kFormat = media::PIXEL_FORMAT_I420;
72
73 // Helper to return the capture parameters packaged in a VideoCaptureParams.
GetCaptureParams()74 media::VideoCaptureParams GetCaptureParams() {
75 media::VideoCaptureParams params;
76 params.requested_format =
77 media::VideoCaptureFormat(kResolution, kMaxFrameRate, kFormat);
78 return params;
79 }
80
81 // Mock for the FrameSinkVideoCapturer running in the VIZ process.
82 class MockFrameSinkVideoCapturer : public viz::mojom::FrameSinkVideoCapturer {
83 public:
84 MockFrameSinkVideoCapturer() = default;
85
is_bound() const86 bool is_bound() const { return receiver_.is_bound(); }
87
Bind(mojo::PendingReceiver<viz::mojom::FrameSinkVideoCapturer> receiver)88 void Bind(
89 mojo::PendingReceiver<viz::mojom::FrameSinkVideoCapturer> receiver) {
90 DCHECK_NOT_ON_DEVICE_THREAD();
91 receiver_.Bind(std::move(receiver));
92 }
93
94 MOCK_METHOD2(SetFormat,
95 void(media::VideoPixelFormat format,
96 const gfx::ColorSpace& color_space));
97 MOCK_METHOD1(SetMinCapturePeriod, void(base::TimeDelta min_period));
98 MOCK_METHOD1(SetMinSizeChangePeriod, void(base::TimeDelta));
99 MOCK_METHOD3(SetResolutionConstraints,
100 void(const gfx::Size& min_size,
101 const gfx::Size& max_size,
102 bool use_fixed_aspect_ratio));
103 MOCK_METHOD1(SetAutoThrottlingEnabled, void(bool));
ChangeTarget(const base::Optional<viz::FrameSinkId> & frame_sink_id)104 void ChangeTarget(
105 const base::Optional<viz::FrameSinkId>& frame_sink_id) final {
106 DCHECK_NOT_ON_DEVICE_THREAD();
107 MockChangeTarget(frame_sink_id ? *frame_sink_id : viz::FrameSinkId());
108 }
109 MOCK_METHOD1(MockChangeTarget, void(const viz::FrameSinkId& frame_sink_id));
Start(mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumer> consumer)110 void Start(
111 mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumer> consumer) final {
112 DCHECK_NOT_ON_DEVICE_THREAD();
113 consumer_.Bind(std::move(consumer));
114 MockStart(consumer_.get());
115 }
116 MOCK_METHOD1(MockStart, void(viz::mojom::FrameSinkVideoConsumer* consumer));
Stop()117 void Stop() final {
118 DCHECK_NOT_ON_DEVICE_THREAD();
119 consumer_.reset();
120 MockStop();
121 }
122 MOCK_METHOD0(MockStop, void());
123 MOCK_METHOD0(RequestRefreshFrame, void());
124 MOCK_METHOD2(
125 CreateOverlay,
126 void(int32_t stacking_index,
127 mojo::PendingReceiver<viz::mojom::FrameSinkVideoCaptureOverlay>
128 receiver));
129
130 private:
131 mojo::Receiver<viz::mojom::FrameSinkVideoCapturer> receiver_{this};
132 mojo::Remote<viz::mojom::FrameSinkVideoConsumer> consumer_;
133 };
134
135 // Represents the FrameSinkVideoConsumerFrameCallbacks instance in the VIZ
136 // process.
137 class MockFrameSinkVideoConsumerFrameCallbacks
138 : public viz::mojom::FrameSinkVideoConsumerFrameCallbacks {
139 public:
140 MockFrameSinkVideoConsumerFrameCallbacks() = default;
141
Bind(mojo::PendingReceiver<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> receiver)142 void Bind(
143 mojo::PendingReceiver<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
144 receiver) {
145 DCHECK_NOT_ON_DEVICE_THREAD();
146 receiver_.Bind(std::move(receiver));
147 }
148
149 MOCK_METHOD0(Done, void());
150 MOCK_METHOD1(ProvideFeedback, void(double utilization));
151
152 private:
153 mojo::Receiver<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> receiver_{
154 this};
155 };
156
157 // Mock for the VideoFrameReceiver, the point-of-injection of video frames into
158 // the video capture stack. It's mocked methods are called on the device thread.
159 // Some methods stash objects of interest, which test code must grab via the
160 // TakeXYZ() utility methods (called on the main thread).
161 class MockVideoFrameReceiver : public media::VideoFrameReceiver {
162 public:
163 using Buffer = media::VideoCaptureDevice::Client::Buffer;
164
~MockVideoFrameReceiver()165 ~MockVideoFrameReceiver() override {
166 DCHECK_ON_DEVICE_THREAD();
167 EXPECT_TRUE(buffer_handles_.empty());
168 EXPECT_TRUE(feedback_ids_.empty());
169 EXPECT_TRUE(access_permissions_.empty());
170 EXPECT_TRUE(frame_infos_.empty());
171 }
172
OnNewBuffer(int buffer_id,media::mojom::VideoBufferHandlePtr buffer_handle)173 void OnNewBuffer(int buffer_id,
174 media::mojom::VideoBufferHandlePtr buffer_handle) final {
175 DCHECK_ON_DEVICE_THREAD();
176 auto* const raw_pointer = buffer_handle.get();
177 buffer_handles_[buffer_id] = std::move(buffer_handle);
178 MockOnNewBuffer(buffer_id, raw_pointer);
179 }
180 MOCK_METHOD2(MockOnNewBuffer,
181 void(int buffer_id,
182 media::mojom::VideoBufferHandle* buffer_handle));
OnFrameReadyInBuffer(int buffer_id,int frame_feedback_id,std::unique_ptr<Buffer::ScopedAccessPermission> buffer_read_permission,media::mojom::VideoFrameInfoPtr frame_info)183 void OnFrameReadyInBuffer(
184 int buffer_id,
185 int frame_feedback_id,
186 std::unique_ptr<Buffer::ScopedAccessPermission> buffer_read_permission,
187 media::mojom::VideoFrameInfoPtr frame_info) final {
188 DCHECK_ON_DEVICE_THREAD();
189 feedback_ids_[buffer_id] = frame_feedback_id;
190 auto* const raw_pointer_to_permission = buffer_read_permission.get();
191 access_permissions_[buffer_id] = std::move(buffer_read_permission);
192 auto* const raw_pointer_to_info = frame_info.get();
193 frame_infos_[buffer_id] = std::move(frame_info);
194 MockOnFrameReadyInBuffer(buffer_id, frame_feedback_id,
195 raw_pointer_to_permission, raw_pointer_to_info);
196 }
197 MOCK_METHOD4(MockOnFrameReadyInBuffer,
198 void(int buffer_id,
199 int frame_feedback_id,
200 Buffer::ScopedAccessPermission* buffer_read_permission,
201 const media::mojom::VideoFrameInfo* frame_info));
202 MOCK_METHOD1(OnBufferRetired, void(int buffer_id));
203 MOCK_METHOD1(OnError, void(media::VideoCaptureError error));
204 MOCK_METHOD1(OnFrameDropped, void(media::VideoCaptureFrameDropReason reason));
205 MOCK_METHOD1(OnLog, void(const std::string& message));
206 MOCK_METHOD0(OnStarted, void());
207 MOCK_METHOD0(OnStopped, void());
OnStartedUsingGpuDecode()208 void OnStartedUsingGpuDecode() final { NOTREACHED(); }
209
TakeBufferHandle(int buffer_id)210 base::ReadOnlySharedMemoryRegion TakeBufferHandle(int buffer_id) {
211 DCHECK_NOT_ON_DEVICE_THREAD();
212 const auto it = buffer_handles_.find(buffer_id);
213 if (it == buffer_handles_.end()) {
214 ADD_FAILURE() << "Missing entry for buffer_id=" << buffer_id;
215 return base::ReadOnlySharedMemoryRegion();
216 }
217 CHECK(it->second->is_read_only_shmem_region());
218 auto buffer = std::move(it->second->get_read_only_shmem_region());
219 buffer_handles_.erase(it);
220 return buffer;
221 }
222
TakeFeedbackId(int buffer_id)223 int TakeFeedbackId(int buffer_id) {
224 DCHECK_NOT_ON_DEVICE_THREAD();
225 const auto it = feedback_ids_.find(buffer_id);
226 if (it == feedback_ids_.end()) {
227 ADD_FAILURE() << "Missing entry for buffer_id=" << buffer_id;
228 return -1;
229 }
230 const int feedback_id = it->second;
231 feedback_ids_.erase(it);
232 return feedback_id;
233 }
234
ReleaseAccessPermission(int buffer_id)235 void ReleaseAccessPermission(int buffer_id) {
236 DCHECK_NOT_ON_DEVICE_THREAD();
237 const auto it = access_permissions_.find(buffer_id);
238 if (it == access_permissions_.end()) {
239 ADD_FAILURE() << "Missing entry for buffer_id=" << buffer_id;
240 return;
241 }
242 access_permissions_.erase(it);
243 }
244
TakeVideoFrameInfo(int buffer_id)245 media::mojom::VideoFrameInfoPtr TakeVideoFrameInfo(int buffer_id) {
246 DCHECK_NOT_ON_DEVICE_THREAD();
247 const auto it = frame_infos_.find(buffer_id);
248 if (it == frame_infos_.end()) {
249 ADD_FAILURE() << "Missing entry for buffer_id=" << buffer_id;
250 return media::mojom::VideoFrameInfoPtr();
251 }
252 media::mojom::VideoFrameInfoPtr info = std::move(it->second);
253 frame_infos_.erase(it);
254 return info;
255 }
256
257 private:
258 base::flat_map<int, media::mojom::VideoBufferHandlePtr> buffer_handles_;
259 base::flat_map<int, int> feedback_ids_;
260 base::flat_map<int, std::unique_ptr<Buffer::ScopedAccessPermission>>
261 access_permissions_;
262 base::flat_map<int, media::mojom::VideoFrameInfoPtr> frame_infos_;
263 };
264
265 // A FrameSinkVideoCaptureDevice, but with CreateCapturer() overridden to bind
266 // to a MockFrameSinkVideoCapturer instead of the real thing.
267 class FrameSinkVideoCaptureDeviceForTest : public FrameSinkVideoCaptureDevice {
268 public:
FrameSinkVideoCaptureDeviceForTest(MockFrameSinkVideoCapturer * capturer)269 explicit FrameSinkVideoCaptureDeviceForTest(
270 MockFrameSinkVideoCapturer* capturer)
271 : capturer_(capturer) {}
272
273 protected:
CreateCapturer(mojo::PendingReceiver<viz::mojom::FrameSinkVideoCapturer> receiver)274 void CreateCapturer(mojo::PendingReceiver<viz::mojom::FrameSinkVideoCapturer>
275 receiver) final {
276 base::PostTask(
277 FROM_HERE, {BrowserThread::UI},
278 base::BindOnce(
279 [](MockFrameSinkVideoCapturer* capturer,
280 mojo::PendingReceiver<viz::mojom::FrameSinkVideoCapturer>
281 receiver) { capturer->Bind(std::move(receiver)); },
282 capturer_, std::move(receiver)));
283 }
284
285 MockFrameSinkVideoCapturer* const capturer_;
286 };
287
288 // Convenience macros to make a non-blocking FrameSinkVideoCaptureDevice method
289 // call on the device thread.
290 #define POST_DEVICE_METHOD_CALL0(method) \
291 POST_DEVICE_TASK(base::BindOnce(&FrameSinkVideoCaptureDevice::method, \
292 base::Unretained(device_.get())))
293 #define POST_DEVICE_METHOD_CALL(method, ...) \
294 POST_DEVICE_TASK(base::BindOnce(&FrameSinkVideoCaptureDevice::method, \
295 base::Unretained(device_.get()), \
296 __VA_ARGS__))
297
298 class FrameSinkVideoCaptureDeviceTest : public testing::Test {
299 public:
FrameSinkVideoCaptureDeviceTest()300 FrameSinkVideoCaptureDeviceTest()
301 : task_environment_(BrowserTaskEnvironment::REAL_IO_THREAD) {}
302
~FrameSinkVideoCaptureDeviceTest()303 ~FrameSinkVideoCaptureDeviceTest() override { EXPECT_FALSE(device_); }
304
SetUp()305 void SetUp() override {
306 // Create the FrameSinkVideoCaptureDevice on the device thread, and block
307 // until complete.
308 POST_DEVICE_TASK(base::BindOnce(
309 [](FrameSinkVideoCaptureDeviceTest* test) {
310 test->device_ = std::make_unique<FrameSinkVideoCaptureDeviceForTest>(
311 &test->capturer_);
312 },
313 this));
314 WAIT_FOR_DEVICE_TASKS();
315 }
316
TearDown()317 void TearDown() override {
318 // Destroy the FrameSinkVideoCaptureDevice on the device thread, and block
319 // until complete.
320 POST_DEVICE_TASK(base::BindOnce(
321 [](FrameSinkVideoCaptureDeviceTest* test) { test->device_.reset(); },
322 this));
323 WAIT_FOR_DEVICE_TASKS();
324 // Some objects owned by the FrameSinkVideoCaptureDevice may need to be
325 // deleted on the UI thread, so run those tasks now.
326 RUN_UI_TASKS();
327 }
328
329 // Starts-up the FrameSinkVideoCaptureDevice: Sets a frame sink target,
330 // creates a capturer, sets the capture parameters; and checks that the mock
331 // capturer receives the correct mojo method calls.
AllocateAndStartSynchronouslyWithExpectations(std::unique_ptr<media::VideoFrameReceiver> receiver)332 void AllocateAndStartSynchronouslyWithExpectations(
333 std::unique_ptr<media::VideoFrameReceiver> receiver) {
334 EXPECT_CALL(capturer_, SetFormat(kFormat, _));
335 EXPECT_CALL(capturer_, SetMinCapturePeriod(kMinCapturePeriod));
336 EXPECT_CALL(capturer_,
337 SetResolutionConstraints(kResolution, kResolution, _));
338 constexpr viz::FrameSinkId frame_sink_id(1, 1);
339 EXPECT_CALL(capturer_, MockChangeTarget(frame_sink_id));
340 EXPECT_CALL(capturer_, MockStart(NotNull()));
341
342 EXPECT_FALSE(capturer_.is_bound());
343 POST_DEVICE_METHOD_CALL(OnTargetChanged, frame_sink_id);
344 POST_DEVICE_METHOD_CALL(AllocateAndStartWithReceiver, GetCaptureParams(),
345 std::move(receiver));
346 WAIT_FOR_DEVICE_TASKS();
347 RUN_UI_TASKS(); // Run the task to create the capturer.
348 EXPECT_TRUE(capturer_.is_bound());
349 WAIT_FOR_DEVICE_TASKS(); // Run the task where the interface is bound, etc.
350 }
351
352 // Stops the FrameSinkVideoCaptureDevice and optionally checks that the mock
353 // capturer received the Stop() call.
StopAndDeAllocateSynchronouslyWithExpectations(bool capturer_stopped_also)354 void StopAndDeAllocateSynchronouslyWithExpectations(
355 bool capturer_stopped_also) {
356 EXPECT_CALL(capturer_, MockStop()).Times(capturer_stopped_also ? 1 : 0);
357 POST_DEVICE_METHOD_CALL0(StopAndDeAllocate);
358 WAIT_FOR_DEVICE_TASKS();
359 }
360
361 // Simulates what the VIZ capturer would do: Allocates a shared memory buffer,
362 // populates it with video content, and calls OnFrameCaptured().
SimulateFrameCapture(int frame_number,MockFrameSinkVideoConsumerFrameCallbacks * callbacks)363 void SimulateFrameCapture(
364 int frame_number,
365 MockFrameSinkVideoConsumerFrameCallbacks* callbacks) {
366 // Allocate a buffer and fill it with values based on |frame_number|.
367 base::MappedReadOnlyRegion region =
368 base::ReadOnlySharedMemoryRegion::Create(
369 media::VideoFrame::AllocationSize(kFormat, kResolution));
370 CHECK(region.IsValid());
371 memset(region.mapping.memory(), GetFrameFillValue(frame_number),
372 region.mapping.size());
373
374 mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
375 callbacks_remote;
376 callbacks->Bind(callbacks_remote.InitWithNewPipeAndPassReceiver());
377 // |callbacks_remote| is bound on the main thread, so it needs to be
378 // re-bound to the device thread before calling OnFrameCaptured().
379 POST_DEVICE_TASK(base::BindOnce(
380 [](FrameSinkVideoCaptureDevice* device,
381 base::ReadOnlySharedMemoryRegion data, int frame_number,
382 mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
383 callbacks_remote) {
384 device->OnFrameCaptured(
385 std::move(data),
386 media::mojom::VideoFrameInfo::New(
387 kMinCapturePeriod * frame_number,
388 base::Value(base::Value::Type::DICTIONARY), kFormat,
389 kResolution, gfx::Rect(kResolution),
390 gfx::ColorSpace::CreateREC709(), nullptr),
391 gfx::Rect(kResolution), std::move(callbacks_remote));
392 },
393 base::Unretained(device_.get()), std::move(region.region), frame_number,
394 std::move(callbacks_remote)));
395 }
396
397 // Returns a byte value based on the given |frame_number|.
GetFrameFillValue(int frame_number)398 static constexpr uint8_t GetFrameFillValue(int frame_number) {
399 return (frame_number % 0x3f) << 2;
400 }
401
402 // Returns true if the |buffer| is filled with the correct byte value for the
403 // given |frame_number|.
IsExpectedBufferContentForFrame(int frame_number,base::ReadOnlySharedMemoryRegion buffer)404 static bool IsExpectedBufferContentForFrame(
405 int frame_number,
406 base::ReadOnlySharedMemoryRegion buffer) {
407 const auto mapping = buffer.Map();
408 const size_t frame_allocation_size =
409 media::VideoFrame::AllocationSize(kFormat, kResolution);
410 CHECK_LE(frame_allocation_size, mapping.size());
411 const uint8_t* src = mapping.GetMemoryAs<const uint8_t>();
412 const uint8_t expected_value = GetFrameFillValue(frame_number);
413 for (size_t i = 0; i < frame_allocation_size; ++i) {
414 if (src[i] != expected_value) {
415 return false;
416 }
417 }
418 return true;
419 }
420
421 protected:
422 // See the threading notes at top of this file.
423 BrowserTaskEnvironment task_environment_;
424
425 NiceMock<MockFrameSinkVideoCapturer> capturer_;
426 std::unique_ptr<FrameSinkVideoCaptureDevice> device_;
427 };
428
429 // Tests a normal session, progressing through the start, frame capture, and
430 // stop phases.
TEST_F(FrameSinkVideoCaptureDeviceTest,CapturesAndDeliversFrames)431 TEST_F(FrameSinkVideoCaptureDeviceTest, CapturesAndDeliversFrames) {
432 auto receiver_ptr = std::make_unique<MockVideoFrameReceiver>();
433 auto* const receiver = receiver_ptr.get();
434 EXPECT_CALL(*receiver, OnStarted());
435 EXPECT_CALL(*receiver, OnError(_)).Times(0);
436
437 AllocateAndStartSynchronouslyWithExpectations(std::move(receiver_ptr));
438 // From this point, there is no reason the capturer should be re-started.
439 EXPECT_CALL(capturer_, MockStart(_)).Times(0);
440
441 // Run 24 frames through the pipeline, one at a time. Then, run 24 more, two
442 // at a time. Then, run 24 more, three at a time.
443 constexpr int kNumFramesToDeliver = 24;
444 constexpr int kMaxSimultaneousFrames = 3;
445 int next_frame_number = 0;
446 for (int in_flight_count = 1; in_flight_count <= kMaxSimultaneousFrames;
447 ++in_flight_count) {
448 for (int iteration = 0; iteration < kNumFramesToDeliver; ++iteration) {
449 int buffer_ids[kMaxSimultaneousFrames] = {-1};
450 MockFrameSinkVideoConsumerFrameCallbacks
451 callbackses[kMaxSimultaneousFrames];
452
453 // Simulate |in_flight_count| frame captures and expect the frames to be
454 // delivered to the VideoFrameReceiver.
455 const int first_frame_number = next_frame_number;
456 for (int i = 0; i < in_flight_count; ++i) {
457 Expectation new_buffer_called =
458 EXPECT_CALL(*receiver, MockOnNewBuffer(Ge(0), NotNull()))
459 .WillOnce(SaveArg<0>(&buffer_ids[i]));
460 EXPECT_CALL(*receiver,
461 MockOnFrameReadyInBuffer(Eq(ByRef(buffer_ids[i])), Ge(0),
462 NotNull(), NotNull()))
463 .After(new_buffer_called);
464 SimulateFrameCapture(next_frame_number, &callbackses[i]);
465 ++next_frame_number;
466 WAIT_FOR_DEVICE_TASKS();
467 }
468
469 // Confirm the VideoFrameReceiver was provided the correct buffer and
470 // VideoFrameInfo struct for each frame in this batch.
471 for (int frame_number = first_frame_number;
472 frame_number < next_frame_number; ++frame_number) {
473 const int buffer_id = buffer_ids[frame_number - first_frame_number];
474
475 auto buffer = receiver->TakeBufferHandle(buffer_id);
476 ASSERT_TRUE(buffer.IsValid());
477 EXPECT_TRUE(
478 IsExpectedBufferContentForFrame(frame_number, std::move(buffer)));
479
480 const auto info = receiver->TakeVideoFrameInfo(buffer_id);
481 ASSERT_TRUE(info);
482 EXPECT_EQ(kMinCapturePeriod * frame_number, info->timestamp);
483 EXPECT_EQ(kFormat, info->pixel_format);
484 EXPECT_EQ(kResolution, info->coded_size);
485 EXPECT_EQ(gfx::Rect(kResolution), info->visible_rect);
486 }
487
488 // Simulate the receiver providing the feedback and done notifications for
489 // each frame and expect the FrameSinkVideoCaptureDevice to process these
490 // notifications.
491 for (int frame_number = first_frame_number;
492 frame_number < next_frame_number; ++frame_number) {
493 const int buffer_id = buffer_ids[frame_number - first_frame_number];
494 MockFrameSinkVideoConsumerFrameCallbacks& callbacks =
495 callbackses[frame_number - first_frame_number];
496
497 const double fake_utilization =
498 static_cast<double>(frame_number) / kNumFramesToDeliver;
499 EXPECT_CALL(callbacks, ProvideFeedback(fake_utilization));
500 EXPECT_CALL(callbacks, Done());
501 EXPECT_CALL(*receiver, OnBufferRetired(buffer_id));
502
503 const int feedback_id = receiver->TakeFeedbackId(buffer_id);
504 POST_DEVICE_METHOD_CALL(OnUtilizationReport, feedback_id,
505 fake_utilization);
506 receiver->ReleaseAccessPermission(buffer_id);
507 WAIT_FOR_DEVICE_TASKS();
508 }
509 }
510 }
511
512 StopAndDeAllocateSynchronouslyWithExpectations(true /* capturer will stop */);
513 }
514
515 // Tests that a client request to Suspend() should stop consumption and ignore
516 // all refresh requests. Likewise, a client request to Resume() will
517 // re-establish consumption and allow refresh requests to propagate to the
518 // capturer again.
TEST_F(FrameSinkVideoCaptureDeviceTest,SuspendsAndResumes)519 TEST_F(FrameSinkVideoCaptureDeviceTest, SuspendsAndResumes) {
520 AllocateAndStartSynchronouslyWithExpectations(
521 std::make_unique<NiceMock<MockVideoFrameReceiver>>());
522
523 // A started device should have started the capturer, and any refresh frame
524 // requests from the client should be propagated to it.
525 {
526 EXPECT_CALL(capturer_, RequestRefreshFrame());
527 POST_DEVICE_METHOD_CALL0(RequestRefreshFrame);
528 WAIT_FOR_DEVICE_TASKS();
529 }
530
531 // Simulate a client request that capture be suspended. The capturer should
532 // receive a Stop() message.
533 {
534 EXPECT_CALL(capturer_, MockStart(_)).Times(0);
535 EXPECT_CALL(capturer_, MockStop());
536 POST_DEVICE_METHOD_CALL0(MaybeSuspend);
537 WAIT_FOR_DEVICE_TASKS();
538 }
539
540 // A suspended device should not propagate any refresh frame requests.
541 {
542 EXPECT_CALL(capturer_, RequestRefreshFrame()).Times(0);
543 POST_DEVICE_METHOD_CALL0(RequestRefreshFrame);
544 WAIT_FOR_DEVICE_TASKS();
545 }
546
547 // Simulate a client request that capture be resumed. The capturer should
548 // receive a Start() message.
549 {
550 EXPECT_CALL(capturer_, MockStart(NotNull()));
551 EXPECT_CALL(capturer_, MockStop()).Times(0);
552 POST_DEVICE_METHOD_CALL0(Resume);
553 WAIT_FOR_DEVICE_TASKS();
554 }
555
556 // Now refresh frame requests should propagate again.
557 {
558 EXPECT_CALL(capturer_, RequestRefreshFrame());
559 POST_DEVICE_METHOD_CALL0(RequestRefreshFrame);
560 WAIT_FOR_DEVICE_TASKS();
561 }
562
563 StopAndDeAllocateSynchronouslyWithExpectations(true /* capturer will stop */);
564 }
565
566 // Tests that the FrameSinkVideoCaptureDevice will shutdown on a fatal error and
567 // refuse to be started again.
TEST_F(FrameSinkVideoCaptureDeviceTest,ShutsDownOnFatalError)568 TEST_F(FrameSinkVideoCaptureDeviceTest, ShutsDownOnFatalError) {
569 auto receiver_ptr = std::make_unique<MockVideoFrameReceiver>();
570 auto* receiver = receiver_ptr.get();
571 Sequence sequence;
572 EXPECT_CALL(*receiver, OnStarted()).InSequence(sequence);
573 EXPECT_CALL(*receiver, OnLog(StrNe(""))).InSequence(sequence);
574 EXPECT_CALL(*receiver, OnError(_)).InSequence(sequence);
575
576 AllocateAndStartSynchronouslyWithExpectations(std::move(receiver_ptr));
577
578 // Notify the device that the target frame sink was lost. This should stop
579 // consumption, unbind the capturer, log an error with the VideoFrameReceiver,
580 // and destroy the VideoFrameReceiver.
581 {
582 EXPECT_CALL(capturer_, MockChangeTarget(viz::FrameSinkId()));
583 EXPECT_CALL(capturer_, MockStop());
584 POST_DEVICE_METHOD_CALL0(OnTargetPermanentlyLost);
585 WAIT_FOR_DEVICE_TASKS();
586 }
587
588 // Shutdown the device. However, the fatal error already stopped consumption,
589 // so don't expect the capturer to be stopped again.
590 StopAndDeAllocateSynchronouslyWithExpectations(false);
591
592 // Now, any further attempts to start the FrameSinkVideoCaptureDevice again
593 // should fail. The VideoFrameReceiver will be provided the same error
594 // message.
595 receiver_ptr = std::make_unique<MockVideoFrameReceiver>();
596 receiver = receiver_ptr.get();
597 {
598 EXPECT_CALL(*receiver, OnStarted()).Times(0);
599 EXPECT_CALL(*receiver, OnLog(StrNe("")));
600 EXPECT_CALL(*receiver, OnError(_));
601 EXPECT_CALL(capturer_, MockStart(_)).Times(0);
602
603 POST_DEVICE_METHOD_CALL(AllocateAndStartWithReceiver, GetCaptureParams(),
604 std::move(receiver_ptr));
605 WAIT_FOR_DEVICE_TASKS();
606 }
607 }
608
609 } // namespace
610 } // namespace content
611