Moving WebRtcVoiceMediaChannel::SendSetCodec to AudioSendStream.
BUG=webrtc:5806, webrtc:4690 Review-Url: https://codereview.webrtc.org/2405183002 Cr-Commit-Position: refs/heads/master@{#14700}
This commit is contained in:
parent
1cb48232ac
commit
7a973447eb
@ -83,17 +83,60 @@ class AudioSendStream {
|
||||
// of Call.
|
||||
int voe_channel_id = -1;
|
||||
|
||||
// Ownership of the encoder object is transferred to Call when the config is
|
||||
// passed to Call::CreateAudioSendStream().
|
||||
// TODO(solenberg): Implement, once we configure codecs through the new API.
|
||||
// std::unique_ptr<AudioEncoder> encoder;
|
||||
int cng_payload_type = -1; // pt, or -1 to disable Comfort Noise Generator.
|
||||
|
||||
// Bitrate limits used for variable audio bitrate streams. Set both to -1 to
|
||||
// disable audio bitrate adaptation.
|
||||
// Note: This is still an experimental feature and not ready for real usage.
|
||||
int min_bitrate_kbps = -1;
|
||||
int max_bitrate_kbps = -1;
|
||||
|
||||
struct SendCodecSpec {
|
||||
SendCodecSpec() {
|
||||
webrtc::CodecInst empty_inst = {0};
|
||||
codec_inst = empty_inst;
|
||||
codec_inst.pltype = -1;
|
||||
}
|
||||
bool operator==(const SendCodecSpec& rhs) const {
|
||||
{
|
||||
if (nack_enabled != rhs.nack_enabled) {
|
||||
return false;
|
||||
}
|
||||
if (transport_cc_enabled != rhs.transport_cc_enabled) {
|
||||
return false;
|
||||
}
|
||||
if (enable_codec_fec != rhs.enable_codec_fec) {
|
||||
return false;
|
||||
}
|
||||
if (enable_opus_dtx != rhs.enable_opus_dtx) {
|
||||
return false;
|
||||
}
|
||||
if (opus_max_playback_rate != rhs.opus_max_playback_rate) {
|
||||
return false;
|
||||
}
|
||||
if (cng_payload_type != rhs.cng_payload_type) {
|
||||
return false;
|
||||
}
|
||||
if (cng_plfreq != rhs.cng_plfreq) {
|
||||
return false;
|
||||
}
|
||||
if (codec_inst != rhs.codec_inst) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool operator!=(const SendCodecSpec& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool nack_enabled = false;
|
||||
bool transport_cc_enabled = false;
|
||||
bool enable_codec_fec = false;
|
||||
bool enable_opus_dtx = false;
|
||||
int opus_max_playback_rate = 0;
|
||||
int cng_payload_type = -1;
|
||||
int cng_plfreq = -1;
|
||||
webrtc::CodecInst codec_inst;
|
||||
} send_codec_spec;
|
||||
};
|
||||
|
||||
// Starts stream activity.
|
||||
|
||||
@ -30,6 +30,34 @@
|
||||
#include "webrtc/voice_engine/voice_engine_impl.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kOpusCodecName[] = "opus";
|
||||
|
||||
// TODO(minyue): Remove |LOG_RTCERR2|.
|
||||
#define LOG_RTCERR2(func, a1, a2, err) \
|
||||
LOG(LS_WARNING) << "" << #func << "(" << a1 << ", " << a2 \
|
||||
<< ") failed, err=" << err
|
||||
|
||||
// TODO(minyue): Remove |LOG_RTCERR3|.
|
||||
#define LOG_RTCERR3(func, a1, a2, a3, err) \
|
||||
LOG(LS_WARNING) << "" << #func << "(" << a1 << ", " << a2 << ", " << a3 \
|
||||
<< ") failed, err=" << err
|
||||
|
||||
std::string ToString(const webrtc::CodecInst& codec) {
|
||||
std::stringstream ss;
|
||||
ss << codec.plname << "/" << codec.plfreq << "/" << codec.channels << " ("
|
||||
<< codec.pltype << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool IsCodec(const webrtc::CodecInst& codec, const char* ref_name) {
|
||||
return (_stricmp(codec.plname, ref_name) == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string AudioSendStream::Config::Rtp::ToString() const {
|
||||
std::stringstream ss;
|
||||
ss << "{ssrc: " << ssrc;
|
||||
@ -52,7 +80,7 @@ std::string AudioSendStream::Config::ToString() const {
|
||||
ss << "{rtp: " << rtp.ToString();
|
||||
ss << ", voe_channel_id: " << voe_channel_id;
|
||||
// TODO(solenberg): Encoder config.
|
||||
ss << ", cng_payload_type: " << cng_payload_type;
|
||||
ss << ", cng_payload_type: " << send_codec_spec.cng_payload_type;
|
||||
ss << '}';
|
||||
return ss.str();
|
||||
}
|
||||
@ -102,6 +130,9 @@ AudioSendStream::AudioSendStream(
|
||||
RTC_NOTREACHED() << "Registering unsupported RTP extension.";
|
||||
}
|
||||
}
|
||||
if (!SetupSendCodec()) {
|
||||
LOG(LS_ERROR) << "Failed to set up send codec state.";
|
||||
}
|
||||
}
|
||||
|
||||
AudioSendStream::~AudioSendStream() {
|
||||
@ -285,5 +316,127 @@ VoiceEngine* AudioSendStream::voice_engine() const {
|
||||
RTC_DCHECK(voice_engine);
|
||||
return voice_engine;
|
||||
}
|
||||
|
||||
// Apply current codec settings to a single voe::Channel used for sending.
|
||||
bool AudioSendStream::SetupSendCodec() {
|
||||
ScopedVoEInterface<VoEBase> base(voice_engine());
|
||||
ScopedVoEInterface<VoECodec> codec(voice_engine());
|
||||
|
||||
const int channel = config_.voe_channel_id;
|
||||
|
||||
// Disable VAD and FEC unless we know the other side wants them.
|
||||
codec->SetVADStatus(channel, false);
|
||||
codec->SetFECStatus(channel, false);
|
||||
|
||||
const auto& send_codec_spec = config_.send_codec_spec;
|
||||
|
||||
// Set the codec immediately, since SetVADStatus() depends on whether
|
||||
// the current codec is mono or stereo.
|
||||
LOG(LS_INFO) << "Send channel " << channel << " selected voice codec "
|
||||
<< ToString(send_codec_spec.codec_inst)
|
||||
<< ", bitrate=" << send_codec_spec.codec_inst.rate;
|
||||
|
||||
// If codec is already configured, we do not it again.
|
||||
// TODO(minyue): check if this check is really needed, or can we move it into
|
||||
// |codec->SetSendCodec|.
|
||||
webrtc::CodecInst current_codec = {0};
|
||||
if (codec->GetSendCodec(channel, current_codec) != 0 ||
|
||||
(send_codec_spec.codec_inst != current_codec)) {
|
||||
if (codec->SetSendCodec(channel, send_codec_spec.codec_inst) == -1) {
|
||||
LOG_RTCERR2(SetSendCodec, channel, ToString(send_codec_spec.codec_inst),
|
||||
base->LastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// FEC should be enabled after SetSendCodec.
|
||||
if (send_codec_spec.enable_codec_fec) {
|
||||
LOG(LS_INFO) << "Attempt to enable codec internal FEC on channel "
|
||||
<< channel;
|
||||
if (codec->SetFECStatus(channel, true) == -1) {
|
||||
// Enable codec internal FEC. Treat any failure as fatal internal error.
|
||||
LOG_RTCERR2(SetFECStatus, channel, true, base->LastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsCodec(send_codec_spec.codec_inst, kOpusCodecName)) {
|
||||
// DTX and maxplaybackrate should be set after SetSendCodec. Because current
|
||||
// send codec has to be Opus.
|
||||
|
||||
// Set Opus internal DTX.
|
||||
LOG(LS_INFO) << "Attempt to "
|
||||
<< (send_codec_spec.enable_opus_dtx ? "enable" : "disable")
|
||||
<< " Opus DTX on channel " << channel;
|
||||
if (codec->SetOpusDtx(channel, send_codec_spec.enable_opus_dtx)) {
|
||||
LOG_RTCERR2(SetOpusDtx, channel, send_codec_spec.enable_opus_dtx,
|
||||
base->LastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// If opus_max_playback_rate <= 0, the default maximum playback rate
|
||||
// (48 kHz) will be used.
|
||||
if (send_codec_spec.opus_max_playback_rate > 0) {
|
||||
LOG(LS_INFO) << "Attempt to set maximum playback rate to "
|
||||
<< send_codec_spec.opus_max_playback_rate
|
||||
<< " Hz on channel " << channel;
|
||||
if (codec->SetOpusMaxPlaybackRate(
|
||||
channel, send_codec_spec.opus_max_playback_rate) == -1) {
|
||||
LOG_RTCERR2(SetOpusMaxPlaybackRate, channel,
|
||||
send_codec_spec.opus_max_playback_rate, base->LastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the CN payloadtype and the VAD status.
|
||||
if (send_codec_spec.cng_payload_type != -1) {
|
||||
// The CN payload type for 8000 Hz clockrate is fixed at 13.
|
||||
if (send_codec_spec.cng_plfreq != 8000) {
|
||||
webrtc::PayloadFrequencies cn_freq;
|
||||
switch (send_codec_spec.cng_plfreq) {
|
||||
case 16000:
|
||||
cn_freq = webrtc::kFreq16000Hz;
|
||||
break;
|
||||
case 32000:
|
||||
cn_freq = webrtc::kFreq32000Hz;
|
||||
break;
|
||||
default:
|
||||
RTC_NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
if (codec->SetSendCNPayloadType(channel, send_codec_spec.cng_payload_type,
|
||||
cn_freq) == -1) {
|
||||
LOG_RTCERR3(SetSendCNPayloadType, channel,
|
||||
send_codec_spec.cng_payload_type, cn_freq,
|
||||
base->LastError());
|
||||
|
||||
// TODO(ajm): This failure condition will be removed from VoE.
|
||||
// Restore the return here when we update to a new enough webrtc.
|
||||
//
|
||||
// Not returning false because the SetSendCNPayloadType will fail if
|
||||
// the channel is already sending.
|
||||
// This can happen if the remote description is applied twice, for
|
||||
// example in the case of ROAP on top of JSEP, where both side will
|
||||
// send the offer.
|
||||
}
|
||||
}
|
||||
|
||||
// Only turn on VAD if we have a CN payload type that matches the
|
||||
// clockrate for the codec we are going to use.
|
||||
if (send_codec_spec.cng_plfreq == send_codec_spec.codec_inst.plfreq &&
|
||||
send_codec_spec.codec_inst.channels == 1) {
|
||||
// TODO(minyue): If CN frequency == 48000 Hz is allowed, consider the
|
||||
// interaction between VAD and Opus FEC.
|
||||
LOG(LS_INFO) << "Enabling VAD";
|
||||
if (codec->SetVADStatus(channel, true) == -1) {
|
||||
LOG_RTCERR2(SetVADStatus, channel, true, base->LastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace webrtc
|
||||
|
||||
@ -61,6 +61,8 @@ class AudioSendStream final : public webrtc::AudioSendStream,
|
||||
private:
|
||||
VoiceEngine* voice_engine() const;
|
||||
|
||||
bool SetupSendCodec();
|
||||
|
||||
rtc::ThreadChecker thread_checker_;
|
||||
rtc::TaskQueue* worker_queue_;
|
||||
const webrtc::AudioSendStream::Config config_;
|
||||
|
||||
@ -44,11 +44,11 @@ const int kEchoReturnLossEnhancement = 101;
|
||||
const unsigned int kSpeechInputLevel = 96;
|
||||
const CallStatistics kCallStats = {
|
||||
1345, 1678, 1901, 1234, 112, 13456, 17890, 1567, -1890, -1123};
|
||||
const CodecInst kCodecInst = {-121, "codec_name_send", 48000, -231, 0, -671};
|
||||
const ReportBlock kReportBlock = {456, 780, 123, 567, 890, 132, 143, 13354};
|
||||
const int kTelephoneEventPayloadType = 123;
|
||||
const int kTelephoneEventCode = 45;
|
||||
const int kTelephoneEventDuration = 6789;
|
||||
const CodecInst kIsacCodec = {103, "isac", 16000, 320, 1, 32000};
|
||||
|
||||
class MockLimitObserver : public BitrateAllocator::LimitObserver {
|
||||
public:
|
||||
@ -111,6 +111,7 @@ struct ConfigHelper {
|
||||
.Times(1); // Destructor resets the event log
|
||||
return channel_proxy_;
|
||||
}));
|
||||
SetupMockForSetupSendCodec();
|
||||
stream_config_.voe_channel_id = kChannelId;
|
||||
stream_config_.rtp.ssrc = kSsrc;
|
||||
stream_config_.rtp.nack.rtp_history_ms = 200;
|
||||
@ -121,6 +122,9 @@ struct ConfigHelper {
|
||||
RtpExtension(RtpExtension::kAbsSendTimeUri, kAbsSendTimeId));
|
||||
stream_config_.rtp.extensions.push_back(RtpExtension(
|
||||
RtpExtension::kTransportSequenceNumberUri, kTransportSequenceNumberId));
|
||||
// Use ISAC as default codec so as to prevent unnecessary |voice_engine_|
|
||||
// calls from the default ctor behavior.
|
||||
stream_config_.send_codec_spec.codec_inst = kIsacCodec;
|
||||
}
|
||||
|
||||
AudioSendStream::Config& config() { return stream_config_; }
|
||||
@ -132,6 +136,19 @@ struct ConfigHelper {
|
||||
BitrateAllocator* bitrate_allocator() { return &bitrate_allocator_; }
|
||||
rtc::TaskQueue* worker_queue() { return &worker_queue_; }
|
||||
RtcEventLog* event_log() { return &event_log_; }
|
||||
MockVoiceEngine* voice_engine() { return &voice_engine_; }
|
||||
|
||||
void SetupMockForSetupSendCodec() {
|
||||
EXPECT_CALL(voice_engine_, SetVADStatus(kChannelId, false, _, _))
|
||||
.WillOnce(Return(0));
|
||||
EXPECT_CALL(voice_engine_, SetFECStatus(kChannelId, false))
|
||||
.WillOnce(Return(0));
|
||||
// Let |GetSendCodec| return -1 for the first time to indicate that no send
|
||||
// codec has been set.
|
||||
EXPECT_CALL(voice_engine_, GetSendCodec(kChannelId, _))
|
||||
.WillOnce(Return(-1));
|
||||
EXPECT_CALL(voice_engine_, SetSendCodec(kChannelId, _)).WillOnce(Return(0));
|
||||
}
|
||||
|
||||
void SetupMockForSendTelephoneEvent() {
|
||||
EXPECT_TRUE(channel_proxy_);
|
||||
@ -162,7 +179,7 @@ struct ConfigHelper {
|
||||
.WillRepeatedly(Return(report_blocks));
|
||||
|
||||
EXPECT_CALL(voice_engine_, GetSendCodec(kChannelId, _))
|
||||
.WillRepeatedly(DoAll(SetArgReferee<1>(kCodecInst), Return(0)));
|
||||
.WillRepeatedly(DoAll(SetArgReferee<1>(kIsacCodec), Return(0)));
|
||||
EXPECT_CALL(voice_engine_, GetSpeechInputLevelFullRange(_))
|
||||
.WillRepeatedly(DoAll(SetArgReferee<0>(kSpeechInputLevel), Return(0)));
|
||||
EXPECT_CALL(voice_engine_, GetEcMetricsStatus(_))
|
||||
@ -201,7 +218,7 @@ TEST(AudioSendStreamTest, ConfigToString) {
|
||||
RtpExtension(RtpExtension::kAbsSendTimeUri, kAbsSendTimeId));
|
||||
config.rtp.c_name = kCName;
|
||||
config.voe_channel_id = kChannelId;
|
||||
config.cng_payload_type = 42;
|
||||
config.send_codec_spec.cng_payload_type = 42;
|
||||
EXPECT_EQ(
|
||||
"{rtp: {ssrc: 1234, extensions: [{uri: "
|
||||
"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time, id: 3}], "
|
||||
@ -253,11 +270,11 @@ TEST(AudioSendStreamTest, GetStats) {
|
||||
EXPECT_EQ(static_cast<int32_t>(kReportBlock.cumulative_num_packets_lost),
|
||||
stats.packets_lost);
|
||||
EXPECT_EQ(Q8ToFloat(kReportBlock.fraction_lost), stats.fraction_lost);
|
||||
EXPECT_EQ(std::string(kCodecInst.plname), stats.codec_name);
|
||||
EXPECT_EQ(std::string(kIsacCodec.plname), stats.codec_name);
|
||||
EXPECT_EQ(static_cast<int32_t>(kReportBlock.extended_highest_sequence_number),
|
||||
stats.ext_seqnum);
|
||||
EXPECT_EQ(static_cast<int32_t>(kReportBlock.interarrival_jitter /
|
||||
(kCodecInst.plfreq / 1000)),
|
||||
(kIsacCodec.plfreq / 1000)),
|
||||
stats.jitter_ms);
|
||||
EXPECT_EQ(kCallStats.rttMs, stats.rtt_ms);
|
||||
EXPECT_EQ(static_cast<int32_t>(kSpeechInputLevel), stats.audio_level);
|
||||
@ -287,5 +304,55 @@ TEST(AudioSendStreamTest, GetStatsTypingNoiseDetected) {
|
||||
voe_observer->CallbackOnError(-1, VE_TYPING_NOISE_OFF_WARNING);
|
||||
EXPECT_FALSE(send_stream.GetStats().typing_noise_detected);
|
||||
}
|
||||
|
||||
TEST(AudioSendStreamTest, SendCodecAppliesConfigParams) {
|
||||
ConfigHelper helper;
|
||||
auto stream_config = helper.config();
|
||||
const CodecInst kOpusCodec = {111, "opus", 48000, 960, 2, 64000};
|
||||
stream_config.send_codec_spec.codec_inst = kOpusCodec;
|
||||
stream_config.send_codec_spec.enable_codec_fec = true;
|
||||
stream_config.send_codec_spec.enable_opus_dtx = true;
|
||||
stream_config.send_codec_spec.opus_max_playback_rate = 12345;
|
||||
stream_config.send_codec_spec.cng_plfreq = 16000;
|
||||
stream_config.send_codec_spec.cng_payload_type = 105;
|
||||
EXPECT_CALL(*helper.voice_engine(), SetFECStatus(kChannelId, true))
|
||||
.WillOnce(Return(0));
|
||||
EXPECT_CALL(
|
||||
*helper.voice_engine(),
|
||||
SetOpusDtx(kChannelId, stream_config.send_codec_spec.enable_opus_dtx))
|
||||
.WillOnce(Return(0));
|
||||
EXPECT_CALL(
|
||||
*helper.voice_engine(),
|
||||
SetOpusMaxPlaybackRate(
|
||||
kChannelId, stream_config.send_codec_spec.opus_max_playback_rate))
|
||||
.WillOnce(Return(0));
|
||||
EXPECT_CALL(*helper.voice_engine(),
|
||||
SetSendCNPayloadType(
|
||||
kChannelId, stream_config.send_codec_spec.cng_payload_type,
|
||||
webrtc::kFreq16000Hz))
|
||||
.WillOnce(Return(0));
|
||||
internal::AudioSendStream send_stream(
|
||||
stream_config, helper.audio_state(), helper.worker_queue(),
|
||||
helper.congestion_controller(), helper.bitrate_allocator(),
|
||||
helper.event_log());
|
||||
}
|
||||
|
||||
// VAD is applied when codec is mono and the CNG frequency matches the codec
|
||||
// sample rate.
|
||||
TEST(AudioSendStreamTest, SendCodecCanApplyVad) {
|
||||
ConfigHelper helper;
|
||||
auto stream_config = helper.config();
|
||||
const CodecInst kG722Codec = {9, "g722", 8000, 160, 1, 16000};
|
||||
stream_config.send_codec_spec.codec_inst = kG722Codec;
|
||||
stream_config.send_codec_spec.cng_plfreq = 8000;
|
||||
stream_config.send_codec_spec.cng_payload_type = 105;
|
||||
EXPECT_CALL(*helper.voice_engine(), SetVADStatus(kChannelId, true, _, _))
|
||||
.WillOnce(Return(0));
|
||||
internal::AudioSendStream send_stream(
|
||||
stream_config, helper.audio_state(), helper.worker_queue(),
|
||||
helper.congestion_controller(), helper.bitrate_allocator(),
|
||||
helper.event_log());
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
@ -464,42 +464,41 @@ const WebRtcVoiceCodecs::CodecPref WebRtcVoiceCodecs::kCodecPrefs[11] = {
|
||||
{kCnCodecName, 8000, 1, 13, false, {}},
|
||||
{kDtmfCodecName, 8000, 1, 126, false, {}}
|
||||
};
|
||||
} // namespace {
|
||||
|
||||
bool SendCodecSpec::operator==(const SendCodecSpec& rhs) const {
|
||||
if (nack_enabled != rhs.nack_enabled) {
|
||||
return false;
|
||||
rtc::Optional<int> ComputeSendBitrate(int max_send_bitrate_bps,
|
||||
int rtp_max_bitrate_bps,
|
||||
const webrtc::CodecInst& codec_inst) {
|
||||
const int bps = MinPositive(max_send_bitrate_bps, rtp_max_bitrate_bps);
|
||||
const int codec_rate = codec_inst.rate;
|
||||
|
||||
if (bps <= 0) {
|
||||
return rtc::Optional<int>(codec_rate);
|
||||
}
|
||||
if (transport_cc_enabled != rhs.transport_cc_enabled) {
|
||||
return false;
|
||||
|
||||
if (codec_inst.pltype == -1) {
|
||||
return rtc::Optional<int>(codec_rate);
|
||||
;
|
||||
}
|
||||
if (enable_codec_fec != rhs.enable_codec_fec) {
|
||||
return false;
|
||||
|
||||
if (WebRtcVoiceCodecs::IsCodecMultiRate(codec_inst)) {
|
||||
// If codec is multi-rate then just set the bitrate.
|
||||
return rtc::Optional<int>(
|
||||
std::min(bps, WebRtcVoiceCodecs::MaxBitrateBps(codec_inst)));
|
||||
}
|
||||
if (enable_opus_dtx != rhs.enable_opus_dtx) {
|
||||
return false;
|
||||
|
||||
if (bps < codec_inst.rate) {
|
||||
// If codec is not multi-rate and |bps| is less than the fixed bitrate then
|
||||
// fail. If codec is not multi-rate and |bps| exceeds or equal the fixed
|
||||
// bitrate then ignore.
|
||||
LOG(LS_ERROR) << "Failed to set codec " << codec_inst.plname
|
||||
<< " to bitrate " << bps << " bps"
|
||||
<< ", requires at least " << codec_inst.rate << " bps.";
|
||||
return rtc::Optional<int>();
|
||||
}
|
||||
if (opus_max_playback_rate != rhs.opus_max_playback_rate) {
|
||||
return false;
|
||||
}
|
||||
if (red_payload_type != rhs.red_payload_type) {
|
||||
return false;
|
||||
}
|
||||
if (cng_payload_type != rhs.cng_payload_type) {
|
||||
return false;
|
||||
}
|
||||
if (cng_plfreq != rhs.cng_plfreq) {
|
||||
return false;
|
||||
}
|
||||
if (codec_inst != rhs.codec_inst) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return rtc::Optional<int>(codec_rate);
|
||||
}
|
||||
|
||||
bool SendCodecSpec::operator!=(const SendCodecSpec& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
} // namespace {
|
||||
|
||||
bool WebRtcVoiceEngine::ToCodecInst(const AudioCodec& in,
|
||||
webrtc::CodecInst* out) {
|
||||
@ -1144,17 +1143,20 @@ AudioCodecs WebRtcVoiceEngine::CollectRecvCodecs() const {
|
||||
class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
|
||||
: public AudioSource::Sink {
|
||||
public:
|
||||
WebRtcAudioSendStream(int ch,
|
||||
webrtc::AudioTransport* voe_audio_transport,
|
||||
uint32_t ssrc,
|
||||
const std::string& c_name,
|
||||
const SendCodecSpec& send_codec_spec,
|
||||
const std::vector<webrtc::RtpExtension>& extensions,
|
||||
webrtc::Call* call,
|
||||
webrtc::Transport* send_transport)
|
||||
WebRtcAudioSendStream(
|
||||
int ch,
|
||||
webrtc::AudioTransport* voe_audio_transport,
|
||||
uint32_t ssrc,
|
||||
const std::string& c_name,
|
||||
const webrtc::AudioSendStream::Config::SendCodecSpec& send_codec_spec,
|
||||
const std::vector<webrtc::RtpExtension>& extensions,
|
||||
int max_send_bitrate_bps,
|
||||
webrtc::Call* call,
|
||||
webrtc::Transport* send_transport)
|
||||
: voe_audio_transport_(voe_audio_transport),
|
||||
call_(call),
|
||||
config_(send_transport),
|
||||
max_send_bitrate_bps_(max_send_bitrate_bps),
|
||||
rtp_parameters_(CreateRtpParametersWithOneEncoding()) {
|
||||
RTC_DCHECK_GE(ch, 0);
|
||||
// TODO(solenberg): Once we're not using FakeWebRtcVoiceEngine anymore:
|
||||
@ -1173,10 +1175,22 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
|
||||
call_->DestroyAudioSendStream(stream_);
|
||||
}
|
||||
|
||||
void RecreateAudioSendStream(const SendCodecSpec& send_codec_spec) {
|
||||
void RecreateAudioSendStream(
|
||||
const webrtc::AudioSendStream::Config::SendCodecSpec& send_codec_spec) {
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
send_codec_spec_ = send_codec_spec;
|
||||
config_.rtp.nack.rtp_history_ms =
|
||||
send_codec_spec.nack_enabled ? kNackRtpHistoryMs : 0;
|
||||
send_codec_spec_.nack_enabled ? kNackRtpHistoryMs : 0;
|
||||
config_.send_codec_spec = send_codec_spec_;
|
||||
|
||||
auto send_rate = ComputeSendBitrate(
|
||||
max_send_bitrate_bps_, rtp_parameters_.encodings[0].max_bitrate_bps,
|
||||
send_codec_spec.codec_inst);
|
||||
if (send_rate) {
|
||||
// Apply a send rate that abides by |max_send_bitrate_bps_| and
|
||||
// |rtp_parameters_| when possible. Otherwise use the codec rate.
|
||||
config_.send_codec_spec.codec_inst.rate = *send_rate;
|
||||
}
|
||||
RecreateAudioSendStream();
|
||||
}
|
||||
|
||||
@ -1187,6 +1201,25 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
|
||||
RecreateAudioSendStream();
|
||||
}
|
||||
|
||||
bool SetMaxSendBitrate(int bps) {
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
auto send_rate =
|
||||
ComputeSendBitrate(bps, rtp_parameters_.encodings[0].max_bitrate_bps,
|
||||
send_codec_spec_.codec_inst);
|
||||
if (!send_rate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
max_send_bitrate_bps_ = bps;
|
||||
|
||||
if (config_.send_codec_spec.codec_inst.rate != *send_rate) {
|
||||
// Recreate AudioSendStream with new bit rate.
|
||||
config_.send_codec_spec.codec_inst.rate = *send_rate;
|
||||
RecreateAudioSendStream();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SendTelephoneEvent(int payload_type, int event, int duration_ms) {
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
RTC_DCHECK(stream_);
|
||||
@ -1279,11 +1312,27 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
|
||||
return rtp_parameters_;
|
||||
}
|
||||
|
||||
void SetRtpParameters(const webrtc::RtpParameters& parameters) {
|
||||
bool SetRtpParameters(const webrtc::RtpParameters& parameters) {
|
||||
RTC_CHECK_EQ(1UL, parameters.encodings.size());
|
||||
auto send_rate = ComputeSendBitrate(max_send_bitrate_bps_,
|
||||
parameters.encodings[0].max_bitrate_bps,
|
||||
send_codec_spec_.codec_inst);
|
||||
if (!send_rate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rtp_parameters_ = parameters;
|
||||
// parameters.encodings[0].active could have changed.
|
||||
UpdateSendState();
|
||||
|
||||
// parameters.encodings[0].encodings[0].max_bitrate_bps could have changed.
|
||||
if (config_.send_codec_spec.codec_inst.rate != *send_rate) {
|
||||
// Recreate AudioSendStream with new bit rate.
|
||||
config_.send_codec_spec.codec_inst.rate = *send_rate;
|
||||
RecreateAudioSendStream();
|
||||
} else {
|
||||
// parameters.encodings[0].active could have changed.
|
||||
UpdateSendState();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1332,7 +1381,9 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
|
||||
AudioSource* source_ = nullptr;
|
||||
bool send_ = false;
|
||||
bool muted_ = false;
|
||||
int max_send_bitrate_bps_;
|
||||
webrtc::RtpParameters rtp_parameters_;
|
||||
webrtc::AudioSendStream::Config::SendCodecSpec send_codec_spec_;
|
||||
|
||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WebRtcAudioSendStream);
|
||||
};
|
||||
@ -1592,15 +1643,18 @@ bool WebRtcVoiceMediaChannel::SetRtpSendParameters(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetChannelSendParameters(it->second->channel(), parameters)) {
|
||||
LOG(LS_WARNING) << "Failed to set send RtpParameters.";
|
||||
return false;
|
||||
}
|
||||
// TODO(minyue): The following legacy actions go into
|
||||
// |WebRtcAudioSendStream::SetRtpParameters()| which is called at the end,
|
||||
// though there are two difference:
|
||||
// 1. |WebRtcVoiceMediaChannel::SetChannelSendParameters()| only calls
|
||||
// |SetSendCodec| while |WebRtcAudioSendStream::SetRtpParameters()| calls
|
||||
// |SetSendCodecs|. The outcome should be the same.
|
||||
// 2. AudioSendStream can be recreated.
|
||||
|
||||
// Codecs are handled at the WebRtcVoiceMediaChannel level.
|
||||
webrtc::RtpParameters reduced_params = parameters;
|
||||
reduced_params.codecs.clear();
|
||||
it->second->SetRtpParameters(reduced_params);
|
||||
return true;
|
||||
return it->second->SetRtpParameters(reduced_params);
|
||||
}
|
||||
|
||||
webrtc::RtpParameters WebRtcVoiceMediaChannel::GetRtpReceiveParameters(
|
||||
@ -1760,7 +1814,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
|
||||
// with the proper configuration for VAD, CNG, NACK and Opus-specific
|
||||
// parameters.
|
||||
// TODO(solenberg): Refactor this logic once we create AudioEncoders here.
|
||||
SendCodecSpec send_codec_spec;
|
||||
webrtc::AudioSendStream::Config::SendCodecSpec send_codec_spec;
|
||||
{
|
||||
send_codec_spec.nack_enabled = send_codec_spec_.nack_enabled;
|
||||
|
||||
@ -1834,9 +1888,6 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
|
||||
send_codec_spec_ = std::move(send_codec_spec);
|
||||
for (const auto& kv : send_streams_) {
|
||||
kv.second->RecreateAudioSendStream(send_codec_spec_);
|
||||
if (!SetSendCodecs(kv.second->channel(), kv.second->rtp_parameters())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1858,131 +1909,6 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apply current codec settings to a single voe::Channel used for sending.
|
||||
bool WebRtcVoiceMediaChannel::SetSendCodecs(
|
||||
int channel,
|
||||
const webrtc::RtpParameters& rtp_parameters) {
|
||||
// Disable VAD and FEC unless we know the other side wants them.
|
||||
engine()->voe()->codec()->SetVADStatus(channel, false);
|
||||
engine()->voe()->codec()->SetFECStatus(channel, false);
|
||||
|
||||
// Set the codec immediately, since SetVADStatus() depends on whether
|
||||
// the current codec is mono or stereo.
|
||||
if (!SetSendCodec(channel, send_codec_spec_.codec_inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FEC should be enabled after SetSendCodec.
|
||||
if (send_codec_spec_.enable_codec_fec) {
|
||||
LOG(LS_INFO) << "Attempt to enable codec internal FEC on channel "
|
||||
<< channel;
|
||||
if (engine()->voe()->codec()->SetFECStatus(channel, true) == -1) {
|
||||
// Enable codec internal FEC. Treat any failure as fatal internal error.
|
||||
LOG_RTCERR2(SetFECStatus, channel, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsCodec(send_codec_spec_.codec_inst, kOpusCodecName)) {
|
||||
// DTX and maxplaybackrate should be set after SetSendCodec. Because current
|
||||
// send codec has to be Opus.
|
||||
|
||||
// Set Opus internal DTX.
|
||||
LOG(LS_INFO) << "Attempt to "
|
||||
<< (send_codec_spec_.enable_opus_dtx ? "enable" : "disable")
|
||||
<< " Opus DTX on channel "
|
||||
<< channel;
|
||||
if (engine()->voe()->codec()->SetOpusDtx(channel,
|
||||
send_codec_spec_.enable_opus_dtx)) {
|
||||
LOG_RTCERR2(SetOpusDtx, channel, send_codec_spec_.enable_opus_dtx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If opus_max_playback_rate <= 0, the default maximum playback rate
|
||||
// (48 kHz) will be used.
|
||||
if (send_codec_spec_.opus_max_playback_rate > 0) {
|
||||
LOG(LS_INFO) << "Attempt to set maximum playback rate to "
|
||||
<< send_codec_spec_.opus_max_playback_rate
|
||||
<< " Hz on channel "
|
||||
<< channel;
|
||||
if (engine()->voe()->codec()->SetOpusMaxPlaybackRate(
|
||||
channel, send_codec_spec_.opus_max_playback_rate) == -1) {
|
||||
LOG_RTCERR2(SetOpusMaxPlaybackRate, channel,
|
||||
send_codec_spec_.opus_max_playback_rate);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO(solenberg): SetMaxSendBitrate() yields another call to SetSendCodec().
|
||||
// Check if it is possible to fuse with the previous call in this function.
|
||||
SetChannelSendParameters(channel, rtp_parameters);
|
||||
|
||||
// Set the CN payloadtype and the VAD status.
|
||||
if (send_codec_spec_.cng_payload_type != -1) {
|
||||
// The CN payload type for 8000 Hz clockrate is fixed at 13.
|
||||
if (send_codec_spec_.cng_plfreq != 8000) {
|
||||
webrtc::PayloadFrequencies cn_freq;
|
||||
switch (send_codec_spec_.cng_plfreq) {
|
||||
case 16000:
|
||||
cn_freq = webrtc::kFreq16000Hz;
|
||||
break;
|
||||
case 32000:
|
||||
cn_freq = webrtc::kFreq32000Hz;
|
||||
break;
|
||||
default:
|
||||
RTC_NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
if (engine()->voe()->codec()->SetSendCNPayloadType(
|
||||
channel, send_codec_spec_.cng_payload_type, cn_freq) == -1) {
|
||||
LOG_RTCERR3(SetSendCNPayloadType, channel,
|
||||
send_codec_spec_.cng_payload_type, cn_freq);
|
||||
// TODO(ajm): This failure condition will be removed from VoE.
|
||||
// Restore the return here when we update to a new enough webrtc.
|
||||
//
|
||||
// Not returning false because the SetSendCNPayloadType will fail if
|
||||
// the channel is already sending.
|
||||
// This can happen if the remote description is applied twice, for
|
||||
// example in the case of ROAP on top of JSEP, where both side will
|
||||
// send the offer.
|
||||
}
|
||||
}
|
||||
|
||||
// Only turn on VAD if we have a CN payload type that matches the
|
||||
// clockrate for the codec we are going to use.
|
||||
if (send_codec_spec_.cng_plfreq == send_codec_spec_.codec_inst.plfreq &&
|
||||
send_codec_spec_.codec_inst.channels == 1) {
|
||||
// TODO(minyue): If CN frequency == 48000 Hz is allowed, consider the
|
||||
// interaction between VAD and Opus FEC.
|
||||
LOG(LS_INFO) << "Enabling VAD";
|
||||
if (engine()->voe()->codec()->SetVADStatus(channel, true) == -1) {
|
||||
LOG_RTCERR2(SetVADStatus, channel, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcVoiceMediaChannel::SetSendCodec(
|
||||
int channel, const webrtc::CodecInst& send_codec) {
|
||||
LOG(LS_INFO) << "Send channel " << channel << " selected voice codec "
|
||||
<< ToString(send_codec) << ", bitrate=" << send_codec.rate;
|
||||
|
||||
webrtc::CodecInst current_codec = {0};
|
||||
if (engine()->voe()->codec()->GetSendCodec(channel, current_codec) == 0 &&
|
||||
(send_codec == current_codec)) {
|
||||
// Codec is already configured, we can return without setting it again.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (engine()->voe()->codec()->SetSendCodec(channel, send_codec) == -1) {
|
||||
LOG_RTCERR2(SetSendCodec, channel, ToString(send_codec));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcVoiceMediaChannel::SetPlayout(bool playout) {
|
||||
TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::SetPlayout");
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
@ -2087,17 +2013,9 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) {
|
||||
|
||||
WebRtcAudioSendStream* stream = new WebRtcAudioSendStream(
|
||||
channel, audio_transport, ssrc, sp.cname, send_codec_spec_,
|
||||
send_rtp_extensions_, call_, this);
|
||||
send_rtp_extensions_, max_send_bitrate_bps_, call_, this);
|
||||
send_streams_.insert(std::make_pair(ssrc, stream));
|
||||
|
||||
// Set the current codecs to be used for the new channel. We need to do this
|
||||
// after adding the channel to send_channels_, because of how max bitrate is
|
||||
// currently being configured by SetSendCodec().
|
||||
if (HasSendCodec() && !SetSendCodecs(channel, stream->rtp_parameters())) {
|
||||
RemoveSendStream(ssrc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point the stream's local SSRC has been updated. If it is the first
|
||||
// send stream, make sure that all the receive streams are updated with the
|
||||
// same SSRC in order to send receiver reports.
|
||||
@ -2471,68 +2389,13 @@ bool WebRtcVoiceMediaChannel::MuteStream(uint32_t ssrc, bool muted) {
|
||||
bool WebRtcVoiceMediaChannel::SetMaxSendBitrate(int bps) {
|
||||
LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetMaxSendBitrate.";
|
||||
max_send_bitrate_bps_ = bps;
|
||||
|
||||
bool success = true;
|
||||
for (const auto& kv : send_streams_) {
|
||||
if (!SetChannelSendParameters(kv.second->channel(),
|
||||
kv.second->rtp_parameters())) {
|
||||
return false;
|
||||
if (!kv.second->SetMaxSendBitrate(max_send_bitrate_bps_)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcVoiceMediaChannel::SetChannelSendParameters(
|
||||
int channel,
|
||||
const webrtc::RtpParameters& parameters) {
|
||||
RTC_CHECK_EQ(1UL, parameters.encodings.size());
|
||||
// TODO(deadbeef): Handle setting parameters with a list of codecs in a
|
||||
// different order (which should change the send codec).
|
||||
return SetMaxSendBitrate(
|
||||
channel, MinPositive(max_send_bitrate_bps_,
|
||||
parameters.encodings[0].max_bitrate_bps));
|
||||
}
|
||||
|
||||
bool WebRtcVoiceMediaChannel::SetMaxSendBitrate(int channel, int bps) {
|
||||
// Bitrate is auto by default.
|
||||
// TODO(bemasc): Fix this so that if SetMaxSendBandwidth(50) is followed by
|
||||
// SetMaxSendBandwith(0), the second call removes the previous limit.
|
||||
if (bps <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!HasSendCodec()) {
|
||||
LOG(LS_INFO) << "The send codec has not been set up yet. "
|
||||
<< "The send bitrate setting will be applied later.";
|
||||
return true;
|
||||
}
|
||||
|
||||
webrtc::CodecInst codec = send_codec_spec_.codec_inst;
|
||||
bool is_multi_rate = WebRtcVoiceCodecs::IsCodecMultiRate(codec);
|
||||
|
||||
if (is_multi_rate) {
|
||||
// If codec is multi-rate then just set the bitrate.
|
||||
int max_bitrate_bps = WebRtcVoiceCodecs::MaxBitrateBps(codec);
|
||||
codec.rate = std::min(bps, max_bitrate_bps);
|
||||
LOG(LS_INFO) << "Setting codec " << codec.plname << " to bitrate " << bps
|
||||
<< " bps.";
|
||||
if (!SetSendCodec(channel, codec)) {
|
||||
LOG(LS_ERROR) << "Failed to set codec " << codec.plname << " to bitrate "
|
||||
<< bps << " bps.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// If codec is not multi-rate and |bps| is less than the fixed bitrate
|
||||
// then fail. If codec is not multi-rate and |bps| exceeds or equal the
|
||||
// fixed bitrate then ignore.
|
||||
if (bps < codec.rate) {
|
||||
LOG(LS_ERROR) << "Failed to set codec " << codec.plname << " to bitrate "
|
||||
<< bps << " bps"
|
||||
<< ", requires at least " << codec.rate << " bps.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void WebRtcVoiceMediaChannel::OnReadyToSend(bool ready) {
|
||||
|
||||
@ -37,26 +37,6 @@ class AudioSource;
|
||||
class VoEWrapper;
|
||||
class WebRtcVoiceMediaChannel;
|
||||
|
||||
struct SendCodecSpec {
|
||||
SendCodecSpec() {
|
||||
webrtc::CodecInst empty_inst = {0};
|
||||
codec_inst = empty_inst;
|
||||
codec_inst.pltype = -1;
|
||||
}
|
||||
bool operator==(const SendCodecSpec& rhs) const;
|
||||
bool operator!=(const SendCodecSpec& rhs) const;
|
||||
|
||||
bool nack_enabled = false;
|
||||
bool transport_cc_enabled = false;
|
||||
bool enable_codec_fec = false;
|
||||
bool enable_opus_dtx = false;
|
||||
int opus_max_playback_rate = 0;
|
||||
int red_payload_type = -1;
|
||||
int cng_payload_type = -1;
|
||||
int cng_plfreq = -1;
|
||||
webrtc::CodecInst codec_inst;
|
||||
};
|
||||
|
||||
// WebRtcVoiceEngine is a class to be used with CompositeMediaEngine.
|
||||
// It uses the WebRtc VoiceEngine library for audio handling.
|
||||
class WebRtcVoiceEngine final : public webrtc::TraceCallback {
|
||||
@ -237,8 +217,6 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
|
||||
bool SetOptions(const AudioOptions& options);
|
||||
bool SetRecvCodecs(const std::vector<AudioCodec>& codecs);
|
||||
bool SetSendCodecs(const std::vector<AudioCodec>& codecs);
|
||||
bool SetSendCodecs(int channel, const webrtc::RtpParameters& rtp_parameters);
|
||||
bool SetSendCodec(int channel, const webrtc::CodecInst& send_codec);
|
||||
bool SetLocalSource(uint32_t ssrc, AudioSource* source);
|
||||
bool MuteStream(uint32_t ssrc, bool mute);
|
||||
|
||||
@ -251,12 +229,6 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
|
||||
return default_recv_ssrc_ == static_cast<int64_t>(ssrc);
|
||||
}
|
||||
bool SetMaxSendBitrate(int bps);
|
||||
bool SetChannelSendParameters(int channel,
|
||||
const webrtc::RtpParameters& parameters);
|
||||
bool SetMaxSendBitrate(int channel, int bps);
|
||||
bool HasSendCodec() const {
|
||||
return send_codec_spec_.codec_inst.pltype != -1;
|
||||
}
|
||||
bool ValidateRtpParameters(const webrtc::RtpParameters& parameters);
|
||||
void SetupRecording();
|
||||
|
||||
@ -293,7 +265,7 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
|
||||
std::map<uint32_t, WebRtcAudioReceiveStream*> recv_streams_;
|
||||
std::vector<webrtc::RtpExtension> recv_rtp_extensions_;
|
||||
|
||||
SendCodecSpec send_codec_spec_;
|
||||
webrtc::AudioSendStream::Config::SendCodecSpec send_codec_spec_;
|
||||
|
||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WebRtcVoiceMediaChannel);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user