File indexing completed on 2024-03-24 17:02:23

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 #pragma once
0008 
0009 #include <QFile>
0010 #include <QImage>
0011 #include <QPoint>
0012 #include <QRunnable>
0013 #include <QThread>
0014 #include <QWaitCondition>
0015 
0016 #include <functional>
0017 #include <optional>
0018 
0019 #include <epoxy/egl.h>
0020 #include <pipewire/pipewire.h>
0021 #include <spa/param/format-utils.h>
0022 #include <spa/param/props.h>
0023 #include <spa/param/video/format-utils.h>
0024 
0025 #include "dmabufhandler.h"
0026 #include "pipewiresourcestream.h"
0027 
0028 struct AVCodec;
0029 struct AVCodecContext;
0030 struct AVFrame;
0031 struct AVFormatContext;
0032 struct AVPacket;
0033 class CustomAVFrame;
0034 class PipeWireRecordProduce;
0035 struct gbm_device;
0036 
0037 class PipeWireRecordWrite : public QObject
0038 {
0039 public:
0040     PipeWireRecordWrite(PipeWireRecordProduce *produce, AVFormatContext *avFormatContext, AVCodecContext *avCodecContext);
0041     ~PipeWireRecordWrite();
0042 
0043     void addFrame(const QImage &image, std::optional<int> sequential, std::optional<std::chrono::nanoseconds> presentationTimestamp);
0044 
0045 private:
0046     QAtomicInt m_active = true;
0047     AVPacket *m_packet;
0048     AVFormatContext *const m_avFormatContext;
0049     AVCodecContext *const m_avCodecContext;
0050     struct SwsContext *sws_context = nullptr;
0051     int64_t m_lastPts = -1;
0052     uint m_lastKeyFrame = 0;
0053     QSize m_lastReceivedSize;
0054 };
0055 
0056 class PipeWireRecordWriteThread : public QThread
0057 {
0058 public:
0059     PipeWireRecordWriteThread(PipeWireRecordProduce *produce, AVFormatContext *avFormatContext, AVCodecContext *avCodecContext);
0060 
0061     void run() override;
0062     void drain();
0063 
0064 private:
0065     PipeWireRecordProduce *const m_produce;
0066     AVFormatContext *const m_avFormatContext;
0067     AVCodecContext *const m_avCodecContext;
0068 };
0069 
0070 class PipeWireRecordProduce : public QObject
0071 {
0072     Q_OBJECT
0073 public:
0074     PipeWireRecordProduce(const QByteArray &encoder, uint nodeId, uint fd, const QString &output);
0075     ~PipeWireRecordProduce() override;
0076 
0077     QString error() const
0078     {
0079         return m_error;
0080     }
0081 
0082 Q_SIGNALS:
0083     void producedFrame(const QImage &image, std::optional<int> sequential, std::optional<std::chrono::nanoseconds> presentationTimestamp);
0084 
0085 private:
0086     friend class PipeWireRecordProduceThread;
0087     void setupStream();
0088     void processFrame(const PipeWireFrame &frame);
0089     void updateTextureImage(const QImage &image, const PipeWireFrame &frame);
0090     void render(const PipeWireFrame &frame);
0091     void stateChanged(pw_stream_state state);
0092 
0093     AVCodecContext *m_avCodecContext = nullptr;
0094     const AVCodec *m_codec = nullptr;
0095     AVFormatContext *m_avFormatContext = nullptr;
0096     const QString m_output;
0097     const uint m_nodeId;
0098     QScopedPointer<PipeWireSourceStream> m_stream;
0099     QString m_error;
0100 
0101     PipeWireRecordWriteThread *m_writeThread = nullptr;
0102     const QByteArray m_encoder;
0103 
0104     struct {
0105         QImage texture;
0106         std::optional<QPoint> position;
0107         QPoint hotspot;
0108         bool dirty = false;
0109     } m_cursor;
0110     QImage m_frameWithoutMetadataCursor;
0111     DmaBufHandler m_dmabufHandler;
0112     QAtomicInt m_deactivated = false;
0113 };
0114 
0115 class PipeWireRecordProduceThread : public QThread
0116 {
0117     Q_OBJECT
0118 public:
0119     PipeWireRecordProduceThread(const QByteArray &encoder, uint nodeId, uint fd, const QString &output)
0120         : m_nodeId(nodeId)
0121         , m_fd(fd)
0122         , m_output(output)
0123         , m_encoder(encoder)
0124     {
0125     }
0126     void run() override;
0127     void deactivate();
0128 
0129 Q_SIGNALS:
0130     void errorFound(const QString &error);
0131 
0132 private:
0133     const uint m_nodeId;
0134     const uint m_fd;
0135     const QString m_output;
0136     PipeWireRecordProduce *m_producer = nullptr;
0137     const QByteArray m_encoder;
0138 };
0139 
0140 struct PipeWireRecordPrivate {
0141     uint m_nodeId = 0;
0142     std::optional<uint> m_fd;
0143     bool m_active = false;
0144     QString m_output;
0145     std::unique_ptr<PipeWireRecordProduceThread> m_recordThread;
0146     bool m_produceThreadFinished = true;
0147     QByteArray m_encoder;
0148 };