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 0006 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 #include "libx264encoder_p.h" 0010 0011 #include <QSize> 0012 #include <QThread> 0013 0014 extern "C" { 0015 #include <libavcodec/avcodec.h> 0016 #include <libavfilter/buffersink.h> 0017 #include <libavfilter/buffersrc.h> 0018 #include <libavutil/pixfmt.h> 0019 } 0020 0021 #include "logging_record.h" 0022 0023 LibX264Encoder::LibX264Encoder(H264Profile profile, PipeWireProduce *produce) 0024 : SoftwareEncoder(produce) 0025 , m_profile(profile) 0026 { 0027 } 0028 0029 bool LibX264Encoder::initialize(const QSize &size) 0030 { 0031 createFilterGraph(size); 0032 0033 auto codec = avcodec_find_encoder_by_name("libx264"); 0034 if (!codec) { 0035 qCWarning(PIPEWIRERECORD_LOGGING) << "h264_vaapi codec not found"; 0036 return false; 0037 } 0038 0039 m_avCodecContext = avcodec_alloc_context3(codec); 0040 if (!m_avCodecContext) { 0041 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not allocate video codec context"; 0042 return false; 0043 } 0044 0045 Q_ASSERT(!size.isEmpty()); 0046 m_avCodecContext->width = size.width(); 0047 m_avCodecContext->height = size.height(); 0048 m_avCodecContext->max_b_frames = 0; 0049 m_avCodecContext->gop_size = 100; 0050 m_avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P; 0051 m_avCodecContext->time_base = AVRational{1, 1000}; 0052 0053 if (m_quality) { 0054 m_avCodecContext->global_quality = percentageToAbsoluteQuality(m_quality); 0055 } else { 0056 m_avCodecContext->global_quality = 35; 0057 } 0058 0059 switch (m_profile) { 0060 case H264Profile::Baseline: 0061 m_avCodecContext->profile = FF_PROFILE_H264_BASELINE; 0062 break; 0063 case H264Profile::Main: 0064 m_avCodecContext->profile = FF_PROFILE_H264_MAIN; 0065 break; 0066 case H264Profile::High: 0067 m_avCodecContext->profile = FF_PROFILE_H264_HIGH; 0068 break; 0069 } 0070 0071 AVDictionary *options = nullptr; 0072 av_dict_set_int(&options, "threads", qMin(16, QThread::idealThreadCount()), 0); 0073 av_dict_set(&options, "preset", "veryfast", 0); 0074 av_dict_set(&options, "tune-content", "screen", 0); 0075 av_dict_set(&options, "deadline", "realtime", 0); 0076 // In theory a lower number should be faster, but the opposite seems to be true 0077 // av_dict_set(&options, "quality", "40", 0); 0078 // Disable motion estimation, not great while dragging windows but speeds up encoding by an order of magnitude 0079 av_dict_set(&options, "flags", "+mv4", 0); 0080 // Disable in-loop filtering 0081 av_dict_set(&options, "-flags", "+loop", 0); 0082 0083 if (int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) { 0084 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open codec" << av_err2str(result); 0085 return false; 0086 } 0087 0088 return true; 0089 } 0090 0091 int LibX264Encoder::percentageToAbsoluteQuality(const std::optional<quint8> &quality) 0092 { 0093 if (!quality) { 0094 return -1; 0095 } 0096 0097 constexpr int MinQuality = 51 + 6 * 6; 0098 return std::max(1, int(MinQuality - (m_quality.value() / 100.0) * MinQuality)); 0099 }