7#include "pipewireproduce_p.h"
12#include <logging_record.h>
16#include <qstringliteral.h>
18#include "h264vaapiencoder_p.h"
19#include "libopenh264encoder_p.h"
20#include "libvpxencoder_p.h"
21#include "libvpxvp9encoder_p.h"
22#include "libx264encoder_p.h"
28Q_DECLARE_METATYPE(std::optional<int>);
29Q_DECLARE_METATYPE(std::optional<std::chrono::nanoseconds>);
31PipeWireProduce::PipeWireProduce(PipeWireBaseEncodedStream::Encoder encoderType, uint nodeId, uint fd,
const Fraction &framerate)
34 , m_encoderType(encoderType)
36 , m_frameRate(framerate)
38 qRegisterMetaType<std::optional<int>>();
39 qRegisterMetaType<std::optional<std::chrono::nanoseconds>>();
42PipeWireProduce::~PipeWireProduce()
46void PipeWireProduce::initialize()
48 m_stream.reset(
new PipeWireSourceStream(
nullptr));
49 m_stream->setMaxFramerate(m_frameRate);
58 m_stream->setUsageHint(Encoder::supportsHardwareEncoding() ? PipeWireSourceStream::UsageHint::EncodeHardware
59 : PipeWireSourceStream::UsageHint::EncodeSoftware);
61 bool created = m_stream->createStream(m_nodeId, m_fd);
62 if (!created || !m_stream->error().isEmpty()) {
63 qCWarning(PIPEWIRERECORD_LOGGING) <<
"failed to set up stream for" << m_nodeId << m_stream->error();
64 m_error = m_stream->error();
65 m_stream.reset(
nullptr);
68 connect(m_stream.get(), &PipeWireSourceStream::streamParametersChanged,
this, &PipeWireProduce::setupStream);
71Fraction PipeWireProduce::maxFramerate()
const
73 return m_maxFramerate;
76void PipeWireProduce::setMaxFramerate(
const Fraction &framerate)
78 m_maxFramerate = framerate;
80 m_stream->setMaxFramerate(framerate);
84int PipeWireProduce::maxPendingFrames()
const
86 return m_maxPendingFrames;
89void PipeWireProduce::setMaxPendingFrames(
int newMaxBufferSize)
91 if (newMaxBufferSize < 3) {
92 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Maxmimum pending frame count of " << newMaxBufferSize <<
" requested. Value must be 3 or higher.";
95 m_maxPendingFrames = newMaxBufferSize;
98void PipeWireProduce::setupStream()
100 qCDebug(PIPEWIRERECORD_LOGGING) <<
"Setting up stream";
101 disconnect(m_stream.get(), &PipeWireSourceStream::streamParametersChanged,
this, &PipeWireProduce::setupStream);
103 m_encoder = makeEncoder();
105 qCWarning(PIPEWIRERECORD_LOGGING) <<
"No encoder could be created";
109 connect(m_stream.get(), &PipeWireSourceStream::stateChanged,
this, &PipeWireProduce::stateChanged);
110 if (!setupFormat()) {
111 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Could not set up the producing thread";
115 connect(m_stream.data(), &PipeWireSourceStream::frameReceived,
this, &PipeWireProduce::processFrame);
117 m_passthroughThread = std::thread([
this]() {
118 m_passthroughRunning =
true;
119 while (m_passthroughRunning) {
120 std::unique_lock<std::mutex> lock(m_passthroughMutex);
121 m_passthroughCondition.wait(lock);
123 if (!m_passthroughRunning) {
127 auto [
filtered, queued] = m_encoder->encodeFrame(m_maxPendingFrames - m_pendingEncodeFrames);
129 m_pendingEncodeFrames += queued;
131 m_outputCondition.notify_all();
134 pthread_setname_np(m_passthroughThread.native_handle(),
"PipeWireProduce::passthrough");
136 m_outputThread = std::thread([
this]() {
137 m_outputRunning =
true;
138 while (m_outputRunning) {
139 std::unique_lock<std::mutex> lock(m_outputMutex);
140 m_outputCondition.wait(lock);
142 if (!m_outputRunning) {
146 auto received = m_encoder->receivePacket();
147 m_pendingEncodeFrames -= received;
155 pthread_setname_np(m_outputThread.native_handle(),
"PipeWireProduce::output");
158void PipeWireProduce::deactivate()
160 m_deactivated =
true;
162 auto streamState = PW_STREAM_STATE_PAUSED;
164 streamState = m_stream->state();
165 m_stream->setActive(
false);
171 if (!m_encoder || streamState != PW_STREAM_STATE_STREAMING) {
176void PipeWireProduce::destroy()
180 Q_ASSERT_X(
QThread::currentThread() == thread(),
"PipeWireProduce",
"destroy() called from a different thread than PipeWireProduce's thread");
186 if (m_passthroughThread.joinable()) {
187 m_passthroughRunning =
false;
188 m_passthroughCondition.notify_all();
189 m_passthroughThread.join();
192 if (m_outputThread.joinable()) {
193 m_outputRunning =
false;
194 m_outputCondition.notify_all();
195 m_outputThread.join();
200 qCDebug(PIPEWIRERECORD_LOGGING) <<
"finished";
205void PipeWireProduce::setQuality(
const std::optional<quint8> &quality)
209 m_encoder->setQuality(quality);
213void PipeWireProduce::setEncodingPreference(
const PipeWireBaseEncodedStream::EncodingPreference &encodingPreference)
215 m_encodingPreference = encodingPreference;
218 m_encoder->setEncodingPreference(encodingPreference);
222void PipeWireProduce::processFrame(
const PipeWireFrame &frame)
227 m_cursor.position = frame.cursor->position;
228 m_cursor.hotspot = frame.cursor->hotspot;
229 if (!frame.cursor->texture.isNull()) {
230 m_cursor.dirty =
true;
231 m_cursor.texture = frame.cursor->texture;
235 auto pts = framePts(frame.presentationTimestamp);
236 if (m_previousPts >= 0 && pts <= m_previousPts) {
240 auto frameTime = 1000.0 / (m_maxFramerate.numerator / m_maxFramerate.denominator);
241 if ((pts - m_previousPts) < frameTime) {
245 if (m_pendingFilterFrames + 1 > m_maxPendingFrames) {
246 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Filter queue is full, dropping frame" << pts;
251 if (!m_encoder->filterFrame(f)) {
255 m_pendingFilterFrames++;
258 m_passthroughCondition.notify_all();
261void PipeWireProduce::stateChanged(pw_stream_state state)
263 if (state != PW_STREAM_STATE_PAUSED || !m_deactivated) {
267 qCDebug(PIPEWIRERECORD_LOGGING) <<
"finished without a stream";
271 disconnect(m_stream.data(), &PipeWireSourceStream::frameReceived,
this, &PipeWireProduce::processFrame);
273 if (m_pendingFilterFrames <= 0 && m_pendingEncodeFrames <= 0) {
283 qCDebug(PIPEWIRERECORD_LOGGING) <<
"Waiting for frame queues to empty, still pending filter" << m_pendingFilterFrames <<
"encode"
284 << m_pendingEncodeFrames;
285 m_passthroughCondition.notify_all();
289void PipeWireProduce::handleEncodedFramesChanged()
291 if (!m_deactivated) {
300 m_passthroughCondition.notify_all();
302 if (m_pendingFilterFrames <= 0) {
305 if (m_pendingEncodeFrames <= 0) {
311std::unique_ptr<Encoder> PipeWireProduce::makeEncoder()
313 auto forcedEncoder = qEnvironmentVariable(
"KPIPEWIRE_FORCE_ENCODER");
314 if (!forcedEncoder.isNull()) {
315 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Forcing encoder to" << forcedEncoder;
318 auto size = m_stream->size();
320 switch (m_encoderType) {
321 case PipeWireBaseEncodedStream::H264Baseline:
322 case PipeWireBaseEncodedStream::H264Main: {
323 auto profile = m_encoderType == PipeWireBaseEncodedStream::H264Baseline ? Encoder::H264Profile::Baseline : Encoder::H264Profile::Main;
325 if (forcedEncoder.isNull() || forcedEncoder == u
"h264_vaapi") {
326 auto hardwareEncoder = std::make_unique<H264VAAPIEncoder>(profile,
this);
327 hardwareEncoder->setQuality(m_quality);
328 hardwareEncoder->setEncodingPreference(m_encodingPreference);
329 if (hardwareEncoder->initialize(size)) {
330 return hardwareEncoder;
334 if (forcedEncoder.isNull() || forcedEncoder == u
"libx264") {
335 auto softwareEncoder = std::make_unique<LibX264Encoder>(profile,
this);
336 softwareEncoder->setQuality(m_quality);
337 softwareEncoder->setEncodingPreference(m_encodingPreference);
338 if (softwareEncoder->initialize(size)) {
339 return softwareEncoder;
344 if (forcedEncoder.isNull() || forcedEncoder == u
"libopenh264") {
345 auto softwareEncoder = std::make_unique<LibOpenH264Encoder>(profile,
this);
346 softwareEncoder->setQuality(m_quality);
347 softwareEncoder->setEncodingPreference(m_encodingPreference);
348 if (softwareEncoder->initialize(size)) {
349 return softwareEncoder;
354 case PipeWireBaseEncodedStream::VP8: {
355 if (forcedEncoder.isNull() || forcedEncoder == u
"libvpx") {
356 auto encoder = std::make_unique<LibVpxEncoder>(
this);
357 encoder->setQuality(m_quality);
358 if (encoder->initialize(size)) {
364 case PipeWireBaseEncodedStream::VP9: {
365 if (forcedEncoder.isNull() || forcedEncoder == u
"libvpx-vp9") {
366 auto encoder = std::make_unique<LibVpxVp9Encoder>(
this);
367 encoder->setQuality(m_quality);
368 if (encoder->initialize(size)) {
375 qCWarning(PIPEWIRERECORD_LOGGING) <<
"Unknown encoder type" << m_encoderType;
381#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()