File indexing completed on 2024-05-05 05:30:18

0001 /*
0002     SPDX-FileCopyrightText: 2023 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 #pragma once
0008 
0009 #include <QObject>
0010 #include <epoxy/egl.h>
0011 
0012 extern "C" {
0013 #include <pipewire/pipewire.h>
0014 #include <spa/param/format-utils.h>
0015 #include <spa/param/props.h>
0016 #include <spa/param/video/format-utils.h>
0017 }
0018 
0019 #include <QFile>
0020 #include <QImage>
0021 #include <QMutex>
0022 #include <QPoint>
0023 #include <QQueue>
0024 #include <QRunnable>
0025 #include <QThread>
0026 #include <QWaitCondition>
0027 
0028 #include <condition_variable>
0029 #include <functional>
0030 #include <mutex>
0031 #include <optional>
0032 #include <thread>
0033 
0034 #include "pipewirebaseencodedstream.h"
0035 #include "pipewiresourcestream.h"
0036 
0037 struct AVCodec;
0038 struct AVCodecContext;
0039 struct AVFrame;
0040 struct AVPacket;
0041 
0042 struct AVFilterContext;
0043 struct AVFilterGraph;
0044 
0045 class CustomAVFrame;
0046 class Encoder;
0047 class PipeWireReceiveEncodedThread;
0048 
0049 class PipeWireProduce : public QObject
0050 {
0051     Q_OBJECT
0052 public:
0053     PipeWireProduce(PipeWireBaseEncodedStream::Encoder encoderType, uint nodeId, uint fd, const Fraction &framerate);
0054     ~PipeWireProduce() override;
0055 
0056     virtual void initialize();
0057 
0058     QString error() const
0059     {
0060         return m_error;
0061     }
0062 
0063     Fraction maxFramerate() const;
0064     void setMaxFramerate(const Fraction &framerate);
0065 
0066     virtual int64_t framePts(const std::optional<std::chrono::nanoseconds> &presentationTimestamp)
0067     {
0068         return std::chrono::duration_cast<std::chrono::milliseconds>(presentationTimestamp.value()).count();
0069     }
0070 
0071     virtual void processPacket(AVPacket *packet) = 0;
0072     virtual bool setupFormat()
0073     {
0074         return true;
0075     }
0076     virtual void cleanup()
0077     {
0078     }
0079 
0080     void stateChanged(pw_stream_state state);
0081     friend class PipeWireProduceThread;
0082     void setupStream();
0083     virtual void processFrame(const PipeWireFrame &frame);
0084     void render(const QImage &image, const PipeWireFrame &frame);
0085     virtual void aboutToEncode(PipeWireFrame &frame)
0086     {
0087         Q_UNUSED(frame);
0088     }
0089 
0090     void deactivate();
0091 
0092     void setQuality(const std::optional<quint8> &quality);
0093 
0094     const uint m_nodeId;
0095     QScopedPointer<PipeWireSourceStream> m_stream;
0096     QString m_error;
0097 
0098     PipeWireBaseEncodedStream::Encoder m_encoderType;
0099     QByteArray m_encoderName;
0100     std::unique_ptr<Encoder> m_encoder;
0101 
0102     uint m_fd;
0103     Fraction m_frameRate;
0104 
0105     std::optional<quint8> m_quality;
0106 
0107     struct {
0108         QImage texture;
0109         std::optional<QPoint> position;
0110         QPoint hotspot;
0111         bool dirty = false;
0112     } m_cursor;
0113 
0114     std::thread m_passthroughThread;
0115     std::thread m_outputThread;
0116     // Can't use jthread directly as it's not available in libc++ yet,
0117     // so manually handle the stop source.
0118     std::atomic_bool m_passthroughRunning = false;
0119     std::atomic_bool m_outputRunning = false;
0120 
0121     std::condition_variable m_frameReceivedCondition;
0122     std::mutex m_frameReceivedMutex;
0123 
0124     std::atomic_bool m_deactivated = false;
0125 
0126     int64_t m_previousPts = -1;
0127 
0128     std::atomic_int m_pendingFilterFrames = 0;
0129     std::atomic_int m_pendingEncodeFrames = 0;
0130 
0131 Q_SIGNALS:
0132     void producedFrames();
0133 
0134 private:
0135     void initFiltersVaapi();
0136     void initFiltersSoftware();
0137     std::unique_ptr<Encoder> makeEncoder();
0138 };