summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Srebrny <piotr.srebrny@qt.io>2022-09-21 15:25:10 +0200
committerPiotr Srebrny <piotr.srebrny@qt.io>2022-10-25 08:30:48 +0200
commita300d2acd6a97a115a70798d9039bb4417c34005 (patch)
treec683cb83460d0a984a8ed4df4f93fb3430ae5904
parent5c3805dcd6d03ef8135640c6ccc18a1bc6466bd3 (diff)
downloadqtmultimedia-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.h23
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp19
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h4
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp4
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp23
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;
}