Conversation
On Live streams, we have currently one chapter, the start of the stream. Being able to jump forward to the 'edge' e.g. 'now' of the stream is useful. Lets add a virtual channel to be able to jump to the edge. That should leave us with two channels, ch0, the start of the stream, ch1, the 'now'/end of the live stream. Having this, allows Kodi to use |< and >| buttons to jump to the start, and back to the 'now' of a live stream. Multi-period and non-live streams are unaffected. Co-authored-by: Claude Opus 4.6 (high) <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
When a live stream contains known random content before the start of the program, we may want to skip that via an offset. Default is 0, preserving existing behaviour when the property is not set. This setting was inspired by Live Delay. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Kodiai Review SummaryWhat ChangedThis PR adds live stream jump functionality with virtual chapters, allowing users to jump to the start or live edge of streams using chapter navigation (|< and >|). Reviewed: core logic Strengths
ObservationsImpact[MAJOR] src/Session.cpp (1563): GetChapterPos called with inconsistent chapter index causes incorrect seek position [MAJOR] src/Session.cpp (1478-1484): Missing null-check for m_adaptiveTree before dereferencing [MEDIUM] src/Session.cpp (1534-1538): SeekTime with max double may cause undefined behavior on systems with different floating-point handling Preference[MINOR] src/main.cpp (503-512): Redundant check could be simplified Suggestions
Verdict🔴 Address before merging -- 2 blocking issue(s) found Review Details
|
|
Since i don't have Piers, I couldn't run that, but the patches are trivial and should be very easy to port, i can push to a branch if needed/open a second MR if needed. |
ReviewMedium (Undefined Behavior Risk) SeekTime(DBL_MAX) for live edge seek (src/Session.cpp:1537) SeekChapter guards the call with m_adaptiveTree->IsLive(), but SeekTime only applies the live clamp inside a separate Low live_offset applied to chapter 1 of all multi-period live streams (src/Session.cpp:1483) GetChapterPos(1) returns GetLiveOffset() for any live stream. For multi-period live streams this offset applies to each period's chapter-1 position, which is incorrect - the offset only makes sense relative to the very first period. Seeking to chapter 1 of period 2 would jump to liveOffset seconds from the absolute stream start instead of period 2's start. Minor Hardcoded "Live" chapter name (src/Session.cpp:1467): Not i18n-aware. Minor if this surfaces in Kodi UI, though consistent with period IDs being returned as raw strings. live_offset is uint64_t (unsigned) (src/CompKodiProps.h:167): No support for negative offsets (seeking slightly before nominal program start). The analogous live_delay is also uint64_t so this appears intentional, just noting the constraint. |
| LOG::LogF(LOGDEBUG, "Switching to new Period (id=%s, start=%llu, seq=%u)", | ||
| nextPeriod->GetId().data(), nextPeriod->GetStart(), nextPeriod->GetSequence()); | ||
| LOG::LogF(LOGDEBUG, "Seeking to live edge (virtual chapter)"); | ||
| SeekTime(std::numeric_limits<double>::max(), 0, false); |
There was a problem hiding this comment.
Using DBL_MAX as a sentinel for "seek to live edge" is fragile. SeekTime clamps this via the if (m_adaptiveTree->IsLive()) block, but if IsLive() returns false inside SeekTime (e.g. live→VOD transition during a manifest update), seekTime stays at DBL_MAX and static_cast<uint64_t>(seekTime * STREAM_TIME_BASE) is undefined behavior (overflow).
| SeekTime(std::numeric_limits<double>::max(), 0, false); | |
| // Seek to the maximum buffered position (live edge) | |
| uint64_t maxTime{0}; | |
| for (auto& stream : m_streams) | |
| { | |
| if (stream->m_isEnabled) | |
| { | |
| uint64_t curTime = stream->m_adStream.getMaxTimeMs(); | |
| if (curTime > maxTime) | |
| maxTime = curTime; | |
| } | |
| } | |
| SeekTime(static_cast<double>(maxTime) / 1000, 0, false); |
| return static_cast<int64_t>(m_adaptiveTree->m_totalTime / 1000); | ||
|
|
||
| // Chapter 1 of a live stream: return configured start offset | ||
| if (ch == 0 && m_adaptiveTree && m_adaptiveTree->IsLive()) |
There was a problem hiding this comment.
This returns liveOffset as the position for any live stream's chapter 1, including multi-period live streams. For period 2+, this is wrong — GetChapterPos(1) when the user is in period 2 should return 0 (start of that period), not the absolute stream-start offset. Consider guarding with a check that the stream has only one period, or only applying the offset when ch == 0 and m_adaptiveTree->m_periods.size() == 1.
|
First and foremost, as per docs/wiki always develops for the master branch and, if allowed in older branches. If for x reason you cannot do so, then do not even propose the PR on old branches (unless we are talking about backports, but each case is evaluated individually) the new property I won't even analyze the other changes because in the old branch i will only accept critical fixes that do not involve too many changes or dont require deeper testing that could potentially break other use cases EDIT: In addition, given the complexity of live streams, you should provide free stream to test the use case. If this is not possible, at least attach one or more manifests to understand the dynamics of the stream |
harsh; but sure fair. i saw that Piers was the default branch, and as I only had omega available i wanted to at least start the discussion. So i'll re-do things on master no problem. But do explain how live_offset is just a hack? what would you propose? how is it different from live_delay? @CastagnaIT much appreciated for your feedback so far, Thank you! |
|
Internal methods of the player should not be exposed to the user as setting, unless it is a problem that cannot be solved at the moment You have not attached any sample stream to reproduce the problem, nor manifests to inspect, exists many different manifest structures of live streams and i have no idea of your use case If you want to discuss about your playback problem i invite you to open an Issue thread and provide the necessary info |
|
For googlers finding this later; I've pushed a branch (doesn't show up here) to the same origin backporting it from piers. |
Description
To be able to jump forward (to the edge, e.g. the live moment) and jump backward (e.g. the start of the stream) on a live stream (when properly exposed/handled by the content provider/API, we need to cherry-pick commit b2da233 (Cleanups to chapter methods) from Piers which fixes the fact that we do not properly report the chapter count. Then add a commit to add a 'virtual' second chapter matching 'the end' which then gives us 2 chapters; ch1 'the start' and ch2 'now'. A third commit is to help with the start offset, like live_delay, we can add a bit of time to the start, where content providers love to add a conservative padding, to ensure they didn't miss the start of the program, or to make sure we get anything that was shown before the program (like an ad).
Motivation and context
Being able to 'jump to start' (using |< for example) or 'jump to live/now' using >| which relies on proper chapters to trigger.
How has this been tested?
Using retrospect and NLZIET we have livestreams from dutch television programs, moving this functionality to the |< and >| buttons allowed us to jump back/forward to the start/edge of a live stream reliably, with offset (where progress bar was showing a 'dot' of the offsetted jump point.
Types of change
Checklist: