OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/common_types.h" | |
12 | |
13 #include <algorithm> // std::max | |
14 | |
15 #include "webrtc/base/checks.h" | |
16 #include "webrtc/base/logging.h" | |
17 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" | |
18 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" | |
19 #include "webrtc/modules/video_coding/main/source/encoded_frame.h" | |
20 #include "webrtc/modules/video_coding/main/source/video_coding_impl.h" | |
21 #include "webrtc/modules/video_coding/utility/include/quality_scaler.h" | |
22 #include "webrtc/system_wrappers/include/clock.h" | |
23 | |
24 namespace webrtc { | |
25 namespace vcm { | |
26 | |
27 VideoSender::VideoSender(Clock* clock, | |
28 EncodedImageCallback* post_encode_callback, | |
29 VideoEncoderRateObserver* encoder_rate_observer, | |
30 VCMQMSettingsCallback* qm_settings_callback) | |
31 : clock_(clock), | |
32 process_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), | |
33 _encoder(nullptr), | |
34 _encodedFrameCallback(post_encode_callback), | |
35 _nextFrameTypes(1, kVideoFrameDelta), | |
36 _mediaOpt(clock_), | |
37 _sendStatsCallback(nullptr), | |
38 _codecDataBase(encoder_rate_observer, &_encodedFrameCallback), | |
39 frame_dropper_enabled_(true), | |
40 _sendStatsTimer(1000, clock_), | |
41 current_codec_(), | |
42 qm_settings_callback_(qm_settings_callback), | |
43 protection_callback_(nullptr), | |
44 encoder_params_({0, 0, 0, 0}) { | |
45 // Allow VideoSender to be created on one thread but used on another, post | |
46 // construction. This is currently how this class is being used by at least | |
47 // one external project (diffractor). | |
48 _mediaOpt.EnableQM(qm_settings_callback_ != nullptr); | |
49 _mediaOpt.Reset(); | |
50 main_thread_.DetachFromThread(); | |
51 } | |
52 | |
53 VideoSender::~VideoSender() {} | |
54 | |
55 int32_t VideoSender::Process() { | |
56 int32_t returnValue = VCM_OK; | |
57 | |
58 if (_sendStatsTimer.TimeUntilProcess() == 0) { | |
59 _sendStatsTimer.Processed(); | |
60 CriticalSectionScoped cs(process_crit_sect_.get()); | |
61 if (_sendStatsCallback != nullptr) { | |
62 uint32_t bitRate = _mediaOpt.SentBitRate(); | |
63 uint32_t frameRate = _mediaOpt.SentFrameRate(); | |
64 _sendStatsCallback->SendStatistics(bitRate, frameRate); | |
65 } | |
66 } | |
67 | |
68 { | |
69 rtc::CritScope cs(¶ms_lock_); | |
70 // Force an encoder parameters update, so that incoming frame rate is | |
71 // updated even if bandwidth hasn't changed. | |
72 encoder_params_.input_frame_rate = _mediaOpt.InputFrameRate(); | |
73 } | |
74 | |
75 return returnValue; | |
76 } | |
77 | |
78 int64_t VideoSender::TimeUntilNextProcess() { | |
79 return _sendStatsTimer.TimeUntilProcess(); | |
80 } | |
81 | |
82 // Register the send codec to be used. | |
83 int32_t VideoSender::RegisterSendCodec(const VideoCodec* sendCodec, | |
84 uint32_t numberOfCores, | |
85 uint32_t maxPayloadSize) { | |
86 RTC_DCHECK(main_thread_.CalledOnValidThread()); | |
87 rtc::CritScope lock(&send_crit_); | |
88 if (sendCodec == nullptr) { | |
89 return VCM_PARAMETER_ERROR; | |
90 } | |
91 | |
92 bool ret = | |
93 _codecDataBase.SetSendCodec(sendCodec, numberOfCores, maxPayloadSize); | |
94 | |
95 // Update encoder regardless of result to make sure that we're not holding on | |
96 // to a deleted instance. | |
97 _encoder = _codecDataBase.GetEncoder(); | |
98 // Cache the current codec here so they can be fetched from this thread | |
99 // without requiring the _sendCritSect lock. | |
100 current_codec_ = *sendCodec; | |
101 | |
102 if (!ret) { | |
103 LOG(LS_ERROR) << "Failed to initialize set encoder with payload name '" | |
104 << sendCodec->plName << "'."; | |
105 return VCM_CODEC_ERROR; | |
106 } | |
107 | |
108 int numLayers; | |
109 if (sendCodec->codecType == kVideoCodecVP8) { | |
110 numLayers = sendCodec->codecSpecific.VP8.numberOfTemporalLayers; | |
111 } else if (sendCodec->codecType == kVideoCodecVP9) { | |
112 numLayers = sendCodec->codecSpecific.VP9.numberOfTemporalLayers; | |
113 } else { | |
114 numLayers = 1; | |
115 } | |
116 | |
117 // If we have screensharing and we have layers, we disable frame dropper. | |
118 bool disable_frame_dropper = | |
119 numLayers > 1 && sendCodec->mode == kScreensharing; | |
120 if (disable_frame_dropper) { | |
121 _mediaOpt.EnableFrameDropper(false); | |
122 } else if (frame_dropper_enabled_) { | |
123 _mediaOpt.EnableFrameDropper(true); | |
124 } | |
125 _nextFrameTypes.clear(); | |
126 _nextFrameTypes.resize(VCM_MAX(sendCodec->numberOfSimulcastStreams, 1), | |
127 kVideoFrameDelta); | |
128 | |
129 _mediaOpt.SetEncodingData(sendCodec->codecType, | |
130 sendCodec->maxBitrate * 1000, | |
131 sendCodec->startBitrate * 1000, | |
132 sendCodec->width, | |
133 sendCodec->height, | |
134 sendCodec->maxFramerate, | |
135 numLayers, | |
136 maxPayloadSize); | |
137 return VCM_OK; | |
138 } | |
139 | |
140 const VideoCodec& VideoSender::GetSendCodec() const { | |
141 RTC_DCHECK(main_thread_.CalledOnValidThread()); | |
142 return current_codec_; | |
143 } | |
144 | |
145 int32_t VideoSender::SendCodecBlocking(VideoCodec* currentSendCodec) const { | |
146 rtc::CritScope lock(&send_crit_); | |
147 if (currentSendCodec == nullptr) { | |
148 return VCM_PARAMETER_ERROR; | |
149 } | |
150 return _codecDataBase.SendCodec(currentSendCodec) ? 0 : -1; | |
151 } | |
152 | |
153 VideoCodecType VideoSender::SendCodecBlocking() const { | |
154 rtc::CritScope lock(&send_crit_); | |
155 return _codecDataBase.SendCodec(); | |
156 } | |
157 | |
158 // Register an external decoder object. | |
159 // This can not be used together with external decoder callbacks. | |
160 int32_t VideoSender::RegisterExternalEncoder(VideoEncoder* externalEncoder, | |
161 uint8_t payloadType, | |
162 bool internalSource /*= false*/) { | |
163 RTC_DCHECK(main_thread_.CalledOnValidThread()); | |
164 | |
165 rtc::CritScope lock(&send_crit_); | |
166 | |
167 if (externalEncoder == nullptr) { | |
168 bool wasSendCodec = false; | |
169 const bool ret = | |
170 _codecDataBase.DeregisterExternalEncoder(payloadType, &wasSendCodec); | |
171 if (wasSendCodec) { | |
172 // Make sure the VCM doesn't use the de-registered codec | |
173 _encoder = nullptr; | |
174 } | |
175 return ret ? 0 : -1; | |
176 } | |
177 _codecDataBase.RegisterExternalEncoder( | |
178 externalEncoder, payloadType, internalSource); | |
179 return 0; | |
180 } | |
181 | |
182 // Get encode bitrate | |
183 int VideoSender::Bitrate(unsigned int* bitrate) const { | |
184 RTC_DCHECK(main_thread_.CalledOnValidThread()); | |
185 // Since we're running on the thread that's the only thread known to modify | |
186 // the value of _encoder, we don't need to grab the lock here. | |
187 | |
188 if (!_encoder) | |
189 return VCM_UNINITIALIZED; | |
190 *bitrate = _encoder->GetEncoderParameters().target_bitrate; | |
191 return 0; | |
192 } | |
193 | |
194 // Get encode frame rate | |
195 int VideoSender::FrameRate(unsigned int* framerate) const { | |
196 RTC_DCHECK(main_thread_.CalledOnValidThread()); | |
197 // Since we're running on the thread that's the only thread known to modify | |
198 // the value of _encoder, we don't need to grab the lock here. | |
199 | |
200 if (!_encoder) | |
201 return VCM_UNINITIALIZED; | |
202 | |
203 *framerate = _encoder->GetEncoderParameters().input_frame_rate; | |
204 return 0; | |
205 } | |
206 | |
207 int32_t VideoSender::SetChannelParameters(uint32_t target_bitrate, | |
208 uint8_t lossRate, | |
209 int64_t rtt) { | |
210 uint32_t target_rate = | |
211 _mediaOpt.SetTargetRates(target_bitrate, lossRate, rtt, | |
212 protection_callback_, qm_settings_callback_); | |
213 | |
214 uint32_t input_frame_rate = _mediaOpt.InputFrameRate(); | |
215 | |
216 rtc::CritScope cs(¶ms_lock_); | |
217 encoder_params_ = {target_rate, lossRate, rtt, input_frame_rate}; | |
218 | |
219 return VCM_OK; | |
220 } | |
221 | |
222 void VideoSender::SetEncoderParameters(EncoderParameters params) { | |
223 if (params.target_bitrate == 0) | |
224 return; | |
225 | |
226 if (params.input_frame_rate == 0) { | |
227 // No frame rate estimate available, use default. | |
228 params.input_frame_rate = current_codec_.maxFramerate; | |
229 } | |
230 if (_encoder != nullptr) | |
231 _encoder->SetEncoderParameters(params); | |
232 } | |
233 | |
234 int32_t VideoSender::RegisterTransportCallback( | |
235 VCMPacketizationCallback* transport) { | |
236 rtc::CritScope lock(&send_crit_); | |
237 _encodedFrameCallback.SetMediaOpt(&_mediaOpt); | |
238 _encodedFrameCallback.SetTransportCallback(transport); | |
239 return VCM_OK; | |
240 } | |
241 | |
242 // Register video output information callback which will be called to deliver | |
243 // information about the video stream produced by the encoder, for instance the | |
244 // average frame rate and bit rate. | |
245 int32_t VideoSender::RegisterSendStatisticsCallback( | |
246 VCMSendStatisticsCallback* sendStats) { | |
247 CriticalSectionScoped cs(process_crit_sect_.get()); | |
248 _sendStatsCallback = sendStats; | |
249 return VCM_OK; | |
250 } | |
251 | |
252 // Register a video protection callback which will be called to deliver the | |
253 // requested FEC rate and NACK status (on/off). | |
254 // Note: this callback is assumed to only be registered once and before it is | |
255 // used in this class. | |
256 int32_t VideoSender::RegisterProtectionCallback( | |
257 VCMProtectionCallback* protection_callback) { | |
258 RTC_DCHECK(protection_callback == nullptr || protection_callback_ == nullptr); | |
259 protection_callback_ = protection_callback; | |
260 return VCM_OK; | |
261 } | |
262 | |
263 // Enable or disable a video protection method. | |
264 void VideoSender::SetVideoProtection(VCMVideoProtection videoProtection) { | |
265 rtc::CritScope lock(&send_crit_); | |
266 switch (videoProtection) { | |
267 case kProtectionNone: | |
268 _mediaOpt.SetProtectionMethod(media_optimization::kNone); | |
269 break; | |
270 case kProtectionNack: | |
271 _mediaOpt.SetProtectionMethod(media_optimization::kNack); | |
272 break; | |
273 case kProtectionNackFEC: | |
274 _mediaOpt.SetProtectionMethod(media_optimization::kNackFec); | |
275 break; | |
276 case kProtectionFEC: | |
277 _mediaOpt.SetProtectionMethod(media_optimization::kFec); | |
278 break; | |
279 } | |
280 } | |
281 // Add one raw video frame to the encoder, blocking. | |
282 int32_t VideoSender::AddVideoFrame(const VideoFrame& videoFrame, | |
283 const VideoContentMetrics* contentMetrics, | |
284 const CodecSpecificInfo* codecSpecificInfo) { | |
285 EncoderParameters encoder_params; | |
286 { | |
287 rtc::CritScope lock(¶ms_lock_); | |
288 encoder_params = encoder_params_; | |
289 } | |
290 rtc::CritScope lock(&send_crit_); | |
291 if (_encoder == nullptr) | |
292 return VCM_UNINITIALIZED; | |
293 SetEncoderParameters(encoder_params); | |
294 // TODO(holmer): Add support for dropping frames per stream. Currently we | |
295 // only have one frame dropper for all streams. | |
296 if (_nextFrameTypes[0] == kEmptyFrame) { | |
297 return VCM_OK; | |
298 } | |
299 if (_mediaOpt.DropFrame()) { | |
300 _encoder->OnDroppedFrame(); | |
301 return VCM_OK; | |
302 } | |
303 _mediaOpt.UpdateContentData(contentMetrics); | |
304 // TODO(pbos): Make sure setting send codec is synchronized with video | |
305 // processing so frame size always matches. | |
306 if (!_codecDataBase.MatchesCurrentResolution(videoFrame.width(), | |
307 videoFrame.height())) { | |
308 LOG(LS_ERROR) << "Incoming frame doesn't match set resolution. Dropping."; | |
309 return VCM_PARAMETER_ERROR; | |
310 } | |
311 VideoFrame converted_frame = videoFrame; | |
312 if (converted_frame.native_handle() && !_encoder->SupportsNativeHandle()) { | |
313 // This module only supports software encoding. | |
314 // TODO(pbos): Offload conversion from the encoder thread. | |
315 converted_frame = converted_frame.ConvertNativeToI420Frame(); | |
316 RTC_CHECK(!converted_frame.IsZeroSize()) | |
317 << "Frame conversion failed, won't be able to encode frame."; | |
318 } | |
319 int32_t ret = | |
320 _encoder->Encode(converted_frame, codecSpecificInfo, _nextFrameTypes); | |
321 if (ret < 0) { | |
322 LOG(LS_ERROR) << "Failed to encode frame. Error code: " << ret; | |
323 return ret; | |
324 } | |
325 for (size_t i = 0; i < _nextFrameTypes.size(); ++i) { | |
326 _nextFrameTypes[i] = kVideoFrameDelta; // Default frame type. | |
327 } | |
328 if (qm_settings_callback_) | |
329 qm_settings_callback_->SetTargetFramerate(_encoder->GetTargetFramerate()); | |
330 return VCM_OK; | |
331 } | |
332 | |
333 int32_t VideoSender::IntraFrameRequest(int stream_index) { | |
334 rtc::CritScope lock(&send_crit_); | |
335 if (stream_index < 0 || | |
336 static_cast<unsigned int>(stream_index) >= _nextFrameTypes.size()) { | |
337 return -1; | |
338 } | |
339 _nextFrameTypes[stream_index] = kVideoFrameKey; | |
340 if (_encoder != nullptr && _encoder->InternalSource()) { | |
341 // Try to request the frame if we have an external encoder with | |
342 // internal source since AddVideoFrame never will be called. | |
343 if (_encoder->RequestFrame(_nextFrameTypes) == WEBRTC_VIDEO_CODEC_OK) { | |
344 _nextFrameTypes[stream_index] = kVideoFrameDelta; | |
345 } | |
346 } | |
347 return VCM_OK; | |
348 } | |
349 | |
350 int32_t VideoSender::EnableFrameDropper(bool enable) { | |
351 rtc::CritScope lock(&send_crit_); | |
352 frame_dropper_enabled_ = enable; | |
353 _mediaOpt.EnableFrameDropper(enable); | |
354 return VCM_OK; | |
355 } | |
356 | |
357 void VideoSender::SuspendBelowMinBitrate() { | |
358 RTC_DCHECK(main_thread_.CalledOnValidThread()); | |
359 int threshold_bps; | |
360 if (current_codec_.numberOfSimulcastStreams == 0) { | |
361 threshold_bps = current_codec_.minBitrate * 1000; | |
362 } else { | |
363 threshold_bps = current_codec_.simulcastStream[0].minBitrate * 1000; | |
364 } | |
365 // Set the hysteresis window to be at 10% of the threshold, but at least | |
366 // 10 kbps. | |
367 int window_bps = std::max(threshold_bps / 10, 10000); | |
368 _mediaOpt.SuspendBelowMinBitrate(threshold_bps, window_bps); | |
369 } | |
370 | |
371 bool VideoSender::VideoSuspended() const { | |
372 return _mediaOpt.IsVideoSuspended(); | |
373 } | |
374 } // namespace vcm | |
375 } // namespace webrtc | |
OLD | NEW |