File indexing completed on 2024-05-05 05:30:18
0001 /* 0002 SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "pipewireproduce_p.h" 0008 0009 #include <QMutex> 0010 #include <QPainter> 0011 #include <QThreadPool> 0012 #include <logging_record.h> 0013 0014 #include <QDateTime> 0015 #include <memory> 0016 #include <qstringliteral.h> 0017 0018 #include "h264vaapiencoder_p.h" 0019 #include "libvpxencoder_p.h" 0020 #include "libvpxvp9encoder_p.h" 0021 #include "libx264encoder_p.h" 0022 0023 extern "C" { 0024 #include <fcntl.h> 0025 } 0026 0027 // The maximum number of frames to allow in either the filter or encode queue. 0028 // Note that this needs to be large enough for the encoder to be able to do 0029 // intra-frame analysis. 0030 static constexpr int MaxQueueSize = 50; 0031 0032 Q_DECLARE_METATYPE(std::optional<int>); 0033 Q_DECLARE_METATYPE(std::optional<std::chrono::nanoseconds>); 0034 0035 PipeWireProduce::PipeWireProduce(PipeWireBaseEncodedStream::Encoder encoderType, uint nodeId, uint fd, const Fraction &framerate) 0036 : QObject() 0037 , m_nodeId(nodeId) 0038 , m_encoderType(encoderType) 0039 , m_fd(fd) 0040 , m_frameRate(framerate) 0041 { 0042 qRegisterMetaType<std::optional<int>>(); 0043 qRegisterMetaType<std::optional<std::chrono::nanoseconds>>(); 0044 } 0045 0046 PipeWireProduce::~PipeWireProduce() 0047 { 0048 } 0049 0050 void PipeWireProduce::initialize() 0051 { 0052 m_stream.reset(new PipeWireSourceStream(nullptr)); 0053 m_stream->setMaxFramerate(m_frameRate); 0054 bool created = m_stream->createStream(m_nodeId, m_fd); 0055 if (!created || !m_stream->error().isEmpty()) { 0056 qCWarning(PIPEWIRERECORD_LOGGING) << "failed to set up stream for" << m_nodeId << m_stream->error(); 0057 m_error = m_stream->error(); 0058 m_stream.reset(nullptr); 0059 return; 0060 } 0061 connect(m_stream.get(), &PipeWireSourceStream::streamParametersChanged, this, &PipeWireProduce::setupStream); 0062 } 0063 0064 Fraction PipeWireProduce::maxFramerate() const 0065 { 0066 return m_stream->framerate(); 0067 } 0068 0069 void PipeWireProduce::setMaxFramerate(const Fraction &framerate) 0070 { 0071 m_stream->setMaxFramerate(framerate); 0072 } 0073 0074 void PipeWireProduce::setupStream() 0075 { 0076 qCDebug(PIPEWIRERECORD_LOGGING) << "Setting up stream"; 0077 disconnect(m_stream.get(), &PipeWireSourceStream::streamParametersChanged, this, &PipeWireProduce::setupStream); 0078 0079 m_encoder = makeEncoder(); 0080 if (!m_encoder) { 0081 qCWarning(PIPEWIRERECORD_LOGGING) << "No encoder could be created"; 0082 return; 0083 } 0084 0085 connect(m_stream.get(), &PipeWireSourceStream::stateChanged, this, &PipeWireProduce::stateChanged); 0086 if (!setupFormat()) { 0087 qCWarning(PIPEWIRERECORD_LOGGING) << "Could not set up the producing thread"; 0088 return; 0089 } 0090 0091 connect(m_stream.data(), &PipeWireSourceStream::frameReceived, this, &PipeWireProduce::processFrame); 0092 0093 m_passthroughThread = std::thread([this]() { 0094 m_passthroughRunning = true; 0095 while (m_passthroughRunning) { 0096 std::unique_lock<std::mutex> lock(m_frameReceivedMutex); 0097 m_frameReceivedCondition.wait(lock); 0098 0099 if (!m_passthroughRunning) { 0100 break; 0101 } 0102 0103 auto [filtered, queued] = m_encoder->encodeFrame(MaxQueueSize - m_pendingEncodeFrames); 0104 m_pendingFilterFrames -= filtered; 0105 m_pendingEncodeFrames += queued; 0106 0107 m_frameReceivedCondition.notify_all(); 0108 } 0109 }); 0110 pthread_setname_np(m_passthroughThread.native_handle(), "PipeWireProduce::passthrough"); 0111 0112 m_outputThread = std::thread([this]() { 0113 m_outputRunning = true; 0114 while (m_outputRunning) { 0115 std::unique_lock<std::mutex> lock(m_frameReceivedMutex); 0116 m_frameReceivedCondition.wait(lock); 0117 0118 if (!m_outputRunning) { 0119 break; 0120 } 0121 0122 auto received = m_encoder->receivePacket(); 0123 m_pendingEncodeFrames -= received; 0124 } 0125 }); 0126 pthread_setname_np(m_outputThread.native_handle(), "PipeWireProduce::output"); 0127 } 0128 0129 void PipeWireProduce::deactivate() 0130 { 0131 m_deactivated = true; 0132 m_stream->setActive(false); 0133 } 0134 0135 void PipeWireProduce::setQuality(const std::optional<quint8> &quality) 0136 { 0137 m_quality = quality; 0138 if (m_encoder) { 0139 m_encoder->setQuality(quality); 0140 } 0141 } 0142 0143 void PipeWireProduce::processFrame(const PipeWireFrame &frame) 0144 { 0145 auto f = frame; 0146 0147 if (frame.cursor) { 0148 m_cursor.position = frame.cursor->position; 0149 m_cursor.hotspot = frame.cursor->hotspot; 0150 if (!frame.cursor->texture.isNull()) { 0151 m_cursor.dirty = true; 0152 m_cursor.texture = frame.cursor->texture; 0153 } 0154 } 0155 0156 auto pts = framePts(frame.presentationTimestamp); 0157 if (m_previousPts >= 0 && pts <= m_previousPts) { 0158 return; 0159 } 0160 0161 if (m_pendingFilterFrames + 1 > MaxQueueSize) { 0162 qCWarning(PIPEWIRERECORD_LOGGING) << "Filter queue is full, dropping frame" << pts; 0163 return; 0164 } 0165 0166 m_pendingFilterFrames++; 0167 m_previousPts = pts; 0168 0169 aboutToEncode(f); 0170 m_encoder->filterFrame(f); 0171 0172 m_frameReceivedCondition.notify_all(); 0173 } 0174 0175 void PipeWireProduce::stateChanged(pw_stream_state state) 0176 { 0177 if (state != PW_STREAM_STATE_PAUSED || !m_deactivated) { 0178 return; 0179 } 0180 if (!m_stream) { 0181 qCDebug(PIPEWIRERECORD_LOGGING) << "finished without a stream"; 0182 return; 0183 } 0184 0185 disconnect(m_stream.data(), &PipeWireSourceStream::frameReceived, this, &PipeWireProduce::processFrame); 0186 0187 m_encoder->finish(); 0188 0189 if (m_passthroughThread.joinable()) { 0190 m_passthroughRunning = false; 0191 m_frameReceivedCondition.notify_all(); 0192 m_passthroughThread.join(); 0193 } 0194 0195 if (m_outputThread.joinable()) { 0196 m_outputRunning = false; 0197 m_frameReceivedCondition.notify_all(); 0198 m_outputThread.join(); 0199 } 0200 0201 qCDebug(PIPEWIRERECORD_LOGGING) << "finished"; 0202 cleanup(); 0203 QThread::currentThread()->quit(); 0204 } 0205 0206 std::unique_ptr<Encoder> PipeWireProduce::makeEncoder() 0207 { 0208 auto encoderType = m_encoderType; 0209 bool forceSoftware = false; 0210 bool forceHardware = false; 0211 0212 if (qEnvironmentVariableIsSet("KPIPEWIRE_FORCE_ENCODER")) { 0213 auto forcedEncoder = qEnvironmentVariable("KPIPEWIRE_FORCE_ENCODER"); 0214 if (forcedEncoder == u"libvpx") { 0215 qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing VP8 Software encoding"; 0216 encoderType = PipeWireBaseEncodedStream::VP8; 0217 forceSoftware = true; 0218 } else if (forcedEncoder == u"libvpx-vp9") { 0219 qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing VP9 Software encoding"; 0220 encoderType = PipeWireBaseEncodedStream::VP9; 0221 forceSoftware = true; 0222 } else if (forcedEncoder == u"libx264") { 0223 qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing H264 Software encoding, main profile"; 0224 encoderType = PipeWireBaseEncodedStream::H264Main; 0225 forceSoftware = true; 0226 } else if (forcedEncoder == u"h264_vaapi") { 0227 qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing H264 Hardware encoding, main profile"; 0228 encoderType = PipeWireBaseEncodedStream::H264Main; 0229 forceHardware = true; 0230 } else if (forcedEncoder == u"libx264_baseline") { 0231 qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing H264 Software encoding, baseline profile"; 0232 encoderType = PipeWireBaseEncodedStream::H264Baseline; 0233 forceSoftware = true; 0234 } else if (forcedEncoder == u"h264_vaapi_baseline") { 0235 qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing H264 Hardware encoding, baseline profile"; 0236 encoderType = PipeWireBaseEncodedStream::H264Baseline; 0237 forceHardware = true; 0238 } 0239 } 0240 0241 auto size = m_stream->size(); 0242 0243 switch (encoderType) { 0244 case PipeWireBaseEncodedStream::H264Baseline: 0245 case PipeWireBaseEncodedStream::H264Main: { 0246 auto profile = m_encoderType == PipeWireBaseEncodedStream::H264Baseline ? Encoder::H264Profile::Baseline : Encoder::H264Profile::Main; 0247 0248 if (!forceSoftware) { 0249 auto hardwareEncoder = std::make_unique<H264VAAPIEncoder>(profile, this); 0250 hardwareEncoder->setQuality(m_quality); 0251 if (hardwareEncoder->initialize(size)) { 0252 return hardwareEncoder; 0253 } 0254 } 0255 0256 if (!forceHardware) { 0257 auto softwareEncoder = std::make_unique<LibX264Encoder>(profile, this); 0258 softwareEncoder->setQuality(m_quality); 0259 if (softwareEncoder->initialize(size)) { 0260 return softwareEncoder; 0261 } 0262 } 0263 break; 0264 } 0265 case PipeWireBaseEncodedStream::VP8: { 0266 if (!forceHardware) { 0267 auto encoder = std::make_unique<LibVpxEncoder>(this); 0268 encoder->setQuality(m_quality); 0269 if (encoder->initialize(size)) { 0270 return encoder; 0271 } 0272 } 0273 break; 0274 } 0275 case PipeWireBaseEncodedStream::VP9: { 0276 if (!forceHardware) { 0277 auto encoder = std::make_unique<LibVpxVp9Encoder>(this); 0278 encoder->setQuality(m_quality); 0279 if (encoder->initialize(size)) { 0280 return encoder; 0281 } 0282 } 0283 break; 0284 } 0285 default: 0286 qCWarning(PIPEWIRERECORD_LOGGING) << "Unknown encoder type" << m_encoderType; 0287 } 0288 0289 return nullptr; 0290 } 0291 0292 #include "moc_pipewireproduce_p.cpp"