天天看點

騰訊雲,雲點播,視訊合成

免費處理視訊素材的網站: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)平均目标碼率模式。設定多少碼率,就可以轉出多少碼率的檔案。

  • 然後資源這塊 那個存儲資源一直顯示了 但服務概況裡有顯示存儲空間,客服說這塊是沒用完不顯示資源占用 具體是不是 還不清楚