Skip to content

Commit ffb9276

Browse files
arch1t3chtdwbuiten
authored andcommitted
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 ea949d0 commit ffb9276

4 files changed

Lines changed: 76 additions & 37 deletions

File tree

src/core/track.cpp

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "indexing.h"
2626

2727
#include <algorithm>
28+
#include <array>
2829
#include <cmath>
2930
#include <cstdlib>
3031

@@ -157,23 +158,79 @@ static bool PTSComparison(FrameInfo FI1, FrameInfo FI2) {
157158
return FI1.PTS < FI2.PTS;
158159
}
159160

160-
int FFMS_Track::FrameFromPTS(int64_t PTS, bool AllowHidden) const {
161+
enum class AVPacketProp {
162+
TS, // can be PTS or DTS depending on UseDTS
163+
Pos,
164+
Hidden,
165+
Key,
166+
};
167+
168+
const std::array<std::vector<AVPacketProp>, 5> FindPacketCheckSequence = {{
169+
{AVPacketProp::TS, AVPacketProp::Pos, AVPacketProp::Hidden, AVPacketProp::Key},
170+
{AVPacketProp::TS, AVPacketProp::Pos, AVPacketProp::Hidden},
171+
{AVPacketProp::TS, AVPacketProp::Pos},
172+
{AVPacketProp::TS},
173+
{AVPacketProp::Pos},
174+
}};
175+
176+
// Attempts to find the given AVPacket in the track's list of FrameInfos,
177+
// returning the FrameInfo's index if one was found and -1 if not.
178+
//
179+
// The finding logic begins by trying to find a *unique* FrameInfo whose
180+
// PTS, Pos, and flags match the given packet.
181+
// If no such packet exists, it tries a fallback sequence of comparing fewer fields.
182+
//
183+
// The current fallback sequence is mostly chosen based on intuition - for
184+
// well-behaved files simply checking all fields should work fine.
185+
// If examples come up where this fallback sequence becomes relevant, it can
186+
// be adjusted.
187+
int FFMS_Track::FindPacket(const AVPacket &packet) const {
161188
FrameInfo F;
162-
F.PTS = PTS;
189+
F.PTS = UseDTS ? packet.dts : packet.pts;
163190

164-
auto Pos = std::lower_bound(begin(), end(), F, PTSComparison);
165-
while (Pos != end() && (!AllowHidden && Pos->Skipped()) && Pos->PTS == PTS)
166-
Pos++;
191+
auto SameTSRange = std::equal_range(begin(), end(), F, PTSComparison);;
167192

168-
if (Pos == end() || Pos->PTS != PTS)
169-
return -1;
170-
return std::distance(begin(), Pos);
171-
}
193+
for (auto const& checks : FindPacketCheckSequence) {
194+
// If the check sequence includes the timestamp, we can begin with a binary search
195+
// since the FrameInfos are sorted by their timestamp, and then do a linear search in the found interval.
196+
// If the check sequence does not include the timestamp, we have to do a full linear search.
197+
auto Begin = begin();
198+
auto End = end();
199+
200+
if (checks[0] == AVPacketProp::TS) {
201+
Begin = SameTSRange.first;
202+
End = SameTSRange.second;
203+
}
204+
205+
int found = 0;
206+
int result = -1;
207+
208+
for (auto it = Begin; it < End; it++) {
209+
bool match = std::all_of(checks.cbegin(), checks.cend(), [&](AVPacketProp check) {
210+
switch (check) {
211+
case AVPacketProp::TS:
212+
return it->PTS == F.PTS;
213+
case AVPacketProp::Pos:
214+
return it->FilePos == packet.pos;
215+
case AVPacketProp::Hidden:
216+
return it->MarkedHidden == (packet.flags & AV_PKT_FLAG_DISCARD);
217+
case AVPacketProp::Key:
218+
return it->KeyFrame == (packet.flags & AV_PKT_FLAG_KEY);
219+
}
220+
return false;
221+
});
222+
223+
if (match) {
224+
found++;
225+
result = std::distance(begin(), it);
226+
}
227+
}
228+
229+
if (found == 1) {
230+
return result;
231+
}
232+
}
172233

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);
177234
return -1;
178235
}
179236

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)
@@ -966,23 +964,7 @@ FFMS_Frame *FFMS_VideoSource::GetFrame(int n) {
966964
continue;
967965

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

987969
// Is the seek destination time known? Does it belong to a frame?
988970
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)