クローズド キャプションと字幕の追加

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

このページでは、出力動画にクローズド キャプションと字幕を追加する方法について説明します。

クローズド キャプションとは、動画内の音声の視覚表示です。一般的に、クローズド キャプションは音声と同じ言語で、背景のサウンドや話者の変更を含みます。

字幕は通常、動画の会話を別の言語に翻訳します。字幕には通常、背景音や話者の変化は含まれません。

このページでは、字幕という用語をクローズド キャプションと字幕の両方の意味で使用します。

ジョブ構成に字幕を追加する

字幕を出力動画ファイル コンテナに埋め込むか、マニフェストから再生できる独立したスタンドアロンの字幕ファイルを作成するかを選択できます。サポートされている入力字幕ファイル形式については、サポートされている入出力形式の表をご覧ください。構成をテストするためのサンプル動画ファイルと字幕ファイルが用意されています。

次のセクションの情報を使用して、ジョブ構成に字幕を追加します。このページは、基本的な JobConfig に精通していることを前提としています。コード変換ジョブの作成の詳細については、ジョブの作成と管理をご覧ください。

埋め込み字幕

出力動画ファイル コンテナに字幕を埋め込むには、次の操作を行います。

  1. ジョブ構成の先頭に inputs 配列を追加します。

  2. 関連付けられた入力動画のキーと URI を定義する Input オブジェクトを inputs 配列に追加します。

  3. 入力字幕ファイルへのパスを含む別の Input オブジェクトを追加します。

  4. editList 配列をジョブ構成に追加します。この配列は、出力動画タイムラインに入力を追加するために使用されます。

  5. EditAtom オブジェクトを editList 配列に追加します。この EditAtom オブジェクトは、inputs 配列に追加した入力動画と字幕のキーを参照する必要があります。startTimeOffsetendTimeOffset を指定して、入力動画をトリミングできます。

  6. 出力コンテナに字幕を追加するには、textStream オブジェクトを elementaryStreams 配列に追加します。サポートされる埋め込みテキスト ストリームは 1 つのみで、すべての出力動画に追加されます(出力タイムラインが 1 つのみであるため)。

  7. mapping 配列を textStream 構成オブジェクトで使用して、EditAtom オブジェクト キーを参照します。

次の構成例では、動画に CEA-608 クローズド キャプションを埋め込んでいます。

"inputs": [
  {
    "key": "input0",
    "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO"
  },
  {
    "key": "caption_input0",
    "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_CAPTIONS_FILE"
  }
],
"editList": [
  {
    "key": "atom0",
    "inputs": [
      "input0",
      "caption_input0"
    ]
  }
],
"elementaryStreams": [
  {
    "key": "cea-stream0",
    "textStream": {
      "codec": "cea608",
      "mapping": [
        {
          "atomKey": "atom0",
          "inputKey": "caption_input0",
          "inputTrack": 0
        }
      ]
    }
  }
],

この構成をジョブ テンプレートに追加するか、アドホック ジョブ構成に含めることができます。

REST とコマンドライン

リクエストのデータを使用する前に、次のように置き換えます。

  • PROJECT_ID: IAM 設定に載っている Google Cloud プロジェクト ID。
  • LOCATION: ジョブを実行するロケーション。サポートされているリージョンのいずれかを使用します。
    • us-central1
    • us-west1
    • us-west2
    • us-east1
    • us-east4
    • southamerica-east1
    • asia-east1
    • asia-south1
    • asia-southeast1
    • europe-west1
    • europe-west2
    • europe-west4
  • STORAGE_BUCKET_NAME: 作成した Cloud Storage バケットの名前。
  • STORAGE_INPUT_VIDEO: コード変換する Cloud Storage バケット内の動画の名前(my-vid.mp4 など)。このフィールドでは、バケットで作成したフォルダ(input/my-vid.mp4 など)を考慮する必要があります。
  • STORAGE_CAPTIONS_FILE: Cloud Storage バケット内の字幕ファイルの名前(my-captions.srt など)。 このフィールドでは、バケットで作成したフォルダ(input/my-captions.srt など)を考慮する必要があります。
  • STORAGE_OUTPUT_FOLDER: エンコードされた動画出力を保存する Cloud Storage フォルダ名。

JSON 本文のリクエスト

{
  "config": {
    "inputs": [
      {
        "key": "input0",
        "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO"
      },
      {
        "key": "caption_input0",
        "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_CAPTIONS_FILE"
      }
    ],
    "editList": [
      {
        "key": "atom0",
        "inputs": [
          "input0",
          "caption_input0"
        ]
      }
    ],
    "elementaryStreams": [
      {
        "key": "video-stream0",
        "videoStream": {
          "h264": {
            "heightPixels": 360,
            "widthPixels": 640,
            "bitrateBps": 550000,
            "frameRate": 60
          }
        }
      },
      {
        "key": "audio-stream0",
        "audioStream": {
          "codec": "aac",
          "bitrateBps": 64000
        }
      },
      {
        "key": "cea-stream0",
        "textStream": {
          "codec": "cea608",
          "mapping": [
            {
              "atomKey": "atom0",
              "inputKey": "caption_input0",
              "inputTrack": 0
            }
          ]
        }
      }
    ],
    "muxStreams": [
      {
        "key": "sd",
        "container": "mp4",
        "elementaryStreams": [
          "video-stream0",
          "audio-stream0"
        ]
      },
      {
        "key": "sd-hls",
        "container": "ts",
        "elementaryStreams": [
          "video-stream0",
          "audio-stream0"
        ]
      },
      {
        "key": "sd-dash",
        "container": "fmp4",
        "elementaryStreams": [
          "video-stream0"
        ]
      },
      {
        "key": "audio-dash",
        "container": "fmp4",
        "elementaryStreams": [
          "audio-stream0"
        ]
      }
    ],
    "manifests": [
      {
        "fileName": "manifest.m3u8",
        "type": "HLS",
        "muxStreams": [
          "sd-hls"
        ]
      },
      {
        "fileName": "manifest.mpd",
        "type": "DASH",
        "muxStreams": [
          "sd-dash",
          "audio-dash"
        ]
      }
    ],
    "output": {
      "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_OUTPUT_FOLDER/"
    }
  }
}

リクエストを送信するには、次のいずれかのオプションを展開します。

次のような JSON レスポンスが返されます。

{
  "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
  "config": {
   ...
  },
  "state": "PENDING",
  "createTime": CREATE_TIME,
  "ttlAfterCompletionDays": 30
}

gcloud

  1. ジョブ フィールドを定義する request.json ファイルを作成します。gcloud コマンドを次のように置き換えます。
    • STORAGE_BUCKET_NAME: 作成した Cloud Storage バケットの名前。
    • STORAGE_INPUT_VIDEO: コード変換する Cloud Storage バケット内の動画の名前(my-vid.mp4 など)。このフィールドでは、バケットで作成したフォルダ(input/my-vid.mp4 など)を考慮する必要があります。
    • LOCATION: ジョブを実行するロケーション。次に挙げるロケーションの 1 つを使用します。
      • us-central1
      • us-west1
      • us-west2
      • us-east1
      • us-east4
      • southamerica-east1
      • asia-east1
      • asia-south1
      • asia-southeast1
      • europe-west1
      • europe-west2
      • europe-west4
    • STORAGE_CAPTIONS_FILE: Cloud Storage バケット内の字幕ファイルの名前(my-captions.srt など)。 このフィールドでは、バケットで作成したフォルダ(input/my-captions.srt など)を考慮する必要があります。
    • STORAGE_OUTPUT_FOLDER: エンコードされた動画出力を保存する Cloud Storage フォルダ名。
    {
      "config": {
        "inputs": [
          {
            "key": "input0",
            "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO"
          },
          {
            "key": "caption_input0",
            "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_CAPTIONS_FILE"
          }
        ],
        "editList": [
          {
            "key": "atom0",
            "inputs": [
              "input0",
              "caption_input0"
            ]
          }
        ],
        "elementaryStreams": [
          {
            "key": "video-stream0",
            "videoStream": {
              "h264": {
                "heightPixels": 360,
                "widthPixels": 640,
                "bitrateBps": 550000,
                "frameRate": 60
              }
            }
          },
          {
            "key": "audio-stream0",
            "audioStream": {
              "codec": "aac",
              "bitrateBps": 64000
            }
          },
          {
            "key": "cea-stream0",
            "textStream": {
              "codec": "cea608",
              "mapping": [
                {
                  "atomKey": "atom0",
                  "inputKey": "caption_input0",
                  "inputTrack": 0
                }
              ]
            }
          }
        ],
        "muxStreams": [
          {
            "key": "sd",
            "container": "mp4",
            "elementaryStreams": [
              "video-stream0",
              "audio-stream0"
            ]
          },
          {
            "key": "sd-hls",
            "container": "ts",
            "elementaryStreams": [
              "video-stream0",
              "audio-stream0"
            ]
          },
          {
            "key": "sd-dash",
            "container": "fmp4",
            "elementaryStreams": [
              "video-stream0"
            ]
          },
          {
            "key": "audio-dash",
            "container": "fmp4",
            "elementaryStreams": [
              "audio-stream0"
            ]
          }
        ],
        "manifests": [
          {
            "fileName": "manifest.m3u8",
            "type": "HLS",
            "muxStreams": [
              "sd-hls"
            ]
          },
          {
            "fileName": "manifest.mpd",
            "type": "DASH",
            "muxStreams": [
              "sd-dash",
              "audio-dash"
            ]
          }
        ],
        "output": {
          "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_OUTPUT_FOLDER/"
        }
      }
    }
    
  2. 次のコマンドを実行します。
    gcloud transcoder jobs create --location=LOCATION --file="request.json"
    
    次のようなレスポンスが表示されます。
    {
      "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
      "config": {
       ...
      },
      "state": "PENDING",
      "createTime": CREATE_TIME,
      "ttlAfterCompletionDays": 30
    }
    

Go

このサンプルを試す前に、クライアント ライブラリを使用した Transcoder API クイックスタートにある Go の設定手順を行ってください。 詳細については、Transcoder API Go の API のリファレンス ドキュメントをご覧ください。

import (
	"context"
	"fmt"
	"io"

	transcoder "cloud.google.com/go/video/transcoder/apiv1"
	transcoderpb "google.golang.org/genproto/googleapis/cloud/video/transcoder/v1"
)

// createJobWithEmbeddedCaptions creates a job that embeds captions in the
// output video. See https://cloud.google.com/transcoder/docs/how-to/captions-and-subtitles
// for more information.
func createJobWithEmbeddedCaptions(w io.Writer, projectID string, location string, inputVideoURI string, inputCaptionsURI string, outputURI string) error {
	// projectID := "my-project-id"
	// location := "us-central1"
	// inputVideoURI := "gs://my-bucket/my-video-file"
	// inputCaptionsURI := "gs://my-bucket/my-captions-file"
	// outputURI := "gs://my-bucket/my-output-folder/"

	ctx := context.Background()
	client, err := transcoder.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("NewClient: %v", err)
	}
	defer client.Close()

	req := &transcoderpb.CreateJobRequest{
		Parent: fmt.Sprintf("projects/%s/locations/%s", projectID, location),
		Job: &transcoderpb.Job{
			OutputUri: outputURI,
			JobConfig: &transcoderpb.Job_Config{
				Config: &transcoderpb.JobConfig{
					Inputs: []*transcoderpb.Input{
						{
							Key: "input0",
							Uri: inputVideoURI,
						},
						{
							Key: "caption_input0",
							Uri: inputCaptionsURI,
						},
					},
					EditList: []*transcoderpb.EditAtom{
						{
							Key:    "atom0",
							Inputs: []string{"input0", "caption_input0"},
						},
					},
					ElementaryStreams: []*transcoderpb.ElementaryStream{
						{
							Key: "video_stream0",
							ElementaryStream: &transcoderpb.ElementaryStream_VideoStream{
								VideoStream: &transcoderpb.VideoStream{
									CodecSettings: &transcoderpb.VideoStream_H264{
										H264: &transcoderpb.VideoStream_H264CodecSettings{
											BitrateBps:   550000,
											FrameRate:    60,
											HeightPixels: 360,
											WidthPixels:  640,
										},
									},
								},
							},
						},
						{
							Key: "audio_stream0",
							ElementaryStream: &transcoderpb.ElementaryStream_AudioStream{
								AudioStream: &transcoderpb.AudioStream{
									Codec:      "aac",
									BitrateBps: 64000,
								},
							},
						},
						{
							Key: "cea-stream0",
							ElementaryStream: &transcoderpb.ElementaryStream_TextStream{
								TextStream: &transcoderpb.TextStream{
									Codec: "cea608",
									Mapping: []*transcoderpb.TextStream_TextMapping{
										{
											AtomKey:    "atom0",
											InputKey:   "caption_input0",
											InputTrack: 0,
										},
									},
								},
							},
						},
					},
					MuxStreams: []*transcoderpb.MuxStream{
						{
							Key:               "sd",
							Container:         "mp4",
							ElementaryStreams: []string{"video_stream0", "audio_stream0"},
						},
						{
							Key:               "sd-hls",
							Container:         "ts",
							ElementaryStreams: []string{"video_stream0", "audio_stream0"},
						},
						{
							Key:               "sd-dash",
							Container:         "fmp4",
							ElementaryStreams: []string{"video_stream0"},
						},
						{
							Key:               "audio-dash",
							Container:         "fmp4",
							ElementaryStreams: []string{"audio_stream0"},
						},
					},
					Manifests: []*transcoderpb.Manifest{
						{
							FileName:   "manifest.m3u8",
							Type:       transcoderpb.Manifest_HLS,
							MuxStreams: []string{"sd-hls"},
						},
						{
							FileName:   "manifest.mpd",
							Type:       transcoderpb.Manifest_DASH,
							MuxStreams: []string{"sd-dash", "audio-dash"},
						},
					},
				},
			},
		},
	}
	// Creates the job. Jobs take a variable amount of time to run.
	// You can query for the job state; see getJob() in get_job.go.
	response, err := client.CreateJob(ctx, req)
	if err != nil {
		return fmt.Errorf("CreateJob: %v", err)
	}

	fmt.Fprintf(w, "Job: %v", response.GetName())
	return nil
}

Java

このサンプルを試す前に、クライアント ライブラリを使用した Transcoder API クイックスタートにある Java の設定手順を行ってください。 詳細については、Transcoder API Java の API のリファレンス ドキュメントをご覧ください。


import com.google.cloud.video.transcoder.v1.AudioStream;
import com.google.cloud.video.transcoder.v1.CreateJobRequest;
import com.google.cloud.video.transcoder.v1.EditAtom;
import com.google.cloud.video.transcoder.v1.ElementaryStream;
import com.google.cloud.video.transcoder.v1.Input;
import com.google.cloud.video.transcoder.v1.Job;
import com.google.cloud.video.transcoder.v1.JobConfig;
import com.google.cloud.video.transcoder.v1.LocationName;
import com.google.cloud.video.transcoder.v1.Manifest;
import com.google.cloud.video.transcoder.v1.Manifest.ManifestType;
import com.google.cloud.video.transcoder.v1.MuxStream;
import com.google.cloud.video.transcoder.v1.Output;
import com.google.cloud.video.transcoder.v1.TextStream;
import com.google.cloud.video.transcoder.v1.TextStream.TextMapping;
import com.google.cloud.video.transcoder.v1.TranscoderServiceClient;
import com.google.cloud.video.transcoder.v1.VideoStream;
import java.io.IOException;

public class CreateJobWithEmbeddedCaptions {

  public static void main(String[] args) throws IOException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project-id";
    String location = "us-central1";
    String inputVideoUri = "gs://my-bucket/my-video-file";
    String inputCaptionsUri = "gs://my-bucket/my-captions-file";
    String outputUri = "gs://my-bucket/my-output-folder/";

    createJobWithEmbeddedCaptions(projectId, location, inputVideoUri, inputCaptionsUri, outputUri);
  }

  // Creates a job from an ad-hoc configuration that embeds captions in the output video.
  public static void createJobWithEmbeddedCaptions(
      String projectId,
      String location,
      String inputVideoUri,
      String inputCaptionsUri,
      String outputUri)
      throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (TranscoderServiceClient transcoderServiceClient = TranscoderServiceClient.create()) {

      VideoStream videoStream0 =
          VideoStream.newBuilder()
              .setH264(
                  VideoStream.H264CodecSettings.newBuilder()
                      .setBitrateBps(550000)
                      .setFrameRate(60)
                      .setHeightPixels(360)
                      .setWidthPixels(640))
              .build();

      AudioStream audioStream0 =
          AudioStream.newBuilder().setCodec("aac").setBitrateBps(64000).build();

      TextStream textStream0 =
          TextStream.newBuilder()
              .setCodec("cea608")
              .addMapping(
                  0,
                  TextMapping.newBuilder()
                      .setAtomKey("atom0")
                      .setInputKey("caption_input0")
                      .setInputTrack(0)
                      .build())
              .build();

      JobConfig config =
          JobConfig.newBuilder()
              .addInputs(Input.newBuilder().setKey("input0").setUri(inputVideoUri))
              .addInputs(Input.newBuilder().setKey("caption_input0").setUri(inputCaptionsUri))
              .addEditList(
                  0, // Index in the edit list
                  EditAtom.newBuilder()
                      .setKey("atom0")
                      .addInputs("input0")
                      .addInputs("caption_input0")
                      .build())
              .setOutput(Output.newBuilder().setUri(outputUri))
              .addElementaryStreams(
                  ElementaryStream.newBuilder()
                      .setKey("video_stream0")
                      .setVideoStream(videoStream0))
              .addElementaryStreams(
                  ElementaryStream.newBuilder()
                      .setKey("audio_stream0")
                      .setAudioStream(audioStream0))
              .addElementaryStreams(
                  ElementaryStream.newBuilder().setKey("cea_stream0").setTextStream(textStream0))
              .addMuxStreams(
                  0,
                  MuxStream.newBuilder()
                      .setKey("sd")
                      .setContainer("mp4")
                      .addElementaryStreams("video_stream0")
                      .addElementaryStreams("audio_stream0")
                      .build())
              .addMuxStreams(
                  1,
                  MuxStream.newBuilder()
                      .setKey("sd_hls")
                      .setContainer("ts")
                      .addElementaryStreams("video_stream0")
                      .addElementaryStreams("audio_stream0")
                      .build())
              .addMuxStreams(
                  2,
                  MuxStream.newBuilder()
                      .setKey("sd_dash")
                      .setContainer("fmp4")
                      .addElementaryStreams("video_stream0")
                      .build())
              .addMuxStreams(
                  3,
                  MuxStream.newBuilder()
                      .setKey("audio_dash")
                      .setContainer("fmp4")
                      .addElementaryStreams("audio_stream0")
                      .build())
              .addManifests(
                  0,
                  Manifest.newBuilder()
                      .setFileName("manifest.m3u8")
                      .setType(ManifestType.HLS)
                      .addMuxStreams("sd_hls")
                      .build())
              .addManifests(
                  1,
                  Manifest.newBuilder()
                      .setFileName("manifest.mpd")
                      .setType(ManifestType.DASH)
                      .addMuxStreams("sd_dash")
                      .addMuxStreams("audio_dash")
                      .build())
              .build();

      var createJobRequest =
          CreateJobRequest.newBuilder()
              .setJob(Job.newBuilder().setOutputUri(outputUri).setConfig(config).build())
              .setParent(LocationName.of(projectId, location).toString())
              .build();

      // Send the job creation request and process the response.
      Job job = transcoderServiceClient.createJob(createJobRequest);
      System.out.println("Job: " + job.getName());
    }
  }
}

Node.js

このサンプルを試す前に、クライアント ライブラリを使用した Transcoder API クイックスタートにある Node.js の設定手順を行ってください。 詳細については、Transcoder API Node.js の API のリファレンス ドキュメントをご覧ください。

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// projectId = 'my-project-id';
// location = 'us-central1';
// inputVideoUri = 'gs://my-bucket/my-video-file';
// inputCaptionsUri = 'gs://my-bucket/my-captions-file';
// outputUri = 'gs://my-bucket/my-output-folder/';

// Imports the Transcoder library
const {TranscoderServiceClient} =
  require('@google-cloud/video-transcoder').v1;

// Instantiates a client
const transcoderServiceClient = new TranscoderServiceClient();

async function createJobWithEmbeddedCaptions() {
  // Construct request
  const request = {
    parent: transcoderServiceClient.locationPath(projectId, location),
    job: {
      outputUri: outputUri,
      config: {
        inputs: [
          {
            key: 'input0',
            uri: inputVideoUri,
          },
          {
            key: 'caption_input0',
            uri: inputCaptionsUri,
          },
        ],
        editList: [
          {
            key: 'atom0',
            inputs: ['input0', 'caption_input0'],
          },
        ],
        elementaryStreams: [
          {
            key: 'video-stream0',
            videoStream: {
              h264: {
                heightPixels: 360,
                widthPixels: 640,
                bitrateBps: 550000,
                frameRate: 60,
              },
            },
          },
          {
            key: 'audio-stream0',
            audioStream: {
              codec: 'aac',
              bitrateBps: 64000,
            },
          },
          {
            key: 'cea-stream0',
            textStream: {
              codec: 'cea608',
              mapping: [
                {
                  atomKey: 'atom0',
                  inputKey: 'caption_input0',
                  inputTrack: 0,
                },
              ],
            },
          },
        ],
        muxStreams: [
          {
            key: 'sd',
            container: 'mp4',
            elementaryStreams: ['video-stream0', 'audio-stream0'],
          },
          {
            key: 'sd-hls',
            container: 'ts',
            elementaryStreams: ['video-stream0', 'audio-stream0'],
          },
          {
            key: 'sd-dash',
            container: 'fmp4',
            elementaryStreams: ['video-stream0'],
          },
          {
            key: 'audio-dash',
            container: 'fmp4',
            elementaryStreams: ['audio-stream0'],
          },
        ],
        manifests: [
          {
            fileName: 'manifest.m3u8',
            type: 'HLS',
            muxStreams: ['sd-hls'],
          },
          {
            fileName: 'manifest.mpd',
            type: 'DASH',
            muxStreams: ['sd-dash', 'audio-dash'],
          },
        ],
      },
    },
  };

  // Run request
  const [response] = await transcoderServiceClient.createJob(request);
  console.log(`Job: ${response.name}`);
}

createJobWithEmbeddedCaptions();

Python

このサンプルを試す前に、クライアント ライブラリを使用した Transcoder API クイックスタートにある Python の設定手順を行ってください。 詳細については、Transcoder API Python の API のリファレンス ドキュメントをご覧ください。


import argparse

from google.cloud.video import transcoder_v1
from google.cloud.video.transcoder_v1.services.transcoder_service import (
    TranscoderServiceClient,
)

def create_job_with_embedded_captions(
    project_id,
    location,
    input_video_uri,
    input_captions_uri,
    output_uri,
):
    """Creates a job based on an ad-hoc job configuration that embeds captions in the output video.

    Args:
        project_id (str): The GCP project ID.
        location (str): The location to start the job in.
        input_video_uri (str): Uri of the input video in the Cloud Storage
          bucket.
        input_captions_uri (str): Uri of the input captions file in the Cloud
          Storage bucket.
        output_uri (str): Uri of the video output folder in the Cloud Storage
          bucket."""

    client = TranscoderServiceClient()

    parent = f"projects/{project_id}/locations/{location}"
    job = transcoder_v1.types.Job()
    job.output_uri = output_uri
    job.config = transcoder_v1.types.JobConfig(
        inputs=[
            transcoder_v1.types.Input(
                key="input0",
                uri=input_video_uri,
            ),
            transcoder_v1.types.Input(
                key="caption-input0",
                uri=input_captions_uri,
            ),
        ],
        edit_list=[
            transcoder_v1.types.EditAtom(
                key="atom0",
                inputs=["input0", "caption-input0"],
            ),
        ],
        elementary_streams=[
            transcoder_v1.types.ElementaryStream(
                key="video-stream0",
                video_stream=transcoder_v1.types.VideoStream(
                    h264=transcoder_v1.types.VideoStream.H264CodecSettings(
                        height_pixels=360,
                        width_pixels=640,
                        bitrate_bps=550000,
                        frame_rate=60,
                    ),
                ),
            ),
            transcoder_v1.types.ElementaryStream(
                key="audio-stream0",
                audio_stream=transcoder_v1.types.AudioStream(
                    codec="aac", bitrate_bps=64000
                ),
            ),
            transcoder_v1.types.ElementaryStream(
                key="cea-stream0",
                text_stream=transcoder_v1.types.TextStream(
                    codec="cea608",
                    mapping_=[
                        transcoder_v1.types.TextStream.TextMapping(
                            atom_key="atom0",
                            input_key="caption-input0",
                            input_track=0,
                        ),
                    ],
                ),
            ),
        ],
        mux_streams=[
            transcoder_v1.types.MuxStream(
                key="sd",
                container="mp4",
                elementary_streams=["video-stream0", "audio-stream0"],
            ),
            transcoder_v1.types.MuxStream(
                key="sd-hls",
                container="ts",
                elementary_streams=["video-stream0", "audio-stream0"],
            ),
            transcoder_v1.types.MuxStream(
                key="sd-dash",
                container="fmp4",
                elementary_streams=["video-stream0"],
            ),
            transcoder_v1.types.MuxStream(
                key="audio-dash",
                container="fmp4",
                elementary_streams=["audio-stream0"],
            ),
        ],
        manifests=[
            transcoder_v1.types.Manifest(
                file_name="manifest.m3u8",
                type_="HLS",
                mux_streams=["sd-hls"],
            ),
            transcoder_v1.types.Manifest(
                file_name="manifest.mpd",
                type_="DASH",
                mux_streams=["sd-dash", "audio-dash"],
            ),
        ],
    )
    response = client.create_job(parent=parent, job=job)
    print(f"Job: {response.name}")
    return response

スタンドアロンの字幕ファイル

