Skip to content

Commit 3c425f0

Browse files
committed
Rework packet finding functions
After seeking, GetFrame() tries to find out where the demuxer ended up after the seek by trying to recognize the first packet that comes out of the demuxer afterwards by its PTS (or DTS if Frames.UseDTS is true), or by its Pos if that fails. Similarly, DecodePacket() needs to recognize the packet to see if it's a second field of some interlaced frame. When the same PTS can appear multiple times in the same file (e.g. in files with edit lists), this can cause issues. This commit replaces the FrameFromPTS / FrameFromPos functions and the fallback logic in GetFrame() by a single FindPacket function that takes an entire AVPacket. FindPacket() tries to find the unique packet in the index whose TS/Pos/Flags match the given packet. If no such packet exists, it tries a fallback sequence of comparing fewer fields. The current fallback sequence is mostly chosen based on intuition - for well-behaved files simply checking all fields should work fine. If examples come up where this fallback sequence becomes relevant, it can be adjusted.
1 parent 5cdc75f commit 3c425f0

4 files changed

Lines changed: 64 additions & 37 deletions

File tree

src/core/track.cpp

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -157,23 +157,68 @@ static bool PTSComparison(FrameInfo FI1, FrameInfo FI2) {
157157
return FI1.PTS < FI2.PTS;
158158
}
159159

