不可 Seek 的媒體檔案
了解 Remotion 中「媒體無法 seek」錯誤的成因與解決方案,包括 HTTP 標頭設定、Faststart 支援以及跨來源資源政策問題。
錯誤訊息說明
如果你在控制台中看到以下錯誤:
The media [src] cannot be seeked.
This could be one of two reasons:
1) The media resource was replaced while the video is playing but it was not loaded yet.
2) The media does not support seeking.
Please see https://remotion.dev/docs/non-seekable-media for assistance.
這代表 Remotion 無法對媒體檔案進行任意時間點的定位(seek)。
原因分析
原因一:媒體在播放中被替換但尚未預載
最常見的情況是:一個音訊或影片檔案在正在播放時被替換(例如因為使用者輸入改變了媒體來源),但新的媒體尚未完成載入。
解決方法: 在將媒體掛載到 <Audio> 或 <Video> 標籤之前,先使用 prefetch() 預先載入資源。注意:資源必須支援 CORS 才能使用此方法。
import { prefetch, staticFile } from "remotion";
import { useState } from "react";
const MyComp = () => {
const [audioUrl, setAudioUrl] = useState(staticFile("sample.mp3"));
return (
<>
<select
onChange={(e) => {
// 先預載,載入完成後再更新狀態
prefetch(e.target.value)
.waitUntilDone()
.then(() => {
setAudioUrl(e.target.value);
});
}}
>
<option value={staticFile("sample.mp3")}>Audio 0</option>
<option value={staticFile("sample2.mp3")}>Audio 1</option>
<option value={staticFile("sample3.mp3")}>Audio 2</option>
</select>
</>
);
};原因二:媒體檔案本身不支援 Seek
媒體伺服器在回應請求時,若缺少必要的 HTTP 標頭,瀏覽器(以及 Remotion)就無法對媒體進行 seek 操作。具體來說,以下任一情況都會造成問題:
- 未回傳
Content-RangeHTTP 標頭 — 此標頭讓瀏覽器能夠請求媒體的特定片段(Range Request),是支援 seek 的必要條件。 - 未回傳
Content-LengthHTTP 標頭 — 瀏覽器需要知道檔案總大小,才能計算 seek 的位置。 - MP4 檔案不支援 Faststart — MP4 格式的 moov atom(媒體的後設資料)若位於檔案末尾,瀏覽器必須下載整個檔案才能開始播放,也無法進行 seek。
驗證方法: 在瀏覽器中直接開啟影片或音訊的 URL,嘗試拖動播放進度條。如果無法 seek,就是這個問題。
你也可以使用 getVideoMetadata() 函式程式化地檢查影片是否支援 seek:
import { getVideoMetadata } from "@remotion/media-utils";
const metadata = await getVideoMetadata("https://example.com/video.mp4");
console.log(metadata);原因三:媒體被伺服器的安全政策阻擋
以下 HTTP 標頭可能會阻止媒體檔案被載入到影片標籤中:
X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
X-Frame-Options: ALLOW-FROM https://example.com/
Content-Security-Policy: frame-ancestors 'none'
Content-Security-Policy: frame-ancestors 'self'
Content-Security-Policy: frame-ancestors https://example.com/
Cross-Origin-Resource-Policy: same-origin
當伺服器回傳上述標頭時,媒體檔案將因安全政策而無法載入,這是伺服器端的限制,無法在 Remotion 端直接解決。
解決方案
根據你的具體情況,可以選擇以下其中一種解法:
方案一:確保伺服器支援 Range Request
確保你的媒體伺服器在回應時包含 Content-Length 和 Content-Range 標頭,並且支援 HTTP Range Request(即回應 206 Partial Content)。
大多數現代的靜態檔案服務(如 AWS S3、Cloudflare、Nginx 正確設定後)都已支援此功能。
方案二:下載媒體並在本地引用
將媒體檔案下載到你的專案目錄中,然後使用 import 或 require() 語句引用:
import { Audio } from "remotion";
import myAudio from "./assets/my-audio.mp3";
export const MyComp = () => {
return <Audio src={myAudio} />;
};或是將檔案放在 public/ 目錄中,使用 staticFile() 引用:
import { Audio, staticFile } from "remotion";
export const MyComp = () => {
return <Audio src={staticFile("my-audio.mp3")} />;
};方案三:改用 <OffthreadVideo>
<OffthreadVideo> 元件使用不同的技術來渲染影片幀,可以繞過瀏覽器的 seek 限制。對於不支援 seek 的影片,這通常是最簡單的解法:
import { OffthreadVideo, staticFile } from "remotion";
export const MyComp = () => {
return (
<OffthreadVideo src="https://example.com/non-seekable-video.mp4" />
);
};注意: 使用 <OffthreadVideo> 後,影片在渲染輸出中可以正確處理,但在 Remotion Studio 預覽或 <Player> 元件中仍可能出現問題。
MP4 Faststart 修復
如果你的 MP4 檔案因為 moov atom 位置問題無法 seek,可以使用 FFmpeg 重新處理,將 moov atom 移到檔案開頭:
ffmpeg -i input.mp4 -movflags faststart -acodec copy -vcodec copy output.mp4這個指令不會重新編碼影片,只是調整 MP4 容器的結構,處理速度很快。