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 auto removeUnavailableEncoders = [&vaapi](const PipeWireBaseEncodedStream::Encoder &encoder) {
213 switch (encoder) {
214 case PipeWireBaseEncodedStream::VP8:
215 if (vaapi->supportsProfile(VAProfileVP8Version0_3) && avcodec_find_encoder_by_name("vp8_vaapi")) {
216 return false;
217 } else {
218 return !avcodec_find_encoder_by_name("libvpx");
219 }
220 case PipeWireBaseEncodedStream::VP9:
221 return !avcodec_find_encoder_by_name("libvpx-vp9");
222 case PipeWireBaseEncodedStream::H264Main:
223 case PipeWireBaseEncodedStream::H264Baseline:
224 if (vaapi->supportsProfile(encoder == PipeWireBaseEncodedStream::H264Main ? VAProfileH264Main : VAProfileH264ConstrainedBaseline)
225 && avcodec_find_encoder_by_name("h264_vaapi")) {
226 return false;
227 } else {
228 return !(avcodec_find_encoder_by_name("libx264") || avcodec_find_encoder_by_name("libopenh264"));
229 }
230 default:
231 return true;
232 }
233 };
234 ret.removeIf(removeUnavailableEncoders);
235 return ret;
236}
237
238void PipeWireBaseEncodedStream::setEncodingPreference(PipeWireBaseEncodedStream::EncodingPreference preference)
239{
240 d->m_encodingPreference = preference;
241 if (d->m_produce) {
242 d->m_produce->setEncodingPreference(d->m_encodingPreference);
243 }
244}
245
246PipeWireBaseEncodedStream::EncodingPreference PipeWireBaseEncodedStream::encodingPreference()
247{
248 return d->m_encodingPreference;
249}
250
251bool PipeWireBaseEncodedStream::isActive() const
252{
253 return d->m_active;
254}
255
256uint PipeWireBaseEncodedStream::nodeId() const
257{
258 return d->m_nodeId;
259}
260
261uint PipeWireBaseEncodedStream::fd() const
262{
263 return d->m_fd.value_or(0);
264}
265
266#include "moc_pipewirebaseencodedstream.cpp"
KGuiItem 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 Fri Nov 22 2024 12:08:09 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.