Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qffmpegstreamdecoder.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
6#include <qloggingcategory.h>
7
9
10static Q_LOGGING_CATEGORY(qLcStreamDecoder, "qt.multimedia.ffmpeg.streamdecoder");
11
12namespace QFFmpeg {
13
15 : m_codec(codec),
16 m_absSeekPos(absSeekPos),
17 m_trackType(MediaDataHolder::trackTypeFromMediaType(codec.context()->codec_type))
18{
19 qCDebug(qLcStreamDecoder) << "Create stream decoder, trackType" << m_trackType
20 << "absSeekPos:" << absSeekPos;
22}
23
25{
26 avcodec_flush_buffers(m_codec.context());
27}
28
33
35{
36 m_absSeekPos = trackPos;
37}
38
40{
41 m_packets.enqueue(packet);
42
44}
45
47{
48 auto packet = m_packets.dequeue();
49
50 auto decodePacket = [this](Packet packet) {
52 decodeSubtitle(packet);
53 else
54 decodeMedia(packet);
55 };
56
57 if (packet.isValid() && packet.loopOffset().index != m_offset.index) {
58 decodePacket({});
59
60 qCDebug(qLcStreamDecoder) << "flush buffers due to new loop:" << packet.loopOffset().index;
61
62 avcodec_flush_buffers(m_codec.context());
63 m_offset = packet.loopOffset();
64 }
65
66 decodePacket(packet);
67
68 setAtEnd(!packet.isValid());
69
70 if (packet.isValid())
71 emit packetProcessed(packet);
72
73 scheduleNextStep(false);
74}
75
77{
78 return m_trackType;
79}
80
82{
83 switch (type) {
84
86 return 3;
88 return 9;
90 return 6; /*main packet and closing packet*/
91 default:
92 Q_UNREACHABLE_RETURN(-1);
93 }
94}
95
97{
98 if (frame.sourceId() != id())
99 return;
100
101 --m_pendingFramesCount;
102 Q_ASSERT(m_pendingFramesCount >= 0);
103
105}
106
108{
109 const qint32 maxCount = maxQueueSize(m_trackType);
110
111 return !m_packets.empty() && m_pendingFramesCount < maxCount
113}
114
115void StreamDecoder::onFrameFound(Frame frame)
116{
117 if (frame.isValid() && frame.absoluteEnd() < m_absSeekPos)
118 return;
119
120 Q_ASSERT(m_pendingFramesCount >= 0);
121 ++m_pendingFramesCount;
123}
124
125void StreamDecoder::decodeMedia(Packet packet)
126{
127 auto sendPacketResult = sendAVPacket(packet);
128
129 if (sendPacketResult == AVERROR(EAGAIN)) {
130 // Doc says:
131 // AVERROR(EAGAIN): input is not accepted in the current state - user
132 // must read output with avcodec_receive_frame() (once
133 // all output is read, the packet should be resent, and
134 // the call will not fail with EAGAIN).
135 receiveAVFrames();
136 sendPacketResult = sendAVPacket(packet);
137
138 if (sendPacketResult != AVERROR(EAGAIN))
139 qWarning() << "Unexpected FFmpeg behavior";
140 }
141
142 if (sendPacketResult == 0)
143 receiveAVFrames();
144}
145
146int StreamDecoder::sendAVPacket(Packet packet)
147{
148 return avcodec_send_packet(m_codec.context(), packet.isValid() ? packet.avPacket() : nullptr);
149}
150
151void StreamDecoder::receiveAVFrames()
152{
153 while (true) {
154 auto avFrame = makeAVFrame();
155
156 const auto receiveFrameResult = avcodec_receive_frame(m_codec.context(), avFrame.get());
157
158 if (receiveFrameResult == AVERROR_EOF || receiveFrameResult == AVERROR(EAGAIN))
159 break;
160
161 if (receiveFrameResult < 0) {
162 emit error(QMediaPlayer::FormatError, err2str(receiveFrameResult));
163 break;
164 }
165
166 onFrameFound({ m_offset, std::move(avFrame), m_codec, 0, id() });
167 }
168}
169
170void StreamDecoder::decodeSubtitle(Packet packet)
171{
172 if (!packet.isValid())
173 return;
174 // qCDebug(qLcDecoder) << " decoding subtitle" << "has delay:" <<
175 // (codec->codec->capabilities & AV_CODEC_CAP_DELAY);
176 AVSubtitle subtitle;
177 memset(&subtitle, 0, sizeof(subtitle));
178 int gotSubtitle = 0;
179
180 const int res =
181 avcodec_decode_subtitle2(m_codec.context(), &subtitle, &gotSubtitle, packet.avPacket());
182 // qCDebug(qLcDecoder) << " subtitle got:" << res << gotSubtitle << subtitle.format <<
183 // Qt::hex << (quint64)subtitle.pts;
184 if (res < 0 || !gotSubtitle)
185 return;
186
187 // apparently the timestamps in the AVSubtitle structure are not always filled in
188 // if they are missing, use the packets pts and duration values instead
190 if (subtitle.pts == AV_NOPTS_VALUE) {
191 start = m_codec.toUs(packet.avPacket()->pts);
192 end = start + m_codec.toUs(packet.avPacket()->duration);
193 } else {
194 auto pts = timeStampUs(subtitle.pts, AVRational{ 1, AV_TIME_BASE });
195 start = *pts + qint64(subtitle.start_display_time) * 1000;
196 end = *pts + qint64(subtitle.end_display_time) * 1000;
197 }
198
199 if (end <= start) {
200 qWarning() << "Invalid subtitle time";
201 return;
202 }
203 // qCDebug(qLcDecoder) << " got subtitle (" << start << "--" << end << "):";
205 for (uint i = 0; i < subtitle.num_rects; ++i) {
206 const auto *r = subtitle.rects[i];
207 // qCDebug(qLcDecoder) << " subtitletext:" << r->text << "/" << r->ass;
208 if (i)
209 text += QLatin1Char('\n');
210 if (r->text)
211 text += QString::fromUtf8(r->text);
212 else {
213 const char *ass = r->ass;
214 int nCommas = 0;
215 while (*ass) {
216 if (nCommas == 8)
217 break;
218 if (*ass == ',')
219 ++nCommas;
220 ++ass;
221 }
222 text += QString::fromUtf8(ass);
223 }
224 }
227 text.replace(QLatin1String("\r\n"), QLatin1String("\n"));
228 if (text.endsWith(QLatin1Char('\n')))
229 text.chop(1);
230
231 onFrameFound({ m_offset, text, start, end - start, id() });
232
233 // TODO: maybe optimize
234 onFrameFound({ m_offset, QString(), end, 0, id() });
235}
236} // namespace QFFmpeg
237
239
240#include "moc_qffmpegstreamdecoder_p.cpp"
AVCodecContext * context() const
qint64 toUs(qint64 ts) const
void scheduleNextStep(bool allowDoImmediatelly=true)
std::chrono::steady_clock::time_point TimePoint
StreamDecoder(const Codec &codec, qint64 absSeekPos)
bool canDoNextStep() const override
static qint32 maxQueueSize(QPlatformMediaPlayer::TrackType type)
void onFrameProcessed(Frame frame)
void packetProcessed(Packet)
void setInitialPosition(TimePoint tp, qint64 trackPos)
void requestHandleFrame(Frame frame)
QPlatformMediaPlayer::TrackType trackType() const
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
QString text
AVFrameUPtr makeAVFrame()
Definition qffmpeg_p.h:136
QString err2str(int errnum)
Definition qffmpeg_p.h:64
std::optional< qint64 > timeStampUs(qint64 ts, AVRational base)
Definition qffmpeg_p.h:54
Combined button and popup list for selecting options.
static void * context
DBusConnection const char DBusError * error
QMediaFormat::AudioCodec codec
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLboolean r
[2]
GLuint GLuint end
GLenum type
GLuint start
GLuint res
GLsizei maxCount
Definition qopenglext.h:677
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define emit
int qint32
Definition qtypes.h:49
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
#define decode(x)
QObject::connect nullptr
QFrame frame
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18