KPipewire

libx264encoder.cpp
1/*
2 SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleixpol@kde.org>
3 SPDX-FileCopyrightText: 2023 Marco Martin <mart@kde.org>
4 SPDX-FileCopyrightText: 2023 Arjen Hiemstra <ahiemstra@heimr.nl>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7*/
8
9#include "libx264encoder_p.h"
10
11#include <QSize>
12#include <QThread>
13
14extern "C" {
15#include <libavcodec/avcodec.h>
16#include <libavfilter/buffersink.h>
17#include <libavfilter/buffersrc.h>
18#include <libavutil/pixfmt.h>
19}
20
21#include "logging_record.h"
22
23using namespace Qt::StringLiterals;
24
25LibX264Encoder::LibX264Encoder(H264Profile profile, PipeWireProduce *produce)
26 : SoftwareEncoder(produce)
27 , m_profile(profile)
28{
29 // Adjust the filter graph to ensure we are using an even frame size using a
30 // pad filter. Otherwise the size adjustment below will insert a row/column
31 // of garbage instead of black.
32 m_filterGraphToParse = u"pad=ceil(iw/2)*2:ceil(ih/2)*2,format=pix_fmts=yuv420p"_s;
33}
34
35bool LibX264Encoder::initialize(const QSize &size)
36{
37 createFilterGraph(size);
38
39 auto codec = avcodec_find_encoder_by_name("libx264");
40 if (!codec) {
41 qCWarning(PIPEWIRERECORD_LOGGING) << "libx264 codec not found";
42 return false;
43 }
44
45 m_avCodecContext = avcodec_alloc_context3(codec);
46 if (!m_avCodecContext) {
47 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not allocate video codec context";
48 return false;
49 }
50
51 Q_ASSERT(!size.isEmpty());
52 // Important: libx264 rejects streams with sizes that are not even. So to
53 // ensure we don't get errors, we need to ensure the size we set here is
54 // even. We also insert a pad filter into the filter chain above to ensure
55 // we don't end up padding with garbage.
56 m_avCodecContext->width = std::ceil(size.width() / 2) * 2;
57 m_avCodecContext->height = std::ceil(size.height() / 2) * 2;
58 m_avCodecContext->max_b_frames = 0;
59 m_avCodecContext->gop_size = 100;
60 m_avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
61 m_avCodecContext->time_base = AVRational{1, 1000};
62
63 if (m_quality) {
64 m_avCodecContext->global_quality = percentageToAbsoluteQuality(m_quality);
65 } else {
66 m_avCodecContext->global_quality = 35;
67 }
68
69 switch (m_profile) {
70 case H264Profile::Baseline:
71 m_avCodecContext->profile = FF_PROFILE_H264_BASELINE;
72 break;
73 case H264Profile::Main:
74 m_avCodecContext->profile = FF_PROFILE_H264_MAIN;
75 break;
76 case H264Profile::High:
77 m_avCodecContext->profile = FF_PROFILE_H264_HIGH;
78 break;
79 }
80
81 AVDictionary *options = nullptr;
82 av_dict_set_int(&options, "threads", qMin(16, QThread::idealThreadCount()), 0);
83 applyEncodingPreference(options);
84
85 if (int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) {
86 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open codec" << av_err2str(result);
87 return false;
88 }
89
90 return true;
91}
92
93int LibX264Encoder::percentageToAbsoluteQuality(const std::optional<quint8> &quality)
94{
95 if (!quality) {
96 return -1;
97 }
98
99 constexpr int MinQuality = 51 + 6 * 6;
100 return std::max(1, int(MinQuality - (m_quality.value() / 100.0) * MinQuality));
101}
102
103void LibX264Encoder::applyEncodingPreference(AVDictionary *options)
104{
105 SoftwareEncoder::applyEncodingPreference(options);
106 // Disable motion estimation, not great while dragging windows but speeds up encoding by an order of magnitude
107 av_dict_set(&options, "flags", "+mv4", 0);
108 // Disable in-loop filtering
109 av_dict_set(&options, "-flags", "+loop", 0);
110}
int height() const const
bool isEmpty() const const
int width() const const
int idealThreadCount()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:05:20 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.