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

0001 /*
0002     SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleixpol@kde.org>
0003     SPDX-FileCopyrightText: 2023 Marco Martin <mart@kde.org>
0004     SPDX-FileCopyrightText: 2023 Arjen Hiemstra <ahiemstra@heimr.nl>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #pragma once
0010 
0011 #include <mutex>
0012 
0013 #include <QObject>
0014 
0015 #include "dmabufhandler.h"
0016 
0017 extern "C" {
0018 #include "libavcodec/avcodec.h"
0019 #include "libavfilter/avfilter.h"
0020 }
0021 
0022 #undef av_err2str
0023 // The one provided by libav fails to compile on GCC due to passing data from the function scope outside
0024 char *av_err2str(int errnum);
0025 
0026 struct PipeWireFrame;
0027 class PipeWireProduce;
0028 
0029 /**
0030  * Base class for objects that encapsulate encoder logic and state.
0031  */
0032 class Encoder : public QObject
0033 {
0034     Q_OBJECT
0035 public:
0036     enum class H264Profile { Baseline, Main, High };
0037 
0038     /**
0039      * Constructor.
0040      *
0041      * @param produce The PipeWireProduce instance that owns this encoder.
0042      */
0043     Encoder(PipeWireProduce *produce);
0044     ~Encoder() override;
0045 
0046     /**
0047      * Initialize and setup the encoder.
0048      *
0049      * @param size The size of the stream being encoded.
0050      *
0051      * @return true if initailization was succesful, false if not.
0052      */
0053     virtual bool initialize(const QSize &size) = 0;
0054     /**
0055      * Process a PipeWire frame and pass it to libav for filtering.
0056      *
0057      * @param frame The frame to process.
0058      *
0059      * @note This method will be called on its own thread.
0060      */
0061     virtual void filterFrame(const PipeWireFrame &frame) = 0;
0062     /**
0063      * Get the next finished frames from the libav filter chain and queue them for encoding.
0064      *
0065      * @param maximumFrames The maximum number of frames that can be queued for encoding.
0066      *
0067      * @return A pair with the number of frames removed from the filter chain as first entry
0068      *         and the number of frames queued for encoding as the second entry.
0069      *
0070      * @note This method will be called on its own thread.
0071      */
0072     virtual std::pair<int, int> encodeFrame(int maximumFrames);
0073     /**
0074      * Get the next encoded frames from libav and pass them to PipeWireProduce.
0075      *
0076      * @return The number of encoded frames that were received.
0077      *
0078      * @note This method will be called on its own thread.
0079      */
0080     virtual int receivePacket();
0081     /**
0082      * End encoding and perform any necessary cleanup.
0083      */
0084     virtual void finish();
0085 
0086     /**
0087      * Return the AVCodecContext for this encoder.
0088      */
0089     AVCodecContext *avCodecContext() const;
0090 
0091     /**
0092      * Set the quality level, from 0 (lowest) to 100 (highest).
0093      *
0094      * Internally this will be converted to an encoder-specific quality value.
0095      */
0096     void setQuality(std::optional<quint8> quality);
0097 
0098 protected:
0099     virtual int percentageToAbsoluteQuality(const std::optional<quint8> &quality) = 0;
0100 
0101     PipeWireProduce *m_produce;
0102 
0103     AVCodecContext *m_avCodecContext = nullptr;
0104     std::mutex m_avCodecMutex;
0105 
0106     AVFilterGraph *m_avFilterGraph = nullptr;
0107     AVFilterContext *m_inputFilter = nullptr;
0108     AVFilterContext *m_outputFilter = nullptr;
0109 
0110     std::optional<quint8> m_quality;
0111 };
0112 
0113 /**
0114  * Encoder subclass that can be used as base class for software encoders.
0115  */
0116 class SoftwareEncoder : public Encoder
0117 {
0118 public:
0119     SoftwareEncoder(PipeWireProduce *produce);
0120 
0121     void filterFrame(const PipeWireFrame &frame) override;
0122 
0123 protected:
0124     /**
0125      * Create a default filter graph that converts from RGBA to YUV420P.
0126      *
0127      * @param size The size of the stream to encode.
0128      */
0129     bool createFilterGraph(const QSize &size);
0130 
0131     DmaBufHandler m_dmaBufHandler;
0132 };
0133 
0134 /**
0135  * Encoder subclass that can be used as base class for hardware encoders.
0136  */
0137 class HardwareEncoder : public Encoder
0138 {
0139 public:
0140     HardwareEncoder(PipeWireProduce *produce);
0141     ~HardwareEncoder() override;
0142 
0143     void filterFrame(const PipeWireFrame &frame) override;
0144 
0145 protected:
0146     /**
0147      * Check if VAAPI is supported for a given size stream.
0148      *
0149      * @param size The size of the stream to check.
0150      *
0151      * @return The path to a device node that can encode this stream. If
0152      *         the stream cannot be encoded by the current hardware, an
0153      *         empty QByteArray will be returned.
0154      */
0155     QByteArray checkVaapi(const QSize &size);
0156     /**
0157      * Create the libav contexts for the DRM subsystem.
0158      *
0159      * These contexts are used when doing import of dma-buf based frames.
0160      *
0161      * @param path The path to a device node where the frames are.
0162      * @param size The size of the frames.
0163      *
0164      * @return true if the contexts were successfully created, false if not.
0165      */
0166     bool createDrmContext(const QSize &size);
0167 
0168     AVBufferRef *m_drmContext = nullptr;
0169     AVBufferRef *m_drmFramesContext = nullptr;
0170 
0171 private:
0172     bool m_supportsHardwareModifiers;
0173 };