マニフェストから再生できる別のスタンドアロン字幕ファイルを作成するには、次のようにします。

  1. inputs 配列をジョブ構成に追加します。

  2. 関連付けられた入力動画のキーと URI を定義する Input オブジェクトを inputs 配列に追加します。

  3. 入力字幕ファイルの URI を定義する別の Input オブジェクトを追加します。

  4. editList 配列を構成に追加します。この配列は、出力動画のタイムラインに入力を追加するために使用されます。

  5. inputs 配列内のオブジェクトをキーで参照する EditAtom オブジェクトを editList 配列に追加します。startTimeOffsetendTimeOffset を指定して、入力動画をトリミングできます。

  6. 出力コンテナに字幕を追加するには、textStream オブジェクトを elementaryStreams 配列に追加します。

  7. スタンドアロンの字幕ファイルの場合は、muxStream 配列でコンテナを指定します。次の構成でキー text-vtt を持つオブジェクトをご覧ください。埋め込み字幕の場合、必要なのはエレメンタリ ストリームのみです。

次の構成では、WebVTT のクローズド キャプション ファイルを生成します。

"inputs": [
  {
    "key": "input0",
    "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO"
  },
  {
    "key": "caption_input0",
    "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_CAPTIONS_FILE"
  }
],
"editList": [
  {
    "key": "atom0",
    "inputs": [
      "input0",
      "caption_input0"
    ]
  }
],
"elementaryStreams": [
  {
    "key": "vtt-stream0",
    "textStream": {
      "codec": "webvtt",
      "mapping": [
        {
          "atomKey": "atom0",
          "inputKey": "caption_input0",
          "inputTrack": 0
        }
      ]
    }
  }
],
"muxStreams": [
  {
    "key": "sd-hls-fmp4",
    "container": "fmp4",
    "elementaryStreams": [
      "video-stream0"
    ]
  },
  {
    "key": "audio-hls-fmp4",
    "container": "fmp4",
    "elementaryStreams": [
      "audio-stream0"
    ]
  },
  {
    "key": "text-vtt",
    "container": "vtt",
    "elementaryStreams": [
      "vtt-stream0"
    ],
    "segmentSettings": {
      "segmentDuration": "6s",
      "individualSegments": true
    }
  }
],
"manifests": [
  {
    "fileName": "manifest.m3u8",
    "type": "HLS",
    "muxStreams": [
      "sd-hls-fmp4",
      "audio-hls-fmp4",
      "text-vtt"
    ]
  }
]

この構成をジョブ テンプレートに追加するか、アドホック ジョブ構成に含めることができます。

REST とコマンドライン

リクエストのデータを使用する前に、次のように置き換えます。

  • PROJECT_ID: IAM 設定に載っている Google Cloud プロジェクト ID。
  • LOCATION: ジョブを実行するロケーション。サポートされているリージョンのいずれかを使用します。
    • us-central1
    • us-west1
    • us-west2
    • us-east1
    • us-east4
    • southamerica-east1
    • asia-east1
    • asia-south1
    • asia-southeast1
    • europe-west1
    • europe-west2
    • europe-west4
  • STORAGE_BUCKET_NAME: 作成した Cloud Storage バケットの名前。
  • STORAGE_INPUT_VIDEO: コード変換する Cloud Storage バケット内の動画の名前(my-vid.mp4 など)。このフィールドでは、バケットで作成したフォルダ(input/my-vid.mp4 など)を考慮する必要があります。
  • STORAGE_CAPTIONS_FILE: Cloud Storage バケット内の字幕ファイルの名前(my-captions.srt など)。 このフィールドでは、バケットで作成したフォルダ(input/my-captions.srt など)を考慮する必要があります。
  • STORAGE_OUTPUT_FOLDER: エンコードされた動画出力を保存する Cloud Storage フォルダ名。

JSON 本文のリクエスト

{
  "config": {
    "inputs": [
      {
        "key": "input0",
        "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO"
      },
      {
        "key": "caption_input0",
        "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_CAPTIONS_FILE"
      }
    ],
    "editList": [
      {
        "key": "atom0",
        "inputs": [
          "input0",
          "caption_input0"
        ]
      }
    ],
    "elementaryStreams": [
      {
        "key": "video-stream0",
        "videoStream": {
          "h264": {
            "heightPixels": 360,
            "widthPixels": 640,
            "bitrateBps": 550000,
            "frameRate": 60
          }
        }
      },
      {
        "key": "audio-stream0",
        "audioStream": {
          "codec": "aac",
          "bitrateBps": 64000
        }
      },
      {
        "key": "vtt-stream0",
        "textStream": {
          "codec": "webvtt",
          "mapping": [
            {
              "atomKey": "atom0",
              "inputKey": "caption_input0",
              "inputTrack": 0
            }
          ]
        }
      }
    ],
    "muxStreams": [
      {
        "key": "sd-hls-fmp4",
        "container": "fmp4",
        "elementaryStreams": [
          "video-stream0"
        ]
      },
      {
        "key": "audio-hls-fmp4",
        "container": "fmp4",
        "elementaryStreams": [
          "audio-stream0"
        ]
      },
      {
        "key": "text-vtt",
        "container": "vtt",
        "elementaryStreams": [
          "vtt-stream0"
        ],
        "segmentSettings": {
          "segmentDuration": "6s",
          "individualSegments": true
        }
      }
    ],
    "manifests": [
      {
        "fileName": "manifest.m3u8",
        "type": "HLS",
        "muxStreams": [
          "sd-hls-fmp4",
          "audio-hls-fmp4",
          "text-vtt"
        ]
      }
    ],
    "output": {
      "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_OUTPUT_FOLDER/"
    }
  }
}

リクエストを送信するには、次のいずれかのオプションを展開します。

次のような JSON レスポンスが返されます。

{
  "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
  "config": {
   ...
  },
  "state": "PENDING",
  "createTime": CREATE_TIME,
  "ttlAfterCompletionDays": 30
}

gcloud

  1. ジョブ フィールドを定義する request.json ファイルを作成します。gcloud コマンドを次のように置き換えます。
    • STORAGE_BUCKET_NAME: 作成した Cloud Storage バケットの名前。
    • STORAGE_INPUT_VIDEO: コード変換する Cloud Storage バケット内の動画の名前(my-vid.mp4 など)。このフィールドでは、バケットで作成したフォルダ(input/my-vid.mp4 など)を考慮する必要があります。
    • LOCATION: ジョブを実行するロケーション。次に挙げるロケーションの 1 つを使用します。
      • us-central1
      • us-west1
      • us-west2
      • us-east1
      • us-east4
      • southamerica-east1
      • asia-east1
      • asia-south1
      • asia-southeast1
      • europe-west1
      • europe-west2
      • europe-west4
    • STORAGE_CAPTIONS_FILE: Cloud Storage バケット内の字幕ファイルの名前(my-captions.srt など)。 このフィールドでは、バケットで作成したフォルダ(input/my-captions.srt など)を考慮する必要があります。
    • STORAGE_OUTPUT_FOLDER: エンコードされた動画出力を保存する Cloud Storage フォルダ名。
    {
      "config": {
        "inputs": [
          {
            "key": "input0",
            "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO"
          },
          {
            "key": "caption_input0",
            "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_CAPTIONS_FILE"
          }
        ],
        "editList": [
          {
            "key": "atom0",
            "inputs": [
              "input0",
              "caption_input0"
            ]
          }
        ],
        "elementaryStreams": [
          {
            "key": "video-stream0",
            "videoStream": {
              "h264": {
                "heightPixels": 360,
                "widthPixels": 640,
                "bitrateBps": 550000,
                "frameRate": 60
              }
            }
          },
          {
            "key": "audio-stream0",
            "audioStream": {
              "codec": "aac",
              "bitrateBps": 64000
            }
          },
          {
            "key": "vtt-stream0",
            "textStream": {
              "codec": "webvtt",
              "mapping": [
                {
                  "atomKey": "atom0",
                  "inputKey": "caption_input0",
                  "inputTrack": 0
                }
              ]
            }
          }
        ],
        "muxStreams": [
          {
            "key": "sd-hls-fmp4",
            "container": "fmp4",
            "elementaryStreams": [
              "video-stream0"
            ]
          },
          {
            "key": "audio-hls-fmp4",
            "container": "fmp4",
            "elementaryStreams": [
              "audio-stream0"
            ]
          },
          {
            "key": "text-vtt",
            "container": "vtt",
            "elementaryStreams": [
              "vtt-stream0"
            ],
            "segmentSettings": {
              "segmentDuration": "6s",
              "individualSegments": true
            }
          }
        ],
        "manifests": [
          {
            "fileName": "manifest.m3u8",
            "type": "HLS",
            "muxStreams": [
              "sd-hls-fmp4",
              "audio-hls-fmp4",
              "text-vtt"
            ]
          }
        ],
        "output": {
          "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_OUTPUT_FOLDER/"
        }
      }
    }
    
  2. 次のコマンドを実行します。
    gcloud transcoder jobs create --location=LOCATION --file="request.json"
    
    次のようなレスポンスが表示されます。
    {
      "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
      "config": {
       ...
      },
      "state": "PENDING",
      "createTime": CREATE_TIME,
      "ttlAfterCompletionDays": 30
    }
    

Go

このサンプルを試す前に、クライアント ライブラリを使用した Transcoder API クイックスタートにある Go の設定手順を行ってください。 詳細については、Transcoder API Go の API のリファレンス ドキュメントをご覧ください。

import (
	"context"
	"fmt"
	"io"

	"github.com/golang/protobuf/ptypes/duration"

	transcoder "cloud.google.com/go/video/transcoder/apiv1"
	transcoderpb "google.golang.org/genproto/googleapis/cloud/video/transcoder/v1"
)

// createJobWithStandaloneCaptions creates a job that can use captions from a
// standalone file. See https://cloud.google.com/transcoder/docs/how-to/captions-and-subtitles
// for more information.
func createJobWithStandaloneCaptions(w io.Writer, projectID string, location string, inputVideoURI string, inputCaptionsURI string, outputURI string) error {
	// projectID := "my-project-id"
	// location := "us-central1"
	// inputVideoURI := "gs://my-bucket/my-video-file"
	// inputCaptionsURI := "gs://my-bucket/my-captions-file"
	// outputURI := "gs://my-bucket/my-output-folder/"

	ctx := context.Background()
	client, err := transcoder.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("NewClient: %v", err)
	}
	defer client.Close()

	req := &transcoderpb.CreateJobRequest{
		Parent: fmt.Sprintf("projects/%s/locations/%s", projectID, location),
		Job: &transcoderpb.Job{
			OutputUri: outputURI,
			JobConfig: &transcoderpb.Job_Config{
				Config: &transcoderpb.JobConfig{
					Inputs: []*transcoderpb.Input{
						{
							Key: "input0",
							Uri: inputVideoURI,
						},
						{
							Key: "caption_input0",
							Uri: inputCaptionsURI,
						},
					},
					EditList: []*transcoderpb.EditAtom{
						{
							Key:    "atom0",
							Inputs: []string{"input0", "caption_input0"},
						},
					},
					ElementaryStreams: []*transcoderpb.ElementaryStream{
						{
							Key: "video_stream0",
							ElementaryStream: &transcoderpb.ElementaryStream_VideoStream{
								VideoStream: &transcoderpb.VideoStream{
									CodecSettings: &transcoderpb.VideoStream_H264{
										H264: &transcoderpb.VideoStream_H264CodecSettings{
											BitrateBps:   550000,
											FrameRate:    60,
											HeightPixels: 360,
											WidthPixels:  640,
										},
									},
								},
							},
						},
						{
							Key: "audio_stream0",
							ElementaryStream: &transcoderpb.ElementaryStream_AudioStream{
								AudioStream: &transcoderpb.AudioStream{
									Codec:      "aac",
									BitrateBps: 64000,
								},
							},
						},
						{
							Key: "vtt-stream0",
							ElementaryStream: &transcoderpb.ElementaryStream_TextStream{
								TextStream: &transcoderpb.TextStream{
									Codec: "webvtt",
									Mapping: []*transcoderpb.TextStream_TextMapping{
										{
											AtomKey:    "atom0",
											InputKey:   "caption_input0",
											InputTrack: 0,
										},
									},
								},
							},
						},
					},
					MuxStreams: []*transcoderpb.MuxStream{
						{
							Key:               "sd-hls-fmp4",
							Container:         "fmp4",
							ElementaryStreams: []string{"video_stream0"},
						},
						{
							Key:               "audio-hls-fmp4",
							Container:         "fmp4",
							ElementaryStreams: []string{"audio_stream0"},
						},
						{
							Key:               "text-vtt",
							Container:         "vtt",
							ElementaryStreams: []string{"vtt-stream0"},
							SegmentSettings: &transcoderpb.SegmentSettings{
								SegmentDuration: &duration.Duration{
									Seconds: 6,
								},
								IndividualSegments: true,
							},
						},
					},
					Manifests: []*transcoderpb.Manifest{
						{
							FileName:   "manifest.m3u8",
							Type:       transcoderpb.Manifest_HLS,
							MuxStreams: []string{"sd-hls-fmp4", "audio-hls-fmp4", "text-vtt"},
						},
					},
				},
			},
		},
	}
	// Creates the job. Jobs take a variable amount of time to run.
	// You can query for the job state; see getJob() in get_job.go.
	response, err := client.CreateJob(ctx, req)
	if err != nil {
		return fmt.Errorf("CreateJob: %v", err)
	}

	fmt.Fprintf(w, "Job: %v", response.GetName())
	return nil
}

Java

このサンプルを試す前に、クライアント ライブラリを使用した Transcoder API クイックスタートにある Java の設定手順を行ってください。 詳細については、Transcoder API Java の API のリファレンス ドキュメントをご覧ください。


import com.google.cloud.video.transcoder.v1.AudioStream;
import com.google.cloud.video.transcoder.v1.CreateJobRequest;
import com.google.cloud.video.transcoder.v1.EditAtom;
import com.google.cloud.video.transcoder.v1.ElementaryStream;
import com.google.cloud.video.transcoder.v1.Input;
import com.google.cloud.video.transcoder.v1.Job;
import com.google.cloud.video.transcoder.v1.JobConfig;
import com.google.cloud.video.transcoder.v1.LocationName;
import com.google.cloud.video.transcoder.v1.Manifest;
import com.google.cloud.video.transcoder.v1.Manifest.ManifestType;
import com.google.cloud.video.transcoder.v1.MuxStream;
import com.google.cloud.video.transcoder.v1.Output;
import com.google.cloud.video.transcoder.v1.SegmentSettings;
import com.google.cloud.video.transcoder.v1.TextStream;
import com.google.cloud.video.transcoder.v1.TextStream.TextMapping;
import com.google.cloud.video.transcoder.v1.TranscoderServiceClient;
import com.google.cloud.video.transcoder.v1.VideoStream;
import com.google.protobuf.Duration;
import java.io.IOException;

public class CreateJobWithStandaloneCaptions {

  public static void main(String[] args) throws IOException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project-id";
    String location = "us-central1";
    String inputVideoUri = "gs://my-bucket/my-video-file";
    String inputCaptionsUri = "gs://my-bucket/my-captions-file";
    String outputUri = "gs://my-bucket/my-output-folder/";

    createJobWithStandaloneCaptions(
        projectId, location, inputVideoUri, inputCaptionsUri, outputUri);
  }

  // Creates a job from an ad-hoc configuration that can use captions from a standalone file.
  public static void createJobWithStandaloneCaptions(
      String projectId,
      String location,
      String inputVideoUri,
      String inputCaptionsUri,
      String outputUri)
      throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (TranscoderServiceClient transcoderServiceClient = TranscoderServiceClient.create()) {

      VideoStream videoStream0 =
          VideoStream.newBuilder()
              .setH264(
                  VideoStream.H264CodecSettings.newBuilder()
                      .setBitrateBps(550000)
                      .setFrameRate(60)
                      .setHeightPixels(360)
                      .setWidthPixels(640))
              .build();

      AudioStream audioStream0 =
          AudioStream.newBuilder().setCodec("aac").setBitrateBps(64000).build();

      TextStream textStream0 =
          TextStream.newBuilder()
              .setCodec("webvtt")
              .addMapping(
                  0,
                  TextMapping.newBuilder()
                      .setAtomKey("atom0")
                      .setInputKey("caption_input0")
                      .setInputTrack(0)
                      .build())
              .build();

      JobConfig config =
          JobConfig.newBuilder()
              .addInputs(Input.newBuilder().setKey("input0").setUri(inputVideoUri))
              .addInputs(Input.newBuilder().setKey("caption_input0").setUri(inputCaptionsUri))
              .addEditList(
                  0, // Index in the edit list
                  EditAtom.newBuilder()
                      .setKey("atom0")
                      .addInputs("input0")
                      .addInputs("caption_input0")
                      .build())
              .setOutput(Output.newBuilder().setUri(outputUri))
              .addElementaryStreams(
                  ElementaryStream.newBuilder()
                      .setKey("video_stream0")
                      .setVideoStream(videoStream0))
              .addElementaryStreams(
                  ElementaryStream.newBuilder()
                      .setKey("audio_stream0")
                      .setAudioStream(audioStream0))
              .addElementaryStreams(
                  ElementaryStream.newBuilder().setKey("vtt_stream0").setTextStream(textStream0))
              .addMuxStreams(
                  0,
                  MuxStream.newBuilder()
                      .setKey("sd_hls_fmp4")
                      .setContainer("fmp4")
                      .addElementaryStreams("video_stream0")
                      .build())
              .addMuxStreams(
                  1,
                  MuxStream.newBuilder()
                      .setKey("audio_hls_fmp4")
                      .setContainer("fmp4")
                      .addElementaryStreams("audio_stream0")
                      .build())
              .addMuxStreams(
                  2,
                  MuxStream.newBuilder()
                      .setKey("text_vtt")
                      .setContainer("vtt")
                      .addElementaryStreams("vtt_stream0")
                      .setSegmentSettings(
                          SegmentSettings.newBuilder()
                              .setSegmentDuration(Duration.newBuilder().setSeconds(6).build())
                              .setIndividualSegments(true)
                              .build())
                      .build())
              .addManifests(
                  0,
                  Manifest.newBuilder()
                      .setFileName("manifest.m3u8")
                      .setType(ManifestType.HLS)
                      .addMuxStreams("sd_hls_fmp4")
                      .addMuxStreams("audio_hls_fmp4")
                      .addMuxStreams("text_vtt")
                      .build())
              .build();

      var createJobRequest =
          CreateJobRequest.newBuilder()
              .setJob(Job.newBuilder().setOutputUri(outputUri).setConfig(config).build())
              .setParent(LocationName.of(projectId, location).toString())
              .build();

      // Send the job creation request and process the response.
      Job job = transcoderServiceClient.createJob(createJobRequest);
      System.out.println("Job: " + job.getName());
    }
  }
}

Node.js

このサンプルを試す前に、クライアント ライブラリを使用した Transcoder API クイックスタートにある Node.js の設定手順を行ってください。 詳細については、Transcoder API Node.js の API のリファレンス ドキュメントをご覧ください。

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// projectId = 'my-project-id';
// location = 'us-central1';
// inputVideoUri = 'gs://my-bucket/my-video-file';
// inputCaptionsUri = 'gs://my-bucket/my-captions-file';
// outputUri = 'gs://my-bucket/my-output-folder/';

// Imports the Transcoder library
const {TranscoderServiceClient} =
  require('@google-cloud/video-transcoder').v1;

// Instantiates a client
const transcoderServiceClient = new TranscoderServiceClient();

