Files
Pichome/dzz/ffmpeg/class/class_fmpeg.php
2024-02-06 00:03:33 +08:00

392 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/*
* @copyright QiaoQiaoShiDai Internet Technology(Shanghai)Co.,Ltd
* @license https://www.oaooa.com/licenses/
*
* @link https://www.oaooa.com
* @author zyx(zyx@oaooa.com)
*/
require_once DZZ_ROOT . './dzz/ffmpeg/vendor/autoload.php';
require_once(DZZ_ROOT . './dzz/class/class_encode.php');
class fmpeg
{
private $fm;
private $status = 0;
private $logger = NULL;
public function __construct($options = array())
{
$appdata = C::t('app_market')->fetch_by_identifier('ffmpeg', 'dzz');
$app = unserialize($appdata['extra']);
$this->status = $app['status'];
$option = array(
'ffmpeg.binaries' => $app['ffmpeg.binaries'] ? $app['ffmpeg.binaries'] : (strstr(PHP_OS, 'WIN') ? DZZ_ROOT . 'dzz\ffmpeg\ffmpeg\ffmpeg.exe' : '/usr/bin/ffmpeg'),
'ffprobe.binaries' => $app['ffprobe.binaries'] ? $app['ffprobe.binaries'] : (strstr(PHP_OS, 'WIN') ? DZZ_ROOT . 'dzz\ffmpeg\ffmpeg\ffprobe.exe' : '/usr/bin/ffprobe'),
'timeout' => $app['timeout'] ? $app['timeout'] : 3600, // The timeout for the underlying process
'ffmpeg.threads' => $app['ffmpeg.threads'] ? $app['ffmpeg.threads'] : 1, // The number of threads that FFMpeg should use
);
$this->fm = FFMpeg\FFMpeg::create($option, $this->logger);
return $this->fm;
}
public function getInfo($data)
{
global $_G;
if($data['aid']){
$attachment = IO::getMeta('attach::'.$data['aid']);
}else{
$attachment = IO::getMeta($data['rid']);
}
$file = IO::getStream($attachment['path']);;
//本地路径
if ($attachment['bz'] != 'dzz::') {
$cachefile = $_G['setting']['attachdir'] . './cache/' . md5($data['path']) . '.' . $data['ext'];
$handle = fopen($cachefile, 'w+');
$fp = fopen($file, 'rb');
while (!feof($fp)) {
fwrite($handle, fread($fp, 8192));
}
fclose($handle);
fclose($fp);
$file = $cachefile;
}
$app = C::t('app_market')->fetch_by_identifier('ffmpeg', 'dzz');
$option = array(
'ffmpeg.binaries' => $app['ffmpeg.binaries'] ? $app['ffmpeg.binaries'] : (strstr(PHP_OS, 'WIN') ? DZZ_ROOT . 'dzz\ffmpeg\ffmpeg\ffmpeg.exe' : '/usr/bin/ffmpeg'),
'ffprobe.binaries' => $app['ffprobe.binaries'] ? $app['ffprobe.binaries'] : (strstr(PHP_OS, 'WIN') ? DZZ_ROOT . 'dzz\ffmpeg\ffmpeg\ffprobe.exe' : '/usr/bin/ffprobe'),
'timeout' => $app['timeout'] ? $app['timeout'] : 3600, // The timeout for the underlying process
'ffmpeg.threads' => $app['ffmpeg.threads'] ? $app['ffmpeg.threads'] : 1, // The number of threads that FFMpeg should use
);
$ffprobe = FFMpeg\FFProbe::create($option, null);
if ('audio' == getTypeByExt($attachment['ext'])) {
$meta = $ffprobe
->streams($file) // extracts streams informations
->audios() // filters video streams
->first();
$info = array();
if ($meta) {
$info = array(
'duration' => round($meta->get('duration'), 2),
);
}
} else {
$meta = $ffprobe
->streams($file) // extracts streams informations
->videos() // filters video streams
->first();
$info = array();
if ($meta) {
$info = array(
'width' => intval($meta->get('width')),
'height' => intval($meta->get('height')),
'duration' => round($meta->get('duration'), 2),
);
}
}
if ($cachefile) @unlink($cachefile);
return $info;
}
/* 获取缩略图
$file:文件地址
$time:获取缩略图的时间点单位秒
*/
public function getThumb($data, $time = 0)
{
global $_G;
if($data['aid']){
$attachment = IO::getMeta('attach::'.$data['aid']);
}else{
$attachment = IO::getMeta($data['rid']);
}
$file = IO::getStream($attachment['path']);
//本地路径
if ($attachment['bz'] != 'dzz::') {
$cachefile = $_G['setting']['attachdir'] . './cache/' . md5($attachment['path']) . '.' . $attachment['ext'];
$handle = fopen($cachefile, 'w+');
$fp = fopen($file, 'rb');
while (!feof($fp)) {
fwrite($handle, fread($fp, 8192));
}
fclose($handle);
fclose($fp);
$file = $cachefile;
}
$thumbpath = $this->getthumbpath('pichomethumb');
if($data['aid'])$thumbname = md5($data['aid'].$data['thumbsign']).'_original.webp';
else $thumbname = md5($data['path'].$data['thumbsign']).'_original.webp';
$target = $thumbpath.$thumbname;
if ('audio' == getTypeByExt($attachment['ext'])) {
//$target = ($data['thumbsign']) ? $data['rid']. '.webp' : $data['rid'] . '.webp';
$jpg = $_G['setting']['attachdir'] . $target;
$jpgpath = dirname($jpg);
dmkdir($jpgpath);
if (!in_array($data['ext'], array('mp3', 'wav'))) {
$audio = $this->fm->open($file);
$audio_format = new FFMpeg\Format\Audio\Mp3();
$tmp = $_G['setting']['attachdir'] . './cache/' . md5($file) . '.mp3';
$audio->save($audio_format, $tmp);
}
$audio = $this->fm->open($tmp ? $tmp : $file);
$waveform = $audio->waveform(720, 360, array('#888888'));
$waveform->save($jpg);
if ($tmp) @unlink($tmp);
} else {
// $target = $data['rid'] . '.jpg';
$jpg = $_G['setting']['attachdir']. $target;
$jpgpath = dirname($jpg);
dmkdir($jpgpath);
$app = C::t('app_market')->fetch_by_identifier('ffmpeg', 'dzz');
if(!$time){
$option = array(
'ffmpeg.binaries' => $app['ffmpeg.binaries'] ? $app['ffmpeg.binaries'] : (strstr(PHP_OS, 'WIN') ? DZZ_ROOT . 'dzz\ffmpeg\ffmpeg\ffmpeg.exe' : '/usr/bin/ffmpeg'),
'ffprobe.binaries' => $app['ffprobe.binaries'] ? $app['ffprobe.binaries'] : (strstr(PHP_OS, 'WIN') ? DZZ_ROOT . 'dzz\ffmpeg\ffmpeg\ffprobe.exe' : '/usr/bin/ffprobe'),
'timeout' => $app['timeout'] ? $app['timeout'] : 3600, // The timeout for the underlying process
'ffmpeg.threads' => $app['ffmpeg.threads'] ? $app['ffmpeg.threads'] : 1, // The number of threads that FFMpeg should use
);
$ffprobe = FFMpeg\FFProbe::create($option, null);
$meta = $ffprobe
->streams($file) // extracts streams informations
->audios() // filters video streams
->first();
$duration = 0;
if ($meta) {
$duration = round($meta->get('duration'), 2);
}
$time = getglobal('config/audiothumetime') ? intval(getglobal('config/audiothumetime')):5;
$time = ($duration > $time) ? $time:ceil($data['duration']);
}
try {
$video = $this->fm->open($file);
$video
->frame(FFMpeg\Coordinate\TimeCode::fromSeconds($time))
->save($jpg);
} catch (\Exception $e) {
runlog('ffmpeg', $e->getMessage() . ' File:' . $file);
}
}
if ($cachefile) @unlink($cachefile);
if (is_file($jpg)) {
$defaultspace = $_G['setting']['defaultspacesetting'];
//如果原文件位置不在本地,则将转换完成文件迁移到对应位置
if ($defaultspace['bz'] != 'dzz') {
$cloudpath = $defaultspace['bz'].':'.$defaultspace['did'] . ':/' .$target;
$filepath = \IO::moveThumbFile($cloudpath, $jpg);
if (!isset($filepath['error'])) {
@unlink($jpg);
return $target;
}
} else {
return $target;
}
}
return false;
}
public function getVideoQuality($videoquality = 1)
{
$templatename = '';
switch ($videoquality) {
case 0://流畅
$templatename = 'pichomeconvert-mp4-640-360-400-mp3';
$width = 640;
$height = 360;
$bitrate = 400;
break;
case 1://标清
$templatename = 'pichomeconvert-mp4-960-540-900-mp3';
$width = 960;
$height = 540;
$bitrate = 900;
break;
case 2://高清
$templatename = 'pichomeconvert-mp4-1280-720-1500-mp3';
$width = 1280;
$height = 720;
$bitrate = 1500;
break;
case 3://超清
$templatename = 'pichomeconvert-mp4-1920-1080-3000-mp3';
$width = 1920;
$height = 1080;
$bitrate = 3000;
break;
case 4://2k
$templatename = 'pichomeconvert-mp4-3500-2560-1440-mp3';
$width = 3500;
$height = 2560;
$bitrate = 1440;
break;
case 5://4k
$templatename = 'pichomeconvert-mp4-3840-2160-6000-mp3';
$width = 3840;
$height = 2160;
$bitrate = 6000;
break;
}
return array($templatename, $width, $height, $bitrate);
}
//转码,windows下大文件可能出现内部错误X264报错不知原因
public function convert($id, $ext = 'mp4', $videoquality = 1, $extra = array())
{
global $_G;
//获取附件信息
//获取记录表数据
$cron = C::t('video_record')->fetch($id);
if($cron['aid']){
$attachment = IO::getMeta('attach::'.$cron['aid']);
}else{
$attachment = IO::getMeta($cron['rid']);
}
list($templatename, $fwidth, $fheight, $fbitrate) = $this->getVideoQuality($videoquality);
//本地文件路径
$target = 'pichomethumb/' . date('Ym') . '/' . date('d') .'/'.md5($attachment['path']) . '.' . $cron['format'];
//本地存储时路径
$recordpath = 'dzz::' . $target;
//文件保存路径
$mp4 = $_G['setting']['attachdir'].$target;
$mp4path = dirname($mp4);
dmkdir($mp4path);
//开始转换过程
$file = IO::getStream($attachment['path']);
if ($attachment['bz'] != 'dzz::') {
$cachefile = $_G['setting']['attachdir'] . './cache/' . md5($attachment['path']) . '.' . $attachment['ext'];
$handle = fopen($cachefile, 'w+');
$fp = fopen($file, 'rb');
while (!feof($fp)) {
fwrite($handle, fread($fp, 8192));
}
fclose($handle);
fclose($fp);
$file = $cachefile;
}
//更新转换执行次数
C::t('video_record')->update($cron['id'], array('status' => 1,'path'=>$target, 'dateline' => TIMESTAMP, 'jobnum' => (($cron['jobnum']) ? intval($cron['jobnum']) + 1 : 1)));
$video = $this->fm->open($file);
//水印
// $video->filters() ->watermark($watermarkPath, array('position' => 'relative','bottom' => 50, 'right' => 50 ));
$video->path = $cron['id'];
switch ($ext) {
case 'mp4':
$format = new FFMpeg\Format\Video\X264('aac');
break;
case 'webm':
$format = new FFMpeg\Format\Video\WebM();
break;
case 'ogg':
$format = new FFMpeg\Format\Video\Ogg();
break;
case 'wmv':
$format = new FFMpeg\Format\Video\WMV();
break;
case 'wav':
$format = new FFMpeg\Format\Audio\Wav();
break;
case 'mp3':
$format = new FFMpeg\Format\Audio\Mp3();
break;
default:
$format = new FFMpeg\Format\Video\X264('aac');
}
if (!in_array($ext, array('mp3', 'wav'))) {
//获取视频信息
try {
$info = $this->getInfo($attachment);
if(!in_array($ext,array('mp3','wav'))){
if($info['width']){
$width=$fwidth;
$height=$info['height']?($width*$info['height']/$info['width']):$fheight;
//指定视频宽高
$video->filters()->resize(new FFMpeg\Coordinate\Dimension($width, $height))->synchronize();
}else{
$video->filters()->resize(new FFMpeg\Coordinate\Dimension($fwidth, $fheight))->synchronize();
}
}
$bitrate=intval($fbitrate>$info['bit_rate']?$fbitrate:$info['bit_rate']);
$format->setKiloBitrate($bitrate);
} catch (\Exception $e) {
};
}
$format->on('progress', function ($video, $format, $percentage) {
C::t('video_record')->update($video->path, array('percent' => $percentage > 0 ? $percentage : 1));
});
try {
$video->save($format, $mp4);
} catch (\Exception $e) {
//失败时记录失败信息
C::t('video_record')->update($cron['id'], array('endtime' => strtotime('now'), 'status' => -1, 'error' => $e->getMessage()));
return array('error' => $e->getMessage());
}
//如果未发生错误更改记录表数据为成功,并存储本地存储时路径
C::t('video_record')->update($cron['id'], array('percent' => 100, 'endtime' => strtotime('now'), 'status' => 2, 'path' => $recordpath));
if ($cachefile) @unlink($cachefile);
//如果原文件位置不在本地,则将转换完成文件迁移到对应位置
if ($attachment['bz'] != 'dzz::') {
//组合云端保存位置
$cloudpath =$attachment['bz']. ':' . '/tmppichomethumb/' . md5($attachment['path']) . '.' . $cron['format'];
$filepath = IO::moveThumbFile($cloudpath, $mp4);
if (!isset($filepath['error'])) {
C::t('video_record')->update($cron['id'], array('path' => $cloudpath,'remoteid'=>$attachment['remoteid']));
@unlink($mp4);
}
}
return $cron;
}
//兼容linux下获取文件名
public function get_basename($filename)
{
if ($filename) {
return preg_replace('/^.+[\\\\\\/]/', '', $filename);
}
return '';
}
public function getPath($dir = 'imgcache/ffmpeg')
{
global $_G;
$target1 = $dir . '/index.html';
$target_attach = $_G['setting']['attachdir'] . './' . $target1;
$targetpath = dirname($target_attach);
dmkdir($targetpath);
return $dir;
}
public function getthumbpath($dir = 'pichomethumb'){
$subdir = $subdir1 = $subdir2 = '';
$subdir1 = date('Ym');
$subdir2 = date('d');
$subdir = $subdir1 . '/' . $subdir2 . '/';
// $target1 = $dir . '/' . $subdir . 'index.html';
$target = $dir . '/' . $subdir;
return $target;
}
}