fframesで動画を作成する
fframesは、コードから動画をレンダリングするRustライブラリです。動画はrender_frame(frame) -> Svgrメソッドを持つRustの構造体で、各フレームに対してsvgr!マクロ({Rust式}を含むSVGマークアップ)を使用してSVGツリーを返します。シーンがタイムラインを分割し、timeline!とスプリングが値をアニメーション化し、オーディオマップがサウンドを配置し、ffmpegが結果をエンコードします。
各プロジェクトにはコマンドライン(fframes::cliから)が付属します。動画のレンダリング、リアルタイムプレビューウィンドウの表示、フレーム、コンタクトシート、ラウドネス数値をファイルとして確認するなど、すべての操作にこれを使用します。デザイン前にはreferences/design.mdを、コード記述中にはreferences/api.mdを、サウンド関連にはreferences/audio.mdを、3Dショット(fframes::three_d)にはreferences/3d.mdを参照してください。
提供された動画と照合する場合、まずreferences/design.mdの参照分析ガイダンスに従ってください。そのカット、ペーシング、モーションは以下のデフォルトデザインよりも優先されます。高速編集や意図的な黒フレームは、要求された結果の一部である可能性があります。
何よりも先に守るべき2つのルール
コードを書く前に計画する
テンプレートを開いてすぐにアニメーションを始めないでください。まずは以下を行います:
- 概要を明確にします:誰が見てどこで見るか(プラットフォームがフォーマットと長さを決定します)、視聴者が覚えなければならない唯一のこと、トーン、そして存在するアセット(ロゴ、製品ショット、映像、音楽、ナレーション)を決めます。不足しており重要と思われる点はユーザーに質問してください。製品の事実、数値、引用を勝手に作り出さないでください。
- 動画を音楽や声に合わせてカットする場合は、まずサウンドを選びます。そのテンポ、ビート、フレーズが他のすべてのタイミングを決定します。
- まずモーションデザインを計画し、モーションデザインをサウンドのピークに同期させて、単一のユニークな流れを作ります:要素がどこから現れ、どのように変形するか。すべてのシーンにユニークなモーションデザインを施して、洗練された見た目になるようにしてください。動画がスライドショーのように見えてはいけません。
- 最終的な動画を見ている人の感情を推測し、それに応じてデザインのすべての部分(流れ、テンポ、ビート、モーション、色)を調整するように努めてください。
スロップ(手抜き)禁止
スロップとは、デフォルトの選択肢だけで作られた動画のことです。テキストを変えただけでどんな製品にも当てはまり、視聴者は一瞬でそれと気づきます。ユーザーが明示的に求めない限り、以下を納品してはいけません:
- テンプレートの残骸:スターターレイアウト、色、フォント、装飾、プレースホルダーテキスト、偽のロゴ、でっち上げの数字。
- ビート連打:すべてのビートでフレーム全体がズーム、シェイク、フラッシュすること。小さなもの(ストローク、単語、スパーク)をビートに同期させ、フレーム全体を変えるのは数回のカットだけにしてください。
- すべてに同じモーション:すべての要素が同じイージングと持続時間で40pxフェードアップすること。
- 静的シーン:何かが現れて、そのままフェードアウトするだけで、他に何も動かないこと。
- 一般的なコピー:「ご紹介」、「解き放て」、「シームレス」、「次のレベル」、「Fast. Simple. Powerful.」のような3語のタグライン。画面テキストは人が話すように書きましょう。
- テキスト全般:テキストの量は制限し、的を絞るべきです。
代わりに、すべての要素に主題に関連した理由を与え、理由のないものは削除してください。言葉ではなく、ディテールとモーションに労力を費やしましょう:セカンダリモーション、ずらした登場、描かれるストローク、互いに反応するオブジェクト、カットを超えて運ばれる要素。ユーザーに何かを見せる前に、ストリップを見て、テキストを変えただけで別の製品でも動画が成立するかどうかを自問してください。成立するなら、色を変えるのではなく、アイデアを変えてください。
1. インストール
https://rustup.rs からRustをインストールし、次にffmpegが使用するシステムライブラリをインストールします:
# macOS
brew install pkg-config ffmpeg x264 x265 opus nasm ninja
# Debian / Ubuntu
sudo apt-get install -y yasm nasm ffmpeg libx264-dev libx265-dev libopus-dev libclang-dev clang ninja-build libvpx-dev libasound2-dev
# Arch
sudo pacman -S ninja yasm nasm ffmpeg x264 x265 opus clang
Windowsでは、代わりにプリビルドされたFFmpeg 9共有ビルドをリンクします(FFMPEG_DIRをBtbN/FFmpeg-Buildsから解凍したffmpeg-n9.0-latest-win64-gpl-sharedビルドに設定し、そのbinをPATHに追加し、winget install LLVM.LLVMでLLVMをインストールし、LIBCLANG_PATHを設定します)。詳細はfframesのREADMEにあります。
2. プロジェクトを作成する
curl -fsSL https://raw.githubusercontent.com/dmtrKovalenko/fframes/main/scripts/new-video.sh | bash -s -- my-video --yes
# または、ジェネレーターを一度インストールして直接使用する:
cargo install --locked cargo-fframes
cargo fframes new my-video --format landscape --fps 30 --yes
オプション:--template single-scene|multi-scene、--format landscape|portrait|square|uhd、--fps、--title、--backend、--dir。プロジェクトはインストールされたcargo-fframesと一致するfframesリリースに依存します。--gitは代わりにリポジトリのmainブランチを使用します。入力待ちにならないように、常に--yesを渡してください。
テンプレート:
single-scene(デフォルト):1つのシーン。すべてのフォーマットに適応します。シングルショットのクリップ、タイトルカード、ポートレート動画はここから始めてください。multi-scene:2つのシーン。複数のシーンがあるものはここから始めてください。landscapeとuhdのみを受け入れます。
テンプレートの内容は、APIを示すプレースホルダーコードです。そのレイアウト、色、フォント、装飾を、ユーザーが要求した動画用にデザインしたものに置き換えてください。
Skia GPUバックエンド(デフォルト)を使用してください。 cargo fframes newは、macOSではMetal、Linux/WindowsではVulkanを選択します。CPUバックエンドよりも約10倍高速にレンダリングし、リアルタイムのpreviewウィンドウを提供します。macOSとLinuxでは、初回ビルド時にプリビルドされたSkiaとffmpegをダウンロードし、Rustの依存関係のみをコンパイルします(約1分。他のターゲット、またはmetalとvulkanを一緒に指定すると、Skiaをソースからコンパイルするのに約20分かかります)。すぐにビルドを開始し、動画を書きながら実行してください:
cd my-video && cargo build --release # バックグラウンドで実行、初回ビルドだけ時間がかかります
--backend cpuはSkiaを必要としませんが、プレビューウィンドウはありません。GPUがない場合のみ選択してください。
プロジェクトは次のように生成されます:
my-video/
src/lib.rs # 動画(テンプレートから)
src/main.rs # コマンドライン
media/ # バイナリにコンパイルされるフォント、画像、オーディオ(1つのスターターフォント)
README.md # 以下のコマンド
以下のすべてのコマンドはcargo run --release -- <command>です。短いシェル関数を定義してください(R="cargo run --release --"のような変数はzsh(macOSのシェル)では単語に分割されません):
R() { cargo run --release -- "$@"; }
3. ループ
このスキルを使用する際は、テストを一切書かないでください。 ユニットテスト、統合テスト、スナップショットテスト、ビジュアルリグレッションテスト、テストモジュール、テストハーネスを作成しないでください。inspect、レンダリングされたフレーム、コンタクトシート、オーディオチェック、ネイティブプレビューを使用して動画を検証してください。
プレビューを使い、レンダリングは避けてください。 動画がGPUレンダリングバックエンドを使用し、このマシンでGPUが利用可能な場合は、変更のたびに動画を再レンダリングする代わりに、ネイティブプレビューを実行してください(またはユーザーにコマンドを表示してください)。変更がレビュー可能になったら:
- 実際のプロジェクトパスとパッケージ名を使用して、コピー可能なシェルコードブロックに完全なコマンドを表示します。ユーザーが
$Rヘルパーを定義していることを前提としないでください。例:cd /absolute/path/to/fframes && cargo run --release -p my-video -- preview 0s。 - そのプレビューをユーザーの代わりに実行することを明示的に申し出てください。ユーザーが既にプレビューの開封または更新を承認している場合は、コマンドを表示し、その承認内で続行してください。
要求された変更の後、コマンドの開始時間を、変更箇所の直前またはその秒に設定し、その登場または遷移を見るのに十分なリードインを設けてください。例えば、preview 8.5sを使用すると、ユーザーは冒頭を再生し直すことなく、約9秒の変更をレビューできます。どのセクションが表示されるかを明記してください。ユーザーが最終エクスポートを要求した場合にのみ、動画全体をレンダリングしてください。その要求が既に明示的である場合、別の確認は必要ありません。
変更のたびに:
$R timelineは、シーンとそのフレーム/秒範囲、およびすべてのオーディオトラックを一覧表示します。ピクセルを見る前に、ここで構造とペーシングを確認してください。$R inspectは、0.25秒ごと、および各シーンの最初と最後のフレームをチェックします。欠落した画像、フォント、グリフ、キャンバスの端で切り取られたテキスト、無効なSVG(サイズがゼロの長方形、不正な半径)、壊れたトランスフォームとパニックを、それぞれ時間とシーンとともに報告します。エラーがあるとコード2で終了します。報告されたものはすべて修正してください。シーンの最初のフレームにのみ表示される警告は通常、登場です。ストリップで確認してください。$R strip <scene or range> -n 12は、コンタクトシート(strip.png)を書き出します:等間隔のフレームを1つのラベル付き画像にまとめます。画像ツールで開いてください。レイアウト、リズム、モーションを判断する最速の方法です。$R strip 2s,5s,9sは、選択した時間を並べて表示します。$R frame Intro@end,Outro@50%は、フルサイズのPNGをframes/ディレクトリに書き出し(タイポグラフィ、配置、コントラストなどの詳細チェック用)、それらのフレームで見つかった問題を表示します。小さな詳細(要素間の接触、エッジ、小さな形状)は、縮小されたフレームではなく、鮮明にズームした切り抜きで判断してください:$R frame 3s --crop 1100,520,240,120(ビデオピクセル、約960px幅にズーム)。--idsは、idを持つすべての要素の輪郭と名前を表示し、どの形状がどれかを特定します。$R onion "[email protected]@1s" -n 6は、フレームを1つの画像(onion.png)にブレンドして、動きの軌跡と間隔(イージング、オーバーシュート、ずらし)を表示します。$R preview Introは、ユーザーが視聴するためのサウンド付きリアルタイムウィンドウを開きます(スペースで再生/一時停止、h/lで1秒シーク、j/kで1フレーム移動、qで終了)。閉じられるまでブロックされるため、ユーザーの代わりに実行する場合はバックグラウンドで起動してください。上記のようにコマンドを表示して起動を申し出てください。編集後は、変更箇所の近くの特定の秒を使用してください。
エンコードされたサンプルが必要な場合、$R render Intro --draftは1つのシーンを半分の解像度でエンコードします。$R renderは、要求された最終エクスポート用に動画全体を書き出します。
完成した動画を表示する前に、$R inspect --popsを実行してください。これはすべてのフレームを小さくレンダリングし、ポップ(静止フレーム間で1フレームで変化する画像の部分。要素が移動せずに現れたり、消えたり、別の要素の前にジャンプしたりするなど)について警告します。各警告は、それを確認するためのframe ... --crop ...コマンドを提供します。同じ領域の繰り返し(カウンター、点滅ライト)は1つの警告にまとめられます。
ルール:
- 何かが良さそうだと言う前に、PNGを確認してください。
- 多数の
frame呼び出しよりもstripを優先してください。構図だけが必要な場合は--scale 0.5を追加してください。 - 出力を解析する場合は
--jsonを追加してください:結果はstdoutに、進捗はstderrに出力されます。 --releaseを維持してください:デバッグビルドはレンダリングが何倍も遅くなります。
時間の指定
120(フレーム)、3.2s、500ms、1:05.5、50%、start、end;構造体名によるシーン、Intro(IntroSceneにもマッチ、大文字小文字を区別しない)、#3(シーンインデックス)、Intro[1](そのタイプの2番目のシーン)。シーン内では[email protected]、Intro@12、Intro@50%、Intro@end。範囲:a..b(終了は含まない)、a..、..b、all、またはシーン全体を示すシーン名。複数の時間はカンマで区切ります。
すべてのコマンド
| コマンド | これを使用する目的 |
|---|---|
timeline | シーン、長さ、オーディオトラックとそのミックス設定を確認する |
inspect [RANGE] [--every 0.1s | --all-frames] [--fail-on warning] [--pops] | ピクセルをレンダリングせずに問題を見つける。--popsはポップイン/アウトする要素も見つける |
strip [RANGE | TIMES] -n N [--columns 4] [--width 480] [--crop x,y,w,h] | 1つの画像でフローとモーションをレビューする |
frame TIMES [-o dir] [--svg] [--crop x,y,w,h] [--zoom N] [--ids] | 選択したフレームのフルサイズPNG(およびレイアウトされたSVG)、鮮明な切り抜き、ラベル付き要素 |
onion RANGE -n N | 動きの軌跡とイージングを確認する |
svg TIME | フレームの最終SVGをテキストとして読み取る(位置、テキスト、色) |
preview [TIME] [--paused] [--mute] | ユーザー向けのサウンド付きリアルタイムウィンドウ |
render [RANGE] [-o out.mp4] [--draft] | 動画またはその一部をエンコードする(オーディオはこれに合わせてカットされる) |
audio analyze [RANGE] [--waveform w.png] | ラウドネス(LUFS)、トゥルーピーク、クリッピング、無音、シーンごと |
audio at TIMES | ある瞬間にどのサウンドが再生されているか、ファイル内のどこで、どのくらいの音量か |
audio render [RANGE] -o a.wav | ミックスをWAVファイルとして出力する |
ブラウザエディタ
fframesには、タイムラインとスクラビングを備えたWebエディタもあり、ユーザーがインタラクティブに動画を調整したい場合に便利です。これはWebAssemblyにコンパイルされた動画を実行し、Node.jsとwasm-packが必要です。セットアップは、fframesリポジトリ(https://github.com/dmtrKovalenko/fframes/tree/main/examples/hello-world)のhello-world例にあるeditor/フォルダです。ユーザーがGUIエディタを要求した場合に言及してください。視聴にはpreviewを、自身のレビューにはCLIを使用してください。
4. 動画を書く
impl Video for MyVideo<'_> {
const FPS: usize = 30; const WIDTH: usize = 1920; const HEIGHT: usize = 1080;
fn duration(&self) -> Duration<'_> { Duration::Auto } // シーンの合計
fn audio(&self) -> AudioMap<'_> { AudioMap::none() } // references/audio.mdを参照
fn define_scenes(&self) -> Scenes<'_> { Scenes::from(vec![&self.intro as &dyn Scene, &self.main]) }
fn render_frame<'a>(&'a self, frame: Frame, ctx: &FFramesContext<'a, '_>) -> Svgr<'a> {
fframes::svgr!(<svg xmlns="http://www.w3.org/2000/svg" width={Self::WIDTH} height={Self::HEIGHT}>
<rect width={Self::WIDTH} height={Self::HEIGHT} fill={BACKGROUND} />
{ctx.render_scenes(&frame)}
</svg>)
}
}
- 1シーン1アイデア、各2~6秒。シーン内では
frame.seconds()がシーン開始からの秒数をカウントします。 - デフォルトでは、固定値を使用したモーションのために
svgr!属性内で直接frame.animate(fframes::timeline!(...))を使用します。svgr!はこれらのアニメーションをキャッシュされた静的変数に格上げします。イージングは、Easing::Spring { mass: 1.0, stiffness: 180.0, damping: 20.0 }を含め、タイムライン内でインラインで定義します。名前付きイージング定数はオプションです。スプリングの終了時間は省略して、そのようにします
…
(翻訳のため省略)