Remotion LabRemotion Lab
字幕匯入 SRT 字幕檔案

匯入 SRT 字幕檔案

使用 @remotion/captions 的 parseSrt() 將現有的 .srt 字幕檔案匯入 Remotion,並轉換為標準 Caption 格式以便後續使用。

匯入 SRT 字幕檔案

如果您已有現成的 .srt 字幕檔,可以使用 @remotion/captions 套件中的 parseSrt() 函式將其匯入 Remotion,並轉換為標準的 Caption 格式。

安裝

npm install @remotion/captions
# 或
yarn add @remotion/captions
# 或
pnpm add @remotion/captions

讀取 .srt 檔案

public 資料夾載入

.srt 字幕檔放置於專案的 public 資料夾中,然後使用 staticFile() 取得路徑,再透過 fetch 讀取並解析:

// MyComponent.tsx
import { useState, useEffect, useCallback } from 'react';
import { AbsoluteFill, staticFile, useDelayRender } from 'remotion';
import { parseSrt } from '@remotion/captions';
import type { Caption } from '@remotion/captions';
 
export const MyComponent: React.FC = () => {
  const [captions, setCaptions] = useState<Caption[] | null>(null);
 
  // useDelayRender 確保 Remotion 等待資料載入完成後再開始渲染
  const { delayRender, continueRender, cancelRender } = useDelayRender();
  const [handle] = useState(() => delayRender());
 
  const fetchCaptions = useCallback(async () => {
    try {
      // staticFile() 將 public 資料夾中的檔案路徑轉換為可存取的 URL
      const response = await fetch(staticFile('subtitles.srt'));
      const text = await response.text();
 
      // parseSrt() 解析 SRT 格式並回傳標準 Caption 陣列
      const { captions: parsed } = parseSrt({ input: text });
      setCaptions(parsed);
 
      // 通知 Remotion 可以繼續渲染
      continueRender(handle);
    } catch (e) {
      // 發生錯誤時取消渲染並回報錯誤
      cancelRender(e);
    }
  }, [continueRender, cancelRender, handle]);
 
  useEffect(() => {
    fetchCaptions();
  }, [fetchCaptions]);
 
  // 資料尚未載入時不渲染任何內容
  if (!captions) {
    return null;
  }
 
  return (
    <AbsoluteFill>
      {/* 在此使用 captions 陣列渲染字幕 */}
    </AbsoluteFill>
  );
};

資料夾結構

my-remotion-project/
├── public/
│   └── subtitles.srt    ← 將 .srt 檔案放在這裡
├── src/
│   └── MyComponent.tsx
└── package.json

從遠端 URL 載入

您也可以直接 fetch 遠端的 .srt 檔案:

import { useState, useEffect, useCallback } from 'react';
import { AbsoluteFill, useDelayRender } from 'remotion';
import { parseSrt } from '@remotion/captions';
import type { Caption } from '@remotion/captions';
 
export const RemoteCaptionsComponent: React.FC = () => {
  const [captions, setCaptions] = useState<Caption[] | null>(null);
  const { delayRender, continueRender, cancelRender } = useDelayRender();
  const [handle] = useState(() => delayRender());
 
  const fetchCaptions = useCallback(async () => {
    try {
      // 直接從遠端 URL 讀取
      const response = await fetch(
        'https://example.com/subtitles/my-video.srt'
      );
      const text = await response.text();
      const { captions: parsed } = parseSrt({ input: text });
      setCaptions(parsed);
      continueRender(handle);
    } catch (e) {
      cancelRender(e);
    }
  }, [continueRender, cancelRender, handle]);
 
  useEffect(() => {
    fetchCaptions();
  }, [fetchCaptions]);
 
  if (!captions) return null;
 
  return (
    <AbsoluteFill>
      {/* 使用字幕資料 */}
    </AbsoluteFill>
  );
};

parseSrt() API

import { parseSrt } from '@remotion/captions';
 
const { captions } = parseSrt({
  input: srtString, // 原始 SRT 格式字串
});

回傳值:Caption 格式

解析完成後,每個字幕項目都會轉換為 Caption 物件:

