KPipewire

pipewirebaseencodedstream.cpp
1/*
2 SPDX-FileCopyrightText: 2022-2023 Aleix Pol Gonzalez <aleixpol@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "pipewirebaseencodedstream.h"
8
9#include <logging_libav.h>
10#include <logging_record.h>
11#include <memory>
12#include <va/va.h>
13
14extern "C" {
15#include <libavcodec/codec.h>
16#include <libavutil/log.h>
17}
18#include <unistd.h>
19
20#include "pipewireproduce_p.h"
21#include "vaapiutils_p.h"
22
23struct PipeWireEncodedStreamPrivate {
24 uint m_nodeId = 0;
25 std::optional<uint> m_fd;
26 Fraction m_maxFramerate;
27 int m_maxPendingFrames = 50;
28 bool m_active = false;
29 PipeWireBaseEncodedStream::Encoder m_encoder;
30 std::optional<quint8> m_quality;
31 PipeWireBaseEncodedStream::EncodingPreference m_encodingPreference;
32
33 std::unique_ptr<QThread> m_produceThread;
34 std::unique_ptr<PipeWireProduce> m_produce;
35};
36
37PipeWireBaseEncodedStream::State PipeWireBaseEncodedStream::state() const
38{
39 if (isActive()) {
40 return Recording;
41 } else if (d->m_produceThread && d->m_produce->m_deactivated && d->m_produceThread->isRunning()) {
42 return Rendering;
43 }
44
45 return Idle;
46}
47
48PipeWireBaseEncodedStream::PipeWireBaseEncodedStream(QObject *parent)
49 : QObject(parent)
50 , d(new PipeWireEncodedStreamPrivate)
51{
52 d->m_encoder = suggestedEncoders().value(0, NoEncoder);
53
54 const auto &category = PIPEWIRELIBAV_LOGGING();
55 if (category.isDebugEnabled()) {
56 av_log_set_level(AV_LOG_DEBUG);
57 } else if (category.isInfoEnabled()) {
58 av_log_set_level(AV_LOG_INFO);
59 } else if (category.isWarningEnabled()) {
60 av_log_set_level(AV_LOG_WARNING);
61 } else {
62 av_log_set_level(AV_LOG_ERROR);
63 }
64}
65
66PipeWireBaseEncodedStream::~PipeWireBaseEncodedStream()
67{
68 setActive(false);
69
70 if (d->m_fd) {
71 close(*d->m_fd);
72 }
73}
74
75void PipeWireBaseEncodedStream::setNodeId(uint nodeId)
76{
77 if (nodeId == d->m_nodeId)
78 return;
79
80 d->m_nodeId = nodeId;
81 refresh();
82 Q_EMIT nodeIdChanged(nodeId);
83}
84
85void PipeWireBaseEncodedStream::setFd(uint fd)
86{
87 if (fd == d->m_fd)
88 return;
89
90 if (d->m_fd) {
91 close(*d->m_fd);
92 }
93 d->m_fd = fd;
94 refresh();
95 Q_EMIT fdChanged(fd);
96}
97
98Fraction PipeWireBaseEncodedStream::maxFramerate() const
99{
100 if (d->m_maxFramerate) {
101 return d->m_maxFramerate;
102 }
103 return Fraction{60, 1};
104}
105
106void PipeWireBaseEncodedStream::setMaxFramerate(const Fraction &framerate)
107{
108 if (d->m_maxFramerate == framerate) {
109 return;
110 }
111 d->m_maxFramerate = framerate;
112
113 if (d->m_produce) {
114 d->m_produce->setMaxFramerate(d->m_maxFramerate);
115 }
116
117 Q_EMIT maxFramerateChanged();
118}
119
120void PipeWireBaseEncodedStream::setMaxFramerate(quint32 numerator, quint32 denominator)
121{
122 setMaxFramerate({numerator, denominator});
123}
124
125void PipeWireBaseEncodedStream::setMaxPendingFrames(int maxPendingFrames)
126{
127 if (d->m_maxPendingFrames == maxPendingFrames) {
128 return;
129 }
130 if (d->m_produce) {
131 d->m_produce->setMaxPendingFrames(maxPendingFrames);
132 }
133 d->m_maxPendingFrames = maxPendingFrames;
134 Q_EMIT maxPendingFramesChanged();
135}
136
137int PipeWireBaseEncodedStream::maxBufferSize() const
138{
139 return d->m_maxPendingFrames;
140}
141
142void PipeWireBaseEncodedStream::setActive(bool active)
143{
144 if (d->m_active == active)
145 return;
146
147 d->m_active = active;
148 refresh();
149 Q_EMIT activeChanged(active);
150}
151
152std::optional<quint8> PipeWireBaseEncodedStream::quality() const
153{
154 return d->m_quality;
155}
156
157void PipeWireBaseEncodedStream::setQuality(quint8 quality)
158{
159 d->m_quality = quality;
160 if (d->m_produce) {
161 d->m_produce->setQuality(d->m_quality);
162 }
163}
164
165void PipeWireBaseEncodedStream::refresh()
166{
167 if (d->m_produceThread) {
168 QMetaObject::invokeMethod(d->m_produce.get(), &PipeWireProduce::deactivate, Qt::QueuedConnection);
169 d->m_produceThread->wait();
170
171 d->m_produce.reset();
172 d->m_produceThread.reset();
173 }
174
175 if (d->m_active && d->m_nodeId > 0) {
176 d->m_produceThread = std::make_unique<QThread>();
177 d->m_produceThread->setObjectName("PipeWireProduce::input");
178 d->m_produce = makeProduce();
179 d->m_produce->setQuality(d->m_quality);
180 d->m_produce->setMaxPendingFrames(d->m_maxPendingFrames);
181 d->m_produce->setEncodingPreference(d->m_encodingPreference);
182 d->m_produce->moveToThread(d->m_produceThread.get());
183 d->m_produceThread->start();
184 QMetaObject::invokeMethod(d->m_produce.get(), &PipeWireProduce::initialize, Qt::QueuedConnection);
185 }
186
187 Q_EMIT stateChanged();
188}
189
190void PipeWireBaseEncodedStream::setEncoder(Encoder encoder)
191{
192 if (d->m_encoder == encoder || !suggestedEncoders().contains(encoder)) {
193 return;
194 }
195 d->m_encoder = encoder;
196 Q_EMIT encoderChanged();
197}
198
199PipeWireBaseEncodedStream::Encoder PipeWireBaseEncodedStream::encoder() const
200{
201 return d->m_encoder;
202}
203
204QList<PipeWireBaseEncodedStream::Encoder> PipeWireBaseEncodedStream::suggestedEncoders() const
205{
206 auto vaapi = VaapiUtils::instance();
207
208 QList<PipeWireBaseEncodedStream::Encoder> ret = {PipeWireBaseEncodedStream::VP8,
209 PipeWireBaseEncodedStream::VP9,
210 PipeWireBaseEncodedStream::H264Main,
211 PipeWireBaseEncodedStream::H264Baseline,
212 PipeWireBaseEncodedStream::WebP,
213 PipeWireBaseEncodedStream::Gif,
214 };
215 auto removeUnavailableEncoders = [&vaapi](const PipeWireBaseEncodedStream::Encoder &encoder) {
216 switch (encoder) {
217 case PipeWireBaseEncodedStream::VP8:
218 if (vaapi->supportsProfile(VAProfileVP8Version0_3) && avcodec_find_encoder_by_name("vp8_vaapi")) {
219 return false;
220 } else {
221 return !avcodec_find_encoder_by_name("libvpx");
222 }
223 case PipeWireBaseEncodedStream::VP9:
224 return !avcodec_find_encoder_by_name("libvpx-vp9");
225 case PipeWireBaseEncodedStream::H264Main:
226 case PipeWireBaseEncodedStream::H264Baseline:
227 if (vaapi->supportsProfile(encoder == PipeWireBaseEncodedStream::H264Main ? VAProfileH264Main : VAProfileH264ConstrainedBaseline)
228 && avcodec_find_encoder_by_name("h264_vaapi")) {
229 return false;
230 } else {
231 return !(avcodec_find_encoder_by_name("libx264") || avcodec_find_encoder_by_name("libopenh264"));
232 }
233 case PipeWireBaseEncodedStream::WebP:
234 return !avcodec_find_encoder_by_name("libwebp");
235 case PipeWireBaseEncodedStream::Gif:
236 return !avcodec_find_encoder_by_name("gif");
237 default:
238 return true;
239 }
240 };
241 ret.removeIf(removeUnavailableEncoders);
242 return ret;
243}
244
245void PipeWireBaseEncodedStream::setEncodingPreference(PipeWireBaseEncodedStream::EncodingPreference preference)
246{
247 d->m_encodingPreference = preference;
248 if (d->m_produce) {
249 d->m_produce->setEncodingPreference(d->m_encodingPreference);
250 }
251}
252
253PipeWireBaseEncodedStream::EncodingPreference PipeWireBaseEncodedStream::encodingPreference()
254{
255 return d->m_encodingPreference;
256}
257
258bool PipeWireBaseEncodedStream::isActive() const
259{
260 return d->m_active;
261}
262
263uint PipeWireBaseEncodedStream::nodeId() const
264{
265 return d->m_nodeId;
266}
267
268uint PipeWireBaseEncodedStream::fd() const
269{
270 return d->m_fd.value_or(0);
271}
272
273#include "moc_pipewirebaseencodedstream.cpp"
const QList< QKeySequence > & close()
Category category(StandardShortcut id)
qsizetype removeIf(Predicate pred)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
Q_EMITQ_EMIT
QueuedConnection
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.