Untertitel hinzufügen

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

Auf dieser Seite wird erläutert, wie du einem Ausgangsvideo Untertitel hinzufügen kannst.

Untertitel sind die visuellen Inhalte der Audioinhalte in einem Video. Untertitel sind in der Regel in der Sprache der Audioinhalte verfügbar und beinhalten Hintergrundgeräusche und Sprecheränderungen.

Untertitel werden in der Regel verwendet, um den Dialog eines Videos in eine andere Sprache zu übersetzen. Untertitel enthalten in der Regel keine Hintergrundgeräusche und keine Änderungen an den Lautsprechern.

Auf dieser Seite wird der Begriff Untertitel sowohl für Untertitel als auch für Untertitel verwendet.

Einer Jobkonfiguration Untertitel hinzufügen

Du kannst entweder Untertitel in den Dateicontainer der Ausgabevideo einbetten oder eine separate eigenständige Untertiteldatei erstellen, die über ein Manifest wiedergegeben werden kann. Informationen zu den unterstützten Dateiformaten für Untertitel finden Sie in der Tabelle der unterstützten Ein- und Ausgaben. Zum Testen deiner Konfiguration erhältst du eine Beispieldatei und eine Untertiteldatei.

Verwenden Sie die Informationen in den folgenden Abschnitten, um Untertitel zu einer Jobkonfiguration hinzuzufügen. Auf dieser Seite wird davon ausgegangen, dass Sie mit einer einfachen JobConfig vertraut sind. Weitere Informationen zum Erstellen von Transcodierungsjobs finden Sie unter Jobs erstellen und verwalten.

Eingebettete Untertitel

So betten Sie Untertitel in den Dateicontainer der Ausgabevideodatei ein:

  1. Fügen Sie am Anfang der Jobkonfiguration ein inputs-Array hinzu.

  2. Fügen Sie dem inputs-Array ein Input-Objekt hinzu, das den Schlüssel und den URI für das verknüpfte Eingabevideo definiert.

  3. Fügen Sie ein weiteres Input-Objekt hinzu, das den Pfad zur eingegebenen Untertiteldatei enthält.

  4. Fügen Sie der Jobkonfiguration ein editList-Array hinzu. Mit diesem Array werden Eingaben zur Zeitachse des Ausgabevideos hinzugefügt.

  5. Fügen Sie dem editList-Array ein EditAtom-Objekt hinzu. Dieses EditAtom-Objekt muss auf die Schlüssel für das Eingabevideo und die Untertitel verweisen, die Sie im inputs-Array hinzugefügt haben. Sie können startTimeOffset und endTimeOffset angeben, um das Eingabevideo zu schneiden.

  6. Fügen Sie den Ausgabecontainern die Untertitel hinzu, indem Sie dem elementaryStreams-Array ein textStream-Objekt hinzufügen. Es wird nur ein eingebetteter Textstream unterstützt und er wird zu allen Ausgabevideos hinzugefügt, da es nur eine Ausgabezeitachse gibt.

  7. Verwenden Sie das Array mapping im Konfigurationsobjekt textStream, um auf den Objektschlüssel EditAtom zu verweisen.

In der folgenden Beispielkonfiguration werden Untertitel in CEA-608 in ein Video eingebettet:

"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
        }
      ]
    }
  }
],

Sie können diese Konfiguration einer Jobvorlage hinzufügen oder sie in eine Ad-hoc-Jobkonfiguration einbinden:

REST UND BEFEHLSZEILE

