text: // Copyright 2020 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef CAST_STANDALONE_SENDER_STREAMING_OPUS_ENCODER_H_
  #define CAST_STANDALONE_SENDER_STREAMING_OPUS_ENCODER_H_
  
  #include <stdint.h>
  
  #include <memory>
  
  #include "cast/streaming/encoded_frame.h"
  #include "cast/streaming/sender.h"
  #include "platform/api/time.h"
  
  extern "C" {
  struct OpusEncoder;
  }
  
  namespace openscreen {
  namespace cast {
  
  // Wraps the libopus encoder so that the application can stream
  // interleaved-floats audio samples to a Sender. Either mono or stereo sound is
  // supported.
  class StreamingOpusEncoder {
   public:
    // Constructs the encoder for mono or stereo sound, dividing the stream of
    // audio samples up into chunks as determined by the given
    // |cast_frames_per_second|, and for EncodedFrame output to the given
    // |sender|. The sample rate of the audio is assumed to be the Sender's fixed
    // |rtp_timebase()|.
    StreamingOpusEncoder(int num_channels,
                         int cast_frames_per_second,
                         Sender* sender);
  
    ~StreamingOpusEncoder();
  
    int num_channels() const { return num_channels_; }
    int sample_rate() const { return sender_->rtp_timebase(); }
  
    int GetBitrate() const;
  
    // Sets the encoder back to its "AUTO" bitrate setting, for standard quality.
    // This and UseHighQuality() may be called as often as needed as conditions
    // change.
    //
    // Note: As of 2020-01-21, the encoder in "auto bitrate" mode would use a
    // bitrate of 102kbps for 2-channel, 48 kHz audio and a 10 ms frame size.
    void UseStandardQuality();
  
    // Sets the encoder to use a high bitrate (virtually no artifacts), when
    // plenty of network bandwidth is available. This and UseStandardQuality() may
    // be called as often as needed as conditions change.
    void UseHighQuality();
  
    // Encode and send the given |interleaved_samples|, which contains
    // |num_samples| tuples (i.e., multiply by the number of channels to determine
    // the number of array elements). The audio is assumed to have been captured
    // at the required |sample_rate()|. |reference_time| refers to the first
    // sample.
    void EncodeAndSend(const float* interleaved_samples,
                       int num_samples,
                       Clock::time_point reference_time);
  
    static constexpr int kDefaultCastAudioFramesPerSecond =
        100;  // 10 ms frame duration.
  
   private:
    OpusEncoder* encoder() const {
      return reinterpret_cast<OpusEncoder*>(encoder_storage_.get());
    }
  
    // Updates the |codec_delay_| based on the current encoder settings.
    void UpdateCodecDelay();
  
    // Sets the next frame's reference time, accounting for codec buffering delay.
    // Also, checks whether the reference time has drifted too far forwards, and
    // skips if necessary.
    void ResolveTimestampsAndMaybeSkip(Clock::time_point reference_time);
  
    // Fills the input buffer as much as possible from the given source data, and
    // returns the number of samples copied into the buffer.
    int FillInputBuffer(const float* interleaved_samples, int num_samples);
  
    const int num_channels_;
    Sender* const sender_;
    const int samples_per_cast_frame_;
    const Clock::duration approximate_cast_frame_duration_;
    const std::unique_ptr<uint8_t[]> encoder_storage_;
    const std::unique_ptr<float[]> input_;     // Interleaved audio samples.
    const std::unique_ptr<uint8_t[]> output_;  // Opus-encoded packet.
  
    // The audio delay introduced by the codec.
    Clock::duration codec_delay_{};
  
    // The number of mono/stereo tuples currently queued in the |input_| buffer.
    // Multiply by |num_channels_| to get the number of array elements.
    int num_samples_queued_ = 0;
  
    // The reference time of the first frame passed to EncodeAndSend(), offset by
    // the codec delay.
    Clock::time_point start_time_ = Clock::time_point::min();
  
    // Initialized and used by EncodeAndSend() to hold the metadata and data
    // pointer for each frame being sent.
    EncodedFrame frame_;
  
    // The |reference_time| for the last sent frame. This is used to check that
    // the reference times are monotonically increasing. If they have [illegally]
    // gone backwards too much, warnings will be logged.
    Clock::time_point last_sent_frame_reference_time_;
  
    // This is the recommended value, according to documentation in
    // src/include/opus.h in libopus, so that the Opus encoder does not degrade
    // the audio due to memory constraints.
    static constexpr int kOpusMaxPayloadSize = 4000;
  };
  
  }  // namespace cast
  }  // namespace openscreen
  
  #endif  // CAST_STANDALONE_SENDER_STREAMING_OPUS_ENCODER_H_
  