Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // This is a modified script using the "Boosh-Transcode using NVENC GPU & FFMPEG"
- // and "Remove letterbox script" by control#0405
- // This script is WIP and improves the logic for cropping black borders
- // It is currently hardcoded to use only NVENC hardware decoding
- // Future expansion will include an option to select the hardware decoder for NVENC
- // This script is designed to set video files to HEVC mkv and remove black borders
- const details = () => ({
- id: 'Tdarr_Plugin_Himea_NVENC_Optimizer',
- Stage: 'Pre-processing',
- Name: 'Himea NVENC Optimizer',
- Type: 'Video',
- Operation: 'Transcode',
- Description: `This is a NVENC specific plugin. 8th+ gen Nvidia NVENC enabled CPUs are required.
- Files not in H265/HEVC will be transcoded into H265/HEVC using NVENC. Files already in H265/HEVC will be
- via Nvidia GPU using FFmpeg. Settings are dependant on file bitrate working by the logic that H265 can support
- the same amount of data at half the bitrate of H264. This plugin will skip files already in HEVC, AV1 & VP9
- unless "reconvert_hevc" is marked as true. If it is then these will be reconverted again into HEVC if they
- exceed the bitrate specified in "hevc_max_bitrate".
- NOTE - Created for use with UNRAID Docker and while it should support Windows/Mac etc, it may require
- a custom version of FFmpeg to work properly.`,
- Version: '1.0',
- Tags: 'pre-processing,ffmpeg,video only,NVENC,h265,hevc,configurable',
- Inputs: [
- {
- name: 'container',
- type: 'string',
- defaultValue: 'mkv',
- inputUI: {
- type: 'dropdown',
- options: [
- 'mkv',
- 'mp4',
- ],
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Specifies the output container of the file.
- \\n Ensure that all stream types you may have are supported by your chosen container.
- \\n
- ==INFO==
- \\n Only MP4 & MKV are supported and MKV is recommended.
- \\nExample:\\n
- mkv
- \\nExample:\\n
- mp4`,
- },
- {
- name: 'force_conform',
- type: 'boolean',
- defaultValue: false,
- inputUI: {
- type: 'dropdown',
- options: [
- 'false',
- 'true',
- ],
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Make the file conform to output containers requirements.
- Use if you need to ensure the encode works from mp4>mkv or mkv>mp4. \\n
- ==WARNING== \\n
- This will remove data of certain types so ensure you are happy with that,
- or use another plugin to convert these data types first!
- \\n
- ==INFO==
- \\n Drop hdmv_pgs_subtitle/eia_608/subrip/timed_id3 for MP4.
- \\n Drop data streams/mov_text/eia_608/timed_id3 for MKV.
- \\n Default is false.
- \\nExample:\\n
- true
- \\nExample:\\n
- false`,
- },
- {
- name: 'output_codec',
- type: 'string',
- defaultValue: 'h265',
- inputUI: {
- type: 'dropdown',
- options: [
- 'h264',
- 'h265'
- ],
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Specify which codec to output as. h265 is recommended for 4k, h264 for 1080p.`
- },
- {
- name: 'encoder_speedpreset',
- type: 'string',
- defaultValue: 'slow',
- inputUI: {
- type: 'dropdown',
- options: [
- 'veryfast',
- 'faster',
- 'fast',
- 'medium',
- 'slow',
- 'slower',
- 'veryslow',
- ],
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Specify the encoder speed/preset to use.
- Slower options mean a slower encode but better quality and faster options mean faster encodes but
- worse quality.
- \\n For more information see Nvidia white paper on FFmpeg results using NVENC: \\n`
- // eslint-disable-next-line max-len
- + `https://www.Nvidia.com/content/dam/www/public/us/en/documents/white-papers/cloud-computing-quicksync-video-ffmpeg-white-paper.pdf
- \\n
- ==INFO==
- \\n Default is "slow".
- \\nExample:\\n
- medium
- \\nExample:\\n
- slower`,
- },
- {
- name: 'extra_NVENC_options',
- type: 'string',
- defaultValue: '',
- inputUI: {
- type: 'text',
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Here you can add extra options to the FFmpeg NVENC ENCODE cmd.
- This does not override the FFmpeg cmd, it just allows additions to it.
- \\n
- There are extra NVENC options that can be
- forced on/off as desired. See here for some possible cmds -
- https://ffmpeg.org/ffmpeg-codecs.html#toc-HEVC-Options-1
- \\n
- ==WARNING== \\n
- Just because a cmd is mentioned doesn't mean your installed version of FFmpeg supports it...
- Be certain to verify the cmds work before adding to your workflow. \\n
- Check Tdarr Help Tab. Enter FFmpeg cmd - "-h encoder=hevc_NVENC". This will give a list of supported commands.
- \\n
- ==INFO==
- \\n Default is empty but a suggested value is below. If unsure just leave empty.
- \\n Ensure to only use cmds valid to encoding NVENC as the script handles other FFmpeg cmds relating to
- bitrate etc. Anything else entered here might be supported but could cause undesired results.
- \\nExample:\\n
- -extbrc 1 -rdo 1 -mbbrc 1 -b_strategy 1 -adaptive_i 1 -adaptive_b 1`,
- },
- {
- name: 'bitrate_cutoff',
- type: 'number',
- defaultValue: 1500,
- inputUI: {
- type: 'text',
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Specify bitrate cutoff, files with a total bitrate lower then this will not be processed. \n
- Since getting the bitrate of the video from files is unreliable, bitrate here refers to the total
- bitrate of the file and not just the video steam.
- \\n
- ==INFO==
- \\n Rate is in kbps.
- \\n Defaults to 0 which means this is disabled.
- \\n Enter a valid number to enable.
- \\nExample:\\n
- 2500
- \\nExample:\\n
- 1500`,
- },
- {
- name: 'max_average_bitrate',
- type: 'number',
- defaultValue: 0,
- inputUI: {
- type: 'text',
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Specify a maximum average video bitrate. When encoding we take the current total bitrate and halve it
- to get an average target. This option sets a upper limit to that average
- (i.e if you have a video bitrate of 10000, half is 5000, if your maximum desired average bitrate is 4000
- then we use that as the target instead of 5000).
- \\n
- ==INFO==
- \\n Bitrate here is referring to video bitrate as we want to set the video bitrate on encode.
- \\n Rate is in kbps.
- \\n Defaults to 0 which means this is disabled.
- \\n Enter a valid number to enable.
- \\nExample:\\n
- 4000
- \\nExample:\\n
- 3000`,
- },
- {
- name: 'min_average_bitrate',
- type: 'number',
- defaultValue: 0,
- inputUI: {
- type: 'text',
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Specify a minimum average video bitrate. When encoding we take the current total bitrate and halve
- it to get an average target. This option sets a lower limit to that average (i.e if you have a video bitrate
- of 3000, half is 1500, if your minimum desired average bitrate is 2000 then we use that as the target instead
- of 1500).
- \\n
- ==INFO==
- \\n Bitrate here is referring to video bitrate as we want to set the video bitrate on encode.
- \\n Rate is in kbps.
- \\n Defaults to 0 which means this is disabled.
- \\n Enter a valid number to enable.
- \\nExample:\\n
- 2000
- \\nExample:\\n
- 1000`,
- },
- {
- name: 'reconvert_hevc',
- type: 'boolean',
- defaultValue: false,
- inputUI: {
- type: 'dropdown',
- options: [
- 'false',
- 'true',
- ],
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Specify if we want to reprocess HEVC, VP9 or AV1 files
- (i.e reduce bitrate of files already in those codecs).
- \\n Since this uses the same logic as normal, halving the current bitrate, this is NOT recommended
- unless you know what you are doing, so leave false if unsure.
- NEEDS to be used in conjunction with "bitrate_cutoff" or "hevc_max_bitrate" otherwise is ignored.
- This is useful in certain situations, perhaps you have a file which is HEVC but has an extremely high
- bitrate and you'd like to reduce it.
- \\n Bare in mind that you can convert a file to HEVC and still be above your cutoff meaning it would
- be converted again if this is set to true (since it's now HEVC). So if you use this be sure to set
- "hevc_max_bitrate" & "max_average_bitrate" to prevent the plugin looping. Also it is highly suggested
- that you have your "hevc_max_bitrate" higher than "max_average_bitrate".
- \\n Again if you are unsure, please leave this as false!
- \\n
- ==WARNING== \\n
- IF YOU HAVE VP9 OR AV1 FILES YOU WANT TO KEEP IN THOSE FORMATS THEN DO NOT USE THIS OPTION.
- \\n
- \\nExample:\\n
- true
- \\nExample:\\n
- false`,
- },
- {
- name: 'hevc_max_bitrate',
- type: 'number',
- defaultValue: 0,
- inputUI: {
- type: 'text',
- },
- tooltip: `\\n
- ==DESCRIPTION==
- \\n Has no effect unless "reconvert_hevc" is set to true. This allows you to specify a maximum
- allowed average bitrate for HEVC or similar files. Much like the "bitrate_cutoff" option, but
- specifically for HEVC files. It should be set HIGHER then your standard cutoff for safety.
- \\n Also, it's highly suggested you use the min & max average bitrate options in combination with this. You
- will want those to control the bitrate otherwise you may end up repeatedly reprocessing HEVC files.
- i.e your file might have a bitrate of 20000, if your hevc cutoff is 5000 then it's going to reconvert
- multiple times before it'll fall below that cutoff. While HEVC reprocessing can be useful
- this is why it is NOT recommended!
- \\n As with the cutoff, getting the bitrate of the video from files is unreliable, so bitrate
- here refers to the total bitrate of the file and not just the video steam.
- \\n
- ==INFO==
- \\n Rate is in kbps.
- \\n Defaults to 0 which means this is disabled.
- \\n Enter a valid number to enable, otherwise we use "bitrate_cutoff" and multiply x2 for a safe limit.
- \\nExample:\\n
- 4000
- \\nExample:\\n
- 3000`,
- },
- ],
- });
- // eslint-disable-next-line no-unused-vars
- const plugin = (file, librarySettings, inputs, otherArguments) => {
- const lib = require('../methods/lib')(); const os = require('os');
- // eslint-disable-next-line no-unused-vars,no-param-reassign
- inputs = lib.loadDefaultValues(inputs, details);
- const response = {
- processFile: false,
- preset: '',
- handBrakeMode: false,
- FFmpegMode: true,
- infoLog: '',
- container: `.${inputs.container}`
- };
- // Set up required variables.
- let duration = 0;
- let videoIdx = 0;
- let extraArguments = '';
- let bitrateSettings = '';
- let inflatedCutoff = 0;
- let main10 = false;
- let cutoff = false;
- // Check if file is a video. If it isn't then exit plugin.
- if (file.fileMedium !== 'video') {
- response.infoLog += 'File is not a video. \n';
- return response;
- }
- // Check if duration info is filled, if so times it by 0.0166667 to get time in minutes.
- // If not filled then get duration of stream 0 and do the same.
- if (parseFloat(file.ffProbeData?.format?.duration) > 0) {
- duration = parseFloat(file.ffProbeData?.format?.duration) * 0.0166667;
- } else if (typeof file.meta.Duration !== 'undefined') {
- duration = file.meta.Duration * 0.0166667;
- } else {
- duration = file.ffProbeData.streams[0].duration * 0.0166667;
- }
- // Work out currentBitrate using "Bitrate = file size / (number of minutes * .0075)"
- // Used from here https://blog.frame.io/2017/03/06/calculate-video-bitrates/
- const currentBitrate = Math.round(file.file_size / (duration * 0.0075));
- // Use the same calculation used for currentBitrate but divide it in half to get targetBitrate.
- // Logic of h265 can be half the bitrate as h264 without losing quality.
- let targetBitrate = Math.round((file.file_size / (duration * 0.0075)) / 2);
- // Allow some leeway under and over the targetBitrate.
- let minimumBitrate = Math.round(targetBitrate * 0.75);
- let maximumBitrate = Math.round(targetBitrate * 1.25);
- response.infoLog += `☑ It looks like the current bitrate is ${currentBitrate}k. \n`;
- // Check if the file needs to be cropped or if file is in 10bit (Plex does not support 10bit HEVC)
- crop_values = generate_crop_values(file, Math.round(duration/0.0166667), otherArguments);
- response.infoLog += `Crop values detected: ${crop_values.w}:${crop_values.h}:${crop_values.x}:${crop_values.y} \n`;
- if (crop_values.x > 10 || crop_values.y > 10) {
- response.infoLog += `☒ File needs to be cropped. Skipping bitrate cutoffs. \n`;
- for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
- // If files are already 10bit then disable hardware decode to avoid problems with encode
- // 10 bit from source file should be retained without extra arguments.
- if (file.ffProbeData.streams[i].profile === 'High 10'
- || file.ffProbeData.streams[i].profile === 'Main 10'
- || file.ffProbeData.streams[i].bits_per_raw_sample === '10') {
- main10 = true;
- response.infoLog += 'Input file is 10bit. \n\n';
- };
- };
- } else {
- // If targetBitrate or currentBitrate comes out as 0 then something
- // has gone wrong and bitrates could not be calculated.
- // Cancel plugin completely.
- if (targetBitrate <= 0 || currentBitrate <= 0) {
- throw new Error('Target bitrate could not be calculated. Skipping this plugin.');
- }
- // If targetBitrate is equal or greater than currentBitrate then something
- // has gone wrong as that is not what we want.
- // Cancel plugin completely.
- if (targetBitrate >= currentBitrate) {
- throw new Error("☒ Target bitrate has been calculated as ${targetBitrate}k. This is equal or greater than the current bitrate... Something has gone wrong and this shouldn't happen! Skipping this plugin.");
- }
- // Ensure that bitrate_cutoff is set if reconvert_hevc is true since we need some protection against a loop
- // Cancel the plugin
- if (inputs.reconvert_hevc === true && inputs.bitrate_cutoff <= 0 && inputs.hevc_max_bitrate <= 0 && crop_values == '0:0:0:0') {
- throw new Error(`☒ Reconvert HEVC is ${inputs.reconvert_hevc}, however there is no bitrate cutoff or HEVC specific cutoff set so we have no way to know when to stop processing this file.
- Either set reconvert_HEVC to false or set a bitrate cutoff and set a hevc_max_bitrate cutoff. Skipping this plugin.`);
- }
- // Check if inputs.bitrate cutoff has something entered.
- // (Entered means user actually wants something to happen, empty would disable this).
- if (inputs.bitrate_cutoff > 0) {
- // Checks if currentBitrate is below inputs.bitrate_cutoff.
- // If so then cancel plugin without touching original files.
- if (currentBitrate <= inputs.bitrate_cutoff) {
- response.infoLog += `☑ Current bitrate is below set cutoff of ${inputs.bitrate_cutoff}k. Cancelling plugin. \n`;
- return response;
- }
- // If above cutoff then carry on
- if (currentBitrate > inputs.bitrate_cutoff && inputs.reconvert_hevc === false) {
- response.infoLog += `☒ Current bitrate appears to be above the cutoff of ${inputs.bitrate_cutoff}k. Need to process \n`;
- cutoff = true;
- }
- }
- if (inputs.max_average_bitrate > 0) {
- // Checks if targetBitrate is above inputs.max_average_bitrate.
- // If so then clamp target bitrate
- if (targetBitrate > inputs.max_average_bitrate) {
- response.infoLog += `Our target bitrate is above the max_average_bitrate so
- target average bitrate clamped at max of ${inputs.max_average_bitrate}k. \n`;
- targetBitrate = Math.round(inputs.max_average_bitrate);
- minimumBitrate = Math.round(targetBitrate * 0.75);
- maximumBitrate = Math.round(targetBitrate * 1.25);
- }
- }
- // Check if inputs.min_average_bitrate has something entered.
- // (Entered means user actually wants something to happen, empty would disable this).
- if (inputs.min_average_bitrate > 0) {
- // Exit the plugin is the cutoff is less than the min average bitrate. Most likely user error
- if (inputs.bitrate_cutoff < inputs.min_average_bitrate) {
- response.infoLog += `☒ Bitrate cutoff ${inputs.bitrate_cutoff}k is less than the set minimum
- average bitrate set of ${inputs.min_average_bitrate}k. We don't want this. Cancelling plugin. \n`;
- return response;
- }
- // Checks if inputs.bitrate_cutoff is below inputs.min_average_bitrate.
- // If so then set currentBitrate to the minimum allowed.)
- if (targetBitrate < inputs.min_average_bitrate) {
- response.infoLog += `Target average bitrate clamped at min of ${inputs.min_average_bitrate}k. \n`;
- targetBitrate = Math.round(inputs.min_average_bitrate);
- minimumBitrate = Math.round(targetBitrate * 0.75);
- maximumBitrate = Math.round(targetBitrate * 1.25);
- }
- }
- // Go through each stream in the file.
- for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
- // Check if stream is a video.
- if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'video') {
- // Check if codec of stream is mjpeg/png, if so then remove this "video" stream.
- // mjpeg/png are usually embedded pictures that can cause havoc with plugins.
- if (file.ffProbeData.streams[i].codec_name === 'mjpeg' || file.ffProbeData.streams[i].codec_name === 'png') {
- extraArguments += `-map -v:${videoIdx} `;
- }
- // Check if video is already cropped and valid format.
- if (crop_values.x < 10 && crop_values.y < 10) {
- // Check if codec of stream is HEVC AND check if file.container matches inputs.container.
- // If so nothing for plugin to do.
- if ((file.ffProbeData.streams[i].codec_name === 'hevc' || file.ffProbeData.streams[i].codec_name === 'h264') && file.container === inputs.container && cutoff === false) {
- response.infoLog += `☑ File is already HEVC or H264 & in ${inputs.container} and meets all cutoffs. \n`;
- return response;
- }
- // Check if codec is in valid decode format
- if (file.ffProbeData.streams[i].codec_name === 'vp9' || file.ffProbeData.streams[i].codec_name === 'av1') {
- throw new Error('VP9 or AV1 detected. This plugin does not support VP9 or AV1 decoding. Please use a different plugin.');
- };
- // Check if codec of stream is HEVC, Vp9 or AV1
- // AND check if file.container does NOT match inputs.container.
- // If so remux file.
- if ((file.ffProbeData.streams[i].codec_name === 'hevc' || file.ffProbeData.streams[i].codec_name === 'h264') && file.container !== inputs.container) {
- response.infoLog += `☒ File is HEVC but is not in ${inputs.container} container. Remuxing. \n`;
- response.preset = `<io> -map 0 -c copy ${extraArguments}`;
- response.processFile = true;
- return response;
- }
- // New logic for reprocessing HEVC. Mainly done for my own use. Since we're reprocessing we're checking
- // bitrate again and since this can be inaccurate (It calculates overall bitrate not video specific)
- // we have to inflate the current bitrate so we don't keep looping this logic.
- } else if (inputs.reconvert_hevc === true && (file.ffProbeData.streams[i].codec_name === 'hevc' || file.ffProbeData.streams[i].codec_name === 'h264') && crop_values.x < 10 && crop_values.y < 10) {
- // If we're using the hevc max bitrate then update the cutoff to use it
- if (inputs.hevc_max_bitrate > 0) {
- if (currentBitrate > inputs.hevc_max_bitrate) {
- // If bitrate is higher then hevc_max_bitrate then need to re-encode
- response.infoLog += `☒ Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC or H254.
- Using HEVC specific cutoff of ${inputs.hevc_max_bitrate}k. \n
- ☒ The file is still above this new cutoff! Reconverting. \n\n`;
- } else {
- // Otherwise we're now below the hevc cutoff and we can exit
- response.infoLog += `☑ Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC or H264.
- Using HEVC specific cutoff of ${inputs.hevc_max_bitrate}k. \n
- ☑ The file is NOT above this new cutoff. Exiting plugin. \n\n`;
- return response;
- }
- // If we're not using the hevc max bitrate then we need a safety net to try and ensure we don't keep
- // looping this plugin. For maximum safety we simply multiply the cutoff by 2.
- } else if (currentBitrate > (inputs.bitrate_cutoff * 2)) {
- inflatedCutoff = Math.round(inputs.bitrate_cutoff * 2);
- response.infoLog += `☒ Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC or H264.
- HEVC specific cutoff not set so bitrate_cutoff is multiplied by 2 for safety!
- Cutoff now temporarily ${inflatedCutoff}k. \n The file is still above this new cutoff! Reconverting. \n\n`;
- } else {
- // File is below cutoff so we can exit
- inflatedCutoff = Math.round(inputs.bitrate_cutoff * 2);
- response.infoLog += `☑ Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, H264
- so bitrate_cutoff is multiplied by 2! Cutoff now temporarily ${inflatedCutoff}k. \n
- The file is NOT above this new cutoff. Exiting plugin. \n\n`;
- return response;
- };
- };
- // If files are already 10bit then disable hardware decode to avoid problems with encode
- // 10 bit from source file should be retained without extra arguments.
- if (file.ffProbeData.streams[i].profile === 'High 10'
- || file.ffProbeData.streams[i].profile === 'Main 10'
- || file.ffProbeData.streams[i].bits_per_raw_sample === '10') {
- main10 = true;
- response.infoLog += 'Input file is 10bit. \n\n';
- };
- // Increment video index. Needed to keep track of video id in case there is more than one video track.
- // (i.e png or mjpeg which we would remove at the start of the loop)
- videoIdx += 1;
- };
- };
- };
- // It's possible to remux or flat out convert from mp4 to mkv so we need to conform to standards
- // So check streams and add any extra parameters required to make file conform with output format.
- // i.e drop mov_text for mkv files and drop pgs_subtitles for mp4
- if (inputs.force_conform === true) {
- if (inputs.container.toLowerCase() === 'mkv') {
- extraArguments += '-map -0:d ';
- for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
- try {
- if (
- file.ffProbeData.streams[i].codec_name
- .toLowerCase() === 'mov_text'
- || file.ffProbeData.streams[i].codec_name
- .toLowerCase() === 'eia_608'
- || file.ffProbeData.streams[i].codec_name
- .toLowerCase() === 'timed_id3'
- ) {
- extraArguments += `-map -0:${i} `;
- }
- } catch (err) {
- // Error
- }
- }
- }
- if (inputs.container.toLowerCase() === 'mp4') {
- for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
- try {
- if (
- file.ffProbeData.streams[i].codec_name
- .toLowerCase() === 'hdmv_pgs_subtitle'
- || file.ffProbeData.streams[i].codec_name
- .toLowerCase() === 'eia_608'
- || file.ffProbeData.streams[i].codec_name
- .toLowerCase() === 'subrip'
- || file.ffProbeData.streams[i].codec_name
- .toLowerCase() === 'timed_id3'
- ) {
- extraArguments += `-map -0:${i} `;
- }
- } catch (err) {
- // Error
- }
- }
- }
- };
- // Set bitrateSettings variable using bitrate information calculated earlier.
- bitrateSettings = `-maxrate ${inputs.bitrate_cutoff}k -bufsize ${currentBitrate}k`;
- // Print to infoLog information around file & bitrate settings.
- response.infoLog += `\nContainer for output selected as ${inputs.container}. \n`;
- response.infoLog += 'Encode variable bitrate settings: \n';
- response.infoLog += `Target = ${targetBitrate}k \n`;
- // response.infoLog += `Minimum = ${minimumBitrate}k \n`;
- response.infoLog += `Maximum = ${inputs.bitrate_cutoff}k \n`;
- // START PRESET
- // DECODE FLAGS
- // -fflags +genpts should regenerate timestamps if they end up missing...
- response.preset = '-fflags +genpts ';
- //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 `;
- };
- };
- if (inputs.output_codec === 'h264') {
- // Add the rest of the FFmpeg command
- response.preset += ` ${bitrateSettings} `
- + `-c:v h264_nvenc -preset ${inputs.encoder_speedpreset} ${inputs.extra_NVENC_options}
- -c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments} -crf 20 -qp 20`;
- } else if (inputs.output_codec === 'h265') {
- // Add the rest of the FFmpeg command
- response.preset += ` ${bitrateSettings} `
- + `-c:v hevc_nvenc -preset ${inputs.encoder_speedpreset} ${inputs.extra_NVENC_options}
- -c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments} -crf 20 -qp 20`;
- };
- // Add crop values to preset if they exist.
- // if (crop_values.x > 10 || crop_values.y > 10) {
- // response.preset += `-vf hwdownload,crop=${crop_values.w}:${crop_values.h}:${crop_values.x}:${crop_values.y},hwupload_cuda`;
- // }
- response.processFile = true;
- response.infoLog += 'File Transcoding... \n';
- return response;
- };
- module.exports.details = details;
- module.exports.plugin = plugin;
- // Custom function to find the mode of a list
- function findMode(lst) {
- const counts = {};
- let maxCount = 0;
- let modes = [];
- for (const num of lst) {
- if (!counts[num]) {
- counts[num] = 0;
- }
- counts[num]++;
- if (counts[num] > maxCount) {
- maxCount = counts[num];
- }
- }
- for (const num in counts) {
- if (counts[num] === maxCount) {
- modes.push(parseInt(num));
- }
- }
- return modes;
- }
- // 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, duration, otherArguments) {
- const fs = require('fs');
- const path = require('path');
- const { execSync } = require('child_process');
- const sourceFile = file.meta.SourceFile;
- // const txtFile = path.join(path.dirname(sourceFile), path.basename(sourceFile, path.extname(sourceFile))) + ".txt";
- // // Check if txtFile exists, and return the crop values from the file if it does
- // if (fs.existsSync(txtFile)) {
- // const cropValues = fs.readFileSync(txtFile, 'utf8');
- // const crop = cropValues.split(',');
- // return {w: parseInt(crop[0]), h: parseInt(crop[1]), x: parseInt(crop[2]), y: parseInt(crop[3])};
- // }
- // 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);
- // // Write the crop values to txtFile
- // const cropValuesStr = `${wMode}:${hMode}:${xMode}:${yMode}`;
- // fs.writeFileSync(txtFile, cropValuesStr);
- // Return a dict of the crop values
- return {w:wMode, h:hMode, x:xMode, y:yMode};
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement