30 const AVPacket &avPacket = *packet.avPacket();
31 const AVStream &avStream = *context->streams[avPacket.stream_index];
32 const AVStreamDuration streamDuration(avStream.duration);
33 if (streamDuration.get() <= 0
34 || context->duration_estimation_method != AVFMT_DURATION_FROM_STREAM)
37 if (avPacket.pts == AV_NOPTS_VALUE) {
38 qWarning() <<
"QFFmpeg::Demuxer received AVPacket with pts == AV_NOPTS_VALUE";
42 if (avStream.start_time != AV_NOPTS_VALUE)
43 return AVStreamDuration(avPacket.pts - avStream.start_time) <= streamDuration;
45 const TrackPosition trackPos = toTrackPosition(AVStreamPosition(avPacket.pts), &avStream, context);
46 const TrackPosition trackPosOfStreamEnd = toTrackDuration(streamDuration, &avStream).asTimePoint();
47 return trackPos <= trackPosOfStreamEnd;
53Demuxer::Demuxer(
const PlaybackEngineObjectID &id, AVFormatContext *context,
54 TrackPosition initialPosUs,
bool seekPending,
const LoopOffset &loopOffset,
55 const StreamIndexes &streamIndexes,
int loops)
56 : PlaybackEngineObject(id),
58 m_sessionCtx{ initialPosUs, loopOffset, !seekPending && initialPosUs == TrackPosition{ 0 } },
61 qCDebug(qLcDemuxer) <<
"Create demuxer."
62 <<
"pos:" << m_sessionCtx.posInLoopUs.get()
63 <<
"loop offset:" << m_sessionCtx.loopOffset.loopStartTimeUs.get()
64 <<
"loop index:" << m_sessionCtx.loopOffset.loopIndex <<
"loops:" << loops;
68 for (
auto i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
69 if (streamIndexes[i] >= 0) {
70 const auto trackType =
static_cast<QPlatformMediaPlayer::TrackType>(i);
71 qCDebug(qLcDemuxer) <<
"Activate demuxing stream" << i <<
", trackType:" << trackType;
72 m_streams[streamIndexes[i]] = { trackType };
77void Demuxer::
seek(quint64 sessionId, TrackPosition initialPosUs,
const LoopOffset &loopOffset)
79 updateSession(sessionId, [
this, initialPosUs, loopOffset]() {
80 m_sessionCtx = { initialPosUs, loopOffset };
82 for (
auto &[id, streamData] : m_streams)
83 streamData = StreamData{ streamData.trackType };
93 Packet packet(m_sessionCtx.loopOffset, AVPacketUPtr{ av_packet_alloc() }, id());
94 AVPacket &avPacket = *packet.avPacket();
96 const int demuxStatus = av_read_frame(m_context, &avPacket);
97 if (demuxStatus == AVERROR_EXIT)
100 const int streamIndex = avPacket.stream_index;
101 auto streamIterator = m_streams.find(streamIndex);
102 const bool streamIsRelevant = streamIterator != m_streams.end();
104 if (demuxStatus == AVERROR_EOF
105 || (streamIsRelevant && !isPacketWithinStreamDuration(m_context, packet))) {
106 ++m_sessionCtx.loopOffset.loopIndex;
108 const auto loops = m_loops.loadAcquire();
109 if (loops >= 0 && m_sessionCtx.loopOffset.loopIndex >= loops) {
110 qCDebug(qLcDemuxer) <<
"finish demuxing";
112 if (!std::exchange(m_sessionCtx.buffered,
true))
113 emit packetsBuffered();
118 m_sessionCtx.seeked =
false;
119 m_sessionCtx.posInLoopUs = TrackPosition(0);
120 m_sessionCtx.loopOffset.loopStartTimeUs = m_sessionCtx.maxPacketsEndPos;
121 m_sessionCtx.maxPacketsEndPos = TrackPosition(0);
125 qCDebug(qLcDemuxer) <<
"Demuxer loops changed. Index:"
126 << m_sessionCtx.loopOffset.loopIndex
127 <<
"Offset:" << m_sessionCtx.loopOffset.loopStartTimeUs.get();
135 if (demuxStatus < 0) {
136 qCWarning(qLcDemuxer) <<
"Demuxing failed" << demuxStatus << AVError(demuxStatus);
138 if (demuxStatus == AVERROR(EAGAIN)
139 && m_sessionCtx.demuxerRetryCount != s_maxDemuxerRetries) {
145 m_sessionCtx.failTimePoint = std::chrono::steady_clock::now();
146 ++m_sessionCtx.demuxerRetryCount;
148 qCDebug(qLcDemuxer) <<
"Retrying";
155 emit error(QMediaPlayer::ResourceError,
156 QLatin1StringView(
"Demuxing failed"));
162 m_sessionCtx.demuxerRetryCount = 0;
163 m_sessionCtx.failTimePoint.reset();
165 if (streamIsRelevant) {
166 auto &streamData = streamIterator->second;
167 const AVStream *stream = m_context->streams[streamIndex];
169 const TrackPosition endPos = packetEndPos(packet, stream, m_context);
170 m_sessionCtx.maxPacketsEndPos = qMax(m_sessionCtx.maxPacketsEndPos, endPos);
174 streamData.bufferedDuration += toTrackDuration(AVStreamDuration(avPacket.duration), stream);
175 streamData.bufferedSize += avPacket.size;
176 streamData.maxSentPacketsPos = qMax(streamData.maxSentPacketsPos, endPos);
177 updateStreamDataLimitFlag(streamData);
179 if (!m_sessionCtx.buffered && streamData.isDataLimitReached) {
180 m_sessionCtx.buffered =
true;
181 emit packetsBuffered();
184 if (!m_sessionCtx.firstPacketFound) {
185 m_sessionCtx.firstPacketFound =
true;
186 emit firstPacketFound(id(),
187 m_sessionCtx.posInLoopUs
188 + m_sessionCtx.loopOffset.loopStartTimeUs.asDuration());
191 auto signal = signalByTrackType(streamData.trackType);
192 emit (
this->*signal)(
std::move(packet));
200 Q_ASSERT(packet.isValid());
202 if (!checkID(packet.sourceID()))
205 auto &avPacket = *packet.avPacket();
207 const auto streamIndex = avPacket.stream_index;
208 const auto stream = m_context->streams[streamIndex];
209 auto it = m_streams.find(streamIndex);
211 if (it != m_streams.end()) {
212 auto &streamData = it->second;
216 streamData.bufferedDuration -= toTrackDuration(AVStreamDuration(avPacket.duration), stream);
217 streamData.bufferedSize -= avPacket.size;
218 streamData.maxProcessedPacketPos =
219 qMax(streamData.maxProcessedPacketPos, packetEndPos(packet, stream, m_context));
221 Q_ASSERT(it->second.bufferedDuration >= TrackDuration(0));
222 Q_ASSERT(it->second.bufferedSize >= 0);
224 updateStreamDataLimitFlag(streamData);
291 case QPlatformMediaPlayer::TrackType::VideoStream:
292 return &Demuxer::requestProcessVideoPacket;
293 case QPlatformMediaPlayer::TrackType::AudioStream:
294 return &Demuxer::requestProcessAudioPacket;
295 case QPlatformMediaPlayer::TrackType::SubtitleStream:
296 return &Demuxer::requestProcessSubtitlePacket;
298 Q_ASSERT(!
"Unknown track type");