Skip to content

Commit

Permalink
add gpu video encoding support
Browse files Browse the repository at this point in the history
  • Loading branch information
gael-connan-cybex committed Aug 12, 2024
1 parent 0a9757b commit a47676a
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 10 deletions.
7 changes: 6 additions & 1 deletion app/Enums/MediaStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ enum MediaStorage: string
*/
public function getDisk(): Filesystem
{
return Storage::disk(config(sprintf('transmorpher.disks.%s', $this->value)));
return Storage::disk($this->getDiskName());
}

public function getDiskName(): string
{
return config(sprintf('transmorpher.disks.%s', $this->value));
}
}
1 change: 1 addition & 0 deletions app/Enums/StreamingFormat.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public function configure(StreamingMedia $video): Streaming
$format = $this->value;
$codec = config('transmorpher.video_codec');

// GPU accelerated encoding cannot be set via $codec('h264_nvenc'). It may be set through the additional params.
return $video->$format()
->$codec()
->autoGenerateRepresentations(config('transmorpher.representations'))
Expand Down
21 changes: 14 additions & 7 deletions app/Jobs/TranscodeVideo.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ protected function transcodeVideo(): void
// Set the necessary file path information.
$this->setFilePaths();


// Generate MP4.
$this->generateMp4($video);
// Generate HLS
Expand All @@ -175,7 +174,7 @@ protected function transcodeVideo(): void
protected function loadVideo(FFMpeg $ffmpeg): StreamingMedia
{
return $this->isLocalFilesystem($this->originalsDisk) ?
$ffmpeg->open($this->originalsDisk->path($this->originalFilePath))
$ffmpeg->customInput($this->originalsDisk->path($this->originalFilePath), config('transmorpher.initial_transcoding_parameters'))
: $this->openFromCloud($ffmpeg);
}

Expand All @@ -201,7 +200,7 @@ protected function openFromCloud(FFMpeg $ffmpeg): StreamingMedia
{
$this->localDisk->writeStream($this->tempOriginalFilename, $this->originalsDisk->readStream($this->originalFilePath));

return $ffmpeg->open($this->localDisk->path($this->tempOriginalFilename));
return $ffmpeg->customInput($this->localDisk->path($this->tempOriginalFilename), config('transmorpher.initial_transcoding_parameters'));
}

/**
Expand All @@ -226,7 +225,7 @@ protected function saveVideo(Streaming $video, string $format): void
{
$tempDerivativeFilePath = $this->getTempDerivativeFilePath($format);

\Log::info(sprintf('Generating %s for media %s and version %s.', $format, $this->version->Media->identifier, $this->version->getKey()));
\Log::info(sprintf('Generating %s for media %s and version %s.', strtoupper($format), $this->version->Media->identifier, $this->version->getKey()));
// Save to temporary folder first, to prevent race conditions when multiple versions are uploaded simultaneously.
if ($this->isLocalFilesystem($this->derivativesDisk)) {
$video->save($this->derivativesDisk->path($tempDerivativeFilePath));
Expand Down Expand Up @@ -261,9 +260,15 @@ protected function generateMp4(StreamingMedia $video): void
{
$tempMp4Filename = $this->getTempMp4Filename();
\Log::info(sprintf('Generating MP4 for media %s and version %s.', $this->version->Media->identifier, $this->version->getKey()));
$video->save((new X264())->setAdditionalParameters(config('transmorpher.additional_transcoding_parameters')), $this->localDisk->path($tempMp4Filename));
// GPU accelerated encoding cannot be set via setVideoCodec(). h264_nvenc may be set through the additional params.
$video->save(
(new X264())
->setInitialParameters(config('transmorpher.initial_transcoding_parameters'))
->setAdditionalParameters(config('transmorpher.additional_transcoding_parameters')),
$this->localDisk->path($tempMp4Filename)
);

\Log::info(sprintf('Writing MP4 to S3 for media %s and version %s.', $this->version->Media->identifier, $this->version->getKey()));
\Log::info(sprintf('Writing MP4 to %s for media %s and version %s.', MediaStorage::VIDEO_DERIVATIVES->getDiskName(), $this->version->Media->identifier, $this->version->getKey()));
$this->derivativesDisk->writeStream(
sprintf('%s.%s', $this->getTempDerivativeFilePath('mp4'), 'mp4'),
$this->localDisk->readStream($tempMp4Filename)
Expand All @@ -283,7 +288,6 @@ protected function moveDerivativesToDestinationPath(): void
if ($this->isMostRecentVersion()) {
// This will make sure we can invalidate the cache before the current derivative gets deleted.
// If this fails, the job will stop and cleanup will be done in the 'failed()'-method.
\Log::info(sprintf('Invalidating CDN cache for media %s and version %s.', $this->version->Media->identifier, $this->version->getKey()));
$this->invalidateCdnCache();

$this->derivativesDisk->deleteDirectory($this->derivativesDestinationPath);
Expand Down Expand Up @@ -350,8 +354,11 @@ protected function moveFromCloudTempDirectory(): void
protected function invalidateCdnCache(): void
{
if (CdnHelper::isConfigured()) {
\Log::info(sprintf('Invalidating CDN cache for media %s and version %s.', $this->version->Media->identifier, $this->version->getKey()));
// If this fails, the 'failed()'-method will handle the cleanup.
CdnHelper::invalidateMedia($this->version->Media->type, $this->derivativesDestinationPath);
} else {
\Log::info('Skipping CDN invalidation. CDN is not configured.');
}
}

Expand Down
9 changes: 9 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ services:
LARAVEL_SAIL: 1
XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
VIDEO_TRANSCODING_WORKERS_AMOUNT: ${VIDEO_TRANSCODING_WORKERS_AMOUNT:-1}
volumes:
- '.:/var/www/html'
networks:
Expand All @@ -27,6 +28,14 @@ services:
- 'traefik.enable=true'
- 'traefik.http.routers.${DOCKER_CONTAINER_NAME:-transmorpher}.rule=Host(`${DOCKER_CONTAINER_DOMAIN:-transmorpher.test}`)'
- 'traefik.http.services.${DOCKER_CONTAINER_NAME:-transmorpher}.loadbalancer.server.port=80'
# runtime: nvidia
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: all
# capabilities: [ gpu,video,utility,compute ]
mysql:
image: 'mysql/mysql-server:8.0'
ports:
Expand Down
20 changes: 19 additions & 1 deletion config/transmorpher.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,20 @@
360, 480, 720, 1080, 1440, 2160
],

/*
|--------------------------------------------------------------------------
| Initial Transcoding Parameters
|--------------------------------------------------------------------------
|
| These parameters will be added to the FFmpeg transcoding command before the input parameter.
*/
'initial_transcoding_parameters' => [
// GPU decoding has issues
// '-hwaccel', 'cuda',
// '-hwaccel_output_format','cuda',
// '-extra_hw_frames', '10',
],

