unifysofttech-pbmedia/laravel-ffmpeg

v1.1.0 2023-08-17 04:32 UTC

This package is auto-updated.

Last update: 2024-09-17 06:37:58 UTC


README

Latest Version on Packagist Software License run-tests Total Downloads

此包为Laravel 9和10提供FFmpeg的集成。Laravel的文件系统处理文件的存储。Laravel的文件系统

赞助此包!

❤️ 我们自豪地通过开发Laravel包并将其免费提供给社区来支持社区。如果这个包为您节省了时间,或者您在专业上依赖它,请考虑赞助维护和开发。跟踪问题和拉取请求需要时间,但我们乐于提供帮助!

Laravel Splade

你听说过Laravel Splade吗?🤩

它是Inertia.js的魔力与Blade的简单性的结合。Splade提供了一种超简单的方式来构建使用Blade模板的单页应用程序。除了那种神奇的SPA感觉外,它还提供了十多个组件来让您的应用程序熠熠生辉并使其交互式,而无需离开Blade。

功能

  • 围绕PHP-FFMpeg的超级简单包装,包括对过滤器和其他高级功能的支持。
  • Laravel的文件系统配置系统错误处理集成。
  • 兼容Laravel 9和10,支持包发现
  • 内置对HLS的支持。
  • 内置对加密HLS(AES-128)和密钥轮换(可选)的支持。
  • 内置对连接、多个输入/输出、图像序列(时间流逝)、复杂过滤器(和映射)、帧/缩略图导出的支持。
  • 内置对水印(位置和操作)的支持。
  • 内置从视频创建马赛克/精灵/瓦片的支持。
  • 内置生成VTT预览缩略图文件的支持。
  • 需要PHP 8.1或8.2。
  • 使用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,这使得异常更具有信息性。更多信息请参阅处理异常部分。
  • 已用log_channel替换了enable_logging配置键以选择用于将消息写入日志时使用的日志通道。如果您仍然想要完全禁用日志记录,可以将新的配置键设置为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的mapfilter_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()方法,其中$filesystemIlluminate\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的Filter对象添加过滤器

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(默认)、insetwidthheight。可选的第四个参数是一个布尔值,表示是否强制使用标准比例。您可以在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配置文件中指定的默认磁盘。

打开您的水印文件后,您可以使用toprightbottomleft方法将其定位。这些方法的第一个参数是偏移量,它是可选的,可以是负数。

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);
    });

除了使用定位方法外,您还可以使用horizontalAlignmentverticalAlignment方法。

对于水平对齐,您可以使用WatermarkFactory::LEFTWatermarkFactory::CENTERWatermarkFactory::RIGHT常量。对于垂直对齐,您可以使用WatermarkFactory::TOPWatermarkFactory::CENTERWatermarkFactory::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支持Tile功能。为了使导出多个帧更快更简单,我们利用此功能添加了一些辅助方法。例如,您可以使用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还具有marginpaddingwidthheight方法。还有一个quality方法,用于指定导出到有损格式(如JPEG)时的质量。JPEG的范围是2-31,其中2是最佳质量,31是最差质量。

此包还可以生成WebVTT文件,为您的视频播放器添加预览缩略图。JW player原生支持此功能,同时也有社区驱动的Video.js插件。您可以通过在上调用generateVTT方法并指定文件名来实现

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');

使用循环进行多次导出

链式多次转换之所以可行,是因为save方法返回一个的新实例。您可以使用这个实例来循环遍历项目,例如,从单个视频中导出多个帧

$mediaOpener = FFMpeg::open('video.mp4');

foreach ([5, 15, 25] as $key => $seconds) {
    $mediaOpener = $mediaOpener->getFrameFromSeconds($seconds)
        ->export()
        ->save("thumb_{$key}.png");
}

还包含一个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的mapfilter_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的mapfilter_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个参数。第一个、第二个和第三个参数提供了关于导出基本名、当前迭代格式和当前迭代键的信息。第四个参数是一个回调,您应使用您的模式调用它。第五个参数是一个回调,您应使用您的播放列表模式调用它。请注意,这不是主播放列表的名称,而是每个格式的播放列表的名称。

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控制器,所以您可以使用权限中间件等功能。

在这个示例中,我们将HLS导出保存到了public磁盘,并将加密密钥存储到了secrets磁盘,该磁盘不可公开访问。由于浏览器无法访问加密密钥,因此它不会播放视频。每个播放列表都有指向加密密钥的路径,我们需要修改这些路径,使其指向可访问的端点。

此实现由两个路由组成。一个是响应加密密钥的路由,另一个是响应修改后的播放列表的路由。第一个路由(video.key)相对简单,您应该在这里添加您的额外逻辑。

第二条路由(video.playlist)使用DynamicHLSPlaylist类。在FFMpeg外观上调用dynamicHLSPlaylist方法,类似于打开媒体文件,你可以通过fromDiskopen方法打开播放列表。然后你必须提供三个回调。每个回调都提供一个相对路径,并期望返回一个完整路径。由于DynamicHLSPlaylist类实现了Illuminate\Contracts\Support\Responsable接口,你可以返回该实例。

第一个回调(KeyUrlResolver)提供加密键的相对路径。第二个回调(MediaUrlResolver)提供媒体片段(.ts文件)的相对路径。第三个回调(PlaylistUrlResolver)提供播放列表的相对路径。

现在,你不再需要使用Storage::disk('public')->url('adaptive_steve.m3u8')来获取主播放列表的完整URL,你可以使用route('video.playlist', ['playlist' => 'adaptive_steve.m3u8'])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类,该类有三个方法:allerrorsoutput。每个方法返回一个包含对应行的数组。

$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、作为ES6模块导入Laravel Mix(Webpack)以及构建可重用的Vue.js组件。

https://www.youtube.com/watch?v=nA1Jy8BPjys

Wiki

变更日志

请参阅CHANGELOG了解最近更改的更多信息。

测试

$ composer test

贡献

请参阅CONTRIBUTING了解详细信息。

其他Laravel包

安全性

如果您发现任何与安全相关的问题,请通过code@protone.media电子邮件,而不是使用问题跟踪器。请不要发送任何问题,如果您有疑问,请打开一个问题。

致谢

许可证

MIT许可证(MIT)。请参阅许可证文件以获取更多信息。