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,
'no-audio' => false,
'audio-bitrate' => 96000,
'audio-channels' => 2,
'audio-rate' => false,
'audio-channels' => false,
'exposure' => '0', // stops
'peak' => '1000', // '10000' is max
'preset' => 'medium',
'preset' => false,
'fps' => '60',
'size' => $maxBytes,
'quality' => 1.0,
@ -41,6 +42,8 @@ $options = [
'crop-top' => false,
'tonemap' => 'hable',
'color-temperature' => false,
'vcodec' => 'libx264',
'stretch' => false,
];
while ( count( $args ) > 0 && substr( $args[0], 0, 2 ) == '--' ) {
@ -153,8 +156,6 @@ function extractTracks( $streams, $type ) {
}
function convert( $src, $dest, $options ) {
$maxBits = 8 * sizify( $options['size'] );
$probe = ffprobe( $src );
$videoTracks = extractTracks( $probe->streams, 'video' );
@ -198,9 +199,14 @@ function convert( $src, $dest, $options ) {
$keyframeInt = intval( ceil( $duration * 60 ) );
}
$bitrate = floor( $maxBits / $duration );
if ( $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' ];
} else {
$audioBitrate = $options[ 'audio-bitrate' ];
$audioChannels = $options[ 'audio-channels' ];
$audio = [
'-ac', $audioChannels,
'-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 = 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;
$base = intval( $mbits * floatval( $options['quality'] ) );
if ( $bitrate < 0.125 * $base || $height < 144 ) {
$frameWidth = 256;
$frameHeight = 144;
$bitrate = min( $bitrate, $base * 0.25 );
} elseif ( $bitrate < 0.25 * $base || $height < 180 ) {
$frameWidth = 320;
$frameHeight = 180;
$bitrate = min( $bitrate, $base * 0.5 );
} elseif ( $bitrate < 0.5 * $base || $height < 288 ) {
$frameWidth = 512;
$frameHeight = 288;
$bitrate = min( $bitrate, $base * 0.5 );
} elseif ( $bitrate < 1 * $base || $height < 480 ) {
$frameWidth = 640;
$frameHeight = 360;
$bitrate = min( $bitrate, $base );
} elseif ( $bitrate < 2 * $base || $height < 540) {
$frameWidth = 854;
$frameHeight = 480;
$bitrate = min( $bitrate, $base * 2 );
} elseif ( $bitrate < 2.5 * $base || $height < 720) {
$frameWidth = 960;
$frameHeight = 540;
$bitrate = min( $bitrate, $base * 2.5 );
} elseif ( $bitrate < 4 * $base || $height < 1080) {
$frameWidth = 1280;
$frameHeight = 720;
$bitrate = min( $bitrate, $base * 4 );
} elseif ( $bitrate < 8 * $base || $height < 1440) {
$frameWidth = 1920;
$frameHeight = 1080;
$bitrate = min( $bitrate, $base * 8 );
} elseif ( $bitrate < 16 * $base || $height < 2160) {
$frameWidth = 2560;
$frameHeight = 1440;
$bitrate = min( $bitrate, $base * 16 );
} else {
$frameWidth = 3840;
$frameHeight = 2160;
$bitrate = min( $bitrate, $base * 32 );
}
}
$aspect = $width / $height;
$pixels = $width * $height;
// canonical min rate is 0.125 megabit at 144p
$bitrate = max( $bitrate, 0.125 * $base );
/*
$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 );
}
if ( $options[ 'stretch' ] ) {
// Use this option when making non-square output
$scaleWidth = $frameWidth;
$scaleHeight = $frameHeight;
} else {
if ( $frameHeight < $minHeight ) {
$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'] );
}
// Assumes square pixels
$wide = $aspect > ( $frameWidth / $frameHeight );
$crop = boolval( $options['crop'] );
$letterbox = boolval( $options['letterbox'] );
@ -331,6 +302,7 @@ function convert( $src, $dest, $options ) {
$scaleWidth = evenize( $frameHeight * $aspect );
}
}
}
$exposure = floatval( $options['exposure'] );
$peakNits = floatval( $options['peak'] );
@ -376,7 +348,22 @@ function convert( $src, $dest, $options ) {
$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);
$passlog = tempnam( '.', $tempPrefix );
@ -386,13 +373,13 @@ function convert( $src, $dest, $options ) {
'-f', 'mp4',
'-fpsmax', $fps,
'-vf', $vf,
'-c:v', 'libx264',
'-c:v', $vcodec,
'-b:v', $bitrate,
'-preset', $preset,
], $preset, [
'-pass', '1',
'-passlogfile', $passlog,
'-g', $keyframeInt,
], $audio, [
'-an',
'-y', '/dev/null'
] )
);
@ -401,14 +388,13 @@ function convert( $src, $dest, $options ) {
'-i', $src,
'-vf', $vf,
'-fpsmax', $fps,
'-c:v', 'libx264',
'-c:v', $vcodec,
'-b:v', $bitrate,
'-preset', $preset,
], $preset, [
'-pass', '2',
'-passlogfile', $passlog,
'-g', $keyframeInt,
], $audio, [
'-movflags', '+faststart',
], $audio, $format, [
'-y', $dest
] )
);