WIP got hls with aac and vp9 fixed with init segments
This commit is contained in:
parent
5a4e9aa6bb
commit
f7a7059e39
7 changed files with 81 additions and 69 deletions
|
|
@ -1,14 +1,14 @@
|
|||
#EXTM3U
|
||||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a1",NAME="English",LANGUAGE="en-US",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2",URI="caminandes-llamigos.webm.audio.aac.mp4.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a1",NAME="English",LANGUAGE="en-US",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2",URI="caminandes-llamigos.webm.audio.aac.m3u8"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="a1"
|
||||
caminandes-llamigos.webm.1080p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.1080p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="a1"
|
||||
caminandes-llamigos.webm.720p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.720p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=875000,RESOLUTION=854x480,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="a1"
|
||||
caminandes-llamigos.webm.480p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.480p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=375000,RESOLUTION=640x360,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="a1"
|
||||
caminandes-llamigos.webm.360p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.360p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=275000,RESOLUTION=426x240,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="a1"
|
||||
caminandes-llamigos.webm.240p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.240p.vp9.fast.m3u8
|
||||
|
|
|
|||
|
|
@ -6,24 +6,24 @@
|
|||
|
||||
# Opus-based preferred
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.1080p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.1080p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.720p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.720p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=875000,RESOLUTION=854x480,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.480p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.480p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=375000,RESOLUTION=640x360,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.360p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.360p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=275000,RESOLUTION=426x240,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.240p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.240p.vp9.fast.m3u8
|
||||
|
||||
# MP3 for Safari
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.1080p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.1080p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.720p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.720p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=875000,RESOLUTION=854x480,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.480p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.480p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=375000,RESOLUTION=640x360,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.360p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.360p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=275000,RESOLUTION=426x240,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.240p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.240p.vp9.fast.m3u8
|
||||
|
|
|
|||
|
|
@ -7,36 +7,36 @@
|
|||
|
||||
# Opus-based preferred
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,FRAME-RATE=24.0,CODECS="vp09.00.10.08,Opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.1080p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.1080p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,FRAME-RATE=24.0,CODECS="vp09.00.10.08,Opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.720p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.720p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=875000,RESOLUTION=854x480,FRAME-RATE=24.0,CODECS="vp09.00.10.08,Opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.480p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.480p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=375000,RESOLUTION=640x360,FRAME-RATE=24.0,CODECS="vp09.00.10.08,Opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.360p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.360p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=275000,RESOLUTION=426x240,FRAME-RATE=24.0,CODECS="vp09.00.10.08,Opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.240p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.240p.vp9.fast.m3u8
|
||||
|
||||
# AAC-based temporary experiment (may not be able to use)
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="aac"
|
||||
caminandes-llamigos.webm.1080p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.1080p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="aac"
|
||||
caminandes-llamigos.webm.720p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.720p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=875000,RESOLUTION=854x480,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="aac"
|
||||
caminandes-llamigos.webm.480p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.480p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=375000,RESOLUTION=640x360,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="aac"
|
||||
caminandes-llamigos.webm.360p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.360p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=275000,RESOLUTION=426x240,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.02",AUDIO="aac"
|
||||
caminandes-llamigos.webm.240p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.240p.vp9.fast.m3u8
|
||||
|
||||
# MP3 for Safari
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.1080p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.1080p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.720p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.720p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=875000,RESOLUTION=854x480,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.480p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.480p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=375000,RESOLUTION=640x360,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.360p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.360p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=275000,RESOLUTION=426x240,FRAME-RATE=24.0,CODECS="vp09.00.10.08,mp4a.40.34",AUDIO="mp3"
|
||||
caminandes-llamigos.webm.240p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.240p.vp9.fast.m3u8
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
# Opus-based preferred
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.1080p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.1080p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.720p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.720p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=875000,RESOLUTION=854x480,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.480p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.480p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=375000,RESOLUTION=640x360,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.360p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.360p.vp9.fast.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=275000,RESOLUTION=426x240,FRAME-RATE=24.0,CODECS="vp09.00.10.08,opus",AUDIO="opus"
|
||||
caminandes-llamigos.webm.240p.vp9.pass2.mp4.m3u8
|
||||
caminandes-llamigos.webm.240p.vp9.fast.m3u8
|
||||
|
|
|
|||
24
ogv.html
24
ogv.html
|
|
@ -16,20 +16,19 @@
|
|||
<h2>Caminandes - Llamigos</h2>
|
||||
|
||||
<!--
|
||||
<p>HLS with Opus-in-MP4 and elementary MP3 audio (mp4a.40.34) and VP9-in-MP4 at several resolutions, with a flat WebM VP8/Vorbis fallback:</p>
|
||||
<p>HLS with VP9-in-MP4 video and Opus-in-MP4 and MP3 audio. Flat WebM fallback with ogv.js loader.</p>
|
||||
<video id=hls1 controls width=640 height=360>
|
||||
<source type=application/vnd.apple.mpegurl src=llamigos-vp9-mp3-opus.m3u8>
|
||||
<source type=video/webm src=caminandes-llamigos.webm.flat.webm>
|
||||
</video>
|
||||
|
||||
<p>Expected behavior:</p>
|
||||
<p>Desired behavior:</p>
|
||||
<ul>
|
||||
<li>Firefox/Chrome: play via MSE with VP9/Opus</li>
|
||||
<li>Desktop Safari with VP9 support: play via MSE with VP9/MP3</li>
|
||||
<li>iOS Safari with VP9 support: play native HLS with VP9/MP3</li>
|
||||
<li>Older iOS and desktop Safari without VP9: play via ogv.js with WebM VP8/Vorbis</li>
|
||||
<li>Firefox/Chrome: play via MSE with VP9/Opus (currently issues with using Opus in VHS)</li>
|
||||
<li>Desktop Safari with VP9 support: play via MSE with VP9/MP3 (MP3 buffers not adding spaces as expected)</li>
|
||||
<li>iOS Safari with VP9 support: play native HLS with VP9/MP3 (works)</li>
|
||||
<li>Older iOS and desktop Safari without VP9: play via ogv.js with WebM VP8/Vorbis (?)</li>
|
||||
<li>Very old Firefox/Chrome: play native WebM VP8/Vorbis</li>
|
||||
<li>edge cases: :(</li>
|
||||
</ul>
|
||||
-->
|
||||
|
||||
|
|
@ -50,7 +49,7 @@
|
|||
</ul>
|
||||
-->
|
||||
|
||||
<p>HLS with VP9 video and Opus, AAC, and MP3 audio. Flag WebM fallback.</p>
|
||||
<p>HLS with VP9 video and AAC audio. Flag WebM fallback.</p>
|
||||
<video id=hls1 controls width=640 height=360>
|
||||
<source type=application/vnd.apple.mpegurl src=llamigos-vp9-aac.m3u8>
|
||||
<source type=video/webm src=caminandes-llamigos.webm.flat.webm>
|
||||
|
|
@ -136,7 +135,8 @@
|
|||
var vp9 = can('video/mp4; codecs="vp09.00.10.08"');
|
||||
var opus = can('audio/mp4; codecs="opus"');
|
||||
var aac = can('audio/mp4; codecs="mp4a.40.02')
|
||||
var mp3 = can('audio/mp3');
|
||||
//var mp3 = can('audio/mp3');
|
||||
var mp3 = can('audio/mp4; codecs="mp4a.40.34"');
|
||||
//var mse = vp9 && (opus || mp3);
|
||||
//var mse = vp9 && mp3;
|
||||
var mse = vp9 && aac;
|
||||
|
|
@ -166,11 +166,9 @@
|
|||
(typeof WebAssembly.Module == 'function');
|
||||
if (ogv) {
|
||||
console.log('can do ogvjs');
|
||||
var base = new URL('./ogvjs-1.8.4', document.location.pathname)
|
||||
console.log(base);
|
||||
console.log(base.pathname);
|
||||
var base = '/misc/hls-test/ogvjs-1.8.4';//new URL('./ogvjs-1.8.4', document.location.pathname)
|
||||
playerConfig.ogvjs = {
|
||||
base: base.pathname
|
||||
base: base
|
||||
};
|
||||
console.log(playerConfig.ogvjs.base);
|
||||
playerConfig.techOrder.push('ogvjs');
|
||||
|
|
|
|||
312
transcode-old.php
Normal file
312
transcode-old.php
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
<?php
|
||||
|
||||
function run( $prog, $params ) {
|
||||
$cmd = escapeshellcmd( $prog ) . " " . implode( ' ', array_map( 'escapeshellarg', $params ) );
|
||||
echo "\n$cmd\n\n";
|
||||
$output = [];
|
||||
$code = 0;
|
||||
if ( exec($cmd, $output, $code) === false ) {
|
||||
throw new Exception( 'failed to exec ffmpeg' );
|
||||
}
|
||||
if ( $code ) {
|
||||
throw new Exception( "ffmpeg returned coded $code" );
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
function ffprobe( $file ) {
|
||||
$output = run( 'ffprobe', [ '-hide_banner', '-show_format', '-show_streams', '-print_format', 'json', '--', $file ] );
|
||||
$json = implode( "\n", $output );
|
||||
return json_decode( $json );
|
||||
}
|
||||
|
||||
class Audio {
|
||||
public const FORMATS = [
|
||||
'aac' => [
|
||||
'options' => [
|
||||
'-acodec', 'aac',
|
||||
'-ar', 44100,
|
||||
'-ac', 2,
|
||||
'-b:a', '112k',
|
||||
],
|
||||
],
|
||||
'opus' => [
|
||||
'options' => [
|
||||
'-acodec', 'libopus',
|
||||
'-ar', 48000,
|
||||
'-ac', 2,
|
||||
'-b:a', '96k',
|
||||
],
|
||||
],
|
||||
'mp3' => [
|
||||
'options' => [
|
||||
'-acodec', 'libmp3lame',
|
||||
'-ar', 44100,
|
||||
'-ac', 2,
|
||||
'-b:a', '128k',
|
||||
],
|
||||
],
|
||||
'alac' => [
|
||||
'options' => [
|
||||
'-acodec', 'alac',
|
||||
'-ar', 11025,
|
||||
'-ac', 2,
|
||||
],
|
||||
],
|
||||
'vorbis' => [
|
||||
'options' => [
|
||||
'-acodec', 'libvorbis',
|
||||
'-ar', 44100,
|
||||
'-ac', 2,
|
||||
'-b:a', '112k',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
class Video {
|
||||
// Normalize input frame rates to the next up of these.
|
||||
// Lets us ensure that keyframes are places where they belong.
|
||||
public const RATES = [
|
||||
15, 24, 25, 30, 48, 50, 60
|
||||
];
|
||||
|
||||
public const FORMATS = [
|
||||
'vp9' => [
|
||||
'options' => [
|
||||
'common' => [
|
||||
'-vcodec', 'libvpx-vp9',
|
||||
'-row-mt', '1',
|
||||
],
|
||||
'fast' => [
|
||||
'-quality', 'realtime',
|
||||
'-cpu-used', '5',
|
||||
],
|
||||
'pass1' => [
|
||||
'-quality', 'good',
|
||||
'-cpu-used', '2',
|
||||
'-pass', '1',
|
||||
],
|
||||
'pass2' => [
|
||||
'-quality', 'good',
|
||||
'-cpu-used', '1',
|
||||
'-pass', '2',
|
||||
]
|
||||
],
|
||||
'resolutions' => [
|
||||
'240p' => [
|
||||
'width' => 426,
|
||||
'height' => 240,
|
||||
'bitrate' => '150k',
|
||||
],
|
||||
'360p' => [
|
||||
'width' => 640,
|
||||
'height' => 360,
|
||||
'bitrate' => '250k',
|
||||
],
|
||||
'480p' => [
|
||||
'width' => 854,
|
||||
'height' => 480,
|
||||
'bitrate' => '750k',
|
||||
],
|
||||
'720p' => [
|
||||
'width' => 1280,
|
||||
'height' => 720,
|
||||
'bitrate' => '2500k',
|
||||
],
|
||||
'1080p' => [
|
||||
'width' => 1920,
|
||||
'height' => 1080,
|
||||
'bitrate' => '5000k',
|
||||
],
|
||||
'1440p' => [
|
||||
'width' => 2560,
|
||||
'height' => 1440,
|
||||
'bitrate' => '9000k',
|
||||
],
|
||||
'2160p' => [
|
||||
'width' => 3840,
|
||||
'height' => 2160,
|
||||
'bitrate' => '12500k',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
class Fraction {
|
||||
public $numerator = 0;
|
||||
public $denominator = 0;
|
||||
|
||||
public function __construct( $num, $denom ) {
|
||||
$this->numerator = $num;
|
||||
$this->denominator = $denom;
|
||||
}
|
||||
|
||||
public function toFloat() {
|
||||
return $this->numerator / $this->denominator;
|
||||
}
|
||||
|
||||
public function toString() {
|
||||
return "$this->numerator/$this->denominator";
|
||||
}
|
||||
|
||||
public static function fromString( $frac ) {
|
||||
list ( $num, $denom ) = array_map( 'intval', explode( '/', $frac, 2 ) );
|
||||
return new Fraction( $num, $denom );
|
||||
}
|
||||
}
|
||||
|
||||
class SourceFile {
|
||||
public $filename = '';
|
||||
public $duration = 0.0;
|
||||
|
||||
public $video = false;
|
||||
public $width = 0;
|
||||
public $height = 0;
|
||||
public $fps = null;
|
||||
|
||||
public $audio = false;
|
||||
public $sampleRate = 0;
|
||||
public $channels = 0;
|
||||
|
||||
public function __construct( $filename ) {
|
||||
$this->filename = $filename;
|
||||
|
||||
$data = ffprobe( $filename );
|
||||
|
||||
$this->duration = $data->format->duration;
|
||||
foreach ( $data->streams as $stream ) {
|
||||
if ( $stream->codec_type == 'video' && !$this->video ) {
|
||||
$this->video = true;
|
||||
$this->width = $stream->width;
|
||||
$this->height = $stream->height;
|
||||
$this->fps = Fraction::fromString( $stream->r_frame_rate );
|
||||
}
|
||||
if ( $stream->codec_type === 'audio' && !$this->audio ) {
|
||||
$this->audio = true;
|
||||
$this->sampleRate = $stream->sample_rate;
|
||||
$this->channels = $stream->channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Transcoder {
|
||||
private $source = null;
|
||||
private $fps = 0;
|
||||
private $gop = 0;
|
||||
|
||||
public const SEGMENT_DURATION = 10;
|
||||
|
||||
public function __construct( SourceFile $source ) {
|
||||
$this->source = $source;
|
||||
|
||||
// Normalize input fps to an even standard
|
||||
$infps = $this->source->fps->toFloat();
|
||||
$this->fps = Video::RATES[0];
|
||||
foreach ( Video::RATES as $rate ) {
|
||||
if ( $rate >= $infps ) {
|
||||
$this->fps = $rate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Each self-contained group of pictures starts with a keyframe.
|
||||
$this->gop = $this->fps * self::SEGMENT_DURATION;
|
||||
}
|
||||
|
||||
private function ffmpeg( $options, $outfile, $container ) {
|
||||
if ( $mode === 'pass1' ) {
|
||||
$filename = '/dev/null';
|
||||
$playlist = '/dev/null';
|
||||
} else {
|
||||
$filename = "$outfile.%04d.$container";
|
||||
$playlist = "$outfile.$container.m3u8";
|
||||
}
|
||||
$ffmpegOptions = array_merge( [
|
||||
'-hide_banner',
|
||||
'-i',
|
||||
$this->source->filename,
|
||||
'-f', 'hls',
|
||||
'-hls_segment_type', 'fmp4',
|
||||
'-hls_time', '10',
|
||||
'-hls_playlist_type', 'vod',
|
||||
'-hls_segment_filename', $filename,
|
||||
], $options, [
|
||||
'-y', $playlist
|
||||
] );
|
||||
|
||||
$output = run( 'ffmpeg', $ffmpegOptions );
|
||||
}
|
||||
|
||||
public function video( $codec, $resolution, $mode ) {
|
||||
if ( !$this->source->video ) {
|
||||
throw new Error('no video');
|
||||
}
|
||||
|
||||
$res = Video::FORMATS[$codec]['resolutions'][$resolution];
|
||||
|
||||
$options = array_merge(
|
||||
[
|
||||
'-pix_fmt', 'yuv420p',
|
||||
'-r', $this->fps,
|
||||
],
|
||||
Video::FORMATS[$codec]['options']['common'],
|
||||
Video::FORMATS[$codec]['options'][$mode],
|
||||
[
|
||||
'-vf', "scale=" . implode( ':', [ $res['width'], $res['height'] ] ),
|
||||
'-b:v', $res['bitrate'],
|
||||
'-g', $this->gop,
|
||||
'-keyint_min', $this->gop, // may not be generic enough
|
||||
'-an',
|
||||
]
|
||||
);
|
||||
|
||||
$outfile = "{$this->source->filename}.{$resolution}.{$codec}.{$mode}";
|
||||
$this->ffmpeg( $options, $outfile, "mp4" );
|
||||
}
|
||||
|
||||
public function audio( $codec ) {
|
||||
if ( !$this->source->audio ) {
|
||||
throw new Error('no audio');
|
||||
}
|
||||
|
||||
$format = Audio::FORMATS[$codec];
|
||||
$options = array_merge(
|
||||
$format['options'],
|
||||
[
|
||||
'-vn',
|
||||
]
|
||||
);
|
||||
|
||||
$outfile = "{$this->source->filename}.audio.{$codec}";
|
||||
$this->ffmpeg( $options, $outfile, "mp4" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
$infiles = [
|
||||
'caminandes-llamigos.webm',
|
||||
];
|
||||
|
||||
foreach ( $infiles as $filename ) {
|
||||
$source = new SourceFile( $filename );
|
||||
$codec = new Transcoder( $source );
|
||||
//$codec->audio('opus');
|
||||
$codec->audio('mp3');
|
||||
//$codec->audio('aac');
|
||||
//$codec->audio('alac');
|
||||
//$codec->audio('vorbis');
|
||||
/*
|
||||
foreach ( Video::FORMATS['vp9']['resolutions'] as $res => $format ) {
|
||||
|
||||
if ( $format['width'] <= $source->width && $format['height'] <= $source->height ) {
|
||||
$codec->video('vp9', $res, 'fast');
|
||||
$codec->video('vp9', $res, 'pass1');
|
||||
$codec->video('vp9', $res, 'pass2');
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
@ -66,6 +66,7 @@ class Video {
|
|||
'common' => [
|
||||
'-vcodec', 'libvpx-vp9',
|
||||
'-row-mt', '1',
|
||||
'-tile-columns', '4',
|
||||
],
|
||||
'fast' => [
|
||||
'-quality', 'realtime',
|
||||
|
|
@ -206,29 +207,37 @@ class Transcoder {
|
|||
}
|
||||
|
||||
private function ffmpeg( $options, $outfile, $container ) {
|
||||
$segmentOptions = [];
|
||||
$playlist = "$outfile.m3u8";
|
||||
$init = "$outfile.init.$container";
|
||||
$segment = "$outfile.%04d.$container";
|
||||
if ( $container == 'mp4' ) {
|
||||
$segmentOptions[] = '-segment_format_options';
|
||||
//$segmentOptions[] = 'movflags=+frag_keyframe+empty_moov';
|
||||
$segmentOptions[] = 'movflags=frag_keyframe';
|
||||
}
|
||||
if ( $container == 'mp3' ) {
|
||||
$segmentOptions[] = '-segment_format_options';
|
||||
$segmentOptions[] = 'id3v2_version=0:write_xing=0:write_id3v1=0';
|
||||
// HLS muxer seems to give the right options for fMP4
|
||||
$segmentOptions = [
|
||||
'-f', 'hls',
|
||||
'-hls_segment_type', 'fmp4',
|
||||
'-hls_time', '10',
|
||||
'-hls_playlist_type', 'vod',
|
||||
'-hls_fmp4_init_filename', $init,
|
||||
'-hls_segment_filename', $segment,
|
||||
'-y', $playlist,
|
||||
];
|
||||
} elseif ( $container == 'mp3' ) {
|
||||
// For MP3, segment it raw.
|
||||
// We'll need to postprocess to add an ID3 tag with timestamp.
|
||||
$segmentOptions = [
|
||||
'-f', 'segment',
|
||||
'-segment_format_options', 'id3v2_version=0:write_xing=0:write_id3v1=0',
|
||||
'-segment_time', '10',
|
||||
'-segment_list', $playlist,
|
||||
'-y', $segment
|
||||
];
|
||||
} else {
|
||||
die( 'missing container in config' );
|
||||
}
|
||||
$ffmpegOptions = array_merge( [
|
||||
'-hide_banner',
|
||||
'-i',
|
||||
$this->source->filename,
|
||||
'-f', 'segment',
|
||||
'-segment_time', '10',
|
||||
'-segment_list', "$outfile.m3u8",
|
||||
],
|
||||
$segmentOptions,
|
||||
$options,
|
||||
[
|
||||
'-y', "$outfile.%04d.$container"
|
||||
] );
|
||||
'-i', $this->source->filename,
|
||||
], $options, $segmentOptions);
|
||||
|
||||
$output = run( 'ffmpeg', $ffmpegOptions );
|
||||
}
|
||||
|
|
@ -298,11 +307,16 @@ foreach ( $infiles as $filename ) {
|
|||
$codec->audio('mp3');
|
||||
$codec->audio('aac');
|
||||
foreach ( Video::FORMATS['vp9']['resolutions'] as $res => $format ) {
|
||||
|
||||
if ( $format['width'] <= $source->width && $format['height'] <= $source->height ) {
|
||||
$codec->video('vp9', $res, 'fast');
|
||||
}
|
||||
}
|
||||
/*
|
||||
foreach ( Video::FORMATS['vp9']['resolutions'] as $res => $format ) {
|
||||
if ( $format['width'] <= $source->width && $format['height'] <= $source->height ) {
|
||||
$codec->video('vp9', $res, 'pass1');
|
||||
$codec->video('vp9', $res, 'pass2');
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue