21 const AVFormatContext *context)
23 const AVPacket &avPacket = *packet.avPacket();
24 return packet.loopOffset().loopStartTimeUs.asDuration()
25 + toTrackPosition(AVStreamPosition(avPacket.pts + avPacket.duration), stream, context);
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(AVFormatContext *context, TrackPosition initialPosUs,
bool seekPending,
54 const LoopOffset &loopOffset,
const StreamIndexes &streamIndexes,
int loops)
56 m_seeked(!seekPending && initialPosUs == TrackPosition{ 0 }),
57 m_posInLoopUs{ initialPosUs },
58 m_loopOffset(loopOffset),
61 qCDebug(qLcDemuxer) <<
"Create demuxer."
62 <<
"pos:" << m_posInLoopUs.get()
63 <<
"loop offset:" << m_loopOffset.loopStartTimeUs.get()
64 <<
"loop index:" << m_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 };
81 Packet packet(m_loopOffset, AVPacketUPtr{ av_packet_alloc() }, id());
82 AVPacket &avPacket = *packet.avPacket();
84 const int demuxStatus = av_read_frame(m_context, &avPacket);
85 if (demuxStatus == AVERROR_EXIT)
88 const int streamIndex = avPacket.stream_index;
89 auto streamIterator = m_streams.find(streamIndex);
90 const bool streamIsRelevant = streamIterator != m_streams.end();
92 if (demuxStatus == AVERROR_EOF
93 || (streamIsRelevant && !isPacketWithinStreamDuration(m_context, packet))) {
94 ++m_loopOffset.loopIndex;
96 const auto loops = m_loops.loadAcquire();
97 if (loops >= 0 && m_loopOffset.loopIndex >= loops) {
98 qCDebug(qLcDemuxer) <<
"finish demuxing";
100 if (!std::exchange(m_buffered,
true))
101 emit packetsBuffered();
107 m_posInLoopUs = TrackPosition(0);
108 m_loopOffset.loopStartTimeUs = m_maxPacketsEndPos;
109 m_maxPacketsEndPos = TrackPosition(0);
113 qCDebug(qLcDemuxer) <<
"Demuxer loops changed. Index:" << m_loopOffset.loopIndex
114 <<
"Offset:" << m_loopOffset.loopStartTimeUs.get();
116 scheduleNextStep(
false);
122 if (demuxStatus < 0) {
123 qCWarning(qLcDemuxer) <<
"Demuxing failed" << demuxStatus << AVError(demuxStatus);
125 if (demuxStatus == AVERROR(EAGAIN) && m_demuxerRetryCount != s_maxDemuxerRetries) {
131 ++m_demuxerRetryCount;
133 qCDebug(qLcDemuxer) <<
"Retrying";
134 scheduleNextStep(
false);
140 emit error(QMediaPlayer::ResourceError,
141 QLatin1StringView(
"Demuxing failed"));
147 m_demuxerRetryCount = 0;
149 if (streamIsRelevant) {
150 auto &streamData = streamIterator->second;
151 const AVStream *stream = m_context->streams[streamIndex];
153 const TrackPosition endPos = packetEndPos(packet, stream, m_context);
154 m_maxPacketsEndPos = qMax(m_maxPacketsEndPos, endPos);
158 streamData.bufferedDuration += toTrackDuration(AVStreamDuration(avPacket.duration), stream);
159 streamData.bufferedSize += avPacket.size;
160 streamData.maxSentPacketsPos = qMax(streamData.maxSentPacketsPos, endPos);
161 updateStreamDataLimitFlag(streamData);
163 if (!m_buffered && streamData.isDataLimitReached) {
165 emit packetsBuffered();
168 if (!m_firstPacketFound) {
169 m_firstPacketFound =
true;
170 emit firstPacketFound(id(), m_posInLoopUs + m_loopOffset.loopStartTimeUs.asDuration());
173 auto signal = signalByTrackType(streamData.trackType);
174 emit (
this->*signal)(packet);
177 scheduleNextStep(
false);
182 Q_ASSERT(packet.isValid());
184 if (packet.sourceId() != id())
187 auto &avPacket = *packet.avPacket();
189 const auto streamIndex = avPacket.stream_index;
190 const auto stream = m_context->streams[streamIndex];
191 auto it = m_streams.find(streamIndex);
193 if (it != m_streams.end()) {
194 auto &streamData = it->second;
198 streamData.bufferedDuration -= toTrackDuration(AVStreamDuration(avPacket.duration), stream);
199 streamData.bufferedSize -= avPacket.size;
200 streamData.maxProcessedPacketPos =
201 qMax(streamData.maxProcessedPacketPos, packetEndPos(packet, stream, m_context));
203 Q_ASSERT(it->second.bufferedDuration >= TrackDuration(0));
204 Q_ASSERT(it->second.bufferedSize >= 0);
206 updateStreamDataLimitFlag(streamData);
253 auto err = av_seek_frame(m_context, -1, seekPos.get(), AVSEEK_FLAG_BACKWARD);
272 case QPlatformMediaPlayer::TrackType::VideoStream:
273 return &Demuxer::requestProcessVideoPacket;
274 case QPlatformMediaPlayer::TrackType::AudioStream:
275 return &Demuxer::requestProcessAudioPacket;
276 case QPlatformMediaPlayer::TrackType::SubtitleStream:
277 return &Demuxer::requestProcessSubtitlePacket;
279 Q_ASSERT(!
"Unknown track type");