File indexing completed on 2024-05-05 05:30:17
0001 /* 0002 SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleixpol@kde.org> 0003 SPDX-FileCopyrightText: 2023 Marco Martin <mart@kde.org> 0004 SPDX-FileCopyrightText: 2023 Arjen Hiemstra <ahiemstra@heimr.nl> 0005 SPDX-FileCopyrightText: 2023 Noah Davis <noahadvs@gmail.com> 0006 0007 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0008 */ 0009 0010 #include "libvpxvp9encoder_p.h" 0011 0012 #include "pipewireproduce_p.h" 0013 0014 #include <QSize> 0015 #include <QThread> 0016 0017 extern "C" { 0018 #include <libavcodec/avcodec.h> 0019 #include <libavfilter/buffersink.h> 0020 #include <libavfilter/buffersrc.h> 0021 #include <libavutil/pixfmt.h> 0022 } 0023 0024 #include "logging_record.h" 0025 0026 LibVpxVp9Encoder::LibVpxVp9Encoder(PipeWireProduce *produce) 0027 : SoftwareEncoder(produce) 0028 { 0029 } 0030 0031 bool LibVpxVp9Encoder::initialize(const QSize &size) 0032 { 0033 createFilterGraph(size); 0034 0035 auto codec = avcodec_find_encoder_by_name("libvpx-vp9"); 0036 if (!codec) { 0037 qCWarning(PIPEWIRERECORD_LOGGING) << "libvpx-vp9 codec not found"; 0038 return false; 0039 } 0040 0041 m_avCodecContext = avcodec_alloc_context3(codec); 0042 if (!m_avCodecContext) { 0043 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not allocate video codec context"; 0044 return false; 0045 } 0046 0047 Q_ASSERT(!size.isEmpty()); 0048 m_avCodecContext->width = size.width(); 0049 m_avCodecContext->height = size.height(); 0050 m_avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P; 0051 m_avCodecContext->time_base = AVRational{1, 1000}; 0052 0053 AVDictionary *options = nullptr; 0054 0055 // We're probably capturing a screen 0056 av_dict_set(&options, "tune-content", "screen", 0); 0057 0058 const auto area = size.width() * size.height(); 0059 // m_avCodecContext->framerate is not set, so we use m_produce->maxFramerate() instead. 0060 const auto maxFramerate = m_produce->maxFramerate(); 0061 const auto fps = qreal(maxFramerate.numerator) / std::max(quint32(1), maxFramerate.denominator); 0062 0063 m_avCodecContext->gop_size = fps * 2; 0064 0065 // TODO: Make bitrate depend on the framerate? More frames is more data. 0066 // maxFramerate can apparently be changed while recording, so keep that in mind. 0067 m_avCodecContext->bit_rate = std::round(area * 2); 0068 m_avCodecContext->rc_min_rate = std::round(area); 0069 m_avCodecContext->rc_max_rate = std::round(area * 3); 0070 0071 m_avCodecContext->rc_buffer_size = m_avCodecContext->bit_rate; 0072 0073 // Lower crf is higher quality. Max 0, min 63. libvpx-vp9 doesn't use global_quality. 0074 int crf = 31; 0075 if (m_quality) { 0076 crf = percentageToAbsoluteQuality(m_quality); 0077 } 0078 av_dict_set_int(&options, "crf", crf, 0); 0079 m_avCodecContext->qmin = std::clamp(crf / 2, 0, crf); 0080 m_avCodecContext->qmax = std::clamp(qRound(crf * 1.5), crf, 63); 0081 0082 // 0-4 are for Video-On-Demand with the good or best deadline. 0083 // Don't use best, it's not worth it. 0084 // 5-8 are for streaming with the realtime deadline. 0085 // Lower is higher quality. 0086 int cpuUsed = 5 + std::max(1, int(3 - std::round(m_quality.value_or(50) / 100.0 * 3))); 0087 av_dict_set_int(&options, "cpu-used", cpuUsed, 0); 0088 av_dict_set(&options, "deadline", "realtime", 0); 0089 0090 m_avCodecContext->thread_count = QThread::idealThreadCount(); 0091 0092 // The value is interpreted as being equivalent to log2(realNumberOfColumns), 0093 // so 3 is 8 columns. 6 is the max amount of columns. 2 is the max amount of rows. 0094 av_dict_set(&options, "tile-columns", "6", 0); 0095 av_dict_set(&options, "tile-rows", "2", 0); 0096 // This should make things faster, but it only seems to consume 100MB more RAM. 0097 // av_dict_set(&options, "row-mt", "1", 0); 0098 av_dict_set(&options, "frame-parallel", "1", 0); 0099 0100 if (int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) { 0101 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open codec" << av_err2str(result); 0102 return false; 0103 } 0104 0105 return true; 0106 } 0107 0108 int LibVpxVp9Encoder::percentageToAbsoluteQuality(const std::optional<quint8> &quality) 0109 { 0110 if (!quality) { 0111 return -1; 0112 } 0113 0114 constexpr int MinQuality = 63; 0115 return std::max(1, int(MinQuality - (m_quality.value() / 100.0) * MinQuality)); 0116 }