← Cloudflare Stream / stream / transform-videos
Привязка к Workers API
A привязка подключает ваш Worker на внешние ресурсы на Developer Platform, например Media Transformations, Бакеты R2, или Пространства имён KV.
Вы можете подключить Media Transformations API к своему Worker, чтобы преобразовывать, изменять размер и извлекать контент из видео без необходимости делать их доступными по URL.
Например, используя Media Transformations в Workers, вы можете:
- Преобразование видео, хранящегося в приватном бакете R2 или другом защищённом источнике
- Оптимизируйте видео и сохраняйте результат прямо в R2, не отдавая его в браузер
- Извлекайте статичные кадры и спрайт-листы для видео и используйте их для классификации или описания с помощью Workers AI
- Извлекайте аудиодорожки из видеофайлов для динамической транскрипции с помощью Workers AI
Настройка
Привязка Media включается отдельно для каждого Worker.
Bindings можно настроить в Cloudflare dashboard для вашего Worker или в конфигурационном файле 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() (необязательно)
Определяет, как входное видео должно быть преобразовано с помощью изменения размера или обрезки. Этот метод необязателен: если изменение размера или обрезка не нужны, можно вызвать .output() напрямую к результату .input().
- Принимает следующие параметры (все необязательные):
width: Целевая ширина в пикселях (от 10 до 2000).height: Целевая высота в пикселях (от 10 до 2000).fit: Как изменить размер видео, чтобы вписать его в заданные размеры.contain: Масштабирует видео так, чтобы оно полностью помещалось в заданные размеры вывода с сохранением соотношения сторон.cover: Масштабирует видео так, чтобы оно полностью покрывало заданные размеры вывода, с обрезкой по центру.scale-down: То же, что иcontain, но только уменьшает размер. Увеличение не выполняется.
- См. Параметры преобразования видео для дополнительных сведений.
.output()
Определяет, что извлекать из видео и как будет отформатирован результат. См. требования к исходному видео и ограничения об ограничениях на входные и выходные данные.
- Принимает следующие параметры:
mode: Тип создаваемого результата.video: Формирует MP4-файл, оптимизированный для H.264/AAC.frame: Формирует статичное изображение (JPEG или PNG).spritesheet: Формирует JPEG-файл, содержащий несколько кадров.audio: Формирует файл M4A с кодированием AAC.
time: Начальная отметка времени для извлечения (например,"2s","1m"). По умолчанию:"0s".duration: Длительность результата дляvideo,audio, илиspritesheetрежимы (например,"5s").imageCount: Количество кадров, включаемых в спрайт-лист.format: Формат вывода дляframeрежим (jpg,png) илиaudioрежим (m4a).audio: Логическое значение, определяющее наличие звука вvideoрежим. По умолчанию:true.
Методы получения результата
После настройки вывода доступны три метода получения результата. Они возвращают Promise и требуют использования await:
.response(): ВозвращаетPromise<Response>: преобразованный медиафайл в виде объекта HTTP Response, готового к возврату клиенту или сохранению в кэше..media(): ВозвращаетPromise<ReadableStream<Uint8Array>>: преобразованный медиафайл в виде потока байтов..contentType(): ВозвращаетPromise<string>: MIME-тип выходного файла (например,video/mp4,image/jpeg,audio/mp4).
Примеры
Создание оптимизированного видеоклипа
Измените размер видео и извлеките пятисекундный фрагмент:
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();
},
};Извлечение статичного кадра
Извлечение одного кадра в виде 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
Извлеките кадр (статичное изображение) из видео, а затем используйте модель, например UForm-Gen в Workers AI чтобы создать субтитры.
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
Извлеките аудио, а затем транскрибируйте его с помощью Whisper on Workers AI.
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.
Billing
См. страницу Stream Цены сведения о стоимости. Преобразования, выполненные через binding, оплачиваются по количеству операций, а не по уникальности запросов. Для оптимальной стоимости и производительности кэшируйте или сохраняйте результаты для повторного использования.
Локальная разработка
API Media Transformations доступен в удалённом режиме для локальной разработки с помощью Wrangler, интерфейс командной строки для Workers. Операции преобразования будут выполняться с использованием удалённого ресурса и оплачиваются сверх включённого бесплатного тарифа.
Чтобы включить использование в локальной разработке, добавьте 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