'file', 'checkTmpRoot' => '', //缓存切片文件 'mp4Root' => '', //MP4目录 ]; $this->config = empty($config) ? $option : $config; if (!empty($_FILES['file'])) { $r = (new RedisCli); $r->connect(); $this->redis = $r->getRedis(); $this->qiniu = new Qiniu; $this->file = $_FILES['file']; } } /** * 设置gid * @param type $gid */ public function setGid($gid) { $this->gid = $gid; } /** * 设置属性 * @param type $name 文件名称 * @param type $size 文件大小 * @param type $chunk 文件当前chunk * @param type $chunks 文件全部chunks */ public function setParam($param) { $this->param = $param; } /** * 返回错误信息 * @return type */ public function getError() { return $this->error; } /** * 获取结果 */ public function getResult() { return $this->result; } /** * 执行数据[分包上传] */ public function render($imgType = 'jpg') { $chunk = $this->param['chunk'] ?? 0; $chunks = $this->param['chunks'] ?? 0; //存入token $checkRoot = $this->config['checkTmpRoot'] . date('Ymd') . '/'; if (!is_dir($checkRoot)) { mkdir($checkRoot, 0777); } $name = empty($this->param['name']) ? $this->file['name'] : $this->param['name']; $size = empty($this->param['size']) ? 0 : $this->param['size']; $token = md5($name . $this->gid . $size) . '.mp4_token'; $file_name = md5(microtime() . $this->file['tmp_name']) . '_' . $chunk . ".tmp"; move_uploaded_file($this->file['tmp_name'], $checkRoot . $file_name); //保存缓存 $save['chunk'] = $chunk; $save['chunks'] = $chunks <= 0 ? 1 : $chunks; $save['file'] = $checkRoot . $file_name; $save['size'] = $size; $save['name'] = $name; $save['status'] = 1; //压着缓存里面 $count = $this->redis->lpush($token, serialize($save)); if ($count == 1) { $this->redis->expire($token, 5 * 60); } //获取所以文件缓存 $len = $this->redis->llen($token); if ($chunks > $len) { $this->result = $save; return true; } $path = $this->config['mp4Root'] . date('Ymd') . '/'; if (!is_dir($path)) { mkdir($path, 0777); } $fileData = []; for ($i = 0; $i < $len; $i++) { $fileData[] = unserialize($this->redis->lpop($token)); } //删除缓存 $this->redis->del($token); //chunk $chunkAr = array_column($fileData, 'chunk'); array_multisort($chunkAr, SORT_ASC, $fileData); //校验文件完整性 for ($i = 0; $i < $chunks; $i++) { if (!in_array($i, $chunkAr)) { $this->error = $i . '文件校验失败,请重新上传视频'; return false; } } //重组文件 $file_ext = explode('.', $fileData[0]['name']); $file = md5($token) . "." . end($file_ext); foreach ($fileData as $v) { if (file_exists($v['file'])) { file_put_contents($path . $file, file_get_contents($v['file']), FILE_APPEND); @unlink($v['file']); } } $rootMp4 = $path . $file; //检验是否获取状态 $mp4Info = $this->video_info($rootMp4, 'ffmpeg'); //格式错误 if (empty($mp4Info['vcodec'])) { $this->error = '上传格式错误,请检查是否视频文件'; return false; } $tmpImg = $path . md5($file_name) . '.' . $imgType; //获取图片位置 if ($imgType == 'gif') { $command = "ffmpeg -y -ss 00:00:01 -t 2 -i " . $rootMp4 . " -vf fps=5,scale=400:-1 -gifflags +transdiff -y " . $tmpImg; } if ($imgType == 'jpg') { $command = "ffmpeg -ss 00:00:02 -i " . $rootMp4 . " -f image2 " . $tmpImg; } exec($command); if (!is_file($tmpImg)) { $this->error = '生成文件失败,请检查视频是否正常'; return false; } //上传第一张图七牛 $result = $this->qiniu->updateFile('img', '1.' . $imgType, $tmpImg); $img = $result['url']; //视频 $qUrl = $this->qiniu->getYuFile('mp4', 'mp4'); $url = $this->qiniu->config['endpoint'] . $qUrl; @unlink($tmpImg); //生成视频信息 //判断视频不大于1920【缩小】 $save = []; $mp4Width = $this->video_width($rootMp4); $save['width'] = $mp4Width['width']; $save['height'] = $mp4Width['height']; $save['size'] = $mp4Info['size']; $save['type'] = 'mp4'; $save['url'] = $url; $save['file'] = $rootMp4; $save['time'] = time(); $save['play_time'] = (int)$mp4Info['play_time']; $id = Db::name('resource')->insertGetId($save); $this->result = ['url' => $url,'resourceId'=>$id, 'play_time' => $save['play_time'], 'img' => $img, 'width' => $save['width'], 'height' => $save['height']]; //任务执行器 return true; } /** * 视频信息 * @param type $file * @param type $ffmpeg * @return type */ public function video_info($file, $ffmpeg) { ob_start(); passthru(sprintf($ffmpeg . ' -i "%s" 2>&1', $file)); $info = ob_get_contents(); ob_end_clean(); // 通过使用输出缓冲,获取到ffmpeg所有输出的内容。 $ret = []; // Duration: 01:24:12.73, start: 0.000000, bitrate: 456 kb/s if (preg_match("/Duration: (.*?), start: (.*?), bitrate: (\d*) kb\/s/", $info, $match)) { $ret['duration'] = $match[1]; // 提取出播放时间 $da = explode(':', $match[1]); $ret['seconds'] = $da[0] * 3600 + $da[1] * 60 + $da[2]; // 转换为秒 $ret['start'] = $match[2]; // 开始时间 $ret['bitrate'] = $match[3]; // bitrate 码率 单位 kb } // Stream #0.1: Video: rv40, yuv420p, 512x384, 355 kb/s, 12.05 fps, 12 tbr, 1k tbn, 12 tbc if (preg_match("/Video: (.*?), (.*?), (.*?)[,\s]/", $info, $match)) { $ret['vcodec'] = $match[1]; // 编码格式 $ret['vformat'] = $match[2]; // 视频格式 $ret['resolution'] = $match[3]; // 分辨率 $a = explode('x', $match[3]); //$ret['width'] = $a[0]; //$ret['height'] = $a[1]; } // Stream #0.0: Audio: cook, 44100 Hz, stereo, s16, 96 kb/s if (preg_match("/Audio: (\w*), (\d*) Hz/", $info, $match)) { $ret['acodec'] = $match[1]; // 音频编码 $ret['asamplerate'] = $match[2]; // 音频采样频率 } if (isset($ret['seconds']) && isset($ret['start'])) { $ret['play_time'] = $ret['seconds'] + $ret['start']; // 实际播放时间 } $ret['size'] = filesize($file); // 文件大小 return $ret; } /** * 获取宽度高度 * @param type $file * @return string */ public function video_width($file) { ob_start(); passthru("ffprobe -select_streams v -show_entries format=duration,size,bit_rate,filename -show_streams -v quiet -of csv=\"p=0\" -of json -i " . $file); $info = ob_get_contents(); ob_end_clean(); $infoAr = json_decode($info,true); if(empty($infoAr['streams'])) { return ['width'=>0,'height'=>0,'qt'=>'']; } $streams = $infoAr['streams'][0]; $ret = []; if(!empty($streams['tags']) && !empty($streams['tags']['rotate']) && in_array(abs($streams['tags']['rotate']),[90,270]) ) { $ret['width'] = $streams['height']; $ret['height'] = $streams['width']; } else { $ret['width'] = empty($streams['width']) ? 0 : $streams['width']; $ret['height'] =empty($streams['height']) ? 0 : $streams['height']; } $ret['qt'] = ''; if (!empty($ret['width']) && !empty($ret['height']) && is_numeric($ret['width']) && is_numeric($ret['height'])) { $w = 640; if ($ret['width'] >= $ret['height']) { $lt = intval($ret['height']) / intval($ret['width']); $hqt = intval($w * $lt); $hqt = intval($hqt / 10) * 10; $ret['qt'] = '640x' . $hqt; } if ($ret['width'] < $ret['height']) { $lt = intval($ret['width']) / intval($ret['height']); $hqt = intval($w / $lt); $hqt = intval($hqt / 10) * 10; $ret['qt'] = '640x' . $hqt; } } return $ret; } }