/*
|--------------------------------------------------------------------------
| Additional Transcoding Parameters
Expand All @@ -122,7 +136,11 @@
| -sn: omit subtitles. Subtitles would need an encoder configuration for DASH, and possibly HLS.
*/
'additional_transcoding_parameters' => [
'-dn', '-map', '-0:t?', '-sn'
'-dn', '-map', '-0:t?', '-sn',

// GPU encoding
// https://trac.ffmpeg.org/wiki/HWAccelIntro#NVENC
'-c:v', 'h264_nvenc',
],

/*
Expand Down
4 changes: 4 additions & 0 deletions docker/8.2/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ RUN apt-get update \
&& apt-get install -y gifsicle \
&& apt-get install -y webp \
&& apt-get install -y ffmpeg \
# && apt-get install -y linux-modules-nvidia-535-generic \
# && apt-get install -y libnvidia-compute-535 \
# && apt-get install -y nvidia-cuda-dev \
# && apt-get install -y nvidia-cuda-toolkit \
&& apt-get -y autoremove \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Expand Down
4 changes: 4 additions & 0 deletions docker/8.3/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ RUN apt-get update \
&& apt-get install -y gifsicle \
&& apt-get install -y webp \
&& apt-get install -y ffmpeg \
# && apt-get install -y linux-modules-nvidia-535-generic \
# && apt-get install -y libnvidia-compute-535 \
# && apt-get install -y nvidia-cuda-dev \
# && apt-get install -y nvidia-cuda-toolkit \
&& apt-get -y autoremove \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Expand Down
10 changes: 9 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ RUN chown -R application:application /var/www/html/storage
RUN php /var/www/html/artisan storage:link

RUN apt update
RUN apt install -y default-mysql-client imagemagick jpegoptim optipng pngquant gifsicle webp ffmpeg
RUN apt install -y default-mysql-client imagemagick jpegoptim optipng pngquant gifsicle webp

# ffmpeg 6
RUN printf "\ndeb https://www.deb-multimedia.org bookworm main non-free" >> /etc/apt/sources.list
RUN apt-get update -oAcquire::AllowInsecureRepositories=true
RUN apt-get -y --allow-unauthenticated install deb-multimedia-keyring
RUN wget https://www.deb-multimedia.org/pool/main/d/deb-multimedia-keyring/deb-multimedia-keyring_2016.8.1_all.deb
RUN dpkg -i deb-multimedia-keyring_2016.8.1_all.deb
RUN apt-get -y --allow-unauthenticated install ffmpeg

RUN docker-service-enable cron
RUN docker-cronjob '* * * * * application /usr/local/bin/php /var/www/html/artisan schedule:run >> /dev/null 2>&1'
Expand Down

0 comments on commit a47676a

Please sign in to comment.