Add latency to remote source api.
Latency corresponds to base minimum delay on NetEq. Bug: webrtc:10287 Change-Id: I538d202e3e4fe07b779c46bf560e2fde38e0468e Reviewed-on: https://webrtc-review.googlesource.com/c/121704 Commit-Queue: Ruslan Burakov <kuddai@google.com> Reviewed-by: Steve Anton <steveanton@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26724}
This commit is contained in:
parent
86f09741a7
commit
7ea460593c
@ -32,4 +32,8 @@ const cricket::AudioOptions AudioSourceInterface::options() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
double AudioSourceInterface::GetLatency() const {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -201,6 +201,12 @@ class AudioSourceInterface : public MediaSourceInterface {
|
||||
// be applied in the track in a way that does not affect clones of the track.
|
||||
virtual void SetVolume(double volume) {}
|
||||
|
||||
// Sets the minimum latency of the remote source until audio playout. Actual
|
||||
// observered latency may differ depending on the source. |latency| is in the
|
||||
// range of [0.0, 10.0] seconds.
|
||||
virtual void SetLatency(double latency) {}
|
||||
virtual double GetLatency() const;
|
||||
|
||||
// Registers/unregisters observers to the audio source.
|
||||
virtual void RegisterAudioObserver(AudioObserver* observer) {}
|
||||
virtual void UnregisterAudioObserver(AudioObserver* observer) {}
|
||||
|
||||
@ -120,12 +120,14 @@ bool FakeVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
|
||||
if (!RtpHelper<VoiceMediaChannel>::AddRecvStream(sp))
|
||||
return false;
|
||||
output_scalings_[sp.first_ssrc()] = 1.0;
|
||||
output_delays_[sp.first_ssrc()] = 0;
|
||||
return true;
|
||||
}
|
||||
bool FakeVoiceMediaChannel::RemoveRecvStream(uint32_t ssrc) {
|
||||
if (!RtpHelper<VoiceMediaChannel>::RemoveRecvStream(ssrc))
|
||||
return false;
|
||||
output_scalings_.erase(ssrc);
|
||||
output_delays_.erase(ssrc);
|
||||
return true;
|
||||
}
|
||||
bool FakeVoiceMediaChannel::CanInsertDtmf() {
|
||||
@ -163,6 +165,23 @@ bool FakeVoiceMediaChannel::GetOutputVolume(uint32_t ssrc, double* volume) {
|
||||
*volume = output_scalings_[ssrc];
|
||||
return true;
|
||||
}
|
||||
bool FakeVoiceMediaChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
|
||||
int delay_ms) {
|
||||
if (output_delays_.find(ssrc) == output_delays_.end()) {
|
||||
return false;
|
||||
} else {
|
||||
output_delays_[ssrc] = delay_ms;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
absl::optional<int> FakeVoiceMediaChannel::GetBaseMinimumPlayoutDelayMs(
|
||||
uint32_t ssrc) const {
|
||||
const auto it = output_delays_.find(ssrc);
|
||||
if (it != output_delays_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
bool FakeVoiceMediaChannel::GetStats(VoiceMediaInfo* info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -349,6 +349,10 @@ class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> {
|
||||
bool SetOutputVolume(uint32_t ssrc, double volume) override;
|
||||
bool GetOutputVolume(uint32_t ssrc, double* volume);
|
||||
|
||||
bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
|
||||
absl::optional<int> GetBaseMinimumPlayoutDelayMs(
|
||||
uint32_t ssrc) const override;
|
||||
|
||||
bool GetStats(VoiceMediaInfo* info) override;
|
||||
|
||||
void SetRawAudioSink(
|
||||
@ -384,6 +388,7 @@ class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> {
|
||||
std::vector<AudioCodec> recv_codecs_;
|
||||
std::vector<AudioCodec> send_codecs_;
|
||||
std::map<uint32_t, double> output_scalings_;
|
||||
std::map<uint32_t, int> output_delays_;
|
||||
std::vector<DtmfInfo> dtmf_info_queue_;
|
||||
AudioOptions options_;
|
||||
std::map<uint32_t, std::unique_ptr<VoiceChannelAudioSink>> local_sinks_;
|
||||
|
||||
@ -737,6 +737,13 @@ class VoiceMediaChannel : public MediaChannel {
|
||||
AudioSource* source) = 0;
|
||||
// Set speaker output volume of the specified ssrc.
|
||||
virtual bool SetOutputVolume(uint32_t ssrc, double volume) = 0;
|
||||
// Set base minimum delay of the receive stream with specified ssrc.
|
||||
// Base minimum delay sets lower bound on minimum delay value which
|
||||
// determines minimum delay until audio playout.
|
||||
// Returns false if there is no stream with given ssrc.
|
||||
virtual bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) = 0;
|
||||
virtual absl::optional<int> GetBaseMinimumPlayoutDelayMs(
|
||||
uint32_t ssrc) const = 0;
|
||||
// Returns if the telephone-event has been negotiated.
|
||||
virtual bool CanInsertDtmf() = 0;
|
||||
// Send a DTMF |event|. The DTMF out-of-band signal will be used.
|
||||
|
||||
@ -94,6 +94,9 @@ class FakeAudioReceiveStream final : public webrtc::AudioReceiveStream {
|
||||
float gain() const { return gain_; }
|
||||
bool DeliverRtp(const uint8_t* packet, size_t length, int64_t packet_time_us);
|
||||
bool started() const { return started_; }
|
||||
int base_mininum_playout_delay_ms() const {
|
||||
return base_mininum_playout_delay_ms_;
|
||||
}
|
||||
|
||||
private:
|
||||
// webrtc::AudioReceiveStream implementation.
|
||||
@ -105,11 +108,11 @@ class FakeAudioReceiveStream final : public webrtc::AudioReceiveStream {
|
||||
void SetSink(webrtc::AudioSinkInterface* sink) override;
|
||||
void SetGain(float gain) override;
|
||||
bool SetBaseMinimumPlayoutDelayMs(int delay_ms) override {
|
||||
base_minimum_playout_delay_ms_ = delay_ms;
|
||||
base_mininum_playout_delay_ms_ = delay_ms;
|
||||
return true;
|
||||
}
|
||||
int GetBaseMinimumPlayoutDelayMs() const override {
|
||||
return base_minimum_playout_delay_ms_;
|
||||
return base_mininum_playout_delay_ms_;
|
||||
}
|
||||
std::vector<webrtc::RtpSource> GetSources() const override {
|
||||
return std::vector<webrtc::RtpSource>();
|
||||
@ -123,7 +126,7 @@ class FakeAudioReceiveStream final : public webrtc::AudioReceiveStream {
|
||||
float gain_ = 1.0f;
|
||||
rtc::Buffer last_packet_;
|
||||
bool started_ = false;
|
||||
int base_minimum_playout_delay_ms_ = 0;
|
||||
int base_mininum_playout_delay_ms_ = 0;
|
||||
};
|
||||
|
||||
class FakeVideoSendStream final
|
||||
|
||||
@ -1173,6 +1173,29 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream {
|
||||
playout_ = playout;
|
||||
}
|
||||
|
||||
bool SetBaseMinimumPlayoutDelayMs(int delay_ms) {
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
RTC_DCHECK(stream_);
|
||||
if (stream_->SetBaseMinimumPlayoutDelayMs(delay_ms)) {
|
||||
// Memorize only valid delay because during stream recreation it will be
|
||||
// passed to the constructor and it must be valid value.
|
||||
config_.jitter_buffer_min_delay_ms = delay_ms;
|
||||
return true;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Failed to SetBaseMinimumPlayoutDelayMs"
|
||||
<< " on AudioReceiveStream on SSRC="
|
||||
<< config_.rtp.remote_ssrc
|
||||
<< " with delay_ms=" << delay_ms;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int GetBaseMinimumPlayoutDelayMs() const {
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
RTC_DCHECK(stream_);
|
||||
return stream_->GetBaseMinimumPlayoutDelayMs();
|
||||
}
|
||||
|
||||
std::vector<webrtc::RtpSource> GetSources() {
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
RTC_DCHECK(stream_);
|
||||
@ -1952,6 +1975,44 @@ bool WebRtcVoiceMediaChannel::SetOutputVolume(uint32_t ssrc, double volume) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcVoiceMediaChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
|
||||
int delay_ms) {
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
std::vector<uint32_t> ssrcs(1, ssrc);
|
||||
// SSRC of 0 represents the default receive stream.
|
||||
if (ssrc == 0) {
|
||||
default_recv_base_minimum_delay_ms_ = delay_ms;
|
||||
ssrcs = unsignaled_recv_ssrcs_;
|
||||
}
|
||||
for (uint32_t ssrc : ssrcs) {
|
||||
const auto it = recv_streams_.find(ssrc);
|
||||
if (it == recv_streams_.end()) {
|
||||
RTC_LOG(LS_WARNING) << "SetBaseMinimumPlayoutDelayMs: no recv stream "
|
||||
<< ssrc;
|
||||
return false;
|
||||
}
|
||||
it->second->SetBaseMinimumPlayoutDelayMs(delay_ms);
|
||||
RTC_LOG(LS_INFO) << "SetBaseMinimumPlayoutDelayMs() to " << delay_ms
|
||||
<< " for recv stream with ssrc " << ssrc;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
absl::optional<int> WebRtcVoiceMediaChannel::GetBaseMinimumPlayoutDelayMs(
|
||||
uint32_t ssrc) const {
|
||||
// SSRC of 0 represents the default receive stream.
|
||||
if (ssrc == 0) {
|
||||
return default_recv_base_minimum_delay_ms_;
|
||||
}
|
||||
|
||||
const auto it = recv_streams_.find(ssrc);
|
||||
|
||||
if (it != recv_streams_.end()) {
|
||||
return it->second->GetBaseMinimumPlayoutDelayMs();
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
bool WebRtcVoiceMediaChannel::CanInsertDtmf() {
|
||||
return dtmf_payload_type_.has_value() && send_;
|
||||
}
|
||||
@ -2047,6 +2108,7 @@ void WebRtcVoiceMediaChannel::OnPacketReceived(rtc::CopyOnWriteBuffer* packet,
|
||||
RTC_DCHECK_GE(kMaxUnsignaledRecvStreams, unsignaled_recv_ssrcs_.size());
|
||||
|
||||
SetOutputVolume(ssrc, default_recv_volume_);
|
||||
SetBaseMinimumPlayoutDelayMs(ssrc, default_recv_base_minimum_delay_ms_);
|
||||
|
||||
// The default sink can only be attached to one stream at a time, so we hook
|
||||
// it up to the *latest* unsignaled stream we've seen, in order to support the
|
||||
|
||||
@ -196,6 +196,10 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
|
||||
// SSRC=0 will apply the new volume to current and future unsignaled streams.
|
||||
bool SetOutputVolume(uint32_t ssrc, double volume) override;
|
||||
|
||||
bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
|
||||
absl::optional<int> GetBaseMinimumPlayoutDelayMs(
|
||||
uint32_t ssrc) const override;
|
||||
|
||||
bool CanInsertDtmf() override;
|
||||
bool InsertDtmf(uint32_t ssrc, int event, int duration) override;
|
||||
|
||||
@ -295,6 +299,10 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
|
||||
|
||||
// Volume for unsignaled streams, which may be set before the stream exists.
|
||||
double default_recv_volume_ = 1.0;
|
||||
|
||||
// Delay for unsignaled streams, which may be set before the stream exists.
|
||||
int default_recv_base_minimum_delay_ms_ = 0;
|
||||
|
||||
// Sink for latest unsignaled stream - may be set before the stream exists.
|
||||
std::unique_ptr<webrtc::AudioSinkInterface> default_sink_;
|
||||
// Default SSRC to use for RTCP receiver reports in case of no signaled
|
||||
|
||||
@ -3168,6 +3168,65 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOutputVolumeUnsignaledRecvStream) {
|
||||
EXPECT_DOUBLE_EQ(4, GetRecvStream(kSsrcX).gain());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVoiceEngineTestFake, BaseMinimumPlayoutDelayMs) {
|
||||
EXPECT_TRUE(SetupChannel());
|
||||
EXPECT_FALSE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcY, 200));
|
||||
EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
|
||||
|
||||
cricket::StreamParams stream;
|
||||
stream.ssrcs.push_back(kSsrcY);
|
||||
EXPECT_TRUE(channel_->AddRecvStream(stream));
|
||||
EXPECT_EQ(0, GetRecvStream(kSsrcY).base_mininum_playout_delay_ms());
|
||||
EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcY, 300));
|
||||
EXPECT_EQ(300, GetRecvStream(kSsrcY).base_mininum_playout_delay_ms());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVoiceEngineTestFake,
|
||||
BaseMinimumPlayoutDelayMsUnsignaledRecvStream) {
|
||||
// Here base minimum delay is abbreviated to delay in comments for shortness.
|
||||
EXPECT_TRUE(SetupChannel());
|
||||
|
||||
// Spawn an unsignaled stream by sending a packet - delay should be 0.
|
||||
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
|
||||
EXPECT_EQ(0, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
|
||||
// Check that it doesn't provide default values for unknown ssrc.
|
||||
EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
|
||||
|
||||
// Check that default value for unsignaled streams is 0.
|
||||
EXPECT_EQ(0, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
|
||||
|
||||
// Should remember the delay 100 which will be set on new unsignaled streams,
|
||||
// and also set the delay to 100 on existing unsignaled streams.
|
||||
EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrc0, 100));
|
||||
EXPECT_EQ(100, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
|
||||
// Check that it doesn't provide default values for unknown ssrc.
|
||||
EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
|
||||
|
||||
// Spawn an unsignaled stream by sending a packet - delay should be 100.
|
||||
unsigned char pcmuFrame2[sizeof(kPcmuFrame)];
|
||||
memcpy(pcmuFrame2, kPcmuFrame, sizeof(kPcmuFrame));
|
||||
rtc::SetBE32(&pcmuFrame2[8], kSsrcX);
|
||||
DeliverPacket(pcmuFrame2, sizeof(pcmuFrame2));
|
||||
EXPECT_EQ(100, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
|
||||
|
||||
// Setting delay with SSRC=0 should affect all unsignaled streams.
|
||||
EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrc0, 300));
|
||||
if (kMaxUnsignaledRecvStreams > 1) {
|
||||
EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
|
||||
}
|
||||
EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
|
||||
|
||||
// Setting delay on an individual stream affects only that.
|
||||
EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcX, 400));
|
||||
if (kMaxUnsignaledRecvStreams > 1) {
|
||||
EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
|
||||
}
|
||||
EXPECT_EQ(400, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
|
||||
EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
|
||||
// Check that it doesn't provide default values for unknown ssrc.
|
||||
EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVoiceEngineTestFake, SetsSyncGroupFromStreamId) {
|
||||
const uint32_t kAudioSsrc = 123;
|
||||
const std::string kStreamId = "AvSyncLabel";
|
||||
|
||||
@ -4670,7 +4670,7 @@ void PeerConnection::UpdateRemoteSendersList(
|
||||
FindSenderInfo(*current_senders, kDefaultStreamId, default_sender_id);
|
||||
if (!default_sender_info) {
|
||||
current_senders->push_back(
|
||||
RtpSenderInfo(kDefaultStreamId, default_sender_id, 0));
|
||||
RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0));
|
||||
OnRemoteSenderAdded(current_senders->back(), media_type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,11 +20,17 @@
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/location.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_checker.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
constexpr int kDefaultLatency = 0;
|
||||
constexpr int kRoundToZeroThresholdMs = 10;
|
||||
} // namespace
|
||||
|
||||
// This proxy is passed to the underlying media engine to receive audio data as
|
||||
// they come in. The data will then be passed back up to the RemoteAudioSource
|
||||
// which will fan it out to all the sinks that have been added to it.
|
||||
@ -64,6 +70,13 @@ void RemoteAudioSource::Start(cricket::VoiceMediaChannel* media_channel,
|
||||
uint32_t ssrc) {
|
||||
RTC_DCHECK_RUN_ON(main_thread_);
|
||||
RTC_DCHECK(media_channel);
|
||||
// Check that there are no consecutive start calls.
|
||||
RTC_DCHECK(!media_channel_ && !ssrc_);
|
||||
|
||||
// Remember media channel ssrc pair for latency calls.
|
||||
media_channel_ = media_channel;
|
||||
ssrc_ = ssrc;
|
||||
|
||||
// Register for callbacks immediately before AddSink so that we always get
|
||||
// notified when a channel goes out of scope (signaled when "AudioDataProxy"
|
||||
// is destroyed).
|
||||
@ -71,12 +84,22 @@ void RemoteAudioSource::Start(cricket::VoiceMediaChannel* media_channel,
|
||||
media_channel->SetRawAudioSink(ssrc,
|
||||
absl::make_unique<AudioDataProxy>(this));
|
||||
});
|
||||
|
||||
// Trying to apply cached latency for the audio stream.
|
||||
if (cached_latency_) {
|
||||
SetLatency(cached_latency_.value());
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteAudioSource::Stop(cricket::VoiceMediaChannel* media_channel,
|
||||
uint32_t ssrc) {
|
||||
RTC_DCHECK_RUN_ON(main_thread_);
|
||||
RTC_DCHECK(media_channel);
|
||||
|
||||
// Assume that audio stream is no longer present for latency calls.
|
||||
media_channel_ = nullptr;
|
||||
ssrc_ = absl::nullopt;
|
||||
|
||||
worker_thread_->Invoke<void>(
|
||||
RTC_FROM_HERE, [&] { media_channel->SetRawAudioSink(ssrc, nullptr); });
|
||||
}
|
||||
@ -99,6 +122,53 @@ void RemoteAudioSource::SetVolume(double volume) {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteAudioSource::SetLatency(double latency) {
|
||||
RTC_DCHECK_GE(latency, 0);
|
||||
RTC_DCHECK_LE(latency, 10);
|
||||
|
||||
int delay_ms = rtc::dchecked_cast<int>(latency * 1000);
|
||||
// In NetEq 0 delay has special meaning of being unconstrained value that is
|
||||
// why we round delay to 0 if it is small enough during conversion from
|
||||
// latency.
|
||||
if (delay_ms <= kRoundToZeroThresholdMs) {
|
||||
delay_ms = 0;
|
||||
}
|
||||
|
||||
cached_latency_ = latency;
|
||||
SetDelayMs(delay_ms);
|
||||
}
|
||||
|
||||
double RemoteAudioSource::GetLatency() const {
|
||||
absl::optional<int> delay_ms = GetDelayMs();
|
||||
|
||||
if (delay_ms) {
|
||||
return delay_ms.value() / 1000.0;
|
||||
} else {
|
||||
return cached_latency_.value_or(kDefaultLatency);
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteAudioSource::SetDelayMs(int delay_ms) {
|
||||
if (!media_channel_ || !ssrc_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
|
||||
media_channel_->SetBaseMinimumPlayoutDelayMs(ssrc_.value(), delay_ms);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
absl::optional<int> RemoteAudioSource::GetDelayMs() const {
|
||||
if (!media_channel_ || !ssrc_) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
return worker_thread_->Invoke<absl::optional<int>>(RTC_FROM_HERE, [&] {
|
||||
return media_channel_->GetBaseMinimumPlayoutDelayMs(ssrc_.value());
|
||||
});
|
||||
}
|
||||
|
||||
void RemoteAudioSource::RegisterAudioObserver(AudioObserver* observer) {
|
||||
RTC_DCHECK(observer != NULL);
|
||||
RTC_DCHECK(!absl::c_linear_search(audio_observers_, observer));
|
||||
|
||||
@ -46,6 +46,8 @@ class RemoteAudioSource : public Notifier<AudioSourceInterface>,
|
||||
|
||||
// AudioSourceInterface implementation.
|
||||
void SetVolume(double volume) override;
|
||||
void SetLatency(double latency) override;
|
||||
double GetLatency() const override;
|
||||
void RegisterAudioObserver(AudioObserver* observer) override;
|
||||
void UnregisterAudioObserver(AudioObserver* observer) override;
|
||||
|
||||
@ -63,12 +65,19 @@ class RemoteAudioSource : public Notifier<AudioSourceInterface>,
|
||||
|
||||
void OnMessage(rtc::Message* msg) override;
|
||||
|
||||
bool SetDelayMs(int delay_ms);
|
||||
absl::optional<int> GetDelayMs() const;
|
||||
|
||||
rtc::Thread* const main_thread_;
|
||||
rtc::Thread* const worker_thread_;
|
||||
std::list<AudioObserver*> audio_observers_;
|
||||
rtc::CriticalSection sink_lock_;
|
||||
std::list<AudioTrackSinkInterface*> sinks_;
|
||||
SourceState state_;
|
||||
// Media channel and ssrc together uniqely identify audio stream.
|
||||
cricket::VoiceMediaChannel* media_channel_ = nullptr;
|
||||
absl::optional<uint32_t> ssrc_;
|
||||
absl::optional<double> cached_latency_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
#include "pc/dtls_srtp_transport.h"
|
||||
#include "pc/local_audio_source.h"
|
||||
#include "pc/media_stream.h"
|
||||
#include "pc/remote_audio_source.h"
|
||||
#include "pc/rtp_receiver.h"
|
||||
#include "pc/rtp_sender.h"
|
||||
#include "pc/rtp_transport_internal.h"
|
||||
@ -522,6 +523,103 @@ TEST_F(RtpSenderReceiverTest, RemoteAudioTrackSetVolume) {
|
||||
DestroyAudioRtpReceiver();
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderReceiverTest, RemoteAudioSourceLatencyCaching) {
|
||||
absl::optional<int> delay_ms; // In milliseconds.
|
||||
double latency_s = 0.5; // In seconds.
|
||||
rtc::scoped_refptr<RemoteAudioSource> source =
|
||||
new rtc::RefCountedObject<RemoteAudioSource>(rtc::Thread::Current());
|
||||
|
||||
// Check default value.
|
||||
EXPECT_DOUBLE_EQ(source->GetLatency(), 0.0);
|
||||
|
||||
// Check caching behaviour.
|
||||
source->SetLatency(latency_s);
|
||||
EXPECT_DOUBLE_EQ(source->GetLatency(), latency_s);
|
||||
|
||||
// Check that cached value applied on the start.
|
||||
source->Start(voice_media_channel_, kAudioSsrc);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc);
|
||||
EXPECT_DOUBLE_EQ(latency_s, delay_ms.value_or(0) / 1000.0);
|
||||
|
||||
// Check that setting latency changes delay.
|
||||
latency_s = 0.8;
|
||||
source->SetLatency(latency_s);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc);
|
||||
EXPECT_DOUBLE_EQ(latency_s, delay_ms.value_or(0) / 1000.0);
|
||||
EXPECT_DOUBLE_EQ(latency_s, source->GetLatency());
|
||||
|
||||
// Check that if underlying delay is changed then remote source will reflect
|
||||
// it.
|
||||
delay_ms = 300;
|
||||
voice_media_channel_->SetBaseMinimumPlayoutDelayMs(kAudioSsrc,
|
||||
delay_ms.value());
|
||||
EXPECT_DOUBLE_EQ(source->GetLatency(), delay_ms.value() / 1000.0);
|
||||
|
||||
// Check that after stop we get last cached value.
|
||||
source->Stop(voice_media_channel_, kAudioSsrc);
|
||||
EXPECT_DOUBLE_EQ(latency_s, source->GetLatency());
|
||||
|
||||
// Check that if we start source again with new ssrc then cached value is
|
||||
// applied.
|
||||
source->Start(voice_media_channel_, kAudioSsrc2);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc2);
|
||||
EXPECT_DOUBLE_EQ(latency_s, delay_ms.value_or(0) / 1000.0);
|
||||
|
||||
// Check rounding behavior.
|
||||
source->SetLatency(2 / 1000.0);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc2);
|
||||
EXPECT_EQ(0, delay_ms.value_or(-1));
|
||||
EXPECT_DOUBLE_EQ(0, source->GetLatency());
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderReceiverTest, RemoteAudioSourceLatencyNoCaching) {
|
||||
int delay_ms = 300; // In milliseconds.
|
||||
rtc::scoped_refptr<RemoteAudioSource> source =
|
||||
new rtc::RefCountedObject<RemoteAudioSource>(rtc::Thread::Current());
|
||||
|
||||
// Set it to value different from default zero.
|
||||
voice_media_channel_->SetBaseMinimumPlayoutDelayMs(kAudioSsrc, delay_ms);
|
||||
|
||||
// Check that calling GetLatency on the source that hasn't been started yet
|
||||
// won't trigger caching.
|
||||
EXPECT_DOUBLE_EQ(source->GetLatency(), 0);
|
||||
source->Start(voice_media_channel_, kAudioSsrc);
|
||||
EXPECT_DOUBLE_EQ(source->GetLatency(), delay_ms / 1000.0);
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderReceiverTest, RemoteAudioTrackSetLatency) {
|
||||
CreateAudioRtpReceiver();
|
||||
|
||||
absl::optional<int> delay_ms; // In milliseconds.
|
||||
double latency_s = 0.5; // In seconds.
|
||||
audio_track_->GetSource()->SetLatency(latency_s);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc);
|
||||
EXPECT_DOUBLE_EQ(latency_s, delay_ms.value_or(0) / 1000.0);
|
||||
|
||||
// Disabling the track should take no effect on previously set value.
|
||||
audio_track_->set_enabled(false);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc);
|
||||
EXPECT_DOUBLE_EQ(latency_s, delay_ms.value_or(0) / 1000.0);
|
||||
|
||||
// When the track is disabled, we still should be able to set latency.
|
||||
latency_s = 0.3;
|
||||
audio_track_->GetSource()->SetLatency(latency_s);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc);
|
||||
EXPECT_DOUBLE_EQ(latency_s, delay_ms.value_or(0) / 1000.0);
|
||||
|
||||
// Enabling the track should take no effect on previously set value.
|
||||
audio_track_->set_enabled(true);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc);
|
||||
EXPECT_DOUBLE_EQ(latency_s, delay_ms.value_or(0) / 1000.0);
|
||||
|
||||
// We still should be able to change latency.
|
||||
latency_s = 0.0;
|
||||
audio_track_->GetSource()->SetLatency(latency_s);
|
||||
delay_ms = voice_media_channel_->GetBaseMinimumPlayoutDelayMs(kAudioSsrc);
|
||||
EXPECT_EQ(0, delay_ms.value_or(-1));
|
||||
EXPECT_DOUBLE_EQ(latency_s, delay_ms.value_or(0) / 1000.0);
|
||||
}
|
||||
|
||||
// Test that the media channel isn't enabled for sending if the audio sender
|
||||
// doesn't have both a track and SSRC.
|
||||
TEST_F(RtpSenderReceiverTest, AudioSenderWithoutTrackAndSsrc) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user