Skip to content

Commit 3beaa75

Browse files
authored
feat: add code sample and test for concatenating two input videos (GoogleCloudPlatform#1574)
1 parent aac52bf commit 3beaa75

File tree

6 files changed

+185
-2
lines changed

6 files changed

+185
-2
lines changed

media/transcoder/README

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,17 @@ See the [Transcoder Documentation](https://cloud.google.com/transcoder/docs/) fo
5050

5151
## Troubleshooting
5252

53-
* See the [Troubleshooting guide](https://cloud.google.com/transcoder/docs/troubleshooting) for more information.
53+
### bcmath extension missing
54+
55+
If you see an error like this:
56+
57+
```
58+
PHP Fatal error: Uncaught Error: Call to undefined function Google\Protobuf\Internal\bcsub()
59+
```
60+
61+
You need to install the BC-Math extension.
62+
63+
See the [Troubleshooting guide](https://cloud.google.com/transcoder/docs/troubleshooting) for more information.
5464

5565
## Contributing changes
5666

media/transcoder/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"require": {
33
"google/cloud-video-transcoder": "^0.3.0",
4-
"google/cloud-storage": "^1.9"
4+
"google/cloud-storage": "^1.9",
5+
"ext-bcmath": "*"
56
}
67
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
2+
3+
/**
4+
* Copyright 2021 Google Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* For instructions on how to run the samples:
21+
*
22+
* @see https://github.com/GoogleCloudPlatform/php-docs-samples/tree/master/media/transcoder/README.md
23+
*/
24+
25+
namespace Google\Cloud\Samples\Media\Transcoder;
26+
27+
# [START transcoder_create_job_with_concatenated_inputs]
28+
use Google\Cloud\Video\Transcoder\V1\AudioStream;
29+
use Google\Cloud\Video\Transcoder\V1\EditAtom;
30+
use Google\Cloud\Video\Transcoder\V1\ElementaryStream;
31+
use Google\Cloud\Video\Transcoder\V1\Input;
32+
use Google\Cloud\Video\Transcoder\V1\Job;
33+
use Google\Cloud\Video\Transcoder\V1\JobConfig;
34+
use Google\Cloud\Video\Transcoder\V1\MuxStream;
35+
use Google\Cloud\Video\Transcoder\V1\TranscoderServiceClient;
36+
use Google\Cloud\Video\Transcoder\V1\VideoStream;
37+
use Google\Protobuf\Duration;
38+
39+
/**
40+
* Creates a job based on a supplied job config that concatenates two input videos.
41+
*
42+
* @param string $projectId The ID of your Google Cloud Platform project.
43+
* @param string $location The location of the job.
44+
* @param string $input1Uri Uri of the first video in the Cloud Storage bucket.
45+
* @param float $startTimeInput1 Start time, in fractional seconds, relative to the first input video timeline.
46+
* @param float $endTimeInput1 End time, in fractional seconds, relative to the first input video timeline.
47+
* @param string $input2Uri Uri of the second video in the Cloud Storage bucket.
48+
* @param float $startTimeInput2 Start time, in fractional seconds, relative to the second input video timeline.
49+
* @param float $endTimeInput2 End time, in fractional seconds, relative to the second input video timeline.
50+
* @param string $outputUri Uri of the video output folder in the Cloud Storage bucket.
51+
*/
52+
function create_job_with_concatenated_inputs($projectId, $location, $input1Uri, $startTimeInput1, $endTimeInput1, $input2Uri, $startTimeInput2, $endTimeInput2, $outputUri)
53+
{
54+
$startTimeInput1Sec = (int) floor(abs($startTimeInput1));
55+
$startTimeInput1Nanos = (int) (1000000000 * bcsub(abs($startTimeInput1), floor(abs($startTimeInput1)), 4));
56+
$endTimeInput1Sec = (int) floor(abs($endTimeInput1));
57+
$endTimeInput1Nanos = (int) (1000000000 * bcsub(abs($endTimeInput1), floor(abs($endTimeInput1)), 4));
58+
59+
$startTimeInput2Sec = (int) floor(abs($startTimeInput2));
60+
$startTimeInput2Nanos = (int) (1000000000 * bcsub(abs($startTimeInput2), floor(abs($startTimeInput2)), 4));
61+
$endTimeInput2Sec = (int) floor(abs($endTimeInput2));
62+
$endTimeInput2Nanos = (int) (1000000000 * bcsub(abs($endTimeInput2), floor(abs($endTimeInput2)), 4));
63+
64+
// Instantiate a client.
65+
$transcoderServiceClient = new TranscoderServiceClient();
66+
67+
$formattedParent = $transcoderServiceClient->locationName($projectId, $location);
68+
$jobConfig =
69+
(new JobConfig())->setInputs([
70+
(new Input())
71+
->setKey('input1')
72+
->setUri($input1Uri),
73+
(new Input())
74+
->setKey('input2')
75+
->setUri($input2Uri)
76+
])->setEditList([
77+
(new EditAtom())
78+
->setKey('atom1')
79+
->setInputs(['input1'])
80+
->setStartTimeOffset(new Duration(['seconds' => $startTimeInput1Sec, 'nanos' => $startTimeInput1Nanos]))
81+
->setEndTimeOffset(new Duration(['seconds' => $endTimeInput1Sec, 'nanos' => $endTimeInput1Nanos])),
82+
(new EditAtom())
83+
->setKey('atom2')
84+
->setInputs(['input2'])
85+
->setStartTimeOffset(new Duration(['seconds' => $startTimeInput2Sec, 'nanos' => $startTimeInput2Nanos]))
86+
->setEndTimeOffset(new Duration(['seconds' => $endTimeInput2Sec, 'nanos' => $endTimeInput2Nanos])),
87+
])->setElementaryStreams([
88+
(new ElementaryStream())
89+
->setKey('video-stream0')
90+
->setVideoStream(
91+
(new VideoStream())->setH264(
92+
(new VideoStream\H264CodecSettings())
93+
->setBitrateBps(550000)
94+
->setFrameRate(60)
95+
->setHeightPixels(360)
96+
->setWidthPixels(640)
97+
)
98+
),
99+
(new ElementaryStream())
100+
->setKey('audio-stream0')
101+
->setAudioStream(
102+
(new AudioStream())
103+
->setCodec('aac')
104+
->setBitrateBps(64000)
105+
)
106+
])->setMuxStreams([
107+
(new MuxStream())
108+
->setKey('sd')
109+
->setContainer('mp4')
110+
->setElementaryStreams(['video-stream0', 'audio-stream0'])
111+
]);
112+
113+
$job = (new Job())
114+
->setOutputUri($outputUri)
115+
->setConfig($jobConfig);
116+
117+
$response = $transcoderServiceClient->createJob($formattedParent, $job);
118+
119+
// Print job name.
120+
printf('Job: %s' . PHP_EOL, $response->getName());
121+
}
122+
# [END transcoder_create_job_with_concatenated_inputs]
123+
124+
require_once __DIR__ . '/../../../testing/sample_helpers.php';
125+
\Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv);
2.19 MB
Binary file not shown.
2.26 MB
Binary file not shown.

media/transcoder/test/transcoderTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,12 @@ class transcoderTest extends TestCase
3939

4040
private static $testVideoFileName = 'ChromeCast.mp4';
4141
private static $testOverlayImageFileName = 'overlay.jpg';
42+
private static $testConcatVideo1FileName = 'ForBiggerEscapes.mp4';
43+
private static $testConcatVideo2FileName = 'ForBiggerJoyrides.mp4';
4244

4345
private static $inputVideoUri;
46+
private static $inputConcatVideo1Uri;
47+
private static $inputConcatVideo2Uri;
4448
private static $inputOverlayUri;
4549
private static $outputUriForPreset;
4650
private static $outputUriForAdHoc;
@@ -49,6 +53,7 @@ class transcoderTest extends TestCase
4953
private static $outputUriForStaticOverlay;
5054
private static $outputUriForPeriodicImagesSpritesheet;
5155
private static $outputUriForSetNumberImagesSpritesheet;
56+
private static $outputUriForConcat;
5257
private static $preset = 'preset/web-hd';
5358

5459
private static $jobIdRegex;
@@ -71,12 +76,24 @@ public static function setUpBeforeClass(): void
7176
'name' => self::$testVideoFileName
7277
]);
7378

79+
$file = fopen(__DIR__ . '/data/' . self::$testConcatVideo1FileName, 'r');
80+
self::$bucket->upload($file, [
81+
'name' => self::$testConcatVideo1FileName
82+
]);
83+
84+
$file = fopen(__DIR__ . '/data/' . self::$testConcatVideo2FileName, 'r');
85+
self::$bucket->upload($file, [
86+
'name' => self::$testConcatVideo2FileName
87+
]);
88+
7489
$file = fopen(__DIR__ . '/data/' . self::$testOverlayImageFileName, 'r');
7590
self::$bucket->upload($file, [
7691
'name' => self::$testOverlayImageFileName
7792
]);
7893

7994
self::$inputVideoUri = sprintf('gs://%s/%s', $bucketName, self::$testVideoFileName);
95+
self::$inputConcatVideo1Uri = sprintf('gs://%s/%s', $bucketName, self::$testConcatVideo1FileName);
96+
self::$inputConcatVideo2Uri = sprintf('gs://%s/%s', $bucketName, self::$testConcatVideo2FileName);
8097
self::$inputOverlayUri = sprintf('gs://%s/%s', $bucketName, self::$testOverlayImageFileName);
8198
self::$outputUriForPreset = sprintf('gs://%s/test-output-preset/', $bucketName);
8299
self::$outputUriForAdHoc = sprintf('gs://%s/test-output-adhoc/', $bucketName);
@@ -85,6 +102,7 @@ public static function setUpBeforeClass(): void
85102
self::$outputUriForStaticOverlay = sprintf('gs://%s/test-output-static-overlay/', $bucketName);
86103
self::$outputUriForPeriodicImagesSpritesheet = sprintf('gs://%s/test-output-periodic-spritesheet/', $bucketName);
87104
self::$outputUriForSetNumberImagesSpritesheet = sprintf('gs://%s/test-output-set-number-spritesheet/', $bucketName);
105+
self::$outputUriForConcat = sprintf('gs://%s/test-output-concat/', $bucketName);
88106

89107
self::$jobIdRegex = sprintf('~projects/%s/locations/%s/jobs/~', self::$projectNumber, self::$location);
90108
}
@@ -341,4 +359,33 @@ public function testJobSetNumberImagesSpritesheet()
341359
$jobId
342360
]);
343361
}
362+
363+
public function testJobConcat()
364+
{
365+
$output = $this->runFunctionSnippet('create_job_with_concatenated_inputs', [
366+
self::$projectId,
367+
self::$location,
368+
self::$inputConcatVideo1Uri,
369+
0,
370+
8.1,
371+
self::$inputConcatVideo2Uri,
372+
3.5,
373+
15,
374+
self::$outputUriForConcat
375+
]);
376+
377+
$this->assertRegExp(sprintf('%s', self::$jobIdRegex), $output);
378+
379+
$jobId = explode('/', $output);
380+
$jobId = trim($jobId[(count($jobId) - 1)]);
381+
382+
sleep(30);
383+
$this->assertJobStateSucceeded($jobId);
384+
385+
$this->runFunctionSnippet('delete_job', [
386+
self::$projectId,
387+
self::$location,
388+
$jobId
389+
]);
390+
}
344391
}

0 commit comments

Comments
 (0)