Menggabungkan beberapa video input

Halaman ini menjelaskan cara menggabungkan beberapa video input menjadi satu video output. Anda juga dapat memangkas linimasa video input.

Untuk setiap video input, tambahkan objek Input ke array inputs. Setiap objek Input menentukan kunci dan URI untuk video input terkait. Anda dapat menambahkan objek PreprocessingConfig opsional ke Input untuk memotong, menambahkan padding, atau melakukan prapemrosesan lainnya pada video input. Array inputs tidak diurutkan; Anda dapat menambahkan video input dalam urutan apa pun.

Untuk menambahkan video input ke linimasa video output, tambahkan objek EditAtom ke array editList. Array editList diurutkan. Input pertama yang ditetapkan dalam array ini akan digunakan terlebih dahulu dalam video output, input kedua akan digunakan berikutnya, dan seterusnya. Anda mengidentifikasi video input berdasarkan kuncinya.

Anda juga dapat menetapkan startTimeOffset dan endTimeOffset untuk memangkas video input. Kolom ini bersifat opsional. Jika Anda tidak menentukan kolom ini, seluruh video input akan digunakan.

Konfigurasi berikut menyambungkan dua video input menjadi satu video output.

"inputs": [
  {
    "key": "input1",
    "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO1"
  },
  {
    "key": "input2",
    "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO2"
  }
],
"editList": [
  {
    "key": "atom1",
    "inputs": [
      "input1"
    ],
    "startTimeOffset": "START_TIME_OFFSET1s",
    "endTimeOffset": "END_TIME_OFFSET1s"
  },
  {
    "key": "atom2",
    "inputs": [
      "input2"
    ],
    "startTimeOffset": "START_TIME_OFFSET2s",
    "endTimeOffset": "END_TIME_OFFSET2s"
  }
],

Anda dapat menambahkan konfigurasi ini ke template tugas atau menyertakannya dalam konfigurasi tugas ad hoc:

REST

Sebelum menggunakan salah satu data permintaan, lakukan penggantian berikut:

  • PROJECT_ID: Project ID Google Cloud Anda yang tercantum di Setelan IAM.
  • LOCATION: Lokasi tempat tugas Anda akan berjalan. Gunakan salah satu region yang didukung.
    Menampilkan lokasi
    • us-central1
    • us-west1
    • us-west2
    • us-east1
    • us-east4
    • southamerica-east1
    • northamerica-northeast1
    • asia-east1
    • asia-northeast1
    • asia-northeast3
    • asia-south1
    • asia-southeast1
    • australia-southeast1
    • europe-west1
    • europe-west2
    • europe-west4
  • STORAGE_BUCKET_NAME: Nama bucket Cloud Storage yang Anda buat.
  • STORAGE_INPUT_VIDEO1: Nama video di bucket Cloud Storage yang Anda transkode, seperti my-vid.mp4. Kolom ini harus memperhitungkan folder yang Anda buat di bucket (misalnya, input/my-vid.mp4). Video ini akan digunakan terlebih dahulu dalam linimasa video output.
  • START_TIME_OFFSET1: Waktu mulai, dalam pecahan detik (misalnya, 0.0), relatif terhadap linimasa video input pertama. Gunakan kolom ini untuk memangkas konten dari awal video.
  • END_TIME_OFFSET1: Waktu akhir, dalam pecahan detik (misalnya, 8.1), relatif terhadap linimasa video input pertama. Gunakan kolom ini untuk memangkas konten dari akhir video.
  • STORAGE_INPUT_VIDEO2: Nama video di bucket Cloud Storage yang Anda transkode, seperti my-vid.mp4. Kolom ini harus memperhitungkan folder yang Anda buat di bucket (misalnya, input/my-vid.mp4). Video ini akan digunakan kedua di linimasa video output.
  • START_TIME_OFFSET2: Waktu mulai, dalam pecahan detik (misalnya, 3.5), relatif terhadap linimasa video input kedua. Gunakan kolom ini untuk memangkas konten dari awal video kedua.
  • END_TIME_OFFSET2: Waktu akhir, dalam pecahan detik (misalnya, 15), relatif terhadap linimasa video input kedua. Gunakan kolom ini untuk memangkas konten dari akhir video kedua.
  • STORAGE_OUTPUT_FOLDER: Nama folder Cloud Storage tempat Anda ingin menyimpan output video yang dienkode.

Untuk mengirim permintaan Anda, perluas salah satu opsi berikut:

Anda akan melihat respons JSON seperti berikut:

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

gcloud

  1. Buat file request.json yang menentukan kolom tugas. Lakukan penggantian berikut untuk perintah gcloud:
    • LOCATION: Lokasi tempat tugas Anda akan dijalankan. Gunakan salah satu region yang didukung.
      Menampilkan lokasi
      • us-central1
      • us-west1
      • us-west2
      • us-east1
      • us-east4
      • southamerica-east1
      • northamerica-northeast1
      • asia-east1
      • asia-northeast1
      • asia-northeast3
      • asia-south1
      • asia-southeast1
      • australia-southeast1
      • europe-west1
      • europe-west2
      • europe-west4
    • STORAGE_BUCKET_NAME: Nama bucket Cloud Storage yang Anda buat.
    • STORAGE_INPUT_VIDEO1: Nama video di bucket Cloud Storage yang Anda transkode, seperti my-vid.mp4. Kolom ini harus memperhitungkan folder yang Anda buat di bucket (misalnya, input/my-vid.mp4). Video ini akan digunakan terlebih dahulu dalam linimasa video output.
    • START_TIME_OFFSET1: Waktu mulai, dalam pecahan detik (misalnya, 0.0), relatif terhadap linimasa video input pertama. Gunakan kolom ini untuk memangkas konten dari awal video.
    • END_TIME_OFFSET1: Waktu akhir, dalam pecahan detik (misalnya, 8.1), relatif terhadap linimasa video input pertama. Gunakan kolom ini untuk memangkas konten dari akhir video.
    • STORAGE_INPUT_VIDEO2: Nama video di bucket Cloud Storage yang Anda transkode, seperti my-vid.mp4. Kolom ini harus memperhitungkan folder yang Anda buat di bucket (misalnya, input/my-vid.mp4). Video ini akan digunakan kedua di linimasa video output.
    • START_TIME_OFFSET2: Waktu mulai, dalam pecahan detik (misalnya, 3.5), relatif terhadap linimasa video input kedua. Gunakan kolom ini untuk memangkas konten dari awal video kedua.
    • END_TIME_OFFSET2: Waktu akhir, dalam pecahan detik (misalnya, 15), relatif terhadap linimasa video input kedua. Gunakan kolom ini untuk memangkas konten dari akhir video kedua.
    • STORAGE_OUTPUT_FOLDER: Nama folder Cloud Storage tempat Anda ingin menyimpan output video yang dienkode.
    {
      "config": {
        "inputs": [
          {
            "key": "input1",
            "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO1"
          },
          {
            "key": "input2",
            "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_INPUT_VIDEO2"
          }
        ],
        "editList": [
          {
            "key": "atom1",
            "inputs": [
              "input1"
            ],
            "startTimeOffset": "START_TIME_OFFSET1s",
            "endTimeOffset": "END_TIME_OFFSET1s"
          },
          {
            "key": "atom2",
            "inputs": [
              "input2"
            ],
            "startTimeOffset": "START_TIME_OFFSET2s",
            "endTimeOffset": "END_TIME_OFFSET2s"
          }
        ],
        "elementaryStreams": [
          {
            "key": "video-stream0",
            "videoStream": {
              "h264": {
                "heightPixels": 360,
                "widthPixels": 640,
                "bitrateBps": 550000,
                "frameRate": 60
              }
            }
          },
          {
            "key": "audio-stream0",
            "audioStream": {
              "codec": "aac",
              "bitrateBps": 64000
            }
          }
        ],
        "muxStreams": [
          {
            "key": "sd",
            "container": "mp4",
            "elementaryStreams": [
              "video-stream0",
              "audio-stream0"
            ]
          }
        ],
        "output": {
          "uri": "gs://STORAGE_BUCKET_NAME/STORAGE_OUTPUT_FOLDER/"
        }
      }
    }
  2. Jalankan perintah berikut:
    gcloud transcoder jobs create --location=LOCATION --file="request.json"
    Anda akan melihat respons seperti berikut:
    {
      "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
      "config": {
       ...
      },
      "state": "PENDING",
      "createTime": CREATE_TIME,
      "ttlAfterCompletionDays": 30
    }
    

C#

Sebelum mencoba contoh ini, ikuti petunjuk penyiapan C# di panduan memulai Transcoder API menggunakan library klien. Untuk mengetahui informasi selengkapnya, lihat dokumentasi referensi API C# Transcoder API.

Untuk melakukan autentikasi ke Transcoder API, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.


using Google.Api.Gax.ResourceNames;
using Google.Cloud.Video.Transcoder.V1;
using Google.Protobuf.WellKnownTypes;
using System;

public class CreateJobWithConcatenatedInputsSample
{
    public Job CreateJobWithConcatenatedInputs(
        string projectId, string location, string inputUri1, TimeSpan startTimeInput1, TimeSpan endTimeInput1, string inputUri2, TimeSpan startTimeInput2, TimeSpan endTimeInput2, string outputUri)
    {

        // Create the client.
        TranscoderServiceClient client = TranscoderServiceClient.Create();

        // Build the parent location name.
        LocationName parent = new LocationName(projectId, location);

        // Build the job config.
        VideoStream videoStream0 = new VideoStream
        {
            H264 = new VideoStream.Types.H264CodecSettings
            {
                BitrateBps = 550000,
                FrameRate = 60,
                HeightPixels = 360,
                WidthPixels = 640
            }
        };

        AudioStream audioStream0 = new AudioStream
        {
            Codec = "aac",
            BitrateBps = 64000
        };

        ElementaryStream elementaryStream0 = new ElementaryStream
        {
            Key = "video_stream0",
            VideoStream = videoStream0
        };

        ElementaryStream elementaryStream1 = new ElementaryStream
        {
            Key = "audio_stream0",
            AudioStream = audioStream0
        };

        MuxStream muxStream0 = new MuxStream
        {
            Key = "sd",
            Container = "mp4",
            ElementaryStreams = { "video_stream0", "audio_stream0" }
        };

        Input input1 = new Input
        {
            Key = "input1",
            Uri = inputUri1
        };

        Input input2 = new Input
        {
            Key = "input2",
            Uri = inputUri2
        };

        EditAtom atom1 = new EditAtom
        {
            Key = "atom1",
            StartTimeOffset = Duration.FromTimeSpan(startTimeInput1),
            EndTimeOffset = Duration.FromTimeSpan(endTimeInput1),
            Inputs = { input1.Key }
        };

        EditAtom atom2 = new EditAtom
        {
            Key = "atom2",
            StartTimeOffset = Duration.FromTimeSpan(startTimeInput2),
            EndTimeOffset = Duration.FromTimeSpan(endTimeInput2),
            Inputs = { input2.Key }
        };

        Output output = new Output
        {
            Uri = outputUri
        };

        JobConfig jobConfig = new JobConfig
        {
            Inputs = { input1, input2 },
            EditList = { atom1, atom2 },
            Output = output,
            ElementaryStreams = { elementaryStream0, elementaryStream1 },
            MuxStreams = { muxStream0 }
        };

        // Build the job.
        Job newJob = new Job
        {
            OutputUri = outputUri,
            Config = jobConfig
        };

        // Call the API.
        Job job = client.CreateJob(parent, newJob);

        // Return the result.
        return job;
    }
}

Go

Sebelum mencoba contoh ini, ikuti petunjuk penyiapan Go di panduan memulai Transcoder API menggunakan library klien. Untuk mengetahui informasi selengkapnya, lihat dokumentasi referensi API Go Transcoder API.

Untuk melakukan autentikasi ke Transcoder API, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

import (
	"context"
	"fmt"
	"io"
	"time"

	transcoder "cloud.google.com/go/video/transcoder/apiv1"
	"cloud.google.com/go/video/transcoder/apiv1/transcoderpb"
	"google.golang.org/protobuf/types/known/durationpb"
)

// createJobWithConcatenatedInputs creates a job that concatenates two input
// videos. See https://cloud.google.com/transcoder/docs/how-to/concatenate-videos
// for more information.
func createJobWithConcatenatedInputs(w io.Writer, projectID string, location string, input1URI string, startTimeInput1 time.Duration, endTimeInput1 time.Duration, input2URI string, startTimeInput2 time.Duration, endTimeInput2 time.Duration, outputURI string) error {
	// projectID := "my-project-id"
	// location := "us-central1"
	//
	// input1URI := "gs://my-bucket/my-video-file1"
	// startTimeInput1 := 0*time.Second
	// endTimeInput1 := 8*time.Second + 100*time.Millisecond
	//
	// input2URI := "gs://my-bucket/my-video-file2"
	// startTimeInput2 := 3*time.Second + 500*time.Millisecond
	// endTimeInput2 := 15*time.Second
	//
	// outputURI := "gs://my-bucket/my-output-folder/"

	ctx := context.Background()
	client, err := transcoder.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("NewClient: %w", 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: "input1",
							Uri: input1URI,
						},
						{
							Key: "input2",
							Uri: input2URI,
						},
					},
					EditList: []*transcoderpb.EditAtom{
						{
							Key:             "atom1",
							Inputs:          []string{"input1"},
							StartTimeOffset: durationpb.New(startTimeInput1),
							EndTimeOffset:   durationpb.New(endTimeInput1),
						},
						{
							Key:             "atom2",
							Inputs:          []string{"input2"},
							StartTimeOffset: durationpb.New(startTimeInput2),
							EndTimeOffset:   durationpb.New(endTimeInput2),
						},
					},
					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,
								},
							},
						},
					},
					MuxStreams: []*transcoderpb.MuxStream{
						{
							Key:               "sd",
							Container:         "mp4",
							ElementaryStreams: []string{"video_stream0", "audio_stream0"},
						},
					},
				},
			},
		},
	}
	// 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: %w", err)
	}

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

Java

Sebelum mencoba contoh ini, ikuti petunjuk penyiapan Java di panduan memulai Transcoder API menggunakan library klien. Untuk mengetahui informasi selengkapnya, lihat dokumentasi referensi API Java Transcoder API.

