123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- <?php
- namespace Plugin\VideoEncoder;
- use App\Core\Event;
- use App\Core\GSFile;
- use function App\Core\I18n\_m;
- use App\Core\Log;
- use App\Core\Modules\Plugin;
- use App\Util\Exception\ServerException;
- use App\Util\Exception\TemporaryFileException;
- use App\Util\Formatting;
- use App\Util\TemporaryFile;
- use Exception;
- use FFMpeg\FFMpeg as ffmpeg;
- use FFMpeg\FFProbe as ffprobe;
- use SplFileInfo;
- class VideoEncoder extends Plugin
- {
-
- public function version(): string
- {
- return '1.0.0';
- }
-
- public function onFileSanitizerAvailable(array &$event_map, string $mimetype): bool
- {
- if (GSFile::mimetypeMajor($mimetype) !== 'video' && $mimetype !== 'image/gif') {
- return Event::next;
- }
- $event_map['video'][] = [$this, 'fileSanitize'];
- $event_map['image/gif'][] = [$this, 'fileSanitize'];
- return Event::next;
- }
-
- public function onFileResizerAvailable(array &$event_map, string $mimetype): bool
- {
- if (GSFile::mimetypeMajor($mimetype) !== 'video' && $mimetype !== 'image/gif') {
- return Event::next;
- }
- $event_map['video'][] = [$this, 'resizeVideoPath'];
- $event_map['image/gif'][] = [$this, 'resizeVideoPath'];
- return Event::next;
- }
-
- public function fileSanitize(SplFileInfo &$file, ?string &$mimetype, ?int &$width, ?int &$height): bool
- {
- if (
- $mimetype !== 'image/gif') {
- return false;
- }
-
-
- $ffprobe = ffprobe::create([
- 'ffmpeg.binaries' => exec('which ffmpeg'),
- 'ffprobe.binaries' => exec('which ffprobe'),
- ]);
- $metadata = $ffprobe->streams($file->getRealPath())
- ->videos()
- ->first();
- $width = $metadata->get('width');
- $height = $metadata->get('height');
-
- $mimetype = 'image/gif';
- return true;
- }
-
- public function resizeVideoPath(string $source, ?TemporaryFile &$destination, int &$width, int &$height, bool $smart_crop, ?string &$mimetype): bool
- {
- switch ($mimetype) {
- case 'image/gif':
-
- if ($this->isAnimatedGif($source)) {
- return $this->resizeImageFileAnimatedGif($source, $destination, $width, $height, $smart_crop, $mimetype);
- }
- break;
- }
- return false;
- }
-
- public function onViewAttachmentVideo(array $vars, array &$res): bool
- {
- $res[] = Formatting::twigRenderFile('videoEncoder/videoEncoderView.html.twig',
- ['attachment' => $vars['attachment'],
- 'thumbnail_parameters' => $vars['thumbnail_parameters'],
- 'note' => $vars['note'],
- ]);
- return Event::stop;
- }
-
- public function isAnimatedGif(string $filepath)
- {
- if (!($fh = @fopen($filepath, 'rb'))) {
- return false;
- }
- $count = 0;
-
-
-
-
-
-
-
-
- while (!feof($fh) && $count < 2) {
- $chunk = fread($fh, 1024 * 100);
- $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00\x2C#s', $chunk, $matches);
-
-
- if (!feof($fh) && ftell($fh) >= 9) {
- fseek($fh, -9, SEEK_CUR);
- }
- }
- fclose($fh);
- return $count >= 1;
- }
-
- public function resizeImageFileAnimatedGif(string $source, ?TemporaryFile &$destination, int &$width, int &$height, bool $smart_crop, ?string &$mimetype): bool
- {
-
-
- $ffmpeg = ffmpeg::create([
- 'ffmpeg.binaries' => exec('which ffmpeg'),
- 'ffprobe.binaries' => exec('which ffprobe'),
- ]);
-
-
- $destination = $destination ?? new TemporaryFile(['prefix' => 'video']);
-
-
- $palette = $this->tempnam_sfx(sys_get_temp_dir(), '.png');
-
- $filters = 'fps=30';
- $filters .= ",scale={$width}:{$height}:flags=lanczos";
-
- $commands[] = $commands_2[] = '-f';
- $commands[] = $commands_2[] = 'gif';
- $commands[] = $commands_2[] = '-i';
- $commands[] = $commands_2[] = $source;
- $commands[] = '-vf';
- $commands[] = $filters . ',palettegen';
- $commands[] = '-y';
- $commands[] = $palette;
-
- $commands_2[] = '-i';
- $commands_2[] = $palette;
- $commands_2[] = '-lavfi';
- $commands_2[] = $filters . ' [x]; [x][1:v] paletteuse';
- $commands_2[] = '-f';
- $commands_2[] = 'gif';
- $commands_2[] = '-y';
- $commands_2[] = $destination->getRealPath();
- $success = true;
-
- try {
- $ffmpeg->getFFMpegDriver()->command($commands);
- } catch (Exception $e) {
- Log::error('Unable to generate the palette image');
- $success = false;
- }
-
- try {
- if ($success) {
- $ffmpeg->getFFMpegDriver()->command($commands_2);
- }
- } catch (Exception $e) {
- Log::error('Unable to generate the GIF image');
- $success = false;
- }
- @unlink($palette);
- $mimetype = 'image/gif';
- return $success;
- }
-
- private function tempnam_sfx(string $dir, string $suffix): string
- {
- do {
- $file = $dir . '/' . mt_rand() . $suffix;
- $fp = @fopen($file, 'x');
- } while (!$fp);
- fclose($fp);
- return $file;
- }
-
- public function onPluginVersion(array &$versions): bool
- {
- $versions[] = ['name' => 'FFmpeg',
- 'version' => self::version(),
- 'author' => 'Bruno Casteleiro, Diogo Peralta Cordeiro',
- 'homepage' => 'https://notabug.org/diogo/gnu-social/src/nightly/plugins/FFmpeg',
- 'rawdescription' =>
- _m('Use PHP-FFMpeg for some more video support.'),
- ];
- return Event::next;
- }
- }
|