Skip to content

Commit

Permalink
Merge branch 'PS-670-rendition-video_enhance1' of github.com:alchemy-…
Browse files Browse the repository at this point in the history
…fr/phraseanet-services into w2443
  • Loading branch information
nmaillat committed Oct 18, 2024
2 parents 3424d8b + 2e5e04c commit cd6da95
Show file tree
Hide file tree
Showing 21 changed files with 989 additions and 194 deletions.
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: ~
173 changes: 173 additions & 0 deletions lib/php/rendition-factory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Rendition Factory for video-input modules (wip)

## 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.

```yaml
module: ffmpeg
options:
format: video-quicktime
filters:
-
name: resize
width: 320
height: 240
mode: inset
-
name: watermark
# 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.
6 changes: 3 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,6 +72,8 @@ 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()));

Expand All @@ -91,8 +93,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
@@ -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;
}
}
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 AnimatedPngFormat implements FormatInterface
{
public static function getAllowedExtensions(): array
{
return ['apng', 'png'];
}

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

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

public static function getFamily(): FamilyEnum
{
return FamilyEnum::Animation;
}
}
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 AnimatedWebpFormat implements FormatInterface
{
public static function getAllowedExtensions(): array
{
return ['webp'];
}

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

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

public static function getFamily(): FamilyEnum
{
return FamilyEnum::Animation;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

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


use Alchemy\RenditionFactory\DTO\FamilyEnum;
// use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

// #[AutoconfigureTag(self::TAG)]
interface FormatInterface
{
final public const TAG = 'alchemy_rendition_factory.ffmpeg_format';

public static function getAllowedExtensions(): array;
public static function getMimeType(): string;
public static function getFormat(): string;
public static function getFamily(): FamilyEnum;
}
Loading

0 comments on commit cd6da95

Please sign in to comment.