Skip to content

Commit a44c9ee

Browse files
committed
wip
1 parent f2f97cb commit a44c9ee

File tree

7 files changed

+206
-146
lines changed

7 files changed

+206
-146
lines changed

src/Session.cpp

Lines changed: 96 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,14 @@ bool SESSION::CSession::PrepareStream(CStream& stream, uint64_t startPts)
717717
}
718718
}
719719

720+
//! @todo: when the stream will be opened (OpenStream) while you change chapter/period during video seek,
721+
//! triggered by DEMUX_SPECIALID_STREAMCHANGE (chapter changed)
722+
//! the adaptive stream start to download segments from the start of period, for nothing,
723+
//! because when CInputStreamAdaptive::DemuxRead call CSession::OnDemuxRead
724+
//! will call Session::SeekTime method that cause to delete and cancel current downloads/buffer,
725+
//! and re-start downloads from the appropriate PTS,
726+
//! this wastes time for the video seek and resources due to unnecessary downloads
727+
//! more likely its more easy to see also cancelled downloads on log with TS streams
720728
stream.m_adStream.start_stream(startPts);
721729
stream.SetAdByteStream(std::make_unique<CAdaptiveByteStream>(&stream.m_adStream));
722730

@@ -839,35 +847,6 @@ uint64_t SESSION::CSession::GetTimeshiftBufferStart()
839847
return 0ULL;
840848
}
841849

842-
// TODO: clean this up along with seektime
843-
void SESSION::CSession::StartReader(
844-
CStream* stream, uint64_t seekTime, int64_t ptsDiff, bool preceeding, bool timing)
845-
{
846-
ISampleReader* streamReader = stream->GetReader();
847-
if (!streamReader)
848-
{
849-
LOG::LogF(LOGERROR, "Cannot get the stream reader");
850-
return;
851-
}
852-
853-
bool bReset = true;
854-
if (timing)
855-
seekTime += stream->m_adStream.GetAbsolutePTSOffset();
856-
else
857-
seekTime -= ptsDiff;
858-
859-
stream->m_adStream.seek_time(static_cast<double>(seekTime / STREAM_TIME_BASE), preceeding,
860-
bReset);
861-
862-
if (bReset)
863-
streamReader->Reset(false);
864-
865-
bool bStarted = false;
866-
streamReader->Start(bStarted);
867-
if (bStarted && (streamReader->GetInformation(stream->m_info)))
868-
m_changed = true;
869-
}
870-
871850
void SESSION::CSession::OnScreenResChange()
872851
{
873852
m_reprChooser->OnUpdateScreenRes();
@@ -992,23 +971,64 @@ bool SESSION::CSession::SeekTime(double seekTime, bool preceeding)
992971
seekTime = maxSeek;
993972
}
994973

974+
// Helper lambda to check if reader is started,
975+
// since after seeking across chapters/periods the reader is not started yet
976+
auto CheckReaderRunning = [this](CStream& stream) -> bool
977+
{
978+
auto reader = stream.GetReader();
979+
if (!reader->IsStarted())
980+
{
981+
bool isStarted = false;
982+
if (AP4_FAILED(reader->Start(isStarted)))
983+
return false;
984+
985+
if (isStarted && reader->GetInformation(stream.m_info))
986+
m_changed = true;
987+
}
988+
return true;
989+
};
990+
991+
// Helper lambda to perform seek on adaptive stream segment buffer
992+
auto SeekAdStream = [&preceeding](CStream& stream, double seekSecs) -> bool
993+
{
994+
bool reset = false;
995+
if (!stream.m_adStream.seek_time(seekSecs, preceeding, reset))
996+
{
997+
stream.GetReader()->Reset(true);
998+
return false;
999+
}
1000+
if (reset)
1001+
stream.GetReader()->Reset(false);
1002+
1003+
return true;
1004+
};
1005+
9951006
// correct for starting segment pts value of chapter and chapter offset within program
9961007
uint64_t seekTimeCorrected{static_cast<uint64_t>(seekTime * STREAM_TIME_BASE)};
9971008
int64_t ptsDiff{0};
1009+
1010+
// If we have a timing stream, ensure it is started first and compute ptsDiff /
1011+
// corrected seek base that will be used by all other streams.
9981012
if (m_timingStream)
9991013
{
1000-
// after seeking across chapters with fmp4 streams the reader will not have started
1001-
// so we start here to ensure that we have the required information to correctly
1002-
// seek with proper stream alignment
10031014
ISampleReader* timingReader{m_timingStream->GetReader()};
10041015
if (!timingReader)
10051016
{
1006-
LOG::LogF(LOGERROR, "Cannot get the stream sample reader");
1017+
LOG::LogF(LOGERROR, "Cannot get the stream sample reader of timing stream");
10071018
return false;
10081019
}
1020+
10091021
timingReader->WaitReadSampleAsyncComplete();
1010-
if (!timingReader->IsStarted())
1011-
StartReader(m_timingStream.get(), seekTimeCorrected, ptsDiff, preceeding, true);
1022+
1023+
double seekSecs =
1024+
static_cast<double>(seekTimeCorrected + m_timingStream->m_adStream.GetAbsolutePTSOffset()) /
1025+
STREAM_TIME_BASE;
1026+
1027+
if (!SeekAdStream(*m_timingStream, seekSecs))
1028+
return false;
1029+
1030+
if (!CheckReaderRunning(*m_timingStream))
1031+
return false;
10121032

10131033
seekTimeCorrected += m_timingStream->m_adStream.GetAbsolutePTSOffset();
10141034
ptsDiff = timingReader->GetPTSDiff();
@@ -1025,54 +1045,50 @@ bool SESSION::CSession::SeekTime(double seekTime, bool preceeding)
10251045
continue;
10261046

10271047
streamReader->WaitReadSampleAsyncComplete();
1028-
if (stream->IsEnabled())
1048+
if (!stream->IsEnabled())
1049+
continue;
1050+
1051+
double seekSecs{static_cast<double>(seekTimeCorrected - ptsDiff) / STREAM_TIME_BASE};
1052+
bool reset{false};
1053+
1054+
// With FMP4 audio stream included to video stream you must not seek with AdaptiveStream
1055+
// segments are managed by AdaptiveStream of the video stream
1056+
// so CFragmentedSampleReader read data from the reader of the video stream
1057+
const bool hasAdStream = !(streamReader->GetType() == ISampleReader::Type::FMP4 &&
1058+
stream->m_adStream.getRepresentation()->IsIncludedStream());
1059+
1060+
if (hasAdStream && m_timingStream != stream) // Do not "seek time" on "timing stream" already done above
1061+
{
1062+
if (!SeekAdStream(*stream, seekSecs))
1063+
continue;
1064+
}
1065+
1066+
if (!CheckReaderRunning(*stream))
1067+
continue;
1068+
1069+
streamReader->SetPTSDiff(ptsDiff);
1070+
1071+
if (!streamReader->TimeSeek(seekTimeCorrected, preceeding))
10291072
{
1030-
bool reset{false};
1031-
// all streams must be started before seeking to ensure cross chapter seeks
1032-
// will seek to the correct location/segment
1033-
if (!streamReader->IsStarted())
1034-
StartReader(stream.get(), seekTimeCorrected, ptsDiff, preceeding, false);
1035-
1036-
streamReader->SetPTSDiff(ptsDiff);
1037-
1038-
double seekSecs{static_cast<double>(seekTimeCorrected - ptsDiff) /
1039-
STREAM_TIME_BASE};
1040-
1041-
// With FMP4 audio stream included to video stream you must not seek with AdaptiveStream
1042-
// segments are managed by AdaptiveStream of the video stream
1043-
// so CFragmentedSampleReader read data from the reader of the video stream
1044-
const bool noAdStream = streamReader->GetType() == ISampleReader::Type::FMP4 &&
1045-
stream->m_adStream.getRepresentation()->IsIncludedStream();
1046-
1047-
if (noAdStream || stream->m_adStream.seek_time(seekSecs, preceeding, reset))
1073+
streamReader->Reset(true);
1074+
}
1075+
else
1076+
{
1077+
double destTime{static_cast<double>(PTSToElapsed(streamReader->PTS())) / STREAM_TIME_BASE};
1078+
1079+
// Note: TS sample reader with included streams have a dynamic streamId,
1080+
// so could be that will be printed on log stream id referred to audio or video,
1081+
// maybe this could be improved on sample reader to always start the seek from a video packet
1082+
LOG::Log(LOGINFO, "Seek time %0.1lf for stream: %i continues at %0.1lf (PTS: %llu)", seekTime,
1083+
streamReader->GetStreamId(), destTime, streamReader->PTS());
1084+
1085+
if (stream->m_info.GetStreamType() == INPUTSTREAM_TYPE_VIDEO)
10481086
{
1049-
if (reset)
1050-
streamReader->Reset(false);
1051-
// advance reader to requested time
1052-
if (!streamReader->TimeSeek(seekTimeCorrected, preceeding))
1053-
{
1054-
streamReader->Reset(true);
1055-
}
1056-
else
1057-
{
1058-
double destTime{static_cast<double>(PTSToElapsed(streamReader->PTS())) /
1059-
STREAM_TIME_BASE};
1060-
LOG::Log(LOGINFO,
1061-
"Seek time %0.1lf for stream: %i (physical index %u) continues at %0.1lf "
1062-
"(PTS: %llu)",
1063-
seekTime, streamReader->GetStreamId(), stream->m_info.GetPhysicalIndex(),
1064-
destTime, streamReader->PTS());
1065-
if (stream->m_info.GetStreamType() == INPUTSTREAM_TYPE_VIDEO)
1066-
{
1067-
seekTime = destTime;
1068-
seekTimeCorrected = streamReader->PTS();
1069-
preceeding = false;
1070-
}
1071-
ret = true;
1072-
}
1087+
seekTime = destTime;
1088+
seekTimeCorrected = streamReader->PTS();
1089+
preceeding = false;
10731090
}
1074-
else
1075-
streamReader->Reset(true);
1091+
ret = true;
10761092
}
10771093
}
10781094

src/Session.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,6 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver
131131
*/
132132
uint64_t GetTimeshiftBufferStart();
133133

134-
/*! \brief Align adaptiveStream and start stream reader
135-
* \param stream The stream to start
136-
* \param seekTime The pts value to start the stream at
137-
* \param ptsDiff The pts difference to adjust seekTime by
138-
* \param preceeding True to ask reader to seek to preceeding sync point
139-
* \param timing True if this is the initial starting stream
140-
*/
141-
void StartReader(
142-
CStream* stream, uint64_t seekTime, int64_t ptsDiff, bool preceeding, bool timing);
143-
144134
/*! \brief Check if the stream has changed, reset changed status
145135
* \param bSet True to keep m_changed value true
146136
* \return True if stream has changed

src/common/AdaptiveStream.cpp

Lines changed: 72 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -271,17 +271,57 @@ void adaptive::AdaptiveStream::ResetSegment(const PLAYLIST::CSegment& segment)
271271
}
272272
}
273273

274-
void adaptive::AdaptiveStream::ResetActiveBuffer()
274+
bool adaptive::AdaptiveStream::AlignActiveBufferToSegment(const PLAYLIST::CSegment& segment)
275275
{
276-
thread_data_->StopDownloads();
276+
if (m_segBuffers.ContainsSegment(segment))
277277
{
278-
std::lock_guard<std::mutex> lckWorker(thread_data_->mutexWorker);
279-
absolute_position_ = 0;
280-
segment_read_pos_ = 0;
278+
thread_data_->PauseDownloads();
279+
// Check if there is a download in progress, if the segment being downloaded
280+
// is older than the one requested, immediately stop the download
281+
if (thread_data_->mutexWorker.try_lock())
282+
{
283+
thread_data_->mutexWorker.unlock();
284+
}
285+
else // You cant take the lock, there is a download in progress
286+
{
287+
// Wait to block read/write operations
288+
std::unique_lock<std::mutex> lckrw(thread_data_->mutexRW);
289+
thread_data_->cvRW.wait(lckrw);
290+
// Note: doesnt matter if the download is finished right now and GetNextDownload return next buffer
291+
SegmentBuffer* currDownload = m_segBuffers.GetNextDownload();
292+
if (currDownload && currDownload->State() == BufferState::DOWNLOADING)
293+
{
294+
const uint64_t downloadStartPTS = currDownload->segment.startPTS_;
295+
const uint64_t targetStartPTS = segment.startPTS_;
281296

282-
m_segBuffers.Reset();
297+
if (downloadStartPTS < targetStartPTS)
298+
thread_data_->StopDownloads();
299+
}
300+
}
301+
{
302+
std::lock_guard<std::mutex> lckWorker(thread_data_->mutexWorker);
303+
while (!m_segBuffers.IsEmpty())
304+
{
305+
if (!segment.IsSame(m_segBuffers.Front().segment))
306+
m_segBuffers.PopFront();
307+
else
308+
break;
309+
}
310+
}
311+
}
312+
else
313+
{
314+
thread_data_->StopDownloads();
315+
{
316+
std::lock_guard<std::mutex> lckWorker(thread_data_->mutexWorker);
317+
m_segBuffers.Reset();
318+
}
283319
}
320+
321+
absolute_position_ = 0;
322+
segment_read_pos_ = 0;
284323
thread_data_->StartDownloads();
324+
return !m_segBuffers.IsEmpty();
285325
}
286326

287327
void adaptive::AdaptiveStream::worker()
@@ -1130,17 +1170,21 @@ void adaptive::AdaptiveStream::Disable()
11301170
m_startEvent = EVENT_TYPE::STREAM_ENABLE;
11311171
}
11321172

1133-
void adaptive::AdaptiveStream::ResetCurrentSegment(const PLAYLIST::CSegment& newSegment)
1173+
void adaptive::AdaptiveStream::UpdateCurrentSegment(const PLAYLIST::CSegment& newSegment)
11341174
{
1135-
// EnsureSegment always loads the segment following the one specified as current, then sets the previous one
1136-
const CSegment* prevSeg = current_rep_->Timeline().GetPrevious(newSegment);
1137-
if (prevSeg)
1138-
current_rep_->current_segment_ = *prevSeg;
1175+
if (AlignActiveBufferToSegment(newSegment))
1176+
{
1177+
current_rep_->current_segment_ = m_segBuffers.Front().segment;
1178+
}
11391179
else
1140-
current_rep_->current_segment_.reset();
1141-
1142-
// TODO: if new segment is already prefetched, don't ResetActiveBuffer;
1143-
ResetActiveBuffer();
1180+
{
1181+
// EnsureSegment always loads the segment following the one specified as current, then sets the previous one
1182+
const CSegment* prevSeg = current_rep_->Timeline().GetPrevious(newSegment);
1183+
if (prevSeg)
1184+
current_rep_->current_segment_ = *prevSeg;
1185+
else
1186+
current_rep_->current_segment_.reset();
1187+
}
11441188
}
11451189

11461190
int adaptive::AdaptiveStream::GetTrackType() const
@@ -1190,44 +1234,17 @@ bool adaptive::AdaptiveStream::seek_time(double seek_seconds, bool preceeding, b
11901234
std::lock_guard<adaptive::AdaptiveTree::TreeUpdateThread> lckUpdTree(m_tree->GetTreeUpdMutex());
11911235

11921236
const uint64_t pts = static_cast<uint64_t>(seek_seconds * current_rep_->GetTimescale());
1193-
11941237
const CSegment* seekSeg = current_rep_->Timeline().FindByPTSOrNext(pts);
1195-
const std::optional<CSegment> oldSeg = current_rep_->current_segment_;
11961238

1197-
if (seekSeg)
1198-
{
1199-
if (oldSeg.has_value() && !seekSeg->IsSame(*oldSeg))
1200-
{
1201-
ResetCurrentSegment(*seekSeg);
1202-
}
1203-
else if (!preceeding)
1204-
{
1205-
// restart stream if it has 'finished', e.g in the case of subtitles
1206-
// where there may be a few or only one segment for the period and
1207-
// the stream is now in EOS state (all data already passed to Kodi)
1208-
1209-
//! @TODO: Twice downloaded segments, cause of video seek delay
1210-
//! Steps to reproduce:
1211-
//! Play any kind of video, do a video seek
1212-
//! on the log after "PosTime" callback print you can see twice downloaded segments
1213-
//! a better way to debug is add a new log into AdaptiveStream::ensureSegment to the code related to Push new segments
1214-
//! into segment buffers var and so print the segment numbers of the segments added to buffers.
1215-
//! the problem is also related to CSession::StartReader called by CSession::SeekTime,
1216-
//! In short the adaptive stream start to download segments seem just to know the "PTS diff"
1217-
//! and these downloads will be deleted just later without to be read, because CSession::SeekTime call these
1218-
//! method two times so by starting two times downloads.
1219-
//! This code has been introduced as part of subtitles fixes from https://github.com/xbmc/inputstream.adaptive/pull/1082
1220-
ResetCurrentSegment(*seekSeg);
1221-
1222-
absolute_position_ -= segment_read_pos_;
1223-
segment_read_pos_ = 0;
1224-
}
1225-
else
1226-
needReset = false;
1227-
return true;
1228-
}
1239+
if (!seekSeg)
1240+
return false;
12291241

1230-
return false;
1242+
UpdateCurrentSegment(*seekSeg);
1243+
1244+
if (preceeding)
1245+
needReset = false;
1246+
1247+
return true;
12311248
}
12321249

12331250
bool adaptive::AdaptiveStream::waitingForSegment() const
@@ -1371,6 +1388,11 @@ void adaptive::AdaptiveStream::THREADDATA::StopDownloads()
13711388
m_state = ThState::STOPPED;
13721389
}
13731390

1391+
void adaptive::AdaptiveStream::THREADDATA::PauseDownloads()
1392+
{
1393+
m_state = ThState::PAUSED;
1394+
}
1395+
13741396
void adaptive::AdaptiveStream::THREADDATA::StartDownloads()
13751397
{
13761398
m_state = ThState::RUNNING;

0 commit comments

Comments
 (0)