Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

Workers API にバインドする

最終更新 Markdown で表示Agent セットアップ

バインディング は、Worker を Developer Platform 上の外部リソース(Media TransformationsR2 バケットKV 名前空間 など)に接続します。

Media Transformations API を Worker にバインドすると、URL 経由で公開しなくても、動画の変換、リサイズ、コンテンツ抽出ができます。

たとえば Workers 内で Media Transformations を使うと、次のことができます。

  • 非公開の R2 バケットや、保護されたソースに保存した動画を変換する
  • 動画を最適化し、ブラウザーへ配信せず、出力を R2 に直接保存する
  • 動画から静止画やスプライトシートを抽出し、Workers AI で分類や説明に使う
  • 動画ファイルから音声トラックを抽出し、Workers AI で動的に文字起こしする

セットアップ

Media バインディングは Worker 単位で有効にします。

バインディング は、Worker 向けの Cloudflare ダッシュボード、またはプロジェクトディレクトリの Wrangler 設定ファイルで構成できます。

Media Transformations を Worker にバインドするには、Wrangler 設定ファイルの末尾に次を追加します。

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "media": {
    "binding": "MEDIA"
  }
}
[media]
binding = "MEDIA" # available in your Worker on env.MEDIA

Worker コード内では env.MEDIA.input() を使い、動画(ReadableStream として渡す)を操作できるオブジェクトを構築します。

メソッド

Media Transformations のバインディングは Images バインディング に似ています。ただし、メソッドチェーンの順序は固定で、input() の結果を複数の変換で再利用できません。

.input()

Media バインディングの起点です。生のコンテンツを受け取ります。

  • 動画バイトを含む ReadableStream<Uint8Array> を受け取ります。

.transform()(任意)

動画入力のリサイズまたはクロップ方法を定義します。このメソッドは任意です。リサイズやクロップが不要なら、.input() の結果に対して直接 .output() を呼べます。

  • 次のパラメーターを受け取ります(すべて任意)。
    • width: 目標の幅(ピクセル、10〜2000)。
    • height: 目標の高さ(ピクセル、10〜2000)。
    • fit: 指定した寸法に動画を合わせる方法です。
      • contain: アスペクト比を保ち、出力寸法の内側に収まるよう全体をスケールします。
      • cover: 出力寸法を完全に覆うようスケールし、中央寄せでクロップします。
      • scale-down: contain と同じですが、縮小のみです。拡大しません。
  • 詳細は 動画変換のオプション を参照してください。

.output()

動画から何を抽出し、出力をどうフォーマットするかを定義します。入力と出力の制約は ソース動画の要件制限 を参照してください。

  • 次のパラメーターを受け取ります。
    • mode: 生成する出力の種類です。
      • video: 最適化した H.264/AAC の MP4 ファイルを出力します。
      • frame: 静止画(JPEG または PNG)を出力します。
      • spritesheet: 複数フレームを含む JPEG を出力します。
      • audio: AAC エンコードの M4A ファイルを出力します。
    • time: 抽出の開始タイムスタンプ(例: "2s""1m")。デフォルト: "0s"
    • duration: videoaudiospritesheet モードの出力時間(例: "5s")。
    • imageCount: スプライトシートに含めるフレーム数です。
    • format: frame モード(jpgpng)または audio モード(m4a)の出力形式です。
    • audio: video モードで音声を含めるかどうかを示す Boolean です。デフォルト: true

結果メソッド

出力を設定したあと、結果を受け取るメソッドが 3 つあります。いずれも Promise を返すので、await する必要があります。

  • .response(): Promise<Response> を返します。変換後のメディアを HTTP Response オブジェクトとして返し、クライアントへ返すかキャッシュに保存できます。
  • .media(): Promise<ReadableStream<Uint8Array>> を返します。変換後のメディアをバイトストリームとして返します。
  • .contentType(): Promise<string> を返します。出力の MIME タイプです(例: video/mp4image/jpegaudio/mp4)。

最適化した動画クリップを生成する

動画をリサイズし、5 秒のクリップを抽出します。

export default {
	async fetch(request, env) {
		const video = await env.R2_BUCKET.get("input.mp4");

		const result = env.MEDIA.input(video.body)
			.transform({ width: 480, height: 270 })
			.output({ mode: "video", time: "0s", duration: "5s" });

		return await result.response();
	},
};

静止画フレームを抽出する

1 フレームを JPEG サムネイルとして抽出します。

export default {
	async fetch(request, env) {
		const video = await env.R2_BUCKET.get("input.mp4");

		const result = env.MEDIA.input(video.body)
			.transform({ width: 640, height: 360 })
			.output({ mode: "frame", time: "2s", format: "jpg" });

		return await result.response();
	},
};

Media Transformations と Workers AI でコンテンツを識別する

動画からフレーム(静止画)を抽出し、Workers AI の UForm-Gen などのモデルでキャプションを生成します。

