Tdarr_Plugins
Tdarr_Plugins copied to clipboard
Plugin to automatically crop black bars from video stream (Left/Right &| Top/Bottom)
It would be great if there was a lossless crop plugin before another encode plugin runs.
Scenario is 1920x1080 video that is actually 4:3 with black bars.
Seconded. This should probably be broken up into two separate flow modules: Detect if black bars exist, and crop black bars. Also the ability to do this with GPU encoders would be a big plus!
This is a function I currently use in a custom plugin to detect black bars. It takes a bit of time and CPU to actually run, so there's probably a better implementation of the actual crop command. I'm just a monkey with a keyboard.
// Use ffmpeg cropdetect to detect black bars.
// If black bars are detected then set the crop variable to the detected values.
function generate_crop_values(file, otherArguments) {
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const sourceFile = file.meta.SourceFile;
// FFmpeg command to extract crop information and frames from the video
const ffmpegCropCommand = `${otherArguments.ffmpegPath} -ss 120 -i \"${sourceFile}\" -t 9:00 -vf fps=1/2,cropdetect -f null - 2>&1`;
const output = execSync(ffmpegCropCommand);
// Extract crop values from output using regex
const crops = output
.toString()
.match(/crop=\S+/g)
.map((crop) => crop.substring(5));
//Get the most commonly returned number and set that as the crop value
//ffmpeg returns 4 values for cropdetect: width:height:x:y
var crop_w_mode = [];
var crop_h_mode = [];
var crop_x_mode = [];
var crop_y_mode = [];
for (var c = 0; c < crops.length; c++) {
crop = crops[c].split(':');
crop_w_mode.push(parseInt(crop[0]));
crop_h_mode.push(parseInt(crop[1]));
crop_x_mode.push(parseInt(crop[2]));
crop_y_mode.push(parseInt(crop[3]));
}
wMode = findMode(crop_w_mode);
hMode = findMode(crop_h_mode);
xMode = findMode(crop_x_mode);
yMode = findMode(crop_y_mode);
// Return a dict of the crop values
return {w:wMode, h:hMode, x:xMode, y:yMode};
};
Here's an example of how I've implemented it with NVENC hardware acceleration. The response.preset value is a string that's returned to Tdarr at the end of the plugin for the ffmpeg command.
//Set hardware acceleration
if (file.ffProbeData.streams[0].codec_name === 'h264') {
if (main10 === false) {
response.preset += '-hwaccel cuvid -hwaccel_output_format cuda -c:v h264_cuvid '
if (crop_values.x > 10 || crop_values.y > 10) {
response.preset += `-crop ${crop_values.y}x${crop_values.y}x${crop_values.x}x${crop_values.x} <io> -map 0 `;
} else {
response.preset += `<io> -map 0 `;
};
} else {
// If file is 10bit then disable hardware decode as it's unsupported
if (crop_values.x > 10 || crop_values.y > 10) {
response.preset += `<io> -map 0 -vf crop=${crop_values.w}:${crop_values.h}:${crop_values.x}:${crop_values.y},hwupload_cuda `
} else {
response.preset += `<io> -map 0 `
}
};
} else if (file.ffProbeData.streams[0].codec_name === 'hevc') {
if (main10 === false) {
response.preset += '-hwaccel cuvid -hwaccel_output_format cuda -c:v hevc_cuvid '
if (crop_values.x > 10 || crop_values.y > 10) {
response.preset += `-crop ${crop_values.y}x${crop_values.y}x${crop_values.x}x${crop_values.x} <io> -map 0 `;
} else {s
response.preset += `<io> -map 0 `;
};
} else {
// If file is 10bit then disable hardware decode as it's unsupported
if (crop_values.x > 10 || crop_values.y > 10) {
response.preset += `<io> -map 0 -vf crop=${crop_values.w}:${crop_values.h}:${crop_values.x}:${crop_values.y},hwupload_cuda `
} else {
response.preset += `<io> -map 0 `
}
};
} else {
// Unexpected file format, running without hardware decode
if (crop_values.x > 10 || crop_values.y > 10) {
response.preset += `<io> -map 0 -vf crop=${crop_values.w}:${crop_values.h}:${crop_values.x}:${crop_values.y},hwupload_cuda `;
} else {
response.preset += `<io> -map 0 `;
};
};