fix: filmstrip — fetch video chunk as Blob URL, append probe to DOM, add timeout
This commit is contained in:
parent
0481fb3ecf
commit
7ea3a235da
1 changed files with 25 additions and 10 deletions
|
|
@ -79,6 +79,10 @@ function AssetDetail({ asset, onClose }) {
|
|||
}, [streamUrl, streamType]);
|
||||
|
||||
// Build filmstrip from real video frames. HLS streams use hls.js probe.
|
||||
// For MP4, we fetch the first 10 MB as a Blob to feed the probe video —
|
||||
// this works reliably even in headless browsers where <video> network loading
|
||||
// often stalls, and ensures the moov atom (placed at the start by faststart)
|
||||
// is already buffered when the probe element reads it.
|
||||
React.useEffect(() => {
|
||||
if (!streamUrl || totalMs <= 0) {
|
||||
setFilmFrames([]);
|
||||
|
|
@ -88,19 +92,23 @@ function AssetDetail({ asset, onClose }) {
|
|||
let cancelled = false;
|
||||
const build = async function() {
|
||||
setFilmstripLoading(true);
|
||||
const probe = document.createElement('video');
|
||||
probe.crossOrigin = 'anonymous';
|
||||
probe.muted = true;
|
||||
probe.playsInline = true;
|
||||
probe.preload = 'auto';
|
||||
probe.style.cssText = 'position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;pointer-events:none';
|
||||
document.body.appendChild(probe);
|
||||
let probe = null;
|
||||
let blobUrl = null;
|
||||
const timeout = setTimeout(function() {
|
||||
probe.remove();
|
||||
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
||||
if (probe) probe.remove();
|
||||
if (!cancelled) { setFilmFrames([]); setFilmstripLoading(false); }
|
||||
cancelled = true;
|
||||
}, 15000);
|
||||
try {
|
||||
probe = document.createElement('video');
|
||||
probe.crossOrigin = 'anonymous';
|
||||
probe.muted = true;
|
||||
probe.playsInline = true;
|
||||
probe.preload = 'auto';
|
||||
probe.style.cssText = 'position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;pointer-events:none';
|
||||
document.body.appendChild(probe);
|
||||
|
||||
if (streamType === 'hls') {
|
||||
if (!window.Hls) throw new Error('hls.js not loaded');
|
||||
await new Promise(function(resolve, reject) {
|
||||
|
|
@ -114,10 +122,16 @@ function AssetDetail({ asset, onClose }) {
|
|||
hls.attachMedia(probe);
|
||||
});
|
||||
} else {
|
||||
// Pre-buffer the first 10 MB as a Blob — proxies use faststart so
|
||||
// the moov atom lives at the start, making this sufficient to parse
|
||||
// the full duration and seek any frame.
|
||||
const resp = await fetch(streamUrl, { headers: { Range: 'bytes=0-10485760' } });
|
||||
const blob = await resp.blob();
|
||||
blobUrl = URL.createObjectURL(blob);
|
||||
await new Promise(function(resolve, reject) {
|
||||
probe.onloadedmetadata = resolve;
|
||||
probe.onerror = reject;
|
||||
probe.src = streamUrl;
|
||||
probe.src = blobUrl;
|
||||
});
|
||||
}
|
||||
const frameCount = 28;
|
||||
|
|
@ -154,7 +168,8 @@ function AssetDetail({ asset, onClose }) {
|
|||
if (!cancelled) setFilmFrames([]);
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
probe.remove();
|
||||
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
||||
if (probe) probe.remove();
|
||||
if (!cancelled) setFilmstripLoading(false);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue