You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1033 lines
44 KiB
1033 lines
44 KiB
// All credit for original plugin logic goes to Migz.
|
|
// This Plugin started as his NVENC/CPU plugin modified to work with QSV & with extra hevc logic.
|
|
// Rewritten to control encoder quality/speed & to allow HEVC files to be reprocessed to reduce file size
|
|
|
|
// NOTE - This does not use VAAPI, it is QSV only. So newer intel iGPUs only. 8th+ gen should work.
|
|
// Additionally this was designed and tested on UNRAID via docker, though there is logic to support use on
|
|
// Windows & Linux - Both platforms have now been confirmed working, however there is no way to test all use cases
|
|
// Mac is supported, however it does not use QSV. This is because ffmpeg on Mac does not actually leverage QSV and
|
|
// instead uses "VideoToolbox" which is more a general video encode accelerator.
|
|
|
|
// White paper from intel regarding QSV performance on linux using FFMPEG here:
|
|
// eslint-disable-next-line max-len
|
|
// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/cloud-computing-quicksync-video-ffmpeg-white-paper.pdf
|
|
|
|
const details = () => ({
|
|
id: 'Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC',
|
|
Stage: 'Pre-processing',
|
|
Name: 'Boosh-Transcode using QSV GPU & FFMPEG',
|
|
Type: 'Video',
|
|
Operation: 'Transcode',
|
|
Description: `==DETAILS== This is a QSV plugin. VAAPI is NOT used. Supports HEVC or AV1 encoding.
|
|
Ensure you have supported hardware!
|
|
\n\n==OS SUPPORT== This plugin supports Linux & Windows using QSV. Mac is not officially supported.
|
|
If you do use Mac, encodes will use videotoolbox instead of QSV & will ignore the encoder selection.
|
|
\n\n==LOGIC== Files will be transcoded into the selected format of HEVC or AV1, using Quick Sync Video (QSV)
|
|
via an Intel GPU using ffmpeg.
|
|
Settings are dependant on file bitrate & a bitrate modifier. The general logic is that either format 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 if they exceed the bitrate
|
|
specified in "hevc_max_bitrate". This plugin relies on understanding the accurate video bitrate of your files.
|
|
It's highly recommended to first remux into MKV & enable "Run mkvpropedit on files before running plugins" under
|
|
Tdarr>Options.`,
|
|
Version: '1.4',
|
|
Tags: 'pre-processing,ffmpeg,video only,qsv,h265,hevc,av1,configurable',
|
|
Inputs: [
|
|
{
|
|
name: 'container',
|
|
type: 'string',
|
|
defaultValue: 'mkv',
|
|
inputUI: {
|
|
type: 'dropdown',
|
|
options: [
|
|
'mkv',
|
|
'mp4',
|
|
],
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nSpecifies the output container of the file.
|
|
\\nEnsure that all stream types you may have are supported by your chosen container.
|
|
\\n
|
|
==INFO==
|
|
\\nOnly MP4 & MKV are supported and MKV is recommended.
|
|
\\nExample:\\n
|
|
mkv
|
|
\\nExample:\\n
|
|
mp4`,
|
|
},
|
|
{
|
|
name: 'encoder',
|
|
type: 'string',
|
|
defaultValue: 'hevc_qsv',
|
|
inputUI: {
|
|
type: 'dropdown',
|
|
options: [
|
|
'hevc_qsv',
|
|
'av1_qsv',
|
|
],
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nSpecifies the hardware encoder to use. Either HEVC or AV1
|
|
\\nEnsure that your hardware is able to use the selected encoder.
|
|
A 8th Gen+ intel CPU is suggested for HEVC, & a Intel Arc GPU for AV1
|
|
\\n
|
|
==INFO==
|
|
\\nOnly HEVC or AV1 encoders are supported!
|
|
\\nWhen using AV1, consider adjusting the Bitrate modifier to tune your output results`,
|
|
},
|
|
{
|
|
name: 'force_conform',
|
|
type: 'boolean',
|
|
defaultValue: false,
|
|
inputUI: {
|
|
type: 'dropdown',
|
|
options: [
|
|
'false',
|
|
'true',
|
|
],
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nMake 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==
|
|
\\nDrop hdmv_pgs_subtitle/eia_608/subrip/timed_id3 for MP4.
|
|
\\nDrop data streams/mov_text/eia_608/timed_id3 for MKV.
|
|
\\nDefault is false.
|
|
\\nExample:\\n
|
|
true
|
|
\\nExample:\\n
|
|
false`,
|
|
},
|
|
{
|
|
name: 'enable_10bit',
|
|
type: 'boolean',
|
|
defaultValue: false,
|
|
inputUI: {
|
|
type: 'dropdown',
|
|
options: [
|
|
'false',
|
|
'true',
|
|
],
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nSpecify if we want to enable 10bit encoding.
|
|
\\nIf this is enabled files will be processed and converted into 10bit
|
|
HEVC using main10 profile and with p010le pixel format.\n
|
|
If you just want to retain files that are already 10 bit then this can be left as false, as
|
|
10bit to 10bit in ffmpeg should be automatic.
|
|
\\n
|
|
==INFO==
|
|
\\nDefault is "false".
|
|
\\nExample:\\n
|
|
true
|
|
\\nExample:\\n
|
|
false`,
|
|
},
|
|
{
|
|
name: 'target_bitrate_modifier',
|
|
type: 'number',
|
|
defaultValue: 0.5,
|
|
inputUI: {
|
|
type: 'text',
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nSpecify the modifier for the target bitrate. The logic is that HEVC can obtain the same quality
|
|
at half the bitrate.
|
|
\\nIf you feel this isn't achieving the quality you want then increase this value.
|
|
\\nRecommended to leave at default. Setting to 1.0 or higher will achieve no size reduction.
|
|
\\nLook at the min & max bitrate options if you just want to set lower & upper limits for acceptable bitrate
|
|
\\n
|
|
==INFO==
|
|
\\nDefault is "0.5".
|
|
\\nExample:\\n
|
|
0.5
|
|
\\nExample:\\n
|
|
0.75`,
|
|
},
|
|
{
|
|
name: 'encoder_speedpreset',
|
|
type: 'string',
|
|
defaultValue: 'slow',
|
|
inputUI: {
|
|
type: 'dropdown',
|
|
options: [
|
|
'veryfast',
|
|
'faster',
|
|
'fast',
|
|
'medium',
|
|
'slow',
|
|
'slower',
|
|
'veryslow',
|
|
],
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nSpecify the encoder speed/preset to use.
|
|
Slower options mean a slower encode but better quality and faster options mean faster encodes but
|
|
worse quality.
|
|
\\nFor more information see intel white paper on ffmpeg results using QSV: \\n`
|
|
// eslint-disable-next-line max-len
|
|
+ `https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/cloud-computing-quicksync-video-ffmpeg-white-paper.pdf
|
|
\\n
|
|
==INFO==
|
|
\\nDefault is "slow".
|
|
\\nExample:\\n
|
|
medium
|
|
\\nExample:\\n
|
|
slower`,
|
|
},
|
|
{
|
|
name: 'extra_qsv_options',
|
|
type: 'string',
|
|
defaultValue: '',
|
|
inputUI: {
|
|
type: 'text',
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nHere you can add extra options to the ffmpeg QSV ENCODE cmd.
|
|
This does not override the ffmpeg cmd, it just allows additions to it.
|
|
\\n
|
|
There are extra QSV 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
|
|
OR https://ffmpeg.org/ffmpeg-codecs.html#AV1-Options
|
|
\\n
|
|
==WARNING== \\n
|
|
Be certain to verify the cmds work before adding to your workflow. \\n
|
|
Check Tdarr Help Tab. Enter ffmpeg cmd - "-h encoder=hevc_qsv" OR "-h encoder=av1_qsv".
|
|
This will give a list of supported commands. \\n
|
|
THERE ARE CMD DIFFERENCES BETWEEN HEVC & AV1! DO NOT JUST BLINDLY COPY CMDS BELOW AND EXPECT THEM TO WORK! \\n
|
|
MAC SPECIFIC - This option is ignored on Mac because videotoolbox is used rather than qsv.
|
|
\\n
|
|
==INFO==
|
|
\\nDefault is empty but the first example below has a suggested value. If unsure just leave empty.
|
|
\\nEnsure to only use cmds valid to encoding QSV as the script handles other ffmpeg cmds relating to
|
|
bitrate etc. Anything else entered here might be supported but could cause undesired results.
|
|
\\nIf you are using a "-vf" cmd, please put it at the end to avoid issues!
|
|
\\nExample:\\n
|
|
-async_depth 4 -look_ahead 1 -look_ahead_depth 100 -extbrc 1 -rdo 1 -mbbrc 1 -b_strategy 1 -adaptive_i 1
|
|
-adaptive_b 1
|
|
\\n FOR HEVC_QSV Above increases async, enables look ahead, extended bitrate control, b-frames, etc.\\n
|
|
\\nExample:\\n
|
|
-async_depth 4 -aq-mode 4 -look_ahead 1 -look_ahead_depth 100 -b_strategy 1 -adaptive_i 1 -adaptive_b 1
|
|
\\n FOR AV1_QSV Above increases async, enable adaptive quantization, look ahead, b-frames, etc.\\n
|
|
\\nExample:\\n
|
|
-vf scale_qsv=w=1280:h=720
|
|
\\nScale video resolution Method 1\\n
|
|
\\nExample:\\n
|
|
-vf scale_qsv=720:-1
|
|
\\nScale video resolution Method 2\\n`,
|
|
},
|
|
{
|
|
name: 'bitrate_cutoff',
|
|
type: 'number',
|
|
defaultValue: 0,
|
|
inputUI: {
|
|
type: 'text',
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nSpecify bitrate cutoff, files with a video bitrate lower then this will not be processed.\n
|
|
\\n
|
|
==INFO==
|
|
\\nRate is in kbps.
|
|
\\nDefaults to 0 which means this is disabled.
|
|
\\nEnter 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==
|
|
\\nSpecify a maximum average video bitrate. When encoding default behaviour is to halve the current video
|
|
bitrate 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==
|
|
\\nBitrate here is referring to video bitrate as we want to set the video bitrate on encode.
|
|
\\nRate is in kbps.
|
|
\\nDefaults to 0 which means this is disabled.
|
|
\\nEnter 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==
|
|
\\nSpecify a minimum average video bitrate. When encoding default behaviour is to halve the current video bitrate
|
|
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==
|
|
\\nBitrate here is referring to video bitrate as we want to set the video bitrate on encode.
|
|
\\nRate is in kbps.
|
|
\\nDefaults to 0 which means this is disabled.
|
|
\\nEnter 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==
|
|
\\nSet to reprocess HEVC/VP9/AV1 files (i.e reduce bitrate of files already in those codecs).
|
|
\\nSince this uses the same logic as normal, halving the current bitrate, this is NOT recommended
|
|
unless you know what you are doing, so please leave FALSE if unsure!
|
|
\\nNEEDS to be used in conjunction with "bitrate_cutoff" or "hevc_max_bitrate" otherwise is ignored.
|
|
\\nThis 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
|
|
==WARNING== \\n
|
|
IF YOU HAVE HEVC/VP9/AV1 FILES YOU WANT TO KEEP IN THOSE FORMATS THEN DO NOT USE THIS OPTION. \\n
|
|
\\nThis option has the potential to LOOP your encodes! You can encode a file to HEVC and still
|
|
be above your cutoff and it would be converted again & 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 help prevent the plugin looping.
|
|
Also it is highly suggested that you have your "hevc_max_bitrate" higher than "max_average_bitrate".
|
|
\\nPlease be certain you want this enabled before setting it otherwise leave this as FALSE!
|
|
While the plugin will attempt to generate accurate video bitrate metadata, it can not always reliably do so
|
|
and will be forced to fall back onto estimates. Please bare this in mind when using the HEVC reprocess option.
|
|
\\n
|
|
\\nExample:\\n
|
|
true
|
|
\\nExample:\\n
|
|
false`,
|
|
},
|
|
{
|
|
name: 'hevc_max_bitrate',
|
|
type: 'number',
|
|
defaultValue: 0,
|
|
inputUI: {
|
|
type: 'text',
|
|
},
|
|
tooltip: `\\n
|
|
==DESCRIPTION==
|
|
\\nHas no effect unless "reconvert_hevc" is set to true. This allows you to specify a maximum
|
|
allowed average OVERALL bitrate for HEVC/AV1/VP9 files. Much like the "bitrate_cutoff" option, but
|
|
specifically for these files. It should be set HIGHER then your standard cutoff for safety.
|
|
\\nAlso, it's highly suggested you use the min & max average bitrate options in combination with this. You will
|
|
want this to control the encoded video bitrate, otherwise you may end up unintentionally reprocessing these files.
|
|
i.e your file might have a overall bitrate of 20000, if your hevc cutoff is 5000 then it's going to
|
|
reconvert multiple times before it'll be below that cutoff.
|
|
\\nWhile HEVC/AV1/VP9 reprocessing can be useful this is why it is NOT recommended unless you know what you are
|
|
doing!
|
|
\\n
|
|
==WARNING== \\n
|
|
While the plugin will attempt to generate accurate video bitrate metadata, it can not always reliably do so
|
|
and will be forced to fall back onto estimates. Please bare this in mind when using the HEVC reprocess option.
|
|
\\n
|
|
==INFO==
|
|
\\nRate is in kbps.
|
|
\\nDefaults to 0 which means this is disabled.
|
|
\\nEnter a valid number to enable, otherwise we use "bitrate_cutoff" and multiply x2 for a safe limit.
|
|
\\nExample:\\n
|
|
4000
|
|
\\nExample:\\n
|
|
3000`,
|
|
},
|
|
],
|
|
});
|
|
|
|
// VARIABLES
|
|
let currentBitrate = 0;
|
|
let overallBitRate = 0;
|
|
let targetBitrate = 0;
|
|
let minimumBitrate = 0;
|
|
let maximumBitrate = 0;
|
|
let duration = '';
|
|
let videoIdx = 0;
|
|
let extraArguments = '';
|
|
let bitrateSettings = '';
|
|
let inflatedCutoff = 0;
|
|
let main10 = false;
|
|
let high10 = false;
|
|
let swDecode = false;
|
|
let videoBR = 0;
|
|
let hdrEnabled = false;
|
|
let videoProfile = '';
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|
const lib = require('../methods/lib')();
|
|
const os = require('os');
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
|
|
inputs = lib.loadDefaultValues(inputs, details);
|
|
const response = {
|
|
processFile: false,
|
|
preset: '',
|
|
handBrakeMode: false,
|
|
FFmpegMode: true,
|
|
reQueueAfter: true,
|
|
infoLog: '',
|
|
container: `.${inputs.container}`,
|
|
};
|
|
|
|
if (file.fileMedium !== 'video') {
|
|
response.processFile = false;
|
|
response.infoLog += `☒ File seems to be ${file.fileMedium} & not video. Exiting\n`;
|
|
return response;
|
|
}
|
|
|
|
if (inputs.Target_bitrate_modifier >= 1) {
|
|
response.processFile = false;
|
|
response.infoLog += '☒ Target bitrate modifier has been set to 1 or higher. Will not encode. Exiting\n';
|
|
return response;
|
|
}
|
|
|
|
// FILE VIDEO BITRATE & DURATION
|
|
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
|
|
const strstreamType = file.ffProbeData.streams[i].codec_type.toLowerCase();
|
|
// Check if stream is a video.
|
|
if (strstreamType === 'video') {
|
|
if (file.ffProbeData.streams[i].codec_name !== 'mjpeg'
|
|
&& file.ffProbeData.streams[i].codec_name !== 'png') {
|
|
if (videoBR <= 0) { // Process if videoBR is not yet valid
|
|
try { // Try checking file stats using Mediainfo first, then ffprobe.
|
|
videoBR = Number(file.mediaInfo.track[i + 1].BitRate) / 1000;
|
|
if (videoBR <= 0 || Number.isNaN(videoBR)) {
|
|
if (Number(file.ffProbeData.streams[i].tags.BPS) > 0) {
|
|
videoBR = file.ffProbeData.streams[i].tags.BPS / 1000;
|
|
} else if (Number(file.ffProbeData.streams[i].tags.BPS['-eng']) > 0) {
|
|
videoBR = file.ffProbeData.streams[i].tags.BPS['-eng'] / 1000;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
// Catch error - Ignore & carry on - If check can bomb out if tags don't exist...
|
|
videoBR = 0; // Set videoBR to 0 for safety
|
|
}
|
|
}
|
|
if (duration <= 0) { // Process if duration is not yet valid
|
|
try { // Attempt to get duration info
|
|
if (Number.isNaN(file.meta.Duration)) {
|
|
duration = file.meta.Duration;
|
|
duration = (new Date(`1970-01-01T${duration}Z`).getTime() / 1000) / 60;
|
|
} else if (file.meta.Duration > 0) {
|
|
duration = file.meta.Duration / 60;
|
|
}
|
|
if (duration <= 0 || Number.isNaN(duration)) {
|
|
if (typeof file.mediaInfo.track[i + 1].Duration !== 'undefined') {
|
|
duration = file.mediaInfo.track[i + 1].Duration;
|
|
duration = (new Date(`1970-01-01T${duration}Z`).getTime() / 1000) / 60;
|
|
} else if (typeof file.ffProbeData.streams[i].tags.DURATION !== 'undefined') {
|
|
duration = file.ffProbeData.streams[i].tags.DURATION;
|
|
duration = (new Date(`1970-01-01T${duration}Z`).getTime() / 1000) / 60;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
// Catch error - Ignore & carry on - If check can bomb out if tags don't exist...
|
|
duration = 0; // Set duration to 0 for safety
|
|
}
|
|
}
|
|
if ((videoBR <= 0 || Number.isNaN(videoBR)) || (duration <= 0 || Number.isNaN(duration))) {
|
|
// videoBR or duration not yet valid so Loop
|
|
} else {
|
|
break;// Exit loop if both valid
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// CATCH BITRATE FAILURE OR SUCCESS
|
|
if (Number.isNaN(videoBR) || videoBR <= 0) {
|
|
// Work out currentBitrate using "Bitrate = file size / (number of minutes * .0075)"
|
|
currentBitrate = Math.round(file.file_size / (duration * 0.0075));
|
|
if (Number.isNaN(currentBitrate) || currentBitrate <= 0) {
|
|
response.infoLog += '==ERROR== Failed to get any bitrate data from this file!\n'
|
|
+ 'This is highly likely due to some file problem. Highly suggested to run '
|
|
+ 'mkvpropedit via Tdarr options or in a Flow to ensure this file has correct stats!\n'
|
|
+ 'THIS ENCODE WILL FAIL/ERROR!';
|
|
} else {
|
|
response.infoLog += '==WARNING== Failed to get an accurate video bitrate, '
|
|
+ `falling back to old method to get OVERALL file bitrate of ${currentBitrate}kbps. `
|
|
+ 'Bitrate calculations for video encode will likely be inaccurate...\n';
|
|
}
|
|
} else {
|
|
currentBitrate = Math.round(videoBR);
|
|
response.infoLog += `☑ It looks like the current video bitrate is ${currentBitrate}kbps.\n`;
|
|
}
|
|
|
|
// Get overall bitrate for use with HEVC reprocessing
|
|
overallBitRate = Math.round(file.file_size / (duration * 0.0075));
|
|
// Default will halve current bitrate for Target bitrate
|
|
// In theory h265 can be half the bitrate as h264 without losing quality.
|
|
targetBitrate = Math.round(currentBitrate * inputs.target_bitrate_modifier);
|
|
// Allow some leeway under and over the targetBitrate.
|
|
minimumBitrate = Math.round(targetBitrate * 0.75);
|
|
maximumBitrate = Math.round(targetBitrate * 1.25);
|
|
|
|
// 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 || overallBitRate <= 0) {
|
|
response.infoLog += '☒ Target bitrates could not be calculated. Skipping this plugin.\n';
|
|
return response;
|
|
}
|
|
|
|
// 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) {
|
|
response.infoLog += `☒ Target bitrate has been calculated as ${targetBitrate}kbps. This is equal or greater than `
|
|
+ "the current bitrate... Something has gone wrong and this shouldn't happen! Skipping this plugin.\n";
|
|
return response;
|
|
}
|
|
|
|
// 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) {
|
|
response.infoLog += `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.\n'
|
|
+ 'Either set reconvert_HEVC to false or set a bitrate cutoff and set a hevc_max_bitrate cutoff.\n'
|
|
+ '☒ Skipping this plugin.\n';
|
|
return response;
|
|
}
|
|
|
|
// 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}kbps.\n`
|
|
+ '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. Need to process\n';
|
|
}
|
|
}
|
|
|
|
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 clamping at max of '
|
|
+ `${inputs.max_average_bitrate}kbps.\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 > 0 && 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}kbps. 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}kbps.\n`;
|
|
targetBitrate = Math.round(inputs.min_average_bitrate);
|
|
minimumBitrate = Math.round(targetBitrate * 0.75);
|
|
maximumBitrate = Math.round(targetBitrate * 1.25);
|
|
}
|
|
}
|
|
|
|
// 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') {
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 -0:v:${videoIdx} `;
|
|
} else { // Ensure to only do further checks if video stream is valid for use
|
|
// Check for HDR in files. Attempt to use same color
|
|
if ((file.ffProbeData.streams[i].color_space === 'bt2020nc'
|
|
|| file.ffProbeData.streams[i].color_space === 'bt2020n')
|
|
&& (file.ffProbeData.streams[i].color_transfer === 'smpte2084'
|
|
|| file.ffProbeData.streams[i].color_transfer === 'arib-std-b67')
|
|
&& file.ffProbeData.streams[i].color_primaries === 'bt2020') {
|
|
response.infoLog += '==WARNING== This looks to be a HDR file. HDR is supported but '
|
|
+ 'correct encoding is not guaranteed.\n';
|
|
extraArguments += `-color_primaries ${file.ffProbeData.streams[i].color_primaries} `
|
|
+ `-color_trc ${file.ffProbeData.streams[i].color_transfer} `
|
|
+ `-colorspace ${file.ffProbeData.streams[i].color_space} `;
|
|
hdrEnabled = true;
|
|
}
|
|
|
|
// VALIDATE HDR - Ignore Dolby vision & badly formatted files
|
|
if (hdrEnabled !== true) {
|
|
// Had at least one case where a file contained no evident HDR data but was marked as HDR content
|
|
// meaning transcode OR plex would butcher the file
|
|
try {
|
|
if (typeof file.mediaInfo.track[i + 1].HDR_Format !== 'undefined') {
|
|
response.infoLog += '==ERROR== This file has Media data implying it is HDR '
|
|
+ `(${file.mediaInfo.track[i + 1].HDR_Format}), `
|
|
+ 'but no details about color space or primaries... '
|
|
+ 'Unable to convert and safely keep HDR data. Aborting!\n';
|
|
return response;
|
|
}
|
|
} catch (err) {
|
|
// Catch error - Ignore & carry on - If check can bomb out if tags don't exist...
|
|
}
|
|
} else {
|
|
// If specifically marked Dolby Vision
|
|
try {
|
|
if (file.mediaInfo.track[i + 1].HDR_Format.search('Dolby Vision') >= 0
|
|
|| file.mediaInfo.track[i + 1].HDR_Format.search('HDR10+') >= 0
|
|
|| file.mediaInfo.track[i + 1].HDR_Format.search('SMPTE ST 2094 App 4') >= 0) {
|
|
response.infoLog += '==ERROR== This file has HDR metadata that cannot be re-encoded '
|
|
+ `(${file.mediaInfo.track[i + 1].HDR_Format}), `
|
|
+ 'Currently we cannot safely convert this HDR format and retain the Dolby Vision or HDR10+ format. '
|
|
+ 'Aborting!\n';
|
|
return response;
|
|
}
|
|
} catch (err) {
|
|
// Catch error - Ignore & carry on - If check can bomb out if tags don't exist...
|
|
}
|
|
}
|
|
|
|
// 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 === 'vp9'
|
|
|| file.ffProbeData.streams[i].codec_name === 'av1') && file.container !== inputs.container) {
|
|
response.infoLog += `☒ File is HEVC, VP9 or AV1 but is not in ${inputs.container} container. Remuxing.\n`;
|
|
response.preset = `<io> -map 0 -c copy ${extraArguments}`;
|
|
response.processFile = true;
|
|
return response;
|
|
}
|
|
|
|
// Now check if we're reprocessing HEVC files, if not then ensure we don't convert HEVC again
|
|
if (inputs.reconvert_hevc === false && (file.ffProbeData.streams[i].codec_name === 'hevc'
|
|
|| file.ffProbeData.streams[i].codec_name === 'vp9' || file.ffProbeData.streams[i].codec_name === 'av1')) {
|
|
// Check if codec of stream is HEVC, VP9 or AV1 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 === 'vp9'
|
|
|| file.ffProbeData.streams[i].codec_name === 'av1') && file.container === inputs.container) {
|
|
response.infoLog += `☑ File is already HEVC, VP9 or AV1 & in ${inputs.container}.\n`;
|
|
return response;
|
|
}
|
|
|
|
// New logic for reprocessing HEVC. Mainly done for my own use.
|
|
// We attempt to get accurate stats earlier - If we can't we fall back onto overall bitrate
|
|
// which can be inaccurate. We may inflate the current bitrate check 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 === 'vp9' || file.ffProbeData.streams[i].codec_name === 'av1')) {
|
|
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, VP9 or AV1. `
|
|
+ `Using HEVC specific cutoff of ${inputs.hevc_max_bitrate}kbps.\n`
|
|
+ '☒ The file is still above this new cutoff! Reconverting.\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, VP9 or AV1. `
|
|
+ `Using HEVC specific cutoff of ${inputs.hevc_max_bitrate}kbps.\n`
|
|
+ '☑ The file is NOT above this new cutoff. Exiting plugin.\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)) {
|
|
if (inputs.bitrate_cutoff > 0) {
|
|
inflatedCutoff = Math.round(inputs.bitrate_cutoff * 2);
|
|
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, VP9 or AV1. `
|
|
+ `Will use Overall file Bitrate for HEVC files as safety, bitrate is ${overallBitRate}kbps.\n`
|
|
+ 'HEVC specific cutoff not set so bitrate_cutoff is multiplied by 2 for safety!\n'
|
|
+ `Cutoff now temporarily ${inflatedCutoff}kbps.\n`
|
|
+ '☒ The file is still above this new cutoff! Reconverting.\n';
|
|
} else {
|
|
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, VP9 or AV1. `
|
|
+ `Will use Overall file Bitrate for HEVC files as safety, bitrate is ${overallBitRate}kbps.\n`
|
|
+ 'HEVC specific cutoff not set & bitrate_cutoff is not set!\n'
|
|
+ '☒ We have no safe way to prevent a transcode loop. Exiting.\n';
|
|
return response;
|
|
}
|
|
} 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, VP9 or AV1. `
|
|
+ `Will use Overall file Bitrate for HEVC files as safety, bitrate is ${overallBitRate}kbps.\n`
|
|
+ 'HEVC specific cutoff not set so bitrate_cutoff is multiplied by 2 for safety!\n'
|
|
+ `Cutoff now temporarily ${inflatedCutoff}kbps.\n`
|
|
+ '☑The file is NOT above this new cutoff. Exiting plugin.\n';
|
|
return response;
|
|
}
|
|
}
|
|
|
|
// Files in the High10 profile are not supported for HW Decode
|
|
if (file.ffProbeData.streams[i].profile === 'High 10') {
|
|
high10 = true;
|
|
main10 = true;
|
|
// If files are 10 bit or the enable_10bit setting is used mark to enable Main10.
|
|
} else if (file.ffProbeData.streams[i].profile === 'Main 10'
|
|
|| file.ffProbeData.streams[i].bits_per_raw_sample === '10' || inputs.enable_10bit === true) {
|
|
main10 = true;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// Specify the output format
|
|
switch (inputs.container) {
|
|
case 'mkv':
|
|
extraArguments += '-f matroska ';
|
|
break;
|
|
case 'mp4':
|
|
extraArguments += '-f mp4 ';
|
|
break;
|
|
default:
|
|
}
|
|
|
|
// Some video codecs don't support HW decode so mark these
|
|
// VC1 & VP8 are no longer supported on new HW, add cases here if your HW does support
|
|
switch (file.video_codec_name) {
|
|
case 'mpeg2':
|
|
break;
|
|
case 'h264':
|
|
if (high10 === true) {
|
|
swDecode = true;
|
|
response.infoLog += 'Input file is h264 High10. Hardware Decode not supported so will SW decode.\n';
|
|
}
|
|
break;
|
|
case 'mjpeg':
|
|
break;
|
|
case 'hevc':
|
|
break;
|
|
case 'vp9':// Should be supported by 8th Gen +
|
|
break;
|
|
case 'av1':// Should be supported by 11th gen +
|
|
break;
|
|
default:
|
|
swDecode = true;
|
|
response.infoLog += `Input file is ${file.video_codec_name}. Hardware Decode not supported.\n`;
|
|
}
|
|
|
|
// Are we encoding to 10 bit? If so enable correct profile & pixel format.
|
|
if (os.platform() !== 'darwin') {
|
|
switch (inputs.encoder) {
|
|
case 'hevc_qsv':
|
|
videoProfile = 'main10';
|
|
break;
|
|
case 'av1_qsv':
|
|
// Change this if you want high profile instead, main should be fine for 10 bit 4:2:0 content
|
|
videoProfile = 'main';
|
|
break;
|
|
default:
|
|
videoProfile = 'main10';
|
|
}
|
|
if (swDecode === true && main10 === true) {
|
|
// This is used if we have High10 or Main10 is enabled & odd format files.
|
|
// SW decode and use standard -pix_fmt p010le
|
|
extraArguments += `-profile:v ${videoProfile} -pix_fmt p010le `;
|
|
response.infoLog += `10 bit encode enabled. Setting ${videoProfile} Profile & 10 bit pixel format\n`;
|
|
} else if (main10 === true) { // Pixel formate method when using HW decode
|
|
if (inputs.extra_qsv_options.search('-vf scale_qsv') >= 0) {
|
|
extraArguments += `-profile:v ${videoProfile}`;
|
|
// eslint-disable-next-line no-param-reassign
|
|
inputs.extra_qsv_options += ',format=p010le'; // Only add on the pixel format to existing scale_qsv cmd
|
|
} else {
|
|
extraArguments += `-profile:v ${videoProfile} -vf scale_qsv=format=p010le`;
|
|
}
|
|
response.infoLog += `10 bit encode enabled. Setting ${videoProfile} `
|
|
+ 'Profile & 10 bit pixel format\n';
|
|
}
|
|
} else {
|
|
// Mac - Video toolbox profile & pixel format
|
|
extraArguments += '-profile:v 2 -pix_fmt yuv420p10le ';
|
|
response.infoLog += '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n';
|
|
}
|
|
|
|
// Set bitrateSettings variable using bitrate information calculated earlier.
|
|
bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k `
|
|
+ `-maxrate ${maximumBitrate}k -bufsize ${currentBitrate}k`;
|
|
// Print to infoLog information around file & bitrate settings.
|
|
response.infoLog += `Container for output selected as ${inputs.container}.\n`
|
|
+ 'Encode variable bitrate settings:\n'
|
|
+ `Target = ${targetBitrate}k\n`
|
|
+ `Minimum = ${minimumBitrate}k\n`
|
|
+ `Maximum = ${maximumBitrate}k\n`;
|
|
|
|
// START PRESET
|
|
// -fflags +genpts should regenerate timestamps if they end up missing...
|
|
response.preset = '-fflags +genpts ';
|
|
|
|
// HW ACCEL FLAGS
|
|
// Account for different OS
|
|
if (swDecode !== true) {
|
|
// Only enable hw decode for accepted formats
|
|
switch (os.platform()) {
|
|
case 'darwin': // Mac OS - Enable videotoolbox instead of QSV
|
|
response.preset += '-hwaccel videotoolbox';
|
|
break;
|
|
case 'linux': // Linux - Full device, should fix child_device_type warnings
|
|
response.preset += '-hwaccel qsv -hwaccel_output_format qsv '
|
|
+ '-init_hw_device qsv:hw_any,child_device_type=vaapi ';
|
|
break;
|
|
case 'win32': // Windows - Full device, should fix child_device_type warnings
|
|
response.preset += '-hwaccel qsv -hwaccel_output_format qsv '
|
|
+ '-init_hw_device qsv:hw,child_device_type=d3d11va ';
|
|
break;
|
|
default:
|
|
response.preset += '-hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any ';
|
|
}
|
|
} else {
|
|
switch (os.platform()) {
|
|
case 'darwin': // Mac OS - Enable videotoolbox instead of QSV
|
|
response.preset += '-hwaccel videotoolbox';
|
|
break;
|
|
case 'linux': // Linux - Full device, should fix child_device_type warnings
|
|
response.preset += '-hwaccel_output_format qsv '
|
|
+ '-init_hw_device qsv:hw_any,child_device_type=vaapi ';
|
|
break;
|
|
case 'win32': // Windows - Full device, should fix child_device_type warnings
|
|
response.preset += '-hwaccel_output_format qsv '
|
|
+ '-init_hw_device qsv:hw,child_device_type=d3d11va ';
|
|
break;
|
|
default:
|
|
// Default to enabling hwaccel for output only
|
|
response.preset += '-hwaccel_output_format qsv -init_hw_device qsv:hw_any ';
|
|
}
|
|
}
|
|
|
|
// DECODE FLAGS
|
|
// VC1 & VP8 are no longer supported on new HW, add cases here if your HW does support
|
|
if (os.platform() !== 'darwin') {
|
|
switch (file.video_codec_name) {
|
|
case 'mpeg2':
|
|
response.preset += '-c:v mpeg2_qsv';
|
|
break;
|
|
case 'h264':
|
|
if (high10 !== true) { // Don't enable for High10
|
|
response.preset += '-c:v h264_qsv';
|
|
} else {
|
|
response.preset += `-c:v ${file.video_codec_name}`;
|
|
}
|
|
break;
|
|
case 'mjpeg':
|
|
response.preset += '-c:v mjpeg_qsv';
|
|
break;
|
|
case 'hevc':
|
|
response.preset += '-c:v hevc_qsv';
|
|
break;
|
|
case 'vp9': // Should be supported by 8th Gen +
|
|
response.preset += '-c:v vp9_qsv';
|
|
break;
|
|
case 'av1': // Should be supported by 11th gen +
|
|
response.preset += '-c:v av1_qsv';
|
|
break;
|
|
default:
|
|
// Use incoming format for software decode
|
|
response.preset += `-c:v ${file.video_codec_name}`;
|
|
}
|
|
}
|
|
|
|
// ENCODE FLAGS
|
|
response.preset += '<io> -map 0 -c:v ';
|
|
|
|
// Account for different OS setup for QSV HEVC encode.
|
|
switch (os.platform()) {
|
|
case 'darwin':
|
|
response.preset += 'hevc_videotoolbox';
|
|
// Mac OS & uses hevc_videotoolbox not QSV - Only shows up on Mac installs
|
|
break;
|
|
case 'linux':
|
|
response.preset += `${inputs.encoder}`;
|
|
break;
|
|
case 'win32':
|
|
response.preset += `${inputs.encoder}`;
|
|
break;
|
|
default:
|
|
response.preset += `${inputs.encoder}`; // Default
|
|
}
|
|
|
|
// Only add on for HW decoded formats
|
|
// VC1 & VP8 are no longer supported on new HW, add cases here if your HW does support
|
|
if (swDecode !== true && os.platform() !== 'darwin') {
|
|
// Check if -vf cmd has already been used on user input
|
|
if (inputs.extra_qsv_options.search('-vf scale_qsv') >= 0) {
|
|
switch (file.video_codec_name) {
|
|
case 'mpeg2':
|
|
// eslint-disable-next-line no-param-reassign
|
|
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'h264':
|
|
// eslint-disable-next-line no-param-reassign
|
|
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'mjpeg':
|
|
// eslint-disable-next-line no-param-reassign
|
|
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'hevc':
|
|
// eslint-disable-next-line no-param-reassign
|
|
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'vp9': // Should be supported by 8th Gen +
|
|
// eslint-disable-next-line no-param-reassign
|
|
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'av1': // Should be supported by 11th gen +
|
|
// eslint-disable-next-line no-param-reassign
|
|
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
default:
|
|
}
|
|
} else if (extraArguments.search('-vf') === -1) {
|
|
// Check if -vf cmd has been used on the other var instead, if not add it & rest of cmd
|
|
switch (file.video_codec_name) {
|
|
case 'mpeg2':
|
|
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'h264':
|
|
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'mjpeg':
|
|
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'hevc':
|
|
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'vp9': // Should be supported by 8th Gen +
|
|
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'av1': // Should be supported by 11th gen +
|
|
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
default:
|
|
}
|
|
} else {
|
|
// Otherwise add the cmd onto the end
|
|
switch (file.video_codec_name) {
|
|
case 'mpeg2':
|
|
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'h264':
|
|
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'mjpeg':
|
|
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'hevc':
|
|
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'vp9': // Should be supported by 8th Gen +
|
|
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
case 'av1': // Should be supported by 11th gen +
|
|
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the rest of the ffmpeg command
|
|
switch (os.platform()) {
|
|
case 'darwin':
|
|
// Mac OS - Don't use extra_qsv_options - These are intended for QSV cmds so videotoolbox causes issues
|
|
response.preset += ` ${bitrateSettings} `
|
|
+ `-preset ${inputs.encoder_speedpreset} -c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments}`;
|
|
response.infoLog += '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
|
+ 'cmds set in extra_qsv_options will be IGNORED!\n';
|
|
break;
|
|
default:
|
|
// Normal behavior
|
|
response.preset += ` ${bitrateSettings} `
|
|
+ `-preset ${inputs.encoder_speedpreset} ${inputs.extra_qsv_options} `
|
|
+ `-c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments}`;
|
|
}
|
|
|
|
response.processFile = true;
|
|
response.infoLog += 'File Transcoding...\n';
|
|
|
|
return response;
|
|
};
|
|
module.exports.details = details;
|
|
module.exports.plugin = plugin;
|