Untuk melakukan autentikasi ke Transcoder API, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.


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.MuxStream;
import com.google.cloud.video.transcoder.v1.Output;
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 CreateJobWithConcatenatedInputs {

  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 inputUri1 = "gs://my-bucket/my-video-file1";
    Duration startTimeInput1 = Duration.newBuilder().setSeconds(0).setNanos(0).build();
    Duration endTimeInput1 = Duration.newBuilder().setSeconds(8).setNanos(100000000).build();
    String inputUri2 = "gs://my-bucket/my-video-file2";
    Duration startTimeInput2 = Duration.newBuilder().setSeconds(3).setNanos(500000000).build();
    Duration endTimeInput2 = Duration.newBuilder().setSeconds(15).setNanos(0).build();
    String outputUri = "gs://my-bucket/my-output-folder/";

    createJobWithConcatenatedInputs(
        projectId,
        location,
        inputUri1,
        startTimeInput1,
        endTimeInput1,
        inputUri2,
        startTimeInput2,
        endTimeInput2,
        outputUri);
  }

  // Creates a job from an ad-hoc configuration that concatenates two input videos.
  public static void createJobWithConcatenatedInputs(
      String projectId,
      String location,
      String inputUri1,
      Duration startTimeInput1,
      Duration endTimeInput1,
      String inputUri2,
      Duration startTimeInput2,
      Duration endTimeInput2,
      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();

      JobConfig config =
          JobConfig.newBuilder()
              .addInputs(Input.newBuilder().setKey("input1").setUri(inputUri1))
              .addInputs(Input.newBuilder().setKey("input2").setUri(inputUri2))
              .setOutput(Output.newBuilder().setUri(outputUri))
              .addElementaryStreams(
                  ElementaryStream.newBuilder()
                      .setKey("video_stream0")
                      .setVideoStream(videoStream0))
              .addElementaryStreams(
                  ElementaryStream.newBuilder()
                      .setKey("audio_stream0")
                      .setAudioStream(audioStream0))
              .addMuxStreams(
                  MuxStream.newBuilder()
                      .setKey("sd")
                      .setContainer("mp4")
                      .addElementaryStreams("video_stream0")
                      .addElementaryStreams("audio_stream0")
                      .build())
              .addEditList(
                  0, // Index in the edit list
                  EditAtom.newBuilder()
                      .setKey("atom1")
                      .addInputs("input1")
                      .setStartTimeOffset(startTimeInput1)
                      .setEndTimeOffset(endTimeInput1)
                      .build())
              .addEditList(
                  1, // Index in the edit list
                  EditAtom.newBuilder()
                      .setKey("atom2")
                      .addInputs("input2")
                      .setStartTimeOffset(startTimeInput2)
                      .setEndTimeOffset(endTimeInput2)
                      .build())
              .build();

      CreateJobRequest 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

Sebelum mencoba contoh ini, ikuti petunjuk penyiapan Node.js di panduan memulai Transcoder API menggunakan library klien. Untuk mengetahui informasi selengkapnya, lihat dokumentasi referensi API Node.js Transcoder API.

Untuk melakukan autentikasi ke Transcoder API, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// projectId = 'my-project-id';
// location = 'us-central1';
// inputUri1 = 'gs://my-bucket/my-video-file1';
// startTimeOffset1 = 0;
// endTimeOffset1 = 8.1;
// inputUri2 = 'gs://my-bucket/my-video-file2';
// startTimeOffset2 = 3.5;
// endTimeOffset2 = 15;
// outputUri = 'gs://my-bucket/my-output-folder/';

function calcOffsetNanoSec(offsetValueFractionalSecs) {
  if (offsetValueFractionalSecs.toString().indexOf('.') !== -1) {
    return (
      1000000000 *
      Number('.' + offsetValueFractionalSecs.toString().split('.')[1])
    );
  }
  return 0;
}
const startTimeOffset1Sec = Math.trunc(startTimeOffset1);
const startTimeOffset1NanoSec = calcOffsetNanoSec(startTimeOffset1);
const endTimeOffset1Sec = Math.trunc(endTimeOffset1);
const endTimeOffset1NanoSec = calcOffsetNanoSec(endTimeOffset1);

const startTimeOffset2Sec = Math.trunc(startTimeOffset2);
const startTimeOffset2NanoSec = calcOffsetNanoSec(startTimeOffset2);
const endTimeOffset2Sec = Math.trunc(endTimeOffset2);
const endTimeOffset2NanoSec = calcOffsetNanoSec(endTimeOffset2);

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

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

async function createJobWithConcatenatedInputs() {
  // Construct request
  const request = {
    parent: transcoderServiceClient.locationPath(projectId, location),
    job: {
      outputUri: outputUri,
      config: {
        inputs: [
          {
            key: 'input1',
            uri: inputUri1,
          },
          {
            key: 'input2',
            uri: inputUri2,
          },
        ],
        editList: [
          {
            key: 'atom1',
            inputs: ['input1'],
            startTimeOffset: {
              seconds: startTimeOffset1Sec,
              nanos: startTimeOffset1NanoSec,
            },
            endTimeOffset: {
              seconds: endTimeOffset1Sec,
              nanos: endTimeOffset1NanoSec,
            },
          },
          {
            key: 'atom2',
            inputs: ['input2'],
            startTimeOffset: {
              seconds: startTimeOffset2Sec,
              nanos: startTimeOffset2NanoSec,
            },
            endTimeOffset: {
              seconds: endTimeOffset2Sec,
              nanos: endTimeOffset2NanoSec,
            },
          },
        ],

        elementaryStreams: [
          {
            key: 'video-stream0',
            videoStream: {
              h264: {
                heightPixels: 360,
                widthPixels: 640,
                bitrateBps: 550000,
                frameRate: 60,
              },
            },
          },
          {
            key: 'audio-stream0',
            audioStream: {
              codec: 'aac',
              bitrateBps: 64000,
            },
          },
        ],
        muxStreams: [
          {
            key: 'sd',
            container: 'mp4',
            elementaryStreams: ['video-stream0', 'audio-stream0'],
          },
        ],
      },
    },
  };

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

createJobWithConcatenatedInputs();

PHP

Sebelum mencoba contoh ini, ikuti petunjuk penyiapan PHP di panduan memulai Transcoder API menggunakan library klien. Untuk mengetahui informasi selengkapnya, lihat dokumentasi referensi API PHP Transcoder API.

Untuk melakukan autentikasi ke Transcoder API, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

use Google\Cloud\Video\Transcoder\V1\AudioStream;
use Google\Cloud\Video\Transcoder\V1\Client\TranscoderServiceClient;
use Google\Cloud\Video\Transcoder\V1\CreateJobRequest;
use Google\Cloud\Video\Transcoder\V1\EditAtom;
use Google\Cloud\Video\Transcoder\V1\ElementaryStream;
use Google\Cloud\Video\Transcoder\V1\Input;
use Google\Cloud\Video\Transcoder\V1\Job;
use Google\Cloud\Video\Transcoder\V1\JobConfig;
use Google\Cloud\Video\Transcoder\V1\MuxStream;
use Google\Cloud\Video\Transcoder\V1\VideoStream;
use Google\Protobuf\Duration;

/**
 * Creates a job based on a supplied job config that concatenates two input videos.
 *
 * @param string $projectId The ID of your Google Cloud Platform project.
 * @param string $location The location of the job.
 * @param string $input1Uri Uri of the first video in the Cloud Storage bucket.
 * @param float  $startTimeInput1 Start time, in fractional seconds, relative to the first input video timeline.
 * @param float  $endTimeInput1 End time, in fractional seconds, relative to the first input video timeline.
 * @param string $input2Uri Uri of the second video in the Cloud Storage bucket.
 * @param float  $startTimeInput2 Start time, in fractional seconds, relative to the second input video timeline.
 * @param float  $endTimeInput2 End time, in fractional seconds, relative to the second input video timeline.
 * @param string $outputUri Uri of the video output folder in the Cloud Storage bucket.
 */
function create_job_with_concatenated_inputs($projectId, $location, $input1Uri, $startTimeInput1, $endTimeInput1, $input2Uri, $startTimeInput2, $endTimeInput2, $outputUri)
{
    $startTimeInput1Sec = (int) floor(abs($startTimeInput1));
    $startTimeInput1Nanos = (int) (1000000000 * bcsub((string) abs($startTimeInput1), (string) floor(abs($startTimeInput1)), 4));
    $endTimeInput1Sec = (int) floor(abs($endTimeInput1));
    $endTimeInput1Nanos = (int) (1000000000 * bcsub((string) abs($endTimeInput1), (string) floor(abs($endTimeInput1)), 4));

    $startTimeInput2Sec = (int) floor(abs($startTimeInput2));
    $startTimeInput2Nanos = (int) (1000000000 * bcsub((string) abs($startTimeInput2), (string) floor(abs($startTimeInput2)), 4));
    $endTimeInput2Sec = (int) floor(abs($endTimeInput2));
    $endTimeInput2Nanos = (int) (1000000000 * bcsub((string) abs($endTimeInput2), (string) floor(abs($endTimeInput2)), 4));

    // Instantiate a client.
    $transcoderServiceClient = new TranscoderServiceClient();

    $formattedParent = $transcoderServiceClient->locationName($projectId, $location);
    $jobConfig =
        (new JobConfig())->setInputs([
            (new Input())
                ->setKey('input1')
                ->setUri($input1Uri),
            (new Input())
                ->setKey('input2')
                ->setUri($input2Uri)
        ])->setEditList([
            (new EditAtom())
                ->setKey('atom1')
                ->setInputs(['input1'])
                ->setStartTimeOffset(new Duration(['seconds' => $startTimeInput1Sec, 'nanos' => $startTimeInput1Nanos]))
                ->setEndTimeOffset(new Duration(['seconds' => $endTimeInput1Sec, 'nanos' => $endTimeInput1Nanos])),
            (new EditAtom())
                ->setKey('atom2')
                ->setInputs(['input2'])
                ->setStartTimeOffset(new Duration(['seconds' => $startTimeInput2Sec, 'nanos' => $startTimeInput2Nanos]))
                ->setEndTimeOffset(new Duration(['seconds' => $endTimeInput2Sec, 'nanos' => $endTimeInput2Nanos])),
        ])->setElementaryStreams([
            (new ElementaryStream())
                ->setKey('video-stream0')
                ->setVideoStream(
                    (new VideoStream())->setH264(
                        (new VideoStream\H264CodecSettings())
                            ->setBitrateBps(550000)
                            ->setFrameRate(60)
                            ->setHeightPixels(360)
                            ->setWidthPixels(640)
                    )
                ),
            (new ElementaryStream())
                ->setKey('audio-stream0')
                ->setAudioStream(
                    (new AudioStream())
                        ->setCodec('aac')
                        ->setBitrateBps(64000)
                )
        ])->setMuxStreams([
            (new MuxStream())
                ->setKey('sd')
                ->setContainer('mp4')
                ->setElementaryStreams(['video-stream0', 'audio-stream0'])
        ]);

    $job = (new Job())
        ->setOutputUri($outputUri)
        ->setConfig($jobConfig);
    $request = (new CreateJobRequest())
        ->setParent($formattedParent)
        ->setJob($job);

    $response = $transcoderServiceClient->createJob($request);

    // Print job name.
    printf('Job: %s' . PHP_EOL, $response->getName());
}

Python

Sebelum mencoba contoh ini, ikuti petunjuk penyiapan Python di panduan memulai Transcoder API menggunakan library klien. Untuk mengetahui informasi selengkapnya, lihat dokumentasi referensi API Python Transcoder API.

Untuk melakukan autentikasi ke Transcoder API, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.


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_concatenated_inputs(
    project_id: str,
    location: str,
    input1_uri: str,
    start_time_input1: str,
    end_time_input1: str,
    input2_uri: str,
    start_time_input2: str,
    end_time_input2: str,
    output_uri: str,
) -> transcoder_v1.types.resources.Job:
    """Creates a job based on an ad-hoc job configuration that concatenates two input videos.

    Args:
        project_id (str): The GCP project ID.
        location (str): The location to start the job in.
        input1_uri (str): Uri of the first video in the Cloud Storage bucket.
        start_time_input1 (str): Start time, in fractional seconds ending in 's'
          (e.g., '0s'), relative to the first input video timeline.
        end_time_input1 (str): End time, in fractional seconds ending in 's'
          (e.g., '8.1s'), relative to the first input video timeline.
        input2_uri (str): Uri of the second video in the Cloud Storage bucket.
        start_time_input2 (str): Start time, in fractional seconds ending in 's'
          (e.g., '3.5s'), relative to the second input video timeline.
        end_time_input2 (str): End time, in fractional seconds ending in 's'
          (e.g., '15s'), relative to the second input video timeline.
        output_uri (str): Uri of the video output folder in the Cloud Storage
          bucket.

    Returns:
        The job resource.
    """

    s1 = duration.Duration()
    s1.FromJsonString(start_time_input1)
    e1 = duration.Duration()
    e1.FromJsonString(end_time_input1)

    s2 = duration.Duration()
    s2.FromJsonString(start_time_input2)
    e2 = duration.Duration()
    e2.FromJsonString(end_time_input2)

    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="input1",
                uri=input1_uri,
            ),
            transcoder_v1.types.Input(
                key="input2",
                uri=input2_uri,
            ),
        ],
        edit_list=[
            transcoder_v1.types.EditAtom(
                key="atom1",
                inputs=["input1"],
                start_time_offset=s1,
                end_time_offset=e1,
            ),
            transcoder_v1.types.EditAtom(
                key="atom2",
                inputs=["input2"],
                start_time_offset=s2,
                end_time_offset=e2,
            ),
        ],
        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
                ),
            ),
        ],
        mux_streams=[
            transcoder_v1.types.MuxStream(
                key="sd",
                container="mp4",
                elementary_streams=["video-stream0", "audio-stream0"],
            ),
        ],
    )
    response = client.create_job(parent=parent, job=job)
    print(f"Job: {response.name}")
    return response

