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"