diff --git a/webrtc/modules/video_coding/main/interface/video_coding_defines.h b/webrtc/modules/video_coding/main/interface/video_coding_defines.h index 43398f4935..d4d1e9e5a4 100644 --- a/webrtc/modules/video_coding/main/interface/video_coding_defines.h +++ b/webrtc/modules/video_coding/main/interface/video_coding_defines.h @@ -98,6 +98,8 @@ class VCMReceiveCallback { const uint64_t pictureId) { return -1; } + // Called when the current receive codec changes. + virtual void IncomingCodecChanged(const VideoCodec& codec) {} protected: virtual ~VCMReceiveCallback() { diff --git a/webrtc/modules/video_coding/main/source/codec_database.cc b/webrtc/modules/video_coding/main/source/codec_database.cc index cf7b5072fa..6f50ddddc6 100644 --- a/webrtc/modules/video_coding/main/source/codec_database.cc +++ b/webrtc/modules/video_coding/main/source/codec_database.cc @@ -510,6 +510,8 @@ VCMGenericDecoder* VCMCodecDataBase::GetDecoder( if (!ptr_decoder_) { return NULL; } + VCMReceiveCallback* callback = decoded_frame_callback->UserReceiveCallback(); + if (callback) callback->IncomingCodecChanged(receive_codec_); if (ptr_decoder_->RegisterDecodeCompleteCallback(decoded_frame_callback) < 0) { ReleaseDecoder(ptr_decoder_); diff --git a/webrtc/modules/video_coding/main/source/generic_decoder.cc b/webrtc/modules/video_coding/main/source/generic_decoder.cc index afc4b1aa11..12464d18d2 100644 --- a/webrtc/modules/video_coding/main/source/generic_decoder.cc +++ b/webrtc/modules/video_coding/main/source/generic_decoder.cc @@ -41,6 +41,12 @@ void VCMDecodedFrameCallback::SetUserReceiveCallback( _receiveCallback = receiveCallback; } +VCMReceiveCallback* VCMDecodedFrameCallback::UserReceiveCallback() +{ + CriticalSectionScoped cs(_critSect); + return _receiveCallback; +} + int32_t VCMDecodedFrameCallback::Decoded(I420VideoFrame& decodedImage) { // TODO(holmer): We should improve this so that we can handle multiple diff --git a/webrtc/modules/video_coding/main/source/generic_decoder.h b/webrtc/modules/video_coding/main/source/generic_decoder.h index 04e6cf4835..7144a099f9 100644 --- a/webrtc/modules/video_coding/main/source/generic_decoder.h +++ b/webrtc/modules/video_coding/main/source/generic_decoder.h @@ -37,6 +37,7 @@ public: VCMDecodedFrameCallback(VCMTiming& timing, Clock* clock); virtual ~VCMDecodedFrameCallback(); void SetUserReceiveCallback(VCMReceiveCallback* receiveCallback); + VCMReceiveCallback* UserReceiveCallback(); virtual int32_t Decoded(I420VideoFrame& decodedImage); virtual int32_t ReceivedDecodedReferenceFrame(const uint64_t pictureId); diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc index 874db7c4c6..2b7649c14b 100644 --- a/webrtc/video_engine/vie_channel.cc +++ b/webrtc/video_engine/vie_channel.cc @@ -1665,6 +1665,10 @@ CallStatsObserver* ViEChannel::GetStatsObserver() { return stats_observer_.get(); } +// Do not acquire the lock of |vcm_| in this function. Decode callback won't +// necessarily be called from the decoding thread. The decoding thread may have +// held the lock when calling VideoDecoder::Decode, Reset, or Release. Acquiring +// the same lock in the path of decode callback can deadlock. int32_t ViEChannel::FrameToRender( I420VideoFrame& video_frame) { // NOLINT CriticalSectionScoped cs(callback_cs_.get()); @@ -1672,20 +1676,11 @@ int32_t ViEChannel::FrameToRender( if (decoder_reset_) { // Trigger a callback to the user if the incoming codec has changed. if (codec_observer_) { - VideoCodec decoder; - memset(&decoder, 0, sizeof(decoder)); - if (vcm_.ReceiveCodec(&decoder) == VCM_OK) { - // VCM::ReceiveCodec returns the codec set by - // RegisterReceiveCodec, which might not be the size we're - // actually decoding. - decoder.width = static_cast(video_frame.width()); - decoder.height = static_cast(video_frame.height()); - codec_observer_->IncomingCodecChanged(channel_id_, decoder); - } else { - assert(false); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), - "%s: Could not get receive codec", __FUNCTION__); - } + // The codec set by RegisterReceiveCodec might not be the size we're + // actually decoding. + receive_codec_.width = static_cast(video_frame.width()); + receive_codec_.height = static_cast(video_frame.height()); + codec_observer_->IncomingCodecChanged(channel_id_, receive_codec_); } decoder_reset_ = false; } @@ -1723,6 +1718,11 @@ int32_t ViEChannel::ReceivedDecodedReferenceFrame( return rtp_rtcp_->SendRTCPReferencePictureSelection(picture_id); } +void ViEChannel::IncomingCodecChanged(const VideoCodec& codec) { + CriticalSectionScoped cs(callback_cs_.get()); + receive_codec_ = codec; +} + int32_t ViEChannel::StoreReceivedFrame( const EncodedVideoData& frame_to_store) { return 0; @@ -1949,9 +1949,8 @@ int32_t ViEChannel::OnInitializeDecoder( payload_type, payload_name); vcm_.ResetDecoder(); - callback_cs_->Enter(); + CriticalSectionScoped cs(callback_cs_.get()); decoder_reset_ = true; - callback_cs_->Leave(); return 0; } diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h index 090582b9cf..31c7b8f800 100644 --- a/webrtc/video_engine/vie_channel.h +++ b/webrtc/video_engine/vie_channel.h @@ -290,6 +290,9 @@ class ViEChannel virtual int32_t ReceivedDecodedReferenceFrame( const uint64_t picture_id); + // Implements VCMReceiveCallback. + virtual void IncomingCodecChanged(const VideoCodec& codec); + // Implements VCM. virtual int32_t StoreReceivedFrame( const EncodedVideoData& frame_to_store); @@ -383,6 +386,8 @@ class ViEChannel Transport* external_transport_; bool decoder_reset_; + // Current receive codec used for codec change callback. + VideoCodec receive_codec_; bool wait_for_key_frame_; ThreadWrapper* decode_thread_;