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 }