diff --git a/media/transcoder/README b/media/transcoder/README index 44de8aa19e..b88313d852 100644 --- a/media/transcoder/README +++ b/media/transcoder/README @@ -50,7 +50,17 @@ See the [Transcoder Documentation](https://cloud.google.com/transcoder/docs/) fo ## Troubleshooting -* See the [Troubleshooting guide](https://cloud.google.com/transcoder/docs/troubleshooting) for more information. +### bcmath extension missing + +If you see an error like this: + +``` +PHP Fatal error: Uncaught Error: Call to undefined function Google\Protobuf\Internal\bcsub() +``` + +You need to install the BC-Math extension. + +See the [Troubleshooting guide](https://cloud.google.com/transcoder/docs/troubleshooting) for more information. ## Contributing changes diff --git a/media/transcoder/composer.json b/media/transcoder/composer.json index 3479371f88..e9713eee86 100644 --- a/media/transcoder/composer.json +++ b/media/transcoder/composer.json @@ -1,6 +1,7 @@ { "require": { "google/cloud-video-transcoder": "^0.3.0", - "google/cloud-storage": "^1.9" + "google/cloud-storage": "^1.9", + "ext-bcmath": "*" } } diff --git a/media/transcoder/src/create_job_with_concatenated_inputs.php b/media/transcoder/src/create_job_with_concatenated_inputs.php new file mode 100644 index 0000000000..ad304c4b12 --- /dev/null +++ b/media/transcoder/src/create_job_with_concatenated_inputs.php @@ -0,0 +1,125 @@ +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()); +} +# [END transcoder_create_job_with_concatenated_inputs] + +require_once __DIR__ . '/../../../testing/sample_helpers.php'; +\Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv); diff --git a/media/transcoder/test/data/ForBiggerEscapes.mp4 b/media/transcoder/test/data/ForBiggerEscapes.mp4 new file mode 100644 index 0000000000..3ae36b91c8 Binary files /dev/null and b/media/transcoder/test/data/ForBiggerEscapes.mp4 differ diff --git a/media/transcoder/test/data/ForBiggerJoyrides.mp4 b/media/transcoder/test/data/ForBiggerJoyrides.mp4 new file mode 100644 index 0000000000..33f1dfe1a2 Binary files /dev/null and b/media/transcoder/test/data/ForBiggerJoyrides.mp4 differ diff --git a/media/transcoder/test/transcoderTest.php b/media/transcoder/test/transcoderTest.php index b8b9f7d2de..ad460caa9b 100644 --- a/media/transcoder/test/transcoderTest.php +++ b/media/transcoder/test/transcoderTest.php @@ -39,8 +39,12 @@ class transcoderTest extends TestCase private static $testVideoFileName = 'ChromeCast.mp4'; private static $testOverlayImageFileName = 'overlay.jpg'; + private static $testConcatVideo1FileName = 'ForBiggerEscapes.mp4'; + private static $testConcatVideo2FileName = 'ForBiggerJoyrides.mp4'; private static $inputVideoUri; + private static $inputConcatVideo1Uri; + private static $inputConcatVideo2Uri; private static $inputOverlayUri; private static $outputUriForPreset; private static $outputUriForAdHoc; @@ -49,6 +53,7 @@ class transcoderTest extends TestCase private static $outputUriForStaticOverlay; private static $outputUriForPeriodicImagesSpritesheet; private static $outputUriForSetNumberImagesSpritesheet; + private static $outputUriForConcat; private static $preset = 'preset/web-hd'; private static $jobIdRegex; @@ -71,12 +76,24 @@ public static function setUpBeforeClass(): void 'name' => self::$testVideoFileName ]); + $file = fopen(__DIR__ . '/data/' . self::$testConcatVideo1FileName, 'r'); + self::$bucket->upload($file, [ + 'name' => self::$testConcatVideo1FileName + ]); + + $file = fopen(__DIR__ . '/data/' . self::$testConcatVideo2FileName, 'r'); + self::$bucket->upload($file, [ + 'name' => self::$testConcatVideo2FileName + ]); + $file = fopen(__DIR__ . '/data/' . self::$testOverlayImageFileName, 'r'); self::$bucket->upload($file, [ 'name' => self::$testOverlayImageFileName ]); self::$inputVideoUri = sprintf('gs://%s/%s', $bucketName, self::$testVideoFileName); + self::$inputConcatVideo1Uri = sprintf('gs://%s/%s', $bucketName, self::$testConcatVideo1FileName); + self::$inputConcatVideo2Uri = sprintf('gs://%s/%s', $bucketName, self::$testConcatVideo2FileName); self::$inputOverlayUri = sprintf('gs://%s/%s', $bucketName, self::$testOverlayImageFileName); self::$outputUriForPreset = sprintf('gs://%s/test-output-preset/', $bucketName); self::$outputUriForAdHoc = sprintf('gs://%s/test-output-adhoc/', $bucketName); @@ -85,6 +102,7 @@ public static function setUpBeforeClass(): void self::$outputUriForStaticOverlay = sprintf('gs://%s/test-output-static-overlay/', $bucketName); self::$outputUriForPeriodicImagesSpritesheet = sprintf('gs://%s/test-output-periodic-spritesheet/', $bucketName); self::$outputUriForSetNumberImagesSpritesheet = sprintf('gs://%s/test-output-set-number-spritesheet/', $bucketName); + self::$outputUriForConcat = sprintf('gs://%s/test-output-concat/', $bucketName); self::$jobIdRegex = sprintf('~projects/%s/locations/%s/jobs/~', self::$projectNumber, self::$location); } @@ -341,4 +359,33 @@ public function testJobSetNumberImagesSpritesheet() $jobId ]); } + + public function testJobConcat() + { + $output = $this->runFunctionSnippet('create_job_with_concatenated_inputs', [ + self::$projectId, + self::$location, + self::$inputConcatVideo1Uri, + 0, + 8.1, + self::$inputConcatVideo2Uri, + 3.5, + 15, + self::$outputUriForConcat + ]); + + $this->assertRegExp(sprintf('%s', self::$jobIdRegex), $output); + + $jobId = explode('/', $output); + $jobId = trim($jobId[(count($jobId) - 1)]); + + sleep(30); + $this->assertJobStateSucceeded($jobId); + + $this->runFunctionSnippet('delete_job', [ + self::$projectId, + self::$location, + $jobId + ]); + } }