开始动工吧
改动之前
在正式开始之前,我觉得有必要稍微花一点点时间看一下改动之前的调用方式,以便可以全面的了解一下,如果不感兴趣的可以直接跳过~
public function transcodeVideo(){ //这里对参数内容进行了简化,关注流程就好:) $url = $this->url; //调用七牛的转码服务 return FileService::getInstance()->transcodeVideo($url,callback?mid=".$this->id); } //调用七牛的转码服务,一开始是考虑直接改这个方法,当然这么做也确实可以。 public function transcodeVideo($video,$callback_url){ Log::info(__CLASS__.'transcodeVideo param:',compact('video','callback_url')); $auth = new Auth(env('QINIU_ACCESSKEY'), env('QINIU_SECRETKEY')); $c = new \Qiniu\Processing\PersistentFop($auth); $ret = $c->execute(env('QINIU_BUCKET'),$video,'avthumb/mp4/vcodec/libx264/s/720x720/autoscale/1|saveas/'.\Qiniu\base64_urlSafeEncode(env('QINIU_BUCKET').':'.md5($video).'.mp4'),'video1',$callback_url,true); Log::info(__CLASS__.'.transcodeVideo ret:',$ret); if(is_array($ret) && count($ret) > 0) { return true; }else{ return false; } }
其实有一个问题就是,因为项目肯定不止一个人在用到这个功能,所以调用方也是五花八门的,不全局搜索一下,你都不知道有多少个人在使用这个功能,所以我们做一个统一入口是很有必要的。
改动之后
首先我们注册一个视频转码的服务提供者,具体怎么创建大家可以参考官方文档,有很详细的介绍,这里就不再多说了。
public function register() { //如果要换其他的服务方,直接把匿名函数中的类改一下就好了 $this->app->singleton(VideoTranscodeService::class, function($app){ return new \Aliyun\VideoTranscodeService(); }); }
那么我们看到了,如果要实现这个服务,我们需要构建的基础代码结构:
1. 定义抽象父类: VideoTranscodeService
2. 定义七牛实现: Qiniu\VideoTranscodeService
3. 定义阿里云实现: Aliyun\VideoTranscodeService
下面附上实现代码,有相关业务需求的同学也可以参考一下。
抽象父类:
abstract class VideoTranscodeService{ protected $sourceUrl = ''; protected $extra = []; protected $scene = ''; /** * 提交转码作业 * @param string $scene 场景 * @param string $sourceUrl 源文件地址 * @param array $extra 额外数据 * @return mixed * @throws BusinessException */ public function videoTranscode($scene, $sourceUrl = '', $extra = []) { Log::info(__CLASS__.'.videoTranscode param:', compact('scene', 'sourceUrl','extra')); if(!$scene) { Log::warning(__CLASS__.'.videoTranscode loss scene'); throw new BusinessException('缺少场景',Error::INVALID_PARAM); } if(!$sourceUrl) { Log::warning(__CLASS__.'.videoTranscode loss sourceUrl'); throw new BusinessException('缺少源文件地址',Error::INVALID_PARAM); } $this->scene = $scene; $this->sourceUrl = $sourceUrl; $this->extra = $extra; return call_user_func([$this, $this->scene]); } /** * @param $func * @param $arg * @throws BusinessException */ public function __call($func = '', $arg = []){ Log::warning(__CLASS__ . $func . ' function not Exists'); throw new BusinessException('访问不存在的方法', Error::INVALID_PARAM); } /** * 根据源文件地址,生成转码后地址 * @param string $sourceUrl * @return mixed */ abstract public function makeCompressUrl($sourceUrl); /** * 根据视频地址,生成封面图地址 * @param $videoUrl * @return mixed */ abstract public function makeCoverImg($videoUrl); }
七牛转码服务:
class VideoTranscodeService extends TranscodeService{ /** * 视频转码 * @param string $sourceUrl * @param array $extra * @return bool * @throws BusinessException */ protected function interaction(){ Log::info(__CLASS__.'.interaction param:', [$this->sourceUrl, $this->extra]); $callbackUrl = $this->makeCallbackUrl($this->extra['id']); return $this->submitTranscodeJob($callbackUrl); } /** * 提交转码作业 * @param $video * @param $callbackUrl * @return bool */ protected function submitTranscodeJob($callbackUrl = ''){ //源文件 $video = basename($this->sourceUrl); //认证 $auth = new Auth(env('QINIU_ACCESSKEY'), env('QINIU_SECRETKEY')); $c = new \Qiniu\Processing\PersistentFop($auth); //输出(转码)地址 $output = VideoTranscodeConf::$videoPath[$this->scene]['output'].md5($video).'.mp4'; $saveAs = \Qiniu\base64_urlSafeEncode(env('QINIU_BUCKET').':'.$output); $fops = 'avthumb/mp4/vcodec/libx264/s/720x720/autoscale/1|saveas/'. $saveAs; //提交转码作业 $ret = $c->execute(env('QINIU_BUCKET'),$video,$fops,'video1',$callbackUrl,true); Log::info(__CLASS__.'.submitTranscodeJob ret:',$ret); if (is_array($ret) && count($ret) > 0) { return true; }else{ return false; } } //生成七牛回调地址 protected function makeCallbackUrl($extra){ $data = $this->scene.'#'.$extra; return route('qiniuCallback',['data' => $data]); } /** * 根据源文件地址,生成转码后地址 * @param string $sourceUrl * @return mixed|string * @throws BusinessException */ public function makeCompressUrl($sourceUrl = ''){ if(!$sourceUrl) { Log::warning(__CLASS__.'.makeCompressUrl sourceName Not Exists'); throw new BusinessException('源文件地址错误',Error::INVALID_PARAM); } return env('QINIU_HOST')."/".md5(basename($sourceUrl)).".mp4"; } /** * 根据视频地址,生成封面图地址 * @param $videoUrl * @return string */ public function makeCoverImg($videoUrl){ return $videoUrl.'?vframe/jpg/offset/0'; } }
阿里云转码服务:
class VideoTranscodeService extends TranscodeService{ /** * * VideoTranscodeService constructor. * @throws ClientException */ public function __construct(){ AlibabaCloud::accessKeyClient(AliyunConf::VIDEO_ACCESS_KEY, AliyunConf::VIDEO_ACCESS_KEY_SECRET) ->regionId(env('ALIYUN_VIDEO_REGION')) ->asGlobalClient(); } /** * 视频转码 * @param string $sourceUrl 输入视频地址 * @param array $extra 额外配置参数: id、 width(视频宽度) * @return bool 请求结果,仅代表请求是否成功,不代表最终转码结果 * @throws BusinessException * @throws ClientException * @throws ServerException */ protected function interaction(){ Log::info(__CLASS__.'.interaction param:', [$this->sourceUrl,$this->extra]); $ossLocation = env('ALIYUN_VIDEO_BUCKET_LOCATION'); $ossBucket = env('ALIYUN_VIDEO_BUCKET_NAME'); $userData = 'interaction#'.$this->extra['id']; $templateId = $this->getTemplate($this->extra['width']); $input = [ 'Location' => $ossLocation, 'Bucket' => $ossBucket, 'Object' => urlencode($this->makeInputObject()) ]; $outputs = [[ 'OutputObject' => urlencode($this->makeOutputObject()), 'Container' => ['Format' => AliyunConf::VIDEO_TRANSCODE_FORMAT], 'TemplateId' => $templateId, 'UserData' => $userData ]]; return $this->submitTranscodeJob($input, $outputs, $ossLocation, $ossBucket); } /** * 提交转码作业 * @param $input * @param $outputs * @param string $ossLocation * @param string $ossBucket * @return bool * @throws ClientException * @throws ServerException */ protected function submitTranscodeJob($input, $outputs, $ossLocation = '', $ossBucket = ''){ $result = AlibabaCloud::mts() ->v20140618() ->submitJobs() ->setAcceptFormat('JSON') ->withInput(json_encode($input)) ->withOutputs(json_encode($outputs)) ->withOutputBucket($ossBucket) ->withOutputLocation($ossLocation) ->withPipelineId(env('ALIYUN_VIDEO_TRANSCODE_PIPELINE')) ->request(); return $result->isSuccess(); } /** * 根据源文件名称,生成转码后地址(主要用作于类外直接调用) * @param string $sourceUrl 转码前源文件名称 * @return string 转码后URL地址 * @throws BusinessException */ public function makeCompressUrl($sourceUrl = ''){ if(!$sourceUrl) { Log::warning(__CLASS__.'.makeCompressUrl sourceName Not Exists'); throw new BusinessException('源文件地址错误',Error::INVALID_PARAM); } $inputFile = basename($sourceUrl); $outputObject = $this->makeOutputObject($inputFile); return env('ALIYUN_VIDEO_BUCKET_URL').$outputObject; } /** * 根据视频地址,生成封面图地址 * @param $videoUrl * @return string */ public function makeCoverImg($videoUrl){ return $videoUrl.'?x-oss-process=video/snapshot,t_0,w_0,h_0,m_fast'; } /** * 生成输出文件bucket地址 * @param $inputFile string 输入文件 -用于类外部直接调用makeCompressUrl时生成的路径 * @return string * @throws BusinessException */ protected function makeOutputObject($inputFile = ''){ if (isset(VideoTranscodeConf::$videoPath[$this->scene]['output'])) { if (!$inputFile) { $inputFile = basename($this->sourceUrl); } return VideoTranscodeConf::$videoPath[$this->scene]['output'].md5($inputFile).'.'. AliyunConf::VIDEO_TRANSCODE_FORMAT; }else{ Log::error(__CLASS__.'.makeOutputObject outputpath not config'); throw new BusinessException('缺少输出配置文件', Error::INVALID_PARAM); } } /** * 生成输入文件bucket地址 * @return string * @throws BusinessException */ protected function makeInputObject(){ if (isset(VideoTranscodeConf::$videoPath[$this->scene]['input'])) { return VideoTranscodeConf::$videoPath[$this->scene]['input'].basename($this->sourceUrl); } else { Log::error(__CLASS__.'.makeInputObject inputpath not config'); throw new BusinessException('缺少输入配置文件', Error::INVALID_PARAM); } } /** * 根据宽度定义转码模板 * @param int $width * @return string */ protected function getTemplate($width = 0){ if($width > 1280) { $templateId = AliyunConf::VIDEO_TRANSCODE_HD; //高清 }else{ $templateId = AliyunConf::VIDEO_TRANSCODE_SD; //标清 } return $templateId; } }
这样我们的服务就写好了,在业务代码中只需要调用下面代码即可完成视频的转码,而不需要关注第三方的服务到底是谁。(代码中隐藏了部分参数配置信息,但不影响整体流程)
app(VideoTranscodeService::class)->videoTranscode('interaction', $this->url, $extra);
© 著作权归作者所有
发表评论