Index: webrtc/audio/audio_send_stream.cc |
diff --git a/webrtc/audio/audio_send_stream.cc b/webrtc/audio/audio_send_stream.cc |
index ee84b96aaa3700a8e7d937e0cf3d4ec1dfd0990d..92d07f516b77babec26860ce1b731a07bb137504 100644 |
--- a/webrtc/audio/audio_send_stream.cc |
+++ b/webrtc/audio/audio_send_stream.cc |
@@ -10,6 +10,7 @@ |
#include "webrtc/audio/audio_send_stream.h" |
+#include <algorithm> |
#include <string> |
#include "webrtc/audio/audio_state.h" |
@@ -30,6 +31,28 @@ |
#include "webrtc/voice_engine/voice_engine_impl.h" |
namespace webrtc { |
+ |
+namespace { |
+ |
+constexpr char kOpusCodecName[] = "opus"; |
+ |
+bool IsCodec(const webrtc::CodecInst& codec, const char* ref_name) { |
+ return (_stricmp(codec.plname, ref_name) == 0); |
+} |
+ |
+template <typename T> |
+static T MinPositive(T a, T b) { |
+ if (a <= 0) { |
+ return b; |
+ } |
+ if (b <= 0) { |
+ return a; |
+ } |
+ return std::min(a, b); |
+} |
+ |
+} // namespace |
+ |
std::string AudioSendStream::Config::Rtp::ToString() const { |
std::stringstream ss; |
ss << "{ssrc: " << ssrc; |
@@ -102,6 +125,7 @@ AudioSendStream::AudioSendStream( |
RTC_NOTREACHED() << "Registering unsupported RTP extension."; |
} |
} |
+ SetSendCodecs(); |
} |
AudioSendStream::~AudioSendStream() { |
@@ -285,5 +309,209 @@ 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::SetSendCodecs() { |
+ 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); |
+ |
+ // Set the codec immediately, since SetVADStatus() depends on whether |
+ // the current codec is mono or stereo. |
+ if (!SetSendCodec(config_.send_codec_spec.codec_inst)) { |
+ return false; |
+ } |
+ |
+ // FEC should be enabled after SetSendCodec. |
+ if (config_.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. |
+ // TODO(minyue): use normal logging. |
+ // LOG_RTCERR2(SetFECStatus, channel, true); |
+ return false; |
+ } |
+ } |
+ |
+ if (IsCodec(config_.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 " |
+ << (config_.send_codec_spec.enable_opus_dtx ? "enable" |
+ : "disable") |
+ << " Opus DTX on channel " << channel; |
+ if (codec->SetOpusDtx(channel, config_.send_codec_spec.enable_opus_dtx)) { |
+ // TODO(minyue): use normal logging. |
+ // LOG_RTCERR2(SetOpusDtx, channel, |
+ // config_.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 (config_.send_codec_spec.opus_max_playback_rate > 0) { |
+ LOG(LS_INFO) << "Attempt to set maximum playback rate to " |
+ << config_.send_codec_spec.opus_max_playback_rate |
+ << " Hz on channel " << channel; |
+ if (codec->SetOpusMaxPlaybackRate( |
+ channel, config_.send_codec_spec.opus_max_playback_rate) == -1) { |
+ // TODO(minyue): use normal logging. |
+ // LOG_RTCERR2(SetOpusMaxPlaybackRate, channel, |
+ // config_.send_codec_spec.opus_max_playback_rate); |
+ return false; |
+ } |
+ } |
+ } |
+ // TODO(solenberg): ApplyMaxSendBitrate() yields another call to |
+ // SetSendCodec(). Check if it is possible to fuse with the previous call |
+ // in this function. |
+ ApplyMaxSendBitrate(); |
minyue-webrtc
2016/10/13 12:48:08
The only reason to place this here is, as far as I
|
+ |
+ // Set the CN payloadtype and the VAD status. |
+ if (config_.send_codec_spec.cng_payload_type != -1) { |
+ // The CN payload type for 8000 Hz clockrate is fixed at 13. |
+ if (config_.send_codec_spec.cng_plfreq != 8000) { |
+ webrtc::PayloadFrequencies cn_freq; |
+ switch (config_.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, |
+ config_.send_codec_spec.cng_payload_type, |
+ cn_freq) == -1) { |
+ // TODO(minyue): use normal logging. |
+ // LOG_RTCERR3(SetSendCNPayloadType, channel, |
+ // config_.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 (config_.send_codec_spec.cng_plfreq == |
+ config_.send_codec_spec.codec_inst.plfreq && |
+ config_.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) { |
+ // TODO(minyue): use normal logging. |
+ // LOG_RTCERR2(SetVADStatus, channel, true); |
+ return false; |
+ } |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool AudioSendStream::SetSendCodec(const webrtc::CodecInst& send_codec) { |
+ // TODO(minyue): avoid ToString |
+ // LOG(LS_INFO) << "Send channel " << channel << " selected voice codec " |
+ // << ToString(send_codec) << ", bitrate=" << send_codec.rate; |
+ |
+ ScopedVoEInterface<VoECodec> codec(voice_engine()); |
+ int channel = config_.voe_channel_id; |
+ |
+ webrtc::CodecInst current_codec = {0}; |
+ if (codec->GetSendCodec(channel, current_codec) == 0 && |
+ (send_codec == current_codec)) { |
+ // Codec is already configured, we can return without setting it again. |
+ return true; |
+ } |
+ |
+ if (codec->SetSendCodec(channel, send_codec) == -1) { |
+ // TODO(minyue): use normal logging. |
+ // LOG_RTCERR2(SetSendCodec, channel, ToString(send_codec)); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+bool AudioSendStream::ApplyMaxSendBitrate() { |
+ int bps = config_.max_send_bitrate_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 (config_.send_codec_spec.codec_inst.pltype == -1) { |
+ 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 = config_.send_codec_spec.codec_inst; |
+ bool is_multi_rate = IsCodecMultiRate(codec); |
+ |
+ if (is_multi_rate) { |
+ // If codec is multi-rate then just set the bitrate. |
+ int max_bitrate_bps = MaxBitrateBps(codec); |
+ codec.rate = std::min(bps, max_bitrate_bps); |
+ LOG(LS_INFO) << "Setting codec " << codec.plname << " to bitrate " << bps |
+ << " bps."; |
+ if (!SetSendCodec(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; |
+ } |
+} |
+ |
+bool AudioSendStream::IsCodecMultiRate(const webrtc::CodecInst& codec) const { |
+ for (size_t i = 0; i < arraysize(config_.codec_prefs); ++i) { |
+ if (IsCodec(codec, config_.codec_prefs[i].name) && |
+ config_.codec_prefs[i].clockrate == codec.plfreq) { |
+ return config_.codec_prefs[i].is_multi_rate; |
+ } |
+ } |
+ return false; |
+} |
+ |
+int AudioSendStream::MaxBitrateBps(const webrtc::CodecInst& codec) const { |
+ for (size_t i = 0; i < arraysize(config_.codec_prefs); ++i) { |
+ if (IsCodec(codec, config_.codec_prefs[i].name) && |
+ config_.codec_prefs[i].clockrate == codec.plfreq) { |
+ return config_.codec_prefs[i].max_bitrate_bps; |
+ } |
+ } |
+ return 0; |
+} |
+ |
} // namespace internal |
} // namespace webrtc |