Skip to content

Commit

Permalink
Cleanup FFmpeg arguments and bitrate logic (was specified too late af…
Browse files Browse the repository at this point in the history
…ter the destination)
  • Loading branch information
rprieto committed Jan 1, 2019
1 parent 25535ce commit 1b5e1ba
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 13 deletions.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,23 +143,27 @@ This method supports all the same options as `.image()`.
```

Transcodes the video in `source` to a web-friendly format and lower bitrate, and writes it in `target`.
You can specify the following options:

#### format
##### Format

The default export format is mp4. You can specify an export format by adding a `format` option whose value can be `mp4` or `webm`.

```js
opts = { format: webm }
opts = { format: 'webm' }
```

#### bitrate
#### Variable bitrate

The default export bitrate depends on the codec used. You can specify an export bitrate by adding a `bitrate` option. Check [ffmpeg docmentation](https://trac.ffmpeg.org/wiki/Encode/H.264) for more information.
You can specify a variable bitrate (a.k.a. average bitrate, or target bitrate) by using the `bitrate` option.
Check the [ffmpeg docmentation](https://trac.ffmpeg.org/wiki/Encode/H.264) for more information.

```js
opts = { bitrate: xxx }
opts = { bitrate: '1200k' }
```

##### Conversion progress

The `.video()` call returns an [EventEmitter](https://nodejs.org/api/events.html)
to follow the progress of the conversion, since it can take a long time.

Expand Down
24 changes: 17 additions & 7 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const gmargs = require('./gmargs')
const ffmpeg = require('./ffmpeg')

const GIF_FILE = /\.gif$/i
const DEFAULT_PHOTO_QUALITY = 90 // percent
const DEFAULT_AUDIO_BITRATE = '96k'
const DEFAULT_VIDEO_FPS = 25

/*
Convert and/or resize an image
Expand Down Expand Up @@ -62,7 +65,7 @@ exports.image = function (source, target, options, callback) {
}

// default quality, for typical web-friendly sizes
image.quality(options.quality || 90)
image.quality(options.quality || DEFAULT_PHOTO_QUALITY)
// apply custom post-processing arguments (sharpen, brightness...)
gmargs.apply(image, options.args)
// write the output image
Expand All @@ -76,26 +79,33 @@ exports.image = function (source, target, options, callback) {
exports.video = function (source, target, options, callback) {
// create target folder if needed
mkdirp.sync(path.dirname(target))
const args = ['-i', source, '-r', '25', '-vsync', '2', '-movflags', '+faststart', '-y', target, '-ab', '96k']

// common options
const args = ['-i', source, '-r', DEFAULT_VIDEO_FPS, '-vsync', '2', '-movflags', '+faststart', '-ab', DEFAULT_AUDIO_BITRATE]

// output to mp4 or webm which are well read on the web
if (options.format === 'webm') {
args.push('-f', 'webm', '-vcodec', 'libvpx-vp9')
args.push('-f', 'webm', '-vcodec', 'libvpx-vp9', '-strict', '-2')
} else {
args.push('-f', 'mp4', '-vcodec', 'libx264')
}

// set video bitrate
// set average bitrate or perceptual quality
if (options.bitrate) {
args.push('-b:v', options.bitrate)
} else {
// TODO: add configurable CRF value
args.push('-b:v', 0, '-crf', 20)
}

// AVCHD/MTS videos need a full-frame export to avoid interlacing artefacts
if (path.extname(source).toLowerCase() === '.mts') {
args.push('-vf', 'yadif=1', '-qscale:v', '4')
} else {
args.push('-vb', '1200k')
args.push('-vf', 'yadif=1')
}

// target filename
args.push('-y', target)

// return a EventEmitter to follow progress, since this can take a long time
return ffmpeg.exec(args, callback)
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ exports.video = function (test, args) {
const expected = `test-data/expected/${args.expect}`
convert.video(input, actual, args.options, (err) => {
if (err) return test.end(err)
convert.still(actual, `${actual}.jpg`, args.options, (err) => {
convert.still(actual, `${actual}.jpg`, {}, (err) => {
if (err) return test.end(err)
compareImage(test, `${expected}.jpg`, `${actual}.jpg`)
})
Expand Down
22 changes: 22 additions & 0 deletions test/video.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ tape('can convert a video to webm', (test) => {
})
})

tape('can convert to MP4 with a target bitrate', (test) => {
diff.video(test, {
input: 'videos/countdown.mp4',
expect: 'videos/countdown-bitrate.mp4',
options: {
format: 'mp4',
bitrate: '100k'
}
})
})

tape('can convert to WEBM with a target bitrate', (test) => {
diff.video(test, {
input: 'videos/countdown.mp4',
expect: 'videos/countdown-bitrate.webm',
options: {
format: 'webm',
bitrate: '100k'
}
})
})

tape('can report progress when processing videos', (t) => {
const report = []
const input = 'test-data/input/videos/big_buck_bunny.mp4'
Expand Down

0 comments on commit 1b5e1ba

Please sign in to comment.