Skip to content

Commit

Permalink
samples: add code sample and test for concatenating two input videos (#…
Browse files Browse the repository at this point in the history
…133)

* feat: add code sample and test for concatenating two input videos

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
  • Loading branch information
irataxy and gcf-owl-bot[bot] committed Dec 20, 2021
1 parent 929c973 commit 4c3725a
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 0 deletions.
158 changes: 158 additions & 0 deletions media/transcoder/createJobWithConcatenatedInputs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* Copyright 2021, Google, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

function main(
projectId,
location,
inputUri1,
startTimeOffset1,
endTimeOffset1,
inputUri2,
startTimeOffset2,
endTimeOffset2,
outputUri
) {
// [START transcoder_create_job_with_concatenated_inputs]
/**
* 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();
// [END transcoder_create_job_with_concatenated_inputs]
}

// node createJobFromStaticOverlay.js <projectId> <location> <inputUri1> <startTimeOffset1> <endTimeOffset1> <inputUri2> <startTimeOffset2> <endTimeOffset2> <outputUri>
process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(...process.argv.slice(2));
60 changes: 60 additions & 0 deletions media/transcoder/test/transcoder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ const templateName = `projects/${projectNumber}/locations/${location}/jobTemplat

const testFileName = 'ChromeCast.mp4';
const testOverlayFileName = 'overlay.jpg';
const testConcat1FileName = 'ForBiggerEscapes.mp4';
const testConcat2FileName = 'ForBiggerJoyrides.mp4';

const inputUri = `gs://${bucketName}/${testFileName}`;
const overlayUri = `gs://${bucketName}/${testOverlayFileName}`;
const concat1Uri = `gs://${bucketName}/${testConcat1FileName}`;
const concat2Uri = `gs://${bucketName}/${testConcat2FileName}`;
const outputUriForPreset = `gs://${bucketName}/test-output-preset/`;
const outputUriForTemplate = `gs://${bucketName}/test-output-template/`;
const outputUriForAdHoc = `gs://${bucketName}/test-output-adhoc/`;
Expand All @@ -53,10 +57,13 @@ const outputUriForPeriodicImagesSpritesheet = `gs://${bucketName}/${outputDirFor
// Spritesheets use the following file naming conventions:
const smallSpriteSheetFileName = 'small-sprite-sheet0000000000.jpeg';
const largeSpriteSheetFileName = 'large-sprite-sheet0000000000.jpeg';
const outputUriForConcatenated = `gs://${bucketName}/test-output-concat/`;

const cwd = path.join(__dirname, '..');
const videoFile = `testdata/${testFileName}`;
const overlayFile = `testdata/${testOverlayFileName}`;
const concat1File = `testdata/${testConcat1FileName}`;
const concat2File = `testdata/${testConcat2FileName}`;

const delay = async (test, addMs) => {
const retries = test.currentRetry();
Expand Down Expand Up @@ -98,6 +105,8 @@ before(async () => {
await storage.createBucket(bucketName);
await storage.bucket(bucketName).upload(videoFile);
await storage.bucket(bucketName).upload(overlayFile);
await storage.bucket(bucketName).upload(concat1File);
await storage.bucket(bucketName).upload(concat2File);
});

after(async () => {
Expand Down Expand Up @@ -591,3 +600,54 @@ describe('Job with periodic images spritesheet', () => {
);
});
});

describe('Job with concatenated inputs functions', () => {
before(function () {
const output = execSync(
`node createJobWithConcatenatedInputs.js ${projectId} ${location} ${concat1Uri} 0 8.1 ${concat2Uri} 3.5 15 ${outputUriForConcatenated}`,
{cwd}
);
assert.ok(
output.includes(`projects/${projectNumber}/locations/${location}/jobs/`)
);
this.concatenatedJobId = output.toString().split('/').pop();
});

after(function () {
const output = execSync(
`node deleteJob.js ${projectId} ${location} ${this.concatenatedJobId}`,
{cwd}
);
assert.ok(output.includes('Deleted job'));
});

it('should get a job', function () {
const output = execSync(
`node getJob.js ${projectId} ${location} ${this.concatenatedJobId}`,
{cwd}
);
const jobName = `projects/${projectNumber}/locations/${location}/jobs/${this.concatenatedJobId}`;
assert.ok(output.includes(jobName));
});

it('should check that the job succeeded', async function () {
this.retries(5);
await delay(this.test, 30000);

let getAttempts = 0;
while (getAttempts < 5) {
const ms = Math.pow(2, getAttempts + 1) * 10000 + Math.random() * 1000;
await wait(ms);
const output = execSync(
`node getJobState.js ${projectId} ${location} ${this.concatenatedJobId}`,
{cwd}
);
if (output.includes('Job state: SUCCEEDED')) {
assert.ok(true);
return;
}
getAttempts++;
}
assert.ok(false);
});
});
Binary file added media/transcoder/testdata/ForBiggerEscapes.mp4
Binary file not shown.
Binary file added media/transcoder/testdata/ForBiggerJoyrides.mp4
Binary file not shown.

0 comments on commit 4c3725a

Please sign in to comment.