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

0001 /*
0002     SPDX-FileCopyrightText: 2022-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 #include "pipewirebaseencodedstream.h"
0008 
0009 #include <logging_libav.h>
0010 #include <logging_record.h>
0011 #include <memory>
0012 #include <va/va.h>
0013 
0014 extern "C" {
0015 #include <libavcodec/codec.h>
0016 #include <libavutil/log.h>
0017 }
0018 #include <unistd.h>
0019 
0020 #include "pipewireproduce_p.h"
0021 #include "vaapiutils_p.h"
0022 
0023 struct PipeWireEncodedStreamPrivate {
0024     uint m_nodeId = 0;
0025     std::optional<uint> m_fd;
0026     Fraction m_maxFramerate;
0027     bool m_active = false;
0028     PipeWireBaseEncodedStream::Encoder m_encoder;
0029     std::optional<quint8> m_quality;
0030 
0031     std::unique_ptr<QThread> m_produceThread;
0032     std::unique_ptr<PipeWireProduce> m_produce;
0033 };
0034 
0035 PipeWireBaseEncodedStream::State PipeWireBaseEncodedStream::state() const
0036 {
0037     if (isActive()) {
0038         return Recording;
0039     } else if (d->m_produceThread && d->m_produce->m_deactivated && d->m_produceThread->isRunning()) {
0040         return Rendering;
0041     }
0042 
0043     return Idle;
0044 }
0045 
0046 PipeWireBaseEncodedStream::PipeWireBaseEncodedStream(QObject *parent)
0047     : QObject(parent)
0048     , d(new PipeWireEncodedStreamPrivate)
0049 {
0050     d->m_encoder = suggestedEncoders().value(0, NoEncoder);
0051 
0052     const auto &category = PIPEWIRELIBAV_LOGGING();
0053     if (category.isDebugEnabled()) {
0054         av_log_set_level(AV_LOG_DEBUG);
0055     } else if (category.isInfoEnabled()) {
0056         av_log_set_level(AV_LOG_INFO);
0057     } else if (category.isWarningEnabled()) {
0058         av_log_set_level(AV_LOG_WARNING);
0059     } else {
0060         av_log_set_level(AV_LOG_ERROR);
0061     }
0062 }
0063 
0064 PipeWireBaseEncodedStream::~PipeWireBaseEncodedStream()
0065 {
0066     setActive(false);
0067 
0068     if (d->m_fd) {
0069         close(*d->m_fd);
0070     }
0071 }
0072 
0073 void PipeWireBaseEncodedStream::setNodeId(uint nodeId)
0074 {
0075     if (nodeId == d->m_nodeId)
0076         return;
0077 
0078     d->m_nodeId = nodeId;
0079     refresh();
0080     Q_EMIT nodeIdChanged(nodeId);
0081 }
0082 
0083 void PipeWireBaseEncodedStream::setFd(uint fd)
0084 {
0085     if (fd == d->m_fd)
0086         return;
0087 
0088     if (d->m_fd) {
0089         close(*d->m_fd);
0090     }
0091     d->m_fd = fd;
0092     refresh();
0093     Q_EMIT fdChanged(fd);
0094 }
0095 
0096 Fraction PipeWireBaseEncodedStream::maxFramerate() const
0097 {
0098     if (d->m_maxFramerate) {
0099         return d->m_maxFramerate;
0100     }
0101     return Fraction{60, 1};
0102 }
0103 
0104 void PipeWireBaseEncodedStream::setMaxFramerate(const Fraction &framerate)
0105 {
0106     if (d->m_maxFramerate == framerate) {
0107         return;
0108     }
0109     d->m_maxFramerate = framerate;
0110     Q_EMIT maxFramerateChanged();
0111 }
0112 
0113 void PipeWireBaseEncodedStream::setMaxFramerate(quint32 numerator, quint32 denominator)
0114 {
0115     setMaxFramerate({numerator, denominator});
0116 }
0117 
0118 void PipeWireBaseEncodedStream::setActive(bool active)
0119 {
0120     if (d->m_active == active)
0121         return;
0122 
0123     d->m_active = active;
0124     refresh();
0125     Q_EMIT activeChanged(active);
0126 }
0127 
0128 std::optional<quint8> PipeWireBaseEncodedStream::quality() const
0129 {
0130     return d->m_quality;
0131 }
0132 
0133 void PipeWireBaseEncodedStream::setQuality(quint8 quality)
0134 {
0135     d->m_quality = quality;
0136     if (d->m_produce) {
0137         d->m_produce->setQuality(d->m_quality);
0138     }
0139 }
0140 
0141 void PipeWireBaseEncodedStream::refresh()
0142 {
0143     if (d->m_produceThread) {
0144         QMetaObject::invokeMethod(d->m_produce.get(), &PipeWireProduce::deactivate, Qt::QueuedConnection);
0145         d->m_produceThread->wait();
0146 
0147         d->m_produce.reset();
0148         d->m_produceThread.reset();
0149     }
0150 
0151     if (d->m_active && d->m_nodeId > 0) {
0152         d->m_produceThread = std::make_unique<QThread>();
0153         d->m_produceThread->setObjectName("PipeWireProduce::input");
0154         d->m_produce = makeProduce();
0155         d->m_produce->setQuality(d->m_quality);
0156         d->m_produce->moveToThread(d->m_produceThread.get());
0157         d->m_produceThread->start();
0158         QMetaObject::invokeMethod(d->m_produce.get(), &PipeWireProduce::initialize, Qt::QueuedConnection);
0159     }
0160 
0161     Q_EMIT stateChanged();
0162 }
0163 
0164 void PipeWireBaseEncodedStream::setEncoder(Encoder encoder)
0165 {
0166     if (d->m_encoder == encoder || !suggestedEncoders().contains(encoder)) {
0167         return;
0168     }
0169     d->m_encoder = encoder;
0170     Q_EMIT encoderChanged();
0171 }
0172 
0173 PipeWireBaseEncodedStream::Encoder PipeWireBaseEncodedStream::encoder() const
0174 {
0175     return d->m_encoder;
0176 }
0177 
0178 QList<PipeWireBaseEncodedStream::Encoder> PipeWireBaseEncodedStream::suggestedEncoders() const
0179 {
0180     VaapiUtils vaapi;
0181 
0182     QList<PipeWireBaseEncodedStream::Encoder> ret = {PipeWireBaseEncodedStream::VP8,
0183                                                      PipeWireBaseEncodedStream::VP9,
0184                                                      PipeWireBaseEncodedStream::H264Main,
0185                                                      PipeWireBaseEncodedStream::H264Baseline};
0186     auto removeUnavailableEncoders = [&vaapi](const PipeWireBaseEncodedStream::Encoder &encoder) {
0187         switch (encoder) {
0188         case PipeWireBaseEncodedStream::VP8:
0189             if (vaapi.supportsProfile(VAProfileVP8Version0_3) && avcodec_find_encoder_by_name("vp8_vaapi")) {
0190                 return false;
0191             } else {
0192                 return !avcodec_find_encoder_by_name("libvpx");
0193             }
0194         case PipeWireBaseEncodedStream::VP9:
0195             return !avcodec_find_encoder_by_name("libvpx-vp9");
0196         case PipeWireBaseEncodedStream::H264Main:
0197         case PipeWireBaseEncodedStream::H264Baseline:
0198             if (vaapi.supportsProfile(encoder == PipeWireBaseEncodedStream::H264Main ? VAProfileH264Main : VAProfileH264ConstrainedBaseline)
0199                 && avcodec_find_encoder_by_name("h264_vaapi")) {
0200                 return false;
0201             } else {
0202                 return !avcodec_find_encoder_by_name("libx264");
0203             }
0204         default:
0205             return true;
0206         }
0207     };
0208     ret.removeIf(removeUnavailableEncoders);
0209     return ret;
0210 }
0211 
0212 bool PipeWireBaseEncodedStream::isActive() const
0213 {
0214     return d->m_active;
0215 }
0216 
0217 uint PipeWireBaseEncodedStream::nodeId() const
0218 {
0219     return d->m_nodeId;
0220 }
0221 
0222 uint PipeWireBaseEncodedStream::fd() const
0223 {
0224     return d->m_fd.value_or(0);
0225 }
0226 
0227 #include "moc_pipewirebaseencodedstream.cpp"