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 "util/trace_logging.h"
8 
9 namespace openscreen {
10 namespace cast {
11 
LoopingFileSender(TaskRunner * task_runner,const char * path,const IPEndpoint & remote_endpoint,SenderSession::ConfiguredSenders senders,int max_bitrate)12 LoopingFileSender::LoopingFileSender(TaskRunner* task_runner,
13                                      const char* path,
14                                      const IPEndpoint& remote_endpoint,
15                                      SenderSession::ConfiguredSenders senders,
16                                      int max_bitrate)
17     : env_(&Clock::now, task_runner),
18       path_(path),
19       packet_router_(&env_),
20       max_bitrate_(max_bitrate),
21       audio_encoder_(senders.audio_sender->config().channels,
22                      StreamingOpusEncoder::kDefaultCastAudioFramesPerSecond,
23                      senders.audio_sender),
24       video_encoder_(StreamingVp8Encoder::Parameters{},
25                      env_.task_runner(),
26                      senders.video_sender),
27       next_task_(env_.now_function(), env_.task_runner()),
28       console_update_task_(env_.now_function(), env_.task_runner()) {
29   env_.set_remote_endpoint(remote_endpoint);
30   // Opus and Vp8 are the default values for the config, and if these are set
31   // to a different value that means we offered a codec that we do not
32   // support, which is a developer error.
33   OSP_CHECK(senders.audio_config.codec == AudioCodec::kOpus);
34   OSP_CHECK(senders.video_config.codec == VideoCodec::kVp8);
35   OSP_LOG_INFO << "Streaming to " << remote_endpoint << "...";
36   OSP_LOG_INFO << "Max allowed media bitrate (audio + video) will be "
37                << max_bitrate_;
38   bandwidth_being_utilized_ = max_bitrate_ / 2;
39   UpdateEncoderBitrates();
40 
__anonc8c487f30102null41   next_task_.Schedule([this] { SendFileAgain(); }, Alarm::kImmediately);
42 }
43 
44 LoopingFileSender::~LoopingFileSender() = default;
45 
UpdateEncoderBitrates()46 void LoopingFileSender::UpdateEncoderBitrates() {
47   if (bandwidth_being_utilized_ >= kHighBandwidthThreshold) {
48     audio_encoder_.UseHighQuality();
49   } else {
50     audio_encoder_.UseStandardQuality();
51   }
52   video_encoder_.SetTargetBitrate(bandwidth_being_utilized_ -
53                                   audio_encoder_.GetBitrate());
54 }
55 
ControlForNetworkCongestion()56 void LoopingFileSender::ControlForNetworkCongestion() {
57   bandwidth_estimate_ = packet_router_.ComputeNetworkBandwidth();
58   if (bandwidth_estimate_ > 0) {
59     // Don't ever try to use *all* of the network bandwidth! However, don't go
60     // below the absolute minimum requirement either.
61     constexpr double kGoodNetworkCitizenFactor = 0.8;
62     const int usable_bandwidth = std::max<int>(
63         kGoodNetworkCitizenFactor * bandwidth_estimate_, kMinRequiredBitrate);
64 
65     // See "congestion control" discussion in the class header comments for
66     // BandwidthEstimator.
67     if (usable_bandwidth > bandwidth_being_utilized_) {
68       constexpr double kConservativeIncrease = 1.1;
69       bandwidth_being_utilized_ = std::min<int>(
70           bandwidth_being_utilized_ * kConservativeIncrease, usable_bandwidth);
71     } else {
72       bandwidth_being_utilized_ = usable_bandwidth;
73     }
74 
75     // Repsect the user's maximum bitrate setting.
76     bandwidth_being_utilized_ =
77         std::min(bandwidth_being_utilized_, max_bitrate_);
78 
79     UpdateEncoderBitrates();
80   } else {
81     // There is no current bandwidth estimate. So, nothing should be adjusted.
82   }
83 
84   next_task_.ScheduleFromNow([this] { ControlForNetworkCongestion(); },
85                              kCongestionCheckInterval);
86 }
87 
SendFileAgain()88 void LoopingFileSender::SendFileAgain() {
89   OSP_LOG_INFO << "Sending " << path_ << " (starts in one second)...";
90   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
91 
92   OSP_DCHECK_EQ(num_capturers_running_, 0);
93   num_capturers_running_ = 2;
94   capture_start_time_ = latest_frame_time_ = env_.now() + seconds(1);
95   audio_capturer_.emplace(&env_, path_, audio_encoder_.num_channels(),
96                           audio_encoder_.sample_rate(), capture_start_time_,
97                           this);
98   video_capturer_.emplace(&env_, path_, capture_start_time_, this);
99 
100   next_task_.ScheduleFromNow([this] { ControlForNetworkCongestion(); },
101                              kCongestionCheckInterval);
102   console_update_task_.Schedule([this] { UpdateStatusOnConsole(); },
103                                 capture_start_time_);
104 }
105 
OnAudioData(const float * interleaved_samples,int num_samples,Clock::time_point capture_time)106 void LoopingFileSender::OnAudioData(const float* interleaved_samples,
107                                     int num_samples,
108                                     Clock::time_point capture_time) {
109   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
110   latest_frame_time_ = std::max(capture_time, latest_frame_time_);
111   audio_encoder_.EncodeAndSend(interleaved_samples, num_samples, capture_time);
112 }
113 
OnVideoFrame(const AVFrame & av_frame,Clock::time_point capture_time)114 void LoopingFileSender::OnVideoFrame(const AVFrame& av_frame,
115                                      Clock::time_point capture_time) {
116   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
117   latest_frame_time_ = std::max(capture_time, latest_frame_time_);
118   StreamingVp8Encoder::VideoFrame frame{};
119   frame.width = av_frame.width - av_frame.crop_left - av_frame.crop_right;
120   frame.height = av_frame.height - av_frame.crop_top - av_frame.crop_bottom;
121   frame.yuv_planes[0] = av_frame.data[0] + av_frame.crop_left +
122                         av_frame.linesize[0] * av_frame.crop_top;
123   frame.yuv_planes[1] = av_frame.data[1] + av_frame.crop_left / 2 +
124                         av_frame.linesize[1] * av_frame.crop_top / 2;
125   frame.yuv_planes[2] = av_frame.data[2] + av_frame.crop_left / 2 +
126                         av_frame.linesize[2] * av_frame.crop_top / 2;
127   for (int i = 0; i < 3; ++i) {
128     frame.yuv_strides[i] = av_frame.linesize[i];
129   }
130   // TODO(miu): Add performance metrics visual overlay (based on Stats
131   // callback).
132   video_encoder_.EncodeAndSend(frame, capture_time, {});
133 }
134 
UpdateStatusOnConsole()135 void LoopingFileSender::UpdateStatusOnConsole() {
136   const Clock::duration elapsed = latest_frame_time_ - capture_start_time_;
137   const auto seconds_part = to_seconds(elapsed);
138   const auto millis_part = to_microseconds(elapsed - seconds_part);
139   // The control codes here attempt to erase the current line the cursor is
140   // on, and then print out the updated status text. If the terminal does not
141   // support simple ANSI escape codes, the following will still work, but
142   // there might sometimes be old status lines not getting erased (i.e., just
143   // partially overwritten).
144   fprintf(stdout,
145           "\r\x1b[2K\rLoopingFileSender: At %01" PRId64
146           ".%03ds in file (est. network bandwidth: %d kbps). \n",
147           static_cast<int64_t>(seconds_part.count()),
148           static_cast<int>(millis_part.count()), bandwidth_estimate_ / 1024);
149   fflush(stdout);
150 
151   console_update_task_.ScheduleFromNow([this] { UpdateStatusOnConsole(); },
152                                        kConsoleUpdateInterval);
153 }
154 
OnEndOfFile(SimulatedCapturer * capturer)155 void LoopingFileSender::OnEndOfFile(SimulatedCapturer* capturer) {
156   OSP_LOG_INFO << "The " << ToTrackName(capturer)
157                << " capturer has reached the end of the media stream.";
158   --num_capturers_running_;
159   if (num_capturers_running_ == 0) {
160     console_update_task_.Cancel();
161     next_task_.Schedule([this] { SendFileAgain(); }, Alarm::kImmediately);
162   }
163 }
164 
OnError(SimulatedCapturer * capturer,std::string message)165 void LoopingFileSender::OnError(SimulatedCapturer* capturer,
166                                 std::string message) {
167   OSP_LOG_ERROR << "The " << ToTrackName(capturer)
168                 << " has failed: " << message;
169   --num_capturers_running_;
170   // If both fail, the application just pauses. This accounts for things like
171   // "file not found" errors. However, if only one track fails, then keep
172   // going.
173 }
174 
ToTrackName(SimulatedCapturer * capturer) const175 const char* LoopingFileSender::ToTrackName(SimulatedCapturer* capturer) const {
176   const char* which;
177   if (capturer == &*audio_capturer_) {
178     which = "audio";
179   } else if (capturer == &*video_capturer_) {
180     which = "video";
181   } else {
182     OSP_NOTREACHED();
183     which = "";
184   }
185   return which;
186 }
187 
188 }  // namespace cast
189 }  // namespace openscreen
190