- "Smart" resize instead of relying on ffmpeg for the logic. This fixes the bug with anamorphic video getting messed up aspect ratios.

- Normalize resolution will always normalize anamorphic videos, even if they have the same dimensions.
- When transcoding and normalize resolution being off, padding will not be added. But there's a special case for odd dimensions, where it needs to add 1 pixel.
- Changed scaling algorithm to 'billinear_fast'. It has 'fast' in the name, so hopefully it's faster?
This commit is contained in:
vexorian 2020-09-05 11:29:45 -04:00
parent ee08b3a601
commit 461dfea071
2 changed files with 87 additions and 13 deletions

View File

@ -215,17 +215,58 @@ class FFMPEG extends events.EventEmitter {
// Resolution fix: Add scale filter, current stream becomes [siz]
let beforeSizeChange = currentVideo;
if (this.ensureResolution && (iW != this.wantedW || iH != this.wantedH) ) {
//Maybe the scaling algorithm could be configurable. bicubic seems good though
videoComplex += `;${currentVideo}scale=${this.wantedW}:${this.wantedH}:flags=bicubic:force_original_aspect_ratio=decrease,pad=${this.wantedW}:${this.wantedH}:(ow-iw)/2:(oh-ih)/2[siz]`
currentVideo = "[siz]";
iW = this.wantedW;
iH = this.wantedH;
} else if ( isLargerResolution(iW, iH, this.wantedW, this.wantedH) ) {
videoComplex += `;${currentVideo}scale=${this.wantedW}:${this.wantedH}:flags=bicubic:force_original_aspect_ratio=decrease,pad=${this.wantedW}:${this.wantedH}:(ow-iw)/2:(oh-ih)/2[minsiz]`
currentVideo = "[minsiz]";
iW = this.wantedW;
iH = this.wantedH;
let algo = "fast_bilinear";
let resizeMsg = "";
if (
(this.ensureResolution && ( streamStats.anamorphic || (iW != this.wantedW || iH != this.wantedH) ) )
||
isLargerResolution(iW, iH, this.wantedW, this.wantedH)
) {
//scaler stuff, need to change the size of the video and also add bars
// calculate wanted aspect ratio
let p = iW * streamStats.pixelP ;
let q = iH * streamStats.pixelQ;
let g = gcd(q,p); // and people kept telling me programming contests knowledge had no use real programming!
p = Math.floor(p / g);
q = Math.floor(q / g);
let hypotheticalW1 = this.wantedW;
let hypotheticalH1 = Math.floor(hypotheticalW1*q / p);
let hypotheticalH2 = this.wantedH;
let hypotheticalW2 = Math.floor( (this.wantedH * p) / q );
let cw, ch;
if (hypotheticalH1 <= this.wantedH) {
cw = hypotheticalW1;
ch = hypotheticalH1;
} else {
cw = hypotheticalW2;
ch = hypotheticalH2;
}
videoComplex += `;${currentVideo}scale=${cw}:${ch}:flags=${algo}[scaled]`;
currentVideo = "scaled";
resizeMsg = `Stretch to ${cw} x ${ch}. To fit target resolution of ${this.wantedW} x ${this.wantedH}.`;
if (this.ensureResolution) {
console.log(`First stretch to ${cw} x ${ch}. Then add padding to make it ${this.wantedW} x ${this.wantedH} `);
} else if (cw % 2 == 1 || ch % 2 ==1) {
//we need to add padding so that the video dimensions are even
let xw = cw + cw % 2;
let xh = ch + ch % 2;
resizeMsg = `Stretch to ${cw} x ${ch}. To fit target resolution of ${this.wantedW} x ${this.wantedH}. Then add 1 pixel of padding so that dimensions are not odd numbers, because they are frowned upon. The final resolution will be ${xw} x ${xh}`;
this.wantedW = xw;
this.wantedH = xh;
} else {
resizeMsg = `Stretch to ${cw} x ${ch}. To fit target resolution of ${this.wantedW} x ${this.wantedH}.`;
}
if ( (this.wantedW != cw) || (this.wantedH != ch) ) {
// also add black bars, because in this case it HAS to be this resolution
videoComplex += `;[${currentVideo}]pad=${this.wantedW}:${this.wantedH}:(ow-iw)/2:(oh-ih)/2[blackpadded]`;
currentVideo = "blackpadded";
}
let name = "siz";
if (! this.ensureResolution) {
name = "minsiz";
}
videoComplex += `;[${currentVideo}]setsar=1[${name}]`;
currentVideo = `[${name}]`;
}
// Channel overlay:
@ -265,6 +306,8 @@ class FFMPEG extends events.EventEmitter {
//do not change resolution if no other transcoding will be done
// and resolution normalization is off
currentVideo = beforeSizeChange;
} else {
console.log(resizeMsg)
}
if (currentVideo != '[video]') {
transcodeVideo = true; //this is useful so that it adds some lines below
@ -411,8 +454,8 @@ function isDifferentAudioCodec(codec, encoder) {
return true;
}
function isLargerResolution(w1,h1, w2,h2) {
return (w1 > w2) || (h1 > h2);
function isLargerResolution( w1,h1, w2,h2) {
return (w1 > w2) || (h1 > h2) || (w1 % 2 ==1) || (h1 % 2 == 1);
}
function parseResolutionString(s) {
@ -429,4 +472,15 @@ function parseResolutionString(s) {
}
}
function gcd(a, b) {
while (b != 0) {
console.log(a,b);
let c = b;
b = a % b;
a = c;
}
return a;
}
module.exports = FFMPEG

View File

@ -208,6 +208,18 @@ lang=en`
streams.forEach(function (stream) {
// Video
if (stream["streamType"] == "1") {
ret.anamorphic = (stream.anamorphic === "1");
if (ret.anamorphic) {
let parsed = parsePixelAspectRatio(stream.pixelAspectRatio);
if (isNaN(parsed.p) || isNaN(parsed.q) ) {
throw Error("isNaN");
}
ret.pixelP = parsed.p;
ret.pixelQ = parsed.q;
} else {
ret.pixelP= 1;
ret.pixelQ = 1;
}
ret.videoCodec = stream.codec;
ret.videoWidth = stream.width;
ret.videoHeight = stream.height;
@ -342,4 +354,12 @@ X-Plex-Token=${this.server.accessToken}`;
}
}
function parsePixelAspectRatio(s) {
let x = s.split(":");
return {
p: parseInt(x[0], 10),
q: parseInt(x[1], 10),
}
}
module.exports = PlexTranscoder