buggedcom/phpvideotoolkit

PHPVideoToolkit 是一系列类,旨在提供模块化、面向对象和易于访问的接口,通过 FFmpeg 程序与视频和音频进行交互。

2.2.5 2017-09-07 08:36 UTC

README

...是一个PHP类集,旨在提供模块化、面向对象和易于访问的接口,通过FFmpeg与视频和音频进行交互。

PHPVideoToolkit 还提供纯PHP的FFmpeg-PHP模拟,因此您无需编译和安装FFmpeg-PHP模块,只需FFmpeg和PHPVideoToolkit即可。由于FFmpeg-PHP自2007年以来没有更新,因此使用FFmpeg-PHP与新版本的FFmpeg一起使用可能会破坏FFmpeg-PHP。使用PHPVideoToolkit对FFmpeg-PHP功能的模拟,您可以升级FFmpeg而不用担心破坏通过FFmpeg-PHP API提供的功能。

重要 PHPVideoToolkit 只与 FFmpeg v1.1.2 版本进行了测试。尽管大多数功能应该在不考虑您的 FFmpeg 版本的情况下正常工作,但我不能保证。如果您发现错误或有补丁,请打开工单或在 https://github.com/buggedcom/phpvideotoolkit-v2 上提交拉取请求。

目录

许可证

PHPVideoToolkit 版权 (c) 2008-2014 Oliver Lillie

双许可:MIT 和 GPL v2

有关更多详细信息,请参阅 LICENSE.md。

文档

下载中包含详细的文档和示例,并可在文档目录中找到。

最新更改

[2.2.0-beta] [10.04.2014]

警告:全面性的代码破坏性更改。请不要将现有稳定脚本升级到这个代码库。请使用2.1.5或以下版本以保持稳定性。

  • 合并到多输出分支,因此主分支现在支持从FFmpeg的多输出。
  • 修复了太多其他错误,无法一一列举。

完整变更日志

使用方法

虽然详细的文档涵盖了几乎所有内容(说实话,文档中只有几页,因为我太忙而没有写太多 - 但下面的示例相当不错),以下是一些您可以做的事情的示例。

配置PHPVideoToolkit

PHPVideoToolkit需要一些基本配置,并通过Config类来实现。Config类随后被用在大多数PHPVideoToolkit类构造函数中。在已经配置好的类中初始化的任何子对象都将继承父对象的配置选项。

namespace PHPVideoToolkit;

$config = new Config(array(
	'temp_directory' => './tmp',
	'ffmpeg' => '/opt/local/bin/ffmpeg',
	'ffprobe' => '/opt/local/bin/ffprobe',
	'yamdi' => '/opt/local/bin/yamdi',
	'qtfaststart' => '/opt/local/bin/qt-faststart',
	'cache_driver' => 'InTempDirectory',
), true);

请注意第二个参数true。如果设置为true,则相关的Config对象将被设置为默认配置实例。这意味着一旦设置为默认实例,就不需要向其他PHPVideoToolkit类构造函数提供Config对象。如果PHPVideoToolkit类没有定义并提供了配置对象,则会创建一个默认的Config对象并将其分配给该类。

以下每个示例都假设在执行之前已经将Config对象设置为默认配置对象,因此不需要在每个示例中提供配置。

访问FFmpeg数据

简单演示如何访问FfmpegParser对象的信息。

namespace PHPVideoToolkit;

$ffmpeg = new FfmpegParser();
$is_available = $ffmpeg->isAvailable(); // returns boolean
$ffmpeg_version = $ffmpeg->getVersion(); // outputs something like - array('version'=>1.0, 'build'=>null)
	

访问媒体文件数据

简单演示如何使用MediaParser对象访问媒体文件的信息。

namespace PHPVideoToolkit;

$parser = new MediaParser();
$data = $parser->getFileInformation('BigBuckBunny_320x180.mp4');
echo '<pre>'.print_r($data, true).'</pre>';
	

PHPVideoToolkit 时间码

PHPVideoToolkit在提取持续时间或起始点等数据,或提取媒体文件的部分时使用Timecode对象。它们相当容易理解。下面创建的所有示例时间码都是相同的。

namespace PHPVideoToolkit;

$timecode = new Timecode(102.34);
$timecode = new Timecode(102.34, Timecode::INPUT_FORMAT_SECONDS);
$timecode = new Timecode(1.705666667, Timecode::INPUT_FORMAT_MINUTES);
$timecode = new Timecode(.028427778, Timecode::INPUT_FORMAT_HOURS);
$timecode = new Timecode('00:01:42.34', Timecode::INPUT_FORMAT_TIMECODE);

您可以相当简单地操作时间码。

namespace PHPVideoToolkit;

$timecode = new Timecode('00:01:42.34', Timecode::INPUT_FORMAT_TIMECODE);
$timecode->hours += 15; // 15:01:42.34
$timecode->seconds -= 54125.5; // 00:00:18.84
$timecode->milliseconds -= 18840; // 00:00:00.00

// ...

$timecode->setSeconds(193.7);
echo $timecode; // Outputs '00:03:13.70'

// ...

$timecode->setTimecode('12:45:39.01');
echo $timecode->total_seconds; // Outputs 45939.01
echo $timecode->seconds; // Outputs 39

非常重要的一点,就像上一个示例一样,访问$timecode->seconds$timecode->total_seconds之间存在巨大的差异。seconds是时间码剩余分钟中的秒数。total_seconds是时间码的总秒数。相同的逻辑也适用于分钟、小时、毫秒及其总前缀的对应值。

PHPVideoToolkit 输出格式

PHPVideoToolkit包含一个基类Format。这个类被三个其他重要的基类扩展,分别是AudioFormatVideoFormatImageFormat。它们按照以下顺序扩展:Format > AudioFormat > VideoFormat > ImageFormat。这允许后面的格式从前面的格式继承功能。

FFmpeg允许您将某些导入格式参数设置到输入媒体中。因此,这些Format类适用于媒体的输入和输出格式。在大多数情况下,除非您需要执行非常具体的事情,否则您不需要担心为要放入FFmpeg的媒体设置输入格式。因此,本说明书将不会解释输入格式。

一般来说,如果您只是从一种格式转换到另一种格式,甚至不需要担心提供输出格式。PHPVideoToolkit将最佳猜测所需的输出格式,然后在您调用'save'以编码媒体时神奇地应用它。为此,您可以使用特定的音频、图像和视频格式。以下列出。如果您对特定文件输出有特定设置,请随时创建自己的媒体特定输出格式并提交拉取请求,我将将其包含在包中。以下列出这些媒体特定格式。

音频

  • AudioFormat_Acc
  • AudioFormat_Flac
  • AudioFormat_Mp3
  • AudioFormat_Oga
  • AudioFormat_Wav

图像

  • ImageFormat_Bmp
  • ImageFormat_Gif
  • ImageFormat_Jpeg
  • ImageFormat_Png
  • ImageFormat_Ppm

视频

  • VideoFormat_3gp
  • VideoFormat_Flv
  • VideoFormat_H264
  • VideoFormat_Mkv
  • VideoFormat_Mp4
  • VideoFormat_Ogg
  • VideoFormat_Webm
  • VideoFormat_Wmv
  • VideoFormat_Wmv3

总的来说,所有这些特定格式的Format类所做的只是设置生成所需输出所需必要的编解码器和设置。然而,像VideoFormat_Mp4VideoFormat_H264VideoFormat_Flv这样的格式包含进一步的功能,并使用编码完成回调来在FFmpeg完成编码后进一步处理媒体。例如,flv格式将FFmpeg的输出结果通过yamdi服务器库注入元数据,或者mp4格式使用qtfaststart创建快速启动流媒体mp4。

所以,让我们来看看您最可能使用的地方... 输出媒体的格式化。

AudioFormat

AudioFormat 及其子类提供了以下功能。

  • disableAudio/enableAudio
  • setAudioCodec
  • setAudioBitrate
  • setAudioSampleFrequency
  • setAudioChannels
  • setVolume
  • setAudioQuality

VideoFormat

VideoFormat 及其子类提供了以下功能。

  • disableVideo/enableVideo
  • setVideoCodec
  • setVideoDimensions
  • setVideoScale
  • setVideoPadding
  • setVideoAspectRatio
  • setVideoFrameRate
  • setVideoMaxFrames
  • setVideoBitrate
  • setVideoPixelFormat
  • setVideoQuality
  • setVideoRotation
  • videoFlipVertical
  • videoFlipHorizontal

ImageFormat

ImageFormat 及相关子类没有其他功能。

基本用法

以下是一个非常简单的视频操作示例。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');

$output_format = new VideoFormat_Mp4();
// attempt to auto rotate the video to the correct orientation (ie mobile phone users - hurgygur)
$output_format->setVideoRotation(true)
			  ->setVideoFrameRate(10)
			  ->setVideoPixelFormat('rgb24')
			  ->setAudioSampleFrequency(44100);

$video->save('output.mp4', $output_format);
				

在使用愚蠢的文件扩展名的同时强制指定特定输出格式

由于输入和输出格式器的先进性,如果提供,您可以编码特定的输出,但使用愚蠢(或自定义)的文件扩展名。不确定为什么您想这么做,但这是可能的。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$video->save('output.my_silly_custom_file_extension', new ImageFormat_Jpeg());
				

提取视频的单帧

以下代码从视频的第40秒提取一个帧。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->extractFrame(new Timecode(40))
				->save('./output/big_buck_bunny_frame.jpg');
$output = $process->getOutput();

从视频的片段中提取多帧

以下代码从第40秒到第50秒之间以父视频的帧率提取帧。如果父视频的帧率为24 fps,则此代码将提取240张图像。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->extractFrames(new Timecode(40), new Timecode(50))
				->save('./output/big_buck_bunny_frame_%timecode.jpg');
$output = $process->getOutput();

每秒提取一帧的视频多帧

有两种方法可以将与父视频不同的帧率导出。第一种是使用输出格式设置视频帧率。

namespace PHPVideoToolkit;

$output_format = new ImageFormat_Jpeg();

/*
OR 

$output_format = new VideoFormat();
$output_format->setFrameRate(1);
// optionally also set the video and output format, however if you use the ImageFormat_Jpeg 
// output format object this is automatically done for you. If you do not add below, FFmpeg
// automatically guesses from your output file extension which format and codecs you wish to use.
$output_format->setVideoCodec('mjpeg')
			  ->setFormat('image2');

*/

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->extractFrames(null, new Timecode(50)) // if null then the extracted segment starts from the begining of the video
				->save('./output/big_buck_bunny_frame_%timecode.jpg', $output_format);
$output = $process->getOutput();

第二种是使用 extractFrames 函数的 $force_frame_rate 选项。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->extractFrames(new Timecode(50), null, 1) // if null then the extracted segment goes from the start timecode to the end of the video
				->save('./output/big_buck_bunny_frame_%timecode.jpg');
$output = $process->getOutput();

每 'x' 秒提取一帧的视频多帧

以下代码使用 $force_frame_rate 参数为 $video->extractFrames(),但是相同的 1/n 标记也可以用于 $video_format->setFrameRate()。此示例将每60秒视频输出1帧。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->extractFrames(new Timecode(40), new Timecode(50), '1/60')
				->save('./output/big_buck_bunny_frame_%timecode.jpg');
$output = $process->getOutput();

提取多帧的注意事项

重要:请注意,如果您导出多帧视频,您可能不会始终获得预期数量的帧。这归因于 FFmpeg 处理时间码的方式。请考虑以下示例。

namespace PHPVideoToolkit;

$video = new \PHPVideoToolkit\Video($example_video_path);

$process = $video->extractSegment(new \PHPVideoToolkit\Timecode(10), new \PHPVideoToolkit\Timecode(20))
				->extractFrames(null, null, 1)
				->save('./output/%timecode.jpg', null, \PHPVideoToolkit\Media::OVERWRITE_EXISTING);

$output = $process->getOutput();

您可能会认为通过查看此示例,您将输出10帧,因为提取的片段长度为10秒。但实际上您只会导出9帧。这是因为结束时间帧被视为小于值,而不是小于等于值。所以当提取帧时,伪代码是这样的。

current = 10;
end = 20;
while(current < end)
{
	extractFrame(current);
	current += 1;
}

因此,如果我们需要10帧,我们必须实际上将结束时间码设置得略大于20秒,如下所示 $video->extractSegment(new \PHPVideoToolkit\Timecode(10), new \PHPVideoToolkit\Timecode(20.1))

将多张图片和音频合并成视频

虽然 PHPVideoToolkit 本身不支持将多个图像和音频组合成视频,但可以通过向进程对象添加自定义命令来实现。

$audio = new Audio('Ballad_of_the_Sneak.mp3');

$process = $audio->getProcess();
$process->addPreInputCommand('-framerate', '1/5');
$process->addPreInputCommand('-pattern_type', 'glob');
$process->addPreInputCommand('-i', 'images/*.jpg');
$process->addCommand('-pix_fmt', 'yuv420p');
$process->addCommand('-shortest', '');

$output_format = new VideoFormat();
$output_format->setVideoFrameRate('1/5');
$output_format->setVideoDimensions(320, 240);

$process = $audio->save('./output/my_homemade_video.mp4', $output_format, Media::OVERWRITE_EXISTING);

提取动画Gif

现在,FFmpeg 对动画 gif 的支持是一堆狗屎。我不明白为什么。然而,PHPVideoToolkit 做的是绕过 FFmpeg 的原生 gif 导出,并提供更好的替代方案。

导出动画 gif 时有几种选项可供选择。您可以使用 Gifsicle、ImageMagick convert 或本地的 PHP GD 和 symbio/gif-creator composer 库。

对于高质量但非常慢的编码,建议使用 Gifsicle 和 Convert 预处理的组合,或者对于快速编码但质量较低的选项,可以使用本地的 PHP GD 或 Convert。以下示例展示了如何区分不同的方法。

关于性能。高帧率会大大影响高质量编码的完成速度。建议如果您需要高质量动画 gif,请将帧率限制在每秒5帧左右。

高质量

Gifsicle with Imagemagick Convert

namespace PHPVideoToolkit;

$config->convert = '/opt/local/bin/convert';
$config->gif_transcoder = 'gifsicle';

$output_path = './output/big_buck_bunny.gif';

$output_format = Format::getFormatFor($output_path, $config, 'ImageFormat');
$output_format->setVideoFrameRate(5);
		
$video = new Video('media/BigBuckBunny_320x180.mp4', $config);
$process = $video->extractSegment(new Timecode(10), new Timecode(20))
				->save($output_path, $output_format);
				
$output = $process->getOutput();

快速编码,但质量较低(仍然比 FFmpeg 好得多)

以下示例按性能顺序列出。

ImageMagick 转换

namespace PHPVideoToolkit;

$config->gif_transcoder = 'convert';

$output_path = './output/big_buck_bunny.gif';

$output_format = Format::getFormatFor($output_path, $config, 'ImageFormat');
$output_format->setVideoFrameRate(5);
		
$video = new Video('media/BigBuckBunny_320x180.mp4', $config);
$process = $video->extractSegment(new Timecode(10), new Timecode(20))
				->save($output_path, $output_format);
				
$output = $process->getOutput();

原生 PHP GD 结合 symbio/gif-creator 库

namespace PHPVideoToolkit;

$config->gif_transcoder = 'php';

$output_path = './output/big_buck_bunny.gif';

$output_format = Format::getFormatFor($output_path, $config, 'ImageFormat');
$output_format->setVideoFrameRate(5);
		
$video = new Video('media/BigBuckBunny_320x180.mp4', $config);
$process = $video->extractSegment(new Timecode(10), new Timecode(20))
				->save($output_path, $output_format);
				
$output = $process->getOutput();

Gifsicle 结合原生 PHP GD

namespace PHPVideoToolkit;

$config->convert = null; // This disables the imagemagick convert path so gifsicle transcoder falls back to GD
$config->gif_transcoder = 'gifsicle';

$output_path = './output/big_buck_bunny.gif';

$output_format = Format::getFormatFor($output_path, $config, 'ImageFormat');
$output_format->setVideoFrameRate(5);
		
$video = new Video('media/BigBuckBunny_320x180.mp4', $config);
$process = $video->extractSegment(new Timecode(10), new Timecode(20))
				->save($output_path, $output_format);
				
$output = $process->getOutput();

调整视频和图片大小

为了调整输出视频和图像的大小,您需要向调用的保存函数提供输出格式。以下示例代码展示了如何调整视频大小,但您可以使用相同的函数调用setVideoDimensions,只是将VideoFormat对象替换为ImageFormat对象。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');

$output_format = new VideoFormat();
$output_format->setVideoDimensions(160, 120);

$video->save('BigBuckBunny_160x120.3gp', $output_format);

从视频中提取音频或视频通道

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->extractAudio()->save('./output/big_buck_bunny.mp3');
// $process = $video->extractVideo()->save('./output/big_buck_bunny.mp4');
				
$output = $process->getOutput();

提取音频或视频文件片段

以下代码从视频的第2分钟22秒提取到第3分钟(即180秒)。注意构建时间码的不同设置。时间码对象可以接受不同的格式来创建时间码。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->extractSegment(new Timecode('00:02:22.0', Timecode::INPUT_FORMAT_TIMECODE), new Timecode(180))
				->save('./output/big_buck_bunny.mp4');
$output = $process->getOutput();

将音频或视频文件分割成多个部分

您可以通过多种方式配置拆分参数。如果提供第一个参数为数组,它必须是一个数组,包含您希望拆分媒体的时间码实例或所有整数。如果提供整数,则这些整数被视为您希望拆分的帧号。但是,您也可以通过提供单个整数作为第一个参数来按等间隔拆分。该整数被视为您希望拆分的秒数。如果您有一个时长为3分30秒的视频,并将拆分设置为60秒,您将得到4个视频。前三个视频长度为60秒,最后一个视频长度为30秒。

以下代码将视频拆分为长度均为45秒的多个片段。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->split(45)
				->save('./output/big_buck_bunny_%timecode.mp4');
$output = $process->getOutput();

清除并添加元数据

不幸的是,使用 FFmpeg 没有添加元数据的方法而不重新编码文件。不过,其他工具可以做到这一点。如果您希望在编码过程中将元数据写入媒体,可以使用如下示例代码。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->purgeMetaData()
				->setMetaData('title', 'Hello World')
				->save('./output/big_buck_bunny.mp4');
$output = $process->getOutput();

更改音频或视频流的编解码器

默认情况下,PHPVideoToolkit 会使用输出文件的文件扩展名来自动生成所需文件格式的 ffmpeg 设置(如果有的话)。但是,如果您想指定不同的编解码器或设置,必须在输出格式容器内指定它们。您可以根据输出格式使用三种不同的格式对象:AudioFormat、VideoFormat 和 ImageFormat。

注意;以下示例仅供参考,可能无法正常工作

更改输出视频的音频和视频编解码器

namespace PHPVideoToolkit;

$output_path = './output/big_buck_bunny.mpeg';

$output_format = new VideoFormat();
$output_format->setAudioCodec('acc')
			  ->setVideoCodec('ogg');

$video = new Video('media/BigBuckBunny_320x180.mp4');
$process = $video->save($output_path, $output_format);
$output = $process->getOutput();

更改音频导出的音频编解码器

namespace PHPVideoToolkit;

$output_path = './output/big_buck_bunny.mp3';

$output_format = new AudioFormat();
$output_format->setAudioCodec('acc');

$video = new Video('media/BigBuckBunny_320x180.mp4');
$process = $video->save($output_path, $output_format);

$output = $process->getOutput();

非阻塞保存

默认的/main save() 函数会阻塞 PHP,直到编码过程完成。这意味着根据您正在编码的媒体大小,脚本可能需要长时间运行。为了解决这个问题,您可以调用 saveNonBlocking() 来开始编码过程而不阻塞 PHP。

然而,在这样做之前,您需要注意一些问题。一旦非阻塞过程开始,如果您的 PHP 脚本关闭,PHPVideoToolkit 就无法再“清理”临时文件或执行对 %index 或 %timecode 输出文件的动态重命名。所有责任都转移给您。当然,如果您在编码完成之前保持 PHP 脚本打开,PHPVideoToolkit 会为您完成所有工作。

以下代码示例展示了如何管理非阻塞保存。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->saveNonBlocking('./output/big_buck_bunny.mov');

// do something else important, db queries etc

while($process->isCompleted() === false)
{
	// do something more stuff in a loop.
	// doesn't have to be a loop, just an example.
	
	sleep(0.5);
}

if($process->hasError() === true)
{
	// an error was encountered, do something with it.
}
else
{
	// encoding has completed and no error was detected so 
	// we can get the output from the process.
	$output = $process->getOutput();
}

使用进度处理程序进行编码

上面的代码看起来像是进度处理器(在某种程度上,它确实是,但它不提供编码进度数据),进度处理器提供了有关当前编码过程更详细的信息。

PHPVideoToolkit 允许您监控 FFmpeg 的编码过程。这是通过使用 ProgressHandler 对象来实现的。存在三种类型的进度处理器。

  • ProgressHandlerNative
  • ProgressHandlerOutput
  • ProgressHandlerPortable

ProgressHandlerNative 和 ProgressHandlerOutput 的工作方式和功能相同,但是前者使用原生 ffmpeg 命令,后者将 ffmpeg 输出缓冲区输出到临时文件。如果你的 FFmpeg 版本较新,你可以使用 ProgressHandlerNative,它使用 FFmpeg 的 '-progress' 命令来提供数据。这两个处理器返回的数据量略有不同,其中更准确、更详细的是 ProgressHandlerOutput,建议在两者之间使用它。然而,它们都返回相同的基本数据,并以相同的方式工作,除非你的 FFmpeg 版本不支持 '-progress',否则没有必要优先考虑其中一个。如果不支持,则在初始化 ProgressHandlerNative 时将抛出异常。在两者之间,我个人推荐使用 ProgressHandlerOutput,因为可用的数据更加详细。

第三种处理器 ProgressHandlerPortable(如下例 3 所示)的工作方式略有不同,专门设计用于与单独的 HTTP 请求或线程一起使用。ProgressHandlerPortable 可以在完全不同的脚本中启动,提供 PHPVideoToolkit 可移植进程 ID,然后独立于编码脚本进行探测。这允许开发人员解耦编码和编码状态脚本。

进度处理器可以被设置为阻塞 PHP,也可以以非阻塞的方式使用。它们甚至可以利用单独的脚本在编码初始化后工作。然而,对于前两个示例,进度处理器基本上是在同一个脚本中阻塞 PHP 进程。然而,前两个示例所示的功能非常不同。

示例 1. 处理器构造函数中的回调

此示例将进度回调处理器作为参数传递给构造函数。该函数随后被调用(默认情况下,每秒调用一次)。以这种方式创建回调将阻塞 PHP,并且不能在调用 saveNonBlocking() 时作为进度处理器分配。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');

$progress_handler = new ProgressHandlerNative(function($data)
{
	echo '<pre>'.print_r($data, true).'</pre>';
});

$process = $video->purgeMetaData()
				->setMetaData('title', 'Hello World')
				->save('./output/big_buck_bunny.mp4', null, Video::OVERWRITE_EXISTING, $progress_handler);
$output = $process->getOutput();

示例 2. 探测处理器

此示例初始化了一个处理器,但没有提供回调函数。相反,你创建自己的方法来创建“进度循环”(或类似的东西),然后只需在处理器上调用 probe()。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');

$progress_handler = new ProgressHandlerNative(null);

$process = $video->purgeMetaData()
				->setMetaData('title', 'Hello World')
				->saveNonBlocking('./output/big_buck_bunny.mp4', null, Video::OVERWRITE_EXISTING, $progress_handler);
				
while($progress_handler->completed !== true)
{
	// note setting true in probe() automatically tells the probe to wait after the data is returned.
	echo '<pre>'.print_r($progress_handler->probe(true), true).'</pre>';
}
				
$output = $process->getOutput();

因此,尽管这两个示例看起来非常相似,并且都阻塞了 PHP,但第二个示例根本不需要阻塞。

示例 3. 使用远程进度处理进行非阻塞保存

此示例(更好的示例可以在 /examples/progress-handler-portability.php 中找到)显示可以在一个请求中进行非阻塞保存,然后后续请求(即 ajax)可以发送到不同的脚本以探测编码进度。

重要提示:请记住,在任何以下脚本中使用的 Config 对象必须在两个脚本中都是相同的,以确保进度数据可以在进度脚本中检索。

编码脚本

namespace PHPVideoToolkit;

session_start();

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->saveNonBlocking('./output/big_buck_bunny.mp4', null, Video::OVERWRITE_EXISTING);
				
$_SESSION['phpvideotoolkit_portable_process_id'] = $video->getPortableId();

探测脚本

namespace PHPVideoToolkit;

session_start();

$handler = new ProgressHandlerPortable($_SESSION['phpvideotoolkit_portable_process_id']);

$probe = $handler->probe();

echo json_encode(array(
	'finished' => $probe['finished'], // true when the process has ended by interruption, error or success
	'completed' => $probe['completed'], // true when the process has ended with a successful encoding that encountered no errors.
	'percentage' => $probe['percentage']
));
exit;

进度处理器注意事项

1:当编码 MP4 并启用 qt-faststart 使用时,无论是通过设置 \PHPVideoToolkit\Config->force_enable_qtfaststart = true; 还是 \PHPVideoToolkit\VideoFormat_Mp4::enableQtFastStart(),保存将被置于阻塞模式,因为使用 qt-faststart 需要进一步的 exec 调用。同样,任何编码后处理(如编码 FLV 时)也将非阻塞保存转换为阻塞保存。

2:当使用 %timecode 或 %index 输出文件并使用 ProgressHandlerPortable 系统时,目前无法自动将生成的临时文件输出重命名为正确的输出文件名。

进度处理程序可用的信息

上述所有进度处理器返回相同的信息,尽管有些数据可能略有不同,但差异可以忽略不计,并且对于本文档的目的,它们实际上并不重要。以下是来自 $progress_handler->probe(); 的数据样本输出。

Array
(
	[error] => 
	[error_message] => 
	[started] => 1
	[finished] => 
	[completed] => 
	[interrupted] => 
	[status] => encoding
	[run_time] => 0.730171918869
	[percentage] => 11.0666666667
	[fps_avg] => 209.539693388
	[size] => 242kB
	[frame] => 153
	[duration] => PHPVideoToolkit\Timecode Object
		(
			[_total_frames:protected] => 
			[_total_milliseconds:protected] => 6640
			[_total_seconds:protected] => 6.64
			[_total_minutes:protected] => 0.110666666667
			[_total_hours:protected] => 0.00184444444444
			[_frames:protected] => 
			[_milliseconds:protected] => 0.64
			[_seconds:protected] => 6
			[_minutes:protected] => 0
			[_hours:protected] => 0
			[_frame_rate:protected] => 
		)

	[expected_duration] => PHPVideoToolkit\Timecode Object
		(
			[_total_frames:protected] => 
			[_total_milliseconds:protected] => 60000
			[_total_seconds:protected] => 60
			[_total_minutes:protected] => 1
			[_total_hours:protected] => 0.0166666666667
			[_frames:protected] => 
			[_milliseconds:protected] => 0
			[_seconds:protected] => 60
			[_minutes:protected] => 0
			[_hours:protected] => 0
			[_frame_rate:protected] => 
		)

	[fps] => 0.0
	[dup] => 
	[drop] => 
	[output_count] => 1
	[output_file] => /@Projects/PHPVideoToolkit/v2/git/examples/output/big_buck_bunny.3gp
	[input_count] => 1
	[input_file] => /@Projects/PHPVideoToolkit/v2/git/examples/media/BigBuckBunny_320x180.mp4
	[process_file] => /@Projects/PHPVideoToolkit/v2/git/examples/tmp/phpvideotoolkit_GsZ7FC
)

输出中存在4个不同的“状态”布尔值和一个特定的状态码,您应该了解它们。它们是启动中断完成结束状态

启动 (布尔值)

一旦PHPVideoToolkit找到进程文件并将解码/编码过程发送给FFmpeg,此值将被设置为true。

中断 (布尔值)

如果由于某种原因服务器提前停止了编码过程,此值将被设置为true。如果遇到这种情况,则表示您的编码过程失败,无法重新启动,除非再次尝试编码。

完成 (布尔值)

一旦FFmpeg发出编码过程已完成的信号,此值将被设置为true。

结束 (布尔值)

一旦PHPVideoToolkit收到命令行上的完成信号,此值将被设置为true。这通常是在“完成”设置为true之后设置的。这是因为编码完成后,FFmpeg会在过程完成后输出更多信息,然后PHPVideoToolkit会对临时文件或特定文件格式进行一些整理。

状态 (常量/字符串)

这个值是根据以下常量定义的,即:ProgressHandlerDefaultData::ENCODING_STATUS_PENDINGProgressHandlerDefaultData::ENCODING_STATUS_DECODINGProgressHandlerDefaultData::ENCODING_STATUS_ENCODINGProgressHandlerDefaultData::ENCODING_STATUS_FINALISINGProgressHandlerDefaultData::ENCODING_STATUS_COMPLETEDProgressHandlerDefaultData::ENCODING_STATUS_FINISHEDProgressHandlerDefaultData::ENCODING_STATUS_INTERRUPTEDProgressHandlerDefaultData::ENCODING_STATUS_ERROR。您会注意到状态比布尔值更多。这是因为“状态”键稍微详细一些。因此,以下是对常量值的解释。

ProgressHandlerDefaultData::ENCODING_STATUS_PENDING

这意味着过程尚未开始解码输入媒体。接下来是...

ProgressHandlerDefaultData::ENCODING_STATUS_DECODING

这意味着FFmpeg目前正在解码输入媒体。接下来是...

ProgressHandlerDefaultData::ENCODING_STATUS_ENCODING

这意味着FFmpeg目前正在编码输出文件。接下来是...

ProgressHandlerDefaultData::ENCODING_STATUS_FINALISING

这意味着FFmpeg已到达编码过程的末尾,目前正在整理,我们处于编码的最后阶段。接下来是...

ProgressHandlerDefaultData::ENCODING_STATUS_COMPLETED

这意味着编码过程已完成,但PHPVideoToolkit还需要一点时间来整理。

ProgressHandlerDefaultData::ENCODING_STATUS_FINISHED

然后给出这个值意味着编码过程完全完成,您可以安全地移动/重命名/使用最终输出媒体。

如果在编码过程中出现问题,您将在任何时刻收到ProgressHandlerDefaultData::ENCODING_STATUS_INTERRUPTEDProgressHandlerDefaultData::ENCODING_STATUS_ERROR。一般来说,如果这两个常量之一给出,则探测数据中的errorerror_message键也将被填充。

通过探测数据返回的其他值如下所述。

run_time (浮点数)

这是过程所花费的总秒数。

percentage (浮点数)

这是编码完成百分比,范围在0-100之间。100表示编码完成。

fps (浮点数)

这是正在处理的每秒帧数。

fps_avg (浮点数)

这是正在处理的平均每秒帧数。

frame (整数)

这是正在处理的当前帧。

size (字符串)

这是当前输出媒体的大小。

duration (PHPVideoToolkit\Timecode对象)

这是输出媒体的当前持续时间(如果适用 - 如果您只输出图像,则此值为 null

expected_duration (PHPVideoToolkit\Timecode 对象)

这是输出媒体持续时间预期的近似值。上面的 'duration'(持续时间)的最终值通常会与这个值有微小的差异。

dup (float)

此值是处理重复帧的数量。

drop (float)

这是丢弃帧的数量。

output_count (integer)

此值是预期的输出文件数量。

output_file (string/array)

此值可以是以下两种情况之一:a) 如果上面的 'output_count' 为 1,则是一个字符串形式的路径值;b) 如果上面的 'output_count' 大于 1,则是一个路径字符串数组。

input_count (integer)

此值是正在使用的输入文件数量。

input_file (string/array)

此值可以是以下两种情况之一:a) 如果上面的 'input_count' 为 1,则是一个字符串形式的路径值;b) 如果上面的 'input_count' 大于 1,则是一个路径字符串数组。

process_file (string)

这是一个指向进度处理程序正在从中读取数据的当前进程文件的路径字符串。

编码多个输出文件

FFmpeg 允许您从单个命令中编码多个输出格式。[创建多个输出] PHPVideoToolkit 也允许您执行此功能。此功能与执行多个保存基本相同,但具有更低的开销优势,因为输入文件只需要在编码之前读取到内存中一次。如果您需要输出多个媒体版本,建议您使用此方法。当然,使用此方法也有一些注意事项。

当将文件分割成多个段或提取视频的一部分时,所有输出的媒体都会执行转换。此功能的示例可以在 convert-to-multiple-output.php 中找到。以下也是一个快速示例。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');

$multi_output = new MultiOutput();

$ogg_output = './output/big_buck_bunny.multi1.ogg';
$format = Format::getFormatFor($ogg_output, $config, 'VideoFormat');
$format->setVideoDimensions(VideoFormat::DIMENSION_SQCIF);
$multi_output->addOutput($ogg_output, $format);

$threegp_output = './output/big_buck_bunny.multi2.3gp';
$format = Format::getFormatFor($threegp_output, $config, 'VideoFormat');
$format->setVideoDimensions(VideoFormat::DIMENSION_XGA);
$multi_output->addOutput($threegp_output, $format);

$output = $video->save($multi_output, null, Media::OVERWRITE_EXISTING);

所有进度处理程序也支持多个输出,但仍然适用之前为 ProgressHandlerPortable 列出的注意事项。

重要 虽然在技术上可能,但根据您的服务器和您生成的输出数量,简单地链接请求可能更快。有关方法链的更多信息,请参阅 链式处理示例

访问执行命令和命令行缓冲区

可能会出现一些问题,PHPVideoToolkit 没有正确阻止或报告任何编码/解码错误,或者您可能只想记录正在发生的事情。您可以像下面的示例那样轻松访问任何执行的命令和命令行输出。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->save('./output/big_buck_bunny.mov');

echo 'Expected Executed Command<br />';
echo '<pre>'.$process->getExecutedCommand().'</pre>';

echo 'Expected Command Line Buffer<br />';
echo '<pre>'.$process->getBuffer().'</pre>';

请注意,ExecBuffer 对象实际上操作了 FfmpegProcess 对象给出的原始命令字符串。这样做是为了使 ExecBuffer 能够成功跟踪错误和处理完成。getExecutedCommand() 和 getBuffer() 返回的数据是预期的但不是实际的值。

要获取实际的执行命令和缓冲区,您可以使用以下方法。

echo 'Actual Executed Command<br />';
echo '<pre>'.$process->getExecutedCommand(true).'</pre>';

echo 'Actual Command Line Buffer<br />';
echo '<pre>'.$process->getRawBuffer().'</pre>';

提供自定义命令

由于 FFmpeg 有特定的顺序需要添加某些命令,因此您应该了解一些函数。下面的代码示例显示了如何访问 FfmpegProcess 对象。进程对象本身是 ProcessBuilder(帮助构建查询)和 ExecBuffer(执行和控制查询)对象的包装器。

进程对象是通过引用传递的,因此对对象的任何更改也会在 Video 对象中执行。

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');
$process = $video->getProcess();

现在您有了访问进程对象的方法,您可以向其中添加特定命令。

// ... continued from above

$process->addPreInputCommand('-custom-command');
$process->addCommand('-custom-command-with-arg', 'arg value');
$process->addPostOutputCommand('-output-command', 'another value');

// ... now save the output video

$video->save('./your/output/file.mp4');

现在上述所有示例命令都会导致FFmpeg失败,它们只是为了说明一个观点。

  • 函数addPreInputCommand()用于向命令字符串中添加在输入命令(-i)添加之前的命令。
  • 函数addCommand()用于向命令字符串中添加在输入命令(-i)添加之后的命令。
  • 函数addPostOutputCommand()用于在命令字符串中添加输出文件之后要添加的命令。

为了进一步解释,以下是一个使用上述自定义命令的简化命令字符串。

/opt/bin/local/ffmpeg -custom-command -i '/your/input/file.mp4' -custom-command-with-arg 'arg value' '/your/output/file.mp4' -output-command 'another value'

但是,有一个重要的注意事项需要您注意,上述命令只是一个示例,用于展示添加命令的位置。使用与上述相同的附加命令,实际执行的命令如下所示

((/opt/local/bin/ffmpeg '-custom-command' '-i' '/your/input/file.mp4' '-custom-command-with-arg' 'arg value' '-y' '-qscale' '4' '-f' 'mp4' '-strict' 'experimental' '-threads' '1' '-acodec' 'mp3' '-vcodec' 'h264' '/your/output/file.mp4' '-output-command' 'another value' && echo '<c-219970-52ea5f8c9ca9d-da39f7c51d495967dfec435dc91e2879>') || echo '<f-219970-52ea5f8c9ca9d-da39f7c51d495967dfec435dc91e2879>' '<c-219970-52ea5f8c9ca9d-da39f7c51d495967dfec435dc91e2879>' '<e-219970-52ea5f8c9ca9d-da39f7c51d495967dfec435dc91e2879>'$?) 2>&1 > '/tmp/phpvideotoolkit_lvsukB' 2>&1 &

强制处理时间限制

您可能希望对编码强制处理时间限制。这样做的原因有很多,应该是不言而喻的。FFmpeg提供了一个命令来实现这一点,可以像这样调用...

namespace PHPVideoToolkit;

$video  = new Video('BigBuckBunny_320x180.mp4');

$process = $video->getProcess();
$process->setProcessTimelimit(10); // in seconds

try
{
	$video->save('output.mp4');
}
catch(FfmpegProcessOutputException $e)
{
	echo $e->getMessage(); // Imposed time limit (10 seconds) exceeded.
}