File indexing completed on 2025-01-19 03:57:06
0001 /********************************************************* 0002 * Copyright (C) 2020, Val Doroshchuk <valbok@gmail.com> * 0003 * * 0004 * This file is part of QtAVPlayer. * 0005 * Free Qt Media Player based on FFmpeg. * 0006 *********************************************************/ 0007 0008 #include "qavstream.h" 0009 #include "qavdemuxer_p.h" 0010 #include "qavcodec_p.h" 0011 #include <QDebug> 0012 0013 extern "C" { 0014 #include <libavformat/avformat.h> 0015 #include <libavutil/display.h> 0016 #include <libavutil/time.h> 0017 #include <libavcodec/version.h> 0018 } 0019 0020 QT_BEGIN_NAMESPACE 0021 0022 class QAVStreamPrivate 0023 { 0024 Q_DECLARE_PUBLIC(QAVStream) 0025 public: 0026 QAVStreamPrivate(QAVStream *q) : q_ptr(q) { } 0027 0028 QAVStream *q_ptr = nullptr; 0029 int index = -1; 0030 AVFormatContext *ctx = nullptr; 0031 QSharedPointer<QAVCodec> codec; 0032 QMap<QString, QString> metadata; 0033 }; 0034 0035 QAVStream::QAVStream() 0036 : d_ptr(new QAVStreamPrivate(this)) 0037 { 0038 } 0039 0040 QAVStream::QAVStream(int index, AVFormatContext *ctx, const QSharedPointer<QAVCodec> &codec) 0041 : QAVStream() 0042 { 0043 d_ptr->index = index; 0044 d_ptr->ctx = ctx; 0045 d_ptr->codec = codec; 0046 } 0047 0048 QAVStream::~QAVStream() 0049 { 0050 } 0051 0052 QAVStream::QAVStream(const QAVStream &other) 0053 : QAVStream() 0054 { 0055 *this = other; 0056 } 0057 0058 QAVStream &QAVStream::operator=(const QAVStream &other) 0059 { 0060 d_ptr->index = other.d_ptr->index; 0061 d_ptr->ctx = other.d_ptr->ctx; 0062 d_ptr->codec = other.d_ptr->codec; 0063 return *this; 0064 } 0065 0066 QAVStream::operator bool() const 0067 { 0068 Q_D(const QAVStream); 0069 return d->ctx != nullptr && d->codec && d->index >= 0; 0070 } 0071 0072 AVStream *QAVStream::stream() const 0073 { 0074 Q_D(const QAVStream); 0075 return d->index >= 0 && d->index < static_cast<int>(d->ctx->nb_streams) ? d->ctx->streams[d->index] : nullptr; 0076 } 0077 0078 int QAVStream::index() const 0079 { 0080 return d_func()->index; 0081 } 0082 0083 static int streamRotation(const AVStream *stream) 0084 { 0085 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 29, 100) 0086 auto ptr = av_packet_side_data_get(stream->codecpar->coded_side_data, 0087 stream->codecpar->nb_coded_side_data, 0088 AV_PKT_DATA_DISPLAYMATRIX); 0089 auto sideData = ptr ? ptr->data : nullptr; 0090 #elif LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 18, 0) 0091 auto sideData = av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, nullptr); 0092 #else 0093 auto cb = [](const auto &data) { return data.type == AV_PKT_DATA_DISPLAYMATRIX; }; 0094 auto end = stream->side_data + stream->nb_side_data; 0095 auto ptr = std::find_if(stream->side_data, end, cb); 0096 auto sideData = ptr != end ? ptr->data : nullptr; 0097 #endif 0098 if (!sideData) 0099 return 0; 0100 auto rotation = static_cast<int>(std::round(av_display_rotation_get(reinterpret_cast<const int32_t *>(sideData)))); 0101 if (rotation % 90 != 0) 0102 return 0; 0103 return rotation > 0 ? -rotation % 360 + 360 : -rotation % 360; 0104 } 0105 0106 static QMap<QString, QString> streamMetadata(const AVStream *stream) 0107 { 0108 QMap<QString, QString> metadata; 0109 AVDictionaryEntry *tag = nullptr; 0110 while ((tag = av_dict_get(stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) 0111 metadata[QString::fromUtf8(tag->key)] = QString::fromUtf8(tag->value); 0112 if (!metadata.contains(QString::fromLatin1("rotate"))) 0113 metadata[QString::fromLatin1("rotate")] = QString::number(streamRotation(stream)); 0114 return metadata; 0115 } 0116 0117 QMap<QString, QString> QAVStream::metadata() const 0118 { 0119 Q_D(const QAVStream); 0120 if (!d->metadata.isEmpty()) 0121 return d->metadata; 0122 auto s = stream(); 0123 if (!s) 0124 return {}; 0125 const_cast<QAVStreamPrivate *>(d)->metadata = streamMetadata(s); 0126 return d->metadata; 0127 } 0128 0129 QSharedPointer<QAVCodec> QAVStream::codec() const 0130 { 0131 Q_D(const QAVStream); 0132 return d->codec; 0133 } 0134 0135 double QAVStream::duration() const 0136 { 0137 Q_D(const QAVStream); 0138 auto s = stream(); 0139 if (!s) 0140 return 0.0; 0141 0142 double ret = 0.0; 0143 if (s->duration != AV_NOPTS_VALUE) 0144 ret = s->duration * av_q2d(s->time_base); 0145 if (!ret && d->ctx->duration != AV_NOPTS_VALUE) 0146 ret = d->ctx->duration / AV_TIME_BASE; 0147 return ret; 0148 } 0149 0150 int64_t QAVStream::framesCount() const 0151 { 0152 auto s = stream(); 0153 if (s == nullptr) 0154 return 0; 0155 0156 auto frames = s->nb_frames; 0157 if (frames) 0158 return frames; 0159 0160 auto dur = duration(); 0161 // If frame count is not known, estimating it 0162 if (s->avg_frame_rate.num && s->avg_frame_rate.den && dur) 0163 return dur * av_q2d(s->avg_frame_rate); 0164 0165 const auto tb = s->time_base; 0166 if ((tb.num == 1 && tb.den >= 24 && tb.den <= 60) || 0167 (tb.num == 1001 && tb.den >= 24000 && tb.den <= 60000)) 0168 { 0169 return s->duration; 0170 } 0171 0172 return 0; 0173 } 0174 0175 double QAVStream::frameRate() const 0176 { 0177 Q_D(const QAVStream); 0178 auto s = stream(); 0179 if (s == nullptr) 0180 return 0.0; 0181 AVRational fr = av_guess_frame_rate(d->ctx, s, nullptr); 0182 return fr.num && fr.den ? av_q2d({fr.den, fr.num}) : 0.0; 0183 } 0184 0185 QAVStream::Progress::Progress(double duration, qint64 frames, double fr) 0186 : m_duration(duration) 0187 , m_expectedFramesCount(frames) 0188 , m_expectedFrameRate(fr) 0189 { 0190 } 0191 0192 QAVStream::Progress::Progress(const Progress &other) 0193 { 0194 *this = other; 0195 } 0196 0197 QAVStream::Progress &QAVStream::Progress::operator=(const Progress &other) 0198 { 0199 m_pts = other.m_pts; 0200 m_duration = other.m_duration; 0201 m_framesCount = other.m_framesCount; 0202 m_expectedFramesCount = other.m_expectedFramesCount; 0203 m_expectedFrameRate = other.m_expectedFrameRate; 0204 m_time = other.m_time; 0205 m_diffs = other.m_diffs; 0206 return *this; 0207 } 0208 0209 double QAVStream::Progress::pts() const 0210 { 0211 return m_pts; 0212 } 0213 0214 double QAVStream::Progress::duration() const 0215 { 0216 return m_duration; 0217 } 0218 0219 qint64 QAVStream::Progress::framesCount() const 0220 { 0221 return m_framesCount; 0222 } 0223 0224 qint64 QAVStream::Progress::expectedFramesCount() const 0225 { 0226 return m_expectedFramesCount; 0227 } 0228 0229 double QAVStream::Progress::expectedFrameRate() const 0230 { 0231 return m_expectedFrameRate; 0232 } 0233 0234 double QAVStream::Progress::frameRate() const 0235 { 0236 return m_framesCount ? m_diffs / 1000000.0 / static_cast<double>(m_framesCount) : 0.0; 0237 } 0238 0239 unsigned QAVStream::Progress::fps() const 0240 { 0241 double fr = frameRate(); 0242 return fr ? static_cast<unsigned>(1 / fr) : 0; 0243 } 0244 0245 void QAVStream::Progress::onFrameSent(double pts) 0246 { 0247 m_pts = pts; 0248 qint64 cur = av_gettime_relative(); 0249 if (m_framesCount++ > 0) { 0250 qint64 diff = cur - m_time; 0251 if (diff > 0) 0252 m_diffs += diff; 0253 } 0254 m_time = cur; 0255 } 0256 0257 bool operator==(const QAVStream &lhs, const QAVStream &rhs) 0258 { 0259 return lhs.index() == rhs.index(); 0260 } 0261 0262 #ifndef QT_NO_DEBUG_STREAM 0263 QDebug operator<<(QDebug dbg, const QAVStream &stream) 0264 { 0265 QDebugStateSaver saver(dbg); 0266 dbg.nospace(); 0267 return dbg << QString(QLatin1String("QAVStream(%1)" )).arg(stream.index()).toLatin1().constData(); 0268 } 0269 0270 QDebug operator<<(QDebug dbg, const QAVStream::Progress &p) 0271 { 0272 QDebugStateSaver saver(dbg); 0273 dbg.nospace(); 0274 return dbg << QString(QLatin1String("Progress(%1/%2 pts, %3/%4 frames, %5/%6 frame rate, %7 fps)")) 0275 .arg(p.pts()) 0276 .arg(p.duration()) 0277 .arg(p.framesCount()) 0278 .arg(p.expectedFramesCount()) 0279 .arg(p.frameRate()) 0280 .arg(p.expectedFrameRate()) 0281 .arg(p.fps()).toLatin1().constData(); 0282 } 0283 #endif 0284 0285 QT_END_NAMESPACE