Add Av1 Decoder wrapper behind a build flag
Bug: webrtc:11404
Change-Id: I090ffd173d667e8845de1b986af462516b7c76e6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/169452
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Michael Horowitz <mhoro@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30757}
diff --git a/.gn b/.gn
index 09f1b65..59dd7d6 100644
--- a/.gn
+++ b/.gn
@@ -72,5 +72,7 @@
# for unittests, it can be disabled (see third_party/libyuv/BUILD.gn)
libyuv_use_gflags = false
+ enable_libaom_decoder = true
+
gtest_enable_absl_printers = true
}
diff --git a/DEPS b/DEPS
index a8bd168..c22ac35 100644
--- a/DEPS
+++ b/DEPS
@@ -1,5 +1,8 @@
# This file contains dependencies for WebRTC.
+gclient_gn_args_file = 'src/build/config/gclient_args.gni'
+gclient_gn_args = []
+
vars = {
# By default, we should check out everything needed to run on the main
# chromium waterfalls. More info at: crbug.com/570091.
@@ -192,6 +195,8 @@
'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@ce0e57e8e636f5132fe6f0590a4dba91f92fd935',
'src/third_party/libsrtp':
'https://chromium.googlesource.com/chromium/deps/libsrtp.git@650611720ecc23e0e6b32b0e3100f8b4df91696c',
+ 'src/third_party/libaom/source/libaom':
+ 'https://aomedia.googlesource.com/aom.git@f83d681765cd2aefa9a70ce771af48edd1dbf416',
'src/third_party/libvpx/source/libvpx':
'https://chromium.googlesource.com/webm/libvpx.git@5532775efe808cb0942e7b99bf2f232c6ce99fee',
'src/third_party/libyuv':
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 8c2d8c0..3b45cf2 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -246,6 +246,7 @@
"../modules/video_coding:webrtc_multiplex",
"../modules/video_coding:webrtc_vp8",
"../modules/video_coding:webrtc_vp9",
+ "../modules/video_coding/codecs/av1:libaom_av1_decoder",
"../rtc_base:checks",
"../rtc_base:deprecation",
"../rtc_base:rtc_base_approved",
@@ -573,6 +574,7 @@
"../modules/video_coding:simulcast_test_fixture_impl",
"../modules/video_coding:video_codec_interface",
"../modules/video_coding:webrtc_vp8",
+ "../modules/video_coding/codecs/av1:libaom_av1_decoder",
"../p2p:p2p_test_utils",
"../rtc_base",
"../rtc_base:checks",
diff --git a/media/base/media_constants.cc b/media/base/media_constants.cc
index 5bd4b75..5144a6e 100644
--- a/media/base/media_constants.cc
+++ b/media/base/media_constants.cc
@@ -105,6 +105,7 @@
const char kVp8CodecName[] = "VP8";
const char kVp9CodecName[] = "VP9";
+const char kAv1CodecName[] = "AV1X";
const char kH264CodecName[] = "H264";
// RFC 6184 RTP Payload Format for H.264 video
diff --git a/media/base/media_constants.h b/media/base/media_constants.h
index 136e9f1..b9b8a33 100644
--- a/media/base/media_constants.h
+++ b/media/base/media_constants.h
@@ -134,6 +134,7 @@
RTC_EXPORT extern const char kVp8CodecName[];
RTC_EXPORT extern const char kVp9CodecName[];
+RTC_EXPORT extern const char kAv1CodecName[];
RTC_EXPORT extern const char kH264CodecName[];
// RFC 6184 RTP Payload Format for H.264 video
diff --git a/media/engine/internal_decoder_factory.cc b/media/engine/internal_decoder_factory.cc
index 5180b28..e68bb36 100644
--- a/media/engine/internal_decoder_factory.cc
+++ b/media/engine/internal_decoder_factory.cc
@@ -14,6 +14,7 @@
#include "api/video_codecs/sdp_video_format.h"
#include "media/base/codec.h"
#include "media/base/media_constants.h"
+#include "modules/video_coding/codecs/av1/libaom_av1_decoder.h"
#include "modules/video_coding/codecs/h264/include/h264.h"
#include "modules/video_coding/codecs/vp8/include/vp8.h"
#include "modules/video_coding/codecs/vp9/include/vp9.h"
@@ -47,6 +48,8 @@
formats.push_back(format);
for (const SdpVideoFormat& h264_format : SupportedH264Codecs())
formats.push_back(h264_format);
+ if (kIsLibaomAv1DecoderSupported)
+ formats.push_back(SdpVideoFormat(cricket::kAv1CodecName));
return formats;
}
@@ -63,6 +66,9 @@
return VP9Decoder::Create();
if (absl::EqualsIgnoreCase(format.name, cricket::kH264CodecName))
return H264Decoder::Create();
+ if (kIsLibaomAv1DecoderSupported &&
+ absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName))
+ return CreateLibaomAv1Decoder();
RTC_NOTREACHED();
return nullptr;
diff --git a/media/engine/internal_decoder_factory_unittest.cc b/media/engine/internal_decoder_factory_unittest.cc
index 5e2bfbf..705933d 100644
--- a/media/engine/internal_decoder_factory_unittest.cc
+++ b/media/engine/internal_decoder_factory_unittest.cc
@@ -13,10 +13,16 @@
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_decoder.h"
#include "media/base/media_constants.h"
+#include "modules/video_coding/codecs/av1/libaom_av1_decoder.h"
+#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
+using ::testing::Contains;
+using ::testing::Field;
+using ::testing::Not;
+
TEST(InternalDecoderFactory, TestVP8) {
InternalDecoderFactory factory;
std::unique_ptr<VideoDecoder> decoder =
@@ -24,4 +30,16 @@
EXPECT_TRUE(decoder);
}
+TEST(InternalDecoderFactory, Av1) {
+ InternalDecoderFactory factory;
+ if (kIsLibaomAv1DecoderSupported) {
+ EXPECT_THAT(factory.GetSupportedFormats(),
+ Contains(Field(&SdpVideoFormat::name, "AV1X")));
+ EXPECT_TRUE(factory.CreateVideoDecoder(SdpVideoFormat("AV1X")));
+ } else {
+ EXPECT_THAT(factory.GetSupportedFormats(),
+ Not(Contains(Field(&SdpVideoFormat::name, "AV1X"))));
+ }
+}
+
} // namespace webrtc
diff --git a/modules/video_coding/codecs/av1/BUILD.gn b/modules/video_coding/codecs/av1/BUILD.gn
new file mode 100644
index 0000000..561c81f
--- /dev/null
+++ b/modules/video_coding/codecs/av1/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright (c) 2020 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.
+
+import("//third_party/libaom/options.gni")
+import("../../../../webrtc.gni")
+
+rtc_library("libaom_av1_decoder") {
+ visibility = [ "*" ]
+ poisonous = [ "software_video_codecs" ]
+ public = [ "libaom_av1_decoder.h" ]
+ deps = [
+ "../../../../api/video_codecs:video_codecs_api",
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ ]
+
+ if (enable_libaom_decoder) {
+ sources = [ "libaom_av1_decoder.cc" ]
+ deps += [
+ "../..:video_codec_interface",
+ "../../../../api:scoped_refptr",
+ "../../../../api/video:encoded_image",
+ "../../../../api/video:video_frame_i420",
+ "../../../../common_video",
+ "../../../../rtc_base:logging",
+ "//third_party/abseil-cpp/absl/types:optional",
+ "//third_party/libaom",
+ "//third_party/libyuv",
+ ]
+ } else {
+ sources = [ "libaom_av1_decoder_absent.cc" ]
+ }
+}
diff --git a/modules/video_coding/codecs/av1/DEPS b/modules/video_coding/codecs/av1/DEPS
new file mode 100644
index 0000000..2577991
--- /dev/null
+++ b/modules/video_coding/codecs/av1/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/libaom",
+]
diff --git a/modules/video_coding/codecs/av1/libaom_av1_decoder.cc b/modules/video_coding/codecs/av1/libaom_av1_decoder.cc
new file mode 100644
index 0000000..122f214
--- /dev/null
+++ b/modules/video_coding/codecs/av1/libaom_av1_decoder.cc
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2020 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/libaom_av1_decoder.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "absl/types/optional.h"
+#include "api/scoped_refptr.h"
+#include "api/video/encoded_image.h"
+#include "api/video/i420_buffer.h"
+#include "api/video_codecs/video_codec.h"
+#include "api/video_codecs/video_decoder.h"
+#include "common_video/include/i420_buffer_pool.h"
+#include "modules/video_coding/include/video_error_codes.h"
+#include "rtc_base/logging.h"
+#include "third_party/libaom/source/libaom/aom/aom_decoder.h"
+#include "third_party/libaom/source/libaom/aom/aomdx.h"
+#include "third_party/libyuv/include/libyuv/convert.h"
+
+namespace webrtc {
+namespace {
+
+constexpr int kConfigLowBitDepth = 1; // 8-bits per luma/chroma sample.
+constexpr int kDecFlags = 0; // 0 signals no post processing.
+
+class LibaomAv1Decoder final : public VideoDecoder {
+ public:
+ LibaomAv1Decoder();
+ LibaomAv1Decoder(const LibaomAv1Decoder&) = delete;
+ LibaomAv1Decoder& operator=(const LibaomAv1Decoder&) = delete;
+ ~LibaomAv1Decoder();
+
+ // Implements VideoDecoder.
+ int32_t InitDecode(const VideoCodec* codec_settings,
+ int number_of_cores) override;
+
+ // Decode an encoded video frame.
+ 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;
+
+ private:
+ aom_codec_ctx_t context_;
+ bool inited_;
+ // Pool of memory buffers to store decoded image data for application access.
+ I420BufferPool buffer_pool_;
+ DecodedImageCallback* decode_complete_callback_;
+};
+
+LibaomAv1Decoder::LibaomAv1Decoder()
+ : context_(), // Force value initialization instead of default one.
+ inited_(false),
+ buffer_pool_(false, /*max_number_of_buffers=*/150),
+ decode_complete_callback_(nullptr) {}
+
+LibaomAv1Decoder::~LibaomAv1Decoder() {
+ Release();
+}
+
+int32_t LibaomAv1Decoder::InitDecode(const VideoCodec* codec_settings,
+ int number_of_cores) {
+ aom_codec_dec_cfg_t config = {
+ static_cast<unsigned int>(number_of_cores), // Max # of threads.
+ 0, // Frame width set after decode.
+ 0, // Frame height set after decode.
+ kConfigLowBitDepth}; // Enable low-bit-depth code path.
+
+ aom_codec_err_t ret =
+ aom_codec_dec_init(&context_, aom_codec_av1_dx(), &config, kDecFlags);
+ if (ret != AOM_CODEC_OK) {
+ RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::InitDecode returned " << ret
+ << " on aom_codec_dec_init.";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ inited_ = true;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t LibaomAv1Decoder::Decode(const EncodedImage& encoded_image,
+ bool missing_frames,
+ int64_t /*render_time_ms*/) {
+ if (!inited_) {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ if (decode_complete_callback_ == nullptr) {
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+
+ // Decode one video frame.
+ aom_codec_err_t ret =
+ aom_codec_decode(&context_, encoded_image.data(), encoded_image.size(),
+ /*user_priv=*/nullptr);
+ if (ret != AOM_CODEC_OK) {
+ RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode returned " << ret
+ << " on aom_codec_decode.";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ // Get decoded frame data.
+ int corrupted_frame = 0;
+ aom_codec_iter_t iter = nullptr;
+ while (aom_image_t* decoded_image = aom_codec_get_frame(&context_, &iter)) {
+ if (aom_codec_control(&context_, AOMD_GET_FRAME_CORRUPTED,
+ &corrupted_frame)) {
+ RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode "
+ "AOM_GET_FRAME_CORRUPTED.";
+ }
+ // Check that decoded image format is I420 and has 8-bit depth.
+ if (decoded_image->fmt != AOM_IMG_FMT_I420) {
+ RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode invalid image format";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ // Return decoded frame data.
+ int qp;
+ ret = aom_codec_control_(&context_, AOMD_GET_LAST_QUANTIZER, &qp);
+ if (ret != AOM_CODEC_OK) {
+ RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode returned " << ret
+ << " on control AOME_GET_LAST_QUANTIZER.";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ // Allocate memory for decoded frame.
+ rtc::scoped_refptr<I420Buffer> buffer =
+ buffer_pool_.CreateBuffer(decoded_image->d_w, decoded_image->d_h);
+ if (!buffer.get()) {
+ // Pool has too many pending frames.
+ RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode returned due to lack of"
+ " space in decoded frame buffer pool.";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ // Copy decoded_image to decoded_frame.
+ libyuv::I420Copy(
+ decoded_image->planes[AOM_PLANE_Y], decoded_image->stride[AOM_PLANE_Y],
+ decoded_image->planes[AOM_PLANE_U], decoded_image->stride[AOM_PLANE_U],
+ decoded_image->planes[AOM_PLANE_V], decoded_image->stride[AOM_PLANE_V],
+ buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataU(),
+ buffer->StrideU(), buffer->MutableDataV(), buffer->StrideV(),
+ decoded_image->d_w, decoded_image->d_h);
+ VideoFrame decoded_frame = VideoFrame::Builder()
+ .set_video_frame_buffer(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;
+}
+
+int32_t LibaomAv1Decoder::RegisterDecodeCompleteCallback(
+ DecodedImageCallback* decode_complete_callback) {
+ decode_complete_callback_ = decode_complete_callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t LibaomAv1Decoder::Release() {
+ if (aom_codec_destroy(&context_) != AOM_CODEC_OK) {
+ return WEBRTC_VIDEO_CODEC_MEMORY;
+ }
+ buffer_pool_.Release();
+ inited_ = false;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+} // namespace
+
+const bool kIsLibaomAv1DecoderSupported = true;
+
+std::unique_ptr<VideoDecoder> CreateLibaomAv1Decoder() {
+ return std::make_unique<LibaomAv1Decoder>();
+}
+
+} // namespace webrtc
diff --git a/modules/video_coding/codecs/av1/libaom_av1_decoder.h b/modules/video_coding/codecs/av1/libaom_av1_decoder.h
new file mode 100644
index 0000000..9b01285
--- /dev/null
+++ b/modules/video_coding/codecs/av1/libaom_av1_decoder.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+#ifndef MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_DECODER_H_
+#define MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_DECODER_H_
+
+#include <memory>
+
+#include "absl/base/attributes.h"
+#include "api/video_codecs/video_decoder.h"
+
+namespace webrtc {
+
+ABSL_CONST_INIT extern const bool kIsLibaomAv1DecoderSupported;
+
+std::unique_ptr<VideoDecoder> CreateLibaomAv1Decoder();
+
+} // namespace webrtc
+
+#endif // MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_DECODER_H_
diff --git a/modules/video_coding/codecs/av1/libaom_av1_decoder_absent.cc b/modules/video_coding/codecs/av1/libaom_av1_decoder_absent.cc
new file mode 100644
index 0000000..b97b68b
--- /dev/null
+++ b/modules/video_coding/codecs/av1/libaom_av1_decoder_absent.cc
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2020 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/libaom_av1_decoder.h"
+
+#include <memory>
+
+#include "api/video_codecs/video_decoder.h"
+
+namespace webrtc {
+
+const bool kIsLibaomAv1DecoderSupported = false;
+
+std::unique_ptr<VideoDecoder> CreateLibaomAv1Decoder() {
+ return nullptr;
+}
+
+} // namespace webrtc
diff --git a/tools_webrtc/libs/generate_licenses.py b/tools_webrtc/libs/generate_licenses.py
index 2202188..b8894ed 100755
--- a/tools_webrtc/libs/generate_licenses.py
+++ b/tools_webrtc/libs/generate_licenses.py
@@ -46,6 +46,7 @@
'guava': ['third_party/guava/LICENSE'],
'ijar': ['third_party/ijar/LICENSE'],
'jsoncpp': ['third_party/jsoncpp/LICENSE'],
+ 'libaom': ['third_party/libaom/source/libaom/LICENSE'],
'libc++': ['buildtools/third_party/libc++/trunk/LICENSE.TXT'],
'libc++abi': ['buildtools/third_party/libc++abi/trunk/LICENSE.TXT'],
'libevent': ['base/third_party/libevent/LICENSE'],