Skip to content

fix(rtc): deliver buffered frames and end iteration at end of stream - #836

Closed
RaphaelFakhri wants to merge 2 commits into
livekit:mainfrom
RaphaelFakhri:fix/media-stream-drain-after-eos
Closed

RaphaelFakhri wants to merge 2 commits into
livekit:mainfrom
RaphaelFakhri:fix/media-stream-drain-after-eos

Conversation

@RaphaelFakhri

Copy link
Copy Markdown

Summary

AudioStream and VideoStream now deliver every frame that was queued before the stream ended, and VideoStream iteration now ends at end of stream instead of waiting forever.

Problem

  • __anext__ returned StopAsyncIteration as soon as the stream's internal task finished. The task finishes when the end-of-stream event arrives, so a consumer that was behind the producer lost every frame still in the queue.
  • VideoStream._run did not enqueue the end marker that AudioStream._run enqueues. A consumer that was already waiting for a frame when the stream ended stayed blocked.

Changes

  • AudioStream.__anext__ and VideoStream.__anext__ stop only when the task is done and the queue is empty.
  • VideoStream._run enqueues None on end of stream, matching AudioStream.
  • Add RingQueue.empty().
  • Add tests/rtc/test_stream_end.py. The tests fake the FFI queue, so they need no native library or server.

Testing

pytest tests/rtc/test_stream_end.py: 3 tests fail without the source change (two lose frames, one times out waiting for the video stream to end) and pass with it. tests/rtc/test_mixer.py, test_ffi_queue.py and test_emitter.py still pass.

@CLAassistant

CLAassistant commented Sep 29, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

devin-ai-integration[bot]

This comment was marked as resolved.

@RaphaelFakhri

Copy link
Copy Markdown
Author

Confirmed. With a bounded queue, the end-of-stream None marker evicted the oldest queued frame, so the last frame was lost when the queue was full. The audio marker had the same effect before this change.

Fixed in a122059. RingQueue has a new put_end method that appends the marker without evicting a frame, and both AudioStream and VideoStream use it. The new tests use capacity=1, queue one frame followed by end of stream, and check that the frame is delivered for both stream types.

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