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/common_video/libyuv/include/webrtc_libyuv.h" 16 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" 17 #include "webrtc/modules/video_coding/main/source/encoded_frame.h" 18 #include "webrtc/modules/video_coding/main/source/video_coding_impl.h" 19 #include "webrtc/system_wrappers/interface/clock.h" 20 #include "webrtc/system_wrappers/interface/logging.h" 21 22 namespace webrtc { 23 namespace vcm { 24 25 class DebugRecorder { 26 public: 27 DebugRecorder() 28 : cs_(CriticalSectionWrapper::CreateCriticalSection()), file_(NULL) {} 29 30 ~DebugRecorder() { Stop(); } 31 32 int Start(const char* file_name_utf8) { 33 CriticalSectionScoped cs(cs_.get()); 34 if (file_) 35 fclose(file_); 36 file_ = fopen(file_name_utf8, "wb"); 37 if (!file_) 38 return VCM_GENERAL_ERROR; 39 return VCM_OK; 40 } 41 42 void Stop() { 43 CriticalSectionScoped cs(cs_.get()); 44 if (file_) { 45 fclose(file_); 46 file_ = NULL; 47 } 48 } 49 50 void Add(const I420VideoFrame& frame) { 51 CriticalSectionScoped cs(cs_.get()); 52 if (file_) 53 PrintI420VideoFrame(frame, file_); 54 } 55 56 private: 57 scoped_ptr<CriticalSectionWrapper> cs_; 58 FILE* file_ GUARDED_BY(cs_); 59 }; 60 61 VideoSender::VideoSender(Clock* clock, 62 EncodedImageCallback* post_encode_callback) 63 : clock_(clock), 64 recorder_(new DebugRecorder()), 65 process_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), 66 _sendCritSect(CriticalSectionWrapper::CreateCriticalSection()), 67 _encoder(), 68 _encodedFrameCallback(post_encode_callback), 69 _nextFrameTypes(1, kVideoFrameDelta), 70 _mediaOpt(clock_), 71 _sendStatsCallback(NULL), 72 _codecDataBase(), 73 frame_dropper_enabled_(true), 74 _sendStatsTimer(1000, clock_), 75 qm_settings_callback_(NULL), 76 protection_callback_(NULL) {} 77 78 VideoSender::~VideoSender() { 79 delete _sendCritSect; 80 } 81 82 int32_t VideoSender::Process() { 83 int32_t returnValue = VCM_OK; 84 85 if (_sendStatsTimer.TimeUntilProcess() == 0) { 86 _sendStatsTimer.Processed(); 87 CriticalSectionScoped cs(process_crit_sect_.get()); 88 if (_sendStatsCallback != NULL) { 89 uint32_t bitRate; 90 uint32_t frameRate; 91 { 92 CriticalSectionScoped cs(_sendCritSect); 93 bitRate = _mediaOpt.SentBitRate(); 94 frameRate = _mediaOpt.SentFrameRate(); 95 } 96 _sendStatsCallback->SendStatistics(bitRate, frameRate); 97 } 98 } 99 100 return returnValue; 101 } 102 103 // Reset send side to initial state - all components 104 int32_t VideoSender::InitializeSender() { 105 CriticalSectionScoped cs(_sendCritSect); 106 _codecDataBase.ResetSender(); 107 _encoder = NULL; 108 _encodedFrameCallback.SetTransportCallback(NULL); 109 _mediaOpt.Reset(); // Resetting frame dropper 110 return VCM_OK; 111 } 112 113 int32_t VideoSender::TimeUntilNextProcess() { 114 return _sendStatsTimer.TimeUntilProcess(); 115 } 116 117 // Register the send codec to be used. 118 int32_t VideoSender::RegisterSendCodec(const VideoCodec* sendCodec, 119 uint32_t numberOfCores, 120 uint32_t maxPayloadSize) { 121 CriticalSectionScoped cs(_sendCritSect); 122 if (sendCodec == NULL) { 123 return VCM_PARAMETER_ERROR; 124 } 125 126 bool ret = _codecDataBase.SetSendCodec( 127 sendCodec, numberOfCores, maxPayloadSize, &_encodedFrameCallback); 128 129 // Update encoder regardless of result to make sure that we're not holding on 130 // to a deleted instance. 131 _encoder = _codecDataBase.GetEncoder(); 132 133 if (!ret) { 134 LOG(LS_ERROR) << "Failed to initialize the encoder with payload name " 135 << sendCodec->plName << ". Error code: " << ret; 136 return VCM_CODEC_ERROR; 137 } 138 139 int numLayers = (sendCodec->codecType != kVideoCodecVP8) 140 ? 1 141 : sendCodec->codecSpecific.VP8.numberOfTemporalLayers; 142 // If we have screensharing and we have layers, we disable frame dropper. 143 bool disable_frame_dropper = 144 numLayers > 1 && sendCodec->mode == kScreensharing; 145 if (disable_frame_dropper) { 146 _mediaOpt.EnableFrameDropper(false); 147 } else if (frame_dropper_enabled_) { 148 _mediaOpt.EnableFrameDropper(true); 149 } 150 _nextFrameTypes.clear(); 151 _nextFrameTypes.resize(VCM_MAX(sendCodec->numberOfSimulcastStreams, 1), 152 kVideoFrameDelta); 153 154 _mediaOpt.SetEncodingData(sendCodec->codecType, 155 sendCodec->maxBitrate * 1000, 156 sendCodec->maxFramerate * 1000, 157 sendCodec->startBitrate * 1000, 158 sendCodec->width, 159 sendCodec->height, 160 numLayers, 161 maxPayloadSize); 162 return VCM_OK; 163 } 164 165 // Get current send codec 166 int32_t VideoSender::SendCodec(VideoCodec* currentSendCodec) const { 167 CriticalSectionScoped cs(_sendCritSect); 168 169 if (currentSendCodec == NULL) { 170 return VCM_PARAMETER_ERROR; 171 } 172 return _codecDataBase.SendCodec(currentSendCodec) ? 0 : -1; 173 } 174 175 // Get the current send codec type 176 VideoCodecType VideoSender::SendCodec() const { 177 CriticalSectionScoped cs(_sendCritSect); 178 179 return _codecDataBase.SendCodec(); 180 } 181 182 // Register an external decoder object. 183 // This can not be used together with external decoder callbacks. 184 int32_t VideoSender::RegisterExternalEncoder(VideoEncoder* externalEncoder, 185 uint8_t payloadType, 186 bool internalSource /*= false*/) { 187 CriticalSectionScoped cs(_sendCritSect); 188 189 if (externalEncoder == NULL) { 190 bool wasSendCodec = false; 191 const bool ret = 192 _codecDataBase.DeregisterExternalEncoder(payloadType, &wasSendCodec); 193 if (wasSendCodec) { 194 // Make sure the VCM doesn't use the de-registered codec 195 _encoder = NULL; 196 } 197 return ret ? 0 : -1; 198 } 199 _codecDataBase.RegisterExternalEncoder( 200 externalEncoder, payloadType, internalSource); 201 return 0; 202 } 203 204 // Get codec config parameters 205 int32_t VideoSender::CodecConfigParameters(uint8_t* buffer, 206 int32_t size) const { 207 CriticalSectionScoped cs(_sendCritSect); 208 if (_encoder != NULL) { 209 return _encoder->CodecConfigParameters(buffer, size); 210 } 211 return VCM_UNINITIALIZED; 212 } 213 214 // TODO(andresp): Make const once media_opt is thread-safe and this has a 215 // pointer to it. 216 int32_t VideoSender::SentFrameCount(VCMFrameCount* frameCount) { 217 CriticalSectionScoped cs(_sendCritSect); 218 *frameCount = _mediaOpt.SentFrameCount(); 219 return VCM_OK; 220 } 221 222 // Get encode bitrate 223 int VideoSender::Bitrate(unsigned int* bitrate) const { 224 CriticalSectionScoped cs(_sendCritSect); 225 // return the bit rate which the encoder is set to 226 if (!_encoder) { 227 return VCM_UNINITIALIZED; 228 } 229 *bitrate = _encoder->BitRate(); 230 return 0; 231 } 232 233 // Get encode frame rate 234 int VideoSender::FrameRate(unsigned int* framerate) const { 235 CriticalSectionScoped cs(_sendCritSect); 236 // input frame rate, not compensated 237 if (!_encoder) { 238 return VCM_UNINITIALIZED; 239 } 240 *framerate = _encoder->FrameRate(); 241 return 0; 242 } 243 244 // Set channel parameters 245 int32_t VideoSender::SetChannelParameters(uint32_t target_bitrate, 246 uint8_t lossRate, 247 uint32_t rtt) { 248 int32_t ret = 0; 249 { 250 CriticalSectionScoped sendCs(_sendCritSect); 251 uint32_t targetRate = _mediaOpt.SetTargetRates(target_bitrate, 252 lossRate, 253 rtt, 254 protection_callback_, 255 qm_settings_callback_); 256 if (_encoder != NULL) { 257 ret = _encoder->SetChannelParameters(lossRate, rtt); 258 if (ret < 0) { 259 return ret; 260 } 261 ret = (int32_t)_encoder->SetRates(targetRate, _mediaOpt.InputFrameRate()); 262 if (ret < 0) { 263 return ret; 264 } 265 } else { 266 return VCM_UNINITIALIZED; 267 } // encoder 268 } // send side 269 return VCM_OK; 270 } 271 272 int32_t VideoSender::RegisterTransportCallback( 273 VCMPacketizationCallback* transport) { 274 CriticalSectionScoped cs(_sendCritSect); 275 _encodedFrameCallback.SetMediaOpt(&_mediaOpt); 276 _encodedFrameCallback.SetTransportCallback(transport); 277 return VCM_OK; 278 } 279 280 // Register video output information callback which will be called to deliver 281 // information about the video stream produced by the encoder, for instance the 282 // average frame rate and bit rate. 283 int32_t VideoSender::RegisterSendStatisticsCallback( 284 VCMSendStatisticsCallback* sendStats) { 285 CriticalSectionScoped cs(process_crit_sect_.get()); 286 _sendStatsCallback = sendStats; 287 return VCM_OK; 288 } 289 290 // Register a video quality settings callback which will be called when frame 291 // rate/dimensions need to be updated for video quality optimization 292 int32_t VideoSender::RegisterVideoQMCallback( 293 VCMQMSettingsCallback* qm_settings_callback) { 294 CriticalSectionScoped cs(_sendCritSect); 295 qm_settings_callback_ = qm_settings_callback; 296 _mediaOpt.EnableQM(qm_settings_callback_ != NULL); 297 return VCM_OK; 298 } 299 300 // Register a video protection callback which will be called to deliver the 301 // requested FEC rate and NACK status (on/off). 302 int32_t VideoSender::RegisterProtectionCallback( 303 VCMProtectionCallback* protection_callback) { 304 CriticalSectionScoped cs(_sendCritSect); 305 protection_callback_ = protection_callback; 306 return VCM_OK; 307 } 308 309 // Enable or disable a video protection method. 310 // Note: This API should be deprecated, as it does not offer a distinction 311 // between the protection method and decoding with or without errors. If such a 312 // behavior is desired, use the following API: SetReceiverRobustnessMode. 313 int32_t VideoSender::SetVideoProtection(VCMVideoProtection videoProtection, 314 bool enable) { 315 switch (videoProtection) { 316 case kProtectionNack: 317 case kProtectionNackSender: { 318 CriticalSectionScoped cs(_sendCritSect); 319 _mediaOpt.EnableProtectionMethod(enable, media_optimization::kNack); 320 break; 321 } 322 323 case kProtectionNackFEC: { 324 CriticalSectionScoped cs(_sendCritSect); 325 _mediaOpt.EnableProtectionMethod(enable, media_optimization::kNackFec); 326 break; 327 } 328 329 case kProtectionFEC: { 330 CriticalSectionScoped cs(_sendCritSect); 331 _mediaOpt.EnableProtectionMethod(enable, media_optimization::kFec); 332 break; 333 } 334 335 case kProtectionPeriodicKeyFrames: { 336 CriticalSectionScoped cs(_sendCritSect); 337 return _codecDataBase.SetPeriodicKeyFrames(enable) ? 0 : -1; 338 break; 339 } 340 case kProtectionNackReceiver: 341 case kProtectionDualDecoder: 342 case kProtectionKeyOnLoss: 343 case kProtectionKeyOnKeyLoss: 344 // Ignore decoder modes. 345 return VCM_OK; 346 } 347 return VCM_OK; 348 } 349 // Add one raw video frame to the encoder, blocking. 350 int32_t VideoSender::AddVideoFrame(const I420VideoFrame& videoFrame, 351 const VideoContentMetrics* contentMetrics, 352 const CodecSpecificInfo* codecSpecificInfo) { 353 CriticalSectionScoped cs(_sendCritSect); 354 if (_encoder == NULL) { 355 return VCM_UNINITIALIZED; 356 } 357 // TODO(holmer): Add support for dropping frames per stream. Currently we 358 // only have one frame dropper for all streams. 359 if (_nextFrameTypes[0] == kFrameEmpty) { 360 return VCM_OK; 361 } 362 if (_mediaOpt.DropFrame()) { 363 return VCM_OK; 364 } 365 _mediaOpt.UpdateContentData(contentMetrics); 366 int32_t ret = 367 _encoder->Encode(videoFrame, codecSpecificInfo, _nextFrameTypes); 368 recorder_->Add(videoFrame); 369 if (ret < 0) { 370 LOG(LS_ERROR) << "Failed to encode frame. Error code: " << ret; 371 return ret; 372 } 373 for (size_t i = 0; i < _nextFrameTypes.size(); ++i) { 374 _nextFrameTypes[i] = kVideoFrameDelta; // Default frame type. 375 } 376 return VCM_OK; 377 } 378 379 int32_t VideoSender::IntraFrameRequest(int stream_index) { 380 CriticalSectionScoped cs(_sendCritSect); 381 if (stream_index < 0 || 382 static_cast<unsigned int>(stream_index) >= _nextFrameTypes.size()) { 383 return -1; 384 } 385 _nextFrameTypes[stream_index] = kVideoFrameKey; 386 if (_encoder != NULL && _encoder->InternalSource()) { 387 // Try to request the frame if we have an external encoder with 388 // internal source since AddVideoFrame never will be called. 389 if (_encoder->RequestFrame(_nextFrameTypes) == WEBRTC_VIDEO_CODEC_OK) { 390 _nextFrameTypes[stream_index] = kVideoFrameDelta; 391 } 392 } 393 return VCM_OK; 394 } 395 396 int32_t VideoSender::EnableFrameDropper(bool enable) { 397 CriticalSectionScoped cs(_sendCritSect); 398 frame_dropper_enabled_ = enable; 399 _mediaOpt.EnableFrameDropper(enable); 400 return VCM_OK; 401 } 402 403 int VideoSender::SetSenderNackMode(SenderNackMode mode) { 404 CriticalSectionScoped cs(_sendCritSect); 405 406 switch (mode) { 407 case VideoCodingModule::kNackNone: 408 _mediaOpt.EnableProtectionMethod(false, media_optimization::kNack); 409 break; 410 case VideoCodingModule::kNackAll: 411 _mediaOpt.EnableProtectionMethod(true, media_optimization::kNack); 412 break; 413 case VideoCodingModule::kNackSelective: 414 return VCM_NOT_IMPLEMENTED; 415 break; 416 } 417 return VCM_OK; 418 } 419 420 int VideoSender::SetSenderReferenceSelection(bool enable) { 421 return VCM_NOT_IMPLEMENTED; 422 } 423 424 int VideoSender::SetSenderFEC(bool enable) { 425 CriticalSectionScoped cs(_sendCritSect); 426 _mediaOpt.EnableProtectionMethod(enable, media_optimization::kFec); 427 return VCM_OK; 428 } 429 430 int VideoSender::SetSenderKeyFramePeriod(int periodMs) { 431 return VCM_NOT_IMPLEMENTED; 432 } 433 434 int VideoSender::StartDebugRecording(const char* file_name_utf8) { 435 return recorder_->Start(file_name_utf8); 436 } 437 438 void VideoSender::StopDebugRecording() { 439 recorder_->Stop(); 440 } 441 442 void VideoSender::SuspendBelowMinBitrate() { 443 CriticalSectionScoped cs(_sendCritSect); 444 VideoCodec current_send_codec; 445 if (SendCodec(¤t_send_codec) != 0) { 446 assert(false); // Must set a send codec before SuspendBelowMinBitrate. 447 return; 448 } 449 int threshold_bps; 450 if (current_send_codec.numberOfSimulcastStreams == 0) { 451 threshold_bps = current_send_codec.minBitrate * 1000; 452 } else { 453 threshold_bps = current_send_codec.simulcastStream[0].minBitrate * 1000; 454 } 455 // Set the hysteresis window to be at 10% of the threshold, but at least 456 // 10 kbps. 457 int window_bps = std::max(threshold_bps / 10, 10000); 458 _mediaOpt.SuspendBelowMinBitrate(threshold_bps, window_bps); 459 } 460 461 bool VideoSender::VideoSuspended() const { 462 CriticalSectionScoped cs(_sendCritSect); 463 return _mediaOpt.IsVideoSuspended(); 464 } 465 } // namespace vcm 466 } // namespace webrtc 467