// 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 #include #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(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 encoder_storage_; const std::unique_ptr input_; // Interleaved audio samples. const std::unique_ptr 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_