Php 从webm 10秒块创建HLS流
我想制作一些流媒体直播功能。我有一个页面,JS捕捉摄像头并将大约10秒的数据块发送到PHP脚本。它使用mediaDevices.getUserMedia()。然后我的PHP脚本捕获这些块并将它们保存为.webm。下一步是将.webm块转换为.ts文件,并最终创建.m3u8播放列表文件。然后将.m3u8播放列表文件链接到观看页面上具有hls扩展名的视频js播放器 .ts和.m3u8文件生成效果很好-当我开始录制并完成时,例如30-40秒后,.m3u8以“#EXT-X-ENDLIST”结束,JS HLS播放器成功播放了整个录制的视频。问题出现在两种情况下:Php 从webm 10秒块创建HLS流,php,ffmpeg,streaming,http-live-streaming,Php,Ffmpeg,Streaming,Http Live Streaming,我想制作一些流媒体直播功能。我有一个页面,JS捕捉摄像头并将大约10秒的数据块发送到PHP脚本。它使用mediaDevices.getUserMedia()。然后我的PHP脚本捕获这些块并将它们保存为.webm。下一步是将.webm块转换为.ts文件,并最终创建.m3u8播放列表文件。然后将.m3u8播放列表文件链接到观看页面上具有hls扩展名的视频js播放器 .ts和.m3u8文件生成效果很好-当我开始录制并完成时,例如30-40秒后,.m3u8以“#EXT-X-ENDLIST”结束,JS H
// get params - chunk number and stream identifier
$prefix = $_GET['n'];
$num = $_GET['c'];
$isFirst = false;
$isLast = isset($_GET['f']) && $_GET['f'];
if(str_replace('0', '', $num) == '') {
mkdir('videos/'.$prefix);
$isFirst = true;
}
$fp = 'videos\\'.$prefix.'\\'.$num.'.webm';
$msg = 'got '.$fp;
$data = file_get_contents('php://input');
file_put_contents($fp, $data);
// we determine exact chunk length
$detailsTxt = 'videos\\'.$prefix.'\\'.$num.'.txt';
exec("ffmpeg -i .\\$fp -f null - > .\\$detailsTxt 2>&1");
preg_match('/(\d{2}:\d{2}:(\d{2}.\d{2}))/', file_get_contents($detailsTxt), $matches);
$matches = array_filter($matches);
$duration = (float)trim($matches[2], '0');
$durationTxt = 'videos\\'.$prefix.'\\'.$num.'_dur.txt';
file_put_contents($durationTxt, $duration);
// ts convertion
// counting start timestamp
$FPS = 25;
$t0 = 0;
$cursorTxt = 'videos\\'.$prefix.'\\cursor.txt';
if(file_exists($cursorTxt)) $t0 = (float)file_get_contents($cursorTxt);
file_put_contents($cursorTxt, $t0 + $duration /*+ 1/$FPS*/);
$fp2 = str_replace('.webm', '.ts', $fp);
$details2Txt = 'videos\\'.$prefix.'\\'.$num.'_conv.txt';
$convertCommand = [
'ffmpeg',
'-v', 'quiet',
'-loglevel', 'error',
'-i', ".\\$fp",
'-vcodec', 'libx264',
'-acodec', 'aac',
'-r', $FPS,
'-profile:v', 'baseline',
'-b:v', '800k',
'-b:a', '48k',
'-f', 'mpegts',
'-strict', 'experimental',
'-mpegts_copyts', '1',
'-filter:v', 'setpts=PTS+' . $t0 . '/TB', // $t0
'-y',
".\\$fp2",
"> .\\$details2Txt 2>&1"
];
exec(implode(" ", $convertCommand));
// generating m3u8 file
// counting max duration
$maxDuration = $duration;
$durationTxt = 'videos\\'.$prefix.'\\duration.txt';
if(file_exists($durationTxt)) {
$tempDuration = (float)file_get_contents($durationTxt);
if($tempDuration > $maxDuration) $maxDuration = $tempDuration;
else file_put_contents($durationTxt, $maxDuration);
}else file_put_contents($durationTxt, $maxDuration);
file_put_contents($cursorTxt, $t0 + $duration);
$m3u8File = 'videos\\'.$prefix.'\\playlist.m3u8';
$m3u8Content = [];
$counter = 0;
do {
$paddedCounter = str_pad($counter, 5, '0', STR_PAD_LEFT);
$chunkFile = 'videos\\'.$prefix.'\\'.$paddedCounter.'.ts';
if(!file_exists($chunkFile)) break;
$counter++;
}while(true);
$m3u8Content[] = "#EXTM3U";
$m3u8Content[] = "#EXT-X-VERSION:3";
$m3u8Content[] = "#EXT-X-MEDIA-SEQUENCE:$counter";
$m3u8Content[] = "#EXT-X-ALLOW-CACHE:YES";
$m3u8Content[] = "#EXT-X-TARGETDURATION:".ceil($maxDuration);
$m3u8Content[] = "";
$counter = 0;
do {
$paddedCounter = str_pad($counter, 5, '0', STR_PAD_LEFT);
$chunkFile = 'videos\\'.$prefix.'\\'.$paddedCounter.'.ts';
if(!file_exists($chunkFile)) break;
$durationTxt = 'videos\\'.$prefix.'\\'.$paddedCounter.'_dur.txt';
$duration = (float)file_get_contents($durationTxt);
$m3u8Content[] = "#EXTINF:$duration,";
$m3u8Content[] = "$paddedCounter.ts";
$counter++;
}while(true);
if($isLast) $m3u8Content[] = "#EXT-X-ENDLIST";
$m3u8Content[] = "";
file_put_contents($m3u8File, implode("\n", $m3u8Content));