async function createJobWithStandaloneCaptions() {
  // Construct request
  const request = {
    parent: transcoderServiceClient.locationPath(projectId, location),
    job: {
      outputUri: outputUri,
      config: {
        inputs: [
          {
            key: 'input0',
            uri: inputVideoUri,
          },
          {
            key: 'caption_input0',
            uri: inputCaptionsUri,
          },
        ],
        editList: [
          {
            key: 'atom0',
            inputs: ['input0', 'caption_input0'],
          },
        ],
        elementaryStreams: [
          {
            key: 'video-stream0',
            videoStream: {
              h264: {
                heightPixels: 360,
                widthPixels: 640,
                bitrateBps: 550000,
                frameRate: 60,
              },
            },
          },
          {
            key: 'audio-stream0',
            audioStream: {
              codec: 'aac',
              bitrateBps: 64000,
            },
          },
          {
            key: 'vtt-stream0',
            textStream: {
              codec: 'webvtt',
              mapping: [
                {
                  atomKey: 'atom0',
                  inputKey: 'caption_input0',
                  inputTrack: 0,
                },
              ],
            },
          },
        ],
        muxStreams: [
          {
            key: 'sd-hls-fmp4',
            container: 'fmp4',
            elementaryStreams: ['video-stream0'],
          },
          {
            key: 'audio-hls-fmp4',
            container: 'fmp4',
            elementaryStreams: ['audio-stream0'],
          },
          {
            key: 'text-vtt',
            container: 'vtt',
            elementaryStreams: ['vtt-stream0'],
            segmentSettings: {
              segmentDuration: {
                seconds: 6,
              },
              individualSegments: true,
            },
          },
        ],
        manifests: [
          {
            fileName: 'manifest.m3u8',
            type: 'HLS',
            muxStreams: ['sd-hls-fmp4', 'audio-hls-fmp4', 'text-vtt'],
          },
        ],
      },
    },
  };

  // Run request
  const [response] = await transcoderServiceClient.createJob(request);
  console.log(`Job: ${response.name}`);
}

createJobWithStandaloneCaptions();

Python

このサンプルを試す前に、クライアント ライブラリを使用した Transcoder API クイックスタートにある Python の設定手順を行ってください。 詳細については、Transcoder API Python の API のリファレンス ドキュメントをご覧ください。


import argparse

from google.cloud.video import transcoder_v1
from google.cloud.video.transcoder_v1.services.transcoder_service import (
    TranscoderServiceClient,
)
from google.protobuf import duration_pb2 as duration

def create_job_with_standalone_captions(
    project_id,
    location,
    input_video_uri,
    input_captions_uri,
    output_uri,
):
    """Creates a job based on an ad-hoc job configuration that can use captions from a standalone file.

    Args:
        project_id (str): The GCP project ID.
        location (str): The location to start the job in.
        input_video_uri (str): Uri of the input video in the Cloud Storage
          bucket.
        input_captions_uri (str): Uri of the input captions file in the Cloud
          Storage bucket.
        output_uri (str): Uri of the video output folder in the Cloud Storage
          bucket."""

    client = TranscoderServiceClient()

    parent = f"projects/{project_id}/locations/{location}"
    job = transcoder_v1.types.Job()
    job.output_uri = output_uri
    job.config = transcoder_v1.types.JobConfig(
        inputs=[
            transcoder_v1.types.Input(
                key="input0",
                uri=input_video_uri,
            ),
            transcoder_v1.types.Input(
                key="caption-input0",
                uri=input_captions_uri,
            ),
        ],
        edit_list=[
            transcoder_v1.types.EditAtom(
                key="atom0",
                inputs=["input0", "caption-input0"],
            ),
        ],
        elementary_streams=[
            transcoder_v1.types.ElementaryStream(
                key="video-stream0",
                video_stream=transcoder_v1.types.VideoStream(
                    h264=transcoder_v1.types.VideoStream.H264CodecSettings(
                        height_pixels=360,
                        width_pixels=640,
                        bitrate_bps=550000,
                        frame_rate=60,
                    ),
                ),
            ),
            transcoder_v1.types.ElementaryStream(
                key="audio-stream0",
                audio_stream=transcoder_v1.types.AudioStream(
                    codec="aac", bitrate_bps=64000
                ),
            ),
            transcoder_v1.types.ElementaryStream(
                key="vtt-stream0",
                text_stream=transcoder_v1.types.TextStream(
                    codec="webvtt",
                    mapping_=[
                        transcoder_v1.types.TextStream.TextMapping(
                            atom_key="atom0",
                            input_key="caption-input0",
                            input_track=0,
                        ),
                    ],
                ),
            ),
        ],
        mux_streams=[
            transcoder_v1.types.MuxStream(
                key="sd-hls-fmp4",
                container="fmp4",
                elementary_streams=["video-stream0"],
            ),
            transcoder_v1.types.MuxStream(
                key="audio-hls-fmp4",
                container="fmp4",
                elementary_streams=["audio-stream0"],
            ),
            transcoder_v1.types.MuxStream(
                key="text-vtt",
                container="vtt",
                elementary_streams=["vtt-stream0"],
                segment_settings=transcoder_v1.types.SegmentSettings(
                    segment_duration=duration.Duration(
                        seconds=6,
                    ),
                    individual_segments=True,
                ),
            ),
        ],
        manifests=[
            transcoder_v1.types.Manifest(
                file_name="manifest.m3u8",
                type_="HLS",
                mux_streams=["sd-hls-fmp4", "audio-hls-fmp4", "text-vtt"],
            ),
        ],
    )
    response = client.create_job(parent=parent, job=job)
    print(f"Job: {response.name}")
    return response

動画の再生

Shaka Player で生成されたメディア ファイルを再生するには、次の手順に従います。

  1. 作成した Cloud Storage バケットを一般公開します
  2. Cloud Storage バケットでクロスオリジン リソース シェアリング(CORS)を有効にするには、次のようにします。
    1. 次の内容を含む JSON ファイルを作成します。
      [
        {
          "origin": ["https://shaka-player-demo.appspot.com/"],
          "responseHeader": ["Content-Type", "Range"],
          "method": ["GET", "HEAD"],
          "maxAgeSeconds": 3600
        }
      ]
    2. JSON_FILE_NAME を前の手順で作成した JSON ファイルの名前に置き換えてから、次のコマンドを実行します。
      gsutil cors set JSON_FILE_NAME.json gs://STORAGE_BUCKET_NAME
  3. Cloud Storage バケットのコード変換ジョブによって生成された MP4 またはマニフェスト ファイルのいずれかを選択します。ファイルの [公開アクセス] 列で [URL をコピー] をクリックします。
  4. オンライン ライブ ストリーム プレーヤーの Shaka Player に移動します。
  5. 上部のナビゲーション バーにある [CUSTOM CONTENT](カスタム コンテンツ)をクリックします。
  6. [+] ボタンをクリックします。
  7. ファイルの公開 URL を [Manifest URL] ボックスに貼り付けます。

    Shaka Player でファイルの URL を入力します。

  8. [Name] ボックスに名前を入力します。

  9. [保存] をクリックします。

  10. [Play] をクリックします。

  11. プレーヤーの右下にある省略記号ボタンを選択して、字幕を有効にします。

次のサンプル動画およびサンプル字幕ファイルを使用して、クローズド キャプション付き動画を出力できます(これらの字幕を表示するには、Google ドライブに MP4 をアップロードして、組み込みのプレーヤーを使用します)。 字幕ファイルでテキスト行の間に空白行を含めることはできません。