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.
5 #include "media/cast/sender/video_sender.h"
10 #include "base/bind.h"
11 #include "base/debug/trace_event.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "media/cast/cast_defines.h"
15 #include "media/cast/net/cast_transport_config.h"
16 #include "media/cast/sender/external_video_encoder.h"
17 #include "media/cast/sender/video_encoder_impl.h"
24 // The following two constants are used to adjust the target
25 // playout delay (when allowed). They were calculated using
26 // a combination of cast_benchmark runs and manual testing.
28 // This is how many round trips we think we need on the network.
29 const int kRoundTripsNeeded
= 4;
30 // This is an estimate of all the the constant time needed independent of
31 // network quality (e.g., additional time that accounts for encode and decode
33 const int kConstantTimeMs
= 75;
37 // Note, we use a fixed bitrate value when external video encoder is used.
38 // Some hardware encoder shows bad behavior if we set the bitrate too
39 // frequently, e.g. quality drop, not abiding by target bitrate, etc.
40 // See details: crbug.com/392086.
41 VideoSender::VideoSender(
42 scoped_refptr
<CastEnvironment
> cast_environment
,
43 const VideoSenderConfig
& video_config
,
44 const CastInitializationCallback
& initialization_cb
,
45 const CreateVideoEncodeAcceleratorCallback
& create_vea_cb
,
46 const CreateVideoEncodeMemoryCallback
& create_video_encode_mem_cb
,
47 CastTransportSender
* const transport_sender
,
48 const PlayoutDelayChangeCB
& playout_delay_change_cb
)
53 base::TimeDelta::FromMilliseconds(video_config
.rtcp_interval
),
56 video_config
.max_frame_rate
,
57 video_config
.min_playout_delay
,
58 video_config
.max_playout_delay
,
59 video_config
.use_external_encoder
?
60 NewFixedCongestionControl(
61 (video_config
.min_bitrate
+ video_config
.max_bitrate
) / 2) :
62 NewAdaptiveCongestionControl(cast_environment
->Clock(),
63 video_config
.max_bitrate
,
64 video_config
.min_bitrate
,
65 video_config
.max_frame_rate
)),
66 frames_in_encoder_(0),
68 playout_delay_change_cb_(playout_delay_change_cb
),
70 cast_initialization_status_
= STATUS_VIDEO_UNINITIALIZED
;
72 if (video_config
.use_external_encoder
) {
73 video_encoder_
.reset(new ExternalVideoEncoder(
76 base::Bind(&VideoSender::OnEncoderInitialized
,
77 weak_factory_
.GetWeakPtr(), initialization_cb
),
79 create_video_encode_mem_cb
));
81 // Software encoder is initialized immediately.
82 video_encoder_
.reset(new VideoEncoderImpl(cast_environment
, video_config
));
83 cast_initialization_status_
= STATUS_VIDEO_INITIALIZED
;
86 if (cast_initialization_status_
== STATUS_VIDEO_INITIALIZED
) {
87 cast_environment
->PostTask(
88 CastEnvironment::MAIN
,
90 base::Bind(initialization_cb
, cast_initialization_status_
));
93 media::cast::CastTransportRtpConfig transport_config
;
94 transport_config
.ssrc
= video_config
.ssrc
;
95 transport_config
.feedback_ssrc
= video_config
.incoming_feedback_ssrc
;
96 transport_config
.rtp_payload_type
= video_config
.rtp_payload_type
;
97 transport_config
.aes_key
= video_config
.aes_key
;
98 transport_config
.aes_iv_mask
= video_config
.aes_iv_mask
;
100 transport_sender
->InitializeVideo(
102 base::Bind(&VideoSender::OnReceivedCastFeedback
,
103 weak_factory_
.GetWeakPtr()),
104 base::Bind(&VideoSender::OnMeasuredRoundTripTime
,
105 weak_factory_
.GetWeakPtr()));
108 VideoSender::~VideoSender() {
111 void VideoSender::InsertRawVideoFrame(
112 const scoped_refptr
<media::VideoFrame
>& video_frame
,
113 const base::TimeTicks
& reference_time
) {
114 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
115 if (cast_initialization_status_
!= STATUS_VIDEO_INITIALIZED
) {
119 DCHECK(video_encoder_
.get()) << "Invalid state";
121 const RtpTimestamp rtp_timestamp
=
122 TimeDeltaToRtpDelta(video_frame
->timestamp(), kVideoFrequency
);
123 const base::TimeTicks insertion_time
= cast_environment_
->Clock()->NowTicks();
124 // TODO(miu): Plumb in capture timestamps. For now, make it look like capture
125 // took zero time by setting the BEGIN and END event to the same timestamp.
126 cast_environment_
->Logging()->InsertFrameEvent(
127 insertion_time
, FRAME_CAPTURE_BEGIN
, VIDEO_EVENT
, rtp_timestamp
,
129 cast_environment_
->Logging()->InsertFrameEvent(
130 insertion_time
, FRAME_CAPTURE_END
, VIDEO_EVENT
, rtp_timestamp
,
133 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
134 TRACE_EVENT_INSTANT2(
135 "cast_perf_test", "InsertRawVideoFrame",
136 TRACE_EVENT_SCOPE_THREAD
,
137 "timestamp", reference_time
.ToInternalValue(),
138 "rtp_timestamp", rtp_timestamp
);
140 // Drop the frame if either its RTP or reference timestamp is not an increase
141 // over the last frame's. This protects: 1) the duration calculations that
142 // assume timestamps are monotonically non-decreasing, and 2) assumptions made
143 // deeper in the implementation where each frame's RTP timestamp needs to be
145 if (!last_enqueued_frame_reference_time_
.is_null() &&
146 (!IsNewerRtpTimestamp(rtp_timestamp
,
147 last_enqueued_frame_rtp_timestamp_
) ||
148 reference_time
<= last_enqueued_frame_reference_time_
)) {
149 VLOG(1) << "Dropping video frame: RTP or reference time did not increase.";
153 // Two video frames are needed to compute the exact media duration added by
154 // the next frame. If there are no frames in the encoder, compute a guess
155 // based on the configured |max_frame_rate_|. Any error introduced by this
156 // guess will be eliminated when |duration_in_encoder_| is updated in
157 // OnEncodedVideoFrame().
158 const base::TimeDelta duration_added_by_next_frame
= frames_in_encoder_
> 0 ?
159 reference_time
- last_enqueued_frame_reference_time_
:
160 base::TimeDelta::FromSecondsD(1.0 / max_frame_rate_
);
162 if (ShouldDropNextFrame(duration_added_by_next_frame
)) {
163 base::TimeDelta new_target_delay
= std::min(
164 current_round_trip_time_
* kRoundTripsNeeded
+
165 base::TimeDelta::FromMilliseconds(kConstantTimeMs
),
167 if (new_target_delay
> target_playout_delay_
) {
168 VLOG(1) << "New target delay: " << new_target_delay
.InMilliseconds();
169 playout_delay_change_cb_
.Run(new_target_delay
);
174 uint32 bitrate
= congestion_control_
->GetBitrate(
175 reference_time
+ target_playout_delay_
, target_playout_delay_
);
176 if (bitrate
!= last_bitrate_
) {
177 video_encoder_
->SetBitRate(bitrate
);
178 last_bitrate_
= bitrate
;
181 if (video_encoder_
->EncodeVideoFrame(
184 base::Bind(&VideoSender::OnEncodedVideoFrame
,
185 weak_factory_
.GetWeakPtr(),
187 frames_in_encoder_
++;
188 duration_in_encoder_
+= duration_added_by_next_frame
;
189 last_enqueued_frame_rtp_timestamp_
= rtp_timestamp
;
190 last_enqueued_frame_reference_time_
= reference_time
;
192 VLOG(1) << "Encoder rejected a frame. Skipping...";
196 int VideoSender::GetNumberOfFramesInEncoder() const {
197 return frames_in_encoder_
;
200 base::TimeDelta
VideoSender::GetInFlightMediaDuration() const {
201 if (GetUnacknowledgedFrameCount() > 0) {
202 const uint32 oldest_unacked_frame_id
= latest_acked_frame_id_
+ 1;
203 return last_enqueued_frame_reference_time_
-
204 GetRecordedReferenceTime(oldest_unacked_frame_id
);
206 return duration_in_encoder_
;
210 void VideoSender::OnAck(uint32 frame_id
) {
211 video_encoder_
->LatestFrameIdToReference(frame_id
);
214 void VideoSender::OnEncoderInitialized(
215 const CastInitializationCallback
& initialization_cb
,
216 CastInitializationStatus status
) {
217 cast_initialization_status_
= status
;
218 initialization_cb
.Run(status
);
221 void VideoSender::OnEncodedVideoFrame(
223 scoped_ptr
<EncodedFrame
> encoded_frame
) {
224 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
226 frames_in_encoder_
--;
227 DCHECK_GE(frames_in_encoder_
, 0);
229 duration_in_encoder_
=
230 last_enqueued_frame_reference_time_
- encoded_frame
->reference_time
;
232 SendEncodedFrame(encoder_bitrate
, encoded_frame
.Pass());