Concatenazione di più video in ingresso

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

In questa pagina viene spiegato come combinare più video di input in un singolo video di output. Puoi anche tagliare le sequenze temporali dei video di input.

Per ogni video di input, aggiungi un oggetto Input all'array di inputs. Ogni oggetto Input definisce la chiave e l'URI del video di input associato. Puoi aggiungere un oggetto PreprocessingConfig facoltativo a una Input per ritagliare, ritagliare o eseguire altre pre-elaborazioni sul video di input. L'array inputs non è ordinato; puoi aggiungere video di input in qualsiasi ordine.

Per aggiungere un video di input alla sequenza temporale di output del video, aggiungi un oggetto EditAtom all'array editList. L'array editList è ordinato. Il primo input designato in questo array verrà utilizzato per primo nel video di output, il secondo verrà utilizzato successivamente e così via. Puoi identificare un video di input in base alla relativa chiave.

Puoi anche impostare startTimeOffset e endTimeOffset per tagliare il video di input. Questi campi sono facoltativi. Se non specifichi questi campi, verrà utilizzato l'intero video di input.

La seguente configurazione concatena due video di input in un unico video di 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"
  }
],

Puoi aggiungere questa configurazione a un modello di job o includerla in una configurazione job ad-hoc:

REST

Prima di utilizzare i dati della richiesta, apporta le seguenti sostituzioni:

  • PROJECT_ID: il tuo ID progetto Google Cloud elencato nelle impostazioni IAM.
  • LOCATION: la località in cui verrà eseguito il job. Utilizza una delle regioni supportate:
    • 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: il nome del bucket Cloud Storage che hai creato.
  • STORAGE_INPUT_VIDEO1: il nome di un video nel bucket Cloud Storage che stai transcodificando, ad esempio my-vid.mp4. Questo campo deve tenere conto di tutte le cartelle che hai creato nel bucket (ad esempio input/my-vid.mp4). Questo video verrà utilizzato per primo nella sequenza temporale di output del video.
  • START_TIME_OFFSET1: l'ora di inizio, in secondi frazionari (ad esempio 0.0), rispetto alla sequenza temporale del primo input. Utilizza questo campo per tagliare i contenuti dall'inizio del video.
  • END_TIME_OFFSET1: l'ora di fine, in secondi frazionari (ad esempio 8.1), rispetto alla sequenza temporale del primo input. Utilizza questo campo per tagliare i contenuti dalla fine del video.
  • STORAGE_INPUT_VIDEO2: il nome di un video nel bucket Cloud Storage che stai transcodificando, ad esempio my-vid.mp4. Questo campo dovrebbe tenere conto di tutte le cartelle che hai creato nel bucket (ad esempio input/my-vid.mp4). Questo video verrà utilizzato per secondo nella sequenza temporale di output del video.
  • START_TIME_OFFSET2: l'ora di inizio, in secondi frazionari (ad esempio 3.5), rispetto alla sequenza temporale del secondo input. Utilizza questo campo per tagliare i contenuti dall'inizio del secondo video.
  • END_TIME_OFFSET2: l'ora di fine, in secondi frazionari (ad esempio 15), rispetto alla sequenza temporale del secondo input. Utilizza questo campo per tagliare i contenuti dalla fine del secondo video.
  • STORAGE_OUTPUT_FOLDER: il nome della cartella Cloud Storage in cui vuoi salvare gli output video codificati.

Testo JSON richiesta:

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

Per inviare la richiesta, espandi una delle seguenti opzioni:

Dovresti ricevere una risposta JSON simile alla seguente:

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

gcloud CLI

  1. Crea un file request.json che definisce i campi del job. Esegui le seguenti sostituzioni per il comando gcloud:
    • LOCATION: la località in cui verrà eseguito il job. Utilizza una delle regioni supportate:
      • 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: il nome del bucket Cloud Storage che hai creato.
    • STORAGE_INPUT_VIDEO1: il nome di un video nel bucket Cloud Storage che stai transcodificando, ad esempio my-vid.mp4. Questo campo deve tenere conto di tutte le cartelle che hai creato nel bucket (ad esempio input/my-vid.mp4). Questo video verrà utilizzato per primo nella sequenza temporale di output del video.
    • START_TIME_OFFSET1: l'ora di inizio, in secondi frazionari (ad esempio 0.0), rispetto alla sequenza temporale del primo input. Utilizza questo campo per tagliare i contenuti dall'inizio del video.
    • END_TIME_OFFSET1: l'ora di fine, in secondi frazionari (ad esempio 8.1), rispetto alla sequenza temporale del primo input. Utilizza questo campo per tagliare i contenuti dalla fine del video.
    • STORAGE_INPUT_VIDEO2: il nome di un video nel bucket Cloud Storage che stai transcodificando, ad esempio my-vid.mp4. Questo campo dovrebbe tenere conto di tutte le cartelle che hai creato nel bucket (ad esempio input/my-vid.mp4). Questo video verrà utilizzato per secondo nella sequenza temporale di output del video.
    • START_TIME_OFFSET2: l'ora di inizio, in secondi frazionari (ad esempio 3.5), rispetto alla sequenza temporale del secondo input. Utilizza questo campo per tagliare i contenuti dall'inizio del secondo video.
    • END_TIME_OFFSET2: l'ora di fine, in secondi frazionari (ad esempio 15), rispetto alla sequenza temporale del secondo input. Utilizza questo campo per tagliare i contenuti dalla fine del secondo video.
    • STORAGE_OUTPUT_FOLDER: il nome della cartella Cloud Storage in cui vuoi salvare gli output video codificati.
    {
      "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. Esegui questo comando:
    gcloud transcoder jobs create --location=LOCATION --file="request.json"
    
    Dovresti vedere una risposta simile alla seguente:
    {
      "name": "projects/PROJECT_NUMBER/locations/LOCATION/jobs/JOB_ID",
      "config": {
       ...
      },
      "state": "PENDING",
      "createTime": CREATE_TIME,
      "ttlAfterCompletionDays": 30
    }
    

C#

Prima di provare questo esempio, segui le istruzioni per la configurazione di C# nella guida rapida all'API Transcoder mediante le librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento per l'API Transcoder API C#.


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

Prima di provare questo esempio, segui le istruzioni per la configurazione di Go nella guida rapida all'API Transcoder mediante le librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento per l'API Transcoder API Go.

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: %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
}

Java

Prima di provare questo esempio, segui le istruzioni per la configurazione di Java nella guida rapida all'API Transcoder mediante le librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento per l'API Transcoder API Java.


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

Prima di provare questo esempio, segui le istruzioni per la configurazione di Node.js nella guida rapida all'API Transcoder mediante le librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento per l'API Transcoder API Node.js.

/**
 * 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

Prima di provare questo esempio, segui le istruzioni per la configurazione di PHP nella guida rapida all'API Transcoder mediante le librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento per l'API Transcoder API PHP.

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

Prima di provare questo esempio, segui le istruzioni per la configurazione di Python nella guida rapida all'API Transcoder mediante le librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento per l'API Transcoder API Python.


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

Prima di provare questo esempio, segui le istruzioni per la configurazione di Ruby nella guida rapida all'API Transcoder mediante le librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento per l'API Transcoder API Ruby.

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

Esempio

Considera i seguenti video di esempio:

Entrambi i video sono simili in quanto contengono tre parti:

  1. Guardare un film o un gioco su un dispositivo mobile
  2. Guardare gli stessi contenuti su uno schermo di casa
  3. Mostra un breve testo pubblicitario per il prodotto

Ad esempio, puoi concatenare questi due video in modo che il video di output mostri le parti una e due del primo video, e poi le parti due e tre del secondo video. Puoi eseguire questa concatenazione utilizzando i seguenti offset di tempo:

Utilizza il codice precedente insieme a questi due video e ai relativi offset temporali per visualizzare un video dei risultati ricco di azioni.