Remotion LabRemotion Lab
疑難排解不可 Seek 的媒體檔案

不可 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 操作。具體來說,以下任一情況都會造成問題:

  1. 未回傳 Content-Range HTTP 標頭 — 此標頭讓瀏覽器能夠請求媒體的特定片段(Range Request),是支援 seek 的必要條件。
  2. 未回傳 Content-Length HTTP 標頭 — 瀏覽器需要知道檔案總大小,才能計算 seek 的位置。
  3. 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-LengthContent-Range 標頭,並且支援 HTTP Range Request(即回應 206 Partial Content)。

大多數現代的靜態檔案服務(如 AWS S3、Cloudflare、Nginx 正確設定後)都已支援此功能。

方案二:下載媒體並在本地引用

將媒體檔案下載到你的專案目錄中,然後使用 importrequire() 語句引用:

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 容器的結構,處理速度很快。


相關資源