我想在调用PHP脚本时创建一个动态的m3u8。我不想将结果m3u8保存在服务器上,而是想将推送到浏览器,以便可以下载。谁能告诉我如何完成这项任务?
要调用的PHP脚本示例:
http://www.asite.com/makeM3u8.php?videoId=1234
可下载的动态m3u8结构:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=900000
http://someserver/channelNameBandwith900000.m3u8?session=3495732948523984eriuwehiurweirew
最佳答案
在进入脚本之前,您必须确定一些事情:
1.- .ts和.aac文件存储在哪里,它们的保护方案是什么,PHP是否能够创建能够访问它们的URL?
2.-您在其中存储原始m3u8信息(每个段的目标持续时间,终止和名称)。在这种情况下,数据库比解析现有文件要快。
3.-如果要处理多比特率,则需要一个脚本,该脚本还生成指向所有其他指针的主m3u8。
话虽如此,这是我想出的并且已经使用了一段时间而没有任何问题的解决方案。不过有两件事,我使用AWS S3进行存储并使用ffmpeg转换现有视频文件。这是一个相当漫长的过程-可能对您想要的东西过于矫视-但它可以工作。
第1部分-编码文件系统接收MP4视频并将其转换为必需的格式。
function ffConvert($origin,$basedir,$res) {
switch($res) {
// SET THE VARIABLES
case "240p": $size = "426x240"; $vbit = "360k"; $level = "3.0"; $abit = "80k"; break;
case "480p": $size = "854x480"; $vbit = "784k"; $level = "3.1"; $abit = "128k"; break;
case "720p": $size = "1280x720"; $vbit = "1648k"; $level = "3.1"; $abit = "192k"; break;
}
// CONVERT THE FILES
exec('/usr/local/bin/ffmpeg -y -async 1 -vsync -1 -analyzeduration 999999999 -i "'.$origin.'" -force_key_frames "expr:gte(t,n_forced*10)" -pix_fmt yuv420p -s '.$size.' -r:v 30 -b:v '.$vbit.' -c:v libx264 -profile:v baseline -level '.$level.' -c:a libfaac -ac 2 -ar 48000 -b:a '.$abit.' -g 90 '.$base.$res.'.mp4');
// VERIFY AND RETURN
if(file_exists($basedir.$res.'.mp4')) {
return TRUE;
} else {
return FALSE;
}
}
第2部分-分割文件系统将转换后的MP4进行分割。
function ffSegment($basedir,$res) {
// CREATE THE SEGMENTS
mkdir($basedir.$res, 0775);
exec('/usr/local/bin/ffmpeg -y -analyzeduration 999999999 -i "'.$basedir.$res.'.mp4" -codec copy -map 0 -f segment -segment_list "'.$basedir.$res.'/stream.m3u8" -segment_time 10 -segment_list_type m3u8 -bsf:v h264_mp4toannexb "'.$basedir.$res.'/segment%05d.ts"');
if(file_exists($basedir.$res.'/stream.m3u8')) {
return TRUE;
} else {
return FALSE;
}
}
第3部分-存储数据系统分析生成的m3u8s并将相关信息存储在数据库中。
table :
CREATE TABLE IF NOT EXISTS `data_contenido_archivos` (
`id` bigint(20) unsigned NOT NULL,
`240p` mediumtext NOT NULL,
`480p` mediumtext NOT NULL,
`720p` mediumtext NOT NULL,
`aac` mediumtext NOT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=TokuDB DEFAULT CHARSET=utf8;
解析功能:
function parseHLS($file) {
$return = array();
$i = 0;
$handle = fopen($file, "r");
if($handle) {
while(($line = fgets($handle)) !== FALSE) {
if(strpos($line,"#EXTINF") !== FALSE) {
$return['data'][$i]['inf'] = str_replace(array("#EXTINF:",",","\r","\n"),array("","","",""),$line);
}
if(strpos($line,".ts") !== FALSE) {
$return['data'][$i]['ts'] = str_replace(array(".ts","segment","\r","\n"),array("","","",""),$line);
$i++;
}
if(strpos($line,"TARGETDURATION") !== FALSE) {
$return['duration'] = str_replace(array("#EXT-X-TARGETDURATION:","\r","\n"),array("","","",""),$line);
}
}
fclose($handle);
} else {
$retorno = FALSE;
}
return $return;
}
每个文件的结果都以json编码的字符串形式存储在每个视频大小的相对列中,请注意,我正在存储尽可能少的信息,以最大程度地减少读取时间并使dB尽可能小。在此步骤中,速度并不重要,因为这是在提供文件之前完成的。
第4部分-提供文件系统读取数据库并为每种尺寸提供正确的文件。
为此,我设置了一个m3u8.domain.com,该程序将所有* .m3u8文件发送到PHP解释器,因此我不介意重命名,它仅提供以下文件:
每个文件都是自己的文件,因为如果不同带宽的m3u8都具有相同的名称,则某些播放器会遇到问题。
master.m3u8执行以下操作:
if($sesion !== FALSE) {
$hls = sql("SELECT A.240p,A.480p,A.720p,A.aac,B.duracion FROM data_contenido_archivos A, data_contenido B WHERE A.id = '".limpia($_GET['i'])."' AND B.id = '".limpia($_GET['i'])."'");
if($hls['status'] == "OK") {
$return = "#EXTM3U\n";
if($hls['data'][0]['240p'] != "{}") {
$return .= "#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=440000, RESOLUTION=426x240\n";
$return .= $domains['m3u8']."/240p.m3u8?i=".$id."&s=".$sesion."\n";
}
if($hls['data'][0]['480p'] != "{}") {
$return .= "#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=912000, RESOLUTION=854x480\n";
$return .= $domains['m3u8']."/480p.m3u8?i=".$id."&s=".$sesion."\n";
}
if($hls['data'][0]['720p'] != "{}") {
$return .= "#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1840000, RESOLUTION=1280x720\n";
$return .= $domains['m3u8']."/720p.m3u8?i=".$id."&s=".$sesion."\n";
}
if($hls['data'][0]['aac'] != "{}") {
$retorno .= "#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=64000\n";
$retorno .= $domains['m3u8']."/aac.m3u8?i=".$id."&s=".$sesion."\n";
}
}
}
header("Content-type: application/x-mpegURL");
echo $return;
它会查询数据库以获取所有不同的分辨率,并为每个分辨率返回正确的URL。如果它不存在(在我的方案中设置为{}),则不会得到回应。
然后,每个res.m3u8都会执行以下操作(当被调用时):
if($sesion) {
$hls = sql("SELECT A.240p,B.duracion FROM data_contenido_archivos A, data_contenido B WHERE A.id = '".limpia($_GET['i'])."' AND B.id = '".limpia($_GET['i'])."'");
if($hls['estado'] == "OK") {
$data = json_decode($hls['data'][0]['240p'],TRUE);
// INICIAMOS EL ARCHIVO
$retorno = "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-ALLOW-CACHE:YES\n#EXT-X-TARGETDURATION:".$data['duration']."\n";
// CALCULAMOS EL VENCIMIENTO (1.5x DURACION DEL VIDEO)
$vence = "+".floor($hls['data'][0]['duracion'] / 60)." minutes";
foreach($data['data'] as $k=>$v) {
$retorno .= "#EXTINF:".$v['inf'].",\n";
$retorno .= S3URL("<BUCKET>",$_GET['i']."/240p/segment".$v['ts'].".ts",$vence)."\n";
}
$retorno .= "#EXT-X-ENDLIST\n";
}
}
header("Content-type: application/x-mpegURL");
echo $retorno;
这里发生了一些事情,所以让我解释一下:
a。-首先,脚本检查有效 session ,如果不存在该 session ,则它将为一个10秒钟的小视频提供m3u8,显示“您无权查看此 session ”。
b.-如果 session checkout ,它将查询数据库并获取所有必需的JSON。它还查询另一个表,我在其中存储文件的持续时间,以便填充TARGETDURATION字符串并计算要生成的安全S3 URL的生命周期。我将网址的有效期设置为视频长度的1.5倍,它对我有用,您的体验可能会有所不同。
c.-然后遍历数据库中的每个组,回显EXTINF并为每个组生成一个安全URL。
关于php - 如何通过将URL粘贴到浏览器中来创建动态m3u8?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25407474/