• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/looping_file_sender.h"
6 
7 #include <utility>
8 
9 #if defined(CAST_STANDALONE_SENDER_HAVE_LIBAOM)
10 #include "cast/standalone_sender/streaming_av1_encoder.h"
11 #endif
12 #include "cast/standalone_sender/streaming_vpx_encoder.h"
13 #include "util/osp_logging.h"
14 #include "util/trace_logging.h"
15 
16 namespace openscreen {
17 namespace cast {
18 
LoopingFileSender(Environment * environment,ConnectionSettings settings,const SenderSession * session,SenderSession::ConfiguredSenders senders,ShutdownCallback shutdown_callback)19 LoopingFileSender::LoopingFileSender(Environment* environment,
20                                      ConnectionSettings settings,
21                                      const SenderSession* session,
22                                      SenderSession::ConfiguredSenders senders,
23                                      ShutdownCallback shutdown_callback)
24     : env_(environment),
25       settings_(std::move(settings)),
26       session_(session),
27       shutdown_callback_(std::move(shutdown_callback)),
28       audio_encoder_(senders.audio_sender->config().channels,
29                      StreamingOpusEncoder::kDefaultCastAudioFramesPerSecond,
30                      senders.audio_sender),
31       video_encoder_(CreateVideoEncoder(
32           StreamingVideoEncoder::Parameters{.codec = settings.codec},
33           env_->task_runner(),
34           senders.video_sender)),
35       next_task_(env_->now_function(), env_->task_runner()),
36       console_update_task_(env_->now_function(), env_->task_runner()) {
37   // Opus and Vp8 are the default values for the config, and if these are set
38   // to a different value that means we offered a codec that we do not
39   // support, which is a developer error.
40   OSP_CHECK(senders.audio_config.codec == AudioCodec::kOpus);
41   OSP_CHECK(senders.video_config.codec == VideoCodec::kVp8 ||
42             senders.video_config.codec == VideoCodec::kVp9 ||
43             senders.video_config.codec == VideoCodec::kAv1);
44   OSP_LOG_INFO << "Max allowed media bitrate (audio + video) will be "
45                << settings_.max_bitrate;
46   bandwidth_being_utilized_ = settings_.max_bitrate / 2;
47   UpdateEncoderBitrates();
48 
__anon2db984b40102null49   next_task_.Schedule([this] { SendFileAgain(); }, Alarm::kImmediately);
50 }
51 
52 LoopingFileSender::~LoopingFileSender() = default;
53 
SetPlaybackRate(double rate)54 void LoopingFileSender::SetPlaybackRate(double rate) {
55   video_capturer_->SetPlaybackRate(rate);
56   audio_capturer_->SetPlaybackRate(rate);
57 }
58 
UpdateEncoderBitrates()59 void LoopingFileSender::UpdateEncoderBitrates() {
60   if (bandwidth_being_utilized_ >= kHighBandwidthThreshold) {
61     audio_encoder_.UseHighQuality();
62   } else {
63     audio_encoder_.UseStandardQuality();
64   }
65   video_encoder_->SetTargetBitrate(bandwidth_being_utilized_ -
66                                    audio_encoder_.GetBitrate());
67 }
68 
ControlForNetworkCongestion()69 void LoopingFileSender::ControlForNetworkCongestion() {
70   bandwidth_estimate_ = session_->GetEstimatedNetworkBandwidth();
71   if (bandwidth_estimate_ > 0) {
72     // Don't ever try to use *all* of the network bandwidth! However, don't go
73     // below the absolute minimum requirement either.
74     constexpr double kGoodNetworkCitizenFactor = 0.8;
75     const int usable_bandwidth = std::max<int>(
76         kGoodNetworkCitizenFactor * bandwidth_estimate_, kMinRequiredBitrate);
77 
78     // See "congestion control" discussion in the class header comments for
79     // BandwidthEstimator.
80     if (usable_bandwidth > bandwidth_being_utilized_) {
81       constexpr double kConservativeIncrease = 1.1;
82       bandwidth_being_utilized_ = std::min<int>(
83           bandwidth_being_utilized_ * kConservativeIncrease, usable_bandwidth);
84     } else {
85       bandwidth_being_utilized_ = usable_bandwidth;
86     }
87 
88     // Repsect the user's maximum bitrate setting.
89     bandwidth_being_utilized_ =
90         std::min(bandwidth_being_utilized_, settings_.max_bitrate);
91 
92     UpdateEncoderBitrates();
93   } else {
94     // There is no current bandwidth estimate. So, nothing should be adjusted.
95   }
96 
97   next_task_.ScheduleFromNow([this] { ControlForNetworkCongestion(); },
98                              kCongestionCheckInterval);
99 }
100 
SendFileAgain()101 void LoopingFileSender::SendFileAgain() {
102   OSP_LOG_INFO << "Sending " << settings_.path_to_file
103                << " (starts in one second)...";
104   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
105 
106   OSP_DCHECK_EQ(num_capturers_running_, 0);
107   num_capturers_running_ = 2;
108   capture_start_time_ = latest_frame_time_ = env_->now() + seconds(1);
109   audio_capturer_.emplace(
110       env_, settings_.path_to_file.c_str(), audio_encoder_.num_channels(),
111       audio_encoder_.sample_rate(), capture_start_time_, this);
112   video_capturer_.emplace(env_, settings_.path_to_file.c_str(),
113                           capture_start_time_, this);
114 
115   next_task_.ScheduleFromNow([this] { ControlForNetworkCongestion(); },
116                              kCongestionCheckInterval);
117   console_update_task_.Schedule([this] { UpdateStatusOnConsole(); },
118                                 capture_start_time_);
119 }
120 
OnAudioData(const float * interleaved_samples,int num_samples,Clock::time_point capture_time)121 void LoopingFileSender::OnAudioData(const float* interleaved_samples,
122                                     int num_samples,
123                                     Clock::time_point capture_time) {
124   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
125   latest_frame_time_ = std::max(capture_time, latest_frame_time_);
126   audio_encoder_.EncodeAndSend(interleaved_samples, num_samples, capture_time);
127 }
128 
OnVideoFrame(const AVFrame & av_frame,Clock::time_point capture_time)129 void LoopingFileSender::OnVideoFrame(const AVFrame& av_frame,
130                                      Clock::time_point capture_time) {
131   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
132   latest_frame_time_ = std::max(capture_time, latest_frame_time_);
133   StreamingVideoEncoder::VideoFrame frame{};
134   frame.width = av_frame.width - av_frame.crop_left - av_frame.crop_right;
135   frame.height = av_frame.height - av_frame.crop_top - av_frame.crop_bottom;
136   frame.yuv_planes[0] = av_frame.data[0] + av_frame.crop_left +
137                         av_frame.linesize[0] * av_frame.crop_top;
138   frame.yuv_planes[1] = av_frame.data[1] + av_frame.crop_left / 2 +
139                         av_frame.linesize[1] * av_frame.crop_top / 2;
140   frame.yuv_planes[2] = av_frame.data[2] + av_frame.crop_left / 2 +
141                         av_frame.linesize[2] * av_frame.crop_top / 2;
142   for (int i = 0; i < 3; ++i) {
143     frame.yuv_strides[i] = av_frame.linesize[i];
144   }
145   // TODO(jophba): Add performance metrics visual overlay (based on Stats
146   // callback).
147   video_encoder_->EncodeAndSend(frame, capture_time, {});
148 }
149 
UpdateStatusOnConsole()150 void LoopingFileSender::UpdateStatusOnConsole() {
151   const Clock::duration elapsed = latest_frame_time_ - capture_start_time_;
152   const auto seconds_part = to_seconds(elapsed);
153   const auto millis_part = to_milliseconds(elapsed - seconds_part);
154   // The control codes here attempt to erase the current line the cursor is
155   // on, and then print out the updated status text. If the terminal does not
156   // support simple ANSI escape codes, the following will still work, but
157   // there might sometimes be old status lines not getting erased (i.e., just
158   // partially overwritten).
159   fprintf(stdout,
160           "\r\x1b[2K\rLoopingFileSender: At %01" PRId64
161           ".%03ds in file (est. network bandwidth: %d kbps). \n",
162           static_cast<int64_t>(seconds_part.count()),
163           static_cast<int>(millis_part.count()), bandwidth_estimate_ / 1024);
164   fflush(stdout);
165 
166   console_update_task_.ScheduleFromNow([this] { UpdateStatusOnConsole(); },
167                                        kConsoleUpdateInterval);
168 }
169 
OnEndOfFile(SimulatedCapturer * capturer)170 void LoopingFileSender::OnEndOfFile(SimulatedCapturer* capturer) {
171   OSP_LOG_INFO << "The " << ToTrackName(capturer)
172                << " capturer has reached the end of the media stream.";
173   --num_capturers_running_;
174   if (num_capturers_running_ == 0) {
175     console_update_task_.Cancel();
176 
177     if (settings_.should_loop_video) {
178       OSP_DLOG_INFO << "Starting the media stream over again.";
179       next_task_.Schedule([this] { SendFileAgain(); }, Alarm::kImmediately);
180     } else {
181       OSP_DLOG_INFO << "Video complete. Exiting...";
182       shutdown_callback_();
183     }
184   }
185 }
186 
OnError(SimulatedCapturer * capturer,std::string message)187 void LoopingFileSender::OnError(SimulatedCapturer* capturer,
188                                 std::string message) {
189   OSP_LOG_ERROR << "The " << ToTrackName(capturer)
190                 << " has failed: " << message;
191   --num_capturers_running_;
192   // If both fail, the application just pauses. This accounts for things like
193   // "file not found" errors. However, if only one track fails, then keep
194   // going.
195 }
196 
ToTrackName(SimulatedCapturer * capturer) const197 const char* LoopingFileSender::ToTrackName(SimulatedCapturer* capturer) const {
198   const char* which;
199   if (capturer == &*audio_capturer_) {
200     which = "audio";
201   } else if (capturer == &*video_capturer_) {
202     which = "video";
203   } else {
204     OSP_NOTREACHED();
205     which = "";
206   }
207   return which;
208 }
209 
CreateVideoEncoder(const StreamingVideoEncoder::Parameters & params,TaskRunner * task_runner,Sender * sender)210 std::unique_ptr<StreamingVideoEncoder> LoopingFileSender::CreateVideoEncoder(
211     const StreamingVideoEncoder::Parameters& params,
212     TaskRunner* task_runner,
213     Sender* sender) {
214   switch (params.codec) {
215     case VideoCodec::kVp8:
216     case VideoCodec::kVp9:
217       return std::make_unique<StreamingVpxEncoder>(params, task_runner, sender);
218     case VideoCodec::kAv1:
219 #if defined(CAST_STANDALONE_SENDER_HAVE_LIBAOM)
220       return std::make_unique<StreamingAv1Encoder>(params, task_runner, sender);
221 #else
222       OSP_LOG_FATAL << "AV1 codec selected, but could not be used because "
223                        "LibAOM not installed.";
224 #endif
225     default:
226       // Since we only support VP8, VP9, and AV1, any other codec value here
227       // should be due only to developer error.
228       OSP_LOG_ERROR << "Unsupported codec " << CodecToString(params.codec);
229       OSP_NOTREACHED();
230   }
231 }
232 
233 }  // namespace cast
234 }  // namespace openscreen
235