diff --git a/src/api.js b/src/api.js index 953dfd4..9365120 100644 --- a/src/api.js +++ b/src/api.js @@ -565,7 +565,7 @@ function api(db, channelDB, fillerDB, xmltvInterval, guideService ) { var data = `#EXTM3U url-tvg="${tvg}" x-tvg-url="${tvg}"\n`; for (var i = 0; i < channels.length; i++) { if (channels[i].stealth!==true) { - data += `#EXTINF:0 tvg-id="${channels[i].number}" tvg-chno="${channels[i].number}" tvg-name="${channels[i].name}" tvg-logo="${channels[i].icon}" group-title="dizqueTV",${channels[i].name}\n` + data += `#EXTINF:0 tvg-id="${channels[i].number}" CUID="${channels[i].number}" tvg-chno="${channels[i].number}" tvg-name="${channels[i].name}" tvg-logo="${channels[i].icon}" group-title="dizqueTV",${channels[i].name}\n` data += `${req.protocol}://${req.get('host')}/video?channel=${channels[i].number}\n` } } diff --git a/src/database-migration.js b/src/database-migration.js index bfab008..1c6f0ef 100644 --- a/src/database-migration.js +++ b/src/database-migration.js @@ -20,7 +20,7 @@ const path = require('path'); var fs = require('fs'); -const TARGET_VERSION = 700; +const TARGET_VERSION = 701; const STEPS = [ // [v, v2, x] : if the current version is v, call x(db), and version becomes v2 @@ -33,6 +33,7 @@ const STEPS = [ [ 501, 600, () => extractFillersFromChannels() ], [ 600, 601, (db) => addFPS(db) ], [ 601, 700, (db) => migrateWatermark(db) ], + [ 700, 701, (db) => addScalingAlgorithm(db) ], ] const { v4: uuidv4 } = require('uuid'); @@ -395,6 +396,7 @@ function ffmpeg() { normalizeResolution: true, normalizeAudio: true, maxFPS: 60, + scalingAlgorithm: "bicubic", } } @@ -746,6 +748,14 @@ function migrateWatermark(db, channelDB) { console.log("Done migrating watermarks in channels."); } +function addScalingAlgorithm(db) { + let ffmpegSettings = db['ffmpeg-settings'].find()[0]; + let f = path.join(process.env.DATABASE, 'ffmpeg-settings.json'); + ffmpegSettings.scalingAlgorithm = "bicubic"; + fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) ); +} + + module.exports = { initDB: initDB, defaultFFMPEG: ffmpeg, diff --git a/src/ffmpeg-info.js b/src/ffmpeg-info.js index ef264fd..00cb1c9 100644 --- a/src/ffmpeg-info.js +++ b/src/ffmpeg-info.js @@ -15,7 +15,12 @@ class FFMPEGInfo { } }); }); - return s.match( /version ([^\s]+) Copyright/ )[1]; + var m = s.match( /version\s+([^\s]+)\s+.*Copyright/ ) + if (m == null) { + console.error("ffmpeg -version command output not in the expected format: " + s); + return s; + } + return m[1]; } catch (err) { console.error("Error getting ffmpeg version", err); return "Error"; diff --git a/src/ffmpeg.js b/src/ffmpeg.js index 1e461be..b884c1c 100644 --- a/src/ffmpeg.js +++ b/src/ffmpeg.js @@ -9,6 +9,7 @@ class FFMPEG extends events.EventEmitter { super() this.opts = opts; this.errorPicturePath = `http://localhost:${process.env.PORT}/images/generic-error-screen.png`; + this.ffmpegName = "unnamed ffmpeg"; if (! this.opts.enableFFMPEGTranscoding) { //this ensures transcoding is completely disabled even if // some settings are true @@ -255,7 +256,7 @@ class FFMPEG extends events.EventEmitter { // Resolution fix: Add scale filter, current stream becomes [siz] let beforeSizeChange = currentVideo; - let algo = "fast_bilinear"; + let algo = this.opts.scalingAlgorithm; let resizeMsg = ""; if ( (this.ensureResolution && ( streamStats.anamorphic || (iW != this.wantedW || iH != this.wantedH) ) ) @@ -321,10 +322,10 @@ class FFMPEG extends events.EventEmitter { var vert = Math.round( mpVert * iH / 100.0 ); let posAry = { - 'top-left': `${horz}:${vert}`, - 'top-right': `W-w-${horz}:${vert}`, - 'bottom-left': `${horz}:H-h-${vert}`, - 'bottom-right': `W-w-${horz}:H-h-${vert}`, + 'top-left': `x=${horz}:y=${vert}`, + 'top-right': `x=W-w-${horz}:y=${vert}`, + 'bottom-left': `x=${horz}:y=H-h-${vert}`, + 'bottom-right': `x=W-w-${horz}:y=H-h-${vert}`, } let icnDur = '' if (watermark.duration > 0) { @@ -339,7 +340,11 @@ class FFMPEG extends events.EventEmitter { if (typeof(p) === 'undefined') { throw Error("Invalid watermark position: " + watermark.position); } - videoComplex += `;${currentVideo}${waterVideo}overlay=${p}${icnDur}[comb]` + let overlayShortest = ""; + if (watermark.animated) { + overlayShortest = "shortest=1:"; + } + videoComplex += `;${currentVideo}${waterVideo}overlay=${overlayShortest}${p}${icnDur}[comb]` currentVideo = '[comb]'; } @@ -464,30 +469,45 @@ class FFMPEG extends events.EventEmitter { ffmpegArgs.push(`pipe:1`) let doLogs = this.opts.logFfmpeg && !isConcatPlaylist; + if (this.hasBeenKilled) { + return ; + } this.ffmpeg = spawn(this.ffmpegPath, ffmpegArgs, { stdio: ['ignore', 'pipe', (doLogs?process.stderr:"ignore") ] } ); + if (this.hasBeenKilled) { + this.ffmpeg.kill("SIGKILL"); + return; + } - let ffmpegName = (isConcatPlaylist ? "Concat FFMPEG": "Stream FFMPEG"); + this.ffmpegName = (isConcatPlaylist ? "Concat FFMPEG": "Stream FFMPEG"); + + this.ffmpeg.on('error', (code, signal) => { + console.log( `${this.ffmpegName} received error event: ${code}, ${signal}` ); + }); this.ffmpeg.on('exit', (code, signal) => { if (code === null) { - console.log( `${ffmpegName} exited due to signal: ${signal}` ); + if (!this.hasBeenKilled) { + console.log( `${this.ffmpegName} exited due to signal: ${signal}` ); + } else { + console.log( `${this.ffmpegName} exited due to signal: ${signal} as expected.`); + } this.emit('close', code) } else if (code === 0) { - console.log( `${ffmpegName} exited normally.` ); + console.log( `${this.ffmpegName} exited normally.` ); this.emit('end') } else if (code === 255) { if (this.hasBeenKilled) { - console.log( `${ffmpegName} finished with code 255.` ); + console.log( `${this.ffmpegName} finished with code 255.` ); this.emit('close', code) return; } if (! this.sentData) { this.emit('error', { code: code, cmd: `${this.opts.ffmpegPath} ${ffmpegArgs.join(' ')}` }) } - console.log( `${ffmpegName} exited with code 255.` ); + console.log( `${this.ffmpegName} exited with code 255.` ); this.emit('close', code) } else { - console.log( `${ffmpegName} exited with code ${code}.` ); + console.log( `${this.ffmpegName} exited with code ${code}.` ); this.emit('error', { code: code, cmd: `${this.opts.ffmpegPath} ${ffmpegArgs.join(' ')}` }) } }); @@ -495,9 +515,11 @@ class FFMPEG extends events.EventEmitter { return this.ffmpeg.stdout; } kill() { - if (typeof this.ffmpeg != "undefined") { - this.hasBeenKilled = true; - this.ffmpeg.kill() + console.log(`${this.ffmpegName} RECEIVED kill() command`); + this.hasBeenKilled = true; + if (typeof(this.ffmpeg) != "undefined") { + console.log(`${this.ffmpegName} this.ffmpeg.kill()`); + this.ffmpeg.kill("SIGKILL") } } } diff --git a/web/directives/ffmpeg-settings.js b/web/directives/ffmpeg-settings.js index 6388b55..f4f66c7 100644 --- a/web/directives/ffmpeg-settings.js +++ b/web/directives/ffmpeg-settings.js @@ -60,6 +60,13 @@ module.exports = function (dizquetv, resolutionOptions) { {id: 60, description: "60 frames per second"}, {id: 120, description: "120 frames per second"}, ]; + scope.scalingOptions = [ + {id: "bicubic", description: "bicubic (default)"}, + {id: "fast_bilinear", description: "fast_bilinear"}, + {id: "lanczos", description: "lanczos"}, + {id: "spline", description: "spline"}, + ]; + } } } \ No newline at end of file diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html index c605114..f88c4b6 100644 --- a/web/public/templates/channel-config.html +++ b/web/public/templates/channel-config.html @@ -740,7 +740,7 @@ - Tick this if and only if the watermark is an animated GIF or PNG. It will make it loop or not loop according to the image's configuration. If the image is not animated, an error will be generated. + Tick this if and only if the watermark is an animated GIF or PNG. It will make it loop or not loop according to the image's configuration. If the image is not animated, there will be playback errors.
diff --git a/web/public/templates/ffmpeg-settings.html b/web/public/templates/ffmpeg-settings.html index 4958c7f..71a66bb 100644 --- a/web/public/templates/ffmpeg-settings.html +++ b/web/public/templates/ffmpeg-settings.html @@ -91,10 +91,18 @@
+
Will transcode videos that have FPS higher than this. + +
+ + + Scaling algorithm to use when the transcoder needs to change the video size. +