Php 从webm 10秒块创建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

我想制作一些流媒体直播功能。我有一个页面,JS捕捉摄像头并将大约10秒的数据块发送到PHP脚本。它使用mediaDevices.getUserMedia()。然后我的PHP脚本捕获这些块并将它们保存为.webm。下一步是将.webm块转换为.ts文件,并最终创建.m3u8播放列表文件。然后将.m3u8播放列表文件链接到观看页面上具有hls扩展名的视频js播放器

.ts和.m3u8文件生成效果很好-当我开始录制并完成时,例如30-40秒后,.m3u8以“#EXT-X-ENDLIST”结束,JS HLS播放器成功播放了整个录制的视频。问题出现在两种情况下:

  • 当正在进行流录制时,会生成.ts文件,并且.m3u8文件会随着下一个.ts不断更新-但是当我尝试通过HLS player播放流时,它会播放第一个块,然后停止加载图标。我可以在浏览器控制台中看到,它下载下一个.ts帧并更新.m3u8文件,但播放器因加载图标而冻结
  • 例如,当流很长时,有20-30个.ts文件(几分钟),我尝试通过HLS播放器播放它-它尝试从n块开始播放实时视频,而不是过去的片段。我可以在浏览器控制台中看到,它从ex.10开始下载。ts文件或更高版本,而不是第一个。但在播放器中,结果是黑屏——它不会开始播放
  • 我认为问题在于将webm转换为ts——肯定有一些选项我没有正确使用或我不知道。或者可能创建的.m3u8文件有问题,这是我的代码(未优化,首先我希望它开始工作):

    编辑:我刚刚发现,当我在getUserMedia中禁用音频流录制时,一切都很好——所以当我只录制视频流时。这很奇怪-就像音频和视频流之间的同步问题一样

    // 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));