thinkphp5之腾讯视频hls片批量多线程下载自动合成mp4

发布于:2023-01-21 ⋅ 阅读:(208) ⋅ 点赞:(0)

环境介绍:

windows10

apache2.4

php7.4

thinkphp5.0

php-curl扩展 composer require php-curl-class/php-curl-class @dev

ffmpeg  https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-essentials.7z 解压后需要配置下bin目录环境变量


路由文件配置:

<?php
use think\Route;
Route::rule('/hls/','Hls/index');
Route::rule('/hls_path/:tsname','Hls/hlsPath'); //映射hls片目录

目录介绍:

cid 腾讯视频唯一id

c:/ku 保存下载的所有文件

c:/ku/list 保存视频m3u8文件

c:/ku/hls  保存所有视频片文件

c:/ku/hls/cid 保存单个视频片文件

c:/ku/video  保存ffmpeg输出的mp4视频文件

请手动创建【c:/ku】【c:/ku/list】【c:/ku/hls 】【c:/ku/video】这几个文件夹,如要保存到其他位置请对应修改下面Hls.php中的配置


腾讯视频hls解析接口:

$hls_api = 'https://movie.etafort.cn/hlsapi/'.$cid;

该接口返回对应视频m3u8文件


核心代码Hls.php:

<?php

namespace app\controller;
use Curl\Curl;
use Curl\MultiCurl;
use think\Request;

@ini_set('memory_limit', '1024M'); //php内存限制调整
class Hls
{
    //本教程下载的视频文件请不要用于商业用途
    private $curl;
    private $list_path = '/ku/list';
    private $hls_path = '/ku/hls';
    private $video_path = '/ku/video';
    private $host = 'http://hls.com';    //本地虚拟域名 C:\Windows\System32\drivers\etc\hosts中配置
    private $urls = false;
    private $binfa = 100;
    private $cid_tmp = false;
    public function __construct()
    {
        $this->curl = new Curl();
    }

    //入口函数
    public function index(){
        $cids = [
            'mzc00200ekwugpe',
            'mzc002003rf9c06',
            'mzc002007bdjvq1',
            'mzc00200jrfpndx',
            'mzc00200z8hg7m6',
            'mzc00200dabqlu0',
            'mzc00200b5n7vvf',
            'mzc002003k94702',
            'mzc002001dhvlxz',
            'mzc0020026w4xg5',
            'mzc00200xut0hno',
            'mzc00200gtanynb',
            'mzc002007gnu3ra',
        ];
        foreach ($cids as $cid){
            $this->downloadList($cid);
            $this->downloadHls($cid);
            $this->downloadVideo($cid);
        }
    }
    
    //本地hls片映射
    public function hlsPath($tsname){
        $file = Request::instance()->path();
        $file = str_replace('hls_path',$this->hls_path,$file);
        return file_get_contents($file);
    }
    
    //下载单个.m3u8文件
    private function downloadList($cid){
        echo '【downloadList】'.$cid."\n";
        $file = $this->list_path.'/'.$cid.'.m3u8';
        if(file_exists($file))
            return;
        $hls_api = 'https://movie.etafort.cn/hlsapi/'.$cid;
        $hls_link = $this->download($hls_api);
        $hls_list = $this->download($hls_link);
        $hls_list_arr = explode("\n",$hls_list);
        $hls_list_arr = array_filter($hls_list_arr);
        $list_new = "";
        foreach ($hls_list_arr as $item){
            if($item[0]==='#'){
                $list_new .= $item."\n";
                continue;
            }
            $list_new .= $this->host.'/hls_path/'.$cid.'/'.$item."\n";
        }
        file_put_contents($file,$list_new);
    }

