免費處理視訊素材的網站:https://www.piaoquantv.com/clip/
騰訊雲代碼都很詳細,可以在調試代碼都有生成
https://cloud.tencent.com/document/product/266/35286
代碼封裝
引入檔案
use TencentCloud\Common\Credential;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Common\Exception\TencentCloudSDKException;
use TencentCloud\Vod\V20180717\VodClient;
常量
private $SECRET_ID = ''; //密鑰的 secretId
private $SECRET_KEY = ''; //密鑰的 secretKey
private $INTRODUCE_PREFIX = 'TencentCloud\Vod\V20180717\Models\\';//引入騰訊雲檔案字首
private $HOST = ['vod.tencentcloudapi.com'];//騰訊雲請求 域名目前隻需要 vod.tencentcloudapi.com 有需要往裡面加
private $METHOD_NAME = ['ComposeMedia','DescribeMediaInfos','DescribeTaskDetail','DeleteMedia','ManageTask','ProcessMedia'];//接口方法名
封裝方法
/**
* 騰訊雲相關接口通路
* @param array $param 請求參數
* @param int $type 請求接口類型 1視訊合成 2擷取媒體詳情 3任務詳情 4删除視訊 5終止任務 6視訊轉碼處理
* @param int $host 請求域名選擇值
* @throws
* @return array
*/
function Interface(array $param, int $type, int $host=0){
try {
$cred = new Credential($this->SECRET_ID, $this->SECRET_KEY);
$httpProfile = new HttpProfile();
$httpProfile->setEndpoint($this->HOST[$host]);
$clientProfile = new ClientProfile();
$clientProfile->setHttpProfile($httpProfile);
$client = new VodClient($cred, "", $clientProfile);
$method_name = $this->METHOD_NAME[$type-1];//擷取方法名
$class = $this->INTRODUCE_PREFIX.$method_name.'Request';//擷取類路徑
$req = new $class();
$req->fromJsonString(json_encode($param));
$resp = $client->$method_name($req);
return json_decode($resp->toJsonString(),true);
}
catch(TencentCloudSDKException $e) {
return ['code'=>400,'msg'=>$e];
}
}
參數在調試中生成的代碼有 根據你需要的參數會生成對應的代碼
完整的代碼:(我這個是處理上傳到抖音的視訊的)
<?php
namespace app\service;
use app\BaseController;
use TencentCloud\Common\Credential;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Common\Exception\TencentCloudSDKException;
use TencentCloud\Vod\V20180717\VodClient;
use think\facade\Db;
class TencentService extends BaseController{
private $SECRET_ID = ''; //密鑰的 secretId
private $SECRET_KEY = ''; //密鑰的 secretKey
private $INTRODUCE_PREFIX = 'TencentCloud\Vod\V20180717\Models\\';//引入騰訊雲檔案字首
private $HOST = ['vod.tencentcloudapi.com'];//騰訊雲請求 域名目前隻需要 vod.tencentcloudapi.com 有需要往裡面加
private $METHOD_NAME = ['ComposeMedia','DescribeMediaInfos','DescribeTaskDetail','DeleteMedia','ManageTask','ProcessMedia'];//接口方法名
private $CLASS_ID = [815251,814659,814710,824533];//分類id 分别是 一級分類id:音頻、商戶視訊 二級分類id:商戶宣傳視訊、商戶合成視訊(根據自己的需要建立 放到這裡就可以)
private $VIDEO_STENCIL_ID = [880999,885999,885998,885997,885996,885995];//視訊模版id(碼率按順序是 1000-6000) 需要讓騰訊雲技術将模版id改成 ABR模式(預設是CRF模式 檔案大小隻能往下壓縮不能往上加 ABR模式可以往上加 極限是原視訊的兩倍)(根據自己的需要建立 放到這裡就可以)
private $AUDIO_BASIS = [['id'=>'3701925919555099999','duration'=>78],['id'=>'3701925919553499999','duration'=>74],['id'=>'3701925919552199999','duration'=>85]];//音頻資訊(上傳三個音頻将 音頻的id以及時長記錄)
private $VIDEO_DATA = ['Type'=>'Video', 'VideoItem'=>['SourceMedia'=>'']];//視訊資料
private $AUDIO_DATA = ['Type'=>'Audio', 'AudioItem'=>['SourceMedia'=>'','SourceMediaStartTime'=>1]];//音頻資料 SourceMedia 是音頻id
private $TRANSITION = ['Type'=>'Transition', 'TransitionItem'=>['Duration'=>0.1, 'Transitions'=>['Type'=>'']]];//視訊過度資料 Duration 過度動畫時間 機關秒 時間會根據這個減少 Transitions内的type是過度效果 對應 $GRADIENT_TYPE 裡面的值
private $EMPTY = ['Type'=>'Empty', 'EmptyItem'=>['Duration'=>1]];//音樂過度資料 Duration 代表過度時間 機關秒 時長會根據這個增加
public $GRADIENT_TYPE = ['ImageFadeInFadeOut', 'BowTieHorizontal', 'BowTieVertical', 'ButterflyWaveScrawler', 'Cannabisleaf', 'Circle', 'CircleCrop', 'Circleopen', 'Crosswarp', 'Cube', 'DoomScreenTransition', 'Doorway', 'Dreamy', 'DreamyZoom', 'FilmBurn', 'GlitchMemories', 'Heart', 'InvertedPageCurl', 'Luma', 'Mosaic', 'Pinwheel', 'PolarFunction', 'PolkaDotsCurtain', 'Radial', 'RotateScaleFade', 'Squeeze', 'Swap', 'Swirl', 'UndulatingBurnOutSwirl', 'Windowblinds', 'WipeDown', 'WipeLeft', 'WipeRight', 'WipeUp', 'ZoomInCircles'];//合并視訊的漸變方式
private $SIGN_TIME = 60*10;//客戶端 簽名有效時長 機關秒
private $VIDEO_RAND_VAL = 25;//視訊截取時間随機取值範圍 機關/100毫秒
public $QUANTITY = ['trigger_num'=>10,'synthesis_num'=>20,'limit_num'=>4,'video_time'=>60,'give_up_time'=>5];//數 分别是 trigger_num:觸發數(剩餘多少宣傳視訊觸發) synthesis_num:合成視訊數量 limit_num:合成視訊最少需要多少個宣傳視訊 video_time:合成視訊最長視訊時間 give_up_time:放棄時長(距離一分鐘還剩多少秒時不在拼接新視訊)
public $VIDEO_SIZE_LIMIT = [5,40];//合成視訊檔案觸發轉碼的檔案大小限制
private $VIDEO_RAND_AREA = [3,5];//合成視訊随機視訊數
/**
* 騰訊雲 簽名 用戶端
* @throws
* @return string
*/
function TencentSign(){
$current = time();//确定簽名的目前時間
$expired = $current + $this->SIGN_TIME; // 簽名有效期
$arg_list = [
'secretId' => $this->SECRET_ID,
'currentTimeStamp' => $current,
'expireTime' => $expired,
'random' => mt_rand()
];// 向參數清單填入參數
$original = http_build_query($arg_list);// 計算簽名
return base64_encode(hash_hmac('SHA1', $original, $this->SECRET_KEY, true).$original);
}
/**
* 騰訊雲相關接口通路
* @param array $param 請求參數
* @param int $type 請求接口類型 1視訊合成 2擷取媒體詳情 3任務詳情 4删除視訊 5終止任務 6視訊壓縮
* @param int $host 請求域名選擇值
* @throws
* @return array
*/
function Interface(array $param, int $type, int $host=0){
try {
$cred = new Credential($this->SECRET_ID, $this->SECRET_KEY);
$httpProfile = new HttpProfile();
$httpProfile->setEndpoint($this->HOST[$host]);
$clientProfile = new ClientProfile();
$clientProfile->setHttpProfile($httpProfile);
$client = new VodClient($cred, "", $clientProfile);
$method_name = $this->METHOD_NAME[$type-1];//擷取方法名
$class = $this->INTRODUCE_PREFIX.$method_name.'Request';//擷取類路徑
if($type==1)$param['Output'] = ['FileName'=>date('Ymd').rand(),'ClassId'=>$this->CLASS_ID[3],'Container'=>'mp4','AudioStream' => ['SampleRate' => 48000]];//拼接輸出視訊資訊
$req = new $class();
$req->fromJsonString(json_encode($param));
$resp = $client->$method_name($req);
return json_decode($resp->toJsonString(),true);
}
catch(TencentCloudSDKException $e) {
return ['code'=>400,'msg'=>$e];
}
}
/**
* 擷取視訊詳情
* @param int $merchant_id 商戶id
* @throws
* @return array
*/
function GetVideoDetail(int $merchant_id){
$FileId = Db::name('merchant_video')->where([['merchant_id','=',$merchant_id],['type','=',2],['is_use','=',1]])->whereNotBetween('size',$this->VIDEO_SIZE_LIMIT)->column('fieldId');
if(count($FileId)==0)return ['code'=>200,'msg'=>'暫無需視訊詳情'];
$return = $this->Interface(['FileIds'=>$FileId,'Filters'=>['transcodeInfo']],2);
if(empty($return['MediaInfoSet'][0]['FileId']))return ['code'=>400,'msg'=>'擷取視訊詳情失敗'];
foreach($return['MediaInfoSet'] as $v){
if(empty($v['TranscodeInfo']['TranscodeSet'][1]['Definition']))continue;
$size = (int)($v['TranscodeInfo']['TranscodeSet'][1]['Size']/1048576);
if($size<$this->VIDEO_SIZE_LIMIT[0]||$size>$this->VIDEO_SIZE_LIMIT[1]){
$save = ['is_use'=>2];
}else{
$save = ['video'=>$v['TranscodeInfo']['TranscodeSet'][1]['Url'],'size'=>$size];
}
Db::name('merchant_video')->where('fieldId',$v['FileId'])->save($save);
}
return ['code'=>200,'msg'=>'處理完成'];
}
/**
* 宣傳視訊合成
* @param int $merchant_id 商戶id
* @throws
* @return array
*/
function VideoSynthesis(int $merchant_id){
$return = $this->SynthesisJudgment($merchant_id);
if($return['code']!=200)return $return;
$num = $return['num'];
for($i=0 ;$i<$num ;$i++){
$rand_num = mt_rand($this->VIDEO_RAND_AREA[0],$this->VIDEO_RAND_AREA[1]);
$video = Db::query('select id,fieldId,duration from backend_merchant_video where merchant_id = '.$merchant_id.' and type = 1 order by rand() limit '.$rand_num);//随機擷取兩條視訊資訊
$data = $this->SynthesisDealWith($video);
if($data['code']!=200)continue;
$return = $this->Interface($data['data'],1);//合成視訊
if(empty($return['TaskId']))continue;
$add[] = ['merchant_id'=>$merchant_id,'TaskId'=>$return['TaskId'],'name'=>'合成視訊','type'=>2,'duration'=>$data['duration'],'out'=>$data['out'],'create_time'=>date('Y-m-d H:i:s')];
}
$return = Db::name('merchant_video')->insertAll($add);
if($return<1)return ['code'=>400,'msg'=>'資料表更改失敗'];
Db::name('merchant')->where('merchant_id',$merchant_id)->inc('use_num',$num)->update();
return ['code'=>200,'msg'=>'合成視訊成功'];
}
/**
* 合成視訊判斷 及 擷取可用資料 合成數量
* @param int $merchant_id 檔案id
* @throws
* @return array
*/
private function SynthesisJudgment(int $merchant_id){
$available_num = Db::name('merchant_video')->where(['merchant_id'=>$merchant_id,'type'=>2,'is_use'=>1])->count();//擷取可用宣傳視訊數
if($available_num>$this->QUANTITY['trigger_num'])return ['code'=>400,'msg'=>'可用視訊還很充足'];
$propaganda_num = Db::name('merchant_video')->where(['merchant_id'=>$merchant_id,'type'=>1])->count();
if($propaganda_num<$this->QUANTITY['limit_num'])return ['code'=>400,'msg'=>'宣傳視訊不足'.$this->QUANTITY['limit_num'].'條'];
$video_num = Db::name('merchant')->where('merchant_id',$merchant_id)->field('video_num,use_num')->find();//擷取商戶可合成視訊的總數量以及使用數量
$num = $video_num['video_num']-$video_num['use_num'];
if($num<1)return ['code'=>400,'msg'=>'你合成視訊的數量已達到上限,如還需合成視訊請聯系平台從業人員'];
if($num>$this->QUANTITY['synthesis_num'])$num=$this->QUANTITY['synthesis_num'];
return ['code'=>200,'num'=>$num];
}
/**
* 擷取合成視訊網址
* @param int $merchant_id 檔案id
* @throws
* @return array
*/
function GetVideoUrl(int $merchant_id){
$taskId = Db::name('merchant_video')->where(['merchant_id'=>$merchant_id,'type'=>2])->whereRaw('video is null and TaskId is not null')->field('TaskId,duration')->select();
if(count($taskId)==0)return ['code'=>200,'msg'=>'網址已全'];
foreach($taskId as $v){
$return = $this->Interface(['TaskId'=>$v['TaskId']],3);//擷取任務詳情
if(!empty($return['ComposeMediaTask']['Output']['FileUrl'])){
$save = ['video'=>$return['ComposeMediaTask']['Output']['FileUrl'],'size'=>(int)($return['ComposeMediaTask']['MetaData']['Size']/1048576),'fieldId'=>$return['ComposeMediaTask']['Output']['FileId']];
if($save['size']<=$this->VIDEO_SIZE_LIMIT[0]||$save['size']>=$this->VIDEO_SIZE_LIMIT[1]){
switch(true) {
case ($v['duration']>=50):
$Definition = 1;
break;
case ($v['duration']<50&&$v['duration']>=35):
$Definition = 2;
break;
case ($v['duration']<35&&$v['duration']>=20):
$Definition = 3;
break;
default:
$Definition = 5;
}
$this->Interface(['FileId' => $save['fieldId'], 'MediaProcessTask' => ['TranscodeTaskSet' => [['Definition' => $this->VIDEO_STENCIL_ID[$Definition]]]]],6);//視訊轉碼
}
$return = Db::name('merchant_video')->where('TaskId',$v['TaskId'])->save($save);
if($return<1)return ['code'=>400,'msg'=>'資料處理失敗'];
}
}
return ['code'=>200,'msg'=>'網址處理完成'];
}
/**
* 合成視訊資料處理
* @param array $video_data 合成視訊參數 包括 duration:時長 fieldId:檔案id
* @throws
* @return array
*/
private function SynthesisDealWith(array $video_data){
$data = ['Tracks'=>[['Type'=>'Video', 'TrackItems'=>[]]]];//建立資料集
$duration = 0;//建立視訊時長接收變量
$val = 0;//建立自增值
$out = ',';//建立混剪參數接收變量
foreach($video_data as $v){
$duration += $v['duration'];
$out .= $v['id'].',';
$data['Tracks'][0]['TrackItems'][$val] = $this->VIDEO_DATA;
$data['Tracks'][0]['TrackItems'][$val]['VideoItem']['SourceMedia'] = $v['fieldId'];
$rand = mt_rand(1,$this->VIDEO_RAND_VAL)/10;
$duration -= $rand;
$data['Tracks'][0]['TrackItems'][$val]['VideoItem']['SourceMediaStartTime'] = $rand;
if($duration>=($this->QUANTITY['video_time']-$this->QUANTITY['give_up_time'])) {
if($duration>$this->QUANTITY['video_time']){
$data['Tracks'][0]['TrackItems'][$val]['VideoItem']['Duration'] = $v['duration']-$rand-($duration-$this->QUANTITY['video_time']);
$duration = $this->QUANTITY['video_time'];
}
break;
}
$val++;
if($v['id']!=$video_data[count($video_data)-1]['id']){
$duration -= 0.1;//減去視訊漸變時間
$data['Tracks'][0]['TrackItems'][$val] = $this->TRANSITION;
$data['Tracks'][0]['TrackItems'][$val]['TransitionItem']['Transitions']['Type'] = $this->GRADIENT_TYPE[rand(1,count($this->GRADIENT_TYPE)-1)];
$val++;
}
}
$data['Tracks'][1] = $this->AudioDealWith($duration);
return ['code'=>200,'data'=>$data,'out'=>$out,'duration'=>$duration];
}
/**
* 音頻資料處理
* @param float $duration 所需總時長
* @throws
* @return array
*/
private function AudioDealWith(float $duration){
$audio = $this->AUDIO_BASIS[mt_rand(1,count($this->AUDIO_BASIS)-1)];
$last_duration = $duration%$audio['duration'];//擷取最後一輪音樂時長
$len = (int)ceil($duration/$audio['duration']);//擷取需要的背景音樂輪數
$data = ['Type'=>'Audio', 'TrackItems'=>[]];//建立音頻資料
$val = 0;//建立自增值
for($i=0 ;$i<$len ;$i++){
$data['TrackItems'][$val] = $this->AUDIO_DATA;
$data['TrackItems'][$val]['AudioItem']['SourceMedia'] = $audio['id'];
if($i==$len-1){
$data['TrackItems'][$val]['AudioItem']['Duration'] = $last_duration;
}else{
$val++;
$data['TrackItems'][$val] = $this->EMPTY;
$val++;
}
if($i==0)$data['TrackItems'][0]['AudioItem']['SourceMediaStartTime'] = 0;
}
return $data;
}
}
我遇到的注意事項
- 視訊合成音頻有很多雜音,是因為多加了這個參數 AudioOperations 這個參數是用來調整音量的但是不傳其實也有聲音的,把這個參數去掉就沒有雜音了
- 兩個視訊合成,視訊與視訊合成中間需要有 Transition 轉場 Duration參數是轉場時長 總視訊時長會等于兩個視訊的時長減去這個參數值 同樣的多音頻也有轉場參數 Empty
- 合成的視訊轉碼中Kbps隻能轉原視訊的一半低一點,(Kbps是影響視訊大小的重要因素,抖音上傳的視訊中需要 大于5M 就可以用到這個) 下面這段是騰訊雲技術客服發我的,你需要找騰訊雲的技術客服把你的 視訊轉碼模版轉成 ABR模式 你需要給你的模版id給他
雲點播預設是按CRF模式(Constant Ratefactor )固定碼率系數模式,即在不影響視覺體驗的前提下,減少不必要幀的品質,不遵循特定的輸出碼率,達到減小碼率的效果。RF的範圍是
[0, 51]。其中0為無損模式,51品質最差。RF值加6,輸出碼率大概減少一半;減6,輸出碼率翻倍。從主觀上講,17~29是一個合理的範圍,17往往被認為從視覺上看是無損的。
優點:減少存儲和流量等資源的浪費
缺點:不能控制檔案大小
【解決方案】
如果你們還是要固定轉碼碼率,可以找我們配置ABR模式(Average Bitrate)平均目标碼率模式。設定多少碼率,就可以轉出多少碼率的檔案。
- 然後資源這塊 那個存儲資源一直顯示了 但服務概況裡有顯示存儲空間,客服說這塊是沒用完不顯示資源占用 具體是不是 還不清楚