Bevor Sie die Anfragedaten verwenden, ersetzen Sie die folgenden Werte:

  • PROJECT_ID: Ihre Google Cloud-Projekt-ID, die unter IAM-Einstellungen aufgeführt ist.
  • LOCATION: Der Standort, an dem der Job ausgeführt werden soll. Verwenden Sie eine der unterstützten Regionen:
    • 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: Der Name des Cloud Storage-Buckets, den Sie erstellt haben.
  • STORAGE_INPUT_VIDEO: Der Name des Videos in Ihrem Cloud Storage-Bucket, den Sie transcodieren, z. B. my-vid.mp4. In diesem Feld sollten alle Ordner berücksichtigt werden, die Sie im Bucket erstellt haben (z. B. input/my-vid.mp4).
  • STORAGE_CAPTIONS_FILE: Der Name einer Untertiteldatei in Ihrem Cloud Storage-Bucket, z. B. my-captions.srt. In diesem Feld sollten alle Ordner berücksichtigt werden, die Sie im Bucket erstellt haben (z. B. input/my-captions.srt).
  • STORAGE_OUTPUT_FOLDER: Der Name des Cloud Storage-Ordners, in dem die codierten Videoausgaben gespeichert werden sollen.

JSON-Text der Anfrage:

{
  "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/"
    }
  }
}

Wenn Sie die Anfrage senden möchten, maximieren Sie eine der folgenden Optionen:

Sie sollten in etwa folgende JSON-Antwort erhalten:

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

gcloud

  1. Erstellen Sie eine request.json-Datei, die die Jobfelder definiert. Ersetzen Sie den Befehl gcloud durch den folgenden Wert:
    • STORAGE_BUCKET_NAME: Der Name des Cloud Storage-Buckets, den Sie erstellt haben.
    • STORAGE_INPUT_VIDEO: Der Name des Videos in Ihrem Cloud Storage-Bucket, den Sie transcodieren, z. B. my-vid.mp4. In diesem Feld sollten alle Ordner berücksichtigt werden, die Sie im Bucket erstellt haben (z. B. input/my-vid.mp4).
    • LOCATION: Der Standort, an dem der Job ausgeführt werden soll. Verwenden Sie einen Standort aus der folgenden Liste:
      • 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: Der Name einer Untertiteldatei in Ihrem Cloud Storage-Bucket, z. B. my-captions.srt. In diesem Feld sollten alle Ordner berücksichtigt werden, die Sie im Bucket erstellt haben (z. B. input/my-captions.srt).
    • STORAGE_OUTPUT_FOLDER: Der Name des Cloud Storage-Ordners, in dem Sie die codierten Videoausgaben speichern möchten.
    {
      "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. Führen Sie dazu diesen Befehl aus:
    gcloud transcoder jobs create --location=LOCATION --file="request.json"
    
    Die Antwort sieht ungefähr so aus:
    {
      "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
      "config": {
       ...
      },
      "state": "PENDING",
      "createTime": CREATE_TIME,
      "ttlAfterCompletionDays": 30
    }
    

Go

Folgen Sie der Einrichtungsanleitung für Go in der Transcoder API-Kurzanleitung mit Clientbibliotheken, bevor Sie dieses Beispiel ausprobieren. Weitere Informationen findest du in der Referenzdokumentation zur 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

Folgen Sie der Einrichtungsanleitung für Java in der Transcoder API-Kurzanleitung mit Clientbibliotheken, bevor Sie dieses Beispiel ausprobieren. Weitere Informationen findest du in der Referenzdokumentation zur 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

Folgen Sie der Einrichtungsanleitung für Node.js in der Transcoder API-Kurzanleitung mit Clientbibliotheken, bevor Sie dieses Beispiel ausprobieren. Weitere Informationen findest du in der Referenzdokumentation zur 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

Folgen Sie der Einrichtungsanleitung für Python in der Transcoder API-Kurzanleitung mit Clientbibliotheken, bevor Sie dieses Beispiel ausprobieren. Weitere Informationen findest du in der Referenzdokumentation zur 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

Eigenständige Untertiteldatei

So erstellst du eine separate eigenständige Untertiteldatei, die über ein Manifest wiedergegeben werden kann:

  1. Fügen Sie der Jobkonfiguration ein inputs-Array hinzu.

  2. Fügen Sie dem inputs-Array ein Input-Objekt hinzu, das den Schlüssel und den URI für das verknüpfte Eingabevideo definiert.

  3. Fügen Sie ein weiteres Input-Objekt hinzu, das den URI für die Untertiteldatei definiert.

  4. Fügen Sie der Konfiguration ein editList-Array hinzu. Mit diesem Array werden die Eingaben der Videozeitachse hinzugefügt.

  5. Fügen Sie dem editList-Array ein EditAtom-Objekt hinzu, das anhand der Schlüssel auf die Objekte im Array inputs verweist. Sie können startTimeOffset und endTimeOffset angeben, um das Eingabevideo zu schneiden.

  6. Fügen Sie den Ausgabecontainern die Untertitel hinzu, indem Sie dem elementaryStreams-Array ein textStream-Objekt hinzufügen.

  7. Geben Sie für die eigenständige Untertiteldatei den Container im Array muxStream an. Siehe dazu das Objekt mit dem Schlüssel text-vtt in der folgenden Konfiguration. Für eingebettete Untertitel benötigst du nur den grundlegenden Stream.

Die folgende Konfiguration generiert eine WebVTT-Untertiteldatei:

"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"
    ]
  }
]

Sie können diese Konfiguration einer Jobvorlage hinzufügen oder sie in eine Ad-hoc-Jobkonfiguration einbinden:

REST UND BEFEHLSZEILE

Bevor Sie die Anfragedaten verwenden, ersetzen Sie die folgenden Werte:

  • PROJECT_ID: Ihre Google Cloud-Projekt-ID, die unter IAM-Einstellungen aufgeführt ist.
  • LOCATION: Der Standort, an dem der Job ausgeführt werden soll. Verwenden Sie eine der unterstützten Regionen:
    • 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: Der Name des Cloud Storage-Buckets, den Sie erstellt haben.
  • STORAGE_INPUT_VIDEO: Der Name des Videos in Ihrem Cloud Storage-Bucket, den Sie transcodieren, z. B. my-vid.mp4. In diesem Feld sollten alle Ordner berücksichtigt werden, die Sie im Bucket erstellt haben (z. B. input/my-vid.mp4).
  • STORAGE_CAPTIONS_FILE: Der Name einer Untertiteldatei in Ihrem Cloud Storage-Bucket, z. B. my-captions.srt. In diesem Feld sollten alle Ordner berücksichtigt werden, die Sie im Bucket erstellt haben (z. B. input/my-captions.srt).
  • STORAGE_OUTPUT_FOLDER: Der Name des Cloud Storage-Ordners, in dem die codierten Videoausgaben gespeichert werden sollen.

JSON-Text der Anfrage:

{
  "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/"
    }
  }
}

Wenn Sie die Anfrage senden möchten, maximieren Sie eine der folgenden Optionen:

Sie sollten in etwa folgende JSON-Antwort erhalten:

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

gcloud

  1. Erstellen Sie eine request.json-Datei, die die Jobfelder definiert. Ersetzen Sie den Befehl gcloud durch den folgenden Wert:
    • STORAGE_BUCKET_NAME: Der Name des Cloud Storage-Buckets, den Sie erstellt haben.
    • STORAGE_INPUT_VIDEO: Der Name des Videos in Ihrem Cloud Storage-Bucket, den Sie transcodieren, z. B. my-vid.mp4. In diesem Feld sollten alle Ordner berücksichtigt werden, die Sie im Bucket erstellt haben (z. B. input/my-vid.mp4).
    • LOCATION: Der Standort, an dem der Job ausgeführt werden soll. Verwenden Sie einen Standort aus der folgenden Liste:
      • 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: Der Name einer Untertiteldatei in Ihrem Cloud Storage-Bucket, z. B. my-captions.srt. In diesem Feld sollten alle Ordner berücksichtigt werden, die Sie im Bucket erstellt haben (z. B. input/my-captions.srt).
    • STORAGE_OUTPUT_FOLDER: Der Name des Cloud Storage-Ordners, in dem Sie die codierten Videoausgaben speichern möchten.
    {
      "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. Führen Sie dazu diesen Befehl aus:
    gcloud transcoder jobs create --location=LOCATION --file="request.json"
    
    Die Antwort sieht ungefähr so aus:
    {
      "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
      "config": {
       ...
      },
      "state": "PENDING",
      "createTime": CREATE_TIME,
      "ttlAfterCompletionDays": 30
    }
    

Go

Folgen Sie der Einrichtungsanleitung für Go in der Transcoder API-Kurzanleitung mit Clientbibliotheken, bevor Sie dieses Beispiel ausprobieren. Weitere Informationen findest du in der Referenzdokumentation zur 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

Folgen Sie der Einrichtungsanleitung für Java in der Transcoder API-Kurzanleitung mit Clientbibliotheken, bevor Sie dieses Beispiel ausprobieren. Weitere Informationen findest du in der Referenzdokumentation zur 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

Folgen Sie der Einrichtungsanleitung für Node.js in der Transcoder API-Kurzanleitung mit Clientbibliotheken, bevor Sie dieses Beispiel ausprobieren. Weitere Informationen findest du in der Referenzdokumentation zur 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

Folgen Sie der Einrichtungsanleitung für Python in der Transcoder API-Kurzanleitung mit Clientbibliotheken, bevor Sie dieses Beispiel ausprobieren. Weitere Informationen findest du in der Referenzdokumentation zur 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

Spielen Sie Ihr Video ab.

Führen Sie die folgenden Schritte aus, um die generierte Mediadatei in Shaka Player abzuspielen:

  1. Machen Sie den von Ihnen erstellten Cloud Storage-Bucket öffentlich.
  2. So aktivieren Sie die ursprungsübergreifende Ressourcenfreigabe (CORS) für einen Cloud Storage-Bucket:
    1. Erstellen Sie eine JSON-Datei, die Folgendes enthält:
      [
        {
          "origin": ["https://shaka-player-demo.appspot.com/"],
          "responseHeader": ["Content-Type", "Range"],
          "method": ["GET", "HEAD"],
          "maxAgeSeconds": 3600
        }
      ]
    2. Führen Sie den folgenden Befehl aus, nachdem Sie JSON_FILE_NAME durch den Namen der JSON-Datei ersetzt haben, die Sie im vorherigen Schritt erstellt haben:
      gsutil cors set JSON_FILE_NAME.json gs://STORAGE_BUCKET_NAME
  3. Wählen Sie eine der MP4- oder Manifestdateien aus, die vom Transcodierungsjob im Cloud Storage-Bucket generiert wurden. Klicken Sie in der Spalte Öffentlicher Zugriff der Datei auf URL kopieren.
  4. Rufen Sie Shaka Player, ein Online-Livestream-Player, auf.
  5. Klicken Sie in der Navigationsleiste oben auf Benutzerdefinierte Inhalte.
  6. Klicken Sie auf die Schaltfläche +.
  7. Fügen Sie die öffentliche URL der Datei in das Feld URL manifestieren ein.

    Geben Sie die URL der Datei in den Shaka Player ein.

  8. Geben Sie einen Namen in das Feld Name ein.

  9. Klicken Sie auf Speichern.

  10. Klicken Sie auf Wiedergabe.

  11. Wähle rechts unten im Player die Schaltfläche mit den drei Punkten aus und aktiviere die Untertitel.

Beispiel

Du kannst die folgende Beispieldatei und Beispieldatei für die Untertitel verwenden, um ein Video mit Untertiteln auszugeben. Wenn du diese Untertitel sehen möchtest, lade die MP4-Datei in Google Drive hoch und verwende den integrierten Player. Die Untertiteldatei darf zwischen den Textzeilen keine leeren Zeilen enthalten.