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