blob: 92bc786adefe27ec8e195e089c6edf668406a22f [file] [log] [blame]
// Copyright (c) 2017 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 "content/renderer/media/webrtc/rtc_rtp_sender.h"
#include <memory>
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/test/scoped_task_environment.h"
#include "build/build_config.h"
#include "content/child/child_process.h"
#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
#include "content/renderer/media/webrtc/test/webrtc_stats_report_obtainer.h"
#include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/platform/web_media_stream_source.h"
#include "third_party/blink/public/platform/web_media_stream_track.h"
#include "third_party/blink/public/platform/web_rtc_stats.h"
#include "third_party/blink/public/platform/web_rtc_void_request.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/web/web_heap.h"
#include "third_party/webrtc/api/stats/rtc_stats_report.h"
#include "third_party/webrtc/api/stats/rtcstats_objects.h"
#include "third_party/webrtc/api/test/mock_rtpsender.h"
using ::testing::_;
using ::testing::Return;
namespace content {
class RTCRtpSenderTest : public ::testing::Test {
public:
void SetUp() override {
dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
main_thread_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
track_map_ = new WebRtcMediaStreamTrackAdapterMap(dependency_factory_.get(),
main_thread_);
peer_connection_ = new rtc::RefCountedObject<MockPeerConnectionImpl>(
dependency_factory_.get(), nullptr);
mock_webrtc_sender_ = new rtc::RefCountedObject<webrtc::MockRtpSender>();
}
void TearDown() override {
sender_.reset();
// Syncing up with the signaling thread ensures any pending operations on
// that thread are executed. If they post back to the main thread, such as
// the sender's destructor traits, this is allowed to execute before the
// test shuts down the threads.
SyncWithSignalingThread();
blink::WebHeap::CollectAllGarbageForTesting();
}
// Wait for the signaling thread to perform any queued tasks, executing tasks
// posted to the current thread in the meantime while waiting.
void SyncWithSignalingThread() const {
base::RunLoop run_loop;
dependency_factory_->GetWebRtcSignalingThread()->PostTask(
FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
}
blink::WebMediaStreamTrack CreateWebTrack(const std::string& id) {
blink::WebMediaStreamSource web_source;
web_source.Initialize(
blink::WebString::FromUTF8(id), blink::WebMediaStreamSource::kTypeAudio,
blink::WebString::FromUTF8("local_audio_track"), false);
blink::MediaStreamAudioSource* audio_source =
new blink::MediaStreamAudioSource(true);
// Takes ownership of |audio_source|.
web_source.SetPlatformSource(base::WrapUnique(audio_source));
blink::WebMediaStreamTrack web_track;
web_track.Initialize(web_source.Id(), web_source);
audio_source->ConnectToTrack(web_track);
return web_track;
}
std::unique_ptr<RTCRtpSender> CreateSender(
blink::WebMediaStreamTrack web_track) {
std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> track_ref;
if (!web_track.IsNull()) {
track_ref = track_map_->GetOrCreateLocalTrackAdapter(web_track);
DCHECK(track_ref->is_initialized());
}
RtpSenderState sender_state(main_thread_,
dependency_factory_->GetWebRtcSignalingThread(),
mock_webrtc_sender_.get(), std::move(track_ref),
std::vector<std::string>());
sender_state.Initialize();
return std::make_unique<RTCRtpSender>(peer_connection_.get(), track_map_,
std::move(sender_state));
}
// Calls replaceTrack(), which is asynchronous, returning a callback that when
// invoked waits for (run-loops) the operation to complete and returns whether
// replaceTrack() was successful.
base::OnceCallback<bool()> ReplaceTrack(
blink::WebMediaStreamTrack web_track) {
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
std::unique_ptr<bool> result_holder(new bool());
// On complete, |*result_holder| is set with the result of replaceTrack()
// and the |run_loop| quit.
sender_->ReplaceTrack(
web_track, base::BindOnce(&RTCRtpSenderTest::CallbackOnComplete,
base::Unretained(this), result_holder.get(),
run_loop.get()));
// When the resulting callback is invoked, waits for |run_loop| to complete
// and returns |*result_holder|.
return base::BindOnce(&RTCRtpSenderTest::RunLoopAndReturnResult,
base::Unretained(this), std::move(result_holder),
std::move(run_loop));
}
scoped_refptr<WebRTCStatsReportObtainer> CallGetStats() {
scoped_refptr<WebRTCStatsReportObtainer> obtainer =
new WebRTCStatsReportObtainer();
sender_->GetStats(obtainer->GetStatsCallbackWrapper(),
blink::RTCStatsFilter::kIncludeOnlyStandardMembers);
return obtainer;
}
protected:
void CallbackOnComplete(bool* result_out,
base::RunLoop* run_loop,
bool result) {
*result_out = result;
run_loop->Quit();
}
bool RunLoopAndReturnResult(std::unique_ptr<bool> result_holder,
std::unique_ptr<base::RunLoop> run_loop) {
run_loop->Run();
return *result_holder;
}
// Code under test expects to be run in a process with an initialized
// ChildProcess, which requires TaskScheduler, and a main-thread MessageLoop,
// which the ScopedTaskEnvironment also provides.
base::test::ScopedTaskEnvironment task_environment_;
ChildProcess child_process_;
std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_map_;
rtc::scoped_refptr<MockPeerConnectionImpl> peer_connection_;
rtc::scoped_refptr<webrtc::MockRtpSender> mock_webrtc_sender_;
std::unique_ptr<RTCRtpSender> sender_;
};
TEST_F(RTCRtpSenderTest, CreateSender) {
auto web_track = CreateWebTrack("track_id");
sender_ = CreateSender(web_track);
EXPECT_FALSE(sender_->Track().IsNull());
EXPECT_EQ(web_track.UniqueId(), sender_->Track().UniqueId());
}
TEST_F(RTCRtpSenderTest, CreateSenderWithNullTrack) {
blink::WebMediaStreamTrack null_track;
sender_ = CreateSender(null_track);
EXPECT_TRUE(sender_->Track().IsNull());
}
TEST_F(RTCRtpSenderTest, ReplaceTrackSetsTrack) {
auto web_track1 = CreateWebTrack("track1");
sender_ = CreateSender(web_track1);
auto web_track2 = CreateWebTrack("track2");
EXPECT_CALL(*mock_webrtc_sender_, SetTrack(_)).WillOnce(Return(true));
auto replaceTrackRunLoopAndGetResult = ReplaceTrack(web_track2);
EXPECT_TRUE(std::move(replaceTrackRunLoopAndGetResult).Run());
ASSERT_FALSE(sender_->Track().IsNull());
EXPECT_EQ(web_track2.UniqueId(), sender_->Track().UniqueId());
}
TEST_F(RTCRtpSenderTest, ReplaceTrackWithNullTrack) {
auto web_track = CreateWebTrack("track_id");
sender_ = CreateSender(web_track);
blink::WebMediaStreamTrack null_track;
EXPECT_CALL(*mock_webrtc_sender_, SetTrack(_)).WillOnce(Return(true));
auto replaceTrackRunLoopAndGetResult = ReplaceTrack(null_track);
EXPECT_TRUE(std::move(replaceTrackRunLoopAndGetResult).Run());
EXPECT_TRUE(sender_->Track().IsNull());
}
TEST_F(RTCRtpSenderTest, ReplaceTrackCanFail) {
auto web_track = CreateWebTrack("track_id");
sender_ = CreateSender(web_track);
ASSERT_FALSE(sender_->Track().IsNull());
EXPECT_EQ(web_track.UniqueId(), sender_->Track().UniqueId());
blink::WebMediaStreamTrack null_track;
EXPECT_CALL(*mock_webrtc_sender_, SetTrack(_)).WillOnce(Return(false));
auto replaceTrackRunLoopAndGetResult = ReplaceTrack(null_track);
EXPECT_FALSE(std::move(replaceTrackRunLoopAndGetResult).Run());
// The track should not have been set.
ASSERT_FALSE(sender_->Track().IsNull());
EXPECT_EQ(web_track.UniqueId(), sender_->Track().UniqueId());
}
TEST_F(RTCRtpSenderTest, ReplaceTrackIsNotSetSynchronously) {
auto web_track1 = CreateWebTrack("track1");
sender_ = CreateSender(web_track1);
auto web_track2 = CreateWebTrack("track2");
EXPECT_CALL(*mock_webrtc_sender_, SetTrack(_)).WillOnce(Return(true));
auto replaceTrackRunLoopAndGetResult = ReplaceTrack(web_track2);
// The track should not be set until the run loop has executed.
ASSERT_FALSE(sender_->Track().IsNull());
EXPECT_NE(web_track2.UniqueId(), sender_->Track().UniqueId());
// Wait for operation to run to ensure EXPECT_CALL is satisfied.
std::move(replaceTrackRunLoopAndGetResult).Run();
}
TEST_F(RTCRtpSenderTest, GetStats) {
auto web_track = CreateWebTrack("track_id");
sender_ = CreateSender(web_track);
// Make the mock return a blink version of the |webtc_report|. The mock does
// not perform any stats filtering, we just set it to a dummy value.
rtc::scoped_refptr<webrtc::RTCStatsReport> webrtc_report =
webrtc::RTCStatsReport::Create(0u);
webrtc_report->AddStats(
std::make_unique<webrtc::RTCOutboundRTPStreamStats>("stats-id", 1234u));
peer_connection_->SetGetStatsReport(webrtc_report);
auto obtainer = CallGetStats();
// Make sure the operation is async.
EXPECT_FALSE(obtainer->report());
// Wait for the report, this performs the necessary run-loop.
auto* report = obtainer->WaitForReport();
EXPECT_TRUE(report);
// Verify dummy value.
EXPECT_EQ(report->Size(), 1u);
auto stats = report->GetStats(blink::WebString::FromUTF8("stats-id"));
EXPECT_TRUE(stats);
EXPECT_EQ(stats->Timestamp(), 1.234);
}
TEST_F(RTCRtpSenderTest, CopiedSenderSharesInternalStates) {
auto web_track = CreateWebTrack("track_id");
sender_ = CreateSender(web_track);
auto copy = std::make_unique<RTCRtpSender>(*sender_);
// Copy shares original's ID.
EXPECT_EQ(sender_->Id(), copy->Id());
blink::WebMediaStreamTrack null_track;
EXPECT_CALL(*mock_webrtc_sender_, SetTrack(_)).WillOnce(Return(true));
auto replaceTrackRunLoopAndGetResult = ReplaceTrack(null_track);
EXPECT_TRUE(std::move(replaceTrackRunLoopAndGetResult).Run());
// Both original and copy shows a modified state.
EXPECT_TRUE(sender_->Track().IsNull());
EXPECT_TRUE(copy->Track().IsNull());
}
} // namespace content