diff options
author | Piotr Srebrny <piotr.srebrny@qt.io> | 2022-09-21 15:25:10 +0200 |
---|---|---|
committer | Piotr Srebrny <piotr.srebrny@qt.io> | 2022-10-25 08:30:48 +0200 |
commit | a300d2acd6a97a115a70798d9039bb4417c34005 (patch) | |
tree | c683cb83460d0a984a8ed4df4f93fb3430ae5904 | |
parent | 5c3805dcd6d03ef8135640c6ccc18a1bc6466bd3 (diff) | |
download | qtmultimedia-a300d2acd6a97a115a70798d9039bb4417c34005.tar.gz |
Clean up multiplication by AVRational, avoid dividing by 0
This patch cleans up multiplication by AVRational as the functions
timeStamp() and timeStampUs() gave incorrect value that is far from true
when ts is close to 1. Additionally, all multiplication and conversion
functions return optional type with nullopt value when rational
denominator is 0.
Change-Id: I5fbfba29fb4717fd53a67afe825a70cc20c16352
Reviewed-by: Lars Knoll <lars@knoll.priv.no>
(cherry picked from commit 7fa57a2cede08d95c2d868c20052552a04ff6ea5)
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpeg_p.h | 23 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp | 19 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h | 4 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp | 4 | ||||
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp | 23 |
5 files changed, 47 insertions, 26 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeg_p.h b/src/plugins/multimedia/ffmpeg/qffmpeg_p.h index 3f06f5f56..6a1d6ab38 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpeg_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpeg_p.h @@ -21,17 +21,30 @@ QT_BEGIN_NAMESPACE namespace QFFmpeg { -inline qint64 timeStamp(qint64 ts, AVRational base) +inline std::optional<qint64> mul(qint64 a, AVRational b) { - return (1000*ts*base.num + 500)/base.den; + return b.den != 0 ? (a * b.num + b.den / 2) / b.den : std::optional<qint64>{}; } -inline qint64 timeStampUs(qint64 ts, AVRational base) +inline std::optional<qreal> mul(qreal a, AVRational b) { - return (1000000*ts*base.num + 500000)/base.den; + return b.den != 0 ? a * qreal(b.num) / qreal(b.den) : std::optional<qreal>{}; } -inline float toFloat(AVRational r) { return float(r.num)/float(r.den); } +inline std::optional<qint64> timeStampMs(qint64 ts, AVRational base) +{ + return mul(1'000 * ts, base); +} + +inline std::optional<qint64> timeStampUs(qint64 ts, AVRational base) +{ + return mul(1'000'000 * ts, base); +} + +inline std::optional<float> toFloat(AVRational r) +{ + return r.den != 0 ? float(r.num) / float(r.den) : std::optional<float>{}; +} inline QString err2str(int errnum) { diff --git a/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp index 41d1e16d5..89a95f5a3 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp @@ -253,7 +253,9 @@ void Demuxer::loop() if (last_pts < 0 && packet->pts != AV_NOPTS_VALUE) { auto *stream = context->streams[packet->stream_index]; - last_pts = timeStamp(packet->pts, stream->time_base); + auto pts = timeStampMs(packet->pts, stream->time_base); + if (pts) + last_pts = *pts; } auto *streamDecoder = streamDecoders.at(packet->stream_index); @@ -484,9 +486,9 @@ void StreamDecoder::decodeSubtitle() start = codec.toUs(packet.avPacket()->pts); end = start + codec.toUs(packet.avPacket()->duration); } else { - qint64 pts = timeStampUs(subtitle.pts, AVRational{1, AV_TIME_BASE}); - start = pts + qint64(subtitle.start_display_time)*1000; - end = pts + qint64(subtitle.end_display_time)*1000; + auto pts = timeStampUs(subtitle.pts, AVRational{1, AV_TIME_BASE}); + start = *pts + qint64(subtitle.start_display_time)*1000; + end = *pts + qint64(subtitle.end_display_time)*1000; } // qCDebug(qLcDecoder) << " got subtitle (" << start << "--" << end << "):"; QString text; @@ -969,8 +971,9 @@ static void insertVideoData(QMediaMetaData &metaData, AVStream *stream) metaData.insert(QMediaMetaData::VideoBitRate, (int)codecPar->bit_rate); metaData.insert(QMediaMetaData::VideoCodec, QVariant::fromValue(QFFmpegMediaFormatInfo::videoCodecForAVCodecId(codecPar->codec_id))); metaData.insert(QMediaMetaData::Resolution, QSize(codecPar->width, codecPar->height)); - metaData.insert(QMediaMetaData::VideoFrameRate, - qreal(stream->avg_frame_rate.num)/qreal(stream->avg_frame_rate.den)); + auto fr = toFloat(stream->avg_frame_rate); + if (fr) + metaData.insert(QMediaMetaData::VideoFrameRate, *fr); }; static void insertAudioData(QMediaMetaData &metaData, AVStream *stream) @@ -1030,7 +1033,9 @@ static void readStreams(const AVFormatContext *context, } map[type].append({ (int)i, isDefault, metaData }); - maxDuration = qMax(maxDuration, timeStampUs(stream->duration, stream->time_base)); + auto maybeDuration = mul(1'000'000ll * stream->duration, stream->time_base); + if (maybeDuration) + maxDuration = qMax(maxDuration, *maybeDuration); } } diff --git a/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h index 69edf26b4..2ee61a68e 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h @@ -85,8 +85,8 @@ struct Codec AVStream *stream() const { return d->stream; } uint streamIndex() const { return d->stream->index; } HWAccel *hwAccel() const { return d->hwAccel.get(); } - qint64 toMs(qint64 ts) const { return timeStamp(ts, d->stream->time_base); } - qint64 toUs(qint64 ts) const { return timeStampUs(ts, d->stream->time_base); } + qint64 toMs(qint64 ts) const { return timeStampMs(ts, d->stream->time_base).value_or(0); } + qint64 toUs(qint64 ts) const { return timeStampUs(ts, d->stream->time_base).value_or(0); } private: Codec(Data *data) : d(data) {} diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp index 3bd87cbfc..b17c04938 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp @@ -150,7 +150,9 @@ float QFFmpegVideoBuffer::maxNits() // TODO: Longer term we might want to also support HDR10+ dynamic metadata if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) { auto *data = reinterpret_cast<AVMasteringDisplayMetadata *>(sd->data); - maxNits = float(data->max_luminance.num)/float(data->max_luminance.den)*10000.; + auto maybeLum = QFFmpeg::mul(10'000., data->max_luminance); + if (maybeLum) + maxNits = float(maybeLum.value()); } } return maxNits; diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp index 2a9438a00..6cb34f56c 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp @@ -226,22 +226,22 @@ void QFFmpeg::VideoFrameEncoder::initWithFormatContext(AVFormatContext *formatCo float delta = 1e10; if (d->codec->supported_framerates) { // codec only supports fixed frame rates - auto *f = d->codec->supported_framerates; - auto *best = f; + auto *best = d->codec->supported_framerates; qCDebug(qLcVideoFrameEncoder) << "Finding fixed rate:"; - while (f->num != 0) { - float rate = float(f->num)/float(f->den); - float d = qAbs(rate - requestedRate); + for (auto *f = d->codec->supported_framerates; f->num != 0; f++) { + auto maybeRate = toFloat(*f); + if (!maybeRate) + continue; + float d = qAbs(*maybeRate - requestedRate); qCDebug(qLcVideoFrameEncoder) << " " << f->num << f->den << d; if (d < delta) { best = f; delta = d; } - ++f; } qCDebug(qLcVideoFrameEncoder) << "Fixed frame rate required. Requested:" << requestedRate << "Using:" << best->num << "/" << best->den; - d->stream->time_base = { best->den, best->num }; - requestedRate = float(best->num)/float(best->den); + d->stream->time_base = *best; + requestedRate = toFloat(*best).value_or(0.f); } Q_ASSERT(d->codec); @@ -285,8 +285,8 @@ bool VideoFrameEncoder::open() qint64 VideoFrameEncoder::getPts(qint64 us) { Q_ASSERT(d); - qint64 div = 1000000*d->stream->time_base.num; - return (us*d->stream->time_base.den + (div>>1))/div; + qint64 div = 1'000'000 * d->stream->time_base.num; + return div != 0 ? (us * d->stream->time_base.den + div / 2) / div : 0; } int VideoFrameEncoder::sendFrame(AVFrame *frame) @@ -363,7 +363,8 @@ AVPacket *VideoFrameEncoder::retrievePacket() qCDebug(qLcVideoFrameEncoder) << "Error receiving packet" << ret << err2str(ret); return nullptr; } - qCDebug(qLcVideoFrameEncoder) << "got a packet" << packet->pts << timeStamp(packet->pts, d->stream->time_base); + auto ts = timeStampMs(packet->pts, d->stream->time_base); + qCDebug(qLcVideoFrameEncoder) << "got a packet" << packet->pts << (ts ? *ts : 0); packet->stream_index = d->stream->id; return packet; } |