6#include "playbackengine/qffmpegmediadataholder_p.h"
7#include <QtCore/qloggingcategory.h>
8#include <QtCore/qregularexpression.h>
9#include <QtCore/qspan.h>
13Q_STATIC_LOGGING_CATEGORY(qLcStreamDecoder,
"qt.multimedia.ffmpeg.streamdecoder");
20 TrackPosition absSeekPos)
26 qCDebug(qLcStreamDecoder) <<
"Create stream decoder, trackType" << m_trackType
27 <<
"absSeekPos:" << absSeekPos.get();
32 avcodec_flush_buffers(m_codecContext.context());
37 updateSession(sessionID, [
this, pos, offset]() {
38 m_sessionCtx = { offset.loopStartTimeUs.asDuration() + pos };
39 avcodec_flush_buffers(m_codecContext.context());
45 if (checkSessionID(sourceID.sessionID))
51 if (packet.isValid() && !checkSessionID(packet.sourceID().sessionID)) {
52 qCDebug(qLcStreamDecoder) <<
"Packet session outdated. Source id:" << packet.sourceID()
53 <<
"current id" << id();
58 m_sessionCtx.packets.enqueue(std::move(packet));
64 Packet packet = m_sessionCtx.packets.dequeue();
66 auto decodePacket = [
this](
const Packet &packet) {
67 if (trackType() == TrackType::SubtitleStream)
68 decodeSubtitle(packet);
73 if (packet.isValid() && packet.loopOffset().loopIndex != m_sessionCtx.offset.loopIndex) {
76 qCDebug(qLcStreamDecoder) <<
"flush buffers due to new loop:"
77 << packet.loopOffset().loopIndex;
79 avcodec_flush_buffers(m_codecContext.context());
80 m_sessionCtx.offset = packet.loopOffset();
85 setAtEnd(!packet.isValid());
88 emit packetProcessed(
std::move(packet));
102 case TrackType::VideoStream:
104 case TrackType::AudioStream:
106 case TrackType::SubtitleStream:
109 Q_UNREACHABLE_RETURN(-1);
115 if (!checkID(frame.sourceID()))
118 --m_sessionCtx.pendingFramesCount;
119 Q_ASSERT(m_sessionCtx.pendingFramesCount >= 0);
126 const qint32 maxCount = maxQueueSize(m_trackType);
128 return !m_sessionCtx.packets.empty() && m_sessionCtx.pendingFramesCount < maxCount
129 && PlaybackEngineObject::canDoNextStep();
134 if (frame.isValid() && frame.absoluteEnd() < m_sessionCtx.absSeekPos)
137 Q_ASSERT(m_sessionCtx.pendingFramesCount >= 0);
138 ++m_sessionCtx.pendingFramesCount;
139 emit requestHandleFrame(frame);
144 auto sendPacketResult = sendAVPacket(packet);
146 if (sendPacketResult == AVERROR(EAGAIN)) {
153 sendPacketResult = sendAVPacket(packet);
155 if (sendPacketResult != AVERROR(EAGAIN))
156 qWarning() <<
"Unexpected FFmpeg behavior";
159 if (sendPacketResult == 0)
160 receiveAVFrames(!packet.isValid());
165 return avcodec_send_packet(m_codecContext.context(), packet.isValid() ? packet.avPacket() :
nullptr);
171 auto avFrame = makeAVFrame();
173 const auto receiveFrameResult = avcodec_receive_frame(m_codecContext.context(), avFrame.get());
175 if (receiveFrameResult == AVERROR_EOF || receiveFrameResult == AVERROR(EAGAIN)) {
176 if (flushPacket && receiveFrameResult == AVERROR(EAGAIN)) {
184 qWarning() <<
"Unexpected FFmpeg behavior: EAGAIN state for avcodec_receive_frame "
185 <<
"at end of the stream";
192 if (receiveFrameResult < 0) {
193 emit error(QMediaPlayer::FormatError, err2str(receiveFrameResult));
199 if (m_trackType == TrackType::VideoStream)
200 avFrame = copyFromHwPool(
std::move(avFrame));
202 onFrameFound({ m_sessionCtx.offset, std::move(avFrame), m_codecContext, id() });
208 if (!packet.isValid())
213 memset(&subtitle, 0,
sizeof(subtitle));
217 avcodec_decode_subtitle2(m_codecContext.context(), &subtitle, &gotSubtitle, packet.avPacket());
220 if (res < 0 || !gotSubtitle)
225 TrackPosition start = 0, end = 0;
226 if (subtitle.pts == AV_NOPTS_VALUE) {
227 start = m_codecContext.toTrackPosition(AVStreamPosition(packet.avPacket()->pts));
228 end = start + m_codecContext.toTrackDuration(AVStreamDuration(packet.avPacket()->duration));
230 auto pts = timeStampUs(subtitle.pts, AVRational{ 1, AV_TIME_BASE });
231 start = TrackPosition(*pts + qint64(subtitle.start_display_time) * 1000);
232 end = TrackPosition(*pts + qint64(subtitle.end_display_time) * 1000);
236 qWarning() <<
"Invalid subtitle time";
240 QString text = subtitleTextFromAVSubtitle(subtitle);
242 onFrameFound({ m_sessionCtx.offset, text, start, end - start, id() });
245 onFrameFound({ m_sessionCtx.offset, QString(), end, TrackDuration(0), id() });
251 return QString::fromUtf8(r.text);
253 const char *ass = r.ass;
262 return QString::fromUtf8(ass);
269 for (
const AVSubtitleRect *r : QSpan(subtitle.rects, subtitle.num_rects)) {
270 if (r != subtitle.rects[0])
273 text += extractSubtitleText(*r);
275 static const QRegularExpression lineBreakRe(u"\\\\N|\\\\n|\\r\\n"_s);
276 text.replace(lineBreakRe, u"\n"_s);
277 if (text.endsWith(u'\n'))
286#include "moc_qffmpegstreamdecoder_p.cpp"
bool canDoNextStep() const override
~StreamDecoder() override
StreamDecoder(const PlaybackEngineObjectID &id, const CodecContext &codecContext, TrackPosition absSeekPos)
void doNextStep() override
void onFrameProcessed(const Frame &frame)
void onFinalPacketReceived(PlaybackEngineObjectID sourceID)
TrackType trackType() const
void seek(quint64 sessionID, TrackPosition pos, const LoopOffset &offset)
QT_MANGLE_NAMESPACE(QMacScreenCaptureStreamDelegate) QMacScreenCaptureStreamDelegate
QString subtitleTextFromAVSubtitle(const AVSubtitle &subtitle)
static QString extractSubtitleText(const AVSubtitleRect &r)
Combined button and popup list for selecting options.