A media server for images and videos.
For a client implementation for Laravel see Laravel Transmorpher Client.
See the Docker Hub repository for images.
To not accidentally upgrade to a new major version, attach the major version you want to use to the image name:
cybexwebdev/transmorpher:0
There needs to be at least 1 Laravel worker to transcode videos. The following variable specifies how many workers should be running in the container:
VIDEO_TRANSCODING_WORKERS_AMOUNT=1
Caution
Using the database queue connection does neither guarantee FIFO nor prevent duplicate runs. It is recommended to use a queue which can guarantee these aspects, such as AWS SQS FIFO. To prevent duplicate runs with database, use only one worker process.
This environment variable has to be passed to the app container in your docker-compose.yml:
environment:
VIDEO_TRANSCODING_WORKERS_AMOUNT: ${VIDEO_TRANSCODING_WORKERS_AMOUNT:-1}
To clone the repository and get your media server running, use:
git clone --branch release/v0 --single-branch https://github.com/cybex-gmbh/transmorpher.git
Install composer dependencies:
composer install --no-dev
See the Dockerfiles for details.
Image manipulation:
Optionally, you can use GD, which can be configured in the Intervention Image configuration file.
Image optimization:
To use video transcoding:
Client notifications will be pushed on the queue client-notifications
. You will need to set up 1 worker for this queue.
There may be some cases (e.g. failed uploads) where chunk files are not deleted and stay on the local disk. To keep the local disk clean, a command is scheduled hourly to delete chunk files older than 24 hours.
See the chunk-upload
configuration file for more information.
To run the scheduler, you will need to add a cron job that runs the schedule:run
command on your server:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
For more information about scheduling, check the Laravel Docs.
- Create an app key:
php artisan key:generate
-
Configure the database in the
.env
file. -
Migrate the database:
php artisan migrate
The media server must use 3 separate Laravel disks to store originals, image derivatives and video derivatives.
Use the provided .env
keys to select the according disks in the filesystems.php
config file.
Note
- The root folder, like images/, of the configured derivatives disks has to always match the prefix provided by the
MediaType
enum. - If this prefix would be changed after initially launching your media server, clients would no longer be able to retrieve their previously uploaded media.
A signed request is used to notify clients about finished transcodings and when derivatives are purged. For this, a Sodium keypair has to be configured.
To create a keypair, use the provided command:
php artisan create:keypair
The newly created keypair has to be written in the .env
file:
TRANSMORPHER_SIGNING_KEYPAIR=
The public key of the media server is available under the /api/v*/publickey
endpoint and can be requested by any client.
The Transmorpher media server is not dependent on a specific cloud service provider, but only provides classes for AWS services out of the box.
- A file storage, for example, AWS S3
- A routing-capable service, for example, a Content Delivery Network, like AWS CloudFront
Create an IAM user with programmatic access. For more information, check the documentation for the corresponding service.
Permissions:
- read/write/delete access to media storage
- read/write/delete access to queue service
- creation of CDN invalidations
Put the credentials into the .env
:
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=eu-central-1
To use AWS S3 disks set the according .env
values:
TRANSMORPHER_DISK_ORIGINALS=s3Originals
TRANSMORPHER_DISK_IMAGE_DERIVATIVES=s3ImageDerivatives
TRANSMORPHER_DISK_VIDEO_DERIVATIVES=s3VideoDerivatives
Define the AWS S3 bucket for each disk:
AWS_BUCKET_ORIGINALS=
AWS_BUCKET_IMAGE_DERIVATIVES=
AWS_BUCKET_VIDEO_DERIVATIVES=
It is technically possible to use the same bucket for all 3, but it is recommended to split it up to help manage and secure the files.
Privacy settings:
- all file storages should be private
- the CDN needs to access the video derivatives storage
Configure your CloudFront-Distribution-ID:
AWS_CLOUDFRONT_DISTRIBUTION_ID=
Changes to media will automatically trigger a cache invalidation. Therefore, the CDN cache duration can be set to a long time.
To forward incoming requests from the CDN to your media server, configure your Transmorpher media server as the main origin. For more information on configuring origins in CloudFront see the documentation page.
To properly use the API, you need to either:
- add a rule to not cache anything under
/api/*
- publish the Transmorpher media server under an additional domain that is not behind the CDN
Content Delivery Network
In the CDN routing create a new behavior which points requests starting with "/videos/*" to a new origin, which is the video derivatives S3 bucket.
Queue
Transcoding jobs are dispatched onto the "video-transcoding" queue. You can have these jobs processed on the main server or dedicated workers. For more information, check the Laravel Queue Documentation.
Note
Since queues are not generally FIFO, it is recommended to use a queue which guarantees FIFO and also prevents duplicate runs. For this, a custom AWS SQS FIFO queue connection is available.
You can define your queue connection in the .env
file:
QUEUE_CONNECTION=sqs-fifo
To configure an AWS SQS queue, see the according keys in the .env
.
- File storage accessible at
storage/app/videos
- File storage able to be symlinked to
public/videos
Select the following Laravel disks in the .env
:
TRANSMORPHER_DISK_ORIGINALS=localOriginals
TRANSMORPHER_DISK_IMAGE_DERIVATIVES=localImageDerivatives
TRANSMORPHER_DISK_VIDEO_DERIVATIVES=localVideoDerivatives
File Storage
To access public derivatives for videos, generate a symlink from the Laravel storage folder to the public folder:
php artisan storage:link
Queue
Transcoding jobs are dispatched onto the "video-transcoding" queue. You can have these jobs processed on the main server or dedicated workers. For more information, check the Laravel Queue Documentation.
You can define your queue connection in the .env
file:
QUEUE_CONNECTION=database
Caution
The database connection does neither guarantee FIFO nor prevent duplicate runs. It is recommended to use a queue which can guarantee these aspects, such as AWS SQS FIFO. To prevent duplicate runs with database, use only one worker process.
By default, the media server stores image derivatives on the image derivatives disk. This can be turned off, so they will always be re-generated on demand instead:
TRANSMORPHER_STORE_DERIVATIVES=true
There are additional settings in the transmorpher.php
config file.
Media always belongs to a user. To easily create one, use the provided command:
php artisan create:user <name> <email> <api_url>
The server sends notifications to the api url, for example, video transcoding information.
For our standard laravel client implementation, this is: https://example.com/transmorpher/notifications
.
This command will provide you with a Laravel Sanctum token, which has to be
written in the .env
file of a client system.
The token will be passed for all API requests for authorization and is connected to the corresponding user.
If you need to re-generate a token for a user, use the provided command:
php artisan create:token <userId>
Media is identified by a string which is passed when uploading media. This "identifier" is unique per user.
When media is uploaded on the same identifier by the same user, a new version for the same media will be created.
The media server provides the following features for media:
- upload
- get derivative
- get original*
- set version
- delete
Marked with * only applies to images.
Images will always be optimized and transformed on the Transmorpher media server. The media server will also directly answer requests for derivatives.
The media server provides the following transformations for images:
- width (w)
- height (h)
- quality (q)
- format (f)
To publicly access an image, the client name and the identifier have to be specified:
https://transmorpher.test/images/<clientname>/<identifier>
Images retrieved from this URL will be derivatives which are optimized. Additionally, you can specify transformation parameters in the following format:
https://transmorpher.test/images/<clientname>/<identifier>/<transformations>
For example:
https://transmorpher.test/images/catworld/european-short-hair/w-1920+h-1080+f-png+q-50
The Laravel Transmorpher Client will receive this information and store it. It can also create URLs with transformations.
Video transcoding is handled as an asynchronous task. The client will receive the information about the transcoded video as soon as it completes. For this, a signed request is sent to the client.
Since video transcoding is a complex task, it may take some time to complete. The client will also be notified about failed attempts.
To publicly access a video, the client name, the identifier and a format have to be specified. There are different formats available:
- HLS (.m3u8)
https://transmorpher.test/videos/<clientname>/<identifier>/hls/video.m3u8
- DASH (.mpd)
https://transmorpher.test/videos/<clientname>/<identifier>/dash/video.mpd
- MP4 (.mp4)
https://transmorpher.test/videos/<clientname>/<identifier>/mp4/video.mp4
The Laravel Transmorpher Client will receive this information and store it.
The bit rate for video transcoding can be set in the .env
file in kilobits:
TRANSMORPHER_VIDEO_ENCODER_BITRATE=9000k
This setting will be ignored for the DASH/HLS streaming formats because they are calculated automatically. For suitable bit rates, see: https://help.twitch.tv/s/article/broadcast-guidelines#recommended
To encode the DASH and HLS formats with either HEVC or h264, set the following environment variable.
TRANSMORPHER_VIDEO_ENCODER=cpu-hevc
or
TRANSMORPHER_VIDEO_ENCODER=cpu-h264
For the MP4 fallback file, h264 is always used because
- FFmpeg doesn't support HEVC in MP4 files when encoding with a CPU.
- h264 is the most widely supported codec, and this file is to be used when a client does not support HLS or DASH.
Videos may be transcoded using a machine's nVidia GPU. This requires the according hardware and driver setup on the host machine.
- https://trac.ffmpeg.org/wiki/HWAccelIntro#NVENC
- https://docs.nvidia.com/video-technologies/video-codec-sdk/pdf/Using_FFmpeg_with_NVIDIA_GPU_Hardware_Acceleration.pdf
The following steps are necessary on a docker host:
- Install nvidia drivers
- Install nvidia container toolkit
- Configure the docker nvidia runtime (note difference for rootless docker)
- Add gpu capabilities and nvidia runtime to according compose.yml files
- Restart docker and according containers
To use GPU encoding with HEVC or h264, set the following environment variable. This controls the codec used when transcoding videos to HLS and DASH, as well as the device used.
TRANSMORPHER_VIDEO_ENCODER=nvidia-hevc
or
TRANSMORPHER_VIDEO_ENCODER=nvidia-h264
The nVidia encoders have different presets available. Higher preset numbers are higher quality and slower. For encoder specific lists of presets see:
ffmpeg -h encoder=h264_nvenc
ffmpeg -h encoder=hevc_nvenc
The default preset is p4
. To set the high quality preset, use the following environment variable:
TRANSMORPHER_VIDEO_ENCODER_NVIDIA_PRESET=p6
Each encoder has its own configuration file in the config/encoder
folder, containing FFmpeg parameters.
Note that the optional GPU video decoding setting is experimental and unstable. By default, videos are decoded using the CPU.
If you want to use a CDN other than CloudFront, you will have to provide a class, which implements the CdnHelperInterface
and
provides the functionality of invalidating the CDN's cache.
The CloudFrontHelper
class provides an implementation for CloudFront and can be viewed as an example.
You will also have to adjust the transmorpher.php
configuration value for the cdn_helper
:
'cdn_helper' => App\Helpers\YourCdnClass::class,
The class to transform images as well as the classes to convert images to different formats are interchangeable. This provides the ability to add additional image manipulation libraries or logic in a modular way.
To add a class for image transformation, create a new class which implements the TransformInterface
.
An example implementation can be found at App\Classes\Intervention\Transform
.
Additionally, the newly created class has to be specified in the transmorpher.php
configuration file:
'transform_class' => App\Classes\YourTransformationClass::class,
If you want to interchange the classes which convert images to different formats, you can do so by creating classes
which implement the ConvertInterface
. An example
implementation can be found at App\Classes\Intervention\Convert
.
You will also have to adjust the configuration values:
'convert_classes' => [
'jpg' => App\Classes\YourClassJpg::class,
'png' => App\Classes\YourClassPng::class,
'gif' => App\Classes\YourClassGif::class,
'webp' => App\Classes\YourClassWebp::class,
],
The image-optimizer.php
configuration file specifies which optimizers should be used. Here you can configure options for each optimizer and add new or remove optimizers.
For more information on adding custom optimizers, check the documentation of the Laravel Image Optimizer package.
By default, the Transmorpher uses FFmpeg and Laravel jobs for transcoding videos. This can be changed similar to the image transformation classes.
To interchange the class, which is responsible for initiating transcoding, create a new class which implements
the TranscodeInterface
. An example implementation, which
dispatches a job, can be found at App\Classes\Transcode.php
.
You will also have to adjust the configuration value:
'transcode_class' => App\Classes\YourTranscodeClass::class,
Adjusting the way derivatives are generated will not be reflected on already existing derivatives. Therefore, you might want to delete all existing derivatives or re-generate them.
We provide a command which will additionally notify clients with a signed request about a new derivatives revision, so they can react accordingly (e.g. update cache buster).
php artisan purge:derivatives
The command accepts the options --image
, --video
and --all
(or -a
) for purging the respective derivatives.
Image derivatives will be deleted, for video derivatives we dispatch a new transcoding job for the current version.
The derivatives revision is available on the route /api/v*/cacheInvalidator
.
To restore operation of the server, restore the following:
- database
- the
originals
disk .env
file*- the
image derivatives
disk* - the
video derivatives
disk*
Marked with * are optional, but recommended.
If the .env
file is lost follow the setup instructions above, including creating a new signing keypair.
If video derivatives are lost, use the purge command to restore them.
Lost image derivatives will automatically be re-generated on demand.
For more information, take a look at the PullPreview section of the github-workflow repository.
App-specific GitHub Secrets:
- PULLPREVIEW_APP_KEY
- PULLPREVIEW_SODIUM_KEYPAIR
- PULLPREVIEW_SANCTUM_AUTH_TOKEN
- PULLPREVIEW_SANCTUM_AUTH_TOKEN_HASH
- PULLPREVIEW_USER_NAME
- PULLPREVIEW_USER_EMAIL
A demonstration app, which implements the client package, is booted with PullPreview and available at the PullPreview root URL. The Transmorpher media server runs under /transmorpherServer
.
The environment is seeded with a user with an auth token. To get access, you will have to locally create a token and use this token and its hash.
php artisan create:user pullpreview [email protected] http://pullpreview.test/transmorpher/notifications
Take the hash of the token from the personal_access_tokens
table and save it to GitHub secrets. The command also provides a TRANSMORPHER_AUTH_TOKEN
, which should be stored
securely to use in client systems.
In addition to the GitHub Secrets, you'll need to set the CLIENT_CONTAINER_NAME
env variable for the Transmorpher server.
You may use the CLIENT_NOTIFICATION_ROUTE
env variable if you have a custom notifications url, which differs from the default client implementation.
The Transmorpher media server is licensed under the MIT license.