Fix GPU encoding black frames and improve debug logging
Some checks are pending
GPU Worker CI/CD / test (push) Waiting to run
GPU Worker CI/CD / deploy (push) Blocked by required conditions

- Add CUDA sync before encoding to ensure RGB->NV12 kernel completes
- Add debug logging for frame data validation (sum check)
- Handle GPUFrame objects in GPUHLSOutput.write()
- Fix cv2.resize for CuPy arrays (use cupyx.scipy.ndimage.zoom)
- Fix fused pipeline parameter ordering (geometric first, color second)
- Add raindrop-style ripple with random position/freq/decay/amp
- Generate final VOD playlist with #EXT-X-ENDLIST

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-04 16:33:12 +00:00
parent b15e381f81
commit 9a8a701492
8 changed files with 471 additions and 37 deletions

View File

@@ -1137,6 +1137,52 @@ async def serve_hls_content(
raise HTTPException(404, f"File not found: {filename}")
@router.get("/{run_id}/playlist.m3u8")
async def get_playlist(run_id: str, request: Request):
"""Get live HLS playlist for a streaming run.
Returns the latest playlist content directly, allowing HLS players
to poll this URL for updates without dealing with changing IPFS CIDs.
"""
import database
import os
import httpx
from fastapi.responses import Response
await database.init_db()
pending = await database.get_pending_run(run_id)
if not pending:
raise HTTPException(404, "Run not found")
ipfs_playlist_cid = pending.get("ipfs_playlist_cid")
if not ipfs_playlist_cid:
raise HTTPException(404, "Playlist not yet available")
# Fetch playlist from local IPFS node
ipfs_api = os.environ.get("IPFS_API_URL", "http://celery_ipfs:5001")
try:
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(f"{ipfs_api}/api/v0/cat?arg={ipfs_playlist_cid}")
if resp.status_code != 200:
raise HTTPException(502, "Failed to fetch playlist from IPFS")
playlist_content = resp.text
except httpx.RequestError as e:
raise HTTPException(502, f"IPFS error: {e}")
return Response(
content=playlist_content,
media_type="application/vnd.apple.mpegurl",
headers={
"Cache-Control": "no-cache, no-store, must-revalidate",
"Pragma": "no-cache",
"Expires": "0",
"Access-Control-Allow-Origin": "*",
}
)
@router.get("/{run_id}/ipfs-stream")
async def get_ipfs_stream_info(run_id: str, request: Request):
"""Get IPFS streaming info for a run.

View File

@@ -103,15 +103,34 @@
const video = document.getElementById('live-video');
const statusEl = document.getElementById('stream-status');
const loadingEl = document.getElementById('stream-loading');
const hlsUrl = '/runs/{{ run.run_id }}/hls/stream.m3u8';
// Use dynamic playlist endpoint with cache busting
const baseUrl = '/runs/{{ run.run_id }}/playlist.m3u8';
function getHlsUrl() {
return baseUrl + '?_t=' + Date.now();
}
let hls = null;
let retryCount = 0;
const maxRetries = 120; // Try for up to 4 minutes
let segmentsLoaded = 0;
// Custom playlist loader that adds cache-busting to every request
class CacheBustingPlaylistLoader extends Hls.DefaultConfig.loader {
load(context, config, callbacks) {
if (context.type === 'manifest' || context.type === 'level') {
const url = new URL(context.url, window.location.origin);
url.searchParams.set('_t', Date.now());
context.url = url.toString();
}
super.load(context, config, callbacks);
}
}
function initHls() {
if (Hls.isSupported()) {
hls = new Hls({
// Custom loader to bust cache on playlist requests
pLoader: CacheBustingPlaylistLoader,
// 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
@@ -177,7 +196,7 @@
// Exponential backoff with jitter
const delay = Math.min(1000 * Math.pow(1.5, Math.min(retryCount, 6)), 10000);
setTimeout(() => {
hls.loadSource(hlsUrl);
hls.loadSource(getHlsUrl());
}, delay + Math.random() * 1000);
} else {
statusEl.textContent = 'Stream unavailable';
@@ -246,11 +265,11 @@
}
}, 1000);
hls.loadSource(hlsUrl);
hls.loadSource(getHlsUrl());
hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// Native HLS support (Safari)
video.src = hlsUrl;
video.src = getHlsUrl();
video.addEventListener('loadedmetadata', function() {
loadingEl.classList.add('hidden');
statusEl.textContent = 'Playing';