Skip to content

Commit 5af793c

Browse files
authored
Merge pull request #1991 from CastagnaIT/fix_seek_end
Seek fixes and improvements
2 parents 888c78a + 49f03f8 commit 5af793c

File tree

11 files changed

+116
-70
lines changed

11 files changed

+116
-70
lines changed

src/Session.cpp

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -911,10 +911,8 @@ bool SESSION::CSession::GetNextSample(ISampleReader*& sampleReader)
911911
return false;
912912
}
913913

914-
bool SESSION::CSession::SeekTime(double seekTime)
914+
bool SESSION::CSession::SeekTime(double seekTime, bool& isError)
915915
{
916-
bool ret{false};
917-
918916
if (m_streams.empty())
919917
return false;
920918

@@ -949,26 +947,14 @@ bool SESSION::CSession::SeekTime(double seekTime)
949947

950948
seekTime -= chapterTime;
951949

952-
// don't try to seek past the end of the stream, leave a sensible amount so we can buffer properly
953950
if (m_adaptiveTree->IsLive())
954951
{
955-
double maxSeek{0};
956-
uint64_t curTime;
957-
uint64_t maxTime{0};
958-
for (auto& stream : m_streams)
959-
{
960-
if (stream->IsEnabled() && (curTime = stream->m_adStream.getMaxTimeMs()) && curTime > maxTime)
961-
{
962-
maxTime = curTime;
963-
}
964-
}
965-
966-
maxSeek = (static_cast<double>(maxTime) / 1000) - m_adaptiveTree->m_liveDelay;
967-
if (maxSeek < 0)
968-
maxSeek = 0;
952+
double delayPtsSecs =
953+
(static_cast<double>(GetMediaDurationMs()) / 1000) - m_adaptiveTree->m_liveDelay;
969954

970-
if (seekTime > maxSeek)
971-
seekTime = maxSeek;
955+
// Check to avoid seek into live delay duration portion, to allow an appropriate live buffering
956+
if (seekTime > delayPtsSecs)
957+
seekTime = delayPtsSecs;
972958
}
973959

974960
// Helper lambda to check if reader is started,
@@ -1023,12 +1009,18 @@ bool SESSION::CSession::SeekTime(double seekTime)
10231009
const double seekSecs = static_cast<double>(seekTimeCorrected) / STREAM_TIME_BASE;
10241010

10251011
if (!SeekAdStream(*m_timingStream, seekSecs))
1012+
{
1013+
isError = true;
10261014
return false;
1015+
}
10271016

1017+
// Note: The following "running" check will download/read the package right now
1018+
// allowing you to read the PTS diff from stream reader
10281019
if (!CheckReaderRunning(*m_timingStream))
10291020
return false;
10301021

10311022
ptsDiff = timingReader->GetPTSDiff();
1023+
10321024
if (ptsDiff < 0 && seekTimeCorrected + ptsDiff > seekTimeCorrected)
10331025
seekTimeCorrected = 0;
10341026
else
@@ -1063,17 +1055,29 @@ bool SESSION::CSession::SeekTime(double seekTime)
10631055
const double seekSecs{static_cast<double>(seekTimeCorrected - ptsDiff) / STREAM_TIME_BASE};
10641056

10651057
if (!SeekAdStream(*stream, seekSecs))
1066-
continue;
1058+
{
1059+
if (stream->m_info.GetStreamType() == INPUTSTREAM_TYPE_SUBTITLE)
1060+
continue; // Subtitles failure should not block the seek operations
1061+
1062+
isError = true;
1063+
return false;
1064+
}
10671065
}
10681066

10691067
if (!CheckReaderRunning(*stream))
1070-
continue;
1068+
return false;
10711069

10721070
streamReader->SetPTSDiff(ptsDiff);
10731071

10741072
if (!streamReader->TimeSeek(seekTimeCorrected))
10751073
{
10761074
streamReader->Reset(true);
1075+
1076+
if (stream->m_info.GetStreamType() == INPUTSTREAM_TYPE_SUBTITLE)
1077+
continue; // Subtitles failure should not block the seek operations
1078+
1079+
isError = true;
1080+
return false;
10771081
}
10781082
else
10791083
{
@@ -1085,16 +1089,20 @@ bool SESSION::CSession::SeekTime(double seekTime)
10851089
LOG::Log(LOGINFO, "Seek time %0.1lf for stream: %i continues at %0.1lf (PTS: %llu)", seekTime,
10861090
streamReader->GetStreamId(), destTime, streamReader->PTS());
10871091

1092+
// We replace the seek time PTS initially requested with the PTS of the video sample found
1093+
// in order to search the audio/subtitle sample packet more accurately.
1094+
// f.e. in the case of MP4 the video packet has very few sample sync points
1095+
// while the audio stream usually has many sample sync points, this cause a misalignment
1096+
// because for the video you will never find an exact sample for the requested PTS.
1097+
// Then get the nearest PTS found for the video, then align the audio/subtitles with it.
10881098
if (stream->m_info.GetStreamType() == INPUTSTREAM_TYPE_VIDEO)
10891099
{
10901100
seekTime = destTime;
10911101
seekTimeCorrected = streamReader->PTS();
10921102
}
1093-
ret = true;
10941103
}
10951104
}
1096-
1097-
return ret;
1105+
return true;
10981106
}
10991107

11001108
void SESSION::CSession::OnDemuxRead()
@@ -1105,7 +1113,10 @@ void SESSION::CSession::OnDemuxRead()
11051113

11061114
if (GetChapterSeekTime() > 0)
11071115
{
1108-
SeekTime(GetChapterSeekTime());
1116+
bool isError{false};
1117+
if (!SeekTime(GetChapterSeekTime(), isError))
1118+
DeleteStreams();
1119+
11091120
ResetChapterSeekTime();
11101121
}
11111122
}
@@ -1349,3 +1360,11 @@ PLAYLIST::CAdaptationSet* SESSION::CSession::DetermineDefaultAdpSet(PLAYLIST::CP
13491360

13501361
return defaultAdp;
13511362
}
1363+
1364+
uint64_t SESSION::CSession::GetMediaDurationMs()
1365+
{
1366+
if (!m_timingStream || !m_timingStream->IsEnabled())
1367+
return 0;
1368+
1369+
return m_timingStream->m_adStream.getMaxTimeMs();
1370+
}

src/Session.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,14 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver
147147
*/
148148
void OnScreenResChange();
149149

150-
/*! \brief Seek streams and readers to a specified time
151-
* \param seekTime The seek time in seconds
152-
* \return True if seeking to another chapter or 1+ streams successfully
153-
* seeked, false on error or no streams seeked
150+
/*!
151+
* \brief Seek streams and readers to a specified time.
152+
* \param seekTime The seek time in seconds.
153+
* \param isError Cannot seek due to unrecoverable error,
154+
* in this case the method always return false.
155+
* \return True if the operation is successful (or seek through chapter/s), otherwise false
154156
*/
155-
bool SeekTime(double seekTime);
157+
bool SeekTime(double seekTime, bool& isError);
156158

157159
/*! \brief Report if the current content is dynamic/live
158160
* \return True if live, false if VOD
@@ -262,6 +264,11 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver
262264
*/
263265
PLAYLIST::CAdaptationSet* DetermineDefaultAdpSet(PLAYLIST::CPeriod* period);
264266

267+
/*!
268+
* \brief Get the media duration in ms, based on segments.
269+
*/
270+
uint64_t GetMediaDurationMs();
271+
265272
private:
266273
DRM::CDRMEngine m_drmEngine;
267274

src/common/AdaptiveStream.cpp

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,25 +1135,21 @@ bool adaptive::AdaptiveStream::seek(uint64_t const pos, bool& isEos)
11351135

11361136
uint64_t adaptive::AdaptiveStream::getMaxTimeMs()
11371137
{
1138-
if (current_rep_->Timeline().IsEmpty())
1138+
const CSegment* lastSeg = current_rep_->Timeline().GetBack();
1139+
if (!lastSeg)
11391140
return 0;
11401141

1141-
uint64_t duration{0};
1142-
if (current_rep_->Timeline().GetSize() > 1)
1142+
const uint64_t maxPts =
1143+
(lastSeg->m_endPts * current_rep_->timescale_ext_) / current_rep_->timescale_int_;
1144+
1145+
if (absolutePTSOffset_ > maxPts)
11431146
{
1144-
duration =
1145-
current_rep_->Timeline().Get(current_rep_->Timeline().GetSize() - 1)->startPTS_ -
1146-
current_rep_->Timeline().Get(current_rep_->Timeline().GetSize() - 2)->startPTS_;
1147+
LOG::LogF(LOGERROR, "Absolute PTS offset (%llu) exceeds max PTS (%llu)", absolutePTSOffset_,
1148+
maxPts);
1149+
return 0;
11471150
}
11481151

1149-
uint64_t timeExt = ((current_rep_->Timeline()
1150-
.Get(current_rep_->Timeline().GetSize() - 1)
1151-
->startPTS_ +
1152-
duration) *
1153-
current_rep_->timescale_ext_) /
1154-
current_rep_->timescale_int_;
1155-
1156-
return (timeExt - absolutePTSOffset_) / 1000;
1152+
return (maxPts - absolutePTSOffset_) / 1000;
11571153
}
11581154

11591155
void adaptive::AdaptiveStream::Disable()

src/main.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -301,13 +301,6 @@ DEMUX_PACKET* CInputStreamAdaptive::DemuxRead(void)
301301
// since it can cause to reopen all streams, reset the check just before
302302
m_checkCoreReopen = false;
303303

304-
if (~m_failedSeekTime)
305-
{
306-
LOG::Log(LOGDEBUG, "Seeking to last failed seek position (%d)", m_failedSeekTime);
307-
m_session->SeekTime(static_cast<double>(m_failedSeekTime) * 0.001f);
308-
m_failedSeekTime = ~0;
309-
}
310-
311304
ISampleReader* sr{nullptr};
312305

313306
if (m_session->GetNextSample(sr))
@@ -425,15 +418,36 @@ void CInputStreamAdaptive::SetVideoResolution(unsigned int width,
425418

426419
bool CInputStreamAdaptive::PosTime(int ms)
427420
{
421+
// Note: VP may require a seek (ms) even beyond the total duration of the media.
422+
// Note: When you return false VP will continue to request packets with DemuxRead callbacks
423+
// VP should expect to continue playback from the last PTS fed (as if the seek had not occurred)
424+
// or you can stop playback by forcibly stop the packet feed from DemuxRead callbacks.
428425
if (!m_session)
429426
return false;
430427

431428
LOG::Log(LOGINFO, "PosTime (%d)", ms);
432429

433-
bool ret = m_session->SeekTime(static_cast<double>(ms) * 0.001f);
434-
m_failedSeekTime = ret ? ~0 : ms;
430+
const uint64_t currentTimeMs = m_session->GetElapsedTimeMs();
431+
bool isError{false};
432+
433+
if (m_session->SeekTime(static_cast<double>(ms) * 0.001f, isError))
434+
return true;
435+
436+
if (!isError)
437+
{
438+
// If for some reason the seek operation fails without errors
439+
// try to restore the streams/readers to previous (current) position
440+
LOG::Log(LOGWARNING, "PosTime - Seek failed. Attempt to restore previous %llu ms position",
441+
currentTimeMs);
435442

436-
return ret;
443+
if (m_session->SeekTime(static_cast<double>(currentTimeMs) * 0.001f, isError))
444+
return false; // returns false because the initially requested seek was unsuccessful
445+
}
446+
447+
// A problem has occurred or EOS, force stop playback
448+
LOG::Log(LOGDEBUG, "PosTime - Cannot seek at %d ms position.", ms);
449+
m_session->DeleteStreams();
450+
return false;
437451
}
438452

439453
int CInputStreamAdaptive::GetTotalTime()

src/main.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ class ATTR_DLL_LOCAL CInputStreamAdaptive : public kodi::addon::CInstanceInputSt
5757

5858
private:
5959
std::shared_ptr<SESSION::CSession> m_session;
60-
int m_failedSeekTime = ~0;
6160

6261
// The last PTS of the segment package fed to kodi.
6362
// NO_PTS_VALUE only when playback starts or a new period starts

src/parser/DASHTree.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,8 +1825,17 @@ void adaptive::CDashTree::GenerateTemplatedSegments(PLAYLIST::CSegmentTemplate&
18251825

18261826
const uint64_t timeEnd = wndEnd * segTimescale / 1000;
18271827

1828-
// Create segments within time window
1829-
while (time + segDuration <= timeEnd && segNumber <= segNumberEnd)
1828+
// Create segments within time window,
1829+
// a segment that begins within the period and partially
1830+
// extends beyond the period boundaries must still be generated
1831+
//! @todo: currently when a segment extends beyond the period boundaries
1832+
//! it will exist also on next period, this will cause duplicate playback of the content (you can see also twice downloads)
1833+
//! in theory this should be fixed in the sample reader, where the sample packets of a segment
1834+
//! (at the beginning or end) must be filtered based on the duration of the period.
1835+
//! In other words, finish playing a segment early by skipping the samples that fall outside the period,
1836+
//! while in the next period, start playing the segment from a sample with a specific PTS,
1837+
//! thus skipping part of the initial samples.
1838+
while (time < timeEnd && segNumber <= segNumberEnd)
18301839
{
18311840
CSegment seg;
18321841
seg.startPTS_ = time;

src/samplereader/ADTSSampleReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,5 @@ bool CADTSSampleReader::TimeSeek(uint64_t pts)
6363
m_started = true;
6464
return AP4_SUCCEEDED(ReadSample());
6565
}
66-
return AP4_ERROR_EOS;
66+
return false; // EOS
6767
}

src/samplereader/FragmentedSampleReader.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,9 @@ bool CFragmentedSampleReader::TimeSeek(uint64_t pts)
313313
AP4_Result result = m_lReader->SeekSample(m_track->GetId(), seekPos, sampleIndex);
314314
if (AP4_FAILED(result))
315315
{
316-
LOG::LogF(LOGERROR, "Cannot seek track id %u, error %i", m_track->GetId(), result);
316+
if (result != AP4_ERROR_EOS)
317+
LOG::LogF(LOGERROR, "Cannot seek track id %u, error %i", m_track->GetId(), result);
318+
317319
return false;
318320
}
319321

src/samplereader/TSSampleReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,5 @@ bool CTSSampleReader::TimeSeek(uint64_t pts)
113113
m_started = true;
114114
return AP4_SUCCEEDED(ReadSample());
115115
}
116-
return AP4_ERROR_EOS;
116+
return false; // EOS
117117
}

src/samplereader/WebmSampleReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,5 @@ bool CWebmSampleReader::TimeSeek(uint64_t pts)
6767
m_started = true;
6868
return AP4_SUCCEEDED(ReadSample());
6969
}
70-
return AP4_ERROR_EOS;
70+
return false; // EOS
7171
}

0 commit comments

Comments
 (0)