Перейти к основному содержимому

Yandex Cloud Video. Часть 1

· 7 мин. чтения

Мне давно была интересна эта тема. Вот посты из 2021 года: Хранение видео в Yandex Cloud, Раздача видео из Yandex Cloud Object Storage и Ограничение доступа к HLS-видео при помощи шифрования AES-128. И хоть в них и описываются базовые принципы того как можно хранить и раздавать видео в облаке, но это лишь небольшая часть тех возможностей, которые предоставляет новый сервис.

схема сервиса

А теперь давайте посмотрим, что же там есть.

Видео

Раньше, если вы хотели разместить видео в облаке, то вам приходилось самим заботиться о транскодировании видео самим. Теперь же это можно делать прямо в облаке. Для этого вам создать ресурс Video и загрузить туда видео файл.

Кроме непосредственно видео вы можете добавить к нему метаданные, такие как название, описание, лейблы и обложку.

Вы можете ограничить доступ к видео, сделав его приватным. В таком случае доступ к видео будет доступен только после авторизации.

страница видео

Как вы видите, после загрузки видео, вы можете его просмотреть прямо в браузере. Также вы можете получить прямую ссылку на видео или код для вставки на сайт.

Про плеер можно подробнее почитать в двух постах от разработчиков на Хабре: Свой плеер для DASH: вошли и вышли, приключение на 20 минут. и Ваш плеер работает неправильно, или Как мы учили свой движок выбирать наилучшее качество видео.

Но я хочу поговорить о другом.

Создание видео

На текущий момент доступны следующие форматы видео: MP4, AVI, MKV, FLV, MOV. Но к сожалению в документации на сегодняшний день загрузка видео через API описана не очень подробно.

Воспользуйтесь вызовом gRPC API VideoService/Create.

Чтож, давайте посмотрим на этот метод.

service VideoService {
// Create video.
rpc Create(CreateVideoRequest) returns (operation.Operation) {
option (yandex.cloud.api.operation) = {
metadata: "CreateVideoMetadata"
response: "Video"
};
}
}

message CreateVideoRequest {
// ID of the channel.
string channel_id = 1;

// Video title.
string title = 2;
// Video description.
string description = 3;
// ID of the thumbnail.
string thumbnail_id = 4;

// Custom labels as `` key:value `` pairs. Maximum 64 per resource.
map<string, string> labels = 200;

// Source type.
oneof source {
// Upload video using the tus protocol.
VideoTUSDParams tusd = 1000;
}

// Video access rights.
oneof access_rights {
// Video is available to everyone.
VideoPublicAccessParams public_access = 2000;
// Checking access rights using the authorization system.
VideoAuthSystemAccessParams auth_system_access = 2002;
}
}

message VideoTUSDParams {
// File size.
int64 file_size = 1;
// File name.
string file_name = 2;
}

На первый взгляд, все просто. Но вот куда грузить файл? Для этого нам нужно внимательно посмотреть на возвращаемое сообщение.

message Video {
// ID of the video.
string id = 1;
// ID of the channel where the video was created.
string channel_id = 2;

// Video title.
string title = 3;
// Video description.
string description = 4;
// ID of the thumbnail.
string thumbnail_id = 5;

// Video status.
VideoStatus status = 6;

// Video duration. Optional, may be empty until the transcoding result is ready.
google.protobuf.Duration duration = 8;

// Video visibility status.
VisibilityStatus visibility_status = 9;

// Source type.
oneof source {
// Upload video using the tus protocol.
VideoTUSDSource tusd = 1000;
}

// Video access rights.
oneof access_rights {
// Video is available to everyone.
VideoPublicAccessRights public_access = 2000;
// Checking access rights using the authorization system.
VideoAuthSystemAccessRights auth_system_access = 2002;
}

// Time when video was created.
google.protobuf.Timestamp created_at = 100;
// Time of last video update.
google.protobuf.Timestamp updated_at = 101;

// Custom labels as `` key:value `` pairs. Maximum 64 per resource.
map<string, string> labels = 200;

enum VideoStatus {
// Video status unspecified.
VIDEO_STATUS_UNSPECIFIED = 0;
// Waiting for the whole number of bytes to be loaded.
WAIT_UPLOADING = 1;
// Video processing.
PROCESSING = 4;
// Video is ready, processing is completed.
READY = 5;
// An error occurred during video processing.
ERROR = 7;
}

enum VisibilityStatus {
// Visibility status unspecified.
VISIBILITY_STATUS_UNSPECIFIED = 0;
// Video is published and available for viewing.
PUBLISHED = 1;
// Video is unpublished, only admin can watch.
UNPUBLISHED = 2;
}
}

