| 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/base/checks.h" | |
| 12 #include "webrtc/base/logging.h" | |
| 13 #include "webrtc/base/trace_event.h" | |
| 14 #include "webrtc/common_types.h" | |
| 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/jitter_buffer.h" | |
| 19 #include "webrtc/modules/video_coding/main/source/packet.h" | |
| 20 #include "webrtc/modules/video_coding/main/source/video_coding_impl.h" | |
| 21 #include "webrtc/system_wrappers/include/clock.h" | |
| 22 | |
| 23 // #define DEBUG_DECODER_BIT_STREAM | |
| 24 | |
| 25 namespace webrtc { | |
| 26 namespace vcm { | |
| 27 | |
| 28 VideoReceiver::VideoReceiver(Clock* clock, EventFactory* event_factory) | |
| 29 : clock_(clock), | |
| 30 process_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), | |
| 31 _receiveCritSect(CriticalSectionWrapper::CreateCriticalSection()), | |
| 32 _timing(clock_), | |
| 33 _receiver(&_timing, clock_, event_factory), | |
| 34 _decodedFrameCallback(_timing, clock_), | |
| 35 _frameTypeCallback(NULL), | |
| 36 _receiveStatsCallback(NULL), | |
| 37 _decoderTimingCallback(NULL), | |
| 38 _packetRequestCallback(NULL), | |
| 39 render_buffer_callback_(NULL), | |
| 40 _decoder(NULL), | |
| 41 #ifdef DEBUG_DECODER_BIT_STREAM | |
| 42 _bitStreamBeforeDecoder(NULL), | |
| 43 #endif | |
| 44 _frameFromFile(), | |
| 45 _scheduleKeyRequest(false), | |
| 46 max_nack_list_size_(0), | |
| 47 pre_decode_image_callback_(NULL), | |
| 48 _codecDataBase(nullptr, nullptr), | |
| 49 _receiveStatsTimer(1000, clock_), | |
| 50 _retransmissionTimer(10, clock_), | |
| 51 _keyRequestTimer(500, clock_) { | |
| 52 assert(clock_); | |
| 53 #ifdef DEBUG_DECODER_BIT_STREAM | |
| 54 _bitStreamBeforeDecoder = fopen("decoderBitStream.bit", "wb"); | |
| 55 #endif | |
| 56 } | |
| 57 | |
| 58 VideoReceiver::~VideoReceiver() { | |
| 59 delete _receiveCritSect; | |
| 60 #ifdef DEBUG_DECODER_BIT_STREAM | |
| 61 fclose(_bitStreamBeforeDecoder); | |
| 62 #endif | |
| 63 } | |
| 64 | |
| 65 int32_t VideoReceiver::Process() { | |
| 66 int32_t returnValue = VCM_OK; | |
| 67 | |
| 68 // Receive-side statistics | |
| 69 if (_receiveStatsTimer.TimeUntilProcess() == 0) { | |
| 70 _receiveStatsTimer.Processed(); | |
| 71 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 72 if (_receiveStatsCallback != NULL) { | |
| 73 uint32_t bitRate; | |
| 74 uint32_t frameRate; | |
| 75 _receiver.ReceiveStatistics(&bitRate, &frameRate); | |
| 76 _receiveStatsCallback->OnReceiveRatesUpdated(bitRate, frameRate); | |
| 77 } | |
| 78 | |
| 79 if (_decoderTimingCallback != NULL) { | |
| 80 int decode_ms; | |
| 81 int max_decode_ms; | |
| 82 int current_delay_ms; | |
| 83 int target_delay_ms; | |
| 84 int jitter_buffer_ms; | |
| 85 int min_playout_delay_ms; | |
| 86 int render_delay_ms; | |
| 87 _timing.GetTimings(&decode_ms, | |
| 88 &max_decode_ms, | |
| 89 ¤t_delay_ms, | |
| 90 &target_delay_ms, | |
| 91 &jitter_buffer_ms, | |
| 92 &min_playout_delay_ms, | |
| 93 &render_delay_ms); | |
| 94 _decoderTimingCallback->OnDecoderTiming(decode_ms, | |
| 95 max_decode_ms, | |
| 96 current_delay_ms, | |
| 97 target_delay_ms, | |
| 98 jitter_buffer_ms, | |
| 99 min_playout_delay_ms, | |
| 100 render_delay_ms); | |
| 101 } | |
| 102 | |
| 103 // Size of render buffer. | |
| 104 if (render_buffer_callback_) { | |
| 105 int buffer_size_ms = _receiver.RenderBufferSizeMs(); | |
| 106 render_buffer_callback_->RenderBufferSizeMs(buffer_size_ms); | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 // Key frame requests | |
| 111 if (_keyRequestTimer.TimeUntilProcess() == 0) { | |
| 112 _keyRequestTimer.Processed(); | |
| 113 bool request_key_frame = false; | |
| 114 { | |
| 115 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 116 request_key_frame = _scheduleKeyRequest && _frameTypeCallback != NULL; | |
| 117 } | |
| 118 if (request_key_frame) { | |
| 119 const int32_t ret = RequestKeyFrame(); | |
| 120 if (ret != VCM_OK && returnValue == VCM_OK) { | |
| 121 returnValue = ret; | |
| 122 } | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 // Packet retransmission requests | |
| 127 // TODO(holmer): Add API for changing Process interval and make sure it's | |
| 128 // disabled when NACK is off. | |
| 129 if (_retransmissionTimer.TimeUntilProcess() == 0) { | |
| 130 _retransmissionTimer.Processed(); | |
| 131 bool callback_registered = false; | |
| 132 uint16_t length; | |
| 133 { | |
| 134 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 135 length = max_nack_list_size_; | |
| 136 callback_registered = _packetRequestCallback != NULL; | |
| 137 } | |
| 138 if (callback_registered && length > 0) { | |
| 139 // Collect sequence numbers from the default receiver. | |
| 140 bool request_key_frame = false; | |
| 141 std::vector<uint16_t> nackList = _receiver.NackList(&request_key_frame); | |
| 142 int32_t ret = VCM_OK; | |
| 143 if (request_key_frame) { | |
| 144 ret = RequestKeyFrame(); | |
| 145 if (ret != VCM_OK && returnValue == VCM_OK) { | |
| 146 returnValue = ret; | |
| 147 } | |
| 148 } | |
| 149 if (ret == VCM_OK && !nackList.empty()) { | |
| 150 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 151 if (_packetRequestCallback != NULL) { | |
| 152 _packetRequestCallback->ResendPackets(&nackList[0], nackList.size()); | |
| 153 } | |
| 154 } | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 return returnValue; | |
| 159 } | |
| 160 | |
| 161 int64_t VideoReceiver::TimeUntilNextProcess() { | |
| 162 int64_t timeUntilNextProcess = _receiveStatsTimer.TimeUntilProcess(); | |
| 163 if (_receiver.NackMode() != kNoNack) { | |
| 164 // We need a Process call more often if we are relying on | |
| 165 // retransmissions | |
| 166 timeUntilNextProcess = | |
| 167 VCM_MIN(timeUntilNextProcess, _retransmissionTimer.TimeUntilProcess()); | |
| 168 } | |
| 169 timeUntilNextProcess = | |
| 170 VCM_MIN(timeUntilNextProcess, _keyRequestTimer.TimeUntilProcess()); | |
| 171 | |
| 172 return timeUntilNextProcess; | |
| 173 } | |
| 174 | |
| 175 int32_t VideoReceiver::SetReceiveChannelParameters(int64_t rtt) { | |
| 176 CriticalSectionScoped receiveCs(_receiveCritSect); | |
| 177 _receiver.UpdateRtt(rtt); | |
| 178 return 0; | |
| 179 } | |
| 180 | |
| 181 // Enable or disable a video protection method. | |
| 182 // Note: This API should be deprecated, as it does not offer a distinction | |
| 183 // between the protection method and decoding with or without errors. If such a | |
| 184 // behavior is desired, use the following API: SetReceiverRobustnessMode. | |
| 185 int32_t VideoReceiver::SetVideoProtection(VCMVideoProtection videoProtection, | |
| 186 bool enable) { | |
| 187 // By default, do not decode with errors. | |
| 188 _receiver.SetDecodeErrorMode(kNoErrors); | |
| 189 switch (videoProtection) { | |
| 190 case kProtectionNack: { | |
| 191 RTC_DCHECK(enable); | |
| 192 _receiver.SetNackMode(kNack, -1, -1); | |
| 193 break; | |
| 194 } | |
| 195 | |
| 196 case kProtectionNackFEC: { | |
| 197 CriticalSectionScoped cs(_receiveCritSect); | |
| 198 RTC_DCHECK(enable); | |
| 199 _receiver.SetNackMode(kNack, media_optimization::kLowRttNackMs, -1); | |
| 200 _receiver.SetDecodeErrorMode(kNoErrors); | |
| 201 break; | |
| 202 } | |
| 203 case kProtectionFEC: | |
| 204 case kProtectionNone: | |
| 205 // No receiver-side protection. | |
| 206 RTC_DCHECK(enable); | |
| 207 _receiver.SetNackMode(kNoNack, -1, -1); | |
| 208 _receiver.SetDecodeErrorMode(kWithErrors); | |
| 209 break; | |
| 210 } | |
| 211 return VCM_OK; | |
| 212 } | |
| 213 | |
| 214 // Register a receive callback. Will be called whenever there is a new frame | |
| 215 // ready for rendering. | |
| 216 int32_t VideoReceiver::RegisterReceiveCallback( | |
| 217 VCMReceiveCallback* receiveCallback) { | |
| 218 CriticalSectionScoped cs(_receiveCritSect); | |
| 219 _decodedFrameCallback.SetUserReceiveCallback(receiveCallback); | |
| 220 return VCM_OK; | |
| 221 } | |
| 222 | |
| 223 int32_t VideoReceiver::RegisterReceiveStatisticsCallback( | |
| 224 VCMReceiveStatisticsCallback* receiveStats) { | |
| 225 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 226 _receiver.RegisterStatsCallback(receiveStats); | |
| 227 _receiveStatsCallback = receiveStats; | |
| 228 return VCM_OK; | |
| 229 } | |
| 230 | |
| 231 int32_t VideoReceiver::RegisterDecoderTimingCallback( | |
| 232 VCMDecoderTimingCallback* decoderTiming) { | |
| 233 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 234 _decoderTimingCallback = decoderTiming; | |
| 235 return VCM_OK; | |
| 236 } | |
| 237 | |
| 238 // Register an externally defined decoder/render object. | |
| 239 // Can be a decoder only or a decoder coupled with a renderer. | |
| 240 int32_t VideoReceiver::RegisterExternalDecoder(VideoDecoder* externalDecoder, | |
| 241 uint8_t payloadType, | |
| 242 bool internalRenderTiming) { | |
| 243 CriticalSectionScoped cs(_receiveCritSect); | |
| 244 if (externalDecoder == NULL) { | |
| 245 // Make sure the VCM updates the decoder next time it decodes. | |
| 246 _decoder = NULL; | |
| 247 return _codecDataBase.DeregisterExternalDecoder(payloadType) ? 0 : -1; | |
| 248 } | |
| 249 return _codecDataBase.RegisterExternalDecoder( | |
| 250 externalDecoder, payloadType, internalRenderTiming) | |
| 251 ? 0 | |
| 252 : -1; | |
| 253 } | |
| 254 | |
| 255 // Register a frame type request callback. | |
| 256 int32_t VideoReceiver::RegisterFrameTypeCallback( | |
| 257 VCMFrameTypeCallback* frameTypeCallback) { | |
| 258 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 259 _frameTypeCallback = frameTypeCallback; | |
| 260 return VCM_OK; | |
| 261 } | |
| 262 | |
| 263 int32_t VideoReceiver::RegisterPacketRequestCallback( | |
| 264 VCMPacketRequestCallback* callback) { | |
| 265 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 266 _packetRequestCallback = callback; | |
| 267 return VCM_OK; | |
| 268 } | |
| 269 | |
| 270 int VideoReceiver::RegisterRenderBufferSizeCallback( | |
| 271 VCMRenderBufferSizeCallback* callback) { | |
| 272 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 273 render_buffer_callback_ = callback; | |
| 274 return VCM_OK; | |
| 275 } | |
| 276 | |
| 277 void VideoReceiver::TriggerDecoderShutdown() { | |
| 278 _receiver.TriggerDecoderShutdown(); | |
| 279 } | |
| 280 | |
| 281 // Decode next frame, blocking. | |
| 282 // Should be called as often as possible to get the most out of the decoder. | |
| 283 int32_t VideoReceiver::Decode(uint16_t maxWaitTimeMs) { | |
| 284 int64_t nextRenderTimeMs; | |
| 285 bool supports_render_scheduling; | |
| 286 { | |
| 287 CriticalSectionScoped cs(_receiveCritSect); | |
| 288 supports_render_scheduling = _codecDataBase.SupportsRenderScheduling(); | |
| 289 } | |
| 290 | |
| 291 VCMEncodedFrame* frame = _receiver.FrameForDecoding( | |
| 292 maxWaitTimeMs, nextRenderTimeMs, supports_render_scheduling); | |
| 293 | |
| 294 if (frame == NULL) { | |
| 295 return VCM_FRAME_NOT_READY; | |
| 296 } else { | |
| 297 CriticalSectionScoped cs(_receiveCritSect); | |
| 298 | |
| 299 // If this frame was too late, we should adjust the delay accordingly | |
| 300 _timing.UpdateCurrentDelay(frame->RenderTimeMs(), | |
| 301 clock_->TimeInMilliseconds()); | |
| 302 | |
| 303 if (pre_decode_image_callback_) { | |
| 304 EncodedImage encoded_image(frame->EncodedImage()); | |
| 305 int qp = -1; | |
| 306 if (qp_parser_.GetQp(*frame, &qp)) { | |
| 307 encoded_image.qp_ = qp; | |
| 308 } | |
| 309 pre_decode_image_callback_->Encoded( | |
| 310 encoded_image, frame->CodecSpecific(), NULL); | |
| 311 } | |
| 312 | |
| 313 #ifdef DEBUG_DECODER_BIT_STREAM | |
| 314 if (_bitStreamBeforeDecoder != NULL) { | |
| 315 // Write bit stream to file for debugging purposes | |
| 316 if (fwrite( | |
| 317 frame->Buffer(), 1, frame->Length(), _bitStreamBeforeDecoder) != | |
| 318 frame->Length()) { | |
| 319 return -1; | |
| 320 } | |
| 321 } | |
| 322 #endif | |
| 323 const int32_t ret = Decode(*frame); | |
| 324 _receiver.ReleaseFrame(frame); | |
| 325 frame = NULL; | |
| 326 if (ret != VCM_OK) { | |
| 327 return ret; | |
| 328 } | |
| 329 } | |
| 330 return VCM_OK; | |
| 331 } | |
| 332 | |
| 333 int32_t VideoReceiver::RequestSliceLossIndication( | |
| 334 const uint64_t pictureID) const { | |
| 335 TRACE_EVENT1("webrtc", "RequestSLI", "picture_id", pictureID); | |
| 336 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 337 if (_frameTypeCallback != NULL) { | |
| 338 const int32_t ret = | |
| 339 _frameTypeCallback->SliceLossIndicationRequest(pictureID); | |
| 340 if (ret < 0) { | |
| 341 return ret; | |
| 342 } | |
| 343 } else { | |
| 344 return VCM_MISSING_CALLBACK; | |
| 345 } | |
| 346 return VCM_OK; | |
| 347 } | |
| 348 | |
| 349 int32_t VideoReceiver::RequestKeyFrame() { | |
| 350 TRACE_EVENT0("webrtc", "RequestKeyFrame"); | |
| 351 CriticalSectionScoped process_cs(process_crit_sect_.get()); | |
| 352 if (_frameTypeCallback != NULL) { | |
| 353 const int32_t ret = _frameTypeCallback->RequestKeyFrame(); | |
| 354 if (ret < 0) { | |
| 355 return ret; | |
| 356 } | |
| 357 _scheduleKeyRequest = false; | |
| 358 } else { | |
| 359 return VCM_MISSING_CALLBACK; | |
| 360 } | |
| 361 return VCM_OK; | |
| 362 } | |
| 363 | |
| 364 // Must be called from inside the receive side critical section. | |
| 365 int32_t VideoReceiver::Decode(const VCMEncodedFrame& frame) { | |
| 366 TRACE_EVENT_ASYNC_STEP1("webrtc", | |
| 367 "Video", | |
| 368 frame.TimeStamp(), | |
| 369 "Decode", | |
| 370 "type", | |
| 371 frame.FrameType()); | |
| 372 // Change decoder if payload type has changed | |
| 373 const bool renderTimingBefore = _codecDataBase.SupportsRenderScheduling(); | |
| 374 _decoder = | |
| 375 _codecDataBase.GetDecoder(frame.PayloadType(), &_decodedFrameCallback); | |
| 376 if (renderTimingBefore != _codecDataBase.SupportsRenderScheduling()) { | |
| 377 // Make sure we reset the decode time estimate since it will | |
| 378 // be zero for codecs without render timing. | |
| 379 _timing.ResetDecodeTime(); | |
| 380 } | |
| 381 if (_decoder == NULL) { | |
| 382 return VCM_NO_CODEC_REGISTERED; | |
| 383 } | |
| 384 // Decode a frame | |
| 385 int32_t ret = _decoder->Decode(frame, clock_->TimeInMilliseconds()); | |
| 386 | |
| 387 // Check for failed decoding, run frame type request callback if needed. | |
| 388 bool request_key_frame = false; | |
| 389 if (ret < 0) { | |
| 390 if (ret == VCM_ERROR_REQUEST_SLI) { | |
| 391 return RequestSliceLossIndication( | |
| 392 _decodedFrameCallback.LastReceivedPictureID() + 1); | |
| 393 } else { | |
| 394 request_key_frame = true; | |
| 395 } | |
| 396 } else if (ret == VCM_REQUEST_SLI) { | |
| 397 ret = RequestSliceLossIndication( | |
| 398 _decodedFrameCallback.LastReceivedPictureID() + 1); | |
| 399 } | |
| 400 if (!frame.Complete() || frame.MissingFrame()) { | |
| 401 request_key_frame = true; | |
| 402 ret = VCM_OK; | |
| 403 } | |
| 404 if (request_key_frame) { | |
| 405 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 406 _scheduleKeyRequest = true; | |
| 407 } | |
| 408 TRACE_EVENT_ASYNC_END0("webrtc", "Video", frame.TimeStamp()); | |
| 409 return ret; | |
| 410 } | |
| 411 | |
| 412 // Reset the decoder state | |
| 413 int32_t VideoReceiver::ResetDecoder() { | |
| 414 bool reset_key_request = false; | |
| 415 { | |
| 416 CriticalSectionScoped cs(_receiveCritSect); | |
| 417 if (_decoder != NULL) { | |
| 418 _receiver.Reset(); | |
| 419 _timing.Reset(); | |
| 420 reset_key_request = true; | |
| 421 _decoder->Reset(); | |
| 422 } | |
| 423 } | |
| 424 if (reset_key_request) { | |
| 425 CriticalSectionScoped cs(process_crit_sect_.get()); | |
| 426 _scheduleKeyRequest = false; | |
| 427 } | |
| 428 return VCM_OK; | |
| 429 } | |
| 430 | |
| 431 // Register possible receive codecs, can be called multiple times | |
| 432 int32_t VideoReceiver::RegisterReceiveCodec(const VideoCodec* receiveCodec, | |
| 433 int32_t numberOfCores, | |
| 434 bool requireKeyFrame) { | |
| 435 CriticalSectionScoped cs(_receiveCritSect); | |
| 436 if (receiveCodec == NULL) { | |
| 437 return VCM_PARAMETER_ERROR; | |
| 438 } | |
| 439 if (!_codecDataBase.RegisterReceiveCodec( | |
| 440 receiveCodec, numberOfCores, requireKeyFrame)) { | |
| 441 return -1; | |
| 442 } | |
| 443 return 0; | |
| 444 } | |
| 445 | |
| 446 // Get current received codec | |
| 447 int32_t VideoReceiver::ReceiveCodec(VideoCodec* currentReceiveCodec) const { | |
| 448 CriticalSectionScoped cs(_receiveCritSect); | |
| 449 if (currentReceiveCodec == NULL) { | |
| 450 return VCM_PARAMETER_ERROR; | |
| 451 } | |
| 452 return _codecDataBase.ReceiveCodec(currentReceiveCodec) ? 0 : -1; | |
| 453 } | |
| 454 | |
| 455 // Get current received codec | |
| 456 VideoCodecType VideoReceiver::ReceiveCodec() const { | |
| 457 CriticalSectionScoped cs(_receiveCritSect); | |
| 458 return _codecDataBase.ReceiveCodec(); | |
| 459 } | |
| 460 | |
| 461 // Incoming packet from network parsed and ready for decode, non blocking. | |
| 462 int32_t VideoReceiver::IncomingPacket(const uint8_t* incomingPayload, | |
| 463 size_t payloadLength, | |
| 464 const WebRtcRTPHeader& rtpInfo) { | |
| 465 if (rtpInfo.frameType == kVideoFrameKey) { | |
| 466 TRACE_EVENT1("webrtc", | |
| 467 "VCM::PacketKeyFrame", | |
| 468 "seqnum", | |
| 469 rtpInfo.header.sequenceNumber); | |
| 470 } | |
| 471 if (incomingPayload == NULL) { | |
| 472 // The jitter buffer doesn't handle non-zero payload lengths for packets | |
| 473 // without payload. | |
| 474 // TODO(holmer): We should fix this in the jitter buffer. | |
| 475 payloadLength = 0; | |
| 476 } | |
| 477 const VCMPacket packet(incomingPayload, payloadLength, rtpInfo); | |
| 478 int32_t ret = _receiver.InsertPacket(packet, rtpInfo.type.Video.width, | |
| 479 rtpInfo.type.Video.height); | |
| 480 // TODO(holmer): Investigate if this somehow should use the key frame | |
| 481 // request scheduling to throttle the requests. | |
| 482 if (ret == VCM_FLUSH_INDICATOR) { | |
| 483 RequestKeyFrame(); | |
| 484 ResetDecoder(); | |
| 485 } else if (ret < 0) { | |
| 486 return ret; | |
| 487 } | |
| 488 return VCM_OK; | |
| 489 } | |
| 490 | |
| 491 // Minimum playout delay (used for lip-sync). This is the minimum delay required | |
| 492 // to sync with audio. Not included in VideoCodingModule::Delay() | |
| 493 // Defaults to 0 ms. | |
| 494 int32_t VideoReceiver::SetMinimumPlayoutDelay(uint32_t minPlayoutDelayMs) { | |
| 495 _timing.set_min_playout_delay(minPlayoutDelayMs); | |
| 496 return VCM_OK; | |
| 497 } | |
| 498 | |
| 499 // The estimated delay caused by rendering, defaults to | |
| 500 // kDefaultRenderDelayMs = 10 ms | |
| 501 int32_t VideoReceiver::SetRenderDelay(uint32_t timeMS) { | |
| 502 _timing.set_render_delay(timeMS); | |
| 503 return VCM_OK; | |
| 504 } | |
| 505 | |
| 506 // Current video delay | |
| 507 int32_t VideoReceiver::Delay() const { return _timing.TargetVideoDelay(); } | |
| 508 | |
| 509 uint32_t VideoReceiver::DiscardedPackets() const { | |
| 510 return _receiver.DiscardedPackets(); | |
| 511 } | |
| 512 | |
| 513 int VideoReceiver::SetReceiverRobustnessMode( | |
| 514 ReceiverRobustness robustnessMode, | |
| 515 VCMDecodeErrorMode decode_error_mode) { | |
| 516 CriticalSectionScoped cs(_receiveCritSect); | |
| 517 switch (robustnessMode) { | |
| 518 case VideoCodingModule::kNone: | |
| 519 _receiver.SetNackMode(kNoNack, -1, -1); | |
| 520 break; | |
| 521 case VideoCodingModule::kHardNack: | |
| 522 // Always wait for retransmissions (except when decoding with errors). | |
| 523 _receiver.SetNackMode(kNack, -1, -1); | |
| 524 break; | |
| 525 case VideoCodingModule::kSoftNack: | |
| 526 #if 1 | |
| 527 assert(false); // TODO(hlundin): Not completed. | |
| 528 return VCM_NOT_IMPLEMENTED; | |
| 529 #else | |
| 530 // Enable hybrid NACK/FEC. Always wait for retransmissions and don't add | |
| 531 // extra delay when RTT is above kLowRttNackMs. | |
| 532 _receiver.SetNackMode(kNack, media_optimization::kLowRttNackMs, -1); | |
| 533 break; | |
| 534 #endif | |
| 535 case VideoCodingModule::kReferenceSelection: | |
| 536 #if 1 | |
| 537 assert(false); // TODO(hlundin): Not completed. | |
| 538 return VCM_NOT_IMPLEMENTED; | |
| 539 #else | |
| 540 if (decode_error_mode == kNoErrors) { | |
| 541 return VCM_PARAMETER_ERROR; | |
| 542 } | |
| 543 _receiver.SetNackMode(kNoNack, -1, -1); | |
| 544 break; | |
| 545 #endif | |
| 546 } | |
| 547 _receiver.SetDecodeErrorMode(decode_error_mode); | |
| 548 return VCM_OK; | |
| 549 } | |
| 550 | |
| 551 void VideoReceiver::SetDecodeErrorMode(VCMDecodeErrorMode decode_error_mode) { | |
| 552 CriticalSectionScoped cs(_receiveCritSect); | |
| 553 _receiver.SetDecodeErrorMode(decode_error_mode); | |
| 554 } | |
| 555 | |
| 556 void VideoReceiver::SetNackSettings(size_t max_nack_list_size, | |
| 557 int max_packet_age_to_nack, | |
| 558 int max_incomplete_time_ms) { | |
| 559 if (max_nack_list_size != 0) { | |
| 560 CriticalSectionScoped process_cs(process_crit_sect_.get()); | |
| 561 max_nack_list_size_ = max_nack_list_size; | |
| 562 } | |
| 563 _receiver.SetNackSettings( | |
| 564 max_nack_list_size, max_packet_age_to_nack, max_incomplete_time_ms); | |
| 565 } | |
| 566 | |
| 567 int VideoReceiver::SetMinReceiverDelay(int desired_delay_ms) { | |
| 568 return _receiver.SetMinReceiverDelay(desired_delay_ms); | |
| 569 } | |
| 570 | |
| 571 void VideoReceiver::RegisterPreDecodeImageCallback( | |
| 572 EncodedImageCallback* observer) { | |
| 573 CriticalSectionScoped cs(_receiveCritSect); | |
| 574 pre_decode_image_callback_ = observer; | |
| 575 } | |
| 576 | |
| 577 } // namespace vcm | |
| 578 } // namespace webrtc | |
| OLD | NEW |