diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad8ebbd6e..7d6c8c727 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: ${{ matrix.cmake-gen }}> runs-on: ubuntu-latest container: - image: olivevideoeditor/ci-olive:2022.2 + image: olivevideoeditor/ci-olive:2022.3 steps: - name: Checkout Source Code diff --git a/app/codec/exportcodec.cpp b/app/codec/exportcodec.cpp index 2a908f8b5..217593d08 100644 --- a/app/codec/exportcodec.cpp +++ b/app/codec/exportcodec.cpp @@ -64,6 +64,8 @@ QString ExportCodec::GetCodecName(ExportCodec::Codec c) return tr("Vorbis"); case kCodecVP9: return tr("VP9"); + case kCodecAV1: + return tr("AV1"); case kCodecSRT: return tr("SubRip SRT"); case kCodecCount: @@ -90,6 +92,7 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c) case kCodecOpus: case kCodecFLAC: case kCodecVP9: + case kCodecAV1: case kCodecSRT: return false; case kCodecOpenEXR: @@ -121,6 +124,7 @@ bool ExportCodec::IsCodecLossless(Codec c) case kCodecVorbis: case kCodecOpus: case kCodecVP9: + case kCodecAV1: case kCodecSRT: case kCodecOpenEXR: case kCodecPNG: diff --git a/app/codec/exportcodec.h b/app/codec/exportcodec.h index 7fff8f311..c78085986 100644 --- a/app/codec/exportcodec.h +++ b/app/codec/exportcodec.h @@ -53,6 +53,7 @@ public: kCodecVorbis, kCodecFLAC, kCodecSRT, + kCodecAV1, kCodecCount }; diff --git a/app/codec/exportformat.cpp b/app/codec/exportformat.cpp index 6e91ff77e..db8fb8bf8 100644 --- a/app/codec/exportformat.cpp +++ b/app/codec/exportformat.cpp @@ -123,7 +123,7 @@ QList ExportFormat::GetVideoCodecs(ExportFormat::Format f) case kFormatQuickTime: return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform}; case kFormatWebM: - return {ExportCodec::kCodecVP9}; + return {ExportCodec::kCodecAV1, ExportCodec::kCodecVP9}; case kFormatOgg: case kFormatWAV: case kFormatMPEG4Audio: @@ -152,7 +152,7 @@ QList ExportFormat::GetAudioCodecs(ExportFormat::Format f) case kFormatQuickTime: return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM}; case kFormatWebM: - return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus}; + return {ExportCodec::kCodecOpus, ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis}; // Audio only formats case kFormatWAV: diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 81c2a510a..f5ca225a4 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -898,6 +898,12 @@ const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c, SampleFormat afor return avcodec_find_encoder(AV_CODEC_ID_HEVC); case ExportCodec::kCodecVP9: return avcodec_find_encoder(AV_CODEC_ID_VP9); + case ExportCodec::kCodecAV1: { + const AVCodec *encoder = avcodec_find_encoder_by_name("libsvtav1"); + if(!encoder) + encoder = avcodec_find_encoder(AV_CODEC_ID_AV1); + return encoder; + } case ExportCodec::kCodecOpenEXR: return avcodec_find_encoder(AV_CODEC_ID_EXR); case ExportCodec::kCodecPNG: diff --git a/app/dialog/export/codec/CMakeLists.txt b/app/dialog/export/codec/CMakeLists.txt index f00ce2690..d68ccd1c3 100644 --- a/app/dialog/export/codec/CMakeLists.txt +++ b/app/dialog/export/codec/CMakeLists.txt @@ -16,6 +16,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + dialog/export/codec/av1section.cpp + dialog/export/codec/av1section.h dialog/export/codec/cineformsection.cpp dialog/export/codec/cineformsection.h dialog/export/codec/codecsection.cpp diff --git a/app/dialog/export/codec/av1section.cpp b/app/dialog/export/codec/av1section.cpp new file mode 100644 index 000000000..8fc5edc44 --- /dev/null +++ b/app/dialog/export/codec/av1section.cpp @@ -0,0 +1,132 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2022 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "av1section.h" + +#include +#include +#include +#include + +#include "common/qtutils.h" +#include "widget/slider/integerslider.h" + +namespace olive { + +AV1Section::AV1Section(QWidget *parent) : + AV1Section(AV1CRFSection::kDefaultAV1CRF, parent) +{ +} + +AV1Section::AV1Section(int default_crf, QWidget *parent) : + CodecSection(parent) +{ + QGridLayout* layout = new QGridLayout(this); + layout->setMargin(0); + + int row = 0; + layout->addWidget(new QLabel(tr("Preset:")), row, 0); + + preset_combobox_ = new QComboBox(); + preset_combobox_->setToolTip(tr("This parameter governs the efficiency/encode-time trade-off.\n" + "Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n" + "Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value.")); + + for (int i = 0; i <= 13; i++) + preset_combobox_->addItem(QString::number(i)); + + preset_combobox_->setCurrentIndex(8); + + layout->addWidget(preset_combobox_, row, 1); + + row++; + + layout->addWidget(new QLabel(tr("Compression Method:")), row, 0); + + QComboBox* compression_box = new QComboBox(); + compression_box->setToolTip(tr("This parameter governs the quality/size trade-off.\n" + "Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n" + "Lower CRF values retain more detail at the cost of larger file sizes.\n" + "The possible range of CRF in SVT-AV1 is 1-63.")); + + // These items must correspond to the CompressionMethod enum + compression_box->addItem(tr("Constant Rate Factor")); + + layout->addWidget(compression_box, row, 1); + + row++; + + compression_method_stack_ = new QStackedWidget(); + layout->addWidget(compression_method_stack_, row, 0, 1, 2); + + crf_section_ = new AV1CRFSection(default_crf); + compression_method_stack_->addWidget(crf_section_); + + connect(compression_box, + static_cast(&QComboBox::currentIndexChanged), + compression_method_stack_, + &QStackedWidget::setCurrentIndex); +} + +void AV1Section::AddOpts(EncodingParams *params) +{ + + CompressionMethod method = static_cast(compression_method_stack_->currentIndex()); + + if (method == kConstantRateFactor) { + + // Set Quantizer value + params->set_video_option(QStringLiteral("qp"), QString::number(crf_section_->GetValue())); + + } + + params->set_video_option(QStringLiteral("preset"), QString::number(preset_combobox_->currentIndex())); +} + +AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) : + QWidget(parent) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + layout->setMargin(0); + + crf_slider_ = new QSlider(Qt::Horizontal); + crf_slider_->setMinimum(kMinimumCRF); + crf_slider_->setMaximum(kMaximumCRF); + crf_slider_->setValue(default_crf); + layout->addWidget(crf_slider_); + + IntegerSlider* crf_input = new IntegerSlider(); + crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH"))); + crf_input->SetMinimum(kMinimumCRF); + crf_input->SetMaximum(kMaximumCRF); + crf_input->SetValue(default_crf); + crf_input->SetDefaultValue(default_crf); + layout->addWidget(crf_input); + + connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue); + connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_, &QSlider::setValue); +} + +int AV1CRFSection::GetValue() const +{ + return crf_slider_->value(); +} + +} diff --git a/app/dialog/export/codec/av1section.h b/app/dialog/export/codec/av1section.h new file mode 100644 index 000000000..065485fbc --- /dev/null +++ b/app/dialog/export/codec/av1section.h @@ -0,0 +1,74 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2022 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef AV1SECTION_H +#define AV1SECTION_H + +#include +#include +#include + +#include "codecsection.h" +#include "widget/slider/floatslider.h" + +namespace olive { + +class AV1CRFSection : public QWidget +{ + Q_OBJECT +public: + AV1CRFSection(int default_crf, QWidget* parent = nullptr); + + int GetValue() const; + + static const int kDefaultAV1CRF = 30; + +private: + static const int kMinimumCRF = 0; + static const int kMaximumCRF = 63; + + QSlider* crf_slider_; + +}; + +class AV1Section : public CodecSection +{ + Q_OBJECT +public: + enum CompressionMethod { + kConstantRateFactor, + }; + + AV1Section(QWidget* parent = nullptr); + AV1Section(int default_crf, QWidget* parent); + + virtual void AddOpts(EncodingParams* params) override; + +private: + QStackedWidget* compression_method_stack_; + + AV1CRFSection* crf_section_; + + QComboBox *preset_combobox_; +}; + +} + +#endif // AV1SECTION_H diff --git a/app/dialog/export/exportvideotab.cpp b/app/dialog/export/exportvideotab.cpp index 579aa746c..f0c130d02 100644 --- a/app/dialog/export/exportvideotab.cpp +++ b/app/dialog/export/exportvideotab.cpp @@ -193,6 +193,9 @@ QWidget *ExportVideoTab::SetupCodecSection() h265_section_ = new H265Section(); codec_stack_->addWidget(h265_section_); + av1_section_ = new AV1Section(); + codec_stack_->addWidget(av1_section_); + cineform_section_ = new CineformSection(); codec_stack_->addWidget(cineform_section_); @@ -253,6 +256,9 @@ void ExportVideoTab::VideoCodecChanged() case ExportCodec::kCodecH265: SetCodecSection(h265_section_); break; + case ExportCodec::kCodecAV1: + SetCodecSection(av1_section_); + break; case ExportCodec::kCodecCineform: SetCodecSection(cineform_section_); break; diff --git a/app/dialog/export/exportvideotab.h b/app/dialog/export/exportvideotab.h index 061dea64f..4e59714be 100644 --- a/app/dialog/export/exportvideotab.h +++ b/app/dialog/export/exportvideotab.h @@ -26,6 +26,7 @@ #include #include "common/qtutils.h" +#include "dialog/export/codec/av1section.h" #include "dialog/export/codec/cineformsection.h" #include "dialog/export/codec/codecstack.h" #include "dialog/export/codec/h264section.h" @@ -181,6 +182,7 @@ private: ImageSection* image_section_; H264Section* h264_section_; H264Section* h265_section_; + AV1Section* av1_section_; CineformSection *cineform_section_; ColorSpaceChooser* color_space_chooser_; diff --git a/docker/ci-ffmpeg/Dockerfile b/docker/ci-ffmpeg/Dockerfile index bbfc50c34..1a9a145ff 100644 --- a/docker/ci-ffmpeg/Dockerfile +++ b/docker/ci-ffmpeg/Dockerfile @@ -9,7 +9,7 @@ ARG CI_COMMON_VERSION=2 ARG FFMPEG_VERSION=5.0 ARG NASM_VERSION=2.15.05 ARG YASM_VERSION=1.3.0 -ARG AOM_VERSION=v3.3.0 +ARG SVTAV1_VERSION=v1.1.0 ARG X265_VERSION=3.5 ARG OGG_VERSION=1.3.5 ARG OPUS_VERSION=1.3.1 @@ -22,7 +22,6 @@ ARG XVID_VERSION=1.3.7 ARG OPENJPEG_VERSION=2.4.0 ARG LIBPNG_VERSION=1.6.37 # TODO: Make version of x264 selectable? -ARG NUM_JOBS=2 FROM ${OLIVE_ORG}/ci-common:${CI_COMMON_VERSION} as ci-ffmpeg @@ -31,7 +30,7 @@ ARG CI_COMMON_VERSION ARG FFMPEG_VERSION ARG NASM_VERSION ARG YASM_VERSION -ARG AOM_VERSION +ARG SVTAV1_VERSION ARG X265_VERSION ARG OGG_VERSION ARG OPUS_VERSION @@ -43,7 +42,6 @@ ARG LAME_VERSION ARG XVID_VERSION ARG OPENJPEG_VERSION ARG LIBPNG_VERSION -ARG NUM_JOBS LABEL maintainer="olivevideoeditor@gmail.com" LABEL org.opencontainers.image.name="$OLIVE_ORG/ci-ffmpeg" @@ -62,7 +60,7 @@ ENV OLIVE_ORG=${OLIVE_ORG} \ FFMPEG_VERSION=${FFMPEG_VERSION} \ NASM_VERSION=${NASM_VERSION} \ YASM_VERSION=${YASM_VERSION} \ - AOM_VERSION=${AOM_VERSION} \ + SVTAV1_VERSION=${SVTAV1_VERSION} \ X265_VERSION=${X265_VERSION} \ OGG_VERSION=${OGG_VERSION} \ OPUS_VERSION=${OPUS_VERSION} \ @@ -74,7 +72,6 @@ ENV OLIVE_ORG=${OLIVE_ORG} \ XVID_VERSION=${XVID_VERSION} \ OPENJPEG_VERSION=${OPENJPEG_VERSION} \ LIBPNG_VERSION=${LIBPNG_VERSION} \ - NUM_JOBS=${NUM_JOBS} \ OLIVE_INSTALL_PREFIX=/usr/local RUN /tmp/before_build.sh && \ diff --git a/docker/scripts/build_ffmpeg.sh b/docker/scripts/build_ffmpeg.sh index 1d59504ad..dc2e5db87 100644 --- a/docker/scripts/build_ffmpeg.sh +++ b/docker/scripts/build_ffmpeg.sh @@ -20,8 +20,8 @@ set -ex cd nasm* ./autogen.sh ./configure \ - --prefix="${OLIVE_INSTALL_PREFIX}" - make -j${NUM_JOBS} + --prefix="/usr" + make -j$(nproc) make install cd .. rm -rf nasm* @@ -34,8 +34,8 @@ set -ex rm -f yasm.tar.gz cd yasm* ./configure \ - --prefix="${OLIVE_INSTALL_PREFIX}" - make -j${NUM_JOBS} + --prefix="/usr" + make -j$(nproc) make install cd .. rm -rf yasm* @@ -44,19 +44,19 @@ set -ex # join jobs, some libs depend on NASM/YASM wait -# Set up libaom +# Set up libsvtav1 { - git clone --depth 1 --branch "${AOM_VERSION}" "https://aomedia.googlesource.com/aom" - cd aom - mkdir _build - cd _build + git clone --depth 1 --branch "${SVTAV1_VERSION}" "https://gitlab.com/AOMediaCodec/SVT-AV1.git" + cd SVT-AV1/Build cmake .. \ + -G "Unix Makefiles" \ -DCMAKE_INSTALL_PREFIX="${OLIVE_INSTALL_PREFIX}" \ - -DBUILD_SHARED_LIBS=1 + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_APPS=OFF cmake --build . cmake --install . cd ../.. - rm -rf aom + rm -rf SVT-AV1 } & # Set up libx264 @@ -269,7 +269,7 @@ PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:/usr/local/lib64/pkgconfig:$PKG_CONFIG --enable-gpl \ --enable-version3 \ --enable-shared \ - --enable-libaom \ + --enable-libsvtav1 \ --enable-libfreetype \ --enable-libx264 \ --enable-libx265 \ @@ -286,7 +286,7 @@ PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:/usr/local/lib64/pkgconfig:$PKG_CONFIG --extra-libs=-lm \ --extra-cflags="-I${OLIVE_INSTALL_PREFIX}/include" \ --extra-ldflags="-L${OLIVE_INSTALL_PREFIX}/lib" -make -j${NUM_JOBS} +make -j$(nproc) make install cd .. rm -rf ffmpeg*