160-
int FFMS_Track::FrameFromPTS(int64_t PTS, bool AllowHidden) const {
160+
enum class AVPacketProp {
161+
TS, // can be PTS or DTS depending on UseDTS
162+
Pos,
163+
Hidden,
164+
Key,
165+
};
166+
167+
std::vector<AVPacketProp> FindPacketCheckSequence[] = {
168+
{AVPacketProp::TS, AVPacketProp::Pos, AVPacketProp::Hidden, AVPacketProp::Key},
169+
{AVPacketProp::TS, AVPacketProp::Pos, AVPacketProp::Hidden},
170+
{AVPacketProp::TS, AVPacketProp::Pos},
171+
{AVPacketProp::TS},
172+
{AVPacketProp::Pos},
173+
};
174+
175+
int FFMS_Track::FindPacket(const AVPacket &packet) const {
161176
FrameInfo F;
162-
F.PTS = PTS;
177+
F.PTS = UseDTS ? packet.dts : packet.pts;
163178

164-
auto Pos = std::lower_bound(begin(), end(), F, PTSComparison);
165-
while (Pos != end() && (!AllowHidden && Pos->Skipped()) && Pos->PTS == PTS)
166-
Pos++;
179+
auto SameTSBegin = std::lower_bound(begin(), end(), F, PTSComparison);;
180+
auto SameTSEnd = SameTSBegin;
181+
while (SameTSEnd != end() && SameTSEnd->PTS == F.PTS)
182+
SameTSEnd++;
167183

168-
if (Pos == end() || Pos->PTS != PTS)
169-
return -1;
170-
return std::distance(begin(), Pos);
171-
}
184+
for (auto const& checks : FindPacketCheckSequence) {
185+
auto Begin = begin();
186+
auto End = end();
187+
188+
if (checks[0] == AVPacketProp::TS) {
189+
Begin = SameTSBegin;
190+
End = SameTSEnd;
191+
}
192+
193+
int found = 0;
194+
int result = -1;
195+
196+
for (auto it = Begin; it < End; it++) {
197+
bool match = std::all_of(checks.cbegin(), checks.cend(), [&](AVPacketProp check) {
198+
switch (check) {
199+
case AVPacketProp::TS:
200+
return it->PTS == F.PTS;
201+
case AVPacketProp::Pos:
202+
return it->FilePos == packet.pos;
203+
case AVPacketProp::Hidden:
204+
return it->MarkedHidden == (packet.flags & AV_PKT_FLAG_DISCARD);
205+
case AVPacketProp::Key:
206+
return it->KeyFrame == (packet.flags & AV_PKT_FLAG_KEY);
207+
}
208+
return false;
209+
});
210+
211+
if (match) {
212+
found++;
213+
result = std::distance(begin(), it);
214+
}
215+
}
216+
217+
if (found == 1) {
218+
return result;
219+
}
220+
}
172221

173-
int FFMS_Track::FrameFromPos(int64_t Pos, bool AllowHidden) const {
174-
for (size_t i = 0; i < size(); i++)
175-
if (Data->Frames[i].FilePos == Pos && (AllowHidden || !Data->Frames[i].Skipped()))
176-
return static_cast<int>(i);
177222
return -1;
178223
}
179224

src/core/track.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <vector>
2828
#include <memory>
2929

30+
struct AVPacket;
3031
class ZipFile;
3132

3233
struct FrameInfo {
@@ -82,8 +83,7 @@ struct FFMS_Track {
8283
void FillAudioGaps();
8384

8485
int FindClosestVideoKeyFrame(int Frame) const;
85-
int FrameFromPTS(int64_t PTS, bool AllowHidden = false) const;
86-
int FrameFromPos(int64_t Pos, bool AllowHidden = false) const;
86+
int FindPacket(const AVPacket &packet) const;
8787
int ClosestFrameFromPTS(int64_t PTS) const;
8888
int RealFrameNumber(int Frame) const;
8989
int VisibleFrameCount() const;

src/core/videosource.cpp

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ bool FFMS_VideoSource::DecodePacket(const AVPacket &Packet) {
744744
std::swap(DecodeFrame, LastDecodedFrame);
745745
ResendPacket = false;
746746

747-
int PacketNum = Frames.FrameFromPTS(Frames.UseDTS ? Packet.dts : Packet.pts, true);
747+
int PacketNum = Frames.FindPacket(Packet);
748748
bool PacketHidden = !!(Packet.flags & AV_PKT_FLAG_DISCARD) || (PacketNum != -1 && Frames[PacketNum].MarkedHidden);
749749
bool SecondField = PacketNum != -1 && Frames[PacketNum].SecondField;
750750

@@ -862,10 +862,8 @@ SmartAVPacket FFMS_VideoSource::DecodeNextFrame() {
862862
continue;
863863
}
864864

865-
if (!FirstPacket->data || (Frames.UseDTS ? FirstPacket->dts : FirstPacket->pts) < 0) {
866-
av_packet_unref(FirstPacket.get());
865+
if (!FirstPacket->data)
867866
av_packet_ref(FirstPacket.get(), Packet.get());
868-
}
869867

870868
bool FrameFinished = DecodePacket(*Packet);
871869
if (ResendPacket)
@@ -971,23 +969,7 @@ FFMS_Frame *FFMS_VideoSource::GetFrame(int n) {
971969
continue;
972970

973971
int64_t StartTime = FirstPacket->data == nullptr ? AV_NOPTS_VALUE : (Frames.UseDTS ? FirstPacket->dts : FirstPacket->pts);
974-
975-
if (StartTime == AV_NOPTS_VALUE && !Frames.HasTS) {
976-
if (FirstPacket->data) {
977-
CurrentFrame = Frames.FrameFromPos(FirstPacket->pos);
978-
if (CurrentFrame >= 0)
979-
continue;
980-
}
981-
// If the track doesn't have timestamps or file positions then
982-
// just trust that we got to the right place, since we have no
983-
// way to tell where we are
984-
else {
985-
CurrentFrame = n;
986-
continue;
987-
}
988-
}
989-
990-
CurrentFrame = Frames.FrameFromPTS(StartTime);
972+
CurrentFrame = Frames.FindPacket(*FirstPacket);
991973

992974
// Is the seek destination time known? Does it belong to a frame?
993975
if (CurrentFrame < 0) {

src/core/videosource.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ struct FFMS_VideoSource {
131131
void SetVideoProperties();
132132
bool DecodePacket(const AVPacket &Packet);
133133

134-
// Returns the first packet read that has nonzero PTS/DTS (depending on Frames.UseDTS)
134+
// Returns the first packet read
135135
SmartAVPacket DecodeNextFrame();
136136
bool SeekTo(int n, int SeekOffset);
137137
int Seek(int n);

0 commit comments

Comments
 (0)