message VideoTUSDSource {
// URL for uploading video via the tus protocol.
string url = 1;
}

Вот, теперь мы видим, что нам нужно будет загружать файл по протоколу tus. Но что это за протокол? Возможно, вы, как и я, впервые слышите о нем.

tus — это протокол для загрузки файлов, который позволяет возобновлять загрузку после обрыва соединения.

Давайте попробуем загрузить видео через этот протокол.

Загрузка видео через tus

Сервисный аккаунт

Для начала нам нужно будет создать сервисный аккаунт от имени которого мы будем загружать видео и выдать ему права. Стоит отметить, что в ресурсной модели Облака ресурсы сервиса Video лежат, не в организации → облаке → фолдере, как большинство других ресурсов, а непосредственно в организации. Поэтому, чтобы выдать права на загрузку видео, вам нужно будет выдать права на уровне организации.

Протокол tus

По сути tus — это простой HTTP-протокол, который позволяет загружать файлы по частям.

Он состоит из core-протокола и расширений. Core-протокол определяет базовые правила продолжения загрузки после обрыва соединения. Расширения добавляют дополнительные возможности:

  • Creation — создание нового ресурса
  • Checksum — проверка целостности файла
  • Expiration — ограничение времени жизни загрузки
  • Termination — прерывание загрузки и удаление частей файла
  • Concatenation — объединение нескольких файлов в один ресурс, позволяя загружать файлы параллельно
  • CreationWithUpload — создание ресурса и загрузка файла в одном запросе

В нашем случае нам нужно Creation выполняется при помощи gRPC метода Create сервиса VideoService. В ответе на этот запрос клиент получает URL в поле tusd.url, по которому он может загружать файл.

Core-протокол tus определяет 2 метода:

HEAD — запрос информации о загрузке, чтобы узнать, смещение загруженных данных

HEAD /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: tus.example.org
Tus-Resumable: 1.0.0

В ответе сервер должен вернуть заголовок Upload-Offset смещение загруженных данных.

HTTP/1.1 200 OK
Upload-Offset: 70
Tus-Resumable: 1.0.0

После этого можно использовать метод PATCH для загрузки данных. Запрос должен содержать заголовок Content-Type: application/offset+octet-stream иначе сервер вернет ошибку 415 Unsupported Media Type. Также в запросе должен быть заголовок Upload-Offset смещение загруженных данных. Он должен быть равен занчению Upload-Offset из ответа на запрос HEAD.

PATCH /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: tus.example.org
Content-Type: application/offset+octet-stream
Content-Length: 30
Upload-Offset: 70
Tus-Resumable: 1.0.0

[remaining 30 bytes]

В ответе сервер должен вернуть заголовок Upload-Offset смещение загруженных данных.

HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
Upload-Offset: 100

Так как при создании видео через gRPC метод Create мы указываем размер файла, то сервер может проверить, что файл загружен полностью.

Подробнее о протоколе tus можно почитать в описании протокола.

Пример

Вы можете реализовать загрузку видео через tus на любом языке программирования сами или воспользоваться готовыми библиотеками. Например, на Python есть библиотека tuspy. С ее помощью вы можете загрузить видео на сервер следующим образом:

    file_stats = os.stat("cat.mp4")

# Use the client to make requests
op = video_client.Create(CreateVideoRequest(
channel_id=channel.id,
title="demo-video",
description="This is a demo video",
tusd=VideoTUSDParams(
file_size=file_stats.st_size,
file_name="cat.mp4",
),
public_access={}, # Video has public access
))

operation_result = sdk.wait_operation_and_get_result(
op,
response_type=Video,
meta_type=CreateVideoMetadata
)

video = operation_result.response

token = get_auth_token(
service_account_key=service_account_key,
)

my_client = client.TusClient(video.tusd.url,
headers={'Authorization': f"Bearer {token}"})

# Here we MUST use the URL from the video object. If you skip this parameter,
# the client will try to create a new upload and will fail with a 405 error,
# because the POST method is not allowed on the video URL.
uploader = my_client.uploader('cat.mp4', url=video.tusd.url)
uploader.upload()

Отдельно хочу подчеркнуть, что вам нужно будет передать в запросе заголовок Authorization с токеном сервисного аккаунта. А также url в Uploader чтобы он не пытался сам создать его через POST запрос.

Заключение

В этом посте я рассказал о новом сервисе Yandex Cloud Video. Я показал как можно загружать видео через tus протокол. Полный код примера вы можете найти в репозитории.