Skip to content

player: a film ends on the pipeline's end-of-stream - #865

Merged
GLinnik21 merged 2 commits into
GLinnik21:mainfrom
tiper:end-of-film-eos
Oct 22, 2026
Merged

GLinnik21 merged 2 commits into
GLinnik21:mainfrom
tiper:end-of-film-eos

Conversation

@tiper

@tiper tiper commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Before: a film whose audio runs more than a second past its last video frame never leaves the player. The EOS is pushed when the video lane drains, the pipeline refuses the remaining audio, its clock stops short of the container's duration, and the player sits on the last frame for good because ended waits for the playhead to come within a second of that duration.

After: the pipeline's own end-of-stream event (type=28, PF_EVENT_TYPE_STR_STATE_UPDATE__ENDOFSTREAM, 0x1c, str=true) received after a picture marks the stream played out alongside the existing arithmetic, so the player closes at the credits. A seek and a new session clear it.

0x1c sits exactly at the point where the event numbering shifts between webOS 4 and 5+, so 28 means the same on both (docs/webos5-port.md §5 is updated to say so).

How: a pipeline_eos flag on Shared, raised in sf_on_event_inner only once seen_frame is set, cleared by reset_session and request_seek; the pump's EOS check becomes a pure stream_ended(pos, duration, pipeline_eos). Two new tests: the callback raises the flag only after a picture and a seek clears it, and the pure check ends a film 3 s short of its duration when the pipeline said so.

Device evidence (LG C1, webOS 6.5.3, a 4K AV1 file whose audio ends 3 s after its last video frame). Before, the clock stopped at 5894 s of 5897 s and the player stayed on the last frame:

EOS pushed at true EOF
smp_cb type=28 num=1 str=true
feed a#184400 sz=2560 fed=5896864000000 reply=e qbytes=7680
loop=60 route=player overlay=none pos=5894s play=0pm ...   (repeating)

With this change, same file, same spot:

EOS pushed at true EOF
smp_cb type=28 num=1 str=true
smp: the pipeline reached end-of-stream
EOS reached: playpos=5894s/5897s → ended
teardown spans: stop total=1.4 ...

cargo test --lib -p plx_media: 1688 passed, 0 failed.

tiper and others added 2 commits October 20, 2026 12:05
The player tore itself down at the credits only once the playhead came
within a second of the container's duration. That duration is the
longest track's: on the C1, a film whose audio ran 3 s past its last
video frame pushed the EOS when the video lane drained, the pipeline
refused the remaining audio, its clock stopped at 5894 s of 5897 s, and
the player sat on the last frame for good. The pipeline had said
`type=28 num=1 str=true` (ENDOFSTREAM, 0x1c) at that moment.

That event, after a picture, now marks the stream played out alongside
the arithmetic. A seek and a new session clear it.

Signed-off-by: Tiago <1698241+tiper@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@GLinnik21
GLinnik21 merged commit 60538b6 into GLinnik21:main Oct 22, 2026
13 checks passed
GLinnik21 added a commit that referenced this pull request Oct 22, 2026
#865 ended a film on the pipeline's own end-of-stream (`type=28`) after
a picture, because a film whose audio runs past its last video frame
never gets its playhead within a second of the container's duration (the
longest track's) and sat on the last frame for good. Nothing in the
pipeline tier could see that: every clip it plays has picture and audio
ending together.

make_fixtures.py gets `pipe_h264_ac3_audio_tail`: 720x480 H.264 + AC-3
5.1, 20 s of picture and 23 s of audio. The knob is one declarative key,
`audio_tail_s`, with `duration` staying the container's length so the
duration check, the burned-in layout and the carried-forward records
all keep agreeing with the file; only the testsrc2 input ends early.
verify() reads both streams' last packet times back out of the finished
file and fails the build unless the audio ends at least 2 s after the
video, so a regenerated clip that lost the tail cannot pass quietly.

The manifest gets `pipe_finish_audio_tail`, graded like `pipe_finish_eos`
(`reaches_eos`) and separate from it for the same reason the replay case
is: ending on the pipeline's word and ending on the playhead are
different failures. The harness rule that only a `reaches_eos` case may
name a clip that runs out no longer assumes one such clip; a static test
also pins each such clip against the 0.6 x run_secs bound that
_resolve_fixtures skips on.

The prose that said `EOS reached` needs the playhead within a second of
the duration, and that a truncated transfer cannot reach it, is
corrected in tests/README.md and `a_finished`'s docstring.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
GLinnik21 added a commit that referenced this pull request Oct 22, 2026
#865 made a film end on the pipeline's own end-of-stream (type=28), but the simulator's
HostSink never sent one: its clock stops at the last fed VIDEO pts, so a film whose audio
outlasts its video hung there exactly as on the television, and #865's arm could not fire.

sf_push_eos now latches the push; once the clock has played out to the last fed video frame,
the ticker sends type=28 num=1 str=true through the real callback, once per pushed EOS. The
latch is cleared wherever the fed timeline restarts (Clock::rewind, sf_seek_ms). Clock::tick
is split out of the ticker thread so a test can take a tick on its own thread.

A hostsim regression test drives the real feed lanes and the real pump against HostSink
(video to 20 s, audio to 23 s, duration 23 s) and asserts player::ended(); it also grades
that nothing is sent before an EOS is pushed. Sink-level tests grade the latch.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
@tiper
tiper deleted the end-of-film-eos branch November 2, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants