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/i420_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 I420BufferPool 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_.CreateBuffer(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