1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "cast/standalone_sender/streaming_opus_encoder.h"
6
7 #include <opus/opus.h>
8
9 #include <algorithm>
10 #include <chrono>
11
12 #include "util/chrono_helpers.h"
13
14 namespace openscreen {
15 namespace cast {
16
17 using openscreen::operator<<; // To pretty-print chrono values.
18
19 namespace {
20
21 // The bitrate at which virtually all stereo audio can be encoded and decoded
22 // without human-perceivable artifacts. Source:
23 // https://wiki.hydrogenaud.io/index.php?title=Opus#Bitrate_performance
24 constexpr opus_int32 kTransparentBitrate = 160000;
25
26 // The maximum number of Cast audio frames the encoder may fall behind by before
27 // skipping-ahead the RTP timestamps to compensate.
28 constexpr int kMaxCastFramesBeforeSkip = 3;
29
30 } // namespace
31
StreamingOpusEncoder(int num_channels,int cast_frames_per_second,Sender * sender)32 StreamingOpusEncoder::StreamingOpusEncoder(int num_channels,
33 int cast_frames_per_second,
34 Sender* sender)
35 : num_channels_(num_channels),
36 sender_(sender),
37 samples_per_cast_frame_(sample_rate() / cast_frames_per_second),
38 approximate_cast_frame_duration_(Clock::to_duration(seconds(1)) /
39 cast_frames_per_second),
40 encoder_storage_(new uint8_t[opus_encoder_get_size(num_channels_)]),
41 input_(new float[num_channels_ * samples_per_cast_frame_]),
42 output_(new uint8_t[kOpusMaxPayloadSize]) {
43 OSP_CHECK_GT(cast_frames_per_second, 0);
44 OSP_DCHECK(sender_);
45 OSP_CHECK_GT(samples_per_cast_frame_, 0);
46 OSP_CHECK_EQ(sample_rate() % cast_frames_per_second, 0);
47 OSP_CHECK(approximate_cast_frame_duration_ > Clock::duration::zero());
48
49 frame_.dependency = EncodedFrame::KEY_FRAME;
50
51 const auto init_result = opus_encoder_init(
52 encoder(), sample_rate(), num_channels_, OPUS_APPLICATION_AUDIO);
53 OSP_CHECK_EQ(init_result, OPUS_OK);
54
55 UseStandardQuality();
56 }
57
58 StreamingOpusEncoder::~StreamingOpusEncoder() = default;
59
GetBitrate() const60 int StreamingOpusEncoder::GetBitrate() const {
61 opus_int32 bitrate;
62 const auto ctl_result =
63 opus_encoder_ctl(encoder(), OPUS_GET_BITRATE(&bitrate));
64 OSP_CHECK_EQ(ctl_result, OPUS_OK);
65 return bitrate;
66 }
67
UseStandardQuality()68 void StreamingOpusEncoder::UseStandardQuality() {
69 const auto ctl_result =
70 opus_encoder_ctl(encoder(), OPUS_SET_BITRATE(OPUS_AUTO));
71 OSP_CHECK_EQ(ctl_result, OPUS_OK);
72 UpdateCodecDelay();
73 }
74
UseHighQuality()75 void StreamingOpusEncoder::UseHighQuality() {
76 // kTransparentBitrate assumes stereo audio. Scale it by the actual number of
77 // channels.
78 const opus_int32 bitrate = kTransparentBitrate * num_channels_ / 2;
79 const auto ctl_result =
80 opus_encoder_ctl(encoder(), OPUS_SET_BITRATE(bitrate));
81 OSP_CHECK_EQ(ctl_result, OPUS_OK);
82 UpdateCodecDelay();
83 }
84
EncodeAndSend(const float * interleaved_samples,int num_samples,Clock::time_point reference_time)85 void StreamingOpusEncoder::EncodeAndSend(const float* interleaved_samples,
86 int num_samples,
87 Clock::time_point reference_time) {
88 OSP_DCHECK(interleaved_samples);
89 OSP_DCHECK_GT(num_samples, 0);
90
91 ResolveTimestampsAndMaybeSkip(reference_time);
92
93 while (num_samples > 0) {
94 const int samples_copied =
95 FillInputBuffer(interleaved_samples, num_samples);
96 num_samples -= samples_copied;
97 interleaved_samples += num_channels_ * samples_copied;
98
99 if (num_samples_queued_ < samples_per_cast_frame_) {
100 return; // Not enough yet for a full Cast audio frame.
101 }
102
103 const opus_int32 packet_size_or_error =
104 opus_encode_float(encoder(), input_.get(), num_samples_queued_,
105 output_.get(), kOpusMaxPayloadSize);
106 num_samples_queued_ = 0;
107 if (packet_size_or_error < 0) {
108 OSP_LOG_FATAL << "AUDIO[" << sender_->ssrc()
109 << "] Error code from opus_encode_float(): "
110 << packet_size_or_error;
111 return;
112 }
113
114 frame_.frame_id = sender_->GetNextFrameId();
115 frame_.referenced_frame_id = frame_.frame_id;
116 // Note: It's possible for Opus to encode a zero byte packet. Send a Cast
117 // audio frame anyway, to represent the passage of silence and to send other
118 // stream metadata.
119 frame_.data = absl::Span<uint8_t>(output_.get(), packet_size_or_error);
120 last_sent_frame_reference_time_ = frame_.reference_time;
121 switch (sender_->EnqueueFrame(frame_)) {
122 case Sender::OK:
123 break;
124 case Sender::PAYLOAD_TOO_LARGE:
125 OSP_NOTREACHED(); // The Opus packet cannot possibly be too large.
126 break;
127 case Sender::REACHED_ID_SPAN_LIMIT:
128 OSP_LOG_WARN << "AUDIO[" << sender_->ssrc()
129 << "] Dropping: FrameId span limit reached.";
130 break;
131 case Sender::MAX_DURATION_IN_FLIGHT:
132 OSP_LOG_INFO << "AUDIO[" << sender_->ssrc()
133 << "] Dropping: In-flight duration would be too high.";
134 break;
135 }
136
137 frame_.rtp_timestamp += RtpTimeDelta::FromTicks(samples_per_cast_frame_);
138 frame_.reference_time += approximate_cast_frame_duration_;
139 }
140 }
141
UpdateCodecDelay()142 void StreamingOpusEncoder::UpdateCodecDelay() {
143 opus_int32 lookahead = 0;
144 const auto ctl_result =
145 opus_encoder_ctl(encoder(), OPUS_GET_LOOKAHEAD(&lookahead));
146 OSP_CHECK_EQ(ctl_result, OPUS_OK);
147 codec_delay_ = RtpTimeDelta::FromTicks(lookahead).ToDuration<Clock::duration>(
148 sample_rate());
149 }
150
ResolveTimestampsAndMaybeSkip(Clock::time_point reference_time)151 void StreamingOpusEncoder::ResolveTimestampsAndMaybeSkip(
152 Clock::time_point reference_time) {
153 // Back-track the reference time to account for the audio delay introduced by
154 // the codec.
155 reference_time -= codec_delay_;
156
157 // Special case: Nothing special for the first frame's timestamps.
158 if (start_time_ == Clock::time_point::min()) {
159 frame_.rtp_timestamp = RtpTimeTicks();
160 frame_.reference_time = start_time_ = reference_time;
161 last_sent_frame_reference_time_ =
162 reference_time - approximate_cast_frame_duration_;
163 return;
164 }
165
166 const RtpTimeTicks current_position =
167 frame_.rtp_timestamp + RtpTimeDelta::FromTicks(num_samples_queued_);
168 const RtpTimeTicks reference_position = RtpTimeTicks::FromTimeSinceOrigin(
169 reference_time - start_time_, sample_rate());
170 const RtpTimeDelta rtp_advancement = reference_position - current_position;
171 const RtpTimeDelta skip_threshold =
172 RtpTimeDelta::FromTicks(samples_per_cast_frame_) *
173 kMaxCastFramesBeforeSkip;
174 if (rtp_advancement > skip_threshold) {
175 OSP_LOG_WARN << "Detected audio gap "
176 << rtp_advancement.ToDuration<microseconds>(sample_rate())
177 << ", skipping ahead...";
178 num_samples_queued_ = 0;
179 frame_.rtp_timestamp = reference_position;
180 }
181
182 // Further back-track the reference time to account for the already-queued
183 // samples.
184 reference_time -= RtpTimeDelta::FromTicks(num_samples_queued_)
185 .ToDuration<Clock::duration>(sample_rate());
186
187 // Frame reference times must be monotonically increasing. A little noise in
188 // the negative direction is okay to cap-off. Log a warning if there's a
189 // bigger problem (at the source).
190 const Clock::time_point lower_bound =
191 last_sent_frame_reference_time_ +
192 RtpTimeDelta::FromTicks(1).ToDuration<Clock::duration>(sample_rate());
193 if (reference_time < lower_bound) {
194 const Clock::duration backwards_amount =
195 last_sent_frame_reference_time_ - reference_time;
196 OSP_LOG_IF(WARN, backwards_amount >= approximate_cast_frame_duration_)
197 << "Reference time went *backwards* too much (" << backwards_amount
198 << " in wrong direction). A/V sync may suffer at the Receiver!";
199 reference_time = lower_bound;
200 }
201
202 frame_.reference_time = reference_time;
203 }
204
FillInputBuffer(const float * interleaved_samples,int num_samples)205 int StreamingOpusEncoder::FillInputBuffer(const float* interleaved_samples,
206 int num_samples) {
207 const int samples_needed = samples_per_cast_frame_ - num_samples_queued_;
208 const int samples_to_copy = std::min(num_samples, samples_needed);
209 std::copy(interleaved_samples,
210 interleaved_samples + num_channels_ * samples_to_copy,
211 input_.get() + num_channels_ * num_samples_queued_);
212 num_samples_queued_ += samples_to_copy;
213 return samples_to_copy;
214 }
215
216 // static
217 constexpr int StreamingOpusEncoder::kDefaultCastAudioFramesPerSecond;
218 // static
219 constexpr int StreamingOpusEncoder::kOpusMaxPayloadSize;
220
221 } // namespace cast
222 } // namespace openscreen
223