File indexing completed on 2024-06-16 04:38:28

0001 /*
0002     SPDX-FileCopyrightText: 2003 Fabrice Bellard
0003     SPDX-FileCopyrightText: 2020-2022 Mladen Milinkovic <max@smoothware.net>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 
0007     Portions of this code are based on FFmpeg
0008 */
0009 
0010 #include "ffplayer.h"
0011 
0012 #include <QDebug>
0013 #include <QThread>
0014 #include <QWidget>
0015 #include <QEvent>
0016 #include <QWaitCondition>
0017 #include <QMutex>
0018 
0019 #include <cinttypes>
0020 #include <cmath>
0021 #include <climits>
0022 #include <csignal>
0023 #include <cstdint>
0024 
0025 #include "videoplayer/backend/decoder.h"
0026 #include "videoplayer/backend/framequeue.h"
0027 #include "videoplayer/backend/glrenderer.h"
0028 #include "videoplayer/backend/packetqueue.h"
0029 #include "videoplayer/backend/renderthread.h"
0030 
0031 extern "C" {
0032 #include "libavformat/avformat.h"
0033 #include "libavcodec/avcodec.h"
0034 }
0035 
0036 using namespace SubtitleComposer;
0037 
0038 FFPlayer::FFPlayer(QObject *parent)
0039     : QObject(parent),
0040       m_muted(false),
0041       m_volume(1.0),
0042       m_vs(nullptr),
0043       m_renderer(new GLRenderer(nullptr))
0044 {
0045     connect(m_renderer, &QObject::destroyed, this, [&](){
0046         close();
0047         m_renderer = nullptr;
0048     });
0049 
0050     qRegisterMetaType<FFPlayer::State>("FFPlayer::State");
0051     connect(&m_positionTimer, &QTimer::timeout, this, [this](){
0052         const double pf = position();
0053         const qint32 p = pf * 1000.;
0054         if(m_postitionLast != p) {
0055             m_postitionLast = p;
0056             emit positionChanged(pf);
0057         }
0058     });
0059 
0060     av_log_set_flags(AV_LOG_SKIP_REPEATED);
0061 #ifndef NDEBUG
0062     av_log_set_level(AV_LOG_VERBOSE);
0063 #else
0064     av_log_set_level(AV_LOG_INFO);
0065 #endif
0066 #ifdef FFMPEG_QT_LOGGING
0067     av_log_set_callback(av_log_callback);
0068 #endif // FFMPEG_QT_LOGGING
0069 
0070     // register all codecs, demux and protocols
0071     avformat_network_init();
0072 }
0073 
0074 FFPlayer::~FFPlayer()
0075 {
0076     if(m_renderer)
0077         m_renderer->deleteLater();
0078 
0079     close();
0080     avformat_network_deinit();
0081     av_log(nullptr, AV_LOG_QUIET, "%s", "");
0082 }
0083 
0084 uint8_t *
0085 FFPlayer::flushPkt()
0086 {
0087     static uint8_t pktData;
0088     return &pktData;
0089 }
0090 
0091 void
0092 FFPlayer::pauseToggle()
0093 {
0094     m_vs->demuxer->pauseToggle();
0095     m_vs->step = 0;
0096     m_vs->notifyState();
0097 }
0098 
0099 bool
0100 FFPlayer::paused()
0101 {
0102     return m_vs->paused || m_vs->step;
0103 }
0104 
0105 void
0106 FFPlayer::seek(double seconds)
0107 {
0108     m_vs->demuxer->seek(seconds * double(AV_TIME_BASE));
0109 }
0110 
0111 void
0112 FFPlayer::stepFrame(int frameCnt)
0113 {
0114     const AVStream *st = m_vs->vidStream;
0115     if(!st)
0116         return;
0117 
0118     if(frameCnt >= 0) {
0119         while(frameCnt--)
0120             m_vs->demuxer->stepFrame();
0121     } else {
0122         if(!m_vs->paused)
0123             m_vs->demuxer->pauseToggle();
0124 
0125         // TODO: FIXME: backward stepping is broken
0126         double seek_seconds = m_vs->vidClk.pts(); // maxrd2: was m_vs->extclk.pts
0127         if(std::isnan(seek_seconds))
0128             return; // maxrd2: was seek_seconds = m_vs->extclk.pts;
0129         seek_seconds += double(frameCnt - 1) / av_q2d(st->r_frame_rate);
0130         seek(seek_seconds);
0131 
0132         m_vs->forceRefresh = true;
0133         m_vs->demuxer->stepFrame();
0134     }
0135     m_vs->notifyState();
0136 }
0137 
0138 //#define FFMPEG_QT_LOGGING
0139 #ifdef FFMPEG_QT_LOGGING
0140 static void
0141 av_log_callback(void *ptr, int severity, const char *format, va_list args)
0142 {
0143     if(severity > av_log_get_level())
0144         return;
0145     static QMap<int, QtMsgType> type {
0146         { AV_LOG_TRACE, QtDebugMsg },
0147         { AV_LOG_DEBUG, QtDebugMsg },
0148         { AV_LOG_VERBOSE, QtInfoMsg },
0149         { AV_LOG_INFO, QtInfoMsg },
0150         { AV_LOG_WARNING, QtWarningMsg },
0151         { AV_LOG_ERROR, QtCriticalMsg },
0152         { AV_LOG_FATAL, QtFatalMsg },
0153         { AV_LOG_PANIC, QtFatalMsg },
0154     };
0155     if(!type.contains(severity))
0156         return;
0157     static char buf[8192];
0158     static int bufpos = 0;
0159     static int bufprt = 0;
0160     static int pfx = 1;
0161     if(pfx) {
0162         // flush
0163         if(bufprt < bufpos)
0164             qt_message_output(type[severity], QMessageLogContext(), QString::fromUtf8(buf + bufprt));
0165         bufpos = bufprt = 0;
0166     }
0167     int ls = sizeof(buf) - bufpos;
0168     const int ll = av_log_format_line2(ptr, severity, format, args, buf + bufpos, ls, &pfx);
0169     if(ll <= 0)
0170         return;
0171     if(ll < ls)
0172         ls = ll;
0173     bufpos += ls - 1;
0174     int bufend = bufprt;
0175     for(;;) {
0176         while(bufend <= bufpos && buf[bufend] != '\n' && buf[bufend] != '\r' && buf[bufend])
0177             bufend++;
0178         if(bufend > bufpos || (buf[bufend] != '\n' && buf[bufend] != '\r'))
0179             break;
0180         buf[bufend++] = 0;
0181         qt_message_output(type[severity], QMessageLogContext(), QString::fromUtf8(buf + bufprt));
0182         bufprt = bufend;
0183     }
0184     if(bufprt == bufpos)
0185         bufpos = bufprt = 0;
0186 }
0187 #endif // FFMPEG_QT_LOGGING
0188 
0189 bool
0190 FFPlayer::open(const char *filename)
0191 {
0192     close();
0193 
0194     m_vs = StreamDemuxer::open(filename);
0195     if(!m_vs) {
0196         av_log(nullptr, AV_LOG_FATAL, "Failed to initialize VideoState!\n");
0197         close();
0198         return false;
0199     }
0200     m_vs->player = this;
0201     m_vs->glRenderer = m_renderer;
0202 
0203     // start event loop
0204     m_vs->renderThread = new RenderThread(m_vs);
0205     m_vs->renderThread->start();
0206 
0207     m_postitionLast = -1;
0208     m_positionTimer.start(100);
0209 
0210     return true;
0211 }
0212 
0213 void
0214 FFPlayer::close()
0215 {
0216     if(m_vs) {
0217         m_positionTimer.stop();
0218 
0219         if(m_vs->renderThread) {
0220             m_vs->renderThread->requestInterruption();
0221             m_vs->renderThread->wait();
0222             delete m_vs->renderThread;
0223             m_vs->renderThread = nullptr;
0224         }
0225         StreamDemuxer::close(m_vs);
0226         m_vs = nullptr;
0227     }
0228 }
0229 
0230 quint32
0231 FFPlayer::videoWidth()
0232 {
0233     return m_vs->vidStream->codecpar->width;
0234 }
0235 
0236 quint32
0237 FFPlayer::videoHeight()
0238 {
0239     return m_vs->vidStream->codecpar->height;
0240 }
0241 
0242 qreal
0243 FFPlayer::videoSAR()
0244 {
0245     AVRational aspectRatio = av_guess_sample_aspect_ratio(m_vs->fmtContext, m_vs->vidStream, nullptr);
0246     if(av_cmp_q(aspectRatio, av_make_q(0, 1)) <= 0)
0247         aspectRatio = av_make_q(1, 1);
0248 
0249     const int picWidth = m_vs->vidStream->codecpar->width;
0250     const int picHeight = m_vs->vidStream->codecpar->height;
0251     aspectRatio = av_mul_q(aspectRatio, av_make_q(picWidth, picHeight));
0252 
0253     // XXX: we suppose the screen has a 1.0 pixel ratio
0254     return av_q2d(aspectRatio);
0255 }
0256 
0257 qreal
0258 FFPlayer::videoFPS()
0259 {
0260     if(!m_vs->vidStream)
0261         return 0.;
0262     return av_q2d(m_vs->vidStream->r_frame_rate);
0263 }
0264 
0265 int
0266 FFPlayer::activeVideoStream()
0267 {
0268     int idx = 0;
0269     for(int i = 0; i < int(m_vs->fmtContext->nb_streams); i++) {
0270         const AVStream *stream = m_vs->fmtContext->streams[i];
0271         if(stream->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
0272             continue;
0273         if(i == m_vs->vidStreamIdx)
0274             return idx;
0275         idx++;
0276     }
0277     return -1;
0278 }
0279 
0280 int
0281 FFPlayer::activeAudioStream()
0282 {
0283     if(m_vs->audStreamIdx < 0)
0284         return -1;
0285     return m_vs->demuxer->relativeStreamIndex(AVMEDIA_TYPE_AUDIO, m_vs->audStreamIdx);
0286 }
0287 
0288 void
0289 FFPlayer::activeAudioStream(int streamIndex)
0290 {
0291     if(!m_vs)
0292         return;
0293     streamIndex = streamIndex < 0 ? -1 : m_vs->demuxer->absoluteStreamIndex(AVMEDIA_TYPE_AUDIO, streamIndex);
0294     m_vs->demuxer->selectStream(AVMEDIA_TYPE_AUDIO, streamIndex);
0295 }
0296 
0297 int
0298 FFPlayer::activeSubtitleStream()
0299 {
0300     int idx = 0;
0301     for(int i = 0; i < int(m_vs->fmtContext->nb_streams); i++) {
0302         const AVStream *st = m_vs->fmtContext->streams[i];
0303         if(st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
0304             continue;
0305         const AVCodecDescriptor *desc = avcodec_descriptor_get(st->codecpar->codec_id);
0306         if(!desc || !(desc->props & AV_CODEC_PROP_TEXT_SUB))
0307             continue;
0308         if(i == m_vs->subStreamIdx)
0309             return idx;
0310         idx++;
0311     }
0312     return -1;
0313 }
0314 
0315 void
0316 FFPlayer::setMuted(bool mute)
0317 {
0318     if(m_muted == mute)
0319         return;
0320     m_muted = mute;
0321 
0322     if(m_vs)
0323         m_vs->audDec.setListenerGain(m_muted ? 0. : m_volume);
0324 }
0325 
0326 void
0327 FFPlayer::setVolume(double volume)
0328 {
0329     volume = qMax(0., volume);
0330     if(m_volume == volume)
0331         return;
0332     m_volume = volume;
0333 
0334     if(m_vs)
0335         m_vs->audDec.setListenerGain(m_volume);
0336 }
0337 
0338 void
0339 FFPlayer::setSpeed(double speed)
0340 {
0341     m_vs->audDec.setPitch(speed);
0342 }