pbmedia / laravel-ffmpeg
Laravel 的 FFMpeg
Requires
- php: ^8.1|^8.2|^8.3
- illuminate/contracts: ^10.0|^11.0
- php-ffmpeg/php-ffmpeg: ^1.2
- ramsey/collection: ^2.0
Requires (Dev)
- league/flysystem-memory: ^3.10
- mockery/mockery: ^1.4.4
- nesbot/carbon: ^2.66|^3.0
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.4
- spatie/image: ^2.2|^3.3
- spatie/phpunit-snapshot-assertions: ^5.0
- dev-main
- 8.5.0
- 8.4.0
- 8.3.0
- 8.2.2
- 8.2.1
- 8.2.0
- 8.1.2
- 8.1.1
- 8.1.0
- 8.0.2
- 8.0.1
- 8.0.0
- 7.x-dev
- 7.8.1
- 7.8.0
- 7.7.3
- 7.7.2
- 7.7.1
- 7.7.0
- 7.6.0
- 7.5.12
- 7.5.11
- 7.5.10
- 7.5.9
- 7.5.8
- 7.5.7
- 7.5.6
- 7.5.5
- 7.5.4
- 7.5.3
- 7.5.2
- 7.5.1
- 7.5.0
- 7.4.1
- 7.4.0
- 7.3.0
- 7.2.0
- 7.1.0
- 7.0.5
- 7.0.4
- 7.0.3
- 7.0.2
- 7.0.1
- 7.0.0
- 6.0.0
- 5.0.1
- 5.0.0
- 4.1.0
- 4.0.1
- 4.0.0
- 3.0.2
- 3.0.1
- 3.0.0
- 2.1.1
- 2.1.0
- 2.0.1
- 2.0.0
- 1.3.0
- 1.2.0
- 1.1.12
- 1.1.11
- 1.1.10
- 1.1.9
- 1.1.8
- 1.1.7
- 1.1.6
- 1.1.5
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.1
- 1.0.0
- dev-php-83
- dev-php-8.2
- dev-resolve-driver-lazily
- dev-temp-encrpyted-hls
- dev-feature/pass-temporary-root-through
- dev-laravel-9
- dev-v7-modernized
- dev-v7-video-attachted-pic
- dev-v7-std-listener
This package is auto-updated.
Last update: 2024-09-20 14:01:49 UTC
README
此包为 Laravel 10 提供了与 FFmpeg 的集成。Laravel 的文件系统负责文件存储。Laravel 文件系统。
赞助我们
❤️ 我们自豪地通过开发并免费提供 Laravel 包来支持社区。如果这个包为您节省了时间,或者您正在专业上依赖它,请考虑赞助维护和开发,并查看我们最新的高级包: Inertia Table。跟踪问题和拉取请求需要时间,但我们乐于提供帮助!
功能
- PHP-FFMpeg 的简单包装,包括对过滤器和其它高级特性的支持。PHP-FFMpeg。
- 与 Laravel 文件系统、配置系统 和 日志处理 集成。
- 与 Laravel 10 兼容,支持 包发现。
- 内置对 HLS 的支持。
- 内置对加密 HLS (AES-128) 和旋转密钥(可选)的支持。
- 内置对拼接、多个输入/输出、图像序列(时间流逝)、复杂过滤器(和映射)、帧/缩略图导出的支持。
- 内置对水印(位置和操作)的支持。
- 内置从视频创建马赛克/精灵/瓦片的支持。
- 内置生成 VTT 预览缩略图 文件的支持。
- 需要 PHP 8.1 或更高版本。
- 已测试与 FFmpeg 4.4 和 5.0 兼容。
安装
验证您已安装最新版本的 FFmpeg
ffmpeg -version
您可以通过 composer 安装此包
composer require pbmedia/laravel-ffmpeg
如果未使用包发现,请将服务提供者和外观添加到您的 app.php
配置文件中
// config/app.php 'providers' => [ ... ProtoneMedia\LaravelFFMpeg\Support\ServiceProvider::class, ... ]; 'aliases' => [ ... 'FFMpeg' => ProtoneMedia\LaravelFFMpeg\Support\FFMpeg::class ... ];
使用 artisan CLI 工具发布配置文件
php artisan vendor:publish --provider="ProtoneMedia\LaravelFFMpeg\Support\ServiceProvider"
升级到 v8
set_command_and_error_output_on_exception
配置键现在默认为true
,使异常更具有信息性。更多信息请参阅 处理异常 部分。enable_logging
配置键已被log_channel
替换,用于选择写入日志时使用的日志通道。如果您希望完全禁用日志记录,可以将新的配置键设置为false
。- HLS 导出的 段长度 和 关键帧间隔 应为
2
或更多;不再支持更少。 - 由于 Laravel 9 已从 Flysystem 1.x 迁移到 3.x,此版本与 Laravel 8 或更早版本不兼容。
- 如果您正在使用水印操作功能,请确保您已将
spatie/image
升级到v2版本。
升级到v7
- 命名空间已更改为
ProtoneMedia\LaravelFFMpeg
,外观已重命名为ProtoneMedia\LaravelFFMpeg\Support\FFMpeg
,服务提供者已重命名为ProtoneMedia\LaravelFFMpeg\Support\ServiceProvider
。 - 仍然支持链式导出,但您必须为每个导出重新应用过滤器。
- HLS播放列表现在包括比特率、帧率和分辨率数据。段也使用新的命名模式(了解更多)。请检查您的导出是否能在您的播放器中正常工作。
- HLS导出现在作为一个单一作业执行,而不是分别导出每个格式/流。它使用FFMpeg的
map
和filter_complex
功能。可能只需将所有对addFilter
的调用替换为addLegacyFilter
即可,但某些过滤器需要手动迁移。请阅读关于HLS的文档以了解更多关于添加过滤器的信息。
用法
转换音频或视频文件
FFMpeg::fromDisk('songs') ->open('yesterday.mp3') ->export() ->toDisk('converted_songs') ->inFormat(new \FFMpeg\Format\Audio\Aac) ->save('yesterday.aac');
除了fromDisk()
方法,您还可以使用fromFilesystem()
方法,其中$filesystem
是Illuminate\Contracts\Filesystem\Filesystem
的实例。
$media = FFMpeg::fromFilesystem($filesystem)->open('yesterday.mp3');
进度监控
您可以监控转码进度。使用onProgress
方法提供一个回调,它给您完成百分比。在之前版本的此包中,您必须将回调传递给格式对象。
FFMpeg::open('steve_howe.mp4') ->export() ->onProgress(function ($percentage) { echo "{$percentage}% transcoded"; });
回调还可以公开$remaining
(以秒为单位)和$rate
FFMpeg::open('steve_howe.mp4') ->export() ->onProgress(function ($percentage, $remaining, $rate) { echo "{$remaining} seconds left at rate: {$rate}"; });
打开上传的文件
您可以直接从Request
实例打开上传的文件。如果请求中断,最好先保存上传的文件,但如果愿意,您也可以打开一个UploadedFile
实例
class UploadVideoController { public function __invoke(Request $request) { FFMpeg::open($request->file('video')); } }
从网络打开文件
您可以使用openUrl
方法从网络打开文件。您可以使用可选的第二参数指定自定义HTTP头。
FFMpeg::openUrl('https://videocoursebuilder.com/lesson-1.mp4'); FFMpeg::openUrl('https://videocoursebuilder.com/lesson-2.mp4', [ 'Authorization' => 'Basic YWRtaW46MTIzNA==', ]);
处理异常
当编码失败时,将抛出ProtoneMedia\LaravelFFMpeg\Exporters\EncodingException
,该异常扩展了底层的FFMpeg\Exception\RuntimeException
类。此类有两个方法可以帮助您识别问题。使用getCommand
方法,您可以获取带有所有参数的执行命令。使用getErrorOutput
方法,您可以获取完整的输出日志。
在之前版本的此包中,异常消息始终是编码失败。您可以通过将set_command_and_error_output_on_exception
配置键更新为false
来降级到此消息。
try { FFMpeg::open('yesterday.mp3') ->export() ->inFormat(new Aac) ->save('yesterday.aac'); } catch (EncodingException $exception) { $command = $exception->getCommand(); $errorLog = $exception->getErrorOutput(); }
过滤器
您可以通过一个Closure
或使用PHP-FFMpeg的过滤器对象来添加过滤器
use FFMpeg\Filters\Video\VideoFilters; FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->addFilter(function (VideoFilters $filters) { $filters->resize(new \FFMpeg\Coordinate\Dimension(640, 480)); }) ->export() ->toDisk('converted_videos') ->inFormat(new \FFMpeg\Format\Video\X264) ->save('small_steve.mkv'); // or $start = \FFMpeg\Coordinate\TimeCode::fromSeconds(5) $clipFilter = new \FFMpeg\Filters\Video\ClipFilter($start); FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->addFilter($clipFilter) ->export() ->toDisk('converted_videos') ->inFormat(new \FFMpeg\Format\Video\X264) ->save('short_steve.mkv');
您还可以在export
方法之后调用addFilter
方法
use FFMpeg\Filters\Video\VideoFilters; FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->export() ->toDisk('converted_videos') ->inFormat(new \FFMpeg\Format\Video\X264) ->addFilter(function (VideoFilters $filters) { $filters->resize(new \FFMpeg\Coordinate\Dimension(640, 480)); }) ->save('small_steve.mkv');
调整大小
由于调整大小是一个常见的操作,因此我们为其添加了专门的方法
FFMpeg::open('steve_howe.mp4') ->export() ->inFormat(new \FFMpeg\Format\Video\X264) ->resize(640, 480) ->save('steve_howe_resized.mp4');
第一个参数是宽度,第二个参数是高度。可选的第三个参数是模式。您可以选择fit
(默认)、inset
、width
或height
。可选的第四个参数是一个布尔值,表示是否强制使用标准比例。您可以在FFMpeg\Filters\Video\ResizeFilter
类中找到有关这些模式的更多信息。
自定义过滤器
有时您不想使用内置的过滤器。您可以通过提供一组选项来应用自己的过滤器。这可以是一个数组或多个字符串作为参数
FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->addFilter(['-itsoffset', 1]); // or FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->addFilter('-itsoffset', 1);
水印过滤器
您可以使用addWatermark
方法轻松添加水印。使用WatermarkFactory
,您可以像打开音频或视频文件一样从特定磁盘打开水印文件。当您取消选择fromDisk
方法时,它将使用在filesystems.php
配置文件中指定的默认磁盘。
打开水印文件后,您可以使用top
、right
、bottom
和left
方法将其定位。这些方法的第一个参数是偏移量,它是可选的,可以是负数。
use ProtoneMedia\LaravelFFMpeg\Filters\WatermarkFactory; FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->addWatermark(function(WatermarkFactory $watermark) { $watermark->fromDisk('local') ->open('logo.png') ->right(25) ->bottom(25); });
除了使用位置方法外,您还可以使用horizontalAlignment
和verticalAlignment
方法。
对于水平对齐,您可以使用WatermarkFactory::LEFT
、WatermarkFactory::CENTER
和WatermarkFactory::RIGHT
常量。对于垂直对齐,您可以使用WatermarkFactory::TOP
、WatermarkFactory::CENTER
和WatermarkFactory::BOTTOM
常量。这两种方法都接受一个可选的第二个参数,即偏移量。
FFMpeg::open('steve_howe.mp4') ->addWatermark(function(WatermarkFactory $watermark) { $watermark->open('logo.png') ->horizontalAlignment(WatermarkFactory::LEFT, 25) ->verticalAlignment(WatermarkFactory::TOP, 25); });
WatermarkFactory
还支持使用openUrl
方法从网络打开文件。它还支持自定义HTTP头。
FFMpeg::open('steve_howe.mp4') ->addWatermark(function(WatermarkFactory $watermark) { $watermark->openUrl('https://videocoursebuilder.com/logo.png'); // or $watermark->openUrl('https://videocoursebuilder.com/logo.png', [ 'Authorization' => 'Basic YWRtaW46MTIzNA==', ]); });
如果您想对GET请求有更多的控制,您可以传递一个可选的第三个参数,它为您提供Curl资源。
$watermark->openUrl('https://videocoursebuilder.com/logo.png', [ 'Authorization' => 'Basic YWRtaW46MTIzNA==', ], function($curl) { curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); });
水印操作
此包可以通过使用Spatie的Image包来操作水印。要开始使用,请使用Composer安装此包
composer require spatie/image
现在您可以在WatermarkFactory
实例上链式调用一个额外的操作方法
FFMpeg::open('steve_howe.mp4') ->addWatermark(function(WatermarkFactory $watermark) { $watermark->open('logo.png') ->right(25) ->bottom(25) ->width(100) ->height(100) ->greyscale(); });
查看文档以获取所有可用方法。
不转码导出
此包附带一个ProtoneMedia\LaravelFFMpeg\FFMpeg\CopyFormat
类,允许您在不转码流的情况下导出文件。您可能想使用此功能来使用其他容器
use ProtoneMedia\LaravelFFMpeg\FFMpeg\CopyFormat; FFMpeg::open('video.mp4') ->export() ->inFormat(new CopyFormat) ->save('video.mkv');
链式多个转换
// The 'fromDisk()' method is not required, the file will now // be opened from the default 'disk', as specified in // the config file. FFMpeg::open('my_movie.mov') // export to FTP, converted in WMV ->export() ->toDisk('ftp') ->inFormat(new \FFMpeg\Format\Video\WMV) ->save('my_movie.wmv') // export to Amazon S3, converted in X264 ->export() ->toDisk('s3') ->inFormat(new \FFMpeg\Format\Video\X264) ->save('my_movie.mkv'); // you could even discard the 'toDisk()' method, // now the converted file will be saved to // the same disk as the source! ->export() ->inFormat(new FFMpeg\Format\Video\WebM) ->save('my_movie.webm') // optionally you could set the visibility // of the exported file ->export() ->inFormat(new FFMpeg\Format\Video\WebM) ->withVisibility('public') ->save('my_movie.webm')
从视频中导出帧
FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->getFrameFromSeconds(10) ->export() ->toDisk('thumnails') ->save('FrameAt10sec.png'); // Instead of the 'getFrameFromSeconds()' method, you could // also use the 'getFrameFromString()' or the // 'getFrameFromTimecode()' methods: $media = FFMpeg::open('steve_howe.mp4'); $frame = $media->getFrameFromString('00:00:13.37'); // or $timecode = new FFMpeg\Coordinate\TimeCode(...); $frame = $media->getFrameFromTimecode($timecode);
您也可以获取帧的原始内容,而不是将其保存到文件系统中
$contents = FFMpeg::open('video.mp4') ->getFrameFromSeconds(2) ->export() ->getFrameContents();
一次性导出多个帧
有一个TileFilter
为拼贴功能提供支持。为了使导出多个帧更快、更简单,我们利用了这个功能来添加一些辅助方法。例如,您可以使用exportFramesByInterval
方法通过固定间隔导出帧。或者,您可以将要导出的帧数传递给exportFramesByAmount
方法,它将根据视频的持续时间来计算间隔。
FFMpeg::open('video.mp4') ->exportFramesByInterval(2) ->save('thumb_%05d.jpg');
两种方法都接受一个可选的第二和第三个参数,用于指定帧的宽度和高度。您也可以只传递其中一个参数。FFmpeg将尊重源的比例。
FFMpeg::open('video.mp4') ->exportFramesByAmount(10, 320, 180) ->save('thumb_%05d.png');
两种方法都接受一个可选的第四个参数,用于在将图像导出到有损格式(如JPEG)时指定图像的质量。JPEG的范围为2-31
,其中2
是最佳质量,31
是最差质量。
FFMpeg::open('video.mp4') ->exportFramesByInterval(2, 640, 360, 5) ->save('thumb_%05d.jpg');
创建帧的拼贴
您可以从视频中创建拼贴。您可以通过调用exportTile
方法来指定如何生成拼贴。在下面的示例中,每个生成的图像由一个3x5网格组成(因此包含15帧),每个帧为160x90像素。从视频中每隔5秒取一个帧。您也可以只传递宽度或高度中的一个参数。FFmpeg将尊重源的比例。
use ProtoneMedia\LaravelFFMpeg\Filters\TileFactory; FFMpeg::open('steve_howe.mp4') ->exportTile(function (TileFactory $factory) { $factory->interval(5) ->scale(160, 90) ->grid(3, 5); }) ->save('tile_%05d.jpg');
您不必同时传递宽度和高度,也可以只传递其中之一,例如 scale(160)
或 scale(null, 90)
。将会尊重宽高比。TileFactory
还具有 margin
、padding
、width
和 height
方法。还有一个 quality
方法,用于在导出为 JPEG 等有损格式时指定质量。JPEG 的范围是 2-31
,其中 2
是最佳质量,而 31
是最差质量。
此软件包还可以生成 WebVTT 文件,以向您的视频播放器添加 预览缩略图。JW 播放器已内置支持(请参阅如何添加预览缩略图),并且还有针对 Video.js 的社区插件。您可以通过在 TileFactory
上调用 generateVTT
方法并指定所需文件名来生成 WebVTT 文件。
FFMpeg::open('steve_howe.mp4') ->exportTile(function (TileFactory $factory) { $factory->interval(10) ->scale(320, 180) ->grid(5, 5) ->generateVTT('thumbnails.vtt'); }) ->save('tile_%05d.jpg');
使用循环进行多次导出
多次转换的链接工作是因为 MediaExporter
的 save
方法返回一个全新的 MediaOpener
实例。您可以使用它来循环遍历项目,例如,从单个视频中导出多个帧。
$mediaOpener = FFMpeg::open('video.mp4'); foreach ([5, 15, 25] as $key => $seconds) { $mediaOpener = $mediaOpener->getFrameFromSeconds($seconds) ->export() ->save("thumb_{$key}.png"); }
MediaOpener
还提供了一个 each
方法。上面的示例可以重构如下:
FFMpeg::open('video.mp4')->each([5, 15, 25], function ($ffmpeg, $seconds, $key) { $ffmpeg->getFrameFromSeconds($seconds)->export()->save("thumb_{$key}.png"); });
创建时间流逝视频
您可以通过在导出器上使用 asTimelapseWithFramerate
方法从一系列图像创建时间流逝视频。
FFMpeg::open('feature_%04d.png') ->export() ->asTimelapseWithFramerate(1) ->inFormat(new X264) ->save('timelapse.mp4');
多个输入
您可以打开多个输入,甚至来自不同的磁盘。这使用了 FFMpeg 的 map
和 filter_complex
功能。您可以通过链式调用 open
方法或使用数组来打开多个文件。您可以混合来自不同磁盘的输入。
FFMpeg::open('video1.mp4')->open('video2.mp4'); FFMpeg::open(['video1.mp4', 'video2.mp4']); FFMpeg::fromDisk('uploads') ->open('video1.mp4') ->fromDisk('archive') ->open('video2.mp4');
当打开多个输入时,您必须添加映射,以便 FFMpeg 知道如何路由它们。此软件包提供了一个 addFormatOutputMapping
方法,它接受三个参数:格式、输出和 -filter_complex
部分的输出标签。
输出(第二个参数)应该是 ProtoneMedia\LaravelFFMpeg\Filesystem\Media
的实例。您可以使用 make
方法进行实例化,并通过名称和路径调用它(请参阅示例)。
查看此示例,该示例将单独的视频和音频输入映射到一个输出。
FFMpeg::fromDisk('local') ->open(['video.mp4', 'audio.m4a']) ->export() ->addFormatOutputMapping(new X264, Media::make('local', 'new_video.mp4'), ['0:v', '1:a']) ->save();
// This code takes 2 input videos, stacks they horizontally in 1 output video and // adds to this new video the audio from the first video. (It is impossible // with a simple filter graph that has only 1 input and only 1 output). FFMpeg::fromDisk('local') ->open(['video.mp4', 'video2.mp4']) ->export() ->addFilter('[0:v][1:v]', 'hstack', '[v]') // $in, $parameters, $out ->addFormatOutputMapping(new X264, Media::make('local', 'stacked_video.mp4'), ['0:a', '[v]']) ->save();
就像单个输入一样,您也可以将回调传递给 addFilter
方法。这将为您提供 \FFMpeg\Filters\AdvancedMedia\ComplexFilters
的实例。
use FFMpeg\Filters\AdvancedMedia\ComplexFilters; FFMpeg::open(['video.mp4', 'video2.mp4']) ->export() ->addFilter(function(ComplexFilters $filters) { // $filters->watermark(...); });
从网络上打开文件的工作方式类似。您可以将 URL 数组传递给 openUrl
方法,可选地附带自定义 HTTP 标头。
FFMpeg::openUrl([ 'https://videocoursebuilder.com/lesson-3.mp4', 'https://videocoursebuilder.com/lesson-4.mp4', ]); FFMpeg::openUrl([ 'https://videocoursebuilder.com/lesson-3.mp4', 'https://videocoursebuilder.com/lesson-4.mp4', ], [ 'Authorization' => 'Basic YWRtaW46MTIzNA==', ]);
如果您想为每个 URL 使用另一组 HTTP 标头,可以链式调用 openUrl
方法。
FFMpeg::openUrl('https://videocoursebuilder.com/lesson-5.mp4', [ 'Authorization' => 'Basic YWRtaW46MTIzNA==', ])->openUrl('https://videocoursebuilder.com/lesson-6.mp4', [ 'Authorization' => 'Basic bmltZGE6NDMyMQ==', ]);
不进行转码的文件连接
FFMpeg::fromDisk('local') ->open(['video.mp4', 'video2.mp4']) ->export() ->concatWithoutTranscoding() ->save('concat.mp4');
进行转码的文件连接
FFMpeg::fromDisk('local') ->open(['video.mp4', 'video2.mp4']) ->export() ->inFormat(new X264) ->concatWithTranscoding($hasVideo = true, $hasAudio = true) ->save('concat.mp4');
确定持续时间
使用 Media
类,您可以确定文件持续时间。
$media = FFMpeg::open('wwdc_2006.mp4'); $durationInSeconds = $media->getDurationInSeconds(); // returns an int $durationInMiliseconds = $media->getDurationInMiliseconds(); // returns a float
处理远程磁盘
当从或向远程磁盘打开或保存文件时,您的服务器上将会创建临时文件。完成导出或处理这些文件后,您可以通过调用 cleanupTemporaryFiles()
方法来清理它们。
FFMpeg::cleanupTemporaryFiles();
默认情况下,临时目录的根由 PHP 的 sys_get_temp_dir()
方法评估,但您可以通过将 temporary_files_root
配置键设置为一个自定义路径来修改它。
HLS
您可以使用 M3U8 播单来创建 HLS。
$lowBitrate = (new X264)->setKiloBitrate(250); $midBitrate = (new X264)->setKiloBitrate(500); $highBitrate = (new X264)->setKiloBitrate(1000); FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->exportForHLS() ->setSegmentLength(10) // optional ->setKeyFrameInterval(48) // optional ->addFormat($lowBitrate) ->addFormat($midBitrate) ->addFormat($highBitrate) ->save('adaptive_steve.m3u8');
HLS 导出器的 addFormat
方法接受一个可选的第二个参数,该参数可以是回调方法。这允许您为每个格式添加不同的过滤器。首先,请查看 多个输入 部分,以了解如何处理复杂过滤器。
您可以使用addFilter
方法添加复杂过滤器(例如查看$lowBitrate
示例)。由于scale
过滤器使用频率很高,因此有一个辅助方法(例如查看$midBitrate
示例)。您还可以使用可调用函数来访问ComplexFilters
实例。该包提供了$in
和$out
参数,因此您无需担心(例如查看$highBitrate
示例)。
使用FFmpeg的map
和filter_complex
功能构建HLS导出。这与早期版本(1.x - 6.x)有重大差异,早期版本对每种格式执行单个导出。如果您正在升级,请将addFilter
调用替换为addLegacyFilter
调用,并验证结果(例如查看$superBitrate
示例)。并非所有过滤器都将以这种方式工作,某些过滤器需要手动升级。
$lowBitrate = (new X264)->setKiloBitrate(250); $midBitrate = (new X264)->setKiloBitrate(500); $highBitrate = (new X264)->setKiloBitrate(1000); $superBitrate = (new X264)->setKiloBitrate(1500); FFMpeg::open('steve_howe.mp4') ->exportForHLS() ->addFormat($lowBitrate, function($media) { $media->addFilter('scale=640:480'); }) ->addFormat($midBitrate, function($media) { $media->scale(960, 720); }) ->addFormat($highBitrate, function ($media) { $media->addFilter(function ($filters, $in, $out) { $filters->custom($in, 'scale=1920:1200', $out); // $in, $parameters, $out }); }) ->addFormat($superBitrate, function($media) { $media->addLegacyFilter(function ($filters) { $filters->resize(new \FFMpeg\Coordinate\Dimension(2560, 1920)); }); }) ->save('adaptive_steve.m3u8');
使用自定义段模式
您可以使用自定义模式命名段和播放列表。useSegmentFilenameGenerator
提供了5个参数。第一个、第二个和第三个参数提供了关于导出基本名、当前迭代格式和当前迭代键的信息。第四个参数是一个回调,您应使用segments模式调用它。第五个参数是一个回调,您应使用playlist模式调用它。请注意,这不是主播放列表的名称,而是每种格式的播放列表的名称。
FFMpeg::fromDisk('videos') ->open('steve_howe.mp4') ->exportForHLS() ->useSegmentFilenameGenerator(function ($name, $format, $key, callable $segments, callable $playlist) { $segments("{$name}-{$format->getKiloBitrate()}-{$key}-%03d.ts"); $playlist("{$name}-{$format->getKiloBitrate()}-{$key}.m3u8"); });
加密HLS
您可以使用AES-128加密加密每个HLS段。为此,请使用密钥在HLS导出器上调用withEncryptionKey
方法。我们在HLSExporter
类上提供了一个generateEncryptionKey
辅助方法来生成密钥。请确保妥善存储密钥,因为没有密钥的导出结果是毫无价值的。默认情况下,密钥的文件名为secret.key
,但您可以使用withEncryptionKey
方法的可选第二个参数来更改它。
use ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter; $encryptionKey = HLSExporter::generateEncryptionKey(); FFMpeg::open('steve_howe.mp4') ->exportForHLS() ->withEncryptionKey($encryptionKey) ->addFormat($lowBitrate) ->addFormat($midBitrate) ->addFormat($highBitrate) ->save('adaptive_steve.m3u8');
为了进一步保护您的HLS导出,您可以在每个导出段上旋转密钥。这样做将生成多个密钥,您需要存储这些密钥。使用withRotatingEncryptionKey
方法启用此功能,并提供一个实现密钥存储的回调。
FFMpeg::open('steve_howe.mp4') ->exportForHLS() ->withRotatingEncryptionKey(function ($filename, $contents) { $videoId = 1; // use this callback to store the encryption keys Storage::disk('secrets')->put($videoId . '/' . $filename, $contents); // or... DB::table('hls_secrets')->insert([ 'video_id' => $videoId, 'filename' => $filename, 'contents' => $contents, ]); }) ->addFormat($lowBitrate) ->addFormat($midBitrate) ->addFormat($highBitrate) ->save('adaptive_steve.m3u8');
withRotatingEncryptionKey
方法有一个可选的第二个参数,用于设置使用相同密钥的段数。默认为1
。
FFMpeg::open('steve_howe.mp4') ->exportForHLS() ->withRotatingEncryptionKey($callable, 10);
一些文件系统,特别是在廉价的慢速VPS上,不足以处理旋转密钥。这可能导致编码异常,如“在密钥信息文件中未指定密钥URI”。一个可能的解决方案是使用不同的密钥存储,您可以使用temporary_files_encrypted_hls
配置密钥指定它。在基于UNIX的系统上,您可以使用tmpfs
文件系统来提高读写速度
// config/laravel-ffmpeg.php return [ 'temporary_files_encrypted_hls' => '/dev/shm' ];
保护您的HLS加密密钥
为了使使用加密HLS的工作更加出色,我们添加了一个DynamicHLSPlaylist
类,该类在实时和专门为您的应用程序修改播放列表。这样,您可以添加您的身份验证和授权逻辑。由于我们使用的是纯Laravel控制器,因此您可以使用Gates和Middleware等特性。
在这个示例中,我们将HLS导出保存到了public
磁盘,并将加密密钥存储到了secrets
磁盘,该磁盘不公开。由于浏览器无法访问加密密钥,因此它不会播放视频。每个播放列表都有指向加密密钥的路径,我们需要修改这些路径以指向可访问的端点。
此实现由两个路由组成。一个响应加密密钥的路由,另一个响应修改后的播放列表的路由。第一个路由(video.key
)相对简单,您应该在这里添加额外的逻辑。
第二条路由(video.playlist
)使用了DynamicHLSPlaylist
类。在FFMpeg
代理上调用dynamicHLSPlaylist
方法,类似于打开媒体文件,你可以使用fromDisk
和open
方法打开播放列表。然后你必须提供三个回调函数。每个回调函数都提供相对路径,并期望返回完整路径。由于DynamicHLSPlaylist
类实现了Illuminate\Contracts\Support\Responsable
接口,因此你可以返回该实例。
第一个回调函数(KeyUrlResolver)提供加密键的相对路径。第二个回调函数(MediaUrlResolver)提供媒体片段(.ts文件)的相对路径。第三个回调函数(PlaylistUrlResolver)提供播放列表的相对路径。
现在,你可以使用route('video.playlist', ['playlist' => 'adaptive_steve.m3u8'])
来替代Storage::disk('public')->url('adaptive_steve.m3u8')
获取主播放列表的完整URL,DynamicHLSPlaylist
类将处理所有路径和URL。
Route::get('/video/secret/{key}', function ($key) { return Storage::disk('secrets')->download($key); })->name('video.key'); Route::get('/video/{playlist}', function ($playlist) { return FFMpeg::dynamicHLSPlaylist() ->fromDisk('public') ->open($playlist) ->setKeyUrlResolver(function ($key) { return route('video.key', ['key' => $key]); }) ->setMediaUrlResolver(function ($mediaFilename) { return Storage::disk('public')->url($mediaFilename); }) ->setPlaylistUrlResolver(function ($playlistFilename) { return route('video.playlist', ['playlist' => $playlistFilename]); }); })->name('video.playlist');
现场编码会议
在这里你可以找到一个关于HLS加密的现场编码会议
https://www.youtube.com/watch?v=WlbzWoAcez4
处理输出
你可以通过调用getProcessOutput
方法来获取原始进程输出。尽管用例有限,但你可以用它来分析文件(例如,使用volumedetect
过滤器)。它返回一个ProtoneMedia\LaravelFFMpeg\Support\ProcessOutput
类,该类有三个方法:all
、errors
和output
。每个方法都返回一个包含对应行的数组。
$processOutput = FFMpeg::open('video.mp4') ->export() ->addFilter(['-filter:a', 'volumedetect', '-f', 'null']) ->getProcessOutput(); $processOutput->all(); $processOutput->errors(); $processOutput->out();
高级
当你“打开”文件时得到的媒体对象,实际上持有属于底层驱动的媒体对象。它可以处理动态方法调用,如你所见这里。这样,底层驱动程序的所有方法仍然对你可用。
// This gives you an instance of ProtoneMedia\LaravelFFMpeg\MediaOpener $media = FFMpeg::fromDisk('videos')->open('video.mp4'); // The 'getStreams' method will be called on the underlying Media object since // it doesn't exists on this object. $codec = $media->getStreams()->first()->get('codec_name');
如果你想要直接访问底层对象,可以将对象作为函数(调用)来调用
// This gives you an instance of ProtoneMedia\LaravelFFMpeg\MediaOpener $media = FFMpeg::fromDisk('videos')->open('video.mp4'); // This gives you an instance of FFMpeg\Media\MediaTypeInterface $baseMedia = $media();
实验性
进度监听器暴露了转码百分比,但底层包还有一个内部的AbstractProgressListener
,它暴露了当前遍历和当前时间。尽管用例有限,你可能会想访问这个监听器实例。你可以通过装饰格式使用ProgressListenerDecorator
来实现这一点。这个特性是非常实验性的,所以在生产中使用之前,请确保彻底测试。
use FFMpeg\Format\ProgressListener\AbstractProgressListener; use ProtoneMedia\LaravelFFMpeg\FFMpeg\ProgressListenerDecorator; $format = new \FFMpeg\Format\Video\X264; $decoratedFormat = ProgressListenerDecorator::decorate($format); FFMpeg::open('video.mp4') ->export() ->inFormat($decoratedFormat) ->onProgress(function () use ($decoratedFormat) { $listeners = $decoratedFormat->getListeners(); // array of listeners $listener = $listeners[0]; // instance of AbstractProgressListener $listener->getCurrentPass(); $listener->getTotalPass(); $listener->getCurrentTime(); }) ->save('new_video.mp4');
由于我们无法消除一些底层选项,你可以通过向导出器添加回调来与最终的FFmpeg命令交互。你可以通过使用beforeSaving
方法添加一个或多个回调
FFMpeg::open('video.mp4') ->export() ->inFormat(new X264) ->beforeSaving(function ($commands) { $commands[] = '-hello'; return $commands; }) ->save('concat.mp4');
注意:这不能与连接和帧导出一起使用
示例应用
这里有一篇博客文章,将帮助你开始使用这个包
https://protone.media/en/blog/how-to-use-ffmpeg-in-your-laravel-projects
使用Video.js在任意浏览器中播放HLS
这里有一个20分钟的概述,介绍如何开始使用Video.js。它涵盖了从CDN引入Video.js、使用Laravel Mix(Webpack)将其作为ES6模块导入以及构建可重用的Vue.js组件。
https://www.youtube.com/watch?v=nA1Jy8BPjys
Wiki
变更日志
请参阅CHANGELOG以获取有关最近更改的更多信息。
测试
$ composer test
贡献
请参阅CONTRIBUTING以获取详细信息。
其他Laravel包
Inertia Table
:Inertia.js的终极表格,内置查询构建器。Laravel Blade On Demand
:Laravel 用于在内存中编译 Blade 模板的包。Laravel Cross Eloquent Search
:Laravel 包,用于在多个 Eloquent 模型中进行搜索。Laravel Eloquent Scope as Select
:停止在 PHP 中重复 Eloquent 查询作用域和约束。此包允许您通过将它们作为子查询添加来重用查询作用域和约束。Laravel MinIO Testing Tools
:在 MinIO S3 服务器上运行您的测试。Laravel Mixins
:Laravel 精美集合。Laravel Paddle
:支持 webhook/events 的 Paddle.com API 集成 Laravel。Laravel Task Runner
:编写类似 Blade 组件的 Shell 脚本,并在本地或远程服务器上运行它们。Laravel Verify New Email
:此包添加了对验证新电子邮件地址的支持:当用户更新其电子邮件地址时,在新的电子邮件地址被验证之前,不会替换旧的电子邮件地址。Laravel XSS Protection
:Laravel 中间件,用于保护您的应用程序免受跨站脚本 (XSS) 攻击。它清理请求输入,并且可以清理 Blade 输出语句。
安全
如果您发现任何与安全相关的问题,请通过电子邮件发送至 code@protone.media,而不是使用问题跟踪器。请勿发送任何问题,如有疑问请提交问题。
鸣谢
许可证
MIT 许可证(MIT)。请参阅 许可证文件 以获取更多信息。