export default {
	async fetch(request, env) {
		// First, load the video file from a source like R2 (or a fetch)

		// Loading from R2
		const video = await env.R2_BUCKET.get("input.mp4");

		// Or using a fetch:
		// const video = await fetch('https://example.com/video.mp4');

		// Isolate a frame (still image)
		const frame = await env.MEDIA.input(video.body)
			.transform({ width: 720 })
			.output({
				mode: 'frame',
				time: '3s',
			})
			.response();

		// Set up the payload for Workers AI
		const payload = {
			image: [...new Uint8Array(await frame.arrayBuffer())],
			prompt: "Generate a caption for this image",
			max_tokens: 512,
		};
		const response = await env.AI.run(
			"@cf/unum/uform-gen2-qwen-500m",
			payload
		);
		return new Response(JSON.stringify(response));
	}
}

音声を抽出する

動画から音声トラックを M4A ファイルとして抽出します。リサイズが不要なため、.transform() を省略する例です。

export default {
	async fetch(request, env) {
		const video = await env.R2_BUCKET.get("input.mp4");

		const result = env.MEDIA.input(video.body).output({
			mode: "audio",
			time: "0s",
			duration: "30s",
		});

		return await result.response();
	},
};

Media Transformations と Workers AI で音声を文字起こしする

音声を抽出し、Workers AI の Whisper で文字起こしします。

export default {
	async fetch(request, env) {
		// First, load the video file from a source like R2 (or a fetch)

		// Loading from R2
		const video = await env.R2_BUCKET.get("input.mp4");

		// Or using a fetch:
		// const video = await fetch('https://example.com/video.mp4');

		// Extract audio using the media transformations binding:
		const audio = await env.MEDIA.input(video.body)
			.transform()
			.output({
				mode: 'audio',
				})
			.response();

		// Prepare and run Workers AI inference
		const payload = {
			audio: [...new Uint8Array(await audio.arrayBuffer())],
		};
		const response = await env.AI.run(
			"@cf/openai/whisper",
			payload
		);

		// response will have props {text, word_count, vtt, words}
		return new Response(
			JSON.stringify(response, null, 2),
			{
				headers: {'Content-Type': 'application/json'}
			}
		);
	}
}

変換後の出力を R2 に保存する

動画を変換し、結果を R2 に直接保存します。

export default {
	async fetch(request, env) {
		const video = await env.R2_BUCKET.get("input.mp4");

		const result = env.MEDIA.input(video.body)
			.transform({ width: 480, height: 270, fit: "contain" })
			.output({ mode: "video", time: "0s", duration: "10s", audio: false });

		// Store the transformed video directly in R2
		await env.R2_BUCKET.put("output-480p.mp4", await result.media(), {
			httpMetadata: { contentType: await result.contentType() },
		});

		return new Response("Video transformed and stored", { status: 200 });
	},
};

エラー処理

エラーは、メソッドチェーンの異なる地点で投げられます。

  • .input() は、アカウント制限(無料枠またはサブスクリプション)やサービス障害に関するエラーを投げることがあります。
  • .output() は、変換操作そのものに関するエラー(無効なパラメーターや未対応の入力形式など)を投げることがあります。

エラーは MediaError を投げます。標準の Error インターフェースを拡張し、次の追加情報を持ちます。

  • code: 数値のエラーコードです。
  • message: エラーの説明です。
  • stack: 任意のスタックトレースです。

エラーは try...catch ブロックで処理します。

export default {
	async fetch(request, env) {
		const video = await env.R2_BUCKET.get("input.mp4");

		try {
			const result = env.MEDIA.input(video.body)
				.transform({ width: 480, height: 270 })
				.output({ mode: "video", time: "0s", duration: "5s" });

			return await result.response();
		} catch (e) {
			if (e instanceof Error && "code" in e) {
				// Handle MediaError
				return new Response(`Transformation failed: ${e.message}`, {
					status: 500,
				});
			}
			throw e;
		}
	},
};

キャッシュ

URL 経由の変換と異なり、Media バインディングのレスポンスは自動ではキャッシュされません。Workers では Cache API を直接使い、キャッシュ動作をカスタマイズできます。スクリプト内で、変換結果を Cloudflare のキャッシュまたは R2 ストレージに保存するロジックを実装できます。

課金

料金は Stream の 料金 を参照してください。バインディング経由の変換は、リクエストの一意性ではなく操作単位で課金されます。コストと性能を最適化するには、出力をキャッシュまたは保存して再利用してください。

ローカル開発

Media Transformations API は、Workers のコマンドラインインターフェースである Wrangler によるローカル開発では、リモートモードで利用できます。変換操作はリモートリソースで実行され、無料枠を超えた分は使用量課金の対象です。

ローカル開発で使うには、バインディング設定に remote を追加します。

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "media": {
    "binding": "MEDIA",
    "remote": true
  }
}
[media]
binding = "MEDIA" # available in your Worker on env.MEDIA
remote = true

次を実行します。

npx wrangler dev

役に立ちましたか?