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