Ruby

Sebelum mencoba contoh ini, ikuti petunjuk penyiapan Ruby di panduan memulai Transcoder API menggunakan library klien. Untuk mengetahui informasi selengkapnya, lihat dokumentasi referensi API Ruby Transcoder API.

Untuk melakukan autentikasi ke Transcoder API, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

# project_id        = # Your project ID, e.g. "my-project"
# location          = # Data location, e.g. "us-central1"
# input1_uri        = # First video, e.g. "gs://my-bucket/my-video-file1"
# start_time_input1 = # Start time in fractional seconds relative to the
#                     # first input video timeline, e.g. 0.0
# end_time_input1   = # End time in fractional seconds relative to the
#                     # first input video timeline, e.g. 8.125
# input2_uri        = # Second video, e.g. "gs://my-bucket/my-video-file2"
# start_time_input2 = # Start time in fractional seconds relative to the
#                     # second input video timeline, e.g. 3.5
# end_time_input2   = # End time in fractional seconds relative to the
#                     # second input video timeline, e.g. 15
# output_uri        = # Output folder, e.g. "gs://my-bucket/my-output-folder/"

s1_sec = start_time_input1.to_i
s1_nanos = (start_time_input1.to_f.remainder(1) * 1_000_000_000).round
e1_sec = end_time_input1.to_i
e1_nanos = (end_time_input1.to_f.remainder(1) * 1_000_000_000).round

s2_sec = start_time_input2.to_i
s2_nanos = (start_time_input2.to_f.remainder(1) * 1_000_000_000).round
e2_sec = end_time_input2.to_i
e2_nanos = (end_time_input2.to_f.remainder(1) * 1_000_000_000).round

# Require the Transcoder client library.
require "google/cloud/video/transcoder"

# Create a Transcoder client.
client = Google::Cloud::Video::Transcoder.transcoder_service

# Build the resource name of the parent.
parent = client.location_path project: project_id, location: location

# Build the job config.
new_job = {
  output_uri: output_uri,
  config: {
    inputs: [
      {
        key: "input1",
        uri: input1_uri
      },
      {
        key: "input2",
        uri: input2_uri
      }
    ],
    edit_list: [
      {
        key: "atom1",
        inputs: ["input1"],
        start_time_offset: {
          seconds: s1_sec,
          nanos: s1_nanos
        },
        end_time_offset: {
          seconds: e1_sec,
          nanos: e1_nanos
        }
      },
      {
        key: "atom2",
        inputs: ["input2"],
        start_time_offset: {
          seconds: s2_sec,
          nanos: s2_nanos
        },
        end_time_offset: {
          seconds: e2_sec,
          nanos: e2_nanos
        }
      }
    ],
    elementary_streams: [
      {
        key: "video-stream0",
        video_stream: {
          h264: {
            height_pixels: 360,
            width_pixels: 640,
            bitrate_bps: 550_000,
            frame_rate: 60
          }
        }
      },
      {
        key: "audio-stream0",
        audio_stream: {
          codec: "aac",
          bitrate_bps: 64_000
        }
      }
    ],
    mux_streams: [
      {
        key: "sd",
        container: "mp4",
        elementary_streams: [
          "video-stream0",
          "audio-stream0"
        ]
      }
    ]
  }
}

job = client.create_job parent: parent, job: new_job

# Print the job name.
puts "Job: #{job.name}"

Contoh

Perhatikan contoh video berikut:

Kedua video tersebut serupa karena berisi tiga bagian:

  1. Menonton film atau pertandingan di perangkat seluler
  2. Menonton konten yang sama di layar TV
  3. Menampilkan teks iklan singkat untuk produk

Misalnya, Anda dapat menyambungkan kedua video ini sehingga video output menampilkan bagian satu dan dua dari video pertama, lalu bagian dua dan tiga dari video kedua. Anda dapat melakukan penyambungan ini menggunakan offset waktu berikut:

Gunakan kode sebelumnya beserta dua video ini dan offset waktunya untuk melihat video hasil yang penuh aksi.