diff --git a/app/templates/runs/detail.html b/app/templates/runs/detail.html
index 40b0d32..339c437 100644
--- a/app/templates/runs/detail.html
+++ b/app/templates/runs/detail.html
@@ -112,21 +112,22 @@
function initHls() {
if (Hls.isSupported()) {
hls = new Hls({
- // Stability over low latency - buffer more for smoother playback
- liveSyncDurationCount: 4, // Stay 4 segments behind live edge
- liveMaxLatencyDurationCount: 8, // Max 8 segments behind
+ // Stay far behind live edge - rendering is slow (~0.1x speed)
+ // 10 segments = 40s of buffer before catching up
+ liveSyncDurationCount: 10, // Stay 10 segments behind live edge
+ liveMaxLatencyDurationCount: 20, // Allow up to 20 segments behind
liveDurationInfinity: true, // Treat as infinite live stream
// Large buffers to absorb rendering speed variations
- maxBufferLength: 60, // Buffer up to 60s ahead
- maxMaxBufferLength: 120, // Allow even more if needed
- maxBufferSize: 60 * 1024 * 1024, // 60MB buffer
+ maxBufferLength: 120, // Buffer up to 120s ahead
+ maxMaxBufferLength: 180, // Allow even more if needed
+ maxBufferSize: 100 * 1024 * 1024, // 100MB buffer
maxBufferHole: 0.5, // Tolerate small gaps
// Back buffer for smooth seeking
- backBufferLength: 30,
+ backBufferLength: 60,
- // Playlist reload settings
+ // Playlist reload settings - check frequently for new segments
manifestLoadingTimeOut: 10000,
manifestLoadingMaxRetry: 4,
levelLoadingTimeOut: 10000,
@@ -202,9 +203,21 @@
}
});
- // Handle video stalls
+ // Handle video stalls - check if we've caught up to live edge
video.addEventListener('waiting', function() {
- statusEl.textContent = 'Buffering...';
+ // Check if we're near the live edge (within 2 segments)
+ if (hls && hls.liveSyncPosition) {
+ const liveEdge = hls.liveSyncPosition;
+ const currentTime = video.currentTime;
+ const behindLive = liveEdge - currentTime;
+ if (behindLive < 8) { // Less than 2 segments behind
+ statusEl.textContent = 'Waiting for rendering...';
+ } else {
+ statusEl.textContent = 'Buffering...';
+ }
+ } else {
+ statusEl.textContent = 'Buffering...';
+ }
statusEl.classList.remove('text-green-400');
statusEl.classList.add('text-yellow-400');
});
@@ -215,6 +228,24 @@
statusEl.classList.add('text-green-400');
});
+ // Periodic check for catching up to live edge
+ setInterval(function() {
+ if (hls && !video.paused && hls.levels && hls.levels.length > 0) {
+ const buffered = video.buffered;
+ if (buffered.length > 0) {
+ const bufferEnd = buffered.end(buffered.length - 1);
+ const currentTime = video.currentTime;
+ const bufferAhead = bufferEnd - currentTime;
+ // If less than 4 seconds buffered, show warning
+ if (bufferAhead < 4) {
+ statusEl.textContent = 'Waiting for rendering...';
+ statusEl.classList.remove('text-green-400');
+ statusEl.classList.add('text-yellow-400');
+ }
+ }
+ }
+ }, 1000);
+
hls.loadSource(hlsUrl);
hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
diff --git a/tasks/streaming.py b/tasks/streaming.py
index 5066b5f..6ba0bef 100644
--- a/tasks/streaming.py
+++ b/tasks/streaming.py
@@ -134,9 +134,18 @@ class CIDVideoSource:
raise ValueError(f"Could not resolve video source '{self.cid}' for actor_id={self.actor_id}")
logger.info(f"CIDVideoSource._ensure_source: resolved to path={path}")
- # Import from primitives where VideoSource is defined
- from sexp_effects.primitive_libs.streaming import VideoSource
- self._source = VideoSource(str(path), self.fps)
+ # Use GPU-accelerated video source if available
+ try:
+ from sexp_effects.primitive_libs.streaming_gpu import GPUVideoSource, GPU_AVAILABLE
+ if GPU_AVAILABLE:
+ logger.info(f"CIDVideoSource: using GPUVideoSource for {path}")
+ self._source = GPUVideoSource(str(path), self.fps, prefer_gpu=True)
+ else:
+ raise ImportError("GPU not available")
+ except (ImportError, Exception) as e:
+ logger.info(f"CIDVideoSource: falling back to CPU VideoSource ({e})")
+ from sexp_effects.primitive_libs.streaming import VideoSource
+ self._source = VideoSource(str(path), self.fps)
def read_at(self, t: float):
self._ensure_source()