Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,13 @@ bool SESSION::CSession::SeekTime(double seekTime, bool preceeding)
seekTimeCorrected += ptsDiff;
}

// Note: At the end of the seek operations, you may notice on Kodi debug log "dropping packets" prints
// this happens because we cannot always guarantee a precise seek, for example
// with FragmentedSampleReader (MP4 samples/packets)
// we need to start feeding the Kodi demuxer with a synchronization sample/packet
// but the sync sample (packet) at least for the video track is usually not available for all PTS
// so we try choose the sample/package closest to the requested "seek" PTS, causing "dropping packets"

for (auto& stream : m_streams)
{
ISampleReader* streamReader{stream->GetReader()};
Expand Down
93 changes: 74 additions & 19 deletions src/samplereader/FragmentedSampleReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

#include <bento4/Ap4SencAtom.h>

#include <limits>

using namespace UTILS;

namespace
Expand Down Expand Up @@ -308,7 +310,7 @@ bool CFragmentedSampleReader::TimeSeek(uint64_t pts, bool preceeding)
AP4_Ordinal sampleIndex;
AP4_UI64 seekPos(static_cast<AP4_UI64>((pts * m_timeBaseInt) / m_timeBaseExt));

AP4_Result result = m_lReader->SeekSample(m_track->GetId(), seekPos, sampleIndex, preceeding);
AP4_Result result = m_lReader->SeekSample(m_track->GetId(), seekPos, sampleIndex);
if (AP4_FAILED(result))
{
LOG::LogF(LOGERROR, "Cannot seek track id %u, error %i", m_track->GetId(), result);
Expand Down Expand Up @@ -813,10 +815,7 @@ AP4_Movie& CLinearReader::GetMovie()
return AP4_LinearReader::m_Movie;
}

AP4_Result CLinearReader::SeekSample(AP4_UI32 track_id,
AP4_UI64 ts,
AP4_Ordinal& sample_index,
bool preceedingSync)
AP4_Result CLinearReader::SeekSample(AP4_UI32 track_id, AP4_UI64 ts, AP4_Ordinal& sample_index)
{
// we only support fragmented sources for now
if (!m_HasFragments)
Expand All @@ -838,24 +837,23 @@ AP4_Result CLinearReader::SeekSample(AP4_UI32 track_id,

AP4_Result result;

if (!tracker->m_SampleTable && AP4_FAILED(result = Advance()))
return result;

while (AP4_FAILED(result = tracker->m_SampleTable->GetSampleIndexForTimeStamp(ts, sample_index)))
if (!tracker->m_SampleTable)
{
if (result == AP4_ERROR_NOT_ENOUGH_DATA)
if (AP4_FAILED(result = Advance()))
return result;
// Ensure that Advance has populated the sample table
if (!tracker->m_SampleTable)
{
tracker->m_NextSampleIndex = tracker->m_SampleTable->GetSampleCount();
if (AP4_FAILED(result = Advance()))
return result;
continue;
LOG::LogF(LOGERROR, "No sample table");
return AP4_ERROR_INVALID_STATE;
}
return result;
}

sample_index = tracker->m_SampleTable->GetNearestSyncSampleIndex(sample_index, preceedingSync);
//we have reached the end -> go for the first sample of the next segment
if (sample_index == tracker->m_SampleTable->GetSampleCount())
//! @todo: remove our custom GetNearestSyncSampleIndex from Bento4
AP4_Cardinal samplesCount = tracker->m_SampleTable->GetSampleCount();
Comment thread
CastagnaIT marked this conversation as resolved.

// No samples, attempt advance to (download/read) next segment
if (samplesCount == 0)
{
tracker->m_NextSampleIndex = tracker->m_SampleTable->GetSampleCount();

Expand All @@ -869,13 +867,70 @@ AP4_Result CLinearReader::SeekSample(AP4_UI32 track_id,
tracker->m_SampleTableIsOwned = isOwned;
return result;
}
// Ensure that Advance has populated the sample table
if (!tracker->m_SampleTable)
{
LOG::LogF(LOGERROR, "No sample table");
return AP4_ERROR_INVALID_STATE;
}

tracker->m_SampleTableIsOwned = isOwned;
sample_index = 0;
samplesCount = tracker->m_SampleTable->GetSampleCount();

if (samplesCount == 0)
{
LOG::LogF(LOGERROR, "No samples found in the segment packet");
return AP4_ERROR_INVALID_STATE;
}
}

constexpr AP4_Cardinal indexNoValue{std::numeric_limits<AP4_Cardinal>::max()};
AP4_Cardinal syncIndex{indexNoValue};
AP4_UI64 syncCts{0};
AP4_Cardinal sampleIndex{indexNoValue};

// Try find a sample "sync", when possible, with equal or higher ts than the target ts
// or, fallback to the sample (without sync) with nearest ts
// NOTE: search exclusively within this segment,
// avoiding requesting (download/read) other segments to speedup the seek operation
for (AP4_Cardinal index{0}; index < samplesCount; ++index)
{
AP4_Sample sample;
if (AP4_FAILED(tracker->m_SampleTable->GetSample(index, sample)))
continue;

const AP4_UI64 cts = sample.GetCts();
if (sample.IsSync())
{
if (syncIndex == indexNoValue || syncCts < ts && cts > syncCts)
{
syncCts = cts;
syncIndex = index;
}
}
// Find any sample (no sync) closer to the target ts
if (cts <= ts)
sampleIndex = index;
}

if (syncIndex != indexNoValue)
sample_index = syncIndex;
else if (sampleIndex != indexNoValue)
{
LOG::LogF(LOGDEBUG, "No sample sync found, fallback to sample with nearest timestamp");
sample_index = sampleIndex;
}
else
{
LOG::LogF(LOGERROR, "Cannot determine the sample index");
return AP4_ERROR_INVALID_STATE;
}

if (!tracker->m_SampleTable) // Required for SetSampleIndex
{
LOG::LogF(LOGERROR, "Missing sample table, cannot set the sample index");
return AP4_ERROR_INVALID_STATE;
}

return SetSampleIndex(tracker->m_Track->GetId(), sample_index);
}
Expand Down
5 changes: 1 addition & 4 deletions src/samplereader/FragmentedSampleReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ class ATTR_DLL_LOCAL CLinearReader : public AP4_LinearReader
// \brief Get AP4_LinearReader::m_Movie
AP4_Movie& GetMovie();

AP4_Result SeekSample(AP4_UI32 track_id,
AP4_UI64 ts,
AP4_Ordinal& sample_index,
bool preceedingSync);
AP4_Result SeekSample(AP4_UI32 track_id, AP4_UI64 ts, AP4_Ordinal& sample_index);

protected:
// AP4_LinearReader implementations
Expand Down