Source code
Revision control
Copy as Markdown
Other Tools
/*
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"
#include <memory>
#include <utility>
#include "api/units/time_delta.h"
#include "modules/desktop_capture/desktop_capturer.h"
#include "modules/desktop_capture/desktop_frame.h"
#include "modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.h"
#include "modules/desktop_capture/rgba_color.h"
#include "rtc_base/event.h"
#include "test/gmock.h"
#include "test/gtest.h"
using ::testing::_;
using ::testing::AtLeast;
using ::testing::Ge;
using ::testing::Invoke;
namespace webrtc {
constexpr TimeDelta kShortWait = TimeDelta::Seconds(5);
constexpr TimeDelta kLongWait = TimeDelta::Seconds(15);
constexpr int kBytesPerPixel = 4;
constexpr int32_t kWidth = 800;
constexpr int32_t kHeight = 640;
class PipeWireStreamTest : public ::testing::Test,
public TestScreenCastStreamProvider::Observer,
public SharedScreenCastStream::Observer {
public:
PipeWireStreamTest() = default;
~PipeWireStreamTest() = default;
// FakeScreenCastPortal::Observer
MOCK_METHOD(void, OnBufferAdded, (), (override));
MOCK_METHOD(void, OnFrameRecorded, (), (override));
MOCK_METHOD(void, OnStreamReady, (uint32_t stream_node_id), (override));
MOCK_METHOD(void, OnStartStreaming, (), (override));
MOCK_METHOD(void, OnStopStreaming, (), (override));
// SharedScreenCastStream::Observer
MOCK_METHOD(void, OnCursorPositionChanged, (), (override));
MOCK_METHOD(void, OnCursorShapeChanged, (), (override));
MOCK_METHOD(void, OnDesktopFrameChanged, (), (override));
MOCK_METHOD(void, OnFailedToProcessBuffer, (), (override));
MOCK_METHOD(void, OnBufferCorruptedMetadata, (), (override));
MOCK_METHOD(void, OnBufferCorruptedData, (), (override));
MOCK_METHOD(void, OnEmptyBuffer, (), (override));
MOCK_METHOD(void, OnStreamConfigured, (), (override));
MOCK_METHOD(void, OnFrameRateChanged, (uint32_t), (override));
void SetUp() override {
shared_screencast_stream_ = SharedScreenCastStream::CreateDefault();
shared_screencast_stream_->SetObserver(this);
test_screencast_stream_provider_ =
std::make_unique<TestScreenCastStreamProvider>(this, kWidth, kHeight);
}
void StartScreenCastStream(uint32_t stream_node_id) {
shared_screencast_stream_->StartScreenCastStream(stream_node_id);
}
protected:
uint recorded_frames_ = 0;
bool streaming_ = false;
std::unique_ptr<TestScreenCastStreamProvider>
test_screencast_stream_provider_;
rtc::scoped_refptr<SharedScreenCastStream> shared_screencast_stream_;
};
TEST_F(PipeWireStreamTest, TestPipeWire) {
// Set expectations for PipeWire to successfully connect both streams
rtc::Event waitConnectEvent;
rtc::Event waitStartStreamingEvent;
rtc::Event waitStreamParamChangedEvent1;
rtc::Event waitStreamParamChangedEvent2;
EXPECT_CALL(*this, OnStreamReady(_))
.WillOnce(Invoke(this, &PipeWireStreamTest::StartScreenCastStream));
EXPECT_CALL(*this, OnStreamConfigured).WillOnce([&waitConnectEvent] {
waitConnectEvent.Set();
});
EXPECT_CALL(*this, OnBufferAdded).Times(AtLeast(3));
EXPECT_CALL(*this, OnStartStreaming).WillOnce([&waitStartStreamingEvent] {
waitStartStreamingEvent.Set();
});
EXPECT_CALL(*this, OnFrameRateChanged(60)).Times(1); // Default frame rate.
// Give it some time to connect, the order between these shouldn't matter, but
// we need to be sure we are connected before we proceed to work with frames.
waitConnectEvent.Wait(kLongWait);
// Wait until we start streaming
waitStartStreamingEvent.Wait(kShortWait);
rtc::Event frameRetrievedEvent;
EXPECT_CALL(*this, OnFrameRecorded).Times(6);
EXPECT_CALL(*this, OnDesktopFrameChanged)
.Times(3)
.WillRepeatedly([&frameRetrievedEvent] { frameRetrievedEvent.Set(); });
// Record a frame in FakePipeWireStream
RgbaColor red_color(0, 0, 255);
test_screencast_stream_provider_->RecordFrame(red_color);
// Retrieve a frame from SharedScreenCastStream
frameRetrievedEvent.Wait(kShortWait);
std::unique_ptr<SharedDesktopFrame> frame =
shared_screencast_stream_->CaptureFrame();
// Check frame parameters
ASSERT_NE(frame, nullptr);
ASSERT_NE(frame->data(), nullptr);
EXPECT_EQ(frame->rect().width(), kWidth);
EXPECT_EQ(frame->rect().height(), kHeight);
EXPECT_EQ(frame->stride(), frame->rect().width() * kBytesPerPixel);
EXPECT_EQ(RgbaColor(frame->data()), red_color);
// Test DesktopFrameQueue
RgbaColor green_color(0, 255, 0);
test_screencast_stream_provider_->RecordFrame(green_color);
frameRetrievedEvent.Wait(kShortWait);
std::unique_ptr<SharedDesktopFrame> frame2 =
shared_screencast_stream_->CaptureFrame();
ASSERT_NE(frame2, nullptr);
ASSERT_NE(frame2->data(), nullptr);
EXPECT_EQ(frame2->rect().width(), kWidth);
EXPECT_EQ(frame2->rect().height(), kHeight);
EXPECT_EQ(frame2->stride(), frame->rect().width() * kBytesPerPixel);
EXPECT_EQ(RgbaColor(frame2->data()), green_color);
// Thanks to DesktopFrameQueue we should be able to have two frames shared
EXPECT_EQ(frame->IsShared(), true);
EXPECT_EQ(frame2->IsShared(), true);
EXPECT_NE(frame->data(), frame2->data());
// This should result into overwriting a frame in use
rtc::Event frameRecordedEvent;
RgbaColor blue_color(255, 0, 0);
EXPECT_CALL(*this, OnFailedToProcessBuffer).WillOnce([&frameRecordedEvent] {
frameRecordedEvent.Set();
});
test_screencast_stream_provider_->RecordFrame(blue_color);
frameRecordedEvent.Wait(kShortWait);
// First frame should be now overwritten with blue color
frameRetrievedEvent.Wait(kShortWait);
EXPECT_EQ(RgbaColor(frame->data()), blue_color);
// Check we don't process faulty buffers
rtc::Event corruptedMetadataFrameEvent;
EXPECT_CALL(*this, OnBufferCorruptedMetadata)
.WillOnce([&corruptedMetadataFrameEvent] {
corruptedMetadataFrameEvent.Set();
});
test_screencast_stream_provider_->RecordFrame(
blue_color, TestScreenCastStreamProvider::CorruptedMetadata);
corruptedMetadataFrameEvent.Wait(kShortWait);
rtc::Event corruptedDataFrameEvent;
EXPECT_CALL(*this, OnBufferCorruptedData)
.WillOnce([&corruptedDataFrameEvent] { corruptedDataFrameEvent.Set(); });
test_screencast_stream_provider_->RecordFrame(
blue_color, TestScreenCastStreamProvider::CorruptedData);
corruptedDataFrameEvent.Wait(kShortWait);
rtc::Event emptyFrameEvent;
EXPECT_CALL(*this, OnEmptyBuffer).WillOnce([&emptyFrameEvent] {
emptyFrameEvent.Set();
});
test_screencast_stream_provider_->RecordFrame(
blue_color, TestScreenCastStreamProvider::EmptyData);
emptyFrameEvent.Wait(kShortWait);
// Update stream parameters.
EXPECT_CALL(*this, OnFrameRateChanged(0))
.Times(1)
.WillOnce([&waitStreamParamChangedEvent1] {
waitStreamParamChangedEvent1.Set();
});
shared_screencast_stream_->UpdateScreenCastStreamFrameRate(0);
waitStreamParamChangedEvent1.Wait(kShortWait);
EXPECT_CALL(*this, OnFrameRateChanged(22))
.Times(1)
.WillOnce([&waitStreamParamChangedEvent2] {
waitStreamParamChangedEvent2.Set();
});
shared_screencast_stream_->UpdateScreenCastStreamFrameRate(22);
waitStreamParamChangedEvent2.Wait(kShortWait);
// Test disconnection from stream
EXPECT_CALL(*this, OnStopStreaming);
shared_screencast_stream_->StopScreenCastStream();
}
} // namespace webrtc