Home | History | Annotate | Download | only in opus
      1 /*
      2  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h"
     12 
     13 #include "webrtc/base/checks.h"
     14 #include "webrtc/base/safe_conversions.h"
     15 #include "webrtc/common_types.h"
     16 #include "webrtc/modules/audio_coding/codecs/opus/opus_interface.h"
     17 
     18 namespace webrtc {
     19 
     20 namespace {
     21 
     22 const int kSampleRateHz = 48000;
     23 const int kMinBitrateBps = 500;
     24 const int kMaxBitrateBps = 512000;
     25 
     26 AudioEncoderOpus::Config CreateConfig(const CodecInst& codec_inst) {
     27   AudioEncoderOpus::Config config;
     28   config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48);
     29   config.num_channels = codec_inst.channels;
     30   config.bitrate_bps = codec_inst.rate;
     31   config.payload_type = codec_inst.pltype;
     32   config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip
     33                                                 : AudioEncoderOpus::kAudio;
     34   return config;
     35 }
     36 
     37 // Optimize the loss rate to configure Opus. Basically, optimized loss rate is
     38 // the input loss rate rounded down to various levels, because a robustly good
     39 // audio quality is achieved by lowering the packet loss down.
     40 // Additionally, to prevent toggling, margins are used, i.e., when jumping to
     41 // a loss rate from below, a higher threshold is used than jumping to the same
     42 // level from above.
     43 double OptimizePacketLossRate(double new_loss_rate, double old_loss_rate) {
     44   RTC_DCHECK_GE(new_loss_rate, 0.0);
     45   RTC_DCHECK_LE(new_loss_rate, 1.0);
     46   RTC_DCHECK_GE(old_loss_rate, 0.0);
     47   RTC_DCHECK_LE(old_loss_rate, 1.0);
     48   const double kPacketLossRate20 = 0.20;
     49   const double kPacketLossRate10 = 0.10;
     50   const double kPacketLossRate5 = 0.05;
     51   const double kPacketLossRate1 = 0.01;
     52   const double kLossRate20Margin = 0.02;
     53   const double kLossRate10Margin = 0.01;
     54   const double kLossRate5Margin = 0.01;
     55   if (new_loss_rate >=
     56       kPacketLossRate20 +
     57           kLossRate20Margin *
     58               (kPacketLossRate20 - old_loss_rate > 0 ? 1 : -1)) {
     59     return kPacketLossRate20;
     60   } else if (new_loss_rate >=
     61              kPacketLossRate10 +
     62                  kLossRate10Margin *
     63                      (kPacketLossRate10 - old_loss_rate > 0 ? 1 : -1)) {
     64     return kPacketLossRate10;
     65   } else if (new_loss_rate >=
     66              kPacketLossRate5 +
     67                  kLossRate5Margin *
     68                      (kPacketLossRate5 - old_loss_rate > 0 ? 1 : -1)) {
     69     return kPacketLossRate5;
     70   } else if (new_loss_rate >= kPacketLossRate1) {
     71     return kPacketLossRate1;
     72   } else {
     73     return 0.0;
     74   }
     75 }
     76 
     77 }  // namespace
     78 
     79 bool AudioEncoderOpus::Config::IsOk() const {
     80   if (frame_size_ms <= 0 || frame_size_ms % 10 != 0)
     81     return false;
     82   if (num_channels != 1 && num_channels != 2)
     83     return false;
     84   if (bitrate_bps < kMinBitrateBps || bitrate_bps > kMaxBitrateBps)
     85     return false;
     86   if (complexity < 0 || complexity > 10)
     87     return false;
     88   return true;
     89 }
     90 
     91 AudioEncoderOpus::AudioEncoderOpus(const Config& config)
     92     : packet_loss_rate_(0.0), inst_(nullptr) {
     93   RTC_CHECK(RecreateEncoderInstance(config));
     94 }
     95 
     96 AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst)
     97     : AudioEncoderOpus(CreateConfig(codec_inst)) {}
     98 
     99 AudioEncoderOpus::~AudioEncoderOpus() {
    100   RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
    101 }
    102 
    103 size_t AudioEncoderOpus::MaxEncodedBytes() const {
    104   // Calculate the number of bytes we expect the encoder to produce,
    105   // then multiply by two to give a wide margin for error.
    106   const size_t bytes_per_millisecond =
    107       static_cast<size_t>(config_.bitrate_bps / (1000 * 8) + 1);
    108   const size_t approx_encoded_bytes =
    109       Num10msFramesPerPacket() * 10 * bytes_per_millisecond;
    110   return 2 * approx_encoded_bytes;
    111 }
    112 
    113 int AudioEncoderOpus::SampleRateHz() const {
    114   return kSampleRateHz;
    115 }
    116 
    117 size_t AudioEncoderOpus::NumChannels() const {
    118   return config_.num_channels;
    119 }
    120 
    121 size_t AudioEncoderOpus::Num10MsFramesInNextPacket() const {
    122   return Num10msFramesPerPacket();
    123 }
    124 
    125 size_t AudioEncoderOpus::Max10MsFramesInAPacket() const {
    126   return Num10msFramesPerPacket();
    127 }
    128 
    129 int AudioEncoderOpus::GetTargetBitrate() const {
    130   return config_.bitrate_bps;
    131 }
    132 
    133 AudioEncoder::EncodedInfo AudioEncoderOpus::EncodeInternal(
    134     uint32_t rtp_timestamp,
    135     rtc::ArrayView<const int16_t> audio,
    136     size_t max_encoded_bytes,
    137     uint8_t* encoded) {
    138   if (input_buffer_.empty())
    139     first_timestamp_in_buffer_ = rtp_timestamp;
    140   RTC_DCHECK_EQ(SamplesPer10msFrame(), audio.size());
    141   input_buffer_.insert(input_buffer_.end(), audio.cbegin(), audio.cend());
    142   if (input_buffer_.size() <
    143       (Num10msFramesPerPacket() * SamplesPer10msFrame())) {
    144     return EncodedInfo();
    145   }
    146   RTC_CHECK_EQ(input_buffer_.size(),
    147                Num10msFramesPerPacket() * SamplesPer10msFrame());
    148   int status = WebRtcOpus_Encode(
    149       inst_, &input_buffer_[0],
    150       rtc::CheckedDivExact(input_buffer_.size(), config_.num_channels),
    151       rtc::saturated_cast<int16_t>(max_encoded_bytes), encoded);
    152   RTC_CHECK_GE(status, 0);  // Fails only if fed invalid data.
    153   input_buffer_.clear();
    154   EncodedInfo info;
    155   info.encoded_bytes = static_cast<size_t>(status);
    156   info.encoded_timestamp = first_timestamp_in_buffer_;
    157   info.payload_type = config_.payload_type;
    158   info.send_even_if_empty = true;  // Allows Opus to send empty packets.
    159   info.speech = (status > 0);
    160   return info;
    161 }
    162 
    163 void AudioEncoderOpus::Reset() {
    164   RTC_CHECK(RecreateEncoderInstance(config_));
    165 }
    166 
    167 bool AudioEncoderOpus::SetFec(bool enable) {
    168   auto conf = config_;
    169   conf.fec_enabled = enable;
    170   return RecreateEncoderInstance(conf);
    171 }
    172 
    173 bool AudioEncoderOpus::SetDtx(bool enable) {
    174   auto conf = config_;
    175   conf.dtx_enabled = enable;
    176   return RecreateEncoderInstance(conf);
    177 }
    178 
    179 bool AudioEncoderOpus::SetApplication(Application application) {
    180   auto conf = config_;
    181   switch (application) {
    182     case Application::kSpeech:
    183       conf.application = AudioEncoderOpus::kVoip;
    184       break;
    185     case Application::kAudio:
    186       conf.application = AudioEncoderOpus::kAudio;
    187       break;
    188   }
    189   return RecreateEncoderInstance(conf);
    190 }
    191 
    192 void AudioEncoderOpus::SetMaxPlaybackRate(int frequency_hz) {
    193   auto conf = config_;
    194   conf.max_playback_rate_hz = frequency_hz;
    195   RTC_CHECK(RecreateEncoderInstance(conf));
    196 }
    197 
    198 void AudioEncoderOpus::SetProjectedPacketLossRate(double fraction) {
    199   double opt_loss_rate = OptimizePacketLossRate(fraction, packet_loss_rate_);
    200   if (packet_loss_rate_ != opt_loss_rate) {
    201     packet_loss_rate_ = opt_loss_rate;
    202     RTC_CHECK_EQ(
    203         0, WebRtcOpus_SetPacketLossRate(
    204                inst_, static_cast<int32_t>(packet_loss_rate_ * 100 + .5)));
    205   }
    206 }
    207 
    208 void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) {
    209   config_.bitrate_bps =
    210       std::max(std::min(bits_per_second, kMaxBitrateBps), kMinBitrateBps);
    211   RTC_DCHECK(config_.IsOk());
    212   RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config_.bitrate_bps));
    213 }
    214 
    215 size_t AudioEncoderOpus::Num10msFramesPerPacket() const {
    216   return static_cast<size_t>(rtc::CheckedDivExact(config_.frame_size_ms, 10));
    217 }
    218 
    219 size_t AudioEncoderOpus::SamplesPer10msFrame() const {
    220   return rtc::CheckedDivExact(kSampleRateHz, 100) * config_.num_channels;
    221 }
    222 
    223 // If the given config is OK, recreate the Opus encoder instance with those
    224 // settings, save the config, and return true. Otherwise, do nothing and return
    225 // false.
    226 bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
    227   if (!config.IsOk())
    228     return false;
    229   if (inst_)
    230     RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
    231   input_buffer_.clear();
    232   input_buffer_.reserve(Num10msFramesPerPacket() * SamplesPer10msFrame());
    233   RTC_CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, config.num_channels,
    234                                            config.application));
    235   RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config.bitrate_bps));
    236   if (config.fec_enabled) {
    237     RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_));
    238   } else {
    239     RTC_CHECK_EQ(0, WebRtcOpus_DisableFec(inst_));
    240   }
    241   RTC_CHECK_EQ(
    242       0, WebRtcOpus_SetMaxPlaybackRate(inst_, config.max_playback_rate_hz));
    243   RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, config.complexity));
    244   if (config.dtx_enabled) {
    245     RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_));
    246   } else {
    247     RTC_CHECK_EQ(0, WebRtcOpus_DisableDtx(inst_));
    248   }
    249   RTC_CHECK_EQ(0,
    250                WebRtcOpus_SetPacketLossRate(
    251                    inst_, static_cast<int32_t>(packet_loss_rate_ * 100 + .5)));
    252   config_ = config;
    253   return true;
    254 }
    255 
    256 }  // namespace webrtc
    257