/* * Copyright (c) 2017 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/codecs/stereo/include/stereo_decoder_adapter.h" #include "api/video/i420_buffer.h" #include "api/video_codecs/sdp_video_format.h" #include "common_video/include/video_frame.h" #include "common_video/include/video_frame_buffer.h" #include "common_video/libyuv/include/webrtc_libyuv.h" #include "rtc_base/keep_ref_until_done.h" #include "rtc_base/logging.h" namespace webrtc { class StereoDecoderAdapter::AdapterDecodedImageCallback : public webrtc::DecodedImageCallback { public: AdapterDecodedImageCallback(webrtc::StereoDecoderAdapter* adapter, AlphaCodecStream stream_idx) : adapter_(adapter), stream_idx_(stream_idx) {} void Decoded(VideoFrame& decodedImage, rtc::Optional decode_time_ms, rtc::Optional qp) override { if (!adapter_) return; adapter_->Decoded(stream_idx_, &decodedImage, decode_time_ms, qp); } int32_t Decoded(VideoFrame& decodedImage) override { RTC_NOTREACHED(); return WEBRTC_VIDEO_CODEC_OK; } int32_t Decoded(VideoFrame& decodedImage, int64_t decode_time_ms) override { RTC_NOTREACHED(); return WEBRTC_VIDEO_CODEC_OK; } private: StereoDecoderAdapter* adapter_; const AlphaCodecStream stream_idx_; }; struct StereoDecoderAdapter::DecodedImageData { explicit DecodedImageData(AlphaCodecStream stream_idx) : stream_idx_(stream_idx), decodedImage_(I420Buffer::Create(1 /* width */, 1 /* height */), 0, 0, kVideoRotation_0) { RTC_DCHECK_EQ(kAXXStream, stream_idx); } DecodedImageData(AlphaCodecStream stream_idx, const VideoFrame& decodedImage, const rtc::Optional& decode_time_ms, const rtc::Optional& qp) : stream_idx_(stream_idx), decodedImage_(decodedImage), decode_time_ms_(decode_time_ms), qp_(qp) {} const AlphaCodecStream stream_idx_; VideoFrame decodedImage_; const rtc::Optional decode_time_ms_; const rtc::Optional qp_; private: RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(DecodedImageData); }; StereoDecoderAdapter::StereoDecoderAdapter(VideoDecoderFactory* factory) : factory_(factory) {} StereoDecoderAdapter::~StereoDecoderAdapter() { Release(); } int32_t StereoDecoderAdapter::InitDecode(const VideoCodec* codec_settings, int32_t number_of_cores) { VideoCodec settings = *codec_settings; settings.codecType = kVideoCodecVP9; for (size_t i = 0; i < kAlphaCodecStreams; ++i) { const SdpVideoFormat format("VP9"); std::unique_ptr decoder = factory_->CreateVideoDecoder(format); const int32_t rv = decoder->InitDecode(&settings, number_of_cores); if (rv) return rv; adapter_callbacks_.emplace_back( new StereoDecoderAdapter::AdapterDecodedImageCallback( this, static_cast(i))); decoder->RegisterDecodeCompleteCallback(adapter_callbacks_.back().get()); decoders_.emplace_back(std::move(decoder)); } return WEBRTC_VIDEO_CODEC_OK; } int32_t StereoDecoderAdapter::Decode( const EncodedImage& input_image, bool missing_frames, const RTPFragmentationHeader* /*fragmentation*/, const CodecSpecificInfo* codec_specific_info, int64_t render_time_ms) { // TODO(emircan): Read |codec_specific_info->stereoInfo| to split frames. int32_t rv = decoders_[kYUVStream]->Decode(input_image, missing_frames, nullptr, codec_specific_info, render_time_ms); if (rv) return rv; rv = decoders_[kAXXStream]->Decode(input_image, missing_frames, nullptr, codec_specific_info, render_time_ms); return rv; } int32_t StereoDecoderAdapter::RegisterDecodeCompleteCallback( DecodedImageCallback* callback) { decoded_complete_callback_ = callback; return WEBRTC_VIDEO_CODEC_OK; } int32_t StereoDecoderAdapter::Release() { for (auto& decoder : decoders_) { const int32_t rv = decoder->Release(); if (rv) return rv; } decoders_.clear(); adapter_callbacks_.clear(); return WEBRTC_VIDEO_CODEC_OK; } void StereoDecoderAdapter::Decoded(AlphaCodecStream stream_idx, VideoFrame* decoded_image, rtc::Optional decode_time_ms, rtc::Optional qp) { const auto& other_decoded_data_it = decoded_data_.find(decoded_image->timestamp()); if (other_decoded_data_it != decoded_data_.end()) { auto& other_image_data = other_decoded_data_it->second; if (stream_idx == kYUVStream) { RTC_DCHECK_EQ(kAXXStream, other_image_data.stream_idx_); MergeAlphaImages(decoded_image, decode_time_ms, qp, &other_image_data.decodedImage_, other_image_data.decode_time_ms_, other_image_data.qp_); } else { RTC_DCHECK_EQ(kYUVStream, other_image_data.stream_idx_); RTC_DCHECK_EQ(kAXXStream, stream_idx); MergeAlphaImages(&other_image_data.decodedImage_, other_image_data.decode_time_ms_, other_image_data.qp_, decoded_image, decode_time_ms, qp); } decoded_data_.erase(decoded_data_.begin(), other_decoded_data_it); return; } RTC_DCHECK(decoded_data_.find(decoded_image->timestamp()) == decoded_data_.end()); decoded_data_.emplace( std::piecewise_construct, std::forward_as_tuple(decoded_image->timestamp()), std::forward_as_tuple(stream_idx, *decoded_image, decode_time_ms, qp)); } void StereoDecoderAdapter::MergeAlphaImages( VideoFrame* decodedImage, const rtc::Optional& decode_time_ms, const rtc::Optional& qp, VideoFrame* alpha_decodedImage, const rtc::Optional& alpha_decode_time_ms, const rtc::Optional& alpha_qp) { // TODO(emircan): Merge the output and put in a VideoFrame container that can // transport I420A. decoded_complete_callback_->Decoded(*decodedImage, decode_time_ms, qp); decoded_complete_callback_->Decoded(*alpha_decodedImage, alpha_decode_time_ms, alpha_qp); } } // namespace webrtc