@@ -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,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
0 commit comments