1 /*
2  *  Copyright (c) 2020 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 #include "modules/video_coding/codecs/av1/libaom_av1_decoder.h"
11 
12 #include <stdint.h>
13 
14 #include <memory>
15 
16 #include "absl/types/optional.h"
17 #include "api/scoped_refptr.h"
18 #include "api/video/encoded_image.h"
19 #include "api/video/i420_buffer.h"
20 #include "api/video_codecs/video_codec.h"
21 #include "api/video_codecs/video_decoder.h"
22 #include "common_video/include/video_frame_buffer_pool.h"
23 #include "modules/video_coding/include/video_error_codes.h"
24 #include "rtc_base/logging.h"
25 #include "third_party/libaom/source/libaom/aom/aom_decoder.h"
26 #include "third_party/libaom/source/libaom/aom/aomdx.h"
27 #include "third_party/libyuv/include/libyuv/convert.h"
28 
29 namespace webrtc {
30 namespace {
31 
32 constexpr int kConfigLowBitDepth = 1;  // 8-bits per luma/chroma sample.
33 constexpr int kDecFlags = 0;           // 0 signals no post processing.
34 
35 class LibaomAv1Decoder final : public VideoDecoder {
36  public:
37   LibaomAv1Decoder();
38   LibaomAv1Decoder(const LibaomAv1Decoder&) = delete;
39   LibaomAv1Decoder& operator=(const LibaomAv1Decoder&) = delete;
40   ~LibaomAv1Decoder();
41 
42   // Implements VideoDecoder.
43   int32_t InitDecode(const VideoCodec* codec_settings,
44                      int number_of_cores) override;
45 
46   // Decode an encoded video frame.
47   int32_t Decode(const EncodedImage& encoded_image,
48                  bool missing_frames,
49                  int64_t render_time_ms) override;
50 
51   int32_t RegisterDecodeCompleteCallback(
52       DecodedImageCallback* callback) override;
53 
54   int32_t Release() override;
55 
56   const char* ImplementationName() const override;
57 
58  private:
59   aom_codec_ctx_t context_;
60   bool inited_;
61   // Pool of memory buffers to store decoded image data for application access.
62   VideoFrameBufferPool buffer_pool_;
63   DecodedImageCallback* decode_complete_callback_;
64 };
65 
LibaomAv1Decoder()66 LibaomAv1Decoder::LibaomAv1Decoder()
67     : context_(),  // Force value initialization instead of default one.
68       inited_(false),
69       buffer_pool_(false, /*max_number_of_buffers=*/150),
70       decode_complete_callback_(nullptr) {}
71 
~LibaomAv1Decoder()72 LibaomAv1Decoder::~LibaomAv1Decoder() {
73   Release();
74 }
75 
InitDecode(const VideoCodec * codec_settings,int number_of_cores)76 int32_t LibaomAv1Decoder::InitDecode(const VideoCodec* codec_settings,
77                                      int number_of_cores) {
78   aom_codec_dec_cfg_t config = {
79       static_cast<unsigned int>(number_of_cores),  // Max # of threads.
80       0,                    // Frame width set after decode.
81       0,                    // Frame height set after decode.
82       kConfigLowBitDepth};  // Enable low-bit-depth code path.
83 
84   aom_codec_err_t ret =
85       aom_codec_dec_init(&context_, aom_codec_av1_dx(), &config, kDecFlags);
86   if (ret != AOM_CODEC_OK) {
87     RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::InitDecode returned " << ret
88                         << " on aom_codec_dec_init.";
89     return WEBRTC_VIDEO_CODEC_ERROR;
90   }
91   inited_ = true;
92   return WEBRTC_VIDEO_CODEC_OK;
93 }
94 
Decode(const EncodedImage & encoded_image,bool missing_frames,int64_t)95 int32_t LibaomAv1Decoder::Decode(const EncodedImage& encoded_image,
96                                  bool missing_frames,
97                                  int64_t /*render_time_ms*/) {
98   if (!inited_) {
99     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
100   }
101   if (decode_complete_callback_ == nullptr) {
102     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
103   }
104 
105   // Decode one video frame.
106   aom_codec_err_t ret =
107       aom_codec_decode(&context_, encoded_image.data(), encoded_image.size(),
108                        /*user_priv=*/nullptr);
109   if (ret != AOM_CODEC_OK) {
110     RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode returned " << ret
111                         << " on aom_codec_decode.";
112     return WEBRTC_VIDEO_CODEC_ERROR;
113   }
114 
115   // Get decoded frame data.
116   int corrupted_frame = 0;
117   aom_codec_iter_t iter = nullptr;
118   while (aom_image_t* decoded_image = aom_codec_get_frame(&context_, &iter)) {
119     if (aom_codec_control(&context_, AOMD_GET_FRAME_CORRUPTED,
120                           &corrupted_frame)) {
121       RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode "
122                              "AOM_GET_FRAME_CORRUPTED.";
123     }
124     // Check that decoded image format is I420 and has 8-bit depth.
125     if (decoded_image->fmt != AOM_IMG_FMT_I420) {
126       RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode invalid image format";
127       return WEBRTC_VIDEO_CODEC_ERROR;
128     }
129 
130     // Return decoded frame data.
131     int qp;
132     ret = aom_codec_control(&context_, AOMD_GET_LAST_QUANTIZER, &qp);
133     if (ret != AOM_CODEC_OK) {
134       RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode returned " << ret
135                           << " on control AOME_GET_LAST_QUANTIZER.";
136       return WEBRTC_VIDEO_CODEC_ERROR;
137     }
138 
139     // Allocate memory for decoded frame.
140     rtc::scoped_refptr<I420Buffer> buffer =
141         buffer_pool_.CreateI420Buffer(decoded_image->d_w, decoded_image->d_h);
142     if (!buffer.get()) {
143       // Pool has too many pending frames.
144       RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode returned due to lack of"
145                              " space in decoded frame buffer pool.";
146       return WEBRTC_VIDEO_CODEC_ERROR;
147     }
148 
149     // Copy decoded_image to decoded_frame.
150     libyuv::I420Copy(
151         decoded_image->planes[AOM_PLANE_Y], decoded_image->stride[AOM_PLANE_Y],
152         decoded_image->planes[AOM_PLANE_U], decoded_image->stride[AOM_PLANE_U],
153         decoded_image->planes[AOM_PLANE_V], decoded_image->stride[AOM_PLANE_V],
154         buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataU(),
155         buffer->StrideU(), buffer->MutableDataV(), buffer->StrideV(),
156         decoded_image->d_w, decoded_image->d_h);
157     VideoFrame decoded_frame = VideoFrame::Builder()
158                                    .set_video_frame_buffer(buffer)
159                                    .set_timestamp_rtp(encoded_image.Timestamp())
160                                    .set_ntp_time_ms(encoded_image.ntp_time_ms_)
161                                    .set_color_space(encoded_image.ColorSpace())
162                                    .build();
163 
164     decode_complete_callback_->Decoded(decoded_frame, absl::nullopt,
165                                        absl::nullopt);
166   }
167   return WEBRTC_VIDEO_CODEC_OK;
168 }
169 
RegisterDecodeCompleteCallback(DecodedImageCallback * decode_complete_callback)170 int32_t LibaomAv1Decoder::RegisterDecodeCompleteCallback(
171     DecodedImageCallback* decode_complete_callback) {
172   decode_complete_callback_ = decode_complete_callback;
173   return WEBRTC_VIDEO_CODEC_OK;
174 }
175 
Release()176 int32_t LibaomAv1Decoder::Release() {
177   if (aom_codec_destroy(&context_) != AOM_CODEC_OK) {
178     return WEBRTC_VIDEO_CODEC_MEMORY;
179   }
180   buffer_pool_.Release();
181   inited_ = false;
182   return WEBRTC_VIDEO_CODEC_OK;
183 }
184 
ImplementationName() const185 const char* LibaomAv1Decoder::ImplementationName() const {
186   return "libaom";
187 }
188 
189 }  // namespace
190 
191 const bool kIsLibaomAv1DecoderSupported = true;
192 
CreateLibaomAv1Decoder()193 std::unique_ptr<VideoDecoder> CreateLibaomAv1Decoder() {
194   return std::make_unique<LibaomAv1Decoder>();
195 }
196 
197 }  // namespace webrtc
198