32 const AVPacket &avPacket = *packet.avPacket();
33 const AVStream &avStream = *context->streams[avPacket.stream_index];
34 const AVStreamDuration streamDuration(avStream.duration);
35 if (streamDuration.get() <= 0
36 || context->duration_estimation_method != AVFMT_DURATION_FROM_STREAM)
39 if (avPacket.pts == AV_NOPTS_VALUE) {
40 qWarning() <<
"QFFmpeg::Demuxer received AVPacket with pts == AV_NOPTS_VALUE";
44 if (avStream.start_time != AV_NOPTS_VALUE)
45 return AVStreamDuration(avPacket.pts - avStream.start_time) <= streamDuration;
47 const TrackPosition trackPos = toTrackPosition(AVStreamPosition(avPacket.pts), &avStream, context);
48 const TrackPosition trackPosOfStreamEnd = toTrackDuration(streamDuration, &avStream).asTimePoint();
49 return trackPos <= trackPosOfStreamEnd;
55Demuxer::Demuxer(
const PlaybackEngineObjectID &id, AVFormatContext *context,
56 TrackPosition initialPosUs,
bool seekPending,
const LoopOffset &loopOffset,
57 const StreamIndexes &streamIndexes,
int loops)
58 : PlaybackEngineObject(id),
60 m_sessionCtx{ initialPosUs, loopOffset, !seekPending && initialPosUs == TrackPosition{ 0 } },
63 qCDebug(qLcDemuxer) <<
"Create demuxer."
64 <<
"pos:" << m_sessionCtx.posInLoopUs.get()
65 <<
"loop offset:" << m_sessionCtx.loopOffset.loopStartTimeUs.get()
66 <<
"loop index:" << m_sessionCtx.loopOffset.loopIndex <<
"loops:" << loops;
70 for (
auto i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
71 if (streamIndexes[i] >= 0) {
72 const auto trackType =
static_cast<QPlatformMediaPlayer::TrackType>(i);
73 qCDebug(qLcDemuxer) <<
"Activate demuxing stream" << i <<
", trackType:" << trackType;
74 m_streams[streamIndexes[i]] = { trackType };
79void Demuxer::
seek(quint64 sessionId, TrackPosition initialPosUs,
const LoopOffset &loopOffset)
81 updateSession(sessionId, [
this, initialPosUs, loopOffset]() {
82 m_sessionCtx = { initialPosUs, loopOffset };
84 for (
auto &[id, streamData] : m_streams)
85 streamData = StreamData{ streamData.trackType };
95 Packet packet(m_sessionCtx.loopOffset, AVPacketUPtr{ av_packet_alloc() }, id());
96 AVPacket &avPacket = *packet.avPacket();
98 const int demuxStatus = av_read_frame(m_context, &avPacket);
99 if (demuxStatus == AVERROR_EXIT)
102 const int streamIndex = avPacket.stream_index;
103 auto streamIterator = m_streams.find(streamIndex);
104 const bool streamIsRelevant = streamIterator != m_streams.end();
106 if (demuxStatus == AVERROR_EOF
107 || (streamIsRelevant && !isPacketWithinStreamDuration(m_context, packet))) {
108 ++m_sessionCtx.loopOffset.loopIndex;
110 const auto loops = m_loops.loadAcquire();
111 if (loops >= 0 && m_sessionCtx.loopOffset.loopIndex >= loops) {
112 qCDebug(qLcDemuxer) <<
"finish demuxing";
114 if (!std::exchange(m_sessionCtx.buffered,
true))
115 emit packetsBuffered();
120 m_sessionCtx.seeked =
false;
121 m_sessionCtx.posInLoopUs = TrackPosition(0);
122 m_sessionCtx.loopOffset.loopStartTimeUs = m_sessionCtx.maxPacketsEndPos;
123 m_sessionCtx.maxPacketsEndPos = TrackPosition(0);
127 qCDebug(qLcDemuxer) <<
"Demuxer loops changed. Index:"
128 << m_sessionCtx.loopOffset.loopIndex
129 <<
"Offset:" << m_sessionCtx.loopOffset.loopStartTimeUs.get();
137 if (demuxStatus < 0) {
138 qCWarning(qLcDemuxer) <<
"Demuxing failed" << demuxStatus << AVError(demuxStatus);
140 if (demuxStatus == AVERROR(EAGAIN)
141 && m_sessionCtx.demuxerRetryCount != s_maxDemuxerRetries) {
147 m_sessionCtx.failTimePoint = std::chrono::steady_clock::now();
148 ++m_sessionCtx.demuxerRetryCount;
150 qCDebug(qLcDemuxer) <<
"Retrying";
157 emit error(QMediaPlayer::ResourceError,
158 QLatin1StringView(
"Demuxing failed"));
164 m_sessionCtx.demuxerRetryCount = 0;
165 m_sessionCtx.failTimePoint.reset();
167 if (streamIsRelevant) {
168 auto &streamData = streamIterator->second;
169 const AVStream *stream = m_context->streams[streamIndex];
171 const TrackPosition endPos = packetEndPos(packet, stream, m_context);
172 m_sessionCtx.maxPacketsEndPos = qMax(m_sessionCtx.maxPacketsEndPos, endPos);
176 streamData.bufferedDuration += toTrackDuration(AVStreamDuration(avPacket.duration), stream);
177 streamData.bufferedSize += avPacket.size;
178 streamData.maxSentPacketsPos = qMax(streamData.maxSentPacketsPos, endPos);
179 updateStreamDataLimitFlag(streamData);
181 if (!m_sessionCtx.buffered && streamData.isDataLimitReached) {
182 m_sessionCtx.buffered =
true;
183 emit packetsBuffered();
186 if (!m_sessionCtx.firstPacketFound) {
187 m_sessionCtx.firstPacketFound =
true;
188 emit firstPacketFound(id(),
189 m_sessionCtx.posInLoopUs
190 + m_sessionCtx.loopOffset.loopStartTimeUs.asDuration());
193 auto signal = signalByTrackType(streamData.trackType);
194 emit (
this->*signal)(
std::move(packet));
200void Demuxer::onPacketProcessed(
const Packet &packet)
202 Q_ASSERT(packet.isValid());
204 if (!checkID(packet.sourceID()))
207 auto &avPacket = *packet.avPacket();
209 const auto streamIndex = avPacket.stream_index;
210 const auto stream = m_context->streams[streamIndex];
211 auto it = m_streams.find(streamIndex);
213 if (it != m_streams.end()) {
214 auto &streamData = it->second;
218 streamData.bufferedDuration -= toTrackDuration(AVStreamDuration(avPacket.duration), stream);
219 streamData.bufferedSize -= avPacket.size;
220 streamData.maxProcessedPacketPos =
221 qMax(streamData.maxProcessedPacketPos, packetEndPos(packet, stream, m_context));
223 Q_ASSERT(it->second.bufferedDuration >= TrackDuration(0));
224 Q_ASSERT(it->second.bufferedSize >= 0);
226 updateStreamDataLimitFlag(streamData);
293 case QPlatformMediaPlayer::TrackType::VideoStream:
294 return &Demuxer::requestProcessVideoPacket;
295 case QPlatformMediaPlayer::TrackType::AudioStream:
296 return &Demuxer::requestProcessAudioPacket;
297 case QPlatformMediaPlayer::TrackType::SubtitleStream:
298 return &Demuxer::requestProcessSubtitlePacket;
300 Q_ASSERT(!
"Unknown track type");