diff --git a/lib/video/ffargs.js b/lib/video/ffargs.js index 9acf110..a08333a 100644 --- a/lib/video/ffargs.js +++ b/lib/video/ffargs.js @@ -64,13 +64,8 @@ exports.prepare = function (source, target, options) { args.push('-b:v', 0, '-crf', exports.crf(quality, encoder)) } - // AVCHD/MTS videos need a full-frame export to avoid interlacing artefacts - if (path.extname(source).toLowerCase() === '.mts') { - args.push('-vf', 'yadif=1') - } else if ((options.bitrate) && (options.hwaccel === 'vaapi')) { - // if VAAPI + here you to add the scaling option too ",scale_vaapi=1280:-1" - args.push('-vf', 'format=nv12|vaapi,hwupload') - } + const filters = exports.videoFilters(source, options) + args.push('-vf', filters) // target filename args.push(target) @@ -85,3 +80,22 @@ exports.crf = function (percent, encoder) { const inverted = ENCODER_CRF[encoder].max - proportion return Math.floor(inverted) } + +// Configure video filters +exports.videoFilters = function (source, options) { + const filters = [] + // AVCHD/MTS videos need a full-frame export to avoid interlacing artefacts + if (path.extname(source).toLowerCase() === '.mts') { + filters.push('yadif=1') + } + // Configure subsampling and hardware acceleration + if ((options.bitrate) && (options.hwaccel === 'vaapi')) { + // VAAPI requires nv12, which is equivalent to yuv420p for an h264 stream + // Here we could add scaling option too e.g. ",scale_vaapi=1280:-1" + filters.push('format=nv12|vaapi', 'hwupload') + } else { + // Standard case: use YUV420p which has the best compatibility + filters.push('format=yuv420p') + } + return filters.join(',') +} diff --git a/test/unit/ffargs.test.js b/test/unit/ffargs.test.js index 9dd62da..c8b31ea 100755 --- a/test/unit/ffargs.test.js +++ b/test/unit/ffargs.test.js @@ -24,10 +24,32 @@ describe('ffargs', () => { should(ffargs.crf(100, 'vpx')).eql(15) }) - it('handles MTS interlacing', () => { - const args = ffargs.prepare('source.mts', 'target.mp4', {}) - const str = args.join(' ') - should(str).match(/-vf yadif=1/) + describe('video filters', () => { + it('uses yuv420p chroma subsampling by default', () => { + // videos from recent iPhones use yuv420p10le + // once converted to h264 they don't play well in browsers / macOS finder + // ffmpeg recommends using yuv420p for best compatibility + // see http://trac.ffmpeg.org/wiki/Encode/H.264 + // and https://trac.ffmpeg.org/wiki/Encode/VP9 + const vf = ffargs.videoFilters('source.mov', {}) + should(vf).match(/format=yuv420p/) + }) + + it('handles MTS interlacing', () => { + const vf = ffargs.videoFilters('source.mts', {}) + should(vf).match(/yadif=1,format=yuv420p/) + }) + + it('handles VAAPI hardware acceleration', () => { + const vf = ffargs.videoFilters('source.mov', { hwaccel: 'vaapi', bitrate: '1200k' }) + should(vf).match(/format=nv12\|vaapi,hwupload/) + }) + + it('passes the video filter argument', () => { + const args = ffargs.prepare('source.mov', 'target.mp4', {}) + const str = args.join(' ') + should(str).match(/-vf format=yuv420p/) + }) }) describe('framerate', () => {