7#include "pipewireproduce_p.h"
12#include <logging_record.h>
16#include <qstringliteral.h>
18#include "gifencoder_p.h"
19#include "h264vaapiencoder_p.h"
20#include "libopenh264encoder_p.h"
21#include "libvpxencoder_p.h"
22#include "libvpxvp9encoder_p.h"
23#include "libwebpencoder_p.h"
24#include "libx264encoder_p.h"
30Q_DECLARE_METATYPE(std::optional<int>);
31Q_DECLARE_METATYPE(std::optional<std::chrono::nanoseconds>);
33PipeWireProduce::PipeWireProduce(PipeWireBaseEncodedStream::Encoder encoderType, uint nodeId, uint fd,
const Fraction &framerate)
36 , m_encoderType(encoderType)
38 , m_frameRate(framerate)
40 qRegisterMetaType<std::optional<int>>();
41 qRegisterMetaType<std::optional<std::chrono::nanoseconds>>();
44PipeWireProduce::~PipeWireProduce()
48void PipeWireProduce::initialize()
50 m_stream.reset(
new PipeWireSourceStream(
nullptr));
51 m_stream->setMaxFramerate(m_frameRate);
60 m_stream->setUsageHint(Encoder::supportsHardwareEncoding() ? PipeWireSourceStream::UsageHint::EncodeHardware
61 : PipeWireSourceStream::UsageHint::EncodeSoftware);
63 bool created = m_stream->createStream(m_nodeId, m_fd);
64 if (!created || !m_stream->error().isEmpty()) {
65 qCWarning(PIPEWIRERECORD_LOGGING) <<
"failed to set up stream for" << m_nodeId << m_stream->error();
66 m_error = m_stream->error();
67 m_stream.reset(
nullptr);
70 connect(m_stream.get(), &PipeWireSourceStream::streamParametersChanged,
this, &PipeWireProduce::setupStream);
80 m_frameRepeatTimer.reset(
new QTimer);
81 m_frameRepeatTimer->setSingleShot(
true);
82 m_frameRepeatTimer->setInterval(100);
87 if (!m_encoder->filterFrame(f)) {
91 m_pendingFilterFrames++;
92 m_passthroughCondition.notify_all();
96Fraction PipeWireProduce::maxFramerate()
const
98 return m_maxFramerate;
101void PipeWireProduce::setMaxFramerate(
const Fraction &framerate)
103 m_maxFramerate = framerate;
105 const double framesPerSecond =
static_cast<double>(framerate.numerator) / framerate.denominator;
106 if (m_frameRepeatTimer) {
107 m_frameRepeatTimer->setInterval((1000 / framesPerSecond) * 2);
110 m_stream->setMaxFramerate(framerate);
114int PipeWireProduce::maxPendingFrames()
const
116 return m_maxPendingFrames;
119void PipeWireProduce::setMaxPendingFrames(
int newMaxBufferSize)
121 if (newMaxBufferSize < 3) {
122 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Maxmimum pending frame count of " << newMaxBufferSize <<
" requested. Value must be 3 or higher.";
123 newMaxBufferSize = 3;
125 m_maxPendingFrames = newMaxBufferSize;
128void PipeWireProduce::setupStream()
130 qCDebug(PIPEWIRERECORD_LOGGING) <<
"Setting up stream";
131 disconnect(m_stream.get(), &PipeWireSourceStream::streamParametersChanged,
this, &PipeWireProduce::setupStream);
133 m_encoder = makeEncoder();
135 qCWarning(PIPEWIRERECORD_LOGGING) <<
"No encoder could be created";
139 connect(m_stream.get(), &PipeWireSourceStream::stateChanged,
this, &PipeWireProduce::stateChanged);
140 if (!setupFormat()) {
141 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Could not set up the producing thread";
145 connect(m_stream.data(), &PipeWireSourceStream::frameReceived,
this, &PipeWireProduce::processFrame);
147 m_passthroughThread = std::thread([
this]() {
148 m_passthroughRunning =
true;
149 while (m_passthroughRunning) {
150 std::unique_lock<std::mutex> lock(m_passthroughMutex);
151 m_passthroughCondition.wait(lock);
153 if (!m_passthroughRunning) {
157 auto [
filtered, queued] = m_encoder->encodeFrame(m_maxPendingFrames - m_pendingEncodeFrames);
159 m_pendingEncodeFrames += queued;
161 m_outputCondition.notify_all();
164 pthread_setname_np(m_passthroughThread.native_handle(),
"PipeWireProduce::passthrough");
166 m_outputThread = std::thread([
this]() {
167 m_outputRunning =
true;
168 while (m_outputRunning) {
169 std::unique_lock<std::mutex> lock(m_outputMutex);
170 m_outputCondition.wait(lock);
172 if (!m_outputRunning) {
176 auto received = m_encoder->receivePacket();
177 m_pendingEncodeFrames -= received;
185 pthread_setname_np(m_outputThread.native_handle(),
"PipeWireProduce::output");
188void PipeWireProduce::deactivate()
190 m_deactivated =
true;
192 auto streamState = PW_STREAM_STATE_PAUSED;
194 streamState = m_stream->state();
195 m_stream->setActive(
false);
201 if (!m_encoder || streamState != PW_STREAM_STATE_STREAMING) {
206void PipeWireProduce::destroy()
210 Q_ASSERT_X(
QThread::currentThread() == thread(),
"PipeWireProduce",
"destroy() called from a different thread than PipeWireProduce's thread");
216 m_frameRepeatTimer->stop();
218 if (m_passthroughThread.joinable()) {
219 m_passthroughRunning =
false;
220 m_passthroughCondition.notify_all();
221 m_passthroughThread.join();
224 if (m_outputThread.joinable()) {
225 m_outputRunning =
false;
226 m_outputCondition.notify_all();
227 m_outputThread.join();
232 qCDebug(PIPEWIRERECORD_LOGGING) <<
"finished";
237void PipeWireProduce::setQuality(
const std::optional<quint8> &quality)
241 m_encoder->setQuality(quality);
245void PipeWireProduce::setEncodingPreference(
const PipeWireBaseEncodedStream::EncodingPreference &encodingPreference)
247 m_encodingPreference = encodingPreference;
250 m_encoder->setEncodingPreference(encodingPreference);
254void PipeWireProduce::processFrame(
const PipeWireFrame &frame)
259 m_frameRepeatTimer->start();
262 m_cursor.position = frame.cursor->position;
263 m_cursor.hotspot = frame.cursor->hotspot;
264 if (!frame.cursor->texture.isNull()) {
265 m_cursor.dirty =
true;
266 m_cursor.texture = frame.cursor->texture;
270 auto pts = framePts(frame.presentationTimestamp);
271 if (m_previousPts >= 0 && pts <= m_previousPts) {
275 auto frameTime = 1000.0 / (m_maxFramerate.numerator / m_maxFramerate.denominator);
276 if ((pts - m_previousPts) < frameTime) {
280 if (m_pendingFilterFrames + 1 > m_maxPendingFrames) {
281 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Filter queue is full, dropping frame" << pts;
286 if (!m_encoder->filterFrame(f)) {
290 m_pendingFilterFrames++;
293 m_passthroughCondition.notify_all();
296void PipeWireProduce::stateChanged(pw_stream_state state)
298 if (state != PW_STREAM_STATE_PAUSED || !m_deactivated) {
302 qCDebug(PIPEWIRERECORD_LOGGING) <<
"finished without a stream";
306 disconnect(m_stream.data(), &PipeWireSourceStream::frameReceived,
this, &PipeWireProduce::processFrame);
308 if (m_pendingFilterFrames <= 0 && m_pendingEncodeFrames <= 0) {
318 qCDebug(PIPEWIRERECORD_LOGGING) <<
"Waiting for frame queues to empty, still pending filter" << m_pendingFilterFrames <<
"encode"
319 << m_pendingEncodeFrames;
320 m_passthroughCondition.notify_all();
324void PipeWireProduce::handleEncodedFramesChanged()
326 if (!m_deactivated) {
335 m_passthroughCondition.notify_all();
337 if (m_pendingFilterFrames <= 0) {
340 if (m_pendingEncodeFrames <= 0) {
346std::unique_ptr<Encoder> PipeWireProduce::makeEncoder()
348 auto forcedEncoder = qEnvironmentVariable(
"KPIPEWIRE_FORCE_ENCODER");
349 if (!forcedEncoder.isNull()) {
350 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Forcing encoder to" << forcedEncoder;
353 auto size = m_stream->size();
355 switch (m_encoderType) {
356 case PipeWireBaseEncodedStream::H264Baseline:
357 case PipeWireBaseEncodedStream::H264Main: {
358 auto profile = m_encoderType == PipeWireBaseEncodedStream::H264Baseline ? Encoder::H264Profile::Baseline : Encoder::H264Profile::Main;
360 if (forcedEncoder.isNull() || forcedEncoder == u
"h264_vaapi") {
361 auto hardwareEncoder = std::make_unique<H264VAAPIEncoder>(profile,
this);
362 hardwareEncoder->setQuality(m_quality);
363 hardwareEncoder->setEncodingPreference(m_encodingPreference);
364 if (hardwareEncoder->initialize(size)) {
365 return hardwareEncoder;
369 if (forcedEncoder.isNull() || forcedEncoder == u
"libx264") {
370 auto softwareEncoder = std::make_unique<LibX264Encoder>(profile,
this);
371 softwareEncoder->setQuality(m_quality);
372 softwareEncoder->setEncodingPreference(m_encodingPreference);
373 if (softwareEncoder->initialize(size)) {
374 return softwareEncoder;
379 if (forcedEncoder.isNull() || forcedEncoder == u
"libopenh264") {
380 auto softwareEncoder = std::make_unique<LibOpenH264Encoder>(profile,
this);
381 softwareEncoder->setQuality(m_quality);
382 softwareEncoder->setEncodingPreference(m_encodingPreference);
383 if (softwareEncoder->initialize(size)) {
384 return softwareEncoder;
389 case PipeWireBaseEncodedStream::VP8: {
390 if (forcedEncoder.isNull() || forcedEncoder == u
"libvpx") {
391 auto encoder = std::make_unique<LibVpxEncoder>(
this);
392 encoder->setQuality(m_quality);
393 if (encoder->initialize(size)) {
399 case PipeWireBaseEncodedStream::VP9: {
400 if (forcedEncoder.isNull() || forcedEncoder == u
"libvpx-vp9") {
401 auto encoder = std::make_unique<LibVpxVp9Encoder>(
this);
402 encoder->setQuality(m_quality);
403 if (encoder->initialize(size)) {
409 case PipeWireBaseEncodedStream::Gif: {
410 if (forcedEncoder.isNull() || forcedEncoder == u
"gif") {
411 auto encoder = std::make_unique<GifEncoder>(
this);
412 if (encoder->initialize(size)) {
418 case PipeWireBaseEncodedStream::WebP: {
419 if (forcedEncoder.isNull() || forcedEncoder == u
"libwebp") {
420 auto encoder = std::make_unique<LibWebPEncoder>(
this);
421 encoder->setQuality(m_quality);
422 if (encoder->initialize(size)) {
429 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Unknown encoder type" << m_encoderType;
435#include "moc_pipewireproduce_p.cpp"
QFuture< typename qValueType< Iterator >::value_type > filtered(Iterator begin, Iterator end, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QThread * currentThread()