forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
broadcasting_receiver_unittest.cc
301 lines (252 loc) · 12.3 KB
/
broadcasting_receiver_unittest.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/video_capture/broadcasting_receiver.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/video_capture/public/cpp/mock_video_frame_handler.h"
#include "services/video_capture/public/mojom/video_frame_handler.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::InvokeWithoutArgs;
namespace video_capture {
static const size_t kArbitraryDummyBufferSize = 8u;
static const int kArbitraryBufferId = 123;
static const int kArbitraryFrameFeedbackId = 456;
class StubReadWritePermission final
: public media::VideoCaptureDevice::Client::Buffer::ScopedAccessPermission {
public:
StubReadWritePermission() = default;
StubReadWritePermission(const StubReadWritePermission&) = delete;
StubReadWritePermission& operator=(const StubReadWritePermission&) = delete;
~StubReadWritePermission() override = default;
};
class BroadcastingReceiverTest : public ::testing::Test {
public:
void SetUp() override {
mojo::PendingRemote<mojom::VideoFrameHandler> video_frame_handler_1;
mojo::PendingRemote<mojom::VideoFrameHandler> video_frame_handler_2;
mock_video_frame_handler_1_ = std::make_unique<MockVideoFrameHandler>(
video_frame_handler_1.InitWithNewPipeAndPassReceiver());
mock_video_frame_handler_2_ = std::make_unique<MockVideoFrameHandler>(
video_frame_handler_2.InitWithNewPipeAndPassReceiver());
client_id_1_ =
broadcaster_.AddClient(std::move(video_frame_handler_1),
media::VideoCaptureBufferType::kSharedMemory);
client_id_2_ =
broadcaster_.AddClient(std::move(video_frame_handler_2),
media::VideoCaptureBufferType::kSharedMemory);
shm_region_ =
base::UnsafeSharedMemoryRegion::Create(kArbitraryDummyBufferSize);
ASSERT_TRUE(shm_region_.IsValid());
media::mojom::VideoBufferHandlePtr buffer_handle =
media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
std::move(shm_region_));
broadcaster_.OnNewBuffer(kArbitraryBufferId, std::move(buffer_handle));
}
size_t HoldBufferContextSize() {
return broadcaster_.scoped_access_permissions_by_buffer_context_id_.size();
}
protected:
BroadcastingReceiver broadcaster_;
std::unique_ptr<MockVideoFrameHandler> mock_video_frame_handler_1_;
std::unique_ptr<MockVideoFrameHandler> mock_video_frame_handler_2_;
int32_t client_id_1_;
int32_t client_id_2_;
base::UnsafeSharedMemoryRegion shm_region_;
base::test::TaskEnvironment task_environment_;
};
TEST_F(
BroadcastingReceiverTest,
HoldsOnToAccessPermissionForRetiredBufferUntilLastClientFinishedConsuming) {
base::RunLoop frame_arrived_at_video_frame_handler_1;
base::RunLoop frame_arrived_at_video_frame_handler_2;
EXPECT_CALL(*mock_video_frame_handler_1_, DoOnFrameReadyInBuffer(_, _, _))
.WillOnce(InvokeWithoutArgs([&frame_arrived_at_video_frame_handler_1]() {
frame_arrived_at_video_frame_handler_1.Quit();
}));
EXPECT_CALL(*mock_video_frame_handler_2_, DoOnFrameReadyInBuffer(_, _, _))
.WillOnce(InvokeWithoutArgs([&frame_arrived_at_video_frame_handler_2]() {
frame_arrived_at_video_frame_handler_2.Quit();
}));
mock_video_frame_handler_1_->HoldAccessPermissions();
mock_video_frame_handler_2_->HoldAccessPermissions();
broadcaster_.OnFrameReadyInBuffer(
media::ReadyFrameInBuffer(kArbitraryBufferId, kArbitraryFrameFeedbackId,
std::make_unique<StubReadWritePermission>(),
media::mojom::VideoFrameInfo::New()),
{});
// mock_video_frame_handler_1_ finishes consuming immediately.
// mock_video_frame_handler_2_ continues consuming.
frame_arrived_at_video_frame_handler_1.Run();
frame_arrived_at_video_frame_handler_2.Run();
base::RunLoop buffer_retired_arrived_at_video_frame_handler_1;
base::RunLoop buffer_retired_arrived_at_video_frame_handler_2;
EXPECT_CALL(*mock_video_frame_handler_1_, DoOnBufferRetired(_))
.WillOnce(InvokeWithoutArgs(
[&buffer_retired_arrived_at_video_frame_handler_1]() {
buffer_retired_arrived_at_video_frame_handler_1.Quit();
}));
EXPECT_CALL(*mock_video_frame_handler_2_, DoOnBufferRetired(_))
.WillOnce(InvokeWithoutArgs(
[&buffer_retired_arrived_at_video_frame_handler_2]() {
buffer_retired_arrived_at_video_frame_handler_2.Quit();
}));
// Retiring the buffer results in both receivers getting the retired event.
broadcaster_.OnBufferRetired(kArbitraryBufferId);
buffer_retired_arrived_at_video_frame_handler_1.Run();
buffer_retired_arrived_at_video_frame_handler_2.Run();
base::RunLoop().RunUntilIdle();
// Despite retiring, the access to the buffer is not released because it is
// still in use by both handlers.
DCHECK_EQ(HoldBufferContextSize(), 1u);
// mock_video_frame_handler_2_ finishes consuming. Access is still not
// released because of mock_video_frame_handler_1_.
mock_video_frame_handler_2_->ReleaseAccessedFrames();
base::RunLoop().RunUntilIdle();
DCHECK_EQ(HoldBufferContextSize(), 1u);
// mock_video_frame_handler_1_ finishes consuming. Now the buffer is finally
// released.
mock_video_frame_handler_1_->ReleaseAccessedFrames();
base::RunLoop().RunUntilIdle();
DCHECK_EQ(HoldBufferContextSize(), 0u);
}
TEST_F(BroadcastingReceiverTest,
DoesNotHoldOnToAccessPermissionWhenAllClientsAreSuspended) {
EXPECT_CALL(*mock_video_frame_handler_1_, DoOnFrameReadyInBuffer(_, _, _))
.Times(0);
EXPECT_CALL(*mock_video_frame_handler_2_, DoOnFrameReadyInBuffer(_, _, _))
.Times(0);
mock_video_frame_handler_1_->HoldAccessPermissions();
mock_video_frame_handler_2_->HoldAccessPermissions();
broadcaster_.SuspendClient(client_id_1_);
broadcaster_.SuspendClient(client_id_2_);
broadcaster_.OnFrameReadyInBuffer(
media::ReadyFrameInBuffer(kArbitraryBufferId, kArbitraryFrameFeedbackId,
std::make_unique<StubReadWritePermission>(),
media::mojom::VideoFrameInfo::New()),
{});
// Because the clients are suspended, frames are automatically released.
base::RunLoop().RunUntilIdle();
DCHECK_EQ(HoldBufferContextSize(), 0u);
// Resume one of the clients and pass another frame.
broadcaster_.ResumeClient(client_id_2_);
EXPECT_CALL(*mock_video_frame_handler_2_, DoOnFrameReadyInBuffer(_, _, _))
.Times(1);
broadcaster_.OnFrameReadyInBuffer(
media::ReadyFrameInBuffer(kArbitraryBufferId, kArbitraryFrameFeedbackId,
std::make_unique<StubReadWritePermission>(),
media::mojom::VideoFrameInfo::New()),
{});
// This time the buffer is not released automatically.
base::RunLoop().RunUntilIdle();
DCHECK_EQ(HoldBufferContextSize(), 1u);
// Releasing mock_video_frame_handler_2_'s frame is sufficient for the buffer
// to be released since the frame was never delivered to
// mock_video_frame_handler_1_.
mock_video_frame_handler_2_->ReleaseAccessedFrames();
base::RunLoop().RunUntilIdle();
DCHECK_EQ(HoldBufferContextSize(), 0u);
}
TEST_F(BroadcastingReceiverTest, ForwardsScaledFrames) {
const int kBufferId = 10;
const int kScaledBufferId = 11;
mock_video_frame_handler_1_->HoldAccessPermissions();
media::mojom::VideoBufferHandlePtr buffer_handle =
media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
base::UnsafeSharedMemoryRegion::Create(kArbitraryDummyBufferSize));
broadcaster_.OnNewBuffer(kBufferId, std::move(buffer_handle));
media::mojom::VideoBufferHandlePtr scaled_buffer_handle =
media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
base::UnsafeSharedMemoryRegion::Create(kArbitraryDummyBufferSize));
broadcaster_.OnNewBuffer(kScaledBufferId, std::move(scaled_buffer_handle));
// Suspend the second client so that the first client alone controls buffer
// access.
broadcaster_.SuspendClient(client_id_2_);
base::RunLoop on_buffer_ready;
EXPECT_CALL(*mock_video_frame_handler_1_, DoOnFrameReadyInBuffer(_, _, _))
.WillOnce(
InvokeWithoutArgs([&on_buffer_ready]() { on_buffer_ready.Quit(); }));
media::ReadyFrameInBuffer ready_buffer =
media::ReadyFrameInBuffer(kBufferId, kArbitraryFrameFeedbackId,
std::make_unique<StubReadWritePermission>(),
media::mojom::VideoFrameInfo::New());
std::vector<media::ReadyFrameInBuffer> scaled_ready_buffers;
scaled_ready_buffers.emplace_back(kScaledBufferId, kArbitraryFrameFeedbackId,
std::make_unique<StubReadWritePermission>(),
media::mojom::VideoFrameInfo::New());
broadcaster_.OnFrameReadyInBuffer(std::move(ready_buffer),
std::move(scaled_ready_buffers));
on_buffer_ready.Run();
DCHECK_EQ(HoldBufferContextSize(), 2u);
// Releasing the handler's buffers releases both frame and scaled frame.
mock_video_frame_handler_1_->ReleaseAccessedFrames();
base::RunLoop().RunUntilIdle();
DCHECK_EQ(HoldBufferContextSize(), 0u);
// Scaled buffers also get retired.
base::RunLoop on_both_buffers_retired;
size_t num_buffers_retired = 0u;
EXPECT_CALL(*mock_video_frame_handler_1_, DoOnBufferRetired(_))
.WillRepeatedly(
InvokeWithoutArgs([&on_both_buffers_retired, &num_buffers_retired]() {
++num_buffers_retired;
if (num_buffers_retired == 2u)
on_both_buffers_retired.Quit();
}));
broadcaster_.OnBufferRetired(kBufferId);
broadcaster_.OnBufferRetired(kScaledBufferId);
on_both_buffers_retired.Run();
}
TEST_F(BroadcastingReceiverTest, AccessPermissionsSurviveStop) {
// For simplicitly, we only care about the first client in this test.
broadcaster_.SuspendClient(client_id_2_);
broadcaster_.OnStarted();
// In this test, two frame handlers are used. In order to inspect all frame
// IDs that have released after the first handler is destroyed.
mock_video_frame_handler_1_->HoldAccessPermissions();
EXPECT_CALL(*mock_video_frame_handler_1_, DoOnFrameReadyInBuffer(_, _, _))
.Times(1);
broadcaster_.OnFrameReadyInBuffer(
media::ReadyFrameInBuffer(kArbitraryBufferId, kArbitraryFrameFeedbackId,
std::make_unique<StubReadWritePermission>(),
media::mojom::VideoFrameInfo::New()),
{});
base::RunLoop().RunUntilIdle();
// The first frame has not been released yet.
DCHECK_EQ(HoldBufferContextSize(), 1u);
// Simulate a device restart.
broadcaster_.OnStopped();
broadcaster_.OnStarted();
base::RunLoop().RunUntilIdle();
// The first frame has still not been released yet.
DCHECK_EQ(HoldBufferContextSize(), 1u);
// Receive another frame after device restart.
base::UnsafeSharedMemoryRegion shm_region2 =
base::UnsafeSharedMemoryRegion::Create(kArbitraryDummyBufferSize);
ASSERT_TRUE(shm_region2.IsValid());
media::mojom::VideoBufferHandlePtr buffer_handle2 =
media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
std::move(shm_region2));
broadcaster_.OnNewBuffer(kArbitraryBufferId + 1, std::move(buffer_handle2));
EXPECT_CALL(*mock_video_frame_handler_1_, DoOnFrameReadyInBuffer(_, _, _))
.Times(1);
broadcaster_.OnFrameReadyInBuffer(
media::ReadyFrameInBuffer(kArbitraryBufferId + 1,
kArbitraryFrameFeedbackId + 1,
std::make_unique<StubReadWritePermission>(),
media::mojom::VideoFrameInfo::New()),
{});
base::RunLoop().RunUntilIdle();
// Neither frame has been released.
DCHECK_EQ(HoldBufferContextSize(), 2u);
// Release all frames. This should inform both the old and the new handler.
mock_video_frame_handler_1_->ReleaseAccessedFrames();
base::RunLoop().RunUntilIdle();
// Both buffers should now be released.
DCHECK_EQ(HoldBufferContextSize(), 0u);
}
} // namespace video_capture