Previously, the Blender video renderer did not have support for encoding video to AV1 (not to be confused with the container AVI). The proposed solution is to leverage the existing FFMpeg renderer to encode to AV1. Note that avcodec_find_encoder(AV_CODEC_ID_AV1) usually returns "libaom-av1" which is the "reference implementation" for AV1 encoding (the default for FFMpeg, and is slow). "libsvtav1" is faster and preferred so there is extra handling when fetching the AV1 codec for encoding such that "libsvtav1" is used when possible. This commit should only affect the options available for video rendering, which includes the additional AV1 codec to choose from, and setting "-crf". Also note that the current release of FFMpeg for ArchLinux does not support "-crf" for "libsvtav1", but the equivalent option "-qp" is supported and used as a fallback when "libsvtav1" is used (as mentioned here: https://trac.ffmpeg.org/wiki/Encode/AV1#SVT-AV1 ). (Actually, both "-crf" and "-qp" is specified with the same value in the code. When a release of FFMpeg obtains support for "-crf" for "libsvtav1" is released, the code shouldn't be needed to change.) The usage of the AV1 codec should be very similar to the usage of the H264 codec, but is limited to the "mp4" and "mkv" containers. This patch pertains to the "VFX & Video" module, as its main purpose is to supplement the Video Sequencer tool with the additional AV1 codec for encoded video output. Differential Revision: https://developer.blender.org/D14920 Reviewed By: sergey , ISS, zeddb
162 lines
4.9 KiB
C++
162 lines
4.9 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "testing/testing.h"
|
|
|
|
extern "C" {
|
|
#include <libavcodec/avcodec.h>
|
|
#include <libavutil/channel_layout.h>
|
|
#include <libavutil/log.h>
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool test_vcodec(const AVCodec *codec, AVPixelFormat pixelformat)
|
|
{
|
|
av_log_set_level(AV_LOG_QUIET);
|
|
bool result = false;
|
|
if (codec) {
|
|
AVCodecContext *ctx = avcodec_alloc_context3(codec);
|
|
if (ctx) {
|
|
ctx->time_base.num = 1;
|
|
ctx->time_base.den = 25;
|
|
ctx->pix_fmt = pixelformat;
|
|
ctx->width = 720;
|
|
ctx->height = 576;
|
|
int open = avcodec_open2(ctx, codec, NULL);
|
|
if (open >= 0) {
|
|
avcodec_free_context(&ctx);
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
bool test_acodec(const AVCodec *codec, AVSampleFormat fmt)
|
|
{
|
|
av_log_set_level(AV_LOG_QUIET);
|
|
bool result = false;
|
|
if (codec) {
|
|
AVCodecContext *ctx = avcodec_alloc_context3(codec);
|
|
if (ctx) {
|
|
ctx->sample_fmt = fmt;
|
|
ctx->sample_rate = 48000;
|
|
ctx->channel_layout = AV_CH_LAYOUT_MONO;
|
|
ctx->bit_rate = 128000;
|
|
int open = avcodec_open2(ctx, codec, NULL);
|
|
if (open >= 0) {
|
|
avcodec_free_context(&ctx);
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool test_codec_video_by_codecid(AVCodecID codec_id, AVPixelFormat pixelformat)
|
|
{
|
|
bool result = false;
|
|
const AVCodec *codec = avcodec_find_encoder(codec_id);
|
|
if (codec)
|
|
result = test_vcodec(codec, pixelformat);
|
|
return result;
|
|
}
|
|
|
|
bool test_codec_video_by_name(const char *codecname, AVPixelFormat pixelformat)
|
|
{
|
|
bool result = false;
|
|
const AVCodec *codec = avcodec_find_encoder_by_name(codecname);
|
|
if (codec)
|
|
result = test_vcodec(codec, pixelformat);
|
|
return result;
|
|
}
|
|
|
|
bool test_codec_audio_by_codecid(AVCodecID codec_id, AVSampleFormat fmt)
|
|
{
|
|
bool result = false;
|
|
const AVCodec *codec = avcodec_find_encoder(codec_id);
|
|
if (codec)
|
|
result = test_acodec(codec, fmt);
|
|
return result;
|
|
}
|
|
|
|
bool test_codec_audio_by_name(const char *codecname, AVSampleFormat fmt)
|
|
{
|
|
bool result = false;
|
|
const AVCodec *codec = avcodec_find_encoder_by_name(codecname);
|
|
if (codec)
|
|
result = test_acodec(codec, fmt);
|
|
return result;
|
|
}
|
|
|
|
#define str(s) #s
|
|
#define FFMPEG_TEST_VCODEC_ID(codec, fmt) \
|
|
TEST(ffmpeg, codec##_##fmt) \
|
|
{ \
|
|
EXPECT_TRUE(test_codec_video_by_codecid(codec, fmt)); \
|
|
}
|
|
|
|
#define FFMPEG_TEST_VCODEC_NAME(codec, fmt) \
|
|
TEST(ffmpeg, codec##_##fmt) \
|
|
{ \
|
|
EXPECT_TRUE(test_codec_video_by_name(str(codec), fmt)); \
|
|
}
|
|
|
|
#define FFMPEG_TEST_ACODEC_ID(codec, fmt) \
|
|
TEST(ffmpeg, codec##_##fmt) \
|
|
{ \
|
|
EXPECT_TRUE(test_codec_audio_by_codecid(codec, fmt)); \
|
|
}
|
|
|
|
#define FFMPEG_TEST_ACODEC_NAME(codec, fmt) \
|
|
TEST(ffmpeg, codec) \
|
|
{ \
|
|
EXPECT_TRUE(test_codec_audio_by_name(str(codec), fmt)); \
|
|
}
|
|
|
|
} // namespace
|
|
|
|
/* generic codec ID's used in blender */
|
|
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_HUFFYUV, AV_PIX_FMT_BGRA)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_HUFFYUV, AV_PIX_FMT_RGB32)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_FFV1, AV_PIX_FMT_RGB32)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_QTRLE, AV_PIX_FMT_ARGB)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_VP9, AV_PIX_FMT_YUVA420P)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_PNG, AV_PIX_FMT_RGBA)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_H264, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_MPEG4, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_THEORA, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_DVVIDEO, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_MPEG1VIDEO, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_MPEG2VIDEO, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_FLV1, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_AV1, AV_PIX_FMT_YUV420P)
|
|
|
|
/* Audio codecs */
|
|
|
|
FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_AAC, AV_SAMPLE_FMT_FLTP)
|
|
FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_AC3, AV_SAMPLE_FMT_FLTP)
|
|
FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_FLAC, AV_SAMPLE_FMT_S16)
|
|
FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_MP2, AV_SAMPLE_FMT_S16)
|
|
FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_MP3, AV_SAMPLE_FMT_FLTP)
|
|
FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_OPUS, AV_SAMPLE_FMT_FLT)
|
|
FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_PCM_S16LE, AV_SAMPLE_FMT_S16)
|
|
FFMPEG_TEST_ACODEC_ID(AV_CODEC_ID_VORBIS, AV_SAMPLE_FMT_FLTP)
|
|
|
|
/* Libraries we count on ffmpeg being linked against */
|
|
|
|
FFMPEG_TEST_VCODEC_NAME(libtheora, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_NAME(libx264, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_NAME(libvpx, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_NAME(libopenjpeg, AV_PIX_FMT_YUV420P)
|
|
FFMPEG_TEST_VCODEC_NAME(libxvid, AV_PIX_FMT_YUV420P)
|
|
/* aom's AV1 encoder is "libaom-av1". FFMPEG_TEST_VCODEC_NAME(libaom-av1, ...)
|
|
* will not work because the dash will not work with the test macro. */
|
|
TEST(ffmpeg, libaom_av1_AV_PIX_FMT_YUV420P)
|
|
{
|
|
EXPECT_TRUE(test_codec_video_by_name("libaom-av1", AV_PIX_FMT_YUV420P));
|
|
}
|
|
FFMPEG_TEST_ACODEC_NAME(libvorbis, AV_SAMPLE_FMT_FLTP)
|
|
FFMPEG_TEST_ACODEC_NAME(libopus, AV_SAMPLE_FMT_FLT)
|
|
FFMPEG_TEST_ACODEC_NAME(libmp3lame, AV_SAMPLE_FMT_FLTP)
|