Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qffmpegdemuxer.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <qloggingcategory.h>
6
8
9// 4 sec for buffering. TODO: maybe move to env var customization
10static constexpr qint64 MaxBufferingTimeUs = 4'000'000;
11
12// Currently, consider only time. TODO: maybe move to env var customization
13static constexpr qint64 MaxBufferingSize = std::numeric_limits<qint64>::max();
14
15namespace QFFmpeg {
16
17static Q_LOGGING_CATEGORY(qLcDemuxer, "qt.multimedia.ffmpeg.demuxer");
18
19static qint64 streamTimeToUs(const AVStream *stream, qint64 time)
20{
22
23 const auto res = mul(time * 1000000, stream->time_base);
24 return res ? *res : time;
25}
26
27Demuxer::Demuxer(AVFormatContext *context, const PositionWithOffset &posWithOffset,
28 const StreamIndexes &streamIndexes, int loops)
29 : m_context(context), m_posWithOffset(posWithOffset)
30{
31 qCDebug(qLcDemuxer) << "Create demuxer."
32 << "pos:" << posWithOffset.pos << "loop offset:" << posWithOffset.offset.pos
33 << "loop index:" << posWithOffset.offset.index << "loops:" << loops;
34 m_loops = loops;
35
36 Q_ASSERT(m_context);
37 Q_ASSERT(m_loops < 0 || m_posWithOffset.offset.index < m_loops);
38
39 for (auto i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
40 if (streamIndexes[i] >= 0) {
41 const auto trackType = static_cast<QPlatformMediaPlayer::TrackType>(i);
42 qCDebug(qLcDemuxer) << "Activate demuxing stream" << i << ", trackType:" << trackType;
43 m_streams[streamIndexes[i]] = { trackType };
44 }
45 }
46}
47
49{
50 ensureSeeked();
51
52 Packet packet(m_posWithOffset.offset, AVPacketUPtr{ av_packet_alloc() }, id());
53 if (av_read_frame(m_context, packet.avPacket()) < 0) {
54 ++m_posWithOffset.offset.index;
55
56 if (m_loops >= 0 && m_posWithOffset.offset.index >= m_loops) {
57 qCDebug(qLcDemuxer) << "finish demuxing";
58 setAtEnd(true);
59 } else {
60 m_seeked = false;
61 m_posWithOffset.pos = 0;
62 m_posWithOffset.offset.pos = m_endPts;
63 m_endPts = 0;
64
65 ensureSeeked();
66
67 qCDebug(qLcDemuxer) << "Demuxer loops changed. Index:" << m_posWithOffset.offset.index
68 << "Offset:" << m_posWithOffset.offset.pos;
69 }
70
71 return;
72 }
73
74 const auto streamIndex = packet.avPacket()->stream_index;
75 const auto stream = m_context->streams[streamIndex];
76
77 auto it = m_streams.find(streamIndex);
78
79 if (it != m_streams.end()) {
80 const auto packetEndPos =
81 streamTimeToUs(stream, packet.avPacket()->pts + packet.avPacket()->duration);
82 m_endPts = std::max(m_endPts, m_posWithOffset.offset.pos + packetEndPos);
83
84 it->second.bufferingTime += streamTimeToUs(stream, packet.avPacket()->duration);
85 it->second.bufferingSize += packet.avPacket()->size;
86
87 auto signal = signalByTrackType(it->second.trackType);
88 emit (this->*signal)(packet);
89 }
90
91 scheduleNextStep(false);
92}
93
95{
96 Q_ASSERT(packet.isValid());
97
98 if (packet.sourceId() != id())
99 return;
100
101 const auto streamIndex = packet.avPacket()->stream_index;
102 auto it = m_streams.find(streamIndex);
103
104 if (it != m_streams.end()) {
105 it->second.bufferingTime -=
106 streamTimeToUs(m_context->streams[streamIndex], packet.avPacket()->duration);
107 it->second.bufferingSize -= packet.avPacket()->size;
108
109 Q_ASSERT(it->second.bufferingTime >= 0);
110 Q_ASSERT(it->second.bufferingSize >= 0);
111 }
112
114}
115
117{
118 if (!PlaybackEngineObject::canDoNextStep() || isAtEnd() || m_streams.empty())
119 return false;
120
121 auto checkBufferingTime = [](const auto &streamIndexToData) {
122 return streamIndexToData.second.bufferingTime < MaxBufferingTimeUs &&
123 streamIndexToData.second.bufferingSize < MaxBufferingSize;
124 };
125
126 return std::all_of(m_streams.begin(), m_streams.end(), checkBufferingTime);
127}
128
129void Demuxer::ensureSeeked()
130{
131 if (std::exchange(m_seeked, true))
132 return;
133
134 const qint64 seekPos = m_posWithOffset.pos * AV_TIME_BASE / 1000000;
135 auto err = av_seek_frame(m_context, -1, seekPos, AVSEEK_FLAG_BACKWARD);
136
137 if (err < 0) {
138 qDebug() << err2str(err);
139 emit error(err, err2str(err));
140 return;
141 }
142
143 setAtEnd(false);
145}
146
148{
149 switch (trackType) {
156 default:
157 Q_ASSERT(!"Unknown track type");
158 }
159
160 return nullptr;
161}
162
163void Demuxer::setLoops(int loopsCount)
164{
165 qCDebug(qLcDemuxer) << "setLoops to demuxer" << loopsCount;
166 m_loops = loopsCount;
167}
168
169} // namespace QFFmpeg
170
172
173#include "moc_qffmpegdemuxer_p.cpp"
Demuxer(AVFormatContext *context, const PositionWithOffset &posWithOffset, const StreamIndexes &streamIndexes, int loops)
void setLoops(int loopsCount)
void doNextStep() override
void requestProcessVideoPacket(Packet)
void(Demuxer::*)(Packet) RequestingSignal
void onPacketProcessed(Packet)
void requestProcessAudioPacket(Packet)
void requestProcessSubtitlePacket(Packet)
static RequestingSignal signalByTrackType(QPlatformMediaPlayer::TrackType trackType)
bool canDoNextStep() const override
void error(int code, const QString &errorString)
void scheduleNextStep(bool allowDoImmediatelly=true)
QSet< QString >::iterator it
auto signal
std::array< int, 3 > StreamIndexes
QString err2str(int errnum)
Definition qffmpeg_p.h:56
std::unique_ptr< AVPacket, AVDeleter< decltype(&av_packet_free), &av_packet_free > > AVPacketUPtr
Definition qffmpeg_p.h:125
std::optional< qint64 > mul(qint64 a, AVRational b)
Definition qffmpeg_p.h:31
static qint64 streamTimeToUs(const AVStream *stream, qint64 time)
Combined button and popup list for selecting options.
static void * context
EGLStreamKHR stream
static QT_BEGIN_NAMESPACE constexpr qint64 MaxBufferingTimeUs
static constexpr qint64 MaxBufferingSize
#define qDebug
[1]
Definition qlogging.h:160
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLuint res
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
long long qint64
Definition qtypes.h:55
AVPacket * avPacket() const
bool isValid() const
quint64 sourceId() const