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

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 протокол. Полный код примера вы можете найти в репозитории.