|  | /* | 
|  | *  Copyright (c) 2021 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/av1/dav1d_decoder.h" | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #include "api/scoped_refptr.h" | 
|  | #include "api/video/encoded_image.h" | 
|  | #include "api/video/video_frame_buffer.h" | 
|  | #include "common_video/include/video_frame_buffer.h" | 
|  | #include "modules/video_coding/include/video_error_codes.h" | 
|  | #include "rtc_base/logging.h" | 
|  | #include "third_party/dav1d/libdav1d/include/dav1d/dav1d.h" | 
|  | #include "third_party/libyuv/include/libyuv/convert.h" | 
|  | #include "third_party/libyuv/include/libyuv/planar_functions.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace { | 
|  |  | 
|  | class Dav1dDecoder : public VideoDecoder { | 
|  | public: | 
|  | Dav1dDecoder(); | 
|  | Dav1dDecoder(const Dav1dDecoder&) = delete; | 
|  | Dav1dDecoder& operator=(const Dav1dDecoder&) = delete; | 
|  |  | 
|  | ~Dav1dDecoder() override; | 
|  |  | 
|  | bool Configure(const Settings& settings) override; | 
|  | int32_t Decode(const EncodedImage& encoded_image, | 
|  | bool missing_frames, | 
|  | int64_t render_time_ms) override; | 
|  | int32_t RegisterDecodeCompleteCallback( | 
|  | DecodedImageCallback* callback) override; | 
|  | int32_t Release() override; | 
|  | DecoderInfo GetDecoderInfo() const override; | 
|  | const char* ImplementationName() const override; | 
|  |  | 
|  | private: | 
|  | Dav1dContext* context_ = nullptr; | 
|  | DecodedImageCallback* decode_complete_callback_ = nullptr; | 
|  | }; | 
|  |  | 
|  | class ScopedDav1dData { | 
|  | public: | 
|  | ~ScopedDav1dData() { dav1d_data_unref(&data_); } | 
|  |  | 
|  | Dav1dData& Data() { return data_; } | 
|  |  | 
|  | private: | 
|  | Dav1dData data_ = {}; | 
|  | }; | 
|  |  | 
|  | class ScopedDav1dPicture | 
|  | : public rtc::RefCountedNonVirtual<ScopedDav1dPicture> { | 
|  | public: | 
|  | ~ScopedDav1dPicture() { dav1d_picture_unref(&picture_); } | 
|  |  | 
|  | Dav1dPicture& Picture() { return picture_; } | 
|  | using rtc::RefCountedNonVirtual<ScopedDav1dPicture>::HasOneRef; | 
|  |  | 
|  | private: | 
|  | Dav1dPicture picture_ = {}; | 
|  | }; | 
|  |  | 
|  | constexpr char kDav1dName[] = "dav1d"; | 
|  |  | 
|  | // Calling `dav1d_data_wrap` requires a `free_callback` to be registered. | 
|  | void NullFreeCallback(const uint8_t* buffer, void* opaque) {} | 
|  |  | 
|  | Dav1dDecoder::Dav1dDecoder() = default; | 
|  |  | 
|  | Dav1dDecoder::~Dav1dDecoder() { | 
|  | Release(); | 
|  | } | 
|  |  | 
|  | bool Dav1dDecoder::Configure(const Settings& settings) { | 
|  | Dav1dSettings s; | 
|  | dav1d_default_settings(&s); | 
|  |  | 
|  | s.n_threads = std::max(2, settings.number_of_cores()); | 
|  | s.max_frame_delay = 1;   // For low latency decoding. | 
|  | s.all_layers = 0;        // Don't output a frame for every spatial layer. | 
|  | s.operating_point = 31;  // Decode all operating points. | 
|  |  | 
|  | return dav1d_open(&context_, &s) == 0; | 
|  | } | 
|  |  | 
|  | int32_t Dav1dDecoder::RegisterDecodeCompleteCallback( | 
|  | DecodedImageCallback* decode_complete_callback) { | 
|  | decode_complete_callback_ = decode_complete_callback; | 
|  | return WEBRTC_VIDEO_CODEC_OK; | 
|  | } | 
|  |  | 
|  | int32_t Dav1dDecoder::Release() { | 
|  | dav1d_close(&context_); | 
|  | if (context_ != nullptr) { | 
|  | return WEBRTC_VIDEO_CODEC_MEMORY; | 
|  | } | 
|  | return WEBRTC_VIDEO_CODEC_OK; | 
|  | } | 
|  |  | 
|  | VideoDecoder::DecoderInfo Dav1dDecoder::GetDecoderInfo() const { | 
|  | DecoderInfo info; | 
|  | info.implementation_name = kDav1dName; | 
|  | info.is_hardware_accelerated = false; | 
|  | return info; | 
|  | } | 
|  |  | 
|  | const char* Dav1dDecoder::ImplementationName() const { | 
|  | return kDav1dName; | 
|  | } | 
|  |  | 
|  | int32_t Dav1dDecoder::Decode(const EncodedImage& encoded_image, | 
|  | bool /*missing_frames*/, | 
|  | int64_t /*render_time_ms*/) { | 
|  | if (!context_ || decode_complete_callback_ == nullptr) { | 
|  | return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 
|  | } | 
|  |  | 
|  | ScopedDav1dData scoped_dav1d_data; | 
|  | Dav1dData& dav1d_data = scoped_dav1d_data.Data(); | 
|  | dav1d_data_wrap(&dav1d_data, encoded_image.data(), encoded_image.size(), | 
|  | /*free_callback=*/&NullFreeCallback, | 
|  | /*user_data=*/nullptr); | 
|  |  | 
|  | if (int decode_res = dav1d_send_data(context_, &dav1d_data)) { | 
|  | RTC_LOG(LS_WARNING) | 
|  | << "Dav1dDecoder::Decode decoding failed with error code " | 
|  | << decode_res; | 
|  | return WEBRTC_VIDEO_CODEC_ERROR; | 
|  | } | 
|  |  | 
|  | rtc::scoped_refptr<ScopedDav1dPicture> scoped_dav1d_picture( | 
|  | new ScopedDav1dPicture{}); | 
|  | Dav1dPicture& dav1d_picture = scoped_dav1d_picture->Picture(); | 
|  | if (int get_picture_res = dav1d_get_picture(context_, &dav1d_picture)) { | 
|  | RTC_LOG(LS_WARNING) | 
|  | << "Dav1dDecoder::Decode getting picture failed with error code " | 
|  | << get_picture_res; | 
|  | return WEBRTC_VIDEO_CODEC_ERROR; | 
|  | } | 
|  |  | 
|  | if (dav1d_picture.p.bpc != 8) { | 
|  | // Only accept 8 bit depth. | 
|  | RTC_LOG(LS_ERROR) << "Dav1dDecoder::Decode unhandled bit depth: " | 
|  | << dav1d_picture.p.bpc; | 
|  | return WEBRTC_VIDEO_CODEC_ERROR; | 
|  | } | 
|  |  | 
|  | rtc::scoped_refptr<VideoFrameBuffer> wrapped_buffer; | 
|  | if (dav1d_picture.p.layout == DAV1D_PIXEL_LAYOUT_I420) { | 
|  | wrapped_buffer = WrapI420Buffer( | 
|  | dav1d_picture.p.w, dav1d_picture.p.h, | 
|  | static_cast<uint8_t*>(dav1d_picture.data[0]), dav1d_picture.stride[0], | 
|  | static_cast<uint8_t*>(dav1d_picture.data[1]), dav1d_picture.stride[1], | 
|  | static_cast<uint8_t*>(dav1d_picture.data[2]), dav1d_picture.stride[1], | 
|  | // To keep |scoped_dav1d_picture.Picture()| alive | 
|  | [scoped_dav1d_picture] {}); | 
|  | } else if (dav1d_picture.p.layout == DAV1D_PIXEL_LAYOUT_I444) { | 
|  | wrapped_buffer = WrapI444Buffer( | 
|  | dav1d_picture.p.w, dav1d_picture.p.h, | 
|  | static_cast<uint8_t*>(dav1d_picture.data[0]), dav1d_picture.stride[0], | 
|  | static_cast<uint8_t*>(dav1d_picture.data[1]), dav1d_picture.stride[1], | 
|  | static_cast<uint8_t*>(dav1d_picture.data[2]), dav1d_picture.stride[1], | 
|  | // To keep |scoped_dav1d_picture.Picture()| alive | 
|  | [scoped_dav1d_picture] {}); | 
|  | } else { | 
|  | // Only accept I420 or I444 pixel format. | 
|  | RTC_LOG(LS_ERROR) << "Dav1dDecoder::Decode unhandled pixel layout: " | 
|  | << dav1d_picture.p.layout; | 
|  | return WEBRTC_VIDEO_CODEC_ERROR; | 
|  | } | 
|  |  | 
|  | if (!wrapped_buffer.get()) { | 
|  | return WEBRTC_VIDEO_CODEC_ERROR; | 
|  | } | 
|  |  | 
|  | VideoFrame decoded_frame = VideoFrame::Builder() | 
|  | .set_video_frame_buffer(wrapped_buffer) | 
|  | .set_timestamp_rtp(encoded_image.Timestamp()) | 
|  | .set_ntp_time_ms(encoded_image.ntp_time_ms_) | 
|  | .set_color_space(encoded_image.ColorSpace()) | 
|  | .build(); | 
|  |  | 
|  | decode_complete_callback_->Decoded(decoded_frame, absl::nullopt, | 
|  | absl::nullopt); | 
|  |  | 
|  | return WEBRTC_VIDEO_CODEC_OK; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | std::unique_ptr<VideoDecoder> CreateDav1dDecoder() { | 
|  | return std::make_unique<Dav1dDecoder>(); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |