@@ -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-
871850void SESSION::CSession::OnScreenResChange ()
872851{
873852 m_reprChooser->OnUpdateScreenRes ();
@@ -992,25 +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 = [](CStream& stream, double seekSecs, bool preceeding) -> bool
993+ {
994+ if (!stream.m_adStream .seek_time (seekSecs))
995+ {
996+ stream.GetReader ()->Reset (true );
997+ return false ;
998+ }
999+ if (!preceeding)
1000+ stream.GetReader ()->Reset (false );
1001+
1002+ return true ;
1003+ };
1004+
9951005 // correct for starting segment pts value of chapter and chapter offset within program
9961006 uint64_t seekTimeCorrected{static_cast <uint64_t >(seekTime * STREAM_TIME_BASE)};
9971007 int64_t ptsDiff{0 };
1008+
1009+ // If we have a timing stream, ensure it is started first and compute ptsDiff /
1010+ // corrected seek base that will be used by all other streams.
9981011 if (m_timingStream)
9991012 {
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
10031013 ISampleReader* timingReader{m_timingStream->GetReader ()};
10041014 if (!timingReader)
10051015 {
1006- LOG::LogF (LOGERROR, " Cannot get the stream sample reader" );
1016+ LOG::LogF (LOGERROR, " Cannot get the stream sample reader of timing stream " );
10071017 return false ;
10081018 }
1019+
10091020 timingReader->WaitReadSampleAsyncComplete ();
1010- if (!timingReader->IsStarted ())
1011- StartReader (m_timingStream.get (), seekTimeCorrected, ptsDiff, preceeding, true );
10121021
10131022 seekTimeCorrected += m_timingStream->m_adStream .GetAbsolutePTSOffset ();
1023+
1024+ const double seekSecs = static_cast <double >(seekTimeCorrected) / STREAM_TIME_BASE;
1025+
1026+ if (!SeekAdStream (*m_timingStream, seekSecs, preceeding))
1027+ return false ;
1028+
1029+ if (!CheckReaderRunning (*m_timingStream))
1030+ return false ;
1031+
10141032 ptsDiff = timingReader->GetPTSDiff ();
10151033 if (ptsDiff < 0 && seekTimeCorrected + ptsDiff > seekTimeCorrected)
10161034 seekTimeCorrected = 0 ;
@@ -1025,54 +1043,49 @@ bool SESSION::CSession::SeekTime(double seekTime, bool preceeding)
10251043 continue ;
10261044
10271045 streamReader->WaitReadSampleAsyncComplete ();
1028- if (stream->IsEnabled ())
1046+ if (!stream->IsEnabled ())
1047+ continue ;
1048+
1049+ // With FMP4 audio stream included to video stream you must not seek with AdaptiveStream
1050+ // segments are managed by AdaptiveStream of the video stream
1051+ // so CFragmentedSampleReader read data from the reader of the video stream
1052+ const bool hasAdStream = !(streamReader->GetType () == ISampleReader::Type::FMP4 &&
1053+ stream->m_adStream .getRepresentation ()->IsIncludedStream ());
1054+
1055+ if (hasAdStream && m_timingStream != stream) // Do not "seek time" on "timing stream" already done above
10291056 {
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))
1057+ const double seekSecs{static_cast <double >(seekTimeCorrected - ptsDiff) / STREAM_TIME_BASE};
1058+
1059+ if (!SeekAdStream (*stream, seekSecs, preceeding))
1060+ continue ;
1061+ }
1062+
1063+ if (!CheckReaderRunning (*stream))
1064+ continue ;
1065+
1066+ streamReader->SetPTSDiff (ptsDiff);
1067+
1068+ if (!streamReader->TimeSeek (seekTimeCorrected, preceeding))
1069+ {
1070+ streamReader->Reset (true );
1071+ }
1072+ else
1073+ {
1074+ double destTime{static_cast <double >(PTSToElapsed (streamReader->PTS ())) / STREAM_TIME_BASE};
1075+
1076+ // Note: TS sample reader with included streams have a dynamic streamId,
1077+ // so could be that will be printed on log stream id referred to audio or video,
1078+ // maybe this could be improved on sample reader to always start the seek from a video packet
1079+ LOG::Log (LOGINFO, " Seek time %0.1lf for stream: %i continues at %0.1lf (PTS: %llu)" , seekTime,
1080+ streamReader->GetStreamId (), destTime, streamReader->PTS ());
1081+
1082+ if (stream->m_info .GetStreamType () == INPUTSTREAM_TYPE_VIDEO)
10481083 {
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- }
1084+ seekTime = destTime;
1085+ seekTimeCorrected = streamReader->PTS ();
1086+ preceeding = false ;
10731087 }
1074- else
1075- streamReader->Reset (true );
1088+ ret = true ;
10761089 }
10771090 }
10781091
0 commit comments