メディアにオススメ!Google 印の Transcoder API 爆誕!
はじめに
こんにちは、Customer Engineer の Dan です。Google Cloud Japan Customer Engineer Advent Calendar 2020 の 11 日目の記事は 2020/11/18 に爆誕したばかりの Transcoder API について紹介します。凄く便利なのにグローバルで驚くほど使用レポート記事がないので(涙)、使い方や使い所がイメージしやすい形でまとめてみました。少しでも興味を持ってもらえる、もしくは皆さんの参考になれば幸いです。
TL;DR
- Transcoder API の概要と特徴
- Transcoder API の基本機能と特徴的な機能の紹介
- Transcoder API の使い方
- Transcoder API に期待すること
Transcoder API とは
トランスコーディング(Transcoding)とは、映像・音声ファイルの圧縮・符号化形式(コーデック: codec)を再変更すること(再エンコード: encode)する技術のことです。動画をインターネットを通じて配信する際に、受信デバイス側が対応している最適なメディア形式(iPhone は HLS, Android は MPEG-DASH など)に変換するため、あるいは低速なインターネット環境でも視聴できるように低ビットレートの映像(500kbps の環境でもローディングアイコンがクルクル回らずに視聴できる容量)を含む様々なビットレートや解像度の映像を用意するために利用されます。
ようは、一つの元動画を、色んなメディア形式に変換する処理ですね。YouTube で画質を設定する機能があると思いますが、あれはアップロードした動画を YouTube がトランスコードしてくれてるわけです。企業が自社で動画配信を行う場合は、自前でトランスコードを行う必要があります。
Transcoder API は、Google Cloud が提供する従量課金型の Transcoding サービスで、その名の通り REST API もしくは RPC API 経由で利用することができます。
特徴
Transcoder API の最大の特徴は、Partition-Based Transcoding という並列トランスコーディング技術による高速なトランスコードになります。処理は、下記のように行われます。
- トランスコード対象の動画を解析し、適切な並列処理数を決定
- 元動画ファイルを複数のチャンクファイルに分割
- それぞれのチャンクファイルを別インスタンス上で並列トランスコード
- 最終的にすべてのトランスコード結果を再結合・一本化
3の並列トランスコード処理部分がまさにクラウドコンピューティングの力を最大限に活用しているところで、必要となるインスタンスを瞬時に起動・停止できる Google Cloud のコンピューティング技術がこの高速トランスコーディングを可能にしているわけです。
参考までに、私が検証した結果を記載します。処理時間はエンコーディング方式(1-pass encoding / 2-pass encoding 等)や入出力の映像フォーマット(ビットレート、プロファイル 等)や数により変わりますので、あくまで参考情報としてご参照ください。
入力映像
mp4 (映像:1920x1080, h.264[main profile], 6Mbps、音声:128kbps, 48kHz, AAC)
出力映像 (2 pass encoding)
- SD 画質の mp4 (映像:640x360, h.264[high profile, very fast], 550kbps[VBR]、音声:64kbps, 48kHz)
- HD 画質の mp4 (映像:1280x720, h.264[high profile, very fast], 2.5Mbps[VBR]、音声:64kbps, 48kHz)
- HLS [TS](映像、音声は上記 SD/HD 設定同様)
- MPEG-DASH [fmp4](映像、音声は上記 SD/HD 設定同様)
結果(表)
結果(グラフ)
一般的な PC スペックで同じような処理を行うと、動画尺の数倍の時間がかかりますが、Transcoder API だと 1 時間映像が 5 分弱、2 時間映像でも 8 分弱でトランスコードが完了しています。その処理の高速さと、また動画尺が長いほど高速化の恩恵を受けられていることが分かります。
動画のトランスコーディング処理時間が長いと動画の公開を急いでるときに間に合わず公開が遅れたりするので、早い=正義ですね。
基本機能
Transcoder API が現時点で提供する基本機能は下記になります。
- 様々なコーデック(H.264, VP9, HEVC 等)に対応 ※詳細は サポートされている入出力形式 を参照
- ストリーミングフォーマット対応(HLS/MPEG-DASH)
- HDR10 サポート
- 広告用 SCTE マーカー対応
- 画像のオーバーレイ表示
- DRM 暗号化対応:Widevine, Fairplay, Playready(CENC, AES-128, Sample-AES)
- 画像抽出:スプライト画像生成
- 編集リスト機能
太字の機能が他トランスコーダーには中々ない、Transcoder API の特徴的な機能ですので、紹介します。
画像抽出:スプライト画像生成
シーンごとの画像を一枚画像(スプライト画像)として生成する機能を Transcoder API の一機能として利用できます。動画プレイヤーのシークバーにカーソルを合わせるとそのシーンの映像がプレビュー画像表示される機能(下図)を YouTube を始め多くの動画配信サービスでも見かけたことがあると思いますが、あのような機能を実装する際に必要な画像ですね。この画像生成機能を、 ffmpeg や ImageMagic などで自前で実装している方も多いと思いますので、非常に便利だと感じてもらえるのではないでしょうか。
スプライト画像の作成方法は、下記のように生成するサムネイルの数を指定する方法と、
"spriteSheets": [
{
"filePrefix": "large-sprite-sheets",
"spriteHeightPixels": 72,
"spriteWidthPixels": 128,
"columnCount": 10,
"rowCount": 10,
"totalCount": 100
}
]
サムネイルを生成する間隔を指定する方法があります。
"spriteSheets": [
{
"filePrefix": "large-sprite-sheets",
"spriteHeightPixels": 72,
"spriteWidthPixels": 128,
"interval": "7s"
}
]
編集リスト機能
編集リスト(Edit List)機能は、入力映像として複数のファイルを指定し、結合する順番とそのタイムコードを指定すると、その順序でファイルを結合することが可能な機能です。
ユースケースとしては、下記のような用途が考えられます。
- バンパーやトレイラーの焼き込み
- 映像内の不要なシーンのトリミング
- 様々な映像を利用した簡易編集
API ベースでこういった動画の簡易編集を実装できるのは動画編集フローの負荷軽減につながりますね。バンパー焼き込むだけにノンリニア編集機立ち上げて編集→レンダリング→エクスポートして再度トランスコーディングなんて煩雑すぎますよね。。。
使い方(編集リストの指定)を説明します。下記の設定では、
- 6 秒のバンパー素材(bumper_6sec.mp4) を先頭に配置
- 素材1つ目(input_file0.mp4)の 0 ~ 60秒地点を結合
- 素材 2 つ目(input_file1.mp4)の 240.5 ~ 360秒地点を結合
と3つの素材を編集・連結させ、出力として 1280x720 h.264 mp4 ファイル(editlist.mp4)を設定しています。これをトランスコード テンプレート ジョブとして登録して利用します(トランスコード テンプレートの登録方法は後述)。タイムオフセット値は小数点 9 桁まで指定できるので、実質フレーム精度で指定可能という理解でよいかと思います。
{
"config": {
"inputs": [
{
"key": "bumper",
"uri": "gs://PROJECT_ID/bumper_6sec.mp4"
},
{
"key": "input0",
"uri": "gs://PROJECT_ID/input_file0.mp4"
},
{
"key": "input1",
"uri": "gs://PROJECT_ID/input_file1.mp4"
}
],
"editList": [
{
"key": "atom0",
"inputs": ["bumper"],
"startTimeOffset": "0s",
"endTimeOffset": "6s"
},
{
"key": "atom1",
"inputs": ["input0"],
"startTimeOffset": "0s",
"endTimeOffset": "60s"
},
{
"key": "atom2",
"inputs": ["input1"],
"startTimeOffset": "240.5s",
"endTimeOffset": "360s"
}
],
"elementaryStreams": [
{
"videoStream": {
"codec": "h264",
"profile": "high",
"preset": "veryfast",
"heightPixels": 360,
"widthPixels": 640,
"pixelFormat": "yuv420p",
"bitrateBps": 550000,
"rateControlMode": "vbr",
"enableTwoPass": true,
"crfLevel": 21,
"vbvSizeBits": 550000,
"vbvFullnessBits": 495000,
"gopDuration": "3s",
"entropyCoder": "cabac",
"bFrameCount": 3,
"frameRate": 30,
"aqStrength": 1
},
"key": "video-stream0"
},
{
"audioStream": {
"codec": "aac",
"bitrateBps": 64000,
"channelCount": 2,
"channelLayout": [
"fl",
"fr"
],
"sampleRateHertz": 48000
},
"key": "audio-stream0"
}
],
"muxStreams": [
{
"key": "editlist",
"fileName": "editlist.mp4",
"container": "mp4",
"elementaryStreams": [
"video-stream0",
"audio-stream0"
]
}
],
"output": {
"uri": "gs://PROJECT_ID/transcoded/"
}
}
}
また、上記ファイル内で input と output の設定も追加したので、編集リスト ジョブ実行時には下記のように編集リスト用のテンプレート ID のみを指定するようにします。(ジョブ実行方法は後述)
{
"templateId": "TEMPLATE_ID"
}
Transcoder API の使い方
勢い余って機能説明と合わせて設定方法を先に説明してしまいましたが、ここからは改めて Transcoder API の基本的な使い方について説明していきます。
前作業
前作業として、下記を行っておく必要があります。詳細な手順についてはこちらのページ(Transcoder API クイックスタート)をご参照ください。
- Google アカウントの作成
- Google Cloud プロジェクトの作成と、課金の有効化
- サービスアカウントを作成し、プロジェクトオーナー権限の付与
- 環境変数 GOOGLE_APPLICATION_CREDENTIALS の設定
- Transcoder API の有効化
- Cloud Storage 上に動画保存用バケット・フォルダの作成
トランスコードの流れ
大枠の流れは下記になります。
- トランスコード テンプレートの作成
- Cloud Storage 上に動画アップロード
- トランスコード ジョブの実行
- トランスコード ジョブの実行結果の確認
それぞれ、実例を上げながら説明していきたます。
トランスコード処理内容を記載したトランスコード テンプレートの作成
Transcoder API ではトランスコード処理内容をジョブテンプレートという形で管理します。出力ファイル形式やビットレート、また前述の機能も json 形式で定義します。いわゆるプリセット機能ですね。ジョブ実行時には処理対象となる Cloud Storage 上の動画とこのジョブ テンプレートを指定します。
参考までに、一つの入力動画を下記のファイル形式にトランスコードするジョブ テンプレートを記載します。
- SD 画質の mp4 (映像:640x360, h.264[high profile], 550kbps[VBR]、音声:64kbps, 48kHz, AAC)
- HD 画質の mp4 (映像:1280x720, h.264[high profile], 2.5Mbps[VBR]、音声:64kbps, 48kHz, AAC)
- HLS [TS](映像、音声は上記 SD/HD 設定同様)
- MPEG-DASH [fmp4](映像、音声は上記 SD/HD 設定同様)
上記ファイルを出力するジョブ テンプレートは下記のような形式となります。今回はこの内容を job_template.json として保存しました。
映像、音声それぞれの ES (Elementary Stream) のプロファイルを記載し、その ES を MUX(重畳)して映像ファイル(mp4 等)やストリーミング配信用フォーマット(HLS/MPEG-DASH)を作成しています。
{
"config": {
"elementaryStreams": [
{
"videoStream": {
"codec": "h264",
"profile": "high",
"preset": "veryfast",
"heightPixels": 360,
"widthPixels": 640,
"pixelFormat": "yuv420p",
"bitrateBps": 550000,
"rateControlMode": "vbr",
"enableTwoPass": true,
"crfLevel": 21,
"vbvSizeBits": 550000,
"vbvFullnessBits": 495000,
"gopDuration": "3s",
"entropyCoder": "cabac",
"bFrameCount": 3,
"frameRate": 30,
"aqStrength": 1
},
"key": "video-stream0"
},
{
"videoStream": {
"codec": "h264",
"profile": "high",
"preset": "veryfast",
"heightPixels": 720,
"widthPixels": 1280,
"pixelFormat": "yuv420p",
"bitrateBps": 2500000,
"rateControlMode": "vbr",
"enableTwoPass": true,
"crfLevel": 21,
"vbvSizeBits": 2500000,
"vbvFullnessBits": 2250000,
"gopDuration": "3s",
"entropyCoder": "cabac",
"bFrameCount": 3,
"frameRate": 30,
"aqStrength": 1
},
"key": "video-stream1"
},
{
"audioStream": {
"codec": "aac",
"bitrateBps": 64000,
"channelCount": 2,
"channelLayout": [
"fl",
"fr"
],
"sampleRateHertz": 48000
},
"key": "audio-stream0"
}
],
"muxStreams": [
{
"key": "sd",
"fileName": "sd.mp4",
"container": "mp4",
"elementaryStreams": [
"video-stream0",
"audio-stream0"
]
},
{
"key": "hd",
"fileName": "hd.mp4",
"container": "mp4",
"elementaryStreams": [
"video-stream1",
"audio-stream0"
]
},
{
"key": "media-sd",
"fileName": "media-sd.ts",
"container": "ts",
"elementaryStreams": [
"video-stream0",
"audio-stream0"
]
},
{
"key": "media-hd",
"fileName": "media-hd.ts",
"container": "ts",
"elementaryStreams": [
"video-stream1",
"audio-stream0"
]
},
{
"key": "video-only-sd",
"fileName": "video-only-sd.m4s",
"container": "fmp4",
"elementaryStreams": [
"video-stream0"
]
},
{
"key": "video-only-hd",
"fileName": "video-only-hd.m4s",
"container": "fmp4",
"elementaryStreams": [
"video-stream1"
]
},
{
"key": "audio-only",
"fileName": "audio-only.m4s",
"container": "fmp4",
"elementaryStreams": [
"audio-stream0"
]
}
],
"manifests": [
{
"fileName": "manifest.m3u8",
"type": "HLS",
"muxStreams": [
"media-sd",
"media-hd"
]
},
{
"fileName": "manifest.mpd",
"type": "DASH",
"muxStreams": [
"video-only-sd",
"video-only-hd",
"audio-only"
]
}
]
}
}
ジョブ テンプレートが保存できたら、ジョブ テンプレート作成用の API に対して先程のファイル(job_template.json)を引数に設定して POST リクエストを実行します。下記は curl でのリクエスト例で、コマンド内の黒字部分はご自身の環境に応じて適切に変更してください。
TEMPLATE_ID は、 4~63 文字で、正規表現 [a-zA-Z][a-zA-Z0–9_-]* にマッチする文字列を設定可能です。ご自身が管理しやすい名称をつけるようにしてください。
LOCATION は Transcoder API 利用可能なリージョン を参照の上、設定してください。現状では日本リージョンはサポートしておらず、日本から最も近いリージョンは台湾リージョン(asia-east1)になります。もちろん、Transcoder API と動画の入出力に利用する Cloud Storage のリージョンが違っても問題なく動作しますのでご安心ください。
curl -X POST \ -H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \ -H "Content-Type: application/json; charset=utf-8" \ -d @job_template.json \ https://transcoder.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/jobTemplates?jobTemplateId=TEMPLATE_ID
Cloud Storage 上に動画アップロード
ジョブ テンプレートの作成が完成しましたら、Cloud Storage に動画をアップします。下記のようなフォーマットの動画パスをメモしておきます。
gs://{バケット名}/{フォルダ名}/{ファイル名}
トランスコード ジョブの実行
ジョブ実行には、下記情報が必要になります。
- (Cloud Storage 上の)入力ファイル URI
- (Cloud Storage 上の)ファイル出力フォルダ URI
- ジョブ テンプレートID
サンプルとしては下記のような json ファイル形式になります。今回は curl で実行する際にこの情報をファイルで指定するので、request.json として保存しておきます。templateId には前述のジョブ テンプレート作成時に指定したものを設定します。
{
"inputUri": "gs://{BUCKET_NAME}/{FOLDER_NAME}/{FILE_NAME}",
"outputUri": "gs://{OUTPUT_BUCKET_NAME}/{OUTPUT_FOLDER_NAME}/",
"templateId": "{TEMPLATE_ID}"
}
そして、ジョブの実行には下記のような形で実行します。
curl -X POST \
-H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \
-H "Content-Type: application/json; charset=utf-8" \
-d @request.json \
https://transcoder.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/jobs
トランスコード ジョブの実行結果の確認
ジョブが受け付けれれると json レスポンスに “name”: “projects/project-number/locations/LOCATION/jobs/JOB_ID” という形式で JOB_ID が返却されます。この JOB_ID を使って以下のようにジョブのステータスを確認することができます。
curl -X GET \
-H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \
https://transcoder.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/jobs/JOB_ID
ジョブの結果は下記のような形で返却されます。(詳細はこちらを参照)ステータスは state 要素で返却され、取りうる値は下記になります。
- PROCESSING_STATE_UNSPECIFIED :処理状態が未確定
- PENDING :実行待ち(キューイング中)
- RUNNING :実行中
- SUCCEEDED :完了(成功)
- FAILED :完了(失敗)詳細は failureReason や failureDetails を参照
{
"name": string,
"inputUri": string,
"outputUri": string,
"priority": integer,
"originUri": {
object (OriginUri)
},
"state": enum (ProcessingState),
"progress": {
object (Progress)
},
"failureReason": string,
"failureDetails": [
{
object (FailureDetail)
}
],
"createTime": string,
"startTime": string,
"endTime": string,
"templateId": string,
"config": {
object (JobConfig)
}
}
実行中のジョブのリスト取得やジョブの削除の API もありますので、詳しくは Transcoder API ジョブの作成と管理 をご参照ください。
また、前述のスプライト画像機能や編集リスト機能などを利用したい場合は、ジョブ テンプレートに設定を追記していくことで利用できます。フォーマットは Transcoder API JobConfig をご参照ください。
ジョブ ステータスの更新通知(Pub/Sub トピック)
ジョブのステータスに更新があった場合、その通知は Pub/Sub 経由で受け取ることもできます。長い動画だと処理終了まで 5 分以上もかかるので、前述の API でポーリング監視するのは煩雑という観点もありますが、Pub/Sub 経由で更新通知を受け取ることは、後段の処理フローに連携できるメリットが大きいです。例えば以下のようなユースケースが考えられます。
- ジョブ エラー時にメールや slack 通知(Pub/Sub -> Cloud Functions -> Slack Incoming Webhook -> Slack Channel)
- トランスコードした動画のコンテンツ品質チェック(QC:Quality Check)
- トランスコードした動画を掲載するページの公開
Pub/Sub 経由の更新通知の受け取り方の流れは下記です。
1.Pub/Sub トピックの作成
管理コンソールでも作成できますが、下記は gcloud コマンドで作成する場合の説明を記載します。
最初に Pub/Sub トピックを作成します。
gcloud pubsub topics create TOPIC_ID
作成した Pub/Sub トピック名の一覧を参照します。
gcloud pubsub topics list
トピック名 (TOPIC_NAME)は下記のような名前になりますのでメモしておきます。
projects/PROJECT_ID/topics/TOPIC_ID
2. Pub/Sub サブスクリプションの作成
次に Pub/Sub の通知を受けるサブスクリプションを作成します。
gcloud pubsub subscriptions create SUBSCRIPTION_ID --topic=projects/PROJECT_ID/topics/TOPIC_NAME
作られるサブスクリプション名(SUBSCRIPTION_NAME)は下記のような形になります。
projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID
3.ジョブ コンフィグに Pub/Sub トピックを設定
Transcoder API のジョブ コンフィグに pubsubDestination 項目を追加し、先程のトピック名を設定します。これで Transcoder API のジョブ ステータス内容の更新通知が Pub/Sub で受け取ることができるようになります。
{
"config": {
(略)
"pubsubDestination": {
"topic": "projects/PROJECT_ID/topics/TOPIC_NAME"
},
(略)
}
}
実際に上記 pubsubDestination 項目を追加したジョブ コンフィグで実行した後、Pub/Sub サブスクリプションで結果を取得してみたいと思います。
gcloud pubsub subscriptions pull --auto-ack projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID
結果は下図のような形で取得できます。
画像が小さくて分かりづらいので DATA 部分だけ抜き出すと、下記のような json 形式で取得できます。(下記は成功時の例)
{
"job":{
"name":
"projects/PROJECT_ID/locations/LOCATION/jobs/JOB_ID",
"state":"SUCCEEDED",
"failureReason":null
}
}
この結果を元に後段のワークフローを動かす場合は、JOB_ID を元にトランスコード処理完了済みファイルを取得し作業していくことになります。
Transcoder API に期待すること
(ポジショントークではない、いち個人の意見です。プロダクトのロードマップとは関係ありません)
トランスコード ソリューション領域は他クラウド事業者や専用のソリューションベンダーがすでに提供しているレッドオーシャンであり、その中でも後発ということもあり、Transcoder API は機能や対応コーデックを絞りつつ、 OTT サービス事業者向けに専用の機能の実装やパフォーマンスを重視して提供しているイメージを受けました。今後の継続的な開発により、対応コーデック(AV1, VC-1, VVC)の拡充や新しいエンコード技術の対応(3-pass Encoding, Per-Title Encoding, AI Encoding)もされていくと思いますが、やはり今回ご紹介したスプライト画像生成や編集リスト機能のような Transcoder を中心としたメディアワークフローの負荷軽減を図る機能は事業者が楽になるソリューションなので、”個人的には” そういった機能の拡充に期待しています。簡易的な QCレポート (トランスコードしたファイルの品質レポート)とかあると、トランスコード処理後の映像で破綻してるシーンがないか簡易チェックできて嬉しいなーと |・ω・*)チラ
終わりに
今回は Transcoder API の特徴や使い方について解説しました。使いやすい API ベース、従量課金、高パフォーマンス、の他、動画配信ビジネスに活用しやすい機能も充実しており、まさに OTT 事業者向けのクラウド トランスコーダーだということがご理解いただけたかと思います。この記事を参考にぜひ一度使ってみてください。(そして使用レポートを…ぜひ…)
明日 2020–12–12 は yutty さんによる ” Cloud Workflows で実現するサーバーレスなワークフロー” です。Cloud Workflows はメディアワークフローの構築にも役立つサービスですので、ぜひご覧ください!