여러 입력 동영상 연결

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

이 페이지에서는 여러 입력 동영상을 단일 출력 동영상으로 결합하는 방법을 설명합니다. 입력 동영상의 타임라인을 자를 수도 있습니다.

입력 동영상마다 Input 객체를 inputs 배열에 추가합니다. 각 Input 객체는 연결된 입력 동영상의 키와 URI를 정의합니다. 선택적 PreprocessingConfig 객체를 Input에 추가하여 입력 동영상에서 자르기, 패딩 또는 다른 사전 처리를 수행할 수 있습니다. inputs 배열은 정렬되지 않습니다. 입력 동영상을 원하는 순서로 추가할 수 있습니다.

입력 동영상을 출력 동영상 타임라인에 추가하려면 EditAtom 객체를 editList 배열에 추가합니다. editList 배열이 정렬됩니다. 이 배열에 지정된 첫 번째 입력이 출력 동영상에서 먼저 사용되고 두 번째 입력이 다음으로 사용됩니다. 키를 기준으로 입력 동영상을 식별합니다. startTimeOffsetendTimeOffset을 지정하여 입력 동영상을 자를 수도 있습니다.

다음 구성에서는 입력 동영상 두 개를 단일 출력 동영상으로 연결합니다.

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

이 구성을 작업 템플릿에 추가하거나 임시 작업 구성에 포함할 수 있습니다.

REST 및 명령줄

요청 데이터를 사용하기 전에 다음을 바꿉니다.

  • PROJECT_ID: IAM 설정에 나열된 Google Cloud 프로젝트 ID입니다.
  • LOCATION: 작업이 실행될 위치입니다. 지원되는 리전 중 하나를 사용합니다.
    • us-central1
    • us-west1
    • us-west2
    • us-east1
    • us-east4
    • southamerica-east1
    • asia-east1
    • asia-south1
    • asia-southeast1
    • europe-west1
    • europe-west2
    • europe-west4
  • STORAGE_BUCKET_NAME: 만든 Cloud Storage 버킷의 이름입니다.
  • STORAGE_INPUT_VIDEO1: 트랜스코딩하는 Cloud Storage 버킷에 있는 동영상의 이름입니다(예: my-vid.mp4). 이 필드는 버킷에서 만든 모든 폴더를 고려해야 합니다(예: input/my-vid.mp4). 이 동영상은 출력 동영상 타임라인에서 먼저 사용됩니다.
  • START_TIME_OFFSET1: 첫 번째 입력 동영상 타임라인을 기준으로 시작 시간(소수부 초 단위)입니다(예: 0.0). 이 필드를 사용하여 동영상 시작 부분에서 콘텐츠를 자릅니다.
  • END_TIME_OFFSET1: 첫 번째 입력 동영상 타임라인을 기준으로 종료 시간(소수부 초 단위)입니다(예: 8.1). 이 필드를 사용하여 동영상 끝 부분에서 콘텐츠를 자릅니다.
  • STORAGE_INPUT_VIDEO2: 트랜스코딩하는 Cloud Storage 버킷에 있는 동영상의 이름입니다(예: my-vid.mp4). 이 필드는 버킷에서 만든 모든 폴더를 고려해야 합니다(예: input/my-vid.mp4). 이 동영상은 출력 동영상 타임라인에서 두 번째로 사용됩니다.
  • START_TIME_OFFSET2: 두 번째 입력 동영상 타임라인을 기준으로 시작 시간(소수부 초 단위)입니다(예: 3.5). 이 필드를 사용하여 두 번째 동영상 시작 부분에서 콘텐츠를 자릅니다.
  • END_TIME_OFFSET2: 두 번째 입력 동영상 타임라인을 기준으로 종료 시간(소수부 초 단위)입니다(예: 15). 이 필드를 사용하여 두 번째 동영상 끝 부분에서 콘텐츠를 자릅니다.
  • STORAGE_OUTPUT_FOLDER: 인코딩된 동영상 출력을 저장할 Cloud Storage 폴더 이름입니다.

JSON 요청 본문:

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

요청을 보내려면 다음 옵션 중 하나를 펼칩니다.

다음과 비슷한 JSON 응답이 표시됩니다.

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

gcloud

  1. 작업 필드를 정의하는 request.json 파일을 만듭니다. gcloud 명령어를 다음과 같이 바꿉니다.
    • LOCATION: 작업이 실행될 위치입니다. 지원되는 리전 중 하나를 사용합니다.
      • us-central1
      • us-west1
      • us-west2
      • us-east1
      • us-east4
      • southamerica-east1
      • asia-east1
      • asia-south1
      • asia-southeast1
      • europe-west1
      • europe-west2
      • europe-west4
    • STORAGE_BUCKET_NAME: 만든 Cloud Storage 버킷의 이름입니다.
    • STORAGE_INPUT_VIDEO1: 트랜스코딩하는 Cloud Storage 버킷에 있는 동영상의 이름입니다(예: my-vid.mp4). 이 필드는 버킷에서 만든 모든 폴더를 고려해야 합니다(예: input/my-vid.mp4). 이 동영상은 출력 동영상 타임라인에서 먼저 사용됩니다.
    • START_TIME_OFFSET1: 첫 번째 입력 동영상 타임라인을 기준으로 시작 시간(소수부 초 단위)입니다(예: 0.0). 이 필드를 사용하여 동영상 시작 부분에서 콘텐츠를 자릅니다.
    • END_TIME_OFFSET1: 첫 번째 입력 동영상 타임라인을 기준으로 종료 시간(소수부 초 단위)입니다(예: 8.1). 이 필드를 사용하여 동영상 끝 부분에서 콘텐츠를 자릅니다.
    • STORAGE_INPUT_VIDEO2: 트랜스코딩하는 Cloud Storage 버킷에 있는 동영상의 이름입니다(예: my-vid.mp4). 이 필드는 버킷에서 만든 모든 폴더를 고려해야 합니다(예: input/my-vid.mp4). 이 동영상은 출력 동영상 타임라인에서 두 번째로 사용됩니다.
    • START_TIME_OFFSET2: 두 번째 입력 동영상 타임라인을 기준으로 시작 시간(소수부 초 단위)입니다(예: 3.5). 이 필드를 사용하여 두 번째 동영상 시작 부분에서 콘텐츠를 자릅니다.
    • END_TIME_OFFSET2: 두 번째 입력 동영상 타임라인을 기준으로 종료 시간(소수부 초 단위)입니다(예: 15). 이 필드를 사용하여 두 번째 동영상 끝 부분에서 콘텐츠를 자릅니다.
    • STORAGE_OUTPUT_FOLDER: 인코딩된 동영상 출력을 저장할 Cloud Storage 폴더 이름입니다.
    {
      "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. 다음 명령어를 실행합니다.
    gcloud transcoder jobs create --location=LOCATION --file="request.json"
    
    다음과 비슷한 응답이 표시됩니다.
    {
      "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
      "config": {
       ...
      },
      "state": "PENDING",
      "createTime": CREATE_TIME,
      "ttlAfterCompletionDays": 30
    }
    

C#

이 샘플을 사용해 보기 전에 Transcoder API 빠른 시작: 클라이언트 라이브러리 사용의 C# 설정 안내를 따르세요. 자세한 내용은 Transcoder API C# API 참조 문서를 확인하세요.


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

이 샘플을 사용해 보기 전에 Transcoder API 빠른 시작: 클라이언트 라이브러리 사용의 Go 설정 안내를 따르세요. 자세한 내용은 Transcoder API Go API 참조 문서를 확인하세요.

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

	transcoder "cloud.google.com/go/video/transcoder/apiv1"
	transcoderpb "google.golang.org/genproto/googleapis/cloud/video/transcoder/v1"
	"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: %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: "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: %v", err)
	}

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

자바

이 샘플을 사용해 보기 전에 Transcoder API 빠른 시작: 클라이언트 라이브러리 사용의 자바 설정 안내를 따르세요. 자세한 내용은 Transcoder API 자바 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.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();

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

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

Node.js

이 샘플을 사용해 보기 전에 Transcoder API 빠른 시작: 클라이언트 라이브러리 사용의 Node.js 설정 안내를 따르세요. 자세한 내용은 Transcoder API Node.js API 참조 문서를 확인하세요.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// projectId = 'my-project-id';
// location = 'us-central1';
// 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

이 샘플을 사용해 보기 전에 Transcoder API 빠른 시작: 클라이언트 라이브러리 사용의 PHP 설정 안내를 따르세요. 자세한 내용은 Transcoder API PHP API 참조 문서를 확인하세요.

use Google\Cloud\Video\Transcoder\V1\AudioStream;
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\TranscoderServiceClient;
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(abs($startTimeInput1), floor(abs($startTimeInput1)), 4));
    $endTimeInput1Sec = (int) floor(abs($endTimeInput1));
    $endTimeInput1Nanos = (int) (1000000000 * bcsub(abs($endTimeInput1), floor(abs($endTimeInput1)), 4));

    $startTimeInput2Sec = (int) floor(abs($startTimeInput2));
    $startTimeInput2Nanos = (int) (1000000000 * bcsub(abs($startTimeInput2), floor(abs($startTimeInput2)), 4));
    $endTimeInput2Sec = (int) floor(abs($endTimeInput2));
    $endTimeInput2Nanos = (int) (1000000000 * bcsub(abs($endTimeInput2), 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);

    $response = $transcoderServiceClient->createJob($formattedParent, $job);

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

Python

이 샘플을 사용해 보기 전에 Transcoder API 빠른 시작: 클라이언트 라이브러리 사용의 Python 설정 안내를 따르세요. 자세한 내용은 Transcoder API Python API 참조 문서를 확인하세요.


import argparse

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

def create_job_with_concatenated_inputs(
    project_id,
    location,
    input1_uri,
    start_time_input1,
    end_time_input1,
    input2_uri,
    start_time_input2,
    end_time_input2,
    output_uri,
):
    """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."""

    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

이 샘플을 사용해 보기 전에 Transcoder API 빠른 시작: 클라이언트 라이브러리 사용의 Ruby 설정 안내를 따르세요. 자세한 내용은 Transcoder API Ruby API 참조 문서를 확인하세요.

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

다음 샘플 동영상을 살펴보세요.

이 두 동영상은 아래 세 파트를 포함한다는 점에서 유사합니다.

  1. 휴대기기에서 영화 또는 게임 보기
  2. 큰 화면에서 동일한 콘텐츠 보기
  3. 제품에 대한 간략한 광고 문구 표시

예를 들어 출력 동영상에서 첫 번째 동영상의 1부와 2부를 보여주고 이후에 두 번째 동영상의 2부와 3부를 보여주는 것과 같이 동영상 두 개를 연결할 수 있습니다. 다음과 같은 타임스탬프를 사용하여 이 연결을 수행할 수 있습니다.

액션이 가득한 결과 동영상을 보려면 이 두 동영상 및 시차와 함께 앞의 코드를 사용합니다.