blob: 3aed0abd1db0d8dde3cf877e7c6681be4eb6b0f4 [file] [edit]
/*
* Copyright 2021 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 "pc/audio_rtp_receiver.h"
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "api/call/audio_sink.h"
#include "api/make_ref_counted.h"
#include "api/media_stream_interface.h"
#include "api/rtp_packet_info.h"
#include "api/rtp_packet_infos.h"
#include "api/scoped_refptr.h"
#include "api/test/rtc_error_matchers.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "media/base/media_channel.h"
#include "pc/test/mock_voice_media_receive_channel_interface.h"
#include "rtc_base/thread.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/run_loop.h"
#include "test/wait_until.h"
using ::testing::_;
using ::testing::Eq;
static const int kTimeOut = 100;
static const double kDefaultVolume = 1;
static const double kVolume = 3.7;
static const double kVolumeMuted = 0.0;
static const uint32_t kSsrc = 3;
namespace webrtc {
class AudioRtpReceiverTest : public ::testing::Test {
protected:
AudioRtpReceiverTest()
: worker_thread_(Thread::Create()),
receiver_(make_ref_counted<AudioRtpReceiver>(
worker_thread_.get(),
std::string(),
std::vector<std::string>(),
/*enable_sframe_at_owner=*/nullptr)) {
worker_thread_->Start();
EXPECT_CALL(receive_channel_, SetRawAudioSink(kSsrc, _));
EXPECT_CALL(receive_channel_, SetBaseMinimumPlayoutDelayMs(kSsrc, _));
}
~AudioRtpReceiverTest() override {
EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolumeMuted));
SetMediaChannel(nullptr);
}
void SetMediaChannel(MediaReceiveChannelInterface* media_channel) {
worker_thread_->BlockingCall(
[&]() { receiver_->SetMediaChannel(media_channel); });
}
test::RunLoop loop_;
std::unique_ptr<Thread> worker_thread_;
scoped_refptr<AudioRtpReceiver> receiver_;
MockVoiceMediaReceiveChannelInterface receive_channel_;
};
TEST_F(AudioRtpReceiverTest, SetOutputVolumeIsCalled) {
std::atomic_int set_volume_calls(0);
EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kDefaultVolume))
.WillOnce([&] {
set_volume_calls++;
return true;
});
receiver_->track();
receiver_->track()->set_enabled(true);
SetMediaChannel(&receive_channel_);
EXPECT_CALL(receive_channel_, SetDefaultRawAudioSink(_)).Times(0);
auto setup_task = receiver_->GetSetupForMediaChannel(kSsrc);
worker_thread_->BlockingCall([&]() { std::move(setup_task)(); });
EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolume)).WillOnce([&] {
set_volume_calls++;
return true;
});
receiver_->OnSetVolume(kVolume);
EXPECT_THAT(WaitUntil([&] { return set_volume_calls.load(); }, Eq(2),
{.timeout = TimeDelta::Millis(kTimeOut)}),
IsRtcOk());
}
TEST_F(AudioRtpReceiverTest, VolumesSetBeforeStartingAreRespected) {
// Set the volume before setting the media channel. It should still be used
// as the initial volume.
receiver_->OnSetVolume(kVolume);
receiver_->track()->set_enabled(true);
SetMediaChannel(&receive_channel_);
// The previosly set initial volume should be propagated to the provided
// media_channel_ as soon as GetSetupForMediaChannel is called.
EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolume));
auto setup_task = receiver_->GetSetupForMediaChannel(kSsrc);
worker_thread_->BlockingCall([&]() { std::move(setup_task)(); });
}
TEST(AudioRtpReceiver, PropagatesPacketInfosToTrackSink) {
test::RunLoop loop;
std::unique_ptr<Thread> worker_thread = Thread::Create();
worker_thread->Start();
MockVoiceMediaReceiveChannelInterface receive_channel;
auto receiver = make_ref_counted<AudioRtpReceiver>(
worker_thread.get(), std::string(), std::vector<std::string>(),
/*enable_sframe_at_owner=*/nullptr);
std::unique_ptr<AudioSinkInterface> audio_sink;
EXPECT_CALL(receive_channel, SetRawAudioSink(kSsrc, _))
.WillOnce(
[&](uint32_t /* ssrc */, std::unique_ptr<AudioSinkInterface> sink) {
audio_sink = std::move(sink);
});
EXPECT_CALL(receive_channel, SetBaseMinimumPlayoutDelayMs(kSsrc, _));
class TestTrackSink : public AudioTrackSinkInterface {
public:
using AudioTrackSinkInterface::OnData;
void OnData(const void* /* audio_data */,
int /* bits_per_sample */,
int /* sample_rate */,
size_t /* number_of_channels */,
size_t /* number_of_frames */,
std::optional<int64_t> /* absolute_capture_timestamp_ms */,
const RtpPacketInfos& packet_infos) override {
called_ = true;
received_packet_infos_ = packet_infos;
}
bool called_ = false;
RtpPacketInfos received_packet_infos_;
};
TestTrackSink sink;
receiver->audio_track()->AddSink(&sink);
receiver->track()->set_enabled(true);
worker_thread->BlockingCall(
[&]() { receiver->SetMediaChannel(&receive_channel); });
auto setup_task = receiver->GetSetupForMediaChannel(kSsrc);
worker_thread->BlockingCall([&]() { std::move(setup_task)(); });
ASSERT_TRUE(audio_sink != nullptr);
RtpPacketInfos::vector_type infos_vec;
infos_vec.emplace_back(
RtpPacketInfo(/*ssrc=*/kSsrc, /*csrcs=*/{5678},
/*rtp_timestamp=*/9999,
/*receive_time=*/Timestamp::Millis(100)));
RtpPacketInfos packet_infos(std::move(infos_vec));
int16_t dummy_data[160] = {0};
AudioSinkInterface::Data audio_data(dummy_data, 160, 16000, 1, 9999,
&packet_infos);
audio_sink->OnData(audio_data);
EXPECT_TRUE(sink.called_);
ASSERT_EQ(sink.received_packet_infos_.size(), 1u);
EXPECT_EQ(sink.received_packet_infos_[0].ssrc(), kSsrc);
EXPECT_THAT(sink.received_packet_infos_[0].csrcs(),
::testing::ElementsAre(5678));
receiver->audio_track()->RemoveSink(&sink);
EXPECT_CALL(receive_channel, SetOutputVolume(kSsrc, kVolumeMuted));
worker_thread->BlockingCall([&]() { receiver->SetMediaChannel(nullptr); });
}
// Tests that OnChanged notifications are processed correctly on the worker
// thread when a media channel pointer is passed to the receiver via the
// constructor.
TEST(AudioRtpReceiver, OnChangedNotificationsAfterConstruction) {
test::RunLoop loop;
std::unique_ptr<Thread> worker_thread = Thread::Create();
worker_thread->Start();
MockVoiceMediaReceiveChannelInterface receive_channel;
auto receiver = make_ref_counted<AudioRtpReceiver>(
worker_thread.get(), std::string(), std::vector<std::string>(),
/*enable_sframe_at_owner=*/nullptr, &receive_channel);
EXPECT_CALL(receive_channel, SetDefaultRawAudioSink(_)).Times(1);
EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kDefaultVolume)).Times(1);
auto setup_task = receiver->GetSetupForUnsignaledMediaChannel();
worker_thread->BlockingCall([&]() { std::move(setup_task)(); });
// When the track is marked as disabled, an async notification is queued
// for the worker thread. This notification should trigger the volume
// of the media channel to be set to kVolumeMuted.
// Set the expectation first for the call, before changing the track state.
EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kVolumeMuted)).Times(1);
// Mark the track as disabled.
receiver->track()->set_enabled(false);
// Flush the worker thread.
worker_thread->BlockingCall([] {});
EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kVolumeMuted)).Times(1);
worker_thread->BlockingCall([&]() { receiver->SetMediaChannel(nullptr); });
}
} // namespace webrtc