Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PS-670 rendition video enhance1 #468

Merged
merged 16 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion infra/docker/dev/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ RUN apk add --no-cache \
mysql-dev \
postgresql-dev \
zlib \
libreoffice \
ffmpeg \
libreoffice \
imagemagick \
rabbitmq-c-dev \
bash \
Expand Down
48 changes: 48 additions & 0 deletions lib/php/rendition-factory-bundle/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ services:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\TransformerModuleInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\VideoToFrameTransformerModule:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\TransformerModuleInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\VideoToAnimationTransformerModule:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\TransformerModuleInterface::TAG }

Alchemy\RenditionFactory\Transformer\Document\DocumentToPdfTransformerModule:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\TransformerModuleInterface::TAG }
Expand All @@ -36,7 +44,47 @@ services:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\TransformerModuleInterface::TAG }

# FFMpeg "formats"
Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\JpegFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\MkvFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\Mpeg4Format:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\MpegFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\QuicktimeFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\WebmFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\AnimatedGifFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\AnimatedPngFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\AnimatedWebpFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }


Imagine\Imagick\Imagine: ~
Imagine\Image\ImagineInterface: '@Imagine\Imagick\Imagine'

Alchemy\RenditionFactory\MimeType\MimeTypeGuesser: ~
Alchemy\RenditionFactory\Format\FormatGuesser: ~
Alchemy\RenditionFactory\Format\FormatFactory: ~
174 changes: 174 additions & 0 deletions lib/php/rendition-factory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Rendition Factory for video-input modules (wip)
jygaulier marked this conversation as resolved.
Show resolved Hide resolved

## Common options

### `enabled` (optional)

Used to disable a whole module from the build chain.

__default__: true

### `format` (mandatory)

A format defines the output file :
- family (image, video, audio, animation, document, unknown)
- mime type (unique mime type for this type of file)
- extension (possible extenstion(s) for this type of file)

For a specific module, only a subset of formats may be available, e.g.:
Since `video_to_frame` extracts one image from the video, the only supported output format(s)
are ones of family=image.

see below "Output formats" for the list of available formats.

### `extension` (optional)

For the file formats that support multiple extensions, e.g.:
`image/jpeg` : [`jpg`, `jpeg`], the prefered extension can be set to override the default (first) one.

__default__: first value in the list of extensions.

### `timeout` (optional)

maximum duration of the ffmpeg command in seconds.

__default__: 3600 seconds

### `threads` (optional)

set the number of threads used by ffmpeg.

__default__: depends on cpu (usually high), so the setting in most usefull to limit the cpu usage

## Common options for video output formats

### `video_kilobitrate`, `audio_kilobitrate` (optionals - advanced -)

For video and audio output formats, change bitrate.

__default__: depends on the output format.

### `video_codec`, `audio_codec`, `passes` (optionals - advanced -)

Video output formats use internaly a "ffmpeg-format" which itself may support multiple codecs.

e.g. `video-mpeg4` uses ffmpeg-format `X264`, which supports many audio codecs like `aac`, `libmp3lame`, ...

One can change the default ffmpeg codec(s) by setting `video_codec` and/or `audio_codec`.

__default__: depends on the output format, if it uses internally a "ffmpeg-format" like X264, Ogg, ...



--------------------------------------------

# Modules

## video_to_frame
Extracts a frame (image) from a video.

- `from_seconds` time in the video where the frame is extracted.

```yaml
# example
video:
normalization: ~
transformations:
-
module: video_to_frame
enabled: true
options:
timeout: 3600
threads: 4
format: image-jpeg
from_seconds: 4
extension: jpeg
```

## video_to_animation
Build an animation from a video.

- `from_seconds` time in the video where the animation begins.
- `duration` duration of the animation in seconds.
- `fps` frames per second of the animation.
- `width`, `height` size of the animation (see below "resize modes").
- `mode` default to `inset` (see below "resize modes").

```yaml
module: video_to_animation
options:
format: animated-gif
from_seconds: 25
duration: 5
fps: 5
width: 200
height: 100
mode: inset
```

## video_summary
Build a video made from extracts of the input video.

- `period` period in seconds between each extract.
- `duration` duration of each extract in seconds.

```yaml
module: video_summary
options:
format: video-quicktime
period: 30
duration: 2
```

## ffmpeg
Generic module to chain ffmpeg "filters" in a single command.

- `filters` list of ffmpeg filters to apply.

Each "filter" has a name and a list of specific options ; Set `enable=false` to disable a filter.

```yaml
module: ffmpeg
options:
format: video-quicktime
filters:
-
name: resize
width: 320
height: 240
mode: inset
-
name: watermark
enabled: false # THIS FILTER IS DISABLED
# only local files are supported for now
path: "/var/workspace/my_watermarks/google_PNG.png"
position: relative
bottom: 50
right: 50
```


--------------------------------------------

## Output formats

| format | family | mime type | extension(s) |
|-----------------|-----------|------------------|--------------|
| animated-gif | Animation | image/gif | gif |
| animated-png | Animation | image/png | apng, png |
| animated-webp | Animation | image/webp | webp |
| image-jpeg | Image | image/jpeg | jpg, jpeg |
| video-mkv | Video | video/x-matroska | mkv |
| video-mpeg4 | Video | video/mp4 | mp4 |
| video-mpeg | Video | video/mpeg | mpeg |
| video-quicktime | Video | video/quicktime | mov |
| video-webm | Video | video/webm | webm |

--------------------------------------------

## Resize modes
### `inset`
The output is garanteed to fit in the requested size (width, height) and the aspect ratio is kept.
- If only one dimension is provided, the other is computed.
- If both dimensions are provided, the output is resize so the biggest dimension fits into the rectangle.
- If no dimension is provided, the output is the same size as the input.
15 changes: 9 additions & 6 deletions lib/php/rendition-factory/phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" backupGlobals="false" colors="true">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.3/phpunit.xsd" backupGlobals="false" colors="true" cacheDirectory=".phpunit.cache">
<php>
<ini name="error_reporting" value="-1"/>
<server name="SHELL_VERBOSITY" value="-1"/>
Expand All @@ -16,4 +11,12 @@
<directory>tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">src</directory>
</include>
<exclude>
<file>tests/AbstractTest.php</file>
</exclude>
</source>
</phpunit>
10 changes: 7 additions & 3 deletions lib/php/rendition-factory/src/Command/CreateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ protected function configure(): void

$this->addArgument('src', InputArgument::REQUIRED, 'The source file');
$this->addArgument('build-config', InputArgument::REQUIRED, 'The build config YAML file');
$this->addOption('type', 't', InputOption::VALUE_OPTIONAL, 'Force the MIME type of file');
$this->addOption('type', 't', InputOption::VALUE_REQUIRED, 'Force the MIME type of file');
$this->addOption('working-dir', 'w', InputOption::VALUE_REQUIRED, 'The working directory. Defaults to system temp directory');
$this->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'The output file name WITHOUT extension');
$this->addOption('debug', 'd', InputOption::VALUE_NONE, 'set to debug mode (keep files in working directory)');
Expand Down Expand Up @@ -72,13 +72,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$buildConfig,
$options
);
$output->writeln(sprintf('Rendition created: %s', $outputFile->getPath()));

} catch (\InvalidArgumentException $e) {
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));

return 1;
}

if ($outputPath = $input->getOption('output')) {
if(substr($outputPath, -1) === '/') {
// a directory is specified, use the filename of the source
$outputPath .= pathinfo($src, PATHINFO_FILENAME);
}
@mkdir(dirname($outputPath), 0755, true);
$outputPath .= '.'.$outputFile->getExtension();
rename($outputFile->getPath(), $outputPath);
Expand All @@ -91,8 +97,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 1;
}

$output->writeln(sprintf('Rendition created: %s', $outputFile->getPath()));

if (!$input->getOption('debug')) {
$this->renditionCreator->cleanUp();
}
Expand Down
1 change: 1 addition & 0 deletions lib/php/rendition-factory/src/Config/YamlLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private function parseTransformation(array $transformation): Transformation
{
return new Transformation(
$transformation['module'],
$transformation['enabled'] ?? true,
$transformation['options'] ?? [],
$transformation['description'] ?? null
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ public function __construct(

public function getTransformations(): array
{
return $this->transformations;
return array_filter(
$this->transformations,
fn (Transformation $transformation) => $transformation->isEnabled()
);
}
jygaulier marked this conversation as resolved.
Show resolved Hide resolved

public function getNormalization(): array
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
{
public function __construct(
private string $module,
private bool $enabled,
private array $options,
private ?string $description,
) {
Expand All @@ -25,4 +26,9 @@ public function getOptions(): array
{
return $this->options;
}

public function isEnabled(): bool
{
return $this->enabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format;

use Alchemy\RenditionFactory\DTO\FamilyEnum;
use FFMpeg\Format\Video\X264;

class AnimatedGifFormat implements FormatInterface
{
public static function getAllowedExtensions(): array
{
return ['gif'];
}

public static function getMimeType(): string
{
return 'image/gif';
}

public static function getFormat(): string
{
return 'animated-gif';
}

public static function getFamily(): FamilyEnum
{
return FamilyEnum::Animation;
}
}
Loading
Loading