also looking at this
meemalabs/laravel-media-converter
[1.0] Introducing the "Simple Usage" API
#14
10 files
+406
-200
| @@ -1,16 +1,19 @@ | ||
| 1 | name: Test | |
| 1 | name: Run Tests | |
| 2 | 2 | |
| 3 | on: [push] | |
| 3 | on: | |
| 4 | push: | |
| 5 | pull_request: | |
| 4 | 6 | |
| 5 | 7 | jobs: |
| 6 | test: | |
| 8 | run-tests: | |
| 7 | 9 | runs-on: ubuntu-latest |
| 8 | 10 | strategy: |
| 9 | 11 | fail-fast: false |
| 10 | 12 | matrix: |
| 11 | php: [7.3, 7.4, 8.0] | |
| 13 | php: [7.4, 8.0] | |
| 12 | 14 | laravel: [7.*, 8.*] |
| 13 | dependency-version: [prefer-lowest, prefer-stable] | |
| 15 | dependency-version: [prefer-stable] | |
| 16 | # dependency-version: [prefer-lowest, prefer-stable] | |
| 14 | 17 | include: |
| 15 | 18 | - laravel: 8.* |
| 16 | 19 | testbench: 6.* |
| @@ -21,20 +24,13 @@ jobs: | ||
| 21 | 24 | |
| 22 | 25 | steps: |
| 23 | 26 | - name: Checkout code |
| 24 | uses: actions/checkout@v1 | |
| 27 | uses: actions/checkout@v2 | |
| 25 | 28 | |
| 26 | 29 | - name: Setup PHP |
| 27 | 30 | uses: shivammathur/setup-php@v2 |
| 28 | 31 | with: |
| 29 | 32 | php-version: ${{ matrix.php }} |
| 30 | 33 | extensions: mbstring, intl, pdo, pdo_sqlite, sqlite, curl |
| 31 | env: | |
| 32 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| 33 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| 34 | AWS_DEFAULT_REGION: 'us-east-1' | |
| 35 | AWS_MEDIACONVERT_ACCOUNT_URL: ${{ secrets.AWS_MEDIACONVERT_ACCOUNT_URL }} | |
| 36 | AWS_IAM_ARN: ${{ secrets.AWS_IAM_ARN }} | |
| 37 | AWS_QUEUE_ARN: ${{ secrets.AWS_QUEUE_ARN }} | |
| 38 | 34 | |
| 39 | 35 | - name: Cache dependencies |
| 40 | 36 | uses: actions/cache@v1 |
| @@ -44,11 +40,15 @@ jobs: | ||
| 44 | 40 | |
| 45 | 41 | - name: Install dependencies |
| 46 | 42 | run: | |
| 47 | composer require "orchestra/testbench:$" --no-interaction --no-update | |
| 43 | composer require "laravel/framework:$" "orchestra/testbench:$" --no-interaction --no-update | |
| 48 | 44 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest |
| 49 | 45 | |
| 50 | - name: Create .env file | |
| 51 | run: touch .env # this can be an empty file, just need the test to pass. GitHub Actions uses GitHub Secrets | |
| 52 | ||
| 53 | 46 | - name: Execute tests |
| 54 | 47 | run: vendor/bin/pest |
| 48 | env: | |
| 49 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| 50 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| 51 | AWS_DEFAULT_REGION: 'us-east-1' | |
| 52 | AWS_MEDIACONVERT_ACCOUNT_URL: ${{ secrets.AWS_MEDIACONVERT_ACCOUNT_URL }} | |
| 53 | AWS_IAM_ARN: ${{ secrets.AWS_IAM_ARN }} | |
| 54 | AWS_QUEUE_ARN: ${{ secrets.AWS_QUEUE_ARN }} | |
| @@ -18,13 +18,20 @@ This is a wrapper package for AWS MediaConvert. Additional drivers may be added. | ||
| 18 | 18 | use Meema\MediaConverter\Facades\MediaConvert; |
| 19 | 19 | use Meema\MediaConverter\Jobs\CreateVideoConversion; |
| 20 | 20 | |
| 21 | // run any of the following MediaConvert methods: | |
| 21 | # simple usage | |
| 22 | MediaConvert::path('video.mkv') // the s3 path to the file inside the bucket defined in your config (filesystems.disks.s3.bucket) | |
| 23 | ->optimizeForWeb() // will generate an optmized MP4 for you | |
| 24 | ->withThumbnails(int $framerateNumerator, int $framerateDenominator, int $maxCaptures, $width = null, $nameModifier = null, $imageQuality = 80) // will generate thumbnails from the video for you, e.g. poster images | |
| 25 | ->saveTo('my-optimized-video.mp4'); // output file name | |
| 26 | ->createJob(); | |
| 27 | ||
| 28 | # advanced usage | |
| 22 | 29 | $result = MediaConvert::cancelJob(string $id); |
| 23 | 30 | $result = MediaConvert::createJob(array $settings, array $metaData = [], int $priority = 0); |
| 24 | 31 | $result = MediaConvert::getJob(string $id); |
| 25 | 32 | $result = MediaConvert::listJobs(array $options); |
| 26 | 33 | |
| 27 | // you may also dispatch a job to convert a video | |
| 34 | # you may also dispatch a job to convert a video | |
| 28 | 35 | dispatch(new CreateVideoConversion($jobSettings, $mediaId)); // $mediaId is optional & refers to the relating model's id |
| 29 | 36 | ``` |
| 30 | 37 | |
| @@ -65,175 +65,5 @@ return [ | ||
| 65 | 65 | */ |
| 66 | 66 | 'statuses_to_track' => ['complete', 'error', 'new_warning', 'progressing', 'status_update', 'input_information', 'queue_hop'], |
| 67 | 67 | |
| 68 | 'settings' => [ | |
| 69 | 'OutputGroups' => [ | |
| 70 | // thumbnail generation | |
| 71 | [ | |
| 72 | 'CustomName' => 'Thumbnails', | |
| 73 | 'Name' => 'File Group', | |
| 74 | 'Outputs' => [ | |
| 75 | [ | |
| 76 | 'ContainerSettings' => [ | |
| 77 | 'Container' => 'RAW', | |
| 78 | ], | |
| 79 | 'VideoDescription' => [ | |
| 80 | 'ScalingBehavior' => 'DEFAULT', | |
| 81 | 'TimecodeInsertion' => 'DISABLED', | |
| 82 | 'AntiAlias' => 'ENABLED', | |
| 83 | 'Sharpness' => 50, | |
| 84 | 'CodecSettings' => [ | |
| 85 | 'Codec' => 'FRAME_CAPTURE', | |
| 86 | 'FrameCaptureSettings' => [ | |
| 87 | 'FramerateNumerator' => null, // to be set dynamically | |
| 88 | 'FramerateDenominator' => null, // to be set dynamically | |
| 89 | 'MaxCaptures' => null, // to be set dynamically | |
| 90 | 'Quality' => 80, | |
| 91 | ], | |
| 92 | ], | |
| 93 | 'AfdSignaling' => 'NONE', | |
| 94 | 'DropFrameTimecode' => 'ENABLED', | |
| 95 | 'RespondToAfd' => 'NONE', | |
| 96 | 'ColorMetadata' => 'INSERT', | |
| 97 | 'Width' => null, // to be set dynamically | |
| 98 | ], | |
| 99 | 'NameModifier' => '.$w$x$h$', // e.g. moderation-test-video.240x136.0000098.jpg | |
| 100 | ], | |
| 101 | ], | |
| 102 | 'OutputGroupSettings' => [ | |
| 103 | 'Type' => 'FILE_GROUP_SETTINGS', | |
| 104 | 'FileGroupSettings' => [ | |
| 105 | 'Destination' => null, // to be set dynamically | |
| 106 | 'DestinationSettings' => [ | |
| 107 | 'S3Settings' => [ | |
| 108 | 'AccessControl' => [ | |
| 109 | 'CannedAcl' => 'PUBLIC_READ', | |
| 110 | ], | |
| 111 | ], | |
| 112 | ], | |
| 113 | ], | |
| 114 | ], | |
| 115 | ], | |
| 116 | ||
| 117 | // optimize-mp4 output setting | |
| 118 | [ | |
| 119 | 'CustomName' => 'MP4', | |
| 120 | 'Name' => 'File Group', | |
| 121 | 'Outputs' => [ | |
| 122 | [ | |
| 123 | 'ContainerSettings' => [ | |
| 124 | 'Container' => 'MP4', | |
| 125 | 'Mp4Settings' => [ | |
| 126 | 'CslgAtom' => 'INCLUDE', | |
| 127 | 'FreeSpaceBox' => 'EXCLUDE', | |
| 128 | 'MoovPlacement' => 'PROGRESSIVE_DOWNLOAD', | |
| 129 | ], | |
| 130 | ], | |
| 131 | 'VideoDescription' => [ | |
| 132 | 'ScalingBehavior' => 'DEFAULT', | |
| 133 | 'TimecodeInsertion' => 'DISABLED', | |
| 134 | 'AntiAlias' => 'ENABLED', | |
| 135 | 'Sharpness' => 50, | |
| 136 | 'CodecSettings' => [ | |
| 137 | 'Codec' => 'H_264', | |
| 138 | 'H264Settings' => [ | |
| 139 | 'InterlaceMode' => 'PROGRESSIVE', | |
| 140 | 'NumberReferenceFrames' => 3, | |
| 141 | 'Syntax' => 'DEFAULT', | |
| 142 | 'Softness' => 0, | |
| 143 | 'GopClosedCadence' => 1, | |
| 144 | 'GopSize' => 90, | |
| 145 | 'Slices' => 1, | |
| 146 | 'GopBReference' => 'DISABLED', | |
| 147 | 'MaxBitrate' => 8000000, | |
| 148 | 'SlowPal' => 'DISABLED', | |
| 149 | 'SpatialAdaptiveQuantization' => 'ENABLED', | |
| 150 | 'TemporalAdaptiveQuantization' => 'ENABLED', | |
| 151 | 'FlickerAdaptiveQuantization' => 'DISABLED', | |
| 152 | 'EntropyEncoding' => 'CABAC', | |
| 153 | 'FramerateControl' => 'INITIALIZE_FROM_SOURCE', | |
| 154 | 'RateControlMode' => 'QVBR', | |
| 155 | 'QvbrSettings' => [ | |
| 156 | 'QvbrQualityLevel' => 7, | |
| 157 | 'QvbrQualityLevelFineTune' => 0, | |
| 158 | ], | |
| 159 | 'CodecProfile' => 'MAIN', | |
| 160 | 'Telecine' => 'NONE', | |
| 161 | 'MinIInterval' => 0, | |
| 162 | 'AdaptiveQuantization' => 'HIGH', | |
| 163 | 'CodecLevel' => 'AUTO', | |
| 164 | 'FieldEncoding' => 'PAFF', | |
| 165 | 'SceneChangeDetect' => 'ENABLED', | |
| 166 | 'QualityTuningLevel' => 'SINGLE_PASS', | |
| 167 | 'FramerateConversionAlgorithm' => 'DUPLICATE_DROP', | |
| 168 | 'UnregisteredSeiTimecode' => 'DISABLED', | |
| 169 | 'GopSizeUnits' => 'FRAMES', | |
| 170 | 'ParControl' => 'INITIALIZE_FROM_SOURCE', | |
| 171 | 'NumberBFramesBetweenReferenceFrames' => 2, | |
| 172 | 'RepeatPps' => 'DISABLED', | |
| 173 | ], | |
| 174 | ], | |
| 175 | 'AfdSignaling' => 'NONE', | |
| 176 | 'DropFrameTimecode' => 'ENABLED', | |
| 177 | 'RespondToAfd' => 'NONE', | |
| 178 | 'ColorMetadata' => 'INSERT', | |
| 179 | ], | |
| 180 | 'AudioDescriptions' => [ | |
| 181 | [ | |
| 182 | 'AudioTypeControl' => 'FOLLOW_INPUT', | |
| 183 | 'CodecSettings' => [ | |
| 184 | 'Codec' => 'AAC', | |
| 185 | 'AacSettings' => [ | |
| 186 | 'AudioDescriptionBroadcasterMix' => 'NORMAL', | |
| 187 | 'Bitrate' => 96000, | |
| 188 | 'RateControlMode' => 'CBR', | |
| 189 | 'CodecProfile' => 'LC', | |
| 190 | 'CodingMode' => 'CODING_MODE_2_0', | |
| 191 | 'RawFormat' => 'NONE', | |
| 192 | 'SampleRate' => 48000, | |
| 193 | 'Specification' => 'MPEG4', | |
| 194 | ], | |
| 195 | ], | |
| 196 | 'LanguageCodeControl' => 'FOLLOW_INPUT', | |
| 197 | ], | |
| 198 | ], | |
| 199 | ], | |
| 200 | ], | |
| 201 | 'OutputGroupSettings' => [ | |
| 202 | 'Type' => 'FILE_GROUP_SETTINGS', | |
| 203 | 'FileGroupSettings' => [ | |
| 204 | 'Destination' => null, // to be set dynamically | |
| 205 | 'DestinationSettings' => [ | |
| 206 | 'S3Settings' => [ | |
| 207 | 'AccessControl' => [ | |
| 208 | 'CannedAcl' => 'PUBLIC_READ', | |
| 209 | ], | |
| 210 | ], | |
| 211 | ], | |
| 212 | ], | |
| 213 | ], | |
| 214 | ], | |
| 215 | ], | |
| 216 | 'AdAvailOffset' => 0, | |
| 217 | 'Inputs' => [ | |
| 218 | [ | |
| 219 | 'AudioSelectors' => [ | |
| 220 | 'Audio Selector 1' => [ | |
| 221 | 'Offset' => 0, | |
| 222 | 'DefaultSelection' => 'DEFAULT', | |
| 223 | 'ProgramSelection' => 1, | |
| 224 | ], | |
| 225 | ], | |
| 226 | 'VideoSelector' => [ | |
| 227 | 'ColorSpace' => 'FOLLOW', | |
| 228 | ], | |
| 229 | 'FilterEnable' => 'AUTO', | |
| 230 | 'PsiControl' => 'USE_PSI', | |
| 231 | 'FilterStrength' => 0, | |
| 232 | 'DeblockFilter' => 'DISABLED', | |
| 233 | 'DenoiseFilter' => 'DISABLED', | |
| 234 | 'TimecodeSource' => 'EMBEDDED', | |
| 235 | 'FileInput' => null, // to be set dynamically | |
| 236 | ], | |
| 237 | ], | |
| 238 | ], | |
| 68 | 'job_settings' => \Meema\MediaConverter\Helpers\DefaultMediaConvertJobSettings::class, | |
| 239 | 69 | ]; |
| @@ -4,6 +4,44 @@ namespace Meema\MediaConverter\Contracts; | ||
| 4 | 4 | |
| 5 | 5 | interface Converter |
| 6 | 6 | { |
| 7 | /** | |
| 8 | * Sets the path of the file input. | |
| 9 | * | |
| 10 | * @param string $path | |
| 11 | * @param string|null $bucket | |
| 12 | * @return \Meema\MediaConverter\Converters\MediaConvert | |
| 13 | */ | |
| 14 | public function path(string $path, $bucket = null); | |
| 15 | ||
| 16 | /** | |
| 17 | * Generates a web optimized MP4. | |
| 18 | * | |
| 19 | * @return \Meema\MediaConverter\Converters\MediaConvert | |
| 20 | */ | |
| 21 | public function optimizeForWeb(); | |
| 22 | ||
| 23 | /** | |
| 24 | * Sets the settings required to generate the proper amount of thumbnails. | |
| 25 | * | |
| 26 | * @param int $framerateNumerator | |
| 27 | * @param int $framerateDenominator | |
| 28 | * @param int $maxCaptures | |
| 29 | * @param int|null $width | |
| 30 | * @param string|null $nameModifier | |
| 31 | * @param int $imageQuality | |
| 32 | * @return \Meema\MediaConverter\Converters\MediaConvert | |
| 33 | */ | |
| 34 | public function withThumbnails(int $framerateNumerator, int $framerateDenominator, int $maxCaptures, $width = null, $nameModifier = null, $imageQuality = 80); | |
| 35 | ||
| 36 | /** | |
| 37 | * Sets the path & executes the job. | |
| 38 | * | |
| 39 | * @param string $s3Path | |
| 40 | * @param string|null $s3bucket | |
| 41 | * @return \Aws\Result | |
| 42 | */ | |
| 43 | public function saveTo(string $s3Path, $s3bucket = null); | |
| 44 | ||
| 7 | 45 | /** |
| 8 | 46 | * Cancels an active job. |
| 9 | 47 | * |
| @@ -15,6 +15,13 @@ class MediaConvert implements Converter | ||
| 15 | 15 | */ |
| 16 | 16 | protected $client; |
| 17 | 17 | |
| 18 | /** | |
| 19 | * The MediaConvert job's settings. | |
| 20 | * | |
| 21 | * @var array | |
| 22 | */ | |
| 23 | public array $jobSettings; | |
| 24 | ||
| 18 | 25 | /** |
| 19 | 26 | * Construct converter. |
| 20 | 27 | * |
| @@ -24,6 +31,8 @@ class MediaConvert implements Converter | ||
| 24 | 31 | { |
| 25 | 32 | $config = config('media-converter'); |
| 26 | 33 | |
| 34 | $this->jobSettings = (new $config['job_settings'])::get(); | |
| 35 | ||
| 27 | 36 | $this->client = new MediaConvertClient([ |
| 28 | 37 | 'version' => $config['version'], |
| 29 | 38 | 'region' => $config['region'], |
| @@ -42,6 +51,80 @@ class MediaConvert implements Converter | ||
| 42 | 51 | return $this->client; |
| 43 | 52 | } |
| 44 | 53 | |
| 54 | /** | |
| 55 | * Sets the path of the file input. | |
| 56 | * | |
| 57 | * @param string $path - represents the S3 path, e.g path/to/file.mp4 | |
| 58 | * @param string|null $bucket - reference to the S3 Bucket name. Defaults to config value. | |
| 59 | * @return \Meema\MediaConverter\Converters\MediaConvert | |
| 60 | */ | |
| 61 | public function path(string $path, $bucket = null): MediaConvert | |
| 62 | { | |
| 63 | $this->setFileInput($path, $bucket); | |
| 64 | ||
| 65 | return $this; | |
| 66 | } | |
| 67 | ||
| 68 | /** | |
| 69 | * Generates a web optimized MP4. | |
| 70 | * | |
| 71 | * @return \Meema\MediaConverter\Converters\MediaConvert | |
| 72 | */ | |
| 73 | public function optimizeForWeb(): MediaConvert | |
| 74 | { | |
| 75 | // At the moment, we can simply return $this, because this method is simply used for readability purposes. | |
| 76 | // The "default job" has all the proper settings already to generate a web optimized mp4. | |
| 77 | return $this; | |
| 78 | } | |
| 79 | ||
| 80 | /** | |
| 81 | * Sets the settings required to generate the proper amount of thumbnails. | |
| 82 | * | |
| 83 | * @param int $framerateNumerator | |
| 84 | * @param int $framerateDenominator | |
| 85 | * @param int $maxCaptures | |
| 86 | * @param int|null $width | |
| 87 | * @param string|null $nameModifier | |
| 88 | * @param int $imageQuality | |
| 89 | * @return \Meema\MediaConverter\Converters\MediaConvert | |
| 90 | */ | |
| 91 | public function withThumbnails(int $framerateNumerator, int $framerateDenominator, int $maxCaptures, $width = null, $nameModifier = null, $imageQuality = 80): MediaConvert | |
| 92 | { | |
| 93 | $this->jobSettings['OutputGroups'][0]['Outputs'][0]['VideoDescription']['CodecSettings']['FrameCaptureSettings'] = [ | |
| 94 | 'FramerateNumerator' => $framerateNumerator, | |
| 95 | 'FramerateDenominator' => $framerateDenominator, | |
| 96 | 'MaxCaptures' => $maxCaptures, | |
| 97 | 'Quality' => $imageQuality, | |
| 98 | ]; | |
| 99 | ||
| 100 | if ($width) { | |
| 101 | $this->jobSettings['OutputGroups'][0]['Outputs'][0]['VideoDescription']['Width'] = $width; | |
| 102 | } | |
| 103 | ||
| 104 | if ($nameModifier) { | |
| 105 | $this->jobSettings['OutputGroups'][0]['Outputs'][0]['NameModifier'] = $nameModifier; | |
| 106 | } | |
| 107 | ||
| 108 | return $this; | |
| 109 | } | |
| 110 | ||
| 111 | /** | |
| 112 | * Sets the S3 path & executes the job. | |
| 113 | * | |
| 114 | * @param string $s3Path | |
| 115 | * @param string|null $s3bucket | |
| 116 | * @return \Meema\MediaConverter\Converters\MediaConvert | |
| 117 | */ | |
| 118 | public function saveTo(string $s3Path, $s3bucket = null): MediaConvert | |
| 119 | { | |
| 120 | $destination = 's3://'.($s3bucket ?? config('filesystems.disks.s3.bucket')); | |
| 121 | ||
| 122 | $this->jobSettings['OutputGroups'][0]['OutputGroupSettings']['FileGroupSettings']['Destination'] = $destination.'/thumbnails/'; | |
| 123 | $this->jobSettings['OutputGroups'][1]['OutputGroupSettings']['FileGroupSettings']['Destination'] = $destination.'/mp4/'; | |
| 124 | ||
| 125 | return $this; | |
| 126 | } | |
| 127 | ||
| 45 | 128 | /** |
| 46 | 129 | * Cancels an active job. |
| 47 | 130 | * |
| @@ -110,4 +193,17 @@ class MediaConvert implements Converter | ||
| 110 | 193 | |
| 111 | 194 | return 'SECONDS_60'; // gracefully default to this value, in case the config value is missing or incorrect |
| 112 | 195 | } |
| 196 | ||
| 197 | /** | |
| 198 | * Sets the S3 input file path. | |
| 199 | * | |
| 200 | * @param string $path | |
| 201 | * @param string|null $bucket | |
| 202 | */ | |
| 203 | protected function setFileInput(string $path, $bucket = null) | |
| 204 | { | |
| 205 | $fileInput = 's3://'.($bucket ?? config('filesystems.disks.s3.bucket')).'/'.$path; | |
| 206 | ||
| 207 | $this->jobSettings['Inputs'][0]['FileInput'] = $fileInput; | |
| 208 | } | |
| 113 | 209 | } |
| @@ -6,6 +6,10 @@ use Illuminate\Support\Facades\Facade; | ||
| 6 | 6 | |
| 7 | 7 | /** |
| 8 | 8 | * @method static \Aws\MediaConvert\MediaConvertClient getClient() |
| 9 | * @method static \Meema\MediaConverter\Converters\MediaConvert path(string $s3Path, $s3bucket = null) | |
| 10 | * @method static \Meema\MediaConverter\Converters\MediaConvert optimizeForWeb() | |
| 11 | * @method static \Meema\MediaConverter\Converters\MediaConvert withThumbnails(int $framerateNumerator, int $framerateDenominator, int $maxCaptures, $width = null, $nameModifier = null, $imageQuality = 80) | |
| 12 | * @method static \Aws\Result saveTo(string $s3Path, $s3bucket = null) | |
| 9 | 13 | * @method static \Aws\Result cancelJob(string $id) |
| 10 | 14 | * @method static \Aws\Result createJob(array $settings, array $metaData = [], int $priority = 0) |
| 11 | 15 | * @method static \Aws\Result getJob(string $id) |
| @@ -0,0 +1,178 @@ | ||
| 1 | <?php | |
| 2 | ||
| 3 | namespace Meema\MediaConverter\Helpers; | |
| 4 | ||
| 5 | class DefaultMediaConvertJobSettings | |
| 6 | { | |
| 7 | public static function get(): array | |
| 8 | { | |
| 9 | return [ | |
| 10 | 'OutputGroups' => [ | |
| 11 | [ | |
| 12 | 'CustomName' => 'Thumbnails', | |
| 13 | 'Name' => 'File Group', | |
| 14 | 'Outputs' => [ | |
| 15 | [ | |
| 16 | 'ContainerSettings' => [ | |
| 17 | 'Container' => 'RAW', | |
| 18 | ], | |
| 19 | 'VideoDescription' => [ | |
| 20 | 'ScalingBehavior' => 'DEFAULT', | |
| 21 | 'TimecodeInsertion' => 'DISABLED', | |
| 22 | 'AntiAlias' => 'ENABLED', | |
| 23 | 'Sharpness' => 50, | |
| 24 | 'CodecSettings' => [ | |
| 25 | 'Codec' => 'FRAME_CAPTURE', | |
| 26 | 'FrameCaptureSettings' => [ | |
| 27 | 'FramerateNumerator' => null, // to be set dynamically | |
| 28 | 'FramerateDenominator' => null, // to be set dynamically | |
| 29 | 'MaxCaptures' => null, // to be set dynamically | |
| 30 | 'Quality' => null, // to be set dynamically | |
| 31 | ], | |
| 32 | ], | |
| 33 | 'AfdSignaling' => 'NONE', | |
| 34 | 'DropFrameTimecode' => 'ENABLED', | |
| 35 | 'RespondToAfd' => 'NONE', | |
| 36 | 'ColorMetadata' => 'INSERT', | |
| 37 | 'Width' => null, // to be set dynamically | |
| 38 | ], | |
| 39 | 'NameModifier' => null, // to be set dynamically | |
| 40 | ], | |
| 41 | ], | |
| 42 | 'OutputGroupSettings' => [ | |
| 43 | 'Type' => 'FILE_GROUP_SETTINGS', | |
| 44 | 'FileGroupSettings' => [ | |
| 45 | 'Destination' => null, // to be set dynamically | |
| 46 | 'DestinationSettings' => [ | |
| 47 | 'S3Settings' => [ | |
| 48 | 'AccessControl' => [ | |
| 49 | 'CannedAcl' => 'PUBLIC_READ', | |
| 50 | ], | |
| 51 | ], | |
| 52 | ], | |
| 53 | ], | |
| 54 | ], | |
| 55 | ], | |
| 56 | [ | |
| 57 | 'CustomName' => 'MP4', | |
| 58 | 'Name' => 'File Group', | |
| 59 | 'Outputs' => [ | |
| 60 | [ | |
| 61 | 'ContainerSettings' => [ | |
| 62 | 'Container' => 'MP4', | |
| 63 | 'Mp4Settings' => [ | |
| 64 | 'CslgAtom' => 'INCLUDE', | |
| 65 | 'FreeSpaceBox' => 'EXCLUDE', | |
| 66 | 'MoovPlacement' => 'PROGRESSIVE_DOWNLOAD', | |
| 67 | ], | |
| 68 | ], | |
| 69 | 'VideoDescription' => [ | |
| 70 | 'ScalingBehavior' => 'DEFAULT', | |
| 71 | 'TimecodeInsertion' => 'DISABLED', | |
| 72 | 'AntiAlias' => 'ENABLED', | |
| 73 | 'Sharpness' => 50, | |
| 74 | 'CodecSettings' => [ | |
| 75 | 'Codec' => 'H_264', | |
| 76 | 'H264Settings' => [ | |
| 77 | 'InterlaceMode' => 'PROGRESSIVE', | |
| 78 | 'NumberReferenceFrames' => 3, | |
| 79 | 'Syntax' => 'DEFAULT', | |
| 80 | 'Softness' => 0, | |
| 81 | 'GopClosedCadence' => 1, | |
| 82 | 'GopSize' => 90, | |
| 83 | 'Slices' => 1, | |
| 84 | 'GopBReference' => 'DISABLED', | |
| 85 | 'MaxBitrate' => 8000000, | |
| 86 | 'SlowPal' => 'DISABLED', | |
| 87 | 'SpatialAdaptiveQuantization' => 'ENABLED', | |
| 88 | 'TemporalAdaptiveQuantization' => 'ENABLED', | |
| 89 | 'FlickerAdaptiveQuantization' => 'DISABLED', | |
| 90 | 'EntropyEncoding' => 'CABAC', | |
| 91 | 'FramerateControl' => 'INITIALIZE_FROM_SOURCE', | |
| 92 | 'RateControlMode' => 'QVBR', | |
| 93 | 'QvbrSettings' => [ | |
| 94 | 'QvbrQualityLevel' => 7, | |
| 95 | 'QvbrQualityLevelFineTune' => 0, | |
| 96 | ], | |
| 97 | 'CodecProfile' => 'MAIN', | |
| 98 | 'Telecine' => 'NONE', | |
| 99 | 'MinIInterval' => 0, | |
| 100 | 'AdaptiveQuantization' => 'HIGH', | |
| 101 | 'CodecLevel' => 'AUTO', | |
| 102 | 'FieldEncoding' => 'PAFF', | |
| 103 | 'SceneChangeDetect' => 'ENABLED', | |
| 104 | 'QualityTuningLevel' => 'SINGLE_PASS', | |
| 105 | 'FramerateConversionAlgorithm' => 'DUPLICATE_DROP', | |
| 106 | 'UnregisteredSeiTimecode' => 'DISABLED', | |
| 107 | 'GopSizeUnits' => 'FRAMES', | |
| 108 | 'ParControl' => 'INITIALIZE_FROM_SOURCE', | |
| 109 | 'NumberBFramesBetweenReferenceFrames' => 2, | |
| 110 | 'RepeatPps' => 'DISABLED', | |
| 111 | ], | |
| 112 | ], | |
| 113 | 'AfdSignaling' => 'NONE', | |
| 114 | 'DropFrameTimecode' => 'ENABLED', | |
| 115 | 'RespondToAfd' => 'NONE', | |
| 116 | 'ColorMetadata' => 'INSERT', | |
| 117 | ], | |
| 118 | 'AudioDescriptions' => [ | |
| 119 | [ | |
| 120 | 'AudioTypeControl' => 'FOLLOW_INPUT', | |
| 121 | 'CodecSettings' => [ | |
| 122 | 'Codec' => 'AAC', | |
| 123 | 'AacSettings' => [ | |
| 124 | 'AudioDescriptionBroadcasterMix' => 'NORMAL', | |
| 125 | 'Bitrate' => 96000, | |
| 126 | 'RateControlMode' => 'CBR', | |
| 127 | 'CodecProfile' => 'LC', | |
| 128 | 'CodingMode' => 'CODING_MODE_2_0', | |
| 129 | 'RawFormat' => 'NONE', | |
| 130 | 'SampleRate' => 48000, | |
| 131 | 'Specification' => 'MPEG4', | |
| 132 | ], | |
| 133 | ], | |
| 134 | 'LanguageCodeControl' => 'FOLLOW_INPUT', | |
| 135 | ], | |
| 136 | ], | |
| 137 | ], | |
| 138 | ], | |
| 139 | 'OutputGroupSettings' => [ | |
| 140 | 'Type' => 'FILE_GROUP_SETTINGS', | |
| 141 | 'FileGroupSettings' => [ | |
| 142 | 'Destination' => null, // to be set dynamically | |
| 143 | 'DestinationSettings' => [ | |
| 144 | 'S3Settings' => [ | |
| 145 | 'AccessControl' => [ | |
| 146 | 'CannedAcl' => 'PUBLIC_READ', | |
| 147 | ], | |
| 148 | ], | |
| 149 | ], | |
| 150 | ], | |
| 151 | ], | |
| 152 | ], | |
| 153 | ], | |
| 154 | 'AdAvailOffset' => 0, | |
| 155 | 'Inputs' => [ | |
| 156 | [ | |
| 157 | 'AudioSelectors' => [ | |
| 158 | 'Audio Selector 1' => [ | |
| 159 | 'Offset' => 0, | |
| 160 | 'DefaultSelection' => 'DEFAULT', | |
| 161 | 'ProgramSelection' => 1, | |
| 162 | ], | |
| 163 | ], | |
| 164 | 'VideoSelector' => [ | |
| 165 | 'ColorSpace' => 'FOLLOW', | |
| 166 | ], | |
| 167 | 'FilterEnable' => 'AUTO', | |
| 168 | 'PsiControl' => 'USE_PSI', | |
| 169 | 'FilterStrength' => 0, | |
| 170 | 'DeblockFilter' => 'DISABLED', | |
| 171 | 'DenoiseFilter' => 'DISABLED', | |
| 172 | 'TimecodeSource' => 'EMBEDDED', | |
| 173 | 'FileInput' => null, // to be set dynamically | |
| 174 | ], | |
| 175 | ], | |
| 176 | ]; | |
| 177 | } | |
| 178 | } | |
| @@ -10,11 +10,63 @@ beforeEach(function () { | ||
| 10 | 10 | }); |
| 11 | 11 | |
| 12 | 12 | it('can successfully initialize settings', function () { |
| 13 | $this->assertTrue(is_array($this->settings)); | |
| 13 | $converter = MediaConvert::path('my-video.mkv', 'test-bucket'); | |
| 14 | $this->assertTrue(is_array($converter->jobSettings)); | |
| 15 | }); | |
| 16 | ||
| 17 | it('can successfully set a path', function () { | |
| 18 | $converter = MediaConvert::path('my-video.mkv', 'test-bucket'); | |
| 19 | $fileInput = 's3://test-bucket/my-video.mkv'; | |
| 20 | $this->assertEquals($converter->jobSettings['Inputs'][0]['FileInput'], $fileInput); | |
| 21 | }); | |
| 22 | ||
| 23 | it('can successfully web optimize a video', function () { | |
| 24 | $converter = MediaConvert::path('my-video.mkv', 'test-bucket')->optimizeForWeb(); | |
| 25 | // todo: we could likely improve this by running an actual test and compare the output file | |
| 26 | $this->assertEquals($converter->jobSettings['OutputGroups'][1]['CustomName'], 'MP4'); | |
| 27 | }); | |
| 28 | ||
| 29 | it('can successfully set the settings to generate thumbnails', function () { | |
| 30 | $framerateNumerator = 1; | |
| 31 | $framerateDenominator = 2; | |
| 32 | $maxCaptures = 3; | |
| 33 | $imageQuality = 75; | |
| 34 | $width = 100; | |
| 35 | $nameModifier = '.$w$x$h$'; | |
| 36 | ||
| 37 | $converter = MediaConvert::path('my-video.mkv', 'test-bucket') | |
| 38 | ->optimizeForWeb() | |
| 39 | ->withThumbnails($framerateNumerator, $framerateDenominator, $maxCaptures, $width, $nameModifier, $imageQuality); | |
| 40 | ||
| 41 | $this->assertEquals($converter->jobSettings['OutputGroups'][0]['Outputs'][0]['VideoDescription']['CodecSettings']['FrameCaptureSettings'], [ | |
| 42 | 'FramerateNumerator' => $framerateNumerator, | |
| 43 | 'FramerateDenominator' => $framerateDenominator, | |
| 44 | 'MaxCaptures' => $maxCaptures, | |
| 45 | 'Quality' => $imageQuality, | |
| 46 | ]); | |
| 47 | ||
| 48 | $this->assertEquals($converter->jobSettings['OutputGroups'][0]['Outputs'][0]['VideoDescription']['Width'], $width); | |
| 49 | $this->assertEquals($converter->jobSettings['OutputGroups'][0]['Outputs'][0]['NameModifier'], $nameModifier); | |
| 50 | }); | |
| 51 | ||
| 52 | it('can successfully update the output path', function () { | |
| 53 | $inputName = 'my-video.mkv'; | |
| 54 | $outputName = 'my-video.mp4'; | |
| 55 | $bucket = 'test-bucket'; | |
| 56 | ||
| 57 | $converter = MediaConvert::path($inputName, 'test-bucket') | |
| 58 | ->optimizeForWeb() | |
| 59 | ->withThumbnails(1, 2, 3, 100) | |
| 60 | ->saveTo($outputName, $bucket); | |
| 61 | ||
| 62 | $destination = 's3://test-bucket'; | |
| 63 | ||
| 64 | $this->assertEquals($converter->jobSettings['OutputGroups'][0]['OutputGroupSettings']['FileGroupSettings']['Destination'], $destination.'/thumbnails/'); | |
| 65 | $this->assertEquals($converter->jobSettings['OutputGroups'][1]['OutputGroupSettings']['FileGroupSettings']['Destination'], $destination.'/mp4/'); | |
| 14 | 66 | }); |
| 15 | 67 | |
| 16 | 68 | it('can successfully create a job', function () { |
| 17 | $response = MediaConvert::createJob($this->settings); | |
| 69 | $response = MediaConvert::createJob($this->jobSettings); | |
| 18 | 70 | |
| 19 | 71 | $this->assertEquals($response['@metadata']['statusCode'], 201); |
| 20 | 72 | }); |
| @@ -30,7 +82,7 @@ it('can successfully get a job', function () { | ||
| 30 | 82 | }); |
| 31 | 83 | |
| 32 | 84 | it('can successfully cancel a job', function () { |
| 33 | $job = MediaConvert::createJob($this->settings, []); | |
| 85 | $job = MediaConvert::createJob($this->jobSettings, []); | |
| 34 | 86 | |
| 35 | 87 | $response = MediaConvert::cancelJob($job['Job']['Id']); |
| 36 | 88 | |
| @@ -8,9 +8,7 @@ use Orchestra\Testbench\TestCase; | ||
| 8 | 8 | |
| 9 | 9 | class MediaConverterTestCase extends TestCase |
| 10 | 10 | { |
| 11 | public $settings = []; | |
| 12 | ||
| 13 | public $sizes = []; | |
| 11 | public $jobSettings = []; | |
| 14 | 12 | |
| 15 | 13 | protected function getPackageProviders($app): array |
| 16 | 14 | { |
| @@ -19,14 +17,16 @@ class MediaConverterTestCase extends TestCase | ||
| 19 | 17 | |
| 20 | 18 | public function initializeDotEnv() |
| 21 | 19 | { |
| 20 | if (! file_exists(__DIR__.'/../.env')) { | |
| 21 | return; | |
| 22 | } | |
| 23 | ||
| 22 | 24 | $dotenv = \Dotenv\Dotenv::createImmutable(dirname(__DIR__)); |
| 23 | 25 | $dotenv->load(); |
| 24 | 26 | } |
| 25 | 27 | |
| 26 | 28 | public function initializeSettings() |
| 27 | 29 | { |
| 28 | $configFile = file_get_contents(__DIR__.'/config/job.json'); | |
| 29 | ||
| 30 | 30 | // let's make sure these config values are set |
| 31 | 31 | Config::set('media-converter.credentials.key', env('AWS_ACCESS_KEY_ID')); |
| 32 | 32 | Config::set('media-converter.credentials.secret', env('AWS_SECRET_ACCESS_KEY')); |
| @@ -34,6 +34,7 @@ class MediaConverterTestCase extends TestCase | ||
| 34 | 34 | Config::set('media-converter.iam_arn', env('AWS_IAM_ARN')); |
| 35 | 35 | Config::set('media-converter.queue_arn', env('AWS_QUEUE_ARN')); |
| 36 | 36 | |
| 37 | $this->settings = json_decode($configFile, true); | |
| 37 | $configFile = file_get_contents(__DIR__.'/config/job.json'); | |
| 38 | $this->jobSettings = json_decode($configFile, true); | |
| 38 | 39 | } |
| 39 | 40 | } |