blob: b93969bb7284c5e02a8bf5345d781840de97cc4f [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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 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));
}
FakeVideoFrameAccessHandler* CreateAndSendFakeVideoFrameAccessHandler() {
mojo::PendingRemote<mojom::VideoFrameAccessHandler> pending_remote;
auto frame_access_handler =
mojo::MakeSelfOwnedReceiver<mojom::VideoFrameAccessHandler>(
std::make_unique<FakeVideoFrameAccessHandler>(),
pending_remote.InitWithNewPipeAndPassReceiver());
broadcaster_.OnFrameAccessHandlerReady(std::move(pending_remote));
return static_cast<FakeVideoFrameAccessHandler*>(
frame_access_handler->impl());
}
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) {
FakeVideoFrameAccessHandler* frame_access_handler =
CreateAndSendFakeVideoFrameAccessHandler();
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();
media::mojom::VideoFrameInfoPtr frame_info =
media::mojom::VideoFrameInfo::New();
broadcaster_.OnFrameReadyInBuffer(
mojom::ReadyFrameInBuffer::New(
kArbitraryBufferId, kArbitraryFrameFeedbackId, std::move(frame_info)),
{});
// 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.
EXPECT_TRUE(frame_access_handler->released_buffer_ids().empty());
// 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();
EXPECT_TRUE(frame_access_handler->released_buffer_ids().empty());
// mock_video_frame_handler_1_ finishes consuming. Now the buffer is finally
// released.
mock_video_frame_handler_1_->ReleaseAccessedFrames();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(frame_access_handler->released_buffer_ids().empty());
}
TEST_F(BroadcastingReceiverTest,
DoesNotHoldOnToAccessPermissionWhenAllClientsAreSuspended) {
FakeVideoFrameAccessHandler* frame_access_handler =
CreateAndSendFakeVideoFrameAccessHandler();
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(
mojom::ReadyFrameInBuffer::New(kArbitraryBufferId,
kArbitraryFrameFeedbackId,
media::mojom::VideoFrameInfo::New()),
{});
// Because the clients are suspended, frames are automatically released.
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(frame_access_handler->released_buffer_ids().empty());
frame_access_handler->ClearReleasedBufferIds();
// 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(
mojom::ReadyFrameInBuffer::New(kArbitraryBufferId,
kArbitraryFrameFeedbackId,
media::mojom::VideoFrameInfo::New()),
{});
// This time the buffer is not released automatically.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(frame_access_handler->released_buffer_ids().empty());
// 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();
EXPECT_FALSE(frame_access_handler->released_buffer_ids().empty());
}
TEST_F(BroadcastingReceiverTest, ForwardsScaledFrames) {
const int kBufferId = 10;
const int kScaledBufferId = 11;
FakeVideoFrameAccessHandler* frame_access_handler =
CreateAndSendFakeVideoFrameAccessHandler();
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(); }));
mojom::ReadyFrameInBufferPtr ready_buffer =
mojom::ReadyFrameInBuffer::New(kBufferId, kArbitraryFrameFeedbackId,
media::mojom::VideoFrameInfo::New());
std::vector<mojom::ReadyFrameInBufferPtr> scaled_ready_buffers;
scaled_ready_buffers.push_back(
mojom::ReadyFrameInBuffer::New(kScaledBufferId, kArbitraryFrameFeedbackId,
media::mojom::VideoFrameInfo::New()));
broadcaster_.OnFrameReadyInBuffer(std::move(ready_buffer),
std::move(scaled_ready_buffers));
on_buffer_ready.Run();
EXPECT_TRUE(frame_access_handler->released_buffer_ids().empty());
// Releasing the handler's buffers releases both frame and scaled frame.
mock_video_frame_handler_1_->ReleaseAccessedFrames();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(frame_access_handler->released_buffer_ids().size(), 2u);
// 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, released
// buffer IDs are stored in |released_buffer_ids|.
std::vector<int32_t> released_buffer_ids;
base::RepeatingCallback<void(int32_t)> buffer_released_callback =
base::BindRepeating(
[](std::vector<int32_t>* released_buffer_ids, int32_t buffer_id) {
released_buffer_ids->push_back(buffer_id);
},
base::Unretained(&released_buffer_ids));
mock_video_frame_handler_1_->HoldAccessPermissions();
{
mojo::PendingRemote<mojom::VideoFrameAccessHandler>
frame_access_handler_proxy;
auto frame_access_handler =
mojo::MakeSelfOwnedReceiver<mojom::VideoFrameAccessHandler>(
std::make_unique<FakeVideoFrameAccessHandler>(
buffer_released_callback),
frame_access_handler_proxy.InitWithNewPipeAndPassReceiver());
broadcaster_.OnFrameAccessHandlerReady(
std::move(frame_access_handler_proxy));
}
EXPECT_CALL(*mock_video_frame_handler_1_, DoOnFrameReadyInBuffer(_, _, _))
.Times(1);
broadcaster_.OnFrameReadyInBuffer(
mojom::ReadyFrameInBuffer::New(kArbitraryBufferId,
kArbitraryFrameFeedbackId,
media::mojom::VideoFrameInfo::New()),
{});
base::RunLoop().RunUntilIdle();
// The first frame has not been released yet.
EXPECT_TRUE(released_buffer_ids.empty());
// Simulate a device restart.
broadcaster_.OnStopped();
broadcaster_.OnStarted();
FakeVideoFrameAccessHandler* second_device_frame_access_handler;
{
mojo::PendingRemote<mojom::VideoFrameAccessHandler>
frame_access_handler_proxy;
auto frame_access_handler =
mojo::MakeSelfOwnedReceiver<mojom::VideoFrameAccessHandler>(
std::make_unique<FakeVideoFrameAccessHandler>(
buffer_released_callback),
frame_access_handler_proxy.InitWithNewPipeAndPassReceiver());
broadcaster_.OnFrameAccessHandlerReady(
std::move(frame_access_handler_proxy));
second_device_frame_access_handler =
static_cast<FakeVideoFrameAccessHandler*>(frame_access_handler->impl());
}
base::RunLoop().RunUntilIdle();
// The first frame has still not been released yet.
EXPECT_TRUE(released_buffer_ids.empty());
// 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(
mojom::ReadyFrameInBuffer::New(kArbitraryBufferId + 1,
kArbitraryFrameFeedbackId + 1,
media::mojom::VideoFrameInfo::New()),
{});
base::RunLoop().RunUntilIdle();
// Neither frame has been released.
EXPECT_TRUE(released_buffer_ids.empty());
// 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.
EXPECT_EQ(released_buffer_ids.size(), 2u);
// The first buffer was released by the first handler, which now no longer
// exist. The second buffer was released by the second handler so we can
// EXPECT it to only have observed a single frame release event.
EXPECT_EQ(second_device_frame_access_handler->released_buffer_ids().size(),
1u);
}
} // namespace video_capture