4#include "playbackengine/qffmpegstreamdecoder_p.h"
5#include "playbackengine/qffmpegmediadataholder_p.h"
6#include <qloggingcategory.h>
14StreamDecoder::StreamDecoder(
const PlaybackEngineObjectID &id,
const CodecContext &codecContext,
15 TrackPosition absSeekPos)
16 : PlaybackEngineObject(id),
17 m_codecContext(codecContext),
18 m_trackType(MediaDataHolder::trackTypeFromMediaType(codecContext.context()->codec_type)),
19 m_sessionCtx{ absSeekPos }
21 qCDebug(qLcStreamDecoder) <<
"Create stream decoder, trackType" << m_trackType
22 <<
"absSeekPos:" << absSeekPos.get();
23 Q_ASSERT(m_trackType != QPlatformMediaPlayer::NTrackTypes);
28 avcodec_flush_buffers(m_codecContext.context());
33 updateSession(sessionID, [
this, pos, offset]() {
34 m_sessionCtx = { offset.loopStartTimeUs.asDuration() + pos };
35 avcodec_flush_buffers(m_codecContext.context());
41 if (checkSessionID(sourceID.sessionID))
47 if (packet.isValid() && !checkSessionID(packet.sourceID().sessionID)) {
48 qCDebug(qLcStreamDecoder) <<
"Packet session outdated. Source id:" << packet.sourceID()
49 <<
"current id" << id();
54 m_sessionCtx.packets.enqueue(std::move(packet));
60 Packet packet = m_sessionCtx.packets.dequeue();
62 auto decodePacket = [
this](
const Packet &packet) {
63 if (trackType() == QPlatformMediaPlayer::SubtitleStream)
64 decodeSubtitle(packet);
69 if (packet.isValid() && packet.loopOffset().loopIndex != m_sessionCtx.offset.loopIndex) {
72 qCDebug(qLcStreamDecoder) <<
"flush buffers due to new loop:"
73 << packet.loopOffset().loopIndex;
75 avcodec_flush_buffers(m_codecContext.context());
76 m_sessionCtx.offset = packet.loopOffset();
81 setAtEnd(!packet.isValid());
84 emit packetProcessed(
std::move(packet));
111 if (!checkID(frame.sourceID()))
114 --m_sessionCtx.pendingFramesCount;
115 Q_ASSERT(m_sessionCtx.pendingFramesCount >= 0);
122 const qint32 maxCount = maxQueueSize(m_trackType);
124 return !m_sessionCtx.packets.empty() && m_sessionCtx.pendingFramesCount < maxCount
125 && PlaybackEngineObject::canDoNextStep();
130 if (frame.isValid() && frame.absoluteEnd() < m_sessionCtx.absSeekPos)
133 Q_ASSERT(m_sessionCtx.pendingFramesCount >= 0);
134 ++m_sessionCtx.pendingFramesCount;
135 emit requestHandleFrame(frame);
140 auto sendPacketResult = sendAVPacket(packet);
142 if (sendPacketResult == AVERROR(EAGAIN)) {
149 sendPacketResult = sendAVPacket(packet);
151 if (sendPacketResult != AVERROR(EAGAIN))
152 qWarning() <<
"Unexpected FFmpeg behavior";
155 if (sendPacketResult == 0)
156 receiveAVFrames(!packet.isValid());
161 return avcodec_send_packet(m_codecContext.context(), packet.isValid() ? packet.avPacket() :
nullptr);
167 auto avFrame = makeAVFrame();
169 const auto receiveFrameResult = avcodec_receive_frame(m_codecContext.context(), avFrame.get());
171 if (receiveFrameResult == AVERROR_EOF || receiveFrameResult == AVERROR(EAGAIN)) {
172 if (flushPacket && receiveFrameResult == AVERROR(EAGAIN)) {
180 qWarning() <<
"Unexpected FFmpeg behavior: EAGAIN state for avcodec_receive_frame "
181 <<
"at end of the stream";
188 if (receiveFrameResult < 0) {
189 emit error(QMediaPlayer::FormatError, err2str(receiveFrameResult));
195 if (m_trackType == QPlatformMediaPlayer::VideoStream)
196 avFrame = copyFromHwPool(
std::move(avFrame));
198 onFrameFound({ m_sessionCtx.offset, std::move(avFrame), m_codecContext, id() });
204 if (!packet.isValid())
209 memset(&subtitle, 0,
sizeof(subtitle));
213 avcodec_decode_subtitle2(m_codecContext.context(), &subtitle, &gotSubtitle, packet.avPacket());
216 if (res < 0 || !gotSubtitle)
221 TrackPosition start = 0, end = 0;
222 if (subtitle.pts == AV_NOPTS_VALUE) {
223 start = m_codecContext.toTrackPosition(AVStreamPosition(packet.avPacket()->pts));
224 end = start + m_codecContext.toTrackDuration(AVStreamDuration(packet.avPacket()->duration));
226 auto pts = timeStampUs(subtitle.pts, AVRational{ 1, AV_TIME_BASE });
227 start = TrackPosition(*pts + qint64(subtitle.start_display_time) * 1000);
228 end = TrackPosition(*pts + qint64(subtitle.end_display_time) * 1000);
232 qWarning() <<
"Invalid subtitle time";
237 for (uint i = 0; i < subtitle.num_rects; ++i) {
238 const auto *r = subtitle.rects[i];
241 text += QLatin1Char(
'\n');
243 text += QString::fromUtf8(r->text);
245 const char *ass = r->ass;
254 text += QString::fromUtf8(ass);
257 text.replace(QLatin1String(
"\\N"), QLatin1String(
"\n"));
258 text.replace(QLatin1String(
"\\n"), QLatin1String(
"\n"));
259 text.replace(QLatin1String(
"\r\n"), QLatin1String(
"\n"));
260 if (text.endsWith(QLatin1Char(
'\n')))
263 onFrameFound({ m_sessionCtx.offset, text, start, end - start, id() });
266 onFrameFound({ m_sessionCtx.offset, QString(), end, TrackDuration(0), id() });
272#include "moc_qffmpegstreamdecoder_p.cpp"
bool canDoNextStep() const override
~StreamDecoder() override
void doNextStep() override
void onFrameProcessed(Frame frame)
void onFinalPacketReceived(PlaybackEngineObjectID sourceID)
void seek(quint64 sessionID, TrackPosition pos, const LoopOffset &offset)
QPlatformMediaPlayer::TrackType trackType() const
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
Combined button and popup list for selecting options.
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)