1 /* 2 * Copyright (c) 2012 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/modules/video_coding/codec_database.h" 12 13 #include <assert.h> 14 15 #include "webrtc/base/checks.h" 16 #include "webrtc/base/logging.h" 17 #include "webrtc/engine_configurations.h" 18 #include "webrtc/modules/video_coding/codecs/h264/include/h264.h" 19 #include "webrtc/modules/video_coding/codecs/i420/include/i420.h" 20 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" 21 #include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" 22 #include "webrtc/modules/video_coding/internal_defines.h" 23 24 namespace { 25 const size_t kDefaultPayloadSize = 1440; 26 const uint8_t kDefaultPayloadType = 100; 27 } 28 29 namespace webrtc { 30 31 VideoCodecVP8 VideoEncoder::GetDefaultVp8Settings() { 32 VideoCodecVP8 vp8_settings; 33 memset(&vp8_settings, 0, sizeof(vp8_settings)); 34 35 vp8_settings.resilience = kResilientStream; 36 vp8_settings.numberOfTemporalLayers = 1; 37 vp8_settings.denoisingOn = true; 38 vp8_settings.errorConcealmentOn = false; 39 vp8_settings.automaticResizeOn = false; 40 vp8_settings.frameDroppingOn = true; 41 vp8_settings.keyFrameInterval = 3000; 42 43 return vp8_settings; 44 } 45 46 VideoCodecVP9 VideoEncoder::GetDefaultVp9Settings() { 47 VideoCodecVP9 vp9_settings; 48 memset(&vp9_settings, 0, sizeof(vp9_settings)); 49 50 vp9_settings.resilience = 1; 51 vp9_settings.numberOfTemporalLayers = 1; 52 vp9_settings.denoisingOn = false; 53 vp9_settings.frameDroppingOn = true; 54 vp9_settings.keyFrameInterval = 3000; 55 vp9_settings.adaptiveQpMode = true; 56 vp9_settings.automaticResizeOn = true; 57 vp9_settings.numberOfSpatialLayers = 1; 58 vp9_settings.flexibleMode = false; 59 return vp9_settings; 60 } 61 62 VideoCodecH264 VideoEncoder::GetDefaultH264Settings() { 63 VideoCodecH264 h264_settings; 64 memset(&h264_settings, 0, sizeof(h264_settings)); 65 66 h264_settings.profile = kProfileBase; 67 h264_settings.frameDroppingOn = true; 68 h264_settings.keyFrameInterval = 3000; 69 h264_settings.spsData = nullptr; 70 h264_settings.spsLen = 0; 71 h264_settings.ppsData = nullptr; 72 h264_settings.ppsLen = 0; 73 74 return h264_settings; 75 } 76 77 VCMDecoderMapItem::VCMDecoderMapItem(VideoCodec* settings, 78 int number_of_cores, 79 bool require_key_frame) 80 : settings(settings), 81 number_of_cores(number_of_cores), 82 require_key_frame(require_key_frame) { 83 assert(number_of_cores >= 0); 84 } 85 86 VCMExtDecoderMapItem::VCMExtDecoderMapItem( 87 VideoDecoder* external_decoder_instance, 88 uint8_t payload_type) 89 : payload_type(payload_type), 90 external_decoder_instance(external_decoder_instance) {} 91 92 VCMCodecDataBase::VCMCodecDataBase( 93 VideoEncoderRateObserver* encoder_rate_observer, 94 VCMEncodedFrameCallback* encoded_frame_callback) 95 : number_of_cores_(0), 96 max_payload_size_(kDefaultPayloadSize), 97 periodic_key_frames_(false), 98 pending_encoder_reset_(true), 99 send_codec_(), 100 receive_codec_(), 101 encoder_payload_type_(0), 102 external_encoder_(nullptr), 103 internal_source_(false), 104 encoder_rate_observer_(encoder_rate_observer), 105 encoded_frame_callback_(encoded_frame_callback), 106 ptr_decoder_(nullptr), 107 dec_map_(), 108 dec_external_map_() {} 109 110 VCMCodecDataBase::~VCMCodecDataBase() { 111 DeleteEncoder(); 112 ReleaseDecoder(ptr_decoder_); 113 for (auto& kv : dec_map_) 114 delete kv.second; 115 for (auto& kv : dec_external_map_) 116 delete kv.second; 117 } 118 119 void VCMCodecDataBase::Codec(VideoCodecType codec_type, VideoCodec* settings) { 120 memset(settings, 0, sizeof(VideoCodec)); 121 switch (codec_type) { 122 case kVideoCodecVP8: 123 strncpy(settings->plName, "VP8", 4); 124 settings->codecType = kVideoCodecVP8; 125 // 96 to 127 dynamic payload types for video codecs. 126 settings->plType = kDefaultPayloadType; 127 settings->startBitrate = kDefaultStartBitrateKbps; 128 settings->minBitrate = VCM_MIN_BITRATE; 129 settings->maxBitrate = 0; 130 settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; 131 settings->width = VCM_DEFAULT_CODEC_WIDTH; 132 settings->height = VCM_DEFAULT_CODEC_HEIGHT; 133 settings->numberOfSimulcastStreams = 0; 134 settings->qpMax = 56; 135 settings->codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings(); 136 return; 137 case kVideoCodecVP9: 138 strncpy(settings->plName, "VP9", 4); 139 settings->codecType = kVideoCodecVP9; 140 // 96 to 127 dynamic payload types for video codecs. 141 settings->plType = kDefaultPayloadType; 142 settings->startBitrate = 100; 143 settings->minBitrate = VCM_MIN_BITRATE; 144 settings->maxBitrate = 0; 145 settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; 146 settings->width = VCM_DEFAULT_CODEC_WIDTH; 147 settings->height = VCM_DEFAULT_CODEC_HEIGHT; 148 settings->numberOfSimulcastStreams = 0; 149 settings->qpMax = 56; 150 settings->codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings(); 151 return; 152 case kVideoCodecH264: 153 strncpy(settings->plName, "H264", 5); 154 settings->codecType = kVideoCodecH264; 155 // 96 to 127 dynamic payload types for video codecs. 156 settings->plType = kDefaultPayloadType; 157 settings->startBitrate = kDefaultStartBitrateKbps; 158 settings->minBitrate = VCM_MIN_BITRATE; 159 settings->maxBitrate = 0; 160 settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; 161 settings->width = VCM_DEFAULT_CODEC_WIDTH; 162 settings->height = VCM_DEFAULT_CODEC_HEIGHT; 163 settings->numberOfSimulcastStreams = 0; 164 settings->qpMax = 56; 165 settings->codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings(); 166 return; 167 case kVideoCodecI420: 168 strncpy(settings->plName, "I420", 5); 169 settings->codecType = kVideoCodecI420; 170 // 96 to 127 dynamic payload types for video codecs. 171 settings->plType = kDefaultPayloadType; 172 // Bitrate needed for this size and framerate. 173 settings->startBitrate = 3 * VCM_DEFAULT_CODEC_WIDTH * 174 VCM_DEFAULT_CODEC_HEIGHT * 8 * 175 VCM_DEFAULT_FRAME_RATE / 1000 / 2; 176 settings->maxBitrate = settings->startBitrate; 177 settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; 178 settings->width = VCM_DEFAULT_CODEC_WIDTH; 179 settings->height = VCM_DEFAULT_CODEC_HEIGHT; 180 settings->minBitrate = VCM_MIN_BITRATE; 181 settings->numberOfSimulcastStreams = 0; 182 return; 183 case kVideoCodecRED: 184 case kVideoCodecULPFEC: 185 case kVideoCodecGeneric: 186 case kVideoCodecUnknown: 187 RTC_NOTREACHED(); 188 return; 189 } 190 } 191 192 // Assuming only one registered encoder - since only one used, no need for more. 193 bool VCMCodecDataBase::SetSendCodec(const VideoCodec* send_codec, 194 int number_of_cores, 195 size_t max_payload_size) { 196 RTC_DCHECK(send_codec); 197 if (max_payload_size == 0) { 198 max_payload_size = kDefaultPayloadSize; 199 } 200 RTC_DCHECK_GE(number_of_cores, 1); 201 RTC_DCHECK_GE(send_codec->plType, 1); 202 // Make sure the start bit rate is sane... 203 RTC_DCHECK_LE(send_codec->startBitrate, 1000000u); 204 RTC_DCHECK(send_codec->codecType != kVideoCodecUnknown); 205 bool reset_required = pending_encoder_reset_; 206 if (number_of_cores_ != number_of_cores) { 207 number_of_cores_ = number_of_cores; 208 reset_required = true; 209 } 210 if (max_payload_size_ != max_payload_size) { 211 max_payload_size_ = max_payload_size; 212 reset_required = true; 213 } 214 215 VideoCodec new_send_codec; 216 memcpy(&new_send_codec, send_codec, sizeof(new_send_codec)); 217 218 if (new_send_codec.maxBitrate == 0) { 219 // max is one bit per pixel 220 new_send_codec.maxBitrate = (static_cast<int>(send_codec->height) * 221 static_cast<int>(send_codec->width) * 222 static_cast<int>(send_codec->maxFramerate)) / 223 1000; 224 if (send_codec->startBitrate > new_send_codec.maxBitrate) { 225 // But if the user tries to set a higher start bit rate we will 226 // increase the max accordingly. 227 new_send_codec.maxBitrate = send_codec->startBitrate; 228 } 229 } 230 231 if (new_send_codec.startBitrate > new_send_codec.maxBitrate) 232 new_send_codec.startBitrate = new_send_codec.maxBitrate; 233 234 if (!reset_required) { 235 reset_required = RequiresEncoderReset(new_send_codec); 236 } 237 238 memcpy(&send_codec_, &new_send_codec, sizeof(send_codec_)); 239 240 if (!reset_required) { 241 encoded_frame_callback_->SetPayloadType(send_codec_.plType); 242 return true; 243 } 244 245 // If encoder exists, will destroy it and create new one. 246 DeleteEncoder(); 247 RTC_DCHECK_EQ(encoder_payload_type_, send_codec_.plType) 248 << "Encoder not registered for payload type " << send_codec_.plType; 249 ptr_encoder_.reset( 250 new VCMGenericEncoder(external_encoder_, encoder_rate_observer_, 251 encoded_frame_callback_, internal_source_)); 252 encoded_frame_callback_->SetPayloadType(send_codec_.plType); 253 encoded_frame_callback_->SetInternalSource(internal_source_); 254 if (ptr_encoder_->InitEncode(&send_codec_, number_of_cores_, 255 max_payload_size_) < 0) { 256 LOG(LS_ERROR) << "Failed to initialize video encoder."; 257 DeleteEncoder(); 258 return false; 259 } 260 261 // Intentionally don't check return value since the encoder registration 262 // shouldn't fail because the codec doesn't support changing the periodic key 263 // frame setting. 264 ptr_encoder_->SetPeriodicKeyFrames(periodic_key_frames_); 265 266 pending_encoder_reset_ = false; 267 268 return true; 269 } 270 271 bool VCMCodecDataBase::SendCodec(VideoCodec* current_send_codec) const { 272 if (!ptr_encoder_) { 273 return false; 274 } 275 memcpy(current_send_codec, &send_codec_, sizeof(VideoCodec)); 276 return true; 277 } 278 279 VideoCodecType VCMCodecDataBase::SendCodec() const { 280 if (!ptr_encoder_) { 281 return kVideoCodecUnknown; 282 } 283 return send_codec_.codecType; 284 } 285 286 bool VCMCodecDataBase::DeregisterExternalEncoder(uint8_t payload_type, 287 bool* was_send_codec) { 288 assert(was_send_codec); 289 *was_send_codec = false; 290 if (encoder_payload_type_ != payload_type) { 291 return false; 292 } 293 if (send_codec_.plType == payload_type) { 294 // De-register as send codec if needed. 295 DeleteEncoder(); 296 memset(&send_codec_, 0, sizeof(VideoCodec)); 297 *was_send_codec = true; 298 } 299 encoder_payload_type_ = 0; 300 external_encoder_ = nullptr; 301 internal_source_ = false; 302 return true; 303 } 304 305 void VCMCodecDataBase::RegisterExternalEncoder(VideoEncoder* external_encoder, 306 uint8_t payload_type, 307 bool internal_source) { 308 // Since only one encoder can be used at a given time, only one external 309 // encoder can be registered/used. 310 external_encoder_ = external_encoder; 311 encoder_payload_type_ = payload_type; 312 internal_source_ = internal_source; 313 pending_encoder_reset_ = true; 314 } 315 316 bool VCMCodecDataBase::RequiresEncoderReset(const VideoCodec& new_send_codec) { 317 if (!ptr_encoder_) 318 return true; 319 320 // Does not check startBitrate or maxFramerate 321 if (new_send_codec.codecType != send_codec_.codecType || 322 strcmp(new_send_codec.plName, send_codec_.plName) != 0 || 323 new_send_codec.plType != send_codec_.plType || 324 new_send_codec.width != send_codec_.width || 325 new_send_codec.height != send_codec_.height || 326 new_send_codec.maxBitrate != send_codec_.maxBitrate || 327 new_send_codec.minBitrate != send_codec_.minBitrate || 328 new_send_codec.qpMax != send_codec_.qpMax || 329 new_send_codec.numberOfSimulcastStreams != 330 send_codec_.numberOfSimulcastStreams || 331 new_send_codec.mode != send_codec_.mode || 332 new_send_codec.extra_options != send_codec_.extra_options) { 333 return true; 334 } 335 336 switch (new_send_codec.codecType) { 337 case kVideoCodecVP8: 338 if (memcmp(&new_send_codec.codecSpecific.VP8, 339 &send_codec_.codecSpecific.VP8, 340 sizeof(new_send_codec.codecSpecific.VP8)) != 0) { 341 return true; 342 } 343 break; 344 case kVideoCodecVP9: 345 if (memcmp(&new_send_codec.codecSpecific.VP9, 346 &send_codec_.codecSpecific.VP9, 347 sizeof(new_send_codec.codecSpecific.VP9)) != 0) { 348 return true; 349 } 350 break; 351 case kVideoCodecH264: 352 if (memcmp(&new_send_codec.codecSpecific.H264, 353 &send_codec_.codecSpecific.H264, 354 sizeof(new_send_codec.codecSpecific.H264)) != 0) { 355 return true; 356 } 357 break; 358 case kVideoCodecGeneric: 359 break; 360 // Known codecs without payload-specifics 361 case kVideoCodecI420: 362 case kVideoCodecRED: 363 case kVideoCodecULPFEC: 364 break; 365 // Unknown codec type, reset just to be sure. 366 case kVideoCodecUnknown: 367 return true; 368 } 369 370 if (new_send_codec.numberOfSimulcastStreams > 0) { 371 for (unsigned char i = 0; i < new_send_codec.numberOfSimulcastStreams; 372 ++i) { 373 if (memcmp(&new_send_codec.simulcastStream[i], 374 &send_codec_.simulcastStream[i], 375 sizeof(new_send_codec.simulcastStream[i])) != 0) { 376 return true; 377 } 378 } 379 } 380 return false; 381 } 382 383 VCMGenericEncoder* VCMCodecDataBase::GetEncoder() { 384 return ptr_encoder_.get(); 385 } 386 387 bool VCMCodecDataBase::SetPeriodicKeyFrames(bool enable) { 388 periodic_key_frames_ = enable; 389 if (ptr_encoder_) { 390 return (ptr_encoder_->SetPeriodicKeyFrames(periodic_key_frames_) == 0); 391 } 392 return true; 393 } 394 395 bool VCMCodecDataBase::DeregisterExternalDecoder(uint8_t payload_type) { 396 ExternalDecoderMap::iterator it = dec_external_map_.find(payload_type); 397 if (it == dec_external_map_.end()) { 398 // Not found 399 return false; 400 } 401 // We can't use payload_type to check if the decoder is currently in use, 402 // because payload type may be out of date (e.g. before we decode the first 403 // frame after RegisterReceiveCodec) 404 if (ptr_decoder_ != nullptr && 405 ptr_decoder_->_decoder == (*it).second->external_decoder_instance) { 406 // Release it if it was registered and in use. 407 ReleaseDecoder(ptr_decoder_); 408 ptr_decoder_ = nullptr; 409 } 410 DeregisterReceiveCodec(payload_type); 411 delete it->second; 412 dec_external_map_.erase(it); 413 return true; 414 } 415 416 // Add the external encoder object to the list of external decoders. 417 // Won't be registered as a receive codec until RegisterReceiveCodec is called. 418 void VCMCodecDataBase::RegisterExternalDecoder(VideoDecoder* external_decoder, 419 uint8_t payload_type) { 420 // Check if payload value already exists, if so - erase old and insert new. 421 VCMExtDecoderMapItem* ext_decoder = 422 new VCMExtDecoderMapItem(external_decoder, payload_type); 423 DeregisterExternalDecoder(payload_type); 424 dec_external_map_[payload_type] = ext_decoder; 425 } 426 427 bool VCMCodecDataBase::DecoderRegistered() const { 428 return !dec_map_.empty(); 429 } 430 431 bool VCMCodecDataBase::RegisterReceiveCodec(const VideoCodec* receive_codec, 432 int number_of_cores, 433 bool require_key_frame) { 434 if (number_of_cores < 0) { 435 return false; 436 } 437 // Check if payload value already exists, if so - erase old and insert new. 438 DeregisterReceiveCodec(receive_codec->plType); 439 if (receive_codec->codecType == kVideoCodecUnknown) { 440 return false; 441 } 442 VideoCodec* new_receive_codec = new VideoCodec(*receive_codec); 443 dec_map_[receive_codec->plType] = new VCMDecoderMapItem( 444 new_receive_codec, number_of_cores, require_key_frame); 445 return true; 446 } 447 448 bool VCMCodecDataBase::DeregisterReceiveCodec(uint8_t payload_type) { 449 DecoderMap::iterator it = dec_map_.find(payload_type); 450 if (it == dec_map_.end()) { 451 return false; 452 } 453 delete it->second; 454 dec_map_.erase(it); 455 if (receive_codec_.plType == payload_type) { 456 // This codec is currently in use. 457 memset(&receive_codec_, 0, sizeof(VideoCodec)); 458 } 459 return true; 460 } 461 462 bool VCMCodecDataBase::ReceiveCodec(VideoCodec* current_receive_codec) const { 463 assert(current_receive_codec); 464 if (!ptr_decoder_) { 465 return false; 466 } 467 memcpy(current_receive_codec, &receive_codec_, sizeof(VideoCodec)); 468 return true; 469 } 470 471 VideoCodecType VCMCodecDataBase::ReceiveCodec() const { 472 if (!ptr_decoder_) { 473 return kVideoCodecUnknown; 474 } 475 return receive_codec_.codecType; 476 } 477 478 VCMGenericDecoder* VCMCodecDataBase::GetDecoder( 479 const VCMEncodedFrame& frame, 480 VCMDecodedFrameCallback* decoded_frame_callback) { 481 uint8_t payload_type = frame.PayloadType(); 482 if (payload_type == receive_codec_.plType || payload_type == 0) { 483 return ptr_decoder_; 484 } 485 // Check for exisitng decoder, if exists - delete. 486 if (ptr_decoder_) { 487 ReleaseDecoder(ptr_decoder_); 488 ptr_decoder_ = nullptr; 489 memset(&receive_codec_, 0, sizeof(VideoCodec)); 490 } 491 ptr_decoder_ = CreateAndInitDecoder(frame, &receive_codec_); 492 if (!ptr_decoder_) { 493 return nullptr; 494 } 495 VCMReceiveCallback* callback = decoded_frame_callback->UserReceiveCallback(); 496 if (callback) 497 callback->OnIncomingPayloadType(receive_codec_.plType); 498 if (ptr_decoder_->RegisterDecodeCompleteCallback(decoded_frame_callback) < 499 0) { 500 ReleaseDecoder(ptr_decoder_); 501 ptr_decoder_ = nullptr; 502 memset(&receive_codec_, 0, sizeof(VideoCodec)); 503 return nullptr; 504 } 505 return ptr_decoder_; 506 } 507 508 void VCMCodecDataBase::ReleaseDecoder(VCMGenericDecoder* decoder) const { 509 if (decoder) { 510 assert(decoder->_decoder); 511 decoder->Release(); 512 if (!decoder->External()) { 513 delete decoder->_decoder; 514 } 515 delete decoder; 516 } 517 } 518 519 bool VCMCodecDataBase::PrefersLateDecoding() const { 520 if (!ptr_decoder_) 521 return true; 522 return ptr_decoder_->PrefersLateDecoding(); 523 } 524 525 bool VCMCodecDataBase::MatchesCurrentResolution(int width, int height) const { 526 return send_codec_.width == width && send_codec_.height == height; 527 } 528 529 VCMGenericDecoder* VCMCodecDataBase::CreateAndInitDecoder( 530 const VCMEncodedFrame& frame, 531 VideoCodec* new_codec) const { 532 uint8_t payload_type = frame.PayloadType(); 533 assert(new_codec); 534 const VCMDecoderMapItem* decoder_item = FindDecoderItem(payload_type); 535 if (!decoder_item) { 536 LOG(LS_ERROR) << "Can't find a decoder associated with payload type: " 537 << static_cast<int>(payload_type); 538 return nullptr; 539 } 540 VCMGenericDecoder* ptr_decoder = nullptr; 541 const VCMExtDecoderMapItem* external_dec_item = 542 FindExternalDecoderItem(payload_type); 543 if (external_dec_item) { 544 // External codec. 545 ptr_decoder = new VCMGenericDecoder( 546 external_dec_item->external_decoder_instance, true); 547 } else { 548 // Create decoder. 549 ptr_decoder = CreateDecoder(decoder_item->settings->codecType); 550 } 551 if (!ptr_decoder) 552 return nullptr; 553 554 // Copy over input resolutions to prevent codec reinitialization due to 555 // the first frame being of a different resolution than the database values. 556 // This is best effort, since there's no guarantee that width/height have been 557 // parsed yet (and may be zero). 558 if (frame.EncodedImage()._encodedWidth > 0 && 559 frame.EncodedImage()._encodedHeight > 0) { 560 decoder_item->settings->width = frame.EncodedImage()._encodedWidth; 561 decoder_item->settings->height = frame.EncodedImage()._encodedHeight; 562 } 563 if (ptr_decoder->InitDecode(decoder_item->settings.get(), 564 decoder_item->number_of_cores) < 0) { 565 ReleaseDecoder(ptr_decoder); 566 return nullptr; 567 } 568 memcpy(new_codec, decoder_item->settings.get(), sizeof(VideoCodec)); 569 return ptr_decoder; 570 } 571 572 void VCMCodecDataBase::DeleteEncoder() { 573 if (!ptr_encoder_) 574 return; 575 ptr_encoder_->Release(); 576 ptr_encoder_.reset(); 577 } 578 579 VCMGenericDecoder* VCMCodecDataBase::CreateDecoder(VideoCodecType type) const { 580 switch (type) { 581 case kVideoCodecVP8: 582 return new VCMGenericDecoder(VP8Decoder::Create()); 583 case kVideoCodecVP9: 584 return new VCMGenericDecoder(VP9Decoder::Create()); 585 case kVideoCodecI420: 586 return new VCMGenericDecoder(new I420Decoder()); 587 case kVideoCodecH264: 588 if (H264Decoder::IsSupported()) { 589 return new VCMGenericDecoder(H264Decoder::Create()); 590 } 591 break; 592 default: 593 break; 594 } 595 LOG(LS_WARNING) << "No internal decoder of this type exists."; 596 return nullptr; 597 } 598 599 const VCMDecoderMapItem* VCMCodecDataBase::FindDecoderItem( 600 uint8_t payload_type) const { 601 DecoderMap::const_iterator it = dec_map_.find(payload_type); 602 if (it != dec_map_.end()) { 603 return (*it).second; 604 } 605 return nullptr; 606 } 607 608 const VCMExtDecoderMapItem* VCMCodecDataBase::FindExternalDecoderItem( 609 uint8_t payload_type) const { 610 ExternalDecoderMap::const_iterator it = dec_external_map_.find(payload_type); 611 if (it != dec_external_map_.end()) { 612 return (*it).second; 613 } 614 return nullptr; 615 } 616 } // namespace webrtc 617