KPipewire

libopenh264encoder.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 SPDX-FileCopyrightText: 2024 Fabian Vogt <fabian@ritter-vogt.de>
6
7 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
8*/
9
10#include "libopenh264encoder_p.h"
11
12#include <QSize>
13#include <QThread>
14
15extern "C" {
16#include <libavcodec/avcodec.h>
17#include <libavfilter/buffersink.h>
18#include <libavfilter/buffersrc.h>
19#include <libavutil/pixfmt.h>
20}
21
22#include "logging_record.h"
23
24LibOpenH264Encoder::LibOpenH264Encoder(H264Profile profile, PipeWireProduce *produce)
25 : SoftwareEncoder(produce)
26 , m_profile(profile)
27{
28}
29
30bool LibOpenH264Encoder::initialize(const QSize &size)
31{
32 createFilterGraph(size);
33
34 auto codec = avcodec_find_encoder_by_name("libopenh264");
35 if (!codec) {
36 qCWarning(PIPEWIRERECORD_LOGGING) << "libopenh264 codec not found";
37 return false;
38 }
39
40 m_avCodecContext = avcodec_alloc_context3(codec);
41 if (!m_avCodecContext) {
42 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not allocate video codec context";
43 return false;
44 }
45
46 Q_ASSERT(!size.isEmpty());
47 m_avCodecContext->width = size.width();
48 m_avCodecContext->height = size.height();
49 m_avCodecContext->max_b_frames = 0;
50 m_avCodecContext->gop_size = 100;
51 m_avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
52 m_avCodecContext->time_base = AVRational{1, 1000};
53
54 if (m_quality) {
55 // "q" here stands for "quantization", but that effectively impacts quality.
56 m_avCodecContext->qmin = m_avCodecContext->qmax = percentageToAbsoluteQuality(m_quality);
57 }
58
59 switch (m_profile) {
60 case H264Profile::Baseline:
61 // libopenh264 only does constrained baseline.
62 // There's a bug in the ffmpeg -> openh264 interface though:
63 // ffmpeg expects CONSTRAINED_BASELINE from the application and
64 // passes that through, but libopenh264 only allows BASELINE.
65 // Until that bug is fixed there'll always be a warning that the
66 // profile is not supported (https://github.com/cisco/openh264/issues/3613)
67 m_avCodecContext->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;
68 break;
69 case H264Profile::Main:
70 m_avCodecContext->profile = FF_PROFILE_H264_MAIN;
71 break;
72 case H264Profile::High:
73 m_avCodecContext->profile = FF_PROFILE_H264_HIGH;
74 break;
75 }
76
77 AVDictionary *options = nullptr;
78 av_dict_set_int(&options, "threads", qMin(16, QThread::idealThreadCount()), 0);
79 applyEncodingPreference(options);
80
81 if (int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) {
82 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open codec" << av_err2str(result);
83 return false;
84 }
85
86 return true;
87}
88
89int LibOpenH264Encoder::percentageToAbsoluteQuality(const std::optional<quint8> &quality)
90{
91 if (!quality) {
92 return -1;
93 }
94
95 // 1-51 (incl.), lower is better
96 return 51 - (m_quality.value() / 100.0) * 50;
97}
98
99void LibOpenH264Encoder::applyEncodingPreference(AVDictionary *options)
100{
101 SoftwareEncoder::applyEncodingPreference(options);
102 // Disable motion estimation, not great while dragging windows but speeds up encoding by an order of magnitude
103 av_dict_set(&options, "flags", "+mv4", 0);
104 // Disable in-loop filtering
105 av_dict_set_int(&options, "loopfilter", 0, 0);
106}
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.