Chromium Code Reviews| 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..00827a27cca54a42a9176c07ebfd3875ef6354b6 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,84 @@ |
| #include "webrtc/voice_engine/voice_engine_impl.h" |
| namespace webrtc { |
| + |
| +namespace { |
| +// TODO(minyue): these are all copied from corresponding cricket. Find good |
| +// place for them. |
| + |
| +const char kOpusCodecName[] = "opus"; |
| +const char kIsacCodecName[] = "isac"; |
| +const char kG722CodecName[] = "g722"; |
| +const char kIlbcCodecName[] = "ilbc"; |
| +const char kPcmuCodecName[] = "pcmu"; |
| +const char kPcmaCodecName[] = "pcma"; |
| +const char kCnCodecName[] = "cn"; |
| +const char kDtmfCodecName[] = "telephone-event"; |
| +const int kOpusMaxBitrate = 510000; |
| +const int kIsacMaxBitrate = 56000; |
| + |
| +static const int kMaxNumPacketSize = 6; |
| +struct CodecPref { |
| + const char* name; |
| + int clockrate; |
| + size_t channels; |
| + int payload_type; |
| + bool is_multi_rate; |
| + int packet_sizes_ms[kMaxNumPacketSize]; |
| + int max_bitrate_bps; |
| +}; |
| + |
| +const CodecPref kCodecPrefs[11] = { |
| + {kOpusCodecName, 48000, 2, 111, true, {10, 20, 40, 60}, kOpusMaxBitrate}, |
| + {kIsacCodecName, 16000, 1, 103, true, {30, 60}, kIsacMaxBitrate}, |
| + {kIsacCodecName, 32000, 1, 104, true, {30}, kIsacMaxBitrate}, |
| + // G722 should be advertised as 8000 Hz because of the RFC "bug". |
| + {kG722CodecName, 8000, 1, 9, false, {10, 20, 30, 40, 50, 60}}, |
| + {kIlbcCodecName, 8000, 1, 102, false, {20, 30, 40, 60}}, |
| + {kPcmuCodecName, 8000, 1, 0, false, {10, 20, 30, 40, 50, 60}}, |
| + {kPcmaCodecName, 8000, 1, 8, false, {10, 20, 30, 40, 50, 60}}, |
| + {kCnCodecName, 32000, 1, 106, false, {}}, |
| + {kCnCodecName, 16000, 1, 105, false, {}}, |
| + {kCnCodecName, 8000, 1, 13, false, {}}, |
| + {kDtmfCodecName, 8000, 1, 126, false, {}}}; |
| + |
| +bool IsCodec(const webrtc::CodecInst& codec, const char* ref_name) { |
| + return (_stricmp(codec.plname, ref_name) == 0); |
| +} |
| + |
| +bool IsCodecMultiRate(const webrtc::CodecInst& codec) { |
| + for (size_t i = 0; i < arraysize(kCodecPrefs); ++i) { |
| + if (IsCodec(codec, kCodecPrefs[i].name) && |
| + kCodecPrefs[i].clockrate == codec.plfreq) { |
| + return kCodecPrefs[i].is_multi_rate; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +int MaxBitrateBps(const webrtc::CodecInst& codec) { |
| + for (size_t i = 0; i < arraysize(kCodecPrefs); ++i) { |
| + if (IsCodec(codec, kCodecPrefs[i].name) && |
| + kCodecPrefs[i].clockrate == codec.plfreq) { |
| + return kCodecPrefs[i].max_bitrate_bps; |
| + } |
| + } |
| + return 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 +181,7 @@ AudioSendStream::AudioSendStream( |
| RTC_NOTREACHED() << "Registering unsupported RTP extension."; |
| } |
| } |
| + SetSendCodecs(); |
| } |
| AudioSendStream::~AudioSendStream() { |
| @@ -285,5 +365,193 @@ 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(); |
| + |
| + // 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 (!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 = 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::HasSendCodec() const { |
| + return config_.send_codec_spec.codec_inst.pltype != -1; |
|
the sun
2016/10/12 15:15:17
Think we can do without this function.
minyue-webrtc
2016/10/12 18:59:21
sure.
|
| +} |
| + |
| } // namespace internal |
| } // namespace webrtc |