retooling a bit

* allow setting vcodec
* remove the min/max bitrate caps
* allow a non-square-output with --stretch
* cleanup bitrate setup
* don't force audio channels
* allow setting audio sample rate
* don't force preset
This commit is contained in:
Brooke Vibber 2025-10-01 12:44:09 -07:00
commit 96f4cf2f72

130
pack-vid
View file

@ -21,10 +21,11 @@ $options = [
'letterbox' => false, 'letterbox' => false,
'no-audio' => false, 'no-audio' => false,
'audio-bitrate' => 96000, 'audio-bitrate' => 96000,
'audio-channels' => 2, 'audio-rate' => false,
'audio-channels' => false,
'exposure' => '0', // stops 'exposure' => '0', // stops
'peak' => '1000', // '10000' is max 'peak' => '1000', // '10000' is max
'preset' => 'medium', 'preset' => false,
'fps' => '60', 'fps' => '60',
'size' => $maxBytes, 'size' => $maxBytes,
'quality' => 1.0, 'quality' => 1.0,
@ -41,6 +42,8 @@ $options = [
'crop-top' => false, 'crop-top' => false,
'tonemap' => 'hable', 'tonemap' => 'hable',
'color-temperature' => false, 'color-temperature' => false,
'vcodec' => 'libx264',
'stretch' => false,
]; ];
while ( count( $args ) > 0 && substr( $args[0], 0, 2 ) == '--' ) { while ( count( $args ) > 0 && substr( $args[0], 0, 2 ) == '--' ) {
@ -153,8 +156,6 @@ function extractTracks( $streams, $type ) {
} }
function convert( $src, $dest, $options ) { function convert( $src, $dest, $options ) {
$maxBits = 8 * sizify( $options['size'] );
$probe = ffprobe( $src ); $probe = ffprobe( $src );
$videoTracks = extractTracks( $probe->streams, 'video' ); $videoTracks = extractTracks( $probe->streams, 'video' );
@ -198,9 +199,14 @@ function convert( $src, $dest, $options ) {
$keyframeInt = intval( ceil( $duration * 60 ) ); $keyframeInt = intval( ceil( $duration * 60 ) );
} }
$bitrate = floor( $maxBits / $duration );
if ( $options['bitrate'] ) { if ( $options['bitrate'] ) {
$bitrate = sizify( $options['bitrate'] ); $bitrate = sizify( $options['bitrate'] );
} else if ( $options[ 'size' ] ) {
$maxBits = 8 * sizify( $options['size'] );
$bitrate = floor( $maxBits / $duration );
} else {
// Calculate a target bitrate from the size later
$bitrate = null;
} }
@ -208,109 +214,74 @@ function convert( $src, $dest, $options ) {
$audio = [ '-an' ]; $audio = [ '-an' ];
} else { } else {
$audioBitrate = $options[ 'audio-bitrate' ]; $audioBitrate = $options[ 'audio-bitrate' ];
$audioChannels = $options[ 'audio-channels' ];
$audio = [ $audio = [
'-ac', $audioChannels,
'-b:a', $audioBitrate, '-b:a', $audioBitrate,
]; ];
if ( $options['audio-channels'] ) {
$audio[] = '-ac';
$audio[] = $options[ 'audio-channels' ];
}
if ( $options[ 'audio-rate' ] ) {
$audio[] = '-ar';
$audio[] = $options[ 'audio-rate' ];
}
$bitrate -= $audioBitrate; $bitrate -= $audioBitrate;
} }
$bitrate = max( $bitrate, 16000 ); if ( $options[ 'width' ] && $options[ 'height' ] ) {
// Use exact given dimensions.
$frameWidth = intval( $options[ 'width' ] );
$frameHeight = intval( $options[ 'height' ] );
} else {
// Select target resolution from the target bitrate...
if ( !$bitrate ) {
// If we didn't get given one, default to 5Mbits 1080HD
$bitrate = 5000000;
}
$mbits = 1000 * 1000; $mbits = 1000 * 1000;
$base = intval( $mbits * floatval( $options['quality'] ) ); $base = intval( $mbits * floatval( $options['quality'] ) );
if ( $bitrate < 0.125 * $base || $height < 144 ) { if ( $bitrate < 0.125 * $base || $height < 144 ) {
$frameWidth = 256; $frameWidth = 256;
$frameHeight = 144; $frameHeight = 144;
$bitrate = min( $bitrate, $base * 0.25 );
} elseif ( $bitrate < 0.25 * $base || $height < 180 ) { } elseif ( $bitrate < 0.25 * $base || $height < 180 ) {
$frameWidth = 320; $frameWidth = 320;
$frameHeight = 180; $frameHeight = 180;
$bitrate = min( $bitrate, $base * 0.5 );
} elseif ( $bitrate < 0.5 * $base || $height < 288 ) { } elseif ( $bitrate < 0.5 * $base || $height < 288 ) {
$frameWidth = 512; $frameWidth = 512;
$frameHeight = 288; $frameHeight = 288;
$bitrate = min( $bitrate, $base * 0.5 );
} elseif ( $bitrate < 1 * $base || $height < 480 ) { } elseif ( $bitrate < 1 * $base || $height < 480 ) {
$frameWidth = 640; $frameWidth = 640;
$frameHeight = 360; $frameHeight = 360;
$bitrate = min( $bitrate, $base );
} elseif ( $bitrate < 2 * $base || $height < 540) { } elseif ( $bitrate < 2 * $base || $height < 540) {
$frameWidth = 854; $frameWidth = 854;
$frameHeight = 480; $frameHeight = 480;
$bitrate = min( $bitrate, $base * 2 );
} elseif ( $bitrate < 2.5 * $base || $height < 720) { } elseif ( $bitrate < 2.5 * $base || $height < 720) {
$frameWidth = 960; $frameWidth = 960;
$frameHeight = 540; $frameHeight = 540;
$bitrate = min( $bitrate, $base * 2.5 );
} elseif ( $bitrate < 4 * $base || $height < 1080) { } elseif ( $bitrate < 4 * $base || $height < 1080) {
$frameWidth = 1280; $frameWidth = 1280;
$frameHeight = 720; $frameHeight = 720;
$bitrate = min( $bitrate, $base * 4 );
} elseif ( $bitrate < 8 * $base || $height < 1440) { } elseif ( $bitrate < 8 * $base || $height < 1440) {
$frameWidth = 1920; $frameWidth = 1920;
$frameHeight = 1080; $frameHeight = 1080;
$bitrate = min( $bitrate, $base * 8 );
} elseif ( $bitrate < 16 * $base || $height < 2160) { } elseif ( $bitrate < 16 * $base || $height < 2160) {
$frameWidth = 2560; $frameWidth = 2560;
$frameHeight = 1440; $frameHeight = 1440;
$bitrate = min( $bitrate, $base * 16 );
} else { } else {
$frameWidth = 3840; $frameWidth = 3840;
$frameHeight = 2160; $frameHeight = 2160;
$bitrate = min( $bitrate, $base * 32 ); }
} }
$aspect = $width / $height; $aspect = $width / $height;
$pixels = $width * $height; $pixels = $width * $height;
// canonical min rate is 0.125 megabit at 144p if ( $options[ 'stretch' ] ) {
$bitrate = max( $bitrate, 0.125 * $base ); // Use this option when making non-square output
$scaleWidth = $frameWidth;
/* $scaleHeight = $frameHeight;
$minWidth = 640;
$minHeight = 360;
$baseWidth = 854;
$baseHeight = 480;
$pixelsPerBit = ( $baseWidth * $baseHeight ) / $base;
$maxWidth = 1920;
$maxHeight = 1080;
$maxrate = $base * ( $maxWidth * $maxHeight ) / ( $baseWidth * $baseHeight );
$pixels = $bitrate * $pixelsPerBit;
$frameHeight = evenize( sqrt( $pixels / $aspect ) );
$frameWidth = evenize( $frameHeight * $aspect );
if ( $aspect > 16 / 9 ) {
if ( $frameWidth < $minWidth ) {
$frameWidth = $minWidth;
$frameHeight = evenize( $frameWidth / $aspect );
} elseif ( $frameWidth > $maxWidth ) {
$frameWidth = $maxWidth;
$frameHeight = evenize( $frameWidth / $aspect );
$bitrate = min( $bitrate, $maxrate );
}
} else { } else {
if ( $frameHeight < $minHeight ) { // Assumes square pixels
$frameHeight = $minHeight;
$frameWidth = evenize( $frameHeight * $aspect );
} elseif ( $frameWidth > $maxWidth ) {
$frameHeight = $maxHeight;
$frameWidth = evenize( $frameHeight * $aspect );
$bitrate = min( $bitrate, $maxrate );
}
}
*/
if ( $options['width'] ) {
$frameWidth = intval( $options['width'] );
}
if ( $options['height'] ) {
$frameHeight = intval( $options['height'] );
}
$wide = $aspect > ( $frameWidth / $frameHeight ); $wide = $aspect > ( $frameWidth / $frameHeight );
$crop = boolval( $options['crop'] ); $crop = boolval( $options['crop'] );
$letterbox = boolval( $options['letterbox'] ); $letterbox = boolval( $options['letterbox'] );
@ -331,6 +302,7 @@ function convert( $src, $dest, $options ) {
$scaleWidth = evenize( $frameHeight * $aspect ); $scaleWidth = evenize( $frameHeight * $aspect );
} }
} }
}
$exposure = floatval( $options['exposure'] ); $exposure = floatval( $options['exposure'] );
$peakNits = floatval( $options['peak'] ); $peakNits = floatval( $options['peak'] );
@ -376,7 +348,22 @@ function convert( $src, $dest, $options ) {
$fps = $options['fps']; $fps = $options['fps'];
$preset = $options['preset']; if ( $options['preset'] ) {
$preset = [ '-preset', $options[ 'preset' ] ];
} else {
$preset = [];
}
$extension = pathinfo( $dest )[ 'extension' ];
if ( $extension === 'mp4' || $extension === 'mov' ) {
$format = [ '-movflags', '+faststart' ];
} else if ( $extension === 'webm' || $extension === 'mkv' ) {
$format = [ '-cues_to_front', 1 ];
} else {
$format = [];
}
$vcodec = $options['vcodec'];
$tempPrefix = 'pack-vid-passlog' . rand(0,1 << 31); $tempPrefix = 'pack-vid-passlog' . rand(0,1 << 31);
$passlog = tempnam( '.', $tempPrefix ); $passlog = tempnam( '.', $tempPrefix );
@ -386,13 +373,13 @@ function convert( $src, $dest, $options ) {
'-f', 'mp4', '-f', 'mp4',
'-fpsmax', $fps, '-fpsmax', $fps,
'-vf', $vf, '-vf', $vf,
'-c:v', 'libx264', '-c:v', $vcodec,
'-b:v', $bitrate, '-b:v', $bitrate,
'-preset', $preset, ], $preset, [
'-pass', '1', '-pass', '1',
'-passlogfile', $passlog, '-passlogfile', $passlog,
'-g', $keyframeInt, '-g', $keyframeInt,
], $audio, [ '-an',
'-y', '/dev/null' '-y', '/dev/null'
] ) ] )
); );
@ -401,14 +388,13 @@ function convert( $src, $dest, $options ) {
'-i', $src, '-i', $src,
'-vf', $vf, '-vf', $vf,
'-fpsmax', $fps, '-fpsmax', $fps,
'-c:v', 'libx264', '-c:v', $vcodec,
'-b:v', $bitrate, '-b:v', $bitrate,
'-preset', $preset, ], $preset, [
'-pass', '2', '-pass', '2',
'-passlogfile', $passlog, '-passlogfile', $passlog,
'-g', $keyframeInt, '-g', $keyframeInt,
], $audio, [ ], $audio, $format, [
'-movflags', '+faststart',
'-y', $dest '-y', $dest
] ) ] )
); );