    //下载.ts片文件
    private function downloadHls($cid){
        echo '【downloadHls】'.$cid."\n";
        $urls = [];
        $hls_api = 'https://movie.etafort.cn/hlsapi/'.$cid;
        $hls_link = $this->download($hls_api);
        $hls_host = $this->getHost($hls_link);
        $hls_list = $this->download($hls_link);
        $hls_list_arr = explode("\n",$hls_list);
        $hls_list_arr = array_filter($hls_list_arr);
        foreach ($hls_list_arr as $item){
            if($item[0]==='#')
                continue;
            $tmp = explode('?',$item);
            $ts_name = $tmp[0];
            $url = $hls_host.$item.'&ts_name='.$ts_name;
            $url_id = md5($url);
            $urls[$url_id]=$url;
        }
        $this->downloads($urls,$cid,true);
    }
    
    //.ts转.mp4
    private function downloadVideo($cid){
        echo '【downloadVideo】'.$cid."\n";
        $list_file = $this->list_path.'/'.$cid.'.m3u8';
        $out_video = $this->video_path.'/'.$cid.'.mp4';
        if(file_exists($out_video))
            return;
        if(!file_exists($list_file))
            die('404');
        exec('ffmpeg -allowed_extensions ALL -protocol_whitelist "file,http,crypto,tcp,https" -i '.$list_file.' -c copy '.$out_video,$res);
        dump($res);
    }
    
    //多线程下载.ts文件
    private function downloads($urls,$cid,$huancun=true){
        if($this->urls===false){
            $this->urls = $urls;
        }
        $this->cid_tmp = $cid;
        if(!is_dir($this->hls_path.'/'.$this->cid_tmp))
            mkdir($this->hls_path.'/'.$this->cid_tmp);
        if(count($this->urls)===0){
            $this->urls=false;
//            echo "下载完成\n";
            return true;
        }
        $multi_curl = new MultiCurl();
        $multi_curl->setOpt(CURLOPT_SSL_VERIFYPEER,false);
        $multi_curl->setOpt(CURLOPT_SSL_VERIFYHOST,false);
        $multi_curl->success(function($instance) {
            $tmp = explode('=',$instance->url);
            $ts_name = $tmp[count($tmp)-1];
            $url_id = md5($instance->url);
            $file = $this->hls_path.'/'.$this->cid_tmp.'/'.$ts_name;
            file_put_contents($file,$instance->response);
            unset($this->urls[$url_id]);
            echo $url_id . " ok<br/>\n";
        });
        $multi_curl->error(function($instance) {
            echo 'call to "' . $instance->url . '" was unsuccessful.' . "<br/>\n";
            echo 'error code: ' . $instance->errorCode . "<br/>\n";
            echo 'error message: ' . $instance->errorMessage . "<br/>\n";
        });
        $multi_curl->complete(function($instance) {
//            echo 'call completed' . "\n";
            usleep(20000);//20毫秒
        });
        $count = 0;
        foreach ($this->urls as $url_id=>$url){
            if($huancun===true){
                $tmp = explode('=',$url);
                $ts_name = $tmp[count($tmp)-1];
                $file = $this->hls_path.'/'.$this->cid_tmp.'/'.$ts_name;
                if(!file_exists($file)){
                    $multi_curl->addGet($url);
                    $count++;
                }else{
                    unset($this->urls[$url_id]);
                }
            }else{
                $multi_curl->addGet($url);
                $count++;
            }
            if($count==$this->binfa||$count==count($this->urls))
                break;

        }
        $multi_curl->start(); // Blocks until all items in the queue have been processed.
        $this->downloads($this->urls,$cid,$huancun);
    }


    //下载单个文件
    private function download($url){
        $this->curl->get($url);
        if($this->curl->error)
            die($this->curl->errorMessage);
        return $this->curl->response;
    }
    
    //解析腾讯视频hls host
    private function getHost($hls_link){
        $host = '';
        $tmp = explode('/',$hls_link);
        unset($tmp[count($tmp)-1]);
        foreach ($tmp as $item){
            $host.=$item.'/';
        }
        return $host;
    }


}

运行:

切换至项目public目录

shift+右键 在此处打开powershell窗口

php index.php /hls/


备注:

切勿用于商业用途

请保证硬盘空间足够,一个视频x2大概2.5G左右

参考ffmpeg合并M3U8加密的视频 ts 合并为 mp4 - 灰信网(软件开发博客聚合)


网站公告

今日签到

点亮在社区的每一天
去签到