diff --git a/transcode-old.php b/transcode-old.php deleted file mode 100644 index 01713e1..0000000 --- a/transcode-old.php +++ /dev/null @@ -1,312 +0,0 @@ - [ - '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'); - } - } - */ -} diff --git a/transcode-segment.php b/transcode-segment.php index ac94f48..2478e18 100644 --- a/transcode-segment.php +++ b/transcode-segment.php @@ -32,7 +32,7 @@ class Audio { ], ], 'opus' => [ - 'container' => 'webm', + 'container' => 'mp4', 'options' => [ '-acodec', 'libopus', '-ar', 48000, @@ -231,13 +231,6 @@ class Transcoder { '-segment_list', $playlist, '-y', $segment ]; - } elseif ( $container == 'webm' ) { - $segmentOptions = [ - '-f', 'segment', - '-segment_time', '10', - '-segment_list', $playlist, - '-y', $segment - ]; } else { die( 'missing container in config' ); } @@ -310,10 +303,9 @@ $infiles = [ foreach ( $infiles as $filename ) { $source = new SourceFile( $filename ); $codec = new Transcoder( $source ); - $codec->audio('opus'); - /* - $codec->audio('mp3'); $codec->audio('aac'); + $codec->audio('opus'); + $codec->audio('mp3'); foreach ( Video::FORMATS['vp9']['resolutions'] as $res => $format ) { if ( $format['width'] <= $source->width && $format['height'] <= $source->height ) { $codec->video('vp9', $res, 'fast'); @@ -325,5 +317,4 @@ foreach ( $infiles as $filename ) { $codec->video('vp9', $res, 'pass2'); } } - */ }