1 // Copyright 2014 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 "media/cast/sender/video_sender.h"
6 
7 #include <stdint.h>
8 #include <algorithm>
9 #include <cmath>
10 #include <cstring>
11 #include <utility>
12 
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/trace_event/trace_event.h"
16 #include "media/cast/net/cast_transport_config.h"
17 #include "media/cast/sender/performance_metrics_overlay.h"
18 #include "media/cast/sender/video_encoder.h"
19 
20 namespace media {
21 namespace cast {
22 
23 namespace {
24 
25 // The following two constants are used to adjust the target
26 // playout delay (when allowed). They were calculated using
27 // a combination of cast_benchmark runs and manual testing.
28 //
29 // This is how many round trips we think we need on the network.
30 const int kRoundTripsNeeded = 4;
31 
32 // This is an estimate of all the the constant time needed independent of
33 // network quality (e.g., additional time that accounts for encode and decode
34 // time).
35 const int kConstantTimeMs = 75;
36 
37 // The target maximum utilization of the encoder and network resources.  This is
38 // used to attenuate the actual measured utilization values in order to provide
39 // "breathing room" (i.e., to ensure there will be sufficient CPU and bandwidth
40 // available to handle the occasional more-complex frames).
41 const int kTargetUtilizationPercentage = 75;
42 
43 // This is the minimum duration in milliseconds that the sender sends key frame
44 // request to the encoder on receiving Pli messages. This is used to prevent
45 // sending multiple requests while the sender is waiting for an encoded key
46 // frame or receiving multiple Pli messages in a short period.
47 const int64_t kMinKeyFrameRequestOnPliIntervalMs = 500;
48 
49 // Extract capture begin/end timestamps from |video_frame|'s metadata and log
50 // it.
LogVideoCaptureTimestamps(CastEnvironment * cast_environment,const media::VideoFrame & video_frame,RtpTimeTicks rtp_timestamp)51 void LogVideoCaptureTimestamps(CastEnvironment* cast_environment,
52                                const media::VideoFrame& video_frame,
53                                RtpTimeTicks rtp_timestamp) {
54   std::unique_ptr<FrameEvent> capture_begin_event(new FrameEvent());
55   capture_begin_event->type = FRAME_CAPTURE_BEGIN;
56   capture_begin_event->media_type = VIDEO_EVENT;
57   capture_begin_event->rtp_timestamp = rtp_timestamp;
58 
59   std::unique_ptr<FrameEvent> capture_end_event(new FrameEvent());
60   capture_end_event->type = FRAME_CAPTURE_END;
61   capture_end_event->media_type = VIDEO_EVENT;
62   capture_end_event->rtp_timestamp = rtp_timestamp;
63   capture_end_event->width = video_frame.visible_rect().width();
64   capture_end_event->height = video_frame.visible_rect().height();
65 
66   if (video_frame.metadata()->capture_begin_time.has_value() &&
67       video_frame.metadata()->capture_end_time.has_value()) {
68     capture_begin_event->timestamp =
69         *video_frame.metadata()->capture_begin_time;
70     capture_end_event->timestamp = *video_frame.metadata()->capture_end_time;
71   } else {
72     // The frame capture timestamps were not provided by the video capture
73     // source.  Simply log the events as happening right now.
74     capture_begin_event->timestamp = capture_end_event->timestamp =
75         cast_environment->Clock()->NowTicks();
76   }
77 
78   cast_environment->logger()->DispatchFrameEvent(
79       std::move(capture_begin_event));
80   cast_environment->logger()->DispatchFrameEvent(std::move(capture_end_event));
81 }
82 
83 }  // namespace
84 
85 // Note, we use a fixed bitrate value when external video encoder is used.
86 // Some hardware encoder shows bad behavior if we set the bitrate too
87 // frequently, e.g. quality drop, not abiding by target bitrate, etc.
88 // See details: crbug.com/392086.
VideoSender(scoped_refptr<CastEnvironment> cast_environment,const FrameSenderConfig & video_config,StatusChangeCallback status_change_cb,const CreateVideoEncodeAcceleratorCallback & create_vea_cb,const CreateVideoEncodeMemoryCallback & create_video_encode_mem_cb,CastTransport * const transport_sender,PlayoutDelayChangeCB playout_delay_change_cb,media::VideoCaptureFeedbackCB feedback_callback)89 VideoSender::VideoSender(
90     scoped_refptr<CastEnvironment> cast_environment,
91     const FrameSenderConfig& video_config,
92     StatusChangeCallback status_change_cb,
93     const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
94     const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
95     CastTransport* const transport_sender,
96     PlayoutDelayChangeCB playout_delay_change_cb,
97     media::VideoCaptureFeedbackCB feedback_callback)
98     : FrameSender(
99           cast_environment,
100           transport_sender,
101           video_config,
102           video_config.use_external_encoder
103               ? NewFixedCongestionControl(
104                     (video_config.min_bitrate + video_config.max_bitrate) / 2)
105               : NewAdaptiveCongestionControl(cast_environment->Clock(),
106                                              video_config.max_bitrate,
107                                              video_config.min_bitrate,
108                                              video_config.max_frame_rate)),
109       frames_in_encoder_(0),
110       last_bitrate_(0),
111       playout_delay_change_cb_(std::move(playout_delay_change_cb)),
112       feedback_cb_(feedback_callback),
113       low_latency_mode_(false),
114       last_reported_encoder_utilization_(-1.0),
115       last_reported_lossy_utilization_(-1.0) {
116   video_encoder_ = VideoEncoder::Create(
117       cast_environment_,
118       video_config,
119       status_change_cb,
120       create_vea_cb,
121       create_video_encode_mem_cb);
122   if (!video_encoder_) {
123     cast_environment_->PostTask(
124         CastEnvironment::MAIN, FROM_HERE,
125         base::BindOnce(std::move(status_change_cb), STATUS_UNSUPPORTED_CODEC));
126   }
127 }
128 
129 VideoSender::~VideoSender() = default;
130 
InsertRawVideoFrame(scoped_refptr<media::VideoFrame> video_frame,const base::TimeTicks & reference_time)131 void VideoSender::InsertRawVideoFrame(
132     scoped_refptr<media::VideoFrame> video_frame,
133     const base::TimeTicks& reference_time) {
134   DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
135 
136   if (!video_encoder_) {
137     NOTREACHED();
138     return;
139   }
140 
141   const RtpTimeTicks rtp_timestamp =
142       RtpTimeTicks::FromTimeDelta(video_frame->timestamp(), kVideoFrequency);
143   LogVideoCaptureTimestamps(cast_environment_.get(), *video_frame,
144                             rtp_timestamp);
145 
146   // Used by chrome/browser/media/cast_mirroring_performance_browsertest.cc
147   TRACE_EVENT_INSTANT2("cast_perf_test", "InsertRawVideoFrame",
148                        TRACE_EVENT_SCOPE_THREAD, "timestamp",
149                        (reference_time - base::TimeTicks()).InMicroseconds(),
150                        "rtp_timestamp", rtp_timestamp.lower_32_bits());
151 
152   {
153     bool new_low_latency_mode = video_frame->metadata()->interactive_content;
154     if (new_low_latency_mode && !low_latency_mode_) {
155       VLOG(1) << "Interactive mode playout time " << min_playout_delay_;
156       playout_delay_change_cb_.Run(min_playout_delay_);
157     }
158     low_latency_mode_ = new_low_latency_mode;
159   }
160 
161   // Drop the frame if either its RTP or reference timestamp is not an increase
162   // over the last frame's.  This protects: 1) the duration calculations that
163   // assume timestamps are monotonically non-decreasing, and 2) assumptions made
164   // deeper in the implementation where each frame's RTP timestamp needs to be
165   // unique.
166   if (!last_enqueued_frame_reference_time_.is_null() &&
167       (rtp_timestamp <= last_enqueued_frame_rtp_timestamp_ ||
168        reference_time <= last_enqueued_frame_reference_time_)) {
169     VLOG(1) << "Dropping video frame: RTP or reference time did not increase.";
170     TRACE_EVENT_INSTANT2("cast.stream", "Video Frame Drop",
171                          TRACE_EVENT_SCOPE_THREAD,
172                          "rtp_timestamp", rtp_timestamp.lower_32_bits(),
173                          "reason", "time did not increase");
174     return;
175   }
176 
177   // Request a key frame when a Pli message was received, and it has been passed
178   // long enough from the last time sending key frame request on receiving a Pli
179   // message.
180   if (picture_lost_at_receiver_) {
181     const int64_t min_attemp_interval_ms =
182         std::max(kMinKeyFrameRequestOnPliIntervalMs,
183                  6 * target_playout_delay_.InMilliseconds());
184     if (last_time_attempted_to_resolve_pli_.is_null() ||
185         ((reference_time - last_time_attempted_to_resolve_pli_)
186              .InMilliseconds() > min_attemp_interval_ms)) {
187       video_encoder_->GenerateKeyFrame();
188       last_time_attempted_to_resolve_pli_ = reference_time;
189     }
190   }
191 
192   // Two video frames are needed to compute the exact media duration added by
193   // the next frame.  If there are no frames in the encoder, compute a guess
194   // based on the configured |max_frame_rate_|.  Any error introduced by this
195   // guess will be eliminated when |duration_in_encoder_| is updated in
196   // OnEncodedVideoFrame().
197   const base::TimeDelta duration_added_by_next_frame = frames_in_encoder_ > 0 ?
198       reference_time - last_enqueued_frame_reference_time_ :
199       base::TimeDelta::FromSecondsD(1.0 / max_frame_rate_);
200 
201   if (ShouldDropNextFrame(duration_added_by_next_frame)) {
202     base::TimeDelta new_target_delay = std::min(
203         current_round_trip_time_ * kRoundTripsNeeded +
204         base::TimeDelta::FromMilliseconds(kConstantTimeMs),
205         max_playout_delay_);
206     // In case of low latency mode, we prefer frame drops over increasing
207     // playout time.
208     if (!low_latency_mode_ && new_target_delay > target_playout_delay_) {
209       // In case we detect user is no longer in a low latency mode and there is
210       // a need to drop a frame, we ensure the playout delay is at-least the
211       // the starting value for playing animated content.
212       // This is intended to minimize freeze when moving from an interactive
213       // session to watching animating content while being limited by end-to-end
214       // delay.
215       VLOG(1) << "Ensure playout time is at least " << animated_playout_delay_;
216       if (new_target_delay < animated_playout_delay_)
217         new_target_delay = animated_playout_delay_;
218       VLOG(1) << "New target delay: " << new_target_delay.InMilliseconds();
219       playout_delay_change_cb_.Run(new_target_delay);
220     }
221 
222     // Some encoder implementations have a frame window for analysis. Since we
223     // are dropping this frame, unless we instruct the encoder to flush all the
224     // frames that have been enqueued for encoding, frames_in_encoder_ and
225     // last_enqueued_frame_reference_time_ will never be updated and we will
226     // drop every subsequent frame for the rest of the session.
227     video_encoder_->EmitFrames();
228 
229     TRACE_EVENT_INSTANT2("cast.stream", "Video Frame Drop",
230                          TRACE_EVENT_SCOPE_THREAD,
231                          "rtp_timestamp", rtp_timestamp.lower_32_bits(),
232                          "reason", "too much in flight");
233     return;
234   }
235 
236   if (video_frame->visible_rect().IsEmpty()) {
237     VLOG(1) << "Rejecting empty video frame.";
238     return;
239   }
240 
241   const int bitrate = congestion_control_->GetBitrate(
242       reference_time + target_playout_delay_, target_playout_delay_);
243   if (bitrate != last_bitrate_) {
244     video_encoder_->SetBitRate(bitrate);
245     last_bitrate_ = bitrate;
246   }
247 
248   TRACE_COUNTER_ID1("cast.stream", "Video Target Bitrate", this, bitrate);
249 
250   const scoped_refptr<VideoFrame> frame_to_encode =
251       MaybeRenderPerformanceMetricsOverlay(
252           GetTargetPlayoutDelay(), low_latency_mode_, bitrate,
253           frames_in_encoder_ + 1, last_reported_encoder_utilization_,
254           last_reported_lossy_utilization_, std::move(video_frame));
255   if (video_encoder_->EncodeVideoFrame(
256           frame_to_encode, reference_time,
257           base::BindOnce(&VideoSender::OnEncodedVideoFrame, AsWeakPtr(),
258                          frame_to_encode, bitrate))) {
259     TRACE_EVENT_ASYNC_BEGIN1("cast.stream", "Video Encode",
260                              frame_to_encode.get(), "rtp_timestamp",
261                              rtp_timestamp.lower_32_bits());
262     frames_in_encoder_++;
263     duration_in_encoder_ += duration_added_by_next_frame;
264     last_enqueued_frame_rtp_timestamp_ = rtp_timestamp;
265     last_enqueued_frame_reference_time_ = reference_time;
266   } else {
267     VLOG(1) << "Encoder rejected a frame.  Skipping...";
268     TRACE_EVENT_INSTANT1("cast.stream", "Video Encode Reject",
269                          TRACE_EVENT_SCOPE_THREAD,
270                          "rtp_timestamp", rtp_timestamp.lower_32_bits());
271   }
272 }
273 
CreateVideoFrameFactory()274 std::unique_ptr<VideoFrameFactory> VideoSender::CreateVideoFrameFactory() {
275   return video_encoder_ ? video_encoder_->CreateVideoFrameFactory() : nullptr;
276 }
277 
AsWeakPtr()278 base::WeakPtr<VideoSender> VideoSender::AsWeakPtr() {
279   return weak_factory_.GetWeakPtr();
280 }
281 
GetNumberOfFramesInEncoder() const282 int VideoSender::GetNumberOfFramesInEncoder() const {
283   return frames_in_encoder_;
284 }
285 
GetInFlightMediaDuration() const286 base::TimeDelta VideoSender::GetInFlightMediaDuration() const {
287   if (GetUnacknowledgedFrameCount() > 0) {
288     const FrameId oldest_unacked_frame_id = latest_acked_frame_id_ + 1;
289     return last_enqueued_frame_reference_time_ -
290         GetRecordedReferenceTime(oldest_unacked_frame_id);
291   } else {
292     return duration_in_encoder_;
293   }
294 }
295 
OnEncodedVideoFrame(scoped_refptr<media::VideoFrame> video_frame,int encoder_bitrate,std::unique_ptr<SenderEncodedFrame> encoded_frame)296 void VideoSender::OnEncodedVideoFrame(
297     scoped_refptr<media::VideoFrame> video_frame,
298     int encoder_bitrate,
299     std::unique_ptr<SenderEncodedFrame> encoded_frame) {
300   DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
301 
302   frames_in_encoder_--;
303   DCHECK_GE(frames_in_encoder_, 0);
304 
305   // Encoding was exited with errors.
306   if (!encoded_frame)
307     return;
308 
309   duration_in_encoder_ =
310       last_enqueued_frame_reference_time_ - encoded_frame->reference_time;
311 
312   last_reported_encoder_utilization_ = encoded_frame->encoder_utilization;
313   last_reported_lossy_utilization_ = encoded_frame->lossy_utilization;
314 
315   TRACE_EVENT_ASYNC_END2("cast.stream", "Video Encode", video_frame.get(),
316                          "encoder_utilization",
317                          last_reported_encoder_utilization_,
318                          "lossy_utilization", last_reported_lossy_utilization_);
319 
320   // Report the resource utilization for processing this frame.  Take the
321   // greater of the two utilization values and attenuate them such that the
322   // target utilization is reported as the maximum sustainable amount.
323   const double attenuated_utilization =
324       std::max(last_reported_encoder_utilization_,
325                last_reported_lossy_utilization_) /
326       (kTargetUtilizationPercentage / 100.0);
327   if (attenuated_utilization >= 0.0) {
328     // Key frames are artificially capped to 1.0 because their actual
329     // utilization is atypical compared to the other frames in the stream, and
330     // this can misguide the producer of the input video frames.
331     VideoFrameFeedback feedback;
332     feedback.resource_utilization =
333         encoded_frame->dependency == EncodedFrame::KEY
334             ? std::min(1.0, attenuated_utilization)
335             : attenuated_utilization;
336     if (feedback_cb_)
337       feedback_cb_.Run(feedback);
338   }
339 
340   SendEncodedFrame(encoder_bitrate, std::move(encoded_frame));
341 }
342 
343 }  // namespace cast
344 }  // namespace media
345