type Caption = {
  text: string;           // 字幕文字內容
  startMs: number;        // 字幕開始時間(毫秒)
  endMs: number;          // 字幕結束時間(毫秒)
  timestampMs: number | null; // 字幕時間戳(毫秒,可為 null)
  confidence: number | null;  // 信心分數(0–1,SRT 解析時通常為 null)
};

SRT 格式範例

1
00:00:01,000 --> 00:00:04,000
歡迎來到 Remotion 教學
 
2
00:00:05,500 --> 00:00:09,000
在這段影片中,我們將學習如何使用字幕
 
3
00:00:10,000 --> 00:00:13,500
讓我們開始吧!

解析後的結果:

[
  {
    "text": "歡迎來到 Remotion 教學",
    "startMs": 1000,
    "endMs": 4000,
    "timestampMs": 1000,
    "confidence": null
  },
  {
    "text": "在這段影片中,我們將學習如何使用字幕",
    "startMs": 5500,
    "endMs": 9000,
    "timestampMs": 5500,
    "confidence": null
  },
  {
    "text": "讓我們開始吧!",
    "startMs": 10000,
    "endMs": 13500,
    "timestampMs": 10000,
    "confidence": null
  }
]

使用已解析的字幕

解析完成後,字幕資料符合標準 Caption 格式,可直接使用 @remotion/captions 的所有工具。

根據當前影格顯示字幕

import { useCurrentFrame, useVideoConfig } from 'remotion';
import type { Caption } from '@remotion/captions';
 
const SubtitleDisplay: React.FC<{ captions: Caption[] }> = ({ captions }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
 
  // 將影格轉換為毫秒
  const currentMs = (frame / fps) * 1000;
 
  // 找出目前時間點應顯示的字幕
  const currentCaption = captions.find(
    (caption) =>
      currentMs >= caption.startMs && currentMs <= caption.endMs
  );
 
  if (!currentCaption) return null;
 
  return (
    <div
      style={{
        position: 'absolute',
        bottom: 80,
        left: 0,
        right: 0,
        textAlign: 'center',
        fontSize: 36,
        color: 'white',
        textShadow: '2px 2px 4px black',
        padding: '0 40px',
      }}
    >
      {currentCaption.text}
    </div>
  );
};

完整整合範例

import { useState, useEffect, useCallback } from 'react';
import {
  AbsoluteFill,
  OffthreadVideo,
  staticFile,
  useCurrentFrame,
  useDelayRender,
  useVideoConfig,
} from 'remotion';
import { parseSrt } from '@remotion/captions';
import type { Caption } from '@remotion/captions';
 
export const VideoWithCaptions: React.FC = () => {
  const [captions, setCaptions] = useState<Caption[] | null>(null);
  const { delayRender, continueRender, cancelRender } = useDelayRender();
  const [handle] = useState(() => delayRender());
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
 
  useEffect(() => {
    (async () => {
      try {
        const response = await fetch(staticFile('subtitles.srt'));
        const text = await response.text();
        const { captions: parsed } = parseSrt({ input: text });
        setCaptions(parsed);
        continueRender(handle);
      } catch (e) {
        cancelRender(e);
      }
    })();
  }, [continueRender, cancelRender, handle]);
 
  if (!captions) return null;
 
  const currentMs = (frame / fps) * 1000;
  const activeCaption = captions.find(
    (c) => currentMs >= c.startMs && currentMs <= c.endMs
  );
 
  return (
    <AbsoluteFill>
      <OffthreadVideo src={staticFile('video.mp4')} />
      {activeCaption && (
        <div
          style={{
            position: 'absolute',
            bottom: 80,
            left: 0,
            right: 0,
            textAlign: 'center',
            fontSize: 36,
            color: 'white',
            textShadow: '2px 2px 4px rgba(0,0,0,0.8)',
          }}
        >
          {activeCaption.text}
        </div>
      )}
    </AbsoluteFill>
  );
};

後續步驟

成功匯入字幕後,您可以:

  • 顯示字幕:將字幕渲染到影片畫面上(詳見顯示字幕
  • 匯出字幕:將字幕導出為其他格式(詳見匯出字幕
  • 字幕動畫:為字幕加入進場、出場動畫效果

相關連結