| /* | 
 |  *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "modules/video_coding/decoder_database.h" | 
 | #include "rtc_base/checks.h" | 
 | #include "rtc_base/logging.h" | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | VCMDecoderMapItem::VCMDecoderMapItem(VideoCodec* settings, | 
 |                                      int number_of_cores, | 
 |                                      bool require_key_frame) | 
 |     : settings(settings), | 
 |       number_of_cores(number_of_cores), | 
 |       require_key_frame(require_key_frame) { | 
 |   RTC_DCHECK_GE(number_of_cores, 0); | 
 | } | 
 |  | 
 | VCMExtDecoderMapItem::VCMExtDecoderMapItem( | 
 |     VideoDecoder* external_decoder_instance, | 
 |     uint8_t payload_type) | 
 |     : payload_type(payload_type), | 
 |       external_decoder_instance(external_decoder_instance) {} | 
 |  | 
 | VCMDecoderMapItem::~VCMDecoderMapItem() {} | 
 |  | 
 | VCMDecoderDataBase::VCMDecoderDataBase() | 
 |     : receive_codec_(), dec_map_(), dec_external_map_() {} | 
 |  | 
 | VCMDecoderDataBase::~VCMDecoderDataBase() { | 
 |   ptr_decoder_.reset(); | 
 |   for (auto& kv : dec_map_) | 
 |     delete kv.second; | 
 |   for (auto& kv : dec_external_map_) | 
 |     delete kv.second; | 
 | } | 
 |  | 
 | bool VCMDecoderDataBase::DeregisterExternalDecoder(uint8_t payload_type) { | 
 |   ExternalDecoderMap::iterator it = dec_external_map_.find(payload_type); | 
 |   if (it == dec_external_map_.end()) { | 
 |     // Not found. | 
 |     return false; | 
 |   } | 
 |   // We can't use payload_type to check if the decoder is currently in use, | 
 |   // because payload type may be out of date (e.g. before we decode the first | 
 |   // frame after RegisterReceiveCodec). | 
 |   if (ptr_decoder_ && | 
 |       ptr_decoder_->IsSameDecoder((*it).second->external_decoder_instance)) { | 
 |     // Release it if it was registered and in use. | 
 |     ptr_decoder_.reset(); | 
 |   } | 
 |   DeregisterReceiveCodec(payload_type); | 
 |   delete it->second; | 
 |   dec_external_map_.erase(it); | 
 |   return true; | 
 | } | 
 |  | 
 | // Add the external decoder object to the list of external decoders. | 
 | // Won't be registered as a receive codec until RegisterReceiveCodec is called. | 
 | void VCMDecoderDataBase::RegisterExternalDecoder(VideoDecoder* external_decoder, | 
 |                                                  uint8_t payload_type) { | 
 |   // If payload value already exists, erase old and insert new. | 
 |   VCMExtDecoderMapItem* ext_decoder = | 
 |       new VCMExtDecoderMapItem(external_decoder, payload_type); | 
 |   DeregisterExternalDecoder(payload_type); | 
 |   dec_external_map_[payload_type] = ext_decoder; | 
 | } | 
 |  | 
 | bool VCMDecoderDataBase::RegisterReceiveCodec(const VideoCodec* receive_codec, | 
 |                                               int number_of_cores, | 
 |                                               bool require_key_frame) { | 
 |   if (number_of_cores < 0) { | 
 |     return false; | 
 |   } | 
 |   // If payload value already exists, erase old and insert new. | 
 |   DeregisterReceiveCodec(receive_codec->plType); | 
 |   VideoCodec* new_receive_codec = new VideoCodec(*receive_codec); | 
 |   dec_map_[receive_codec->plType] = new VCMDecoderMapItem( | 
 |       new_receive_codec, number_of_cores, require_key_frame); | 
 |   return true; | 
 | } | 
 |  | 
 | bool VCMDecoderDataBase::DeregisterReceiveCodec(uint8_t payload_type) { | 
 |   DecoderMap::iterator it = dec_map_.find(payload_type); | 
 |   if (it == dec_map_.end()) { | 
 |     return false; | 
 |   } | 
 |   delete it->second; | 
 |   dec_map_.erase(it); | 
 |   if (receive_codec_.plType == payload_type) { | 
 |     // This codec is currently in use. | 
 |     memset(&receive_codec_, 0, sizeof(VideoCodec)); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | VCMGenericDecoder* VCMDecoderDataBase::GetDecoder( | 
 |     const VCMEncodedFrame& frame, | 
 |     VCMDecodedFrameCallback* decoded_frame_callback) { | 
 |   RTC_DCHECK(decoded_frame_callback->UserReceiveCallback()); | 
 |   uint8_t payload_type = frame.PayloadType(); | 
 |   if (payload_type == receive_codec_.plType || payload_type == 0) { | 
 |     return ptr_decoder_.get(); | 
 |   } | 
 |   // If decoder exists - delete. | 
 |   if (ptr_decoder_) { | 
 |     ptr_decoder_.reset(); | 
 |     memset(&receive_codec_, 0, sizeof(VideoCodec)); | 
 |   } | 
 |   ptr_decoder_ = CreateAndInitDecoder(frame, &receive_codec_); | 
 |   if (!ptr_decoder_) { | 
 |     return nullptr; | 
 |   } | 
 |   VCMReceiveCallback* callback = decoded_frame_callback->UserReceiveCallback(); | 
 |   callback->OnIncomingPayloadType(receive_codec_.plType); | 
 |   if (ptr_decoder_->RegisterDecodeCompleteCallback(decoded_frame_callback) < | 
 |       0) { | 
 |     ptr_decoder_.reset(); | 
 |     memset(&receive_codec_, 0, sizeof(VideoCodec)); | 
 |     return nullptr; | 
 |   } | 
 |   return ptr_decoder_.get(); | 
 | } | 
 |  | 
 | bool VCMDecoderDataBase::PrefersLateDecoding() const { | 
 |   return ptr_decoder_ ? ptr_decoder_->PrefersLateDecoding() : true; | 
 | } | 
 |  | 
 | std::unique_ptr<VCMGenericDecoder> VCMDecoderDataBase::CreateAndInitDecoder( | 
 |     const VCMEncodedFrame& frame, | 
 |     VideoCodec* new_codec) const { | 
 |   uint8_t payload_type = frame.PayloadType(); | 
 |   RTC_LOG(LS_INFO) << "Initializing decoder with payload type '" | 
 |                    << static_cast<int>(payload_type) << "'."; | 
 |   RTC_DCHECK(new_codec); | 
 |   const VCMDecoderMapItem* decoder_item = FindDecoderItem(payload_type); | 
 |   if (!decoder_item) { | 
 |     RTC_LOG(LS_ERROR) << "Can't find a decoder associated with payload type: " | 
 |                       << static_cast<int>(payload_type); | 
 |     return nullptr; | 
 |   } | 
 |   std::unique_ptr<VCMGenericDecoder> ptr_decoder; | 
 |   const VCMExtDecoderMapItem* external_dec_item = | 
 |       FindExternalDecoderItem(payload_type); | 
 |   if (external_dec_item) { | 
 |     // External codec. | 
 |     ptr_decoder.reset(new VCMGenericDecoder( | 
 |         external_dec_item->external_decoder_instance, true)); | 
 |   } else { | 
 |     RTC_LOG(LS_ERROR) << "No decoder of this type exists."; | 
 |   } | 
 |   if (!ptr_decoder) | 
 |     return nullptr; | 
 |  | 
 |   // Copy over input resolutions to prevent codec reinitialization due to | 
 |   // the first frame being of a different resolution than the database values. | 
 |   // This is best effort, since there's no guarantee that width/height have been | 
 |   // parsed yet (and may be zero). | 
 |   if (frame.EncodedImage()._encodedWidth > 0 && | 
 |       frame.EncodedImage()._encodedHeight > 0) { | 
 |     decoder_item->settings->width = frame.EncodedImage()._encodedWidth; | 
 |     decoder_item->settings->height = frame.EncodedImage()._encodedHeight; | 
 |   } | 
 |   if (ptr_decoder->InitDecode(decoder_item->settings.get(), | 
 |                               decoder_item->number_of_cores) < 0) { | 
 |     return nullptr; | 
 |   } | 
 |   memcpy(new_codec, decoder_item->settings.get(), sizeof(VideoCodec)); | 
 |   return ptr_decoder; | 
 | } | 
 |  | 
 | const VCMDecoderMapItem* VCMDecoderDataBase::FindDecoderItem( | 
 |     uint8_t payload_type) const { | 
 |   DecoderMap::const_iterator it = dec_map_.find(payload_type); | 
 |   if (it != dec_map_.end()) { | 
 |     return (*it).second; | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | const VCMExtDecoderMapItem* VCMDecoderDataBase::FindExternalDecoderItem( | 
 |     uint8_t payload_type) const { | 
 |   ExternalDecoderMap::const_iterator it = dec_external_map_.find(payload_type); | 
 |   if (it != dec_external_map_.end()) { | 
 |     return (*it).second; | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | }  // namespace webrtc |