From 77cf08260aee96d734d3b7e2f4be8ce32ca03d99 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Sat, 10 Sep 2022 22:52:40 -0500 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Support=20attachments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Attachment.php | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/Map.php | 25 ++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/Attachment.php diff --git a/src/Attachment.php b/src/Attachment.php new file mode 100644 index 0000000..5db4fd8 --- /dev/null +++ b/src/Attachment.php @@ -0,0 +1,46 @@ +map = $map; + $this->input = $file; + // Shouldn't be necessary, but just in case + $this->codec = new Format\Copy(); + } + + public function mime(string $mime): static + { + return $this->addMetadata('mimetype', $mime); + } + + public function setCodec(FormatInterface $codec): static + { + return $this; + } + + protected function parseCodec(): static + { + return $this; + } + + public function saveAttachment(): Map + { + return $this->saveStream();; + } + + public function buildCommand(int $idx = 0): array + { + $commands = parent::buildCommand($idx); + $commands[0] = '-attach'; + + return $commands; + } +} diff --git a/src/Map.php b/src/Map.php index d18196a..c6ef98c 100644 --- a/src/Map.php +++ b/src/Map.php @@ -13,6 +13,8 @@ class Map protected string $path; /** @var Stream[] */ protected array $streams = []; + /** @var Attachment[] */ + protected array $attachments = []; /** @var AbstractProgressListener[] */ protected array $listeners = []; @@ -35,9 +37,8 @@ class Map return $this; } - public function stream(callable $callback = null): Stream|static + protected function doStream(Stream $stream, callable $callback = null): Stream|static { - $stream = new Stream($this); if (!$callback) { return $stream; } @@ -48,8 +49,24 @@ class Map return $this; } + public function stream(callable $callback = null): Stream|static + { + return $this->doStream(new Stream($this), $callback); + } + + public function attach(string $file = '', callable $callback = null): Attachment|static + { + return $this->doStream(new Attachment($this, $file), $callback); + } + public function saveStream(Stream $stream): static { + if ($stream instanceof Attachment){ + $this->attachments[] = $stream; + + return $this; + } + $this->streams[] = $stream; $format = $stream->getCodec(); if ($format instanceof ProgressableInterface) { @@ -72,7 +89,9 @@ class Map public function buildCommand(): array { $commands = []; - foreach ($this->streams as $idx => $stream) { + $streams = $this->streams; + array_push($streams, ...$this->attachments); + foreach ($streams as $idx => $stream) { array_push($commands, ...$stream->buildCommand($idx)); } foreach ($this->metadata as $k => $v) { From cb892134ca0866ef7e7fc223abb15f011886e1a7 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Sat, 10 Sep 2022 23:19:18 -0500 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=9A=B8=20Get=20full=20fully-escaped?= =?UTF-8?q?=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MappableMedia.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MappableMedia.php b/src/MappableMedia.php index c2fc9c9..b3f81c2 100644 --- a/src/MappableMedia.php +++ b/src/MappableMedia.php @@ -125,7 +125,9 @@ class MappableMedia extends AbstractMediaType implements EventEmitterInterface public function getFinalCommand(): string { - return implode(' ', $this->buildCommand()); + $proc = $this->driver->getProcessBuilderFactory()->create($this->buildCommand()); + + return $proc->getCommandLine(); } public function map(callable $callback = null): Map|static From 249a559c1277440e16671cdaab6fc87d1d2c6211 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Sat, 10 Sep 2022 23:45:52 -0500 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=93=9D=20Document=20attachments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ README.md | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 915529f..2ec1abe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `ffmpeg-mappable-media` will be documented in this file. +## 0.2.0 + +- ✨ Support attachments + ## 0.1.3 - ✨ Support progress listeners diff --git a/README.md b/README.md index 38e0585..bd97fa0 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ use FFMpeg\Format\Video\X264; MappableMedia::make($ffmpeg) ->addInput('input.mkv') ->map() - ->saveAs('output.mk') + ->saveAs('output.mkv') ->stream()->setCodec(new X264())->saveStream() ->saveMap() ->save() @@ -112,7 +112,7 @@ The `map` and `stream` methods can take an optional callback, allowing you to se MappableMedia::make($ffmpeg) ->addInput('input.mkv') ->map(function (Map $map) { - $map->saveAs('output.mk') + $map->saveAs('output.mkv') ->stream(function (Stream $stream) { $stream->copy()->setInput('0:0'); }); @@ -129,7 +129,7 @@ It is possible to set a listener on the individual streams, using the Format cla MappableMedia::make($ffmpeg) ->addInput('input.mkv') ->map() - ->saveAs('output.mk') + ->saveAs('output.mkv') ->stream()->copy()->saveStream() ->saveMap() ->on('progress', function (MappableMedia $media, int $percent, int $remaining, int $rate) { @@ -138,6 +138,47 @@ MappableMedia::make($ffmpeg) ->save() ``` +### Attachments + +Some formats (mkv, for example) support arbitrary data attachments. These can be used as cover art, fonts for subtitles, or any arbitrary data. + +FFMpeg does support attachments as an additional input. This works well for images, but can be finicky for other file types. Because of this, FFMpeg also supports an `-attach` flag which can be used to explicitly attach a new stream. + +Due to the way FFMpeg handles `-attach` differently than `-i`, these need to be added as streams to a specific map, rather than the whole media. Here are a few examples. + +```php +MappableMedia::make($ffmpeg) + ->addInput('input.mkv') + ->map() + ->saveAs('output.mkv') + ->stream()->copy()->saveStream() + ->attach('image.jpg') + ->mime('image/jpeg') + ->addMetadata('filename', 'cover.jpg') + ->saveAttachment() + ->saveMap() + ->save(); +``` + +In this example, we added cover art to the file. Notice the use of the `mime` method to specify the mime-type. **This must always be done**. Note that we also specified a different filename so that the media player would recognize it as cover art. If you don't specify a filename, the original will be used. + +```php +MappableMedia::make($ffmpeg) + ->addInput('input.mkv') + ->addInput('subs.ass') + ->map() + ->saveAs('output.mkv') + ->stream()->copy()->saveStream() + ->stream()->setInput('1:0')->copy()->saveStream() + ->attach(verdana.ttf') + ->mime('application/x-truetype-font') + ->saveAttachment() + ->saveMap() + ->save(); +``` + +In this example, we've added a font, which is likely referenced in the subtitle file, `subs.ass`. + ## Future Plans - [ ] Add listeners that return all the stdin/stderr