У меня есть клиент с тысячами аудио / видео файлов, которые они передают внутри, все они сегментированы (.ts) и сохранены в корзине S3 с соответствующими метаданными в базе данных SQL. Теперь они попросили меня создать два «живых» потока, один для аудио, другой для видео, которые они могут установить и забыть.
Не желая заново сегментировать все или объединять все файлы, которые я пытаюсь взломать, в «живом» m3u8, который просматривает уже существующие файлы (все они закодированы одинаково).
Что я сделал, так это сгенерировал «список воспроизведения радио», который сохраняется в базе данных с 40-секундными интервалами (x3 .ts на m3u8), каждый из которых помечен временем начала и окончания и соответствующим EXT-X-MEDIA-SEQUENCE. Затем я выбираю между NOW () и нажимаю на файл.
Это работает, но иногда время подходит, и оно попадает в ту же группу для первого и последнего файла и буферизируется. У меня есть полный контроль над плеером (VideoJS) и сервером, чтобы это работало.
Это код, который у меня есть до сих пор … как бы я мог сделать эту работу? Я еще не пробовал играть с буферами на vJS (не знаю как …)
Вся основная информация о файле хранится в базе данных, как это
INSERT INTO `contenido_audio_hls` (`id`, `audio_s`, `duration`) VALUES ('f2z7dcwc0l7rleig', '["10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","10.000000","4.100000"]', 10);
Когда создается список воспроизведения, я вынимаю необходимые данные
$radio = sql("SELECT lista_contenido.orden,lista_contenido.contenido,contenido_audio_hls.audio_s FROM lista_listas LEFT JOIN lista_contenido ON (lista_listas.id = lista_contenido.lista) LEFT JOIN contenido_audio_hls ON (lista_contenido.contenido = contenido_audio_hls.id) WHERE (lista_listas.tipo = 'radio') ORDER BY lista_contenido.orden ASC");
foreach($radio['data'] as $k=>$v) {
$arreglo = json_decode($v['audio_s'],TRUE);
foreach($arreglo as $kk=>$vv) {
$puro[] = array("extinf"=>'#EXTINF:'.$vv.',',"id"=>$v['contenido'],"segment"=>$kk);
}
}
Я перебираю их для создания групп
$segundos = 0;
$grupo = 1;
$contador = 1;
foreach($puro as $k=>$v) {
if($segundos <= 30) {
$m3u8[$grupo][] = $puro[$k];
$contador++;
} else {
$m3u8[$grupo][] = $puro[$k];
$grupo = $grupo + $contador;
$segundos = 0;
}
$segundos = $segundos + 10;
}
Затем положите их в свой стол
$largo = 0;
foreach($m3u8 as $k=>$v) {
$ini = sprintf('%02d:%02d:%02d',($largo/3600),($largo/60%60),$largo%60);
$localfin = $largo + 40;
$fin = sprintf('%02d:%02d:%02d',($localfin/3600),($localfin/60%60),$localfin%60);
$query = "INSERT INTO lista_m3u8 (ini,fin,tipo,sequence,data) VALUES('".$ini."','".$fin."','radio','".$k."','".json_encode($v)."')";
sql($query);
$largo = $largo + 40;
}
Что дает мне это
INSERT INTO `lista_m3u8` (`ini`, `fin`, `tipo`, `sequence`, `data`) VALUES ('06:54:00', '06:54:40', 'radio', 580636, '[{"extinf":"#EXTINF:10.000000,","id":"f2z7de0quwgehw23","segment":14},{"extinf":"#EXTINF:10.000000,","id":"f2z7de0quwgehw23","segment":15},{"extinf":"#EXTINF:10.000000,","id":"f2z7de0quwgehw23","segment":16},{"extinf":"#EXTINF:10.000000,","id":"f2z7de0quwgehw23","segment":17}]');
INSERT INTO `lista_m3u8` (`ini`, `fin`, `tipo`, `sequence`, `data`) VALUES ('06:54:40', '06:55:20', 'radio', 582504, '[{"extinf":"#EXTINF:10.000000,","id":"f2z7de0quwgehw23","segment":18},{"extinf":"#EXTINF:10.000000,","id":"f2z7de0quwgehw23","segment":19},{"extinf":"#EXTINF:0.766667,","id":"f2z7de0quwgehw23","segment":20},{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":0}]');
INSERT INTO `lista_m3u8` (`ini`, `fin`, `tipo`, `sequence`, `data`) VALUES ('06:55:20', '06:56:00', 'radio', 584375, '[{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":1},{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":2},{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":3},{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":4}]');
INSERT INTO `lista_m3u8` (`ini`, `fin`, `tipo`, `sequence`, `data`) VALUES ('06:56:00', '06:56:40', 'radio', 586249, '[{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":5},{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":6},{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":7},{"extinf":"#EXTINF:10.000000,","id":"f2z7dft8c217xyp","segment":8}]');
Затем генерируется m3u8
$audio = sql("SELECT sequence, data FROM lista_m3u8 WHERE tipo = 'radio' AND ini <= DATE_FORMAT(NOW(),'%H:%i:%s') AND fin >= DATE_FORMAT(NOW(),'%H:%i:%s')");
$sale = '#EXTM3U'.PHP_EOL;
$sale .= '#EXT-X-VERSION:3'.PHP_EOL;
$sale .= '#EXT-X-MEDIA-SEQUENCE:'.$audio['data'][0]['sequence'].PHP_EOL;
$sale .= '#EXT-X-TARGETDURATION:10'.PHP_EOL;
$arreglo = json_decode($audio['data'][0]['data'],TRUE);
foreach($arreglo as $k=>$v) {
$sale .= $v['extinf'].PHP_EOL;
$sale .= S3URL("bucket-audio",$v['id']."/segment".sprintf('%05d',$v['segment']).".ts",(count($arreglo) * 25)).PHP_EOL;
}
header("Content-type: application/x-mpegURL");
echo $sale.PHP_EOL;
Я считаю, что я решил это. Я только что вернулся из игры в течение последних 16 часов, и она все еще продолжается, мои журналы AWS подтверждают это.
Первоначально я подходил к этому неправильно, пытаясь сгенерировать консервированные файлы m3u8; мне действительно нужно было знать две вещи:
1.- Какой сегмент (независимо от исходного файла) должен воспроизводиться сейчас?
2.- Сколько сегментов было бы воспроизведено с начала «потока» (файл 0, сегмент 0)?
Новый метод теперь берет исходный список воспроизведения и создает строку для каждого сегмента, указывая его время начала, продолжительность, файл сегмента и позицию в потоке. Затем генерируется m3u8 с несколькими сегментами позади и несколькими после, вычисляя правильную EXT-X-MEDIA-SEQUENCE с начала потока. Я также добавил EXT-X-DISCONTINUITY между файлами, чтобы он не зависал при получении неожиданных заголовков.
Итак, теперь я получаю список файлов / сегментов из моей исходной таблицы:
$ini = 0;
$conteo = 0;
$radio = sql("SELECT lista_contenido.orden,lista_contenido.contenido,contenido_audio_hls.audio_s FROM lista_listas LEFT JOIN lista_contenido ON (lista_listas.id = lista_contenido.lista) LEFT JOIN contenido_audio_hls ON (lista_contenido.contenido = contenido_audio_hls.id) WHERE (lista_listas.tipo = 'radio') ORDER BY lista_contenido.orden ASC");
foreach($radio['data'] as $k=>$v) {
$arreglo = json_decode($v['audio_s'],TRUE);
$seg = 0;
foreach($arreglo as $kk=>$vv) {
sql("INSERT INTO lista_m3u8 (tipo,orden,contenido,segmento,extinf,ini) VALUES('radio','".$conteo."','".$v['contenido']."','segment".sprintf('%05d',$seg).".ts','".$vv."','".sprintf('%02d:%02d:%02d',($ini/3600),($ini/60%60),$ini%60)."')");
$ini = $ini + ceil($vv * 1);
$seg++;
$conteo++;
}
}
Что дает мне таблицу примерно так:
INSERT INTO `lista_m3u8` (`tipo`, `orden`, `contenido`, `segmento`, `extinf`, `ini`) VALUES ('radio', 20, 'f2z7ddw7r6bb7gfy', 'segment00018.ts', 10.000000000000, '00:03:11');
INSERT INTO `lista_m3u8` (`tipo`, `orden`, `contenido`, `segmento`, `extinf`, `ini`) VALUES ('radio', 21, 'f2z7ddw7r6bb7gfy', 'segment00019.ts', 10.000000000000, '00:03:21');
INSERT INTO `lista_m3u8` (`tipo`, `orden`, `contenido`, `segmento`, `extinf`, `ini`) VALUES ('radio', 22, 'f2z7ddw7r6bb7gfy', 'segment00020.ts', 6.066667079926, '00:03:31');
INSERT INTO `lista_m3u8` (`tipo`, `orden`, `contenido`, `segmento`, `extinf`, `ini`) VALUES ('radio', 23, 'f2z7df1bb66be7h3', 'segment00000.ts', 10.000000000000, '00:03:38');
INSERT INTO `lista_m3u8` (`tipo`, `orden`, `contenido`, `segmento`, `extinf`, `ini`) VALUES ('radio', 24, 'f2z7df1bb66be7h3', 'segment00001.ts', 10.000000000000, '00:03:48');
INSERT INTO `lista_m3u8` (`tipo`, `orden`, `contenido`, `segmento`, `extinf`, `ini`) VALUES ('radio', 25, 'f2z7df1bb66be7h3', 'segment00002.ts', 10.000000000000, '00:03:58');
INSERT INTO `lista_m3u8` (`tipo`, `orden`, `contenido`, `segmento`, `extinf`, `ini`) VALUES ('radio', 26, 'f2z7df1bb66be7h3', 'segment00003.ts', 10.000000000000, '00:04:08');
Это создает тысячи строк дБ (~ 9000 для 24-часового радиопотока), но они индексируются по времени, поэтому SELECTing происходит мгновенно.
Последний скрипт m3u8 делает это:
$actual = sql("SELECT orden, extinf, contenido, segmento, ini FROM lista_m3u8 WHERE tipo = 'radio' AND ini >= DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 MINUTE),'%H:%i:%s') AND ini <= DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 3 MINUTE),'%H:%i:%s') ORDER BY orden ASC");
$sale = '#EXTM3U'.PHP_EOL;
$sale .= '#EXT-X-VERSION:3'.PHP_EOL;
$sale .= '#EXT-X-MEDIA-SEQUENCE:'.($actual['data'][$actual['total']-1]['orden'] - $actual['total']).PHP_EOL;
$sale .= '#EXT-X-TARGETDURATION:10'.PHP_EOL;
$contenido = $actual['data'][0]['contenido'];
foreach($actual['data'] as $k=>$v) {
if($v['contenido'] != $contenido) { $sale .= "#EXT-X-DISCONTINUITY".PHP_EOL; }
$sale .= "#EXTINF:".$v['extinf'].",".PHP_EOL;
$sale .= S3URL("audio-bucket",$v['contenido']."/".$v['segmento'],180).PHP_EOL;
$contenido = $v['contenido'];
}
header("Content-type: application/x-mpegURL");
echo $sale.PHP_EOL;
Обратите внимание на две вещи, происходящие здесь: EXT-X-MEDIA-SEQUENCE рассчитывается путем вычитания позиции текущего сегмента из списка в целом, а XT-X-DISCONTINUITY помещается между изменениями файла.
Я собираюсь провести еще какое-то тестирование, чтобы увидеть, работает ли это в разных браузерах (я пока тестировал только Chrome и IEG); но я считаю, что это работоспособное решение.
Других решений пока нет …