├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── Makefile ├── README.md ├── doc ├── FfmpegCommand.html ├── audio.js.html ├── capabilities.js.html ├── custom.js.html ├── ffprobe.js.html ├── fluent-ffmpeg.js.html ├── global.html ├── index.html ├── inputs.js.html ├── misc.js.html ├── options_audio.js.html ├── options_custom.js.html ├── options_inputs.js.html ├── options_misc.js.html ├── options_output.js.html ├── options_video.js.html ├── options_videosize.js.html ├── output.js.html ├── processor.js.html ├── recipes.js.html ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── lang-css.js │ │ └── prettify.js ├── styles │ ├── jsdoc-default.css │ ├── prettify-jsdoc.css │ └── prettify-tomorrow.css ├── utils.js.html ├── video.js.html └── videosize.js.html ├── examples ├── any-to-mp4-steam.js ├── express-stream.js ├── flowplayer │ ├── flowplayer.controls.swf │ ├── flowplayer.min.js │ ├── flowplayer.swf │ └── index.html ├── full.js ├── image2video.js ├── input-stream.js ├── livertmp2hls.js ├── mergeVideos.js ├── metadata.js ├── preset.js ├── progress.js ├── stream.js └── thumbnails.js ├── index.js ├── lib ├── capabilities.js ├── ffprobe.js ├── fluent-ffmpeg.js ├── options │ ├── audio.js │ ├── custom.js │ ├── inputs.js │ ├── misc.js │ ├── output.js │ ├── video.js │ └── videosize.js ├── presets │ ├── divx.js │ ├── flashvideo.js │ └── podcast.js ├── processor.js ├── recipes.js └── utils.js ├── package.json ├── test ├── aliases.test.js ├── args.test.js ├── assets │ ├── ffserver.conf │ ├── presets │ │ └── custompreset.js │ ├── te[s]t_ video ' _ .flv │ ├── testaudio-one.wav │ ├── testaudio-three.wav │ ├── testaudio-two.wav │ ├── teststream.ffm │ ├── testvideo-169.avi │ ├── testvideo-43.avi │ └── testvideo-5m.mpg ├── capabilities.test.js ├── helpers.js ├── metadata.test.js ├── processor.test.js └── utils.test.js ├── tools ├── jsdoc-aliases.js ├── jsdoc-conf.json └── jsdoc-template │ ├── README.md │ ├── publish.js │ ├── static │ ├── scripts │ │ ├── linenumber.js │ │ └── prettify │ │ │ ├── Apache-License-2.0.txt │ │ │ ├── lang-css.js │ │ │ └── prettify.js │ └── styles │ │ ├── jsdoc-default.css │ │ ├── prettify-jsdoc.css │ │ └── prettify-tomorrow.css │ └── tmpl │ ├── aliases.tmpl │ ├── container.tmpl │ ├── details.tmpl │ ├── example.tmpl │ ├── examples.tmpl │ ├── exceptions.tmpl │ ├── layout.tmpl │ ├── mainpage.tmpl │ ├── members.tmpl │ ├── method.tmpl │ ├── params.tmpl │ ├── properties.tmpl │ ├── returns.tmpl │ ├── source.tmpl │ ├── tutorial.tmpl │ └── type.tmpl └── yarn.lock /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | ## Reporting issues 4 | 5 | Please start by [reading the FAQ][faq] first. If your question is not answered, here are some guidelines on how to effectively report issues. 6 | 7 | ### Required information 8 | 9 | When reporting issues be sure to include at least: 10 | 11 | * Some code that may be used to reproduce the problem 12 | * Which version of fluent-ffmpeg, of ffmpeg and which OS you're using 13 | * If the problem only happens with some input sources, please include a link to a source that may be used to reproduce the problem 14 | * Be sure to include the full error message when there is one 15 | * When an ffmpeg error happens (eg. 'ffmpeg exited with code 1'), you should also include the full output from ffmpeg (stdout and stderr), as it may contain useful information about what whent wrong. You can do that by looking at the 2nd and 3rd parameters of the `error` event handler on an FfmpegCommand, for example: 16 | 17 | ```js 18 | ffmpeg('some/input.mp4') 19 | .on('error', function(err, stdout, stderr) { 20 | console.log('An error happened: ' + err.message); 21 | console.log('ffmpeg standard output:\n' + stdout); 22 | console.log('ffmpeg standard error:\n' + stderr); 23 | }); 24 | ``` 25 | 26 | ### Ffmpeg usage 27 | 28 | If your command ends up with an ffmpeg error (eg. 'ffmpeg exited with code X : ...'), be sure to try the command manually from command line. You can get the ffmpeg command line that is executed for a specific Fluent-ffmpeg command by using the `start` event: 29 | 30 | ```js 31 | ffmpeg('some/input.mp4') 32 | .on('start', function(cmdline) { 33 | console.log('Command line: ' + cmdline); 34 | }) 35 | ... 36 | ``` 37 | 38 | If it does not work, you most likely have a ffmpeg-related problem that does not fit as a fluent-ffmpeg issue; in that case head to the [ffmpeg documentation](ffmpeg.org/documentation.html) to find out what you did wrong. 39 | 40 | If it _does_ work, please double-check how you escaped arguments and options when passing them to fluent-ffmpeg. For example, when running from command line, you may have to quote arguments to tell your shell that a space is indeed part of a filename or option. When using fluent-ffmpeg, you don't have to do that, as you're already passing options and arguments separately. Here's a (dumb) example: 41 | 42 | ```sh 43 | $ ffmpeg -i video with spaces.avi 44 | Cannot find "video", or unknown option "with", etc... 45 | $ ffmpeg -i "video with spaces.avi" 46 | Works 47 | ``` 48 | 49 | ```js 50 | // Works 51 | ffmpeg('video with spaces.avi')...; 52 | 53 | // Fails, looks for a file with actual double quotes in its name 54 | ffmpeg('"video with spaces.avi"')...; 55 | ``` 56 | 57 | [faq]: https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/wiki/FAQ 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | <!-- Please fill the following template --> 2 | 3 | ### Version information 4 | 5 | * fluent-ffmpeg version: 6 | * ffmpeg version: 7 | * OS: 8 | 9 | ### Code to reproduce 10 | 11 | ```js 12 | 13 | 14 | ``` 15 | 16 | (note: if the problem only happens with some inputs, include a link to such an input file) 17 | 18 | ### Expected results 19 | 20 | 21 | 22 | ### Observed results 23 | 24 | 25 | 26 | ### Checklist 27 | 28 | <!-- you may delete that checklist when you have checked everything --> 29 | 30 | * [ ] I have read the [FAQ](https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/wiki/FAQ) 31 | * [ ] I tried the same with command line ffmpeg and it works correctly (hint: if the problem also happens this way, this is an ffmpeg problem and you're not reporting it to the right place) 32 | * [ ] I have included full stderr/stdout output from ffmpeg 33 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI Testing 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | jobs: 8 | test: 9 | name: Run tests 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node: [18, 20, 21] 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | - name: Install flvtool2 18 | run: sudo gem install flvtool2 19 | - name: Install ffmpeg 20 | run: sudo apt install -y ffmpeg 21 | - name: Setup node 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: ${{ matrix.node }} 25 | cache: yarn 26 | - name: Install dependencies 27 | run: yarn 28 | - name: Run tests 29 | run: yarn test 30 | - name: Generate coverage report 31 | run: yarn coverage 32 | - name: Store coveralls coverage 33 | uses: coverallsapp/github-action@v2 34 | with: 35 | flag-name: linux-node-${{ matrix.node }} 36 | parallel: true 37 | - name: Upload to codecov 38 | uses: codecov/codecov-action@v3 39 | env: 40 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 41 | with: 42 | files: coverage/lcov.info 43 | name: ubuntu-latest-node-${{ matrix.node }} 44 | 45 | upload-coverage: 46 | name: Upload coverage 47 | needs: test 48 | if: ${{ always() }} 49 | runs-on: ubuntu-latest 50 | steps: 51 | - name: Upload to coveralls 52 | uses: coverallsapp/github-action@v2 53 | with: 54 | parallel-finished: true 55 | carryforward: "linux-node-18,linux-node-20,linux-node-21" 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.project 2 | node_modules 3 | .nyc_output 4 | *.swp 5 | .idea 6 | *.iml 7 | coverage 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.md 2 | .git* 3 | test/ 4 | examples/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2011-2015 The fluent-ffmpeg contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REPORTER = spec 2 | MOCHA = node_modules/.bin/mocha 3 | 4 | test: 5 | @NODE_ENV=test $(MOCHA) --require should --reporter $(REPORTER) 6 | 7 | test-colors: 8 | @NODE_ENV=test $(MOCHA) --require should --reporter $(REPORTER) --colors 9 | 10 | publish: 11 | @npm version patch -m "version bump" 12 | @npm publish 13 | 14 | JSDOC = node_modules/.bin/jsdoc 15 | JSDOC_CONF = tools/jsdoc-conf.json 16 | 17 | doc: 18 | $(JSDOC) --configure $(JSDOC_CONF) 19 | 20 | .PHONY: test test-colors publish doc -------------------------------------------------------------------------------- /doc/audio.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/audio.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/audio.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var utils = require('../utils'); 32 | 33 | 34 | /* 35 | *! Audio-related methods 36 | */ 37 | 38 | module.exports = function(proto) { 39 | /** 40 | * Disable audio in the output 41 | * 42 | * @method FfmpegCommand#noAudio 43 | * @category Audio 44 | * @aliases withNoAudio 45 | * @return FfmpegCommand 46 | */ 47 | proto.withNoAudio = 48 | proto.noAudio = function() { 49 | this._currentOutput.audio.clear(); 50 | this._currentOutput.audioFilters.clear(); 51 | this._currentOutput.audio('-an'); 52 | 53 | return this; 54 | }; 55 | 56 | 57 | /** 58 | * Specify audio codec 59 | * 60 | * @method FfmpegCommand#audioCodec 61 | * @category Audio 62 | * @aliases withAudioCodec 63 | * 64 | * @param {String} codec audio codec name 65 | * @return FfmpegCommand 66 | */ 67 | proto.withAudioCodec = 68 | proto.audioCodec = function(codec) { 69 | this._currentOutput.audio('-acodec', codec); 70 | 71 | return this; 72 | }; 73 | 74 | 75 | /** 76 | * Specify audio bitrate 77 | * 78 | * @method FfmpegCommand#audioBitrate 79 | * @category Audio 80 | * @aliases withAudioBitrate 81 | * 82 | * @param {String|Number} bitrate audio bitrate in kbps (with an optional 'k' suffix) 83 | * @return FfmpegCommand 84 | */ 85 | proto.withAudioBitrate = 86 | proto.audioBitrate = function(bitrate) { 87 | this._currentOutput.audio('-b:a', ('' + bitrate).replace(/k?$/, 'k')); 88 | return this; 89 | }; 90 | 91 | 92 | /** 93 | * Specify audio channel count 94 | * 95 | * @method FfmpegCommand#audioChannels 96 | * @category Audio 97 | * @aliases withAudioChannels 98 | * 99 | * @param {Number} channels channel count 100 | * @return FfmpegCommand 101 | */ 102 | proto.withAudioChannels = 103 | proto.audioChannels = function(channels) { 104 | this._currentOutput.audio('-ac', channels); 105 | return this; 106 | }; 107 | 108 | 109 | /** 110 | * Specify audio frequency 111 | * 112 | * @method FfmpegCommand#audioFrequency 113 | * @category Audio 114 | * @aliases withAudioFrequency 115 | * 116 | * @param {Number} freq audio frequency in Hz 117 | * @return FfmpegCommand 118 | */ 119 | proto.withAudioFrequency = 120 | proto.audioFrequency = function(freq) { 121 | this._currentOutput.audio('-ar', freq); 122 | return this; 123 | }; 124 | 125 | 126 | /** 127 | * Specify audio quality 128 | * 129 | * @method FfmpegCommand#audioQuality 130 | * @category Audio 131 | * @aliases withAudioQuality 132 | * 133 | * @param {Number} quality audio quality factor 134 | * @return FfmpegCommand 135 | */ 136 | proto.withAudioQuality = 137 | proto.audioQuality = function(quality) { 138 | this._currentOutput.audio('-aq', quality); 139 | return this; 140 | }; 141 | 142 | 143 | /** 144 | * Specify custom audio filter(s) 145 | * 146 | * Can be called both with one or many filters, or a filter array. 147 | * 148 | * @example 149 | * command.audioFilters('filter1'); 150 | * 151 | * @example 152 | * command.audioFilters('filter1', 'filter2=param1=value1:param2=value2'); 153 | * 154 | * @example 155 | * command.audioFilters(['filter1', 'filter2']); 156 | * 157 | * @example 158 | * command.audioFilters([ 159 | * { 160 | * filter: 'filter1' 161 | * }, 162 | * { 163 | * filter: 'filter2', 164 | * options: 'param=value:param=value' 165 | * } 166 | * ]); 167 | * 168 | * @example 169 | * command.audioFilters( 170 | * { 171 | * filter: 'filter1', 172 | * options: ['value1', 'value2'] 173 | * }, 174 | * { 175 | * filter: 'filter2', 176 | * options: { param1: 'value1', param2: 'value2' } 177 | * } 178 | * ); 179 | * 180 | * @method FfmpegCommand#audioFilters 181 | * @aliases withAudioFilter,withAudioFilters,audioFilter 182 | * @category Audio 183 | * 184 | * @param {...String|String[]|Object[]} filters audio filter strings, string array or 185 | * filter specification array, each with the following properties: 186 | * @param {String} filters.filter filter name 187 | * @param {String|String[]|Object} [filters.options] filter option string, array, or object 188 | * @return FfmpegCommand 189 | */ 190 | proto.withAudioFilter = 191 | proto.withAudioFilters = 192 | proto.audioFilter = 193 | proto.audioFilters = function(filters) { 194 | if (arguments.length > 1) { 195 | filters = [].slice.call(arguments); 196 | } 197 | 198 | if (!Array.isArray(filters)) { 199 | filters = [filters]; 200 | } 201 | 202 | this._currentOutput.audioFilters(utils.makeFilterStrings(filters)); 203 | return this; 204 | }; 205 | }; 206 | </code></pre> 207 | </article> 208 | </section> 209 | 210 | 211 | 212 | 213 | </div> 214 | 215 | <nav> 216 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 217 | </nav> 218 | 219 | <br clear="both"> 220 | 221 | <footer> 222 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha5</a> on Tue Jul 08 2014 21:22:19 GMT+0200 (CEST) 223 | </footer> 224 | 225 | <script> prettyPrint(); </script> 226 | <script src="scripts/linenumber.js"> </script> 227 | </body> 228 | </html> 229 | -------------------------------------------------------------------------------- /doc/inputs.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/inputs.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/inputs.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var utils = require('../utils'); 32 | 33 | /* 34 | *! Input-related methods 35 | */ 36 | 37 | module.exports = function(proto) { 38 | /** 39 | * Add an input to command 40 | * 41 | * Also switches "current input", that is the input that will be affected 42 | * by subsequent input-related methods. 43 | * 44 | * Note: only one stream input is supported for now. 45 | * 46 | * @method FfmpegCommand#input 47 | * @category Input 48 | * @aliases mergeAdd,addInput 49 | * 50 | * @param {String|Readable} source input file path or readable stream 51 | * @return FfmpegCommand 52 | */ 53 | proto.mergeAdd = 54 | proto.addInput = 55 | proto.input = function(source) { 56 | var isFile = false; 57 | 58 | if (typeof source !== 'string') { 59 | if (!('readable' in source) || !(source.readable)) { 60 | throw new Error('Invalid input'); 61 | } 62 | 63 | var hasInputStream = this._inputs.some(function(input) { 64 | return typeof input.source !== 'string'; 65 | }); 66 | 67 | if (hasInputStream) { 68 | throw new Error('Only one input stream is supported'); 69 | } 70 | 71 | source.pause(); 72 | } else { 73 | var protocol = source.match(/^([a-z]{2,}):/i); 74 | isFile = !protocol || protocol[0] === 'file'; 75 | } 76 | 77 | this._inputs.push(this._currentInput = { 78 | source: source, 79 | isFile: isFile, 80 | options: utils.args() 81 | }); 82 | 83 | return this; 84 | }; 85 | 86 | 87 | /** 88 | * Specify input format for the last specified input 89 | * 90 | * @method FfmpegCommand#inputFormat 91 | * @category Input 92 | * @aliases withInputFormat,fromFormat 93 | * 94 | * @param {String} format input format 95 | * @return FfmpegCommand 96 | */ 97 | proto.withInputFormat = 98 | proto.inputFormat = 99 | proto.fromFormat = function(format) { 100 | if (!this._currentInput) { 101 | throw new Error('No input specified'); 102 | } 103 | 104 | this._currentInput.options('-f', format); 105 | return this; 106 | }; 107 | 108 | 109 | /** 110 | * Specify input FPS for the last specified input 111 | * (only valid for raw video formats) 112 | * 113 | * @method FfmpegCommand#inputFps 114 | * @category Input 115 | * @aliases withInputFps,withInputFPS,withFpsInput,withFPSInput,inputFPS,inputFps,fpsInput 116 | * 117 | * @param {Number} fps input FPS 118 | * @return FfmpegCommand 119 | */ 120 | proto.withInputFps = 121 | proto.withInputFPS = 122 | proto.withFpsInput = 123 | proto.withFPSInput = 124 | proto.inputFPS = 125 | proto.inputFps = 126 | proto.fpsInput = 127 | proto.FPSInput = function(fps) { 128 | if (!this._currentInput) { 129 | throw new Error('No input specified'); 130 | } 131 | 132 | this._currentInput.options('-r', fps); 133 | return this; 134 | }; 135 | 136 | 137 | /** 138 | * Use native framerate for the last specified input 139 | * 140 | * @method FfmpegCommand#native 141 | * @category Input 142 | * @aliases nativeFramerate,withNativeFramerate 143 | * 144 | * @return FfmmegCommand 145 | */ 146 | proto.nativeFramerate = 147 | proto.withNativeFramerate = 148 | proto.native = function() { 149 | if (!this._currentInput) { 150 | throw new Error('No input specified'); 151 | } 152 | 153 | this._currentInput.options('-re'); 154 | return this; 155 | }; 156 | 157 | 158 | /** 159 | * Specify input seek time for the last specified input 160 | * 161 | * @method FfmpegCommand#seekInput 162 | * @category Input 163 | * @aliases setStartTime,seekTo 164 | * 165 | * @param {String|Number} seek seek time in seconds or as a '[hh:[mm:]]ss[.xxx]' string 166 | * @return FfmpegCommand 167 | */ 168 | proto.setStartTime = 169 | proto.seekInput = function(seek) { 170 | if (!this._currentInput) { 171 | throw new Error('No input specified'); 172 | } 173 | 174 | this._currentInput.options('-ss', seek); 175 | 176 | return this; 177 | }; 178 | 179 | 180 | /** 181 | * Loop over the last specified input 182 | * 183 | * @method FfmpegCommand#loop 184 | * @category Input 185 | * 186 | * @param {String|Number} [duration] loop duration in seconds or as a '[[hh:]mm:]ss[.xxx]' string 187 | * @return FfmpegCommand 188 | */ 189 | proto.loop = function(duration) { 190 | if (!this._currentInput) { 191 | throw new Error('No input specified'); 192 | } 193 | 194 | this._currentInput.options('-loop', '1'); 195 | 196 | if (typeof duration !== 'undefined') { 197 | this.duration(duration); 198 | } 199 | 200 | return this; 201 | }; 202 | }; 203 | </code></pre> 204 | </article> 205 | </section> 206 | 207 | 208 | 209 | 210 | </div> 211 | 212 | <nav> 213 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 214 | </nav> 215 | 216 | <br clear="both"> 217 | 218 | <footer> 219 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha5</a> on Tue Jul 08 2014 21:22:19 GMT+0200 (CEST) 220 | </footer> 221 | 222 | <script> prettyPrint(); </script> 223 | <script src="scripts/linenumber.js"> </script> 224 | </body> 225 | </html> 226 | -------------------------------------------------------------------------------- /doc/misc.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/misc.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/misc.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var path = require('path'); 32 | 33 | /* 34 | *! Miscellaneous methods 35 | */ 36 | 37 | module.exports = function(proto) { 38 | /** 39 | * Use preset 40 | * 41 | * @method FfmpegCommand#preset 42 | * @category Miscellaneous 43 | * @aliases usingPreset 44 | * 45 | * @param {String|Function} preset preset name or preset function 46 | */ 47 | proto.usingPreset = 48 | proto.preset = function(preset) { 49 | if (typeof preset === 'function') { 50 | preset(this); 51 | } else { 52 | try { 53 | var modulePath = path.join(this.options.presets, preset); 54 | var module = require(modulePath); 55 | 56 | if (typeof module.load === 'function') { 57 | module.load(this); 58 | } else { 59 | throw new Error('preset ' + modulePath + ' has no load() function'); 60 | } 61 | } catch (err) { 62 | throw new Error('preset ' + modulePath + ' could not be loaded: ' + err.message); 63 | } 64 | } 65 | 66 | return this; 67 | }; 68 | }; 69 | </code></pre> 70 | </article> 71 | </section> 72 | 73 | 74 | 75 | 76 | </div> 77 | 78 | <nav> 79 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 80 | </nav> 81 | 82 | <br clear="both"> 83 | 84 | <footer> 85 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha5</a> on Tue Jul 08 2014 21:22:19 GMT+0200 (CEST) 86 | </footer> 87 | 88 | <script> prettyPrint(); </script> 89 | <script src="scripts/linenumber.js"> </script> 90 | </body> 91 | </html> 92 | -------------------------------------------------------------------------------- /doc/options_audio.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/audio.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/audio.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var utils = require('../utils'); 32 | 33 | 34 | /* 35 | *! Audio-related methods 36 | */ 37 | 38 | module.exports = function(proto) { 39 | /** 40 | * Disable audio in the output 41 | * 42 | * @method FfmpegCommand#noAudio 43 | * @category Audio 44 | * @aliases withNoAudio 45 | * @return FfmpegCommand 46 | */ 47 | proto.withNoAudio = 48 | proto.noAudio = function() { 49 | this._currentOutput.audio.clear(); 50 | this._currentOutput.audioFilters.clear(); 51 | this._currentOutput.audio('-an'); 52 | 53 | return this; 54 | }; 55 | 56 | 57 | /** 58 | * Specify audio codec 59 | * 60 | * @method FfmpegCommand#audioCodec 61 | * @category Audio 62 | * @aliases withAudioCodec 63 | * 64 | * @param {String} codec audio codec name 65 | * @return FfmpegCommand 66 | */ 67 | proto.withAudioCodec = 68 | proto.audioCodec = function(codec) { 69 | this._currentOutput.audio('-acodec', codec); 70 | 71 | return this; 72 | }; 73 | 74 | 75 | /** 76 | * Specify audio bitrate 77 | * 78 | * @method FfmpegCommand#audioBitrate 79 | * @category Audio 80 | * @aliases withAudioBitrate 81 | * 82 | * @param {String|Number} bitrate audio bitrate in kbps (with an optional 'k' suffix) 83 | * @return FfmpegCommand 84 | */ 85 | proto.withAudioBitrate = 86 | proto.audioBitrate = function(bitrate) { 87 | this._currentOutput.audio('-b:a', ('' + bitrate).replace(/k?$/, 'k')); 88 | return this; 89 | }; 90 | 91 | 92 | /** 93 | * Specify audio channel count 94 | * 95 | * @method FfmpegCommand#audioChannels 96 | * @category Audio 97 | * @aliases withAudioChannels 98 | * 99 | * @param {Number} channels channel count 100 | * @return FfmpegCommand 101 | */ 102 | proto.withAudioChannels = 103 | proto.audioChannels = function(channels) { 104 | this._currentOutput.audio('-ac', channels); 105 | return this; 106 | }; 107 | 108 | 109 | /** 110 | * Specify audio frequency 111 | * 112 | * @method FfmpegCommand#audioFrequency 113 | * @category Audio 114 | * @aliases withAudioFrequency 115 | * 116 | * @param {Number} freq audio frequency in Hz 117 | * @return FfmpegCommand 118 | */ 119 | proto.withAudioFrequency = 120 | proto.audioFrequency = function(freq) { 121 | this._currentOutput.audio('-ar', freq); 122 | return this; 123 | }; 124 | 125 | 126 | /** 127 | * Specify audio quality 128 | * 129 | * @method FfmpegCommand#audioQuality 130 | * @category Audio 131 | * @aliases withAudioQuality 132 | * 133 | * @param {Number} quality audio quality factor 134 | * @return FfmpegCommand 135 | */ 136 | proto.withAudioQuality = 137 | proto.audioQuality = function(quality) { 138 | this._currentOutput.audio('-aq', quality); 139 | return this; 140 | }; 141 | 142 | 143 | /** 144 | * Specify custom audio filter(s) 145 | * 146 | * Can be called both with one or many filters, or a filter array. 147 | * 148 | * @example 149 | * command.audioFilters('filter1'); 150 | * 151 | * @example 152 | * command.audioFilters('filter1', 'filter2=param1=value1:param2=value2'); 153 | * 154 | * @example 155 | * command.audioFilters(['filter1', 'filter2']); 156 | * 157 | * @example 158 | * command.audioFilters([ 159 | * { 160 | * filter: 'filter1' 161 | * }, 162 | * { 163 | * filter: 'filter2', 164 | * options: 'param=value:param=value' 165 | * } 166 | * ]); 167 | * 168 | * @example 169 | * command.audioFilters( 170 | * { 171 | * filter: 'filter1', 172 | * options: ['value1', 'value2'] 173 | * }, 174 | * { 175 | * filter: 'filter2', 176 | * options: { param1: 'value1', param2: 'value2' } 177 | * } 178 | * ); 179 | * 180 | * @method FfmpegCommand#audioFilters 181 | * @aliases withAudioFilter,withAudioFilters,audioFilter 182 | * @category Audio 183 | * 184 | * @param {...String|String[]|Object[]} filters audio filter strings, string array or 185 | * filter specification array, each with the following properties: 186 | * @param {String} filters.filter filter name 187 | * @param {String|String[]|Object} [filters.options] filter option string, array, or object 188 | * @return FfmpegCommand 189 | */ 190 | proto.withAudioFilter = 191 | proto.withAudioFilters = 192 | proto.audioFilter = 193 | proto.audioFilters = function(filters) { 194 | if (arguments.length > 1) { 195 | filters = [].slice.call(arguments); 196 | } 197 | 198 | if (!Array.isArray(filters)) { 199 | filters = [filters]; 200 | } 201 | 202 | this._currentOutput.audioFilters(utils.makeFilterStrings(filters)); 203 | return this; 204 | }; 205 | }; 206 | </code></pre> 207 | </article> 208 | </section> 209 | 210 | 211 | 212 | 213 | </div> 214 | 215 | <nav> 216 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 217 | </nav> 218 | 219 | <br clear="both"> 220 | 221 | <footer> 222 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun May 01 2016 12:10:37 GMT+0200 (CEST) 223 | </footer> 224 | 225 | <script> prettyPrint(); </script> 226 | <script src="scripts/linenumber.js"> </script> 227 | </body> 228 | </html> 229 | -------------------------------------------------------------------------------- /doc/options_inputs.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/inputs.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/inputs.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var utils = require('../utils'); 32 | 33 | /* 34 | *! Input-related methods 35 | */ 36 | 37 | module.exports = function(proto) { 38 | /** 39 | * Add an input to command 40 | * 41 | * Also switches "current input", that is the input that will be affected 42 | * by subsequent input-related methods. 43 | * 44 | * Note: only one stream input is supported for now. 45 | * 46 | * @method FfmpegCommand#input 47 | * @category Input 48 | * @aliases mergeAdd,addInput 49 | * 50 | * @param {String|Readable} source input file path or readable stream 51 | * @return FfmpegCommand 52 | */ 53 | proto.mergeAdd = 54 | proto.addInput = 55 | proto.input = function(source) { 56 | var isFile = false; 57 | var isStream = false; 58 | 59 | if (typeof source !== 'string') { 60 | if (!('readable' in source) || !(source.readable)) { 61 | throw new Error('Invalid input'); 62 | } 63 | 64 | var hasInputStream = this._inputs.some(function(input) { 65 | return input.isStream; 66 | }); 67 | 68 | if (hasInputStream) { 69 | throw new Error('Only one input stream is supported'); 70 | } 71 | 72 | isStream = true; 73 | source.pause(); 74 | } else { 75 | var protocol = source.match(/^([a-z]{2,}):/i); 76 | isFile = !protocol || protocol[0] === 'file'; 77 | } 78 | 79 | this._inputs.push(this._currentInput = { 80 | source: source, 81 | isFile: isFile, 82 | isStream: isStream, 83 | options: utils.args() 84 | }); 85 | 86 | return this; 87 | }; 88 | 89 | 90 | /** 91 | * Specify input format for the last specified input 92 | * 93 | * @method FfmpegCommand#inputFormat 94 | * @category Input 95 | * @aliases withInputFormat,fromFormat 96 | * 97 | * @param {String} format input format 98 | * @return FfmpegCommand 99 | */ 100 | proto.withInputFormat = 101 | proto.inputFormat = 102 | proto.fromFormat = function(format) { 103 | if (!this._currentInput) { 104 | throw new Error('No input specified'); 105 | } 106 | 107 | this._currentInput.options('-f', format); 108 | return this; 109 | }; 110 | 111 | 112 | /** 113 | * Specify input FPS for the last specified input 114 | * (only valid for raw video formats) 115 | * 116 | * @method FfmpegCommand#inputFps 117 | * @category Input 118 | * @aliases withInputFps,withInputFPS,withFpsInput,withFPSInput,inputFPS,inputFps,fpsInput 119 | * 120 | * @param {Number} fps input FPS 121 | * @return FfmpegCommand 122 | */ 123 | proto.withInputFps = 124 | proto.withInputFPS = 125 | proto.withFpsInput = 126 | proto.withFPSInput = 127 | proto.inputFPS = 128 | proto.inputFps = 129 | proto.fpsInput = 130 | proto.FPSInput = function(fps) { 131 | if (!this._currentInput) { 132 | throw new Error('No input specified'); 133 | } 134 | 135 | this._currentInput.options('-r', fps); 136 | return this; 137 | }; 138 | 139 | 140 | /** 141 | * Use native framerate for the last specified input 142 | * 143 | * @method FfmpegCommand#native 144 | * @category Input 145 | * @aliases nativeFramerate,withNativeFramerate 146 | * 147 | * @return FfmmegCommand 148 | */ 149 | proto.nativeFramerate = 150 | proto.withNativeFramerate = 151 | proto.native = function() { 152 | if (!this._currentInput) { 153 | throw new Error('No input specified'); 154 | } 155 | 156 | this._currentInput.options('-re'); 157 | return this; 158 | }; 159 | 160 | 161 | /** 162 | * Specify input seek time for the last specified input 163 | * 164 | * @method FfmpegCommand#seekInput 165 | * @category Input 166 | * @aliases setStartTime,seekTo 167 | * 168 | * @param {String|Number} seek seek time in seconds or as a '[hh:[mm:]]ss[.xxx]' string 169 | * @return FfmpegCommand 170 | */ 171 | proto.setStartTime = 172 | proto.seekInput = function(seek) { 173 | if (!this._currentInput) { 174 | throw new Error('No input specified'); 175 | } 176 | 177 | this._currentInput.options('-ss', seek); 178 | 179 | return this; 180 | }; 181 | 182 | 183 | /** 184 | * Loop over the last specified input 185 | * 186 | * @method FfmpegCommand#loop 187 | * @category Input 188 | * 189 | * @param {String|Number} [duration] loop duration in seconds or as a '[[hh:]mm:]ss[.xxx]' string 190 | * @return FfmpegCommand 191 | */ 192 | proto.loop = function(duration) { 193 | if (!this._currentInput) { 194 | throw new Error('No input specified'); 195 | } 196 | 197 | this._currentInput.options('-loop', '1'); 198 | 199 | if (typeof duration !== 'undefined') { 200 | this.duration(duration); 201 | } 202 | 203 | return this; 204 | }; 205 | }; 206 | </code></pre> 207 | </article> 208 | </section> 209 | 210 | 211 | 212 | 213 | </div> 214 | 215 | <nav> 216 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 217 | </nav> 218 | 219 | <br clear="both"> 220 | 221 | <footer> 222 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun May 01 2016 12:10:37 GMT+0200 (CEST) 223 | </footer> 224 | 225 | <script> prettyPrint(); </script> 226 | <script src="scripts/linenumber.js"> </script> 227 | </body> 228 | </html> 229 | -------------------------------------------------------------------------------- /doc/options_misc.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/misc.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/misc.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var path = require('path'); 32 | 33 | /* 34 | *! Miscellaneous methods 35 | */ 36 | 37 | module.exports = function(proto) { 38 | /** 39 | * Use preset 40 | * 41 | * @method FfmpegCommand#preset 42 | * @category Miscellaneous 43 | * @aliases usingPreset 44 | * 45 | * @param {String|Function} preset preset name or preset function 46 | */ 47 | proto.usingPreset = 48 | proto.preset = function(preset) { 49 | if (typeof preset === 'function') { 50 | preset(this); 51 | } else { 52 | try { 53 | var modulePath = path.join(this.options.presets, preset); 54 | var module = require(modulePath); 55 | 56 | if (typeof module.load === 'function') { 57 | module.load(this); 58 | } else { 59 | throw new Error('preset ' + modulePath + ' has no load() function'); 60 | } 61 | } catch (err) { 62 | throw new Error('preset ' + modulePath + ' could not be loaded: ' + err.message); 63 | } 64 | } 65 | 66 | return this; 67 | }; 68 | }; 69 | </code></pre> 70 | </article> 71 | </section> 72 | 73 | 74 | 75 | 76 | </div> 77 | 78 | <nav> 79 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 80 | </nav> 81 | 82 | <br clear="both"> 83 | 84 | <footer> 85 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun May 01 2016 12:10:37 GMT+0200 (CEST) 86 | </footer> 87 | 88 | <script> prettyPrint(); </script> 89 | <script src="scripts/linenumber.js"> </script> 90 | </body> 91 | </html> 92 | -------------------------------------------------------------------------------- /doc/options_output.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/output.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/output.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var utils = require('../utils'); 32 | 33 | 34 | /* 35 | *! Output-related methods 36 | */ 37 | 38 | module.exports = function(proto) { 39 | /** 40 | * Add output 41 | * 42 | * @method FfmpegCommand#output 43 | * @category Output 44 | * @aliases addOutput 45 | * 46 | * @param {String|Writable} target target file path or writable stream 47 | * @param {Object} [pipeopts={}] pipe options (only applies to streams) 48 | * @return FfmpegCommand 49 | */ 50 | proto.addOutput = 51 | proto.output = function(target, pipeopts) { 52 | var isFile = false; 53 | 54 | if (!target && this._currentOutput) { 55 | // No target is only allowed when called from constructor 56 | throw new Error('Invalid output'); 57 | } 58 | 59 | if (target && typeof target !== 'string') { 60 | if (!('writable' in target) || !(target.writable)) { 61 | throw new Error('Invalid output'); 62 | } 63 | } else if (typeof target === 'string') { 64 | var protocol = target.match(/^([a-z]{2,}):/i); 65 | isFile = !protocol || protocol[0] === 'file'; 66 | } 67 | 68 | if (target && !('target' in this._currentOutput)) { 69 | // For backwards compatibility, set target for first output 70 | this._currentOutput.target = target; 71 | this._currentOutput.isFile = isFile; 72 | this._currentOutput.pipeopts = pipeopts || {}; 73 | } else { 74 | if (target && typeof target !== 'string') { 75 | var hasOutputStream = this._outputs.some(function(output) { 76 | return typeof output.target !== 'string'; 77 | }); 78 | 79 | if (hasOutputStream) { 80 | throw new Error('Only one output stream is supported'); 81 | } 82 | } 83 | 84 | this._outputs.push(this._currentOutput = { 85 | target: target, 86 | isFile: isFile, 87 | flags: {}, 88 | pipeopts: pipeopts || {} 89 | }); 90 | 91 | var self = this; 92 | ['audio', 'audioFilters', 'video', 'videoFilters', 'sizeFilters', 'options'].forEach(function(key) { 93 | self._currentOutput[key] = utils.args(); 94 | }); 95 | 96 | if (!target) { 97 | // Call from constructor: remove target key 98 | delete this._currentOutput.target; 99 | } 100 | } 101 | 102 | return this; 103 | }; 104 | 105 | 106 | /** 107 | * Specify output seek time 108 | * 109 | * @method FfmpegCommand#seek 110 | * @category Input 111 | * @aliases seekOutput 112 | * 113 | * @param {String|Number} seek seek time in seconds or as a '[hh:[mm:]]ss[.xxx]' string 114 | * @return FfmpegCommand 115 | */ 116 | proto.seekOutput = 117 | proto.seek = function(seek) { 118 | this._currentOutput.options('-ss', seek); 119 | return this; 120 | }; 121 | 122 | 123 | /** 124 | * Set output duration 125 | * 126 | * @method FfmpegCommand#duration 127 | * @category Output 128 | * @aliases withDuration,setDuration 129 | * 130 | * @param {String|Number} duration duration in seconds or as a '[[hh:]mm:]ss[.xxx]' string 131 | * @return FfmpegCommand 132 | */ 133 | proto.withDuration = 134 | proto.setDuration = 135 | proto.duration = function(duration) { 136 | this._currentOutput.options('-t', duration); 137 | return this; 138 | }; 139 | 140 | 141 | /** 142 | * Set output format 143 | * 144 | * @method FfmpegCommand#format 145 | * @category Output 146 | * @aliases toFormat,withOutputFormat,outputFormat 147 | * 148 | * @param {String} format output format name 149 | * @return FfmpegCommand 150 | */ 151 | proto.toFormat = 152 | proto.withOutputFormat = 153 | proto.outputFormat = 154 | proto.format = function(format) { 155 | this._currentOutput.options('-f', format); 156 | return this; 157 | }; 158 | 159 | 160 | /** 161 | * Add stream mapping to output 162 | * 163 | * @method FfmpegCommand#map 164 | * @category Output 165 | * 166 | * @param {String} spec stream specification string, with optional square brackets 167 | * @return FfmpegCommand 168 | */ 169 | proto.map = function(spec) { 170 | this._currentOutput.options('-map', spec.replace(utils.streamRegexp, '[$1]')); 171 | return this; 172 | }; 173 | 174 | 175 | /** 176 | * Run flvtool2/flvmeta on output 177 | * 178 | * @method FfmpegCommand#flvmeta 179 | * @category Output 180 | * @aliases updateFlvMetadata 181 | * 182 | * @return FfmpegCommand 183 | */ 184 | proto.updateFlvMetadata = 185 | proto.flvmeta = function() { 186 | this._currentOutput.flags.flvmeta = true; 187 | return this; 188 | }; 189 | }; 190 | </code></pre> 191 | </article> 192 | </section> 193 | 194 | 195 | 196 | 197 | </div> 198 | 199 | <nav> 200 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 201 | </nav> 202 | 203 | <br clear="both"> 204 | 205 | <footer> 206 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun May 01 2016 12:10:37 GMT+0200 (CEST) 207 | </footer> 208 | 209 | <script> prettyPrint(); </script> 210 | <script src="scripts/linenumber.js"> </script> 211 | </body> 212 | </html> 213 | -------------------------------------------------------------------------------- /doc/options_video.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/video.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/video.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var utils = require('../utils'); 32 | 33 | 34 | /* 35 | *! Video-related methods 36 | */ 37 | 38 | module.exports = function(proto) { 39 | /** 40 | * Disable video in the output 41 | * 42 | * @method FfmpegCommand#noVideo 43 | * @category Video 44 | * @aliases withNoVideo 45 | * 46 | * @return FfmpegCommand 47 | */ 48 | proto.withNoVideo = 49 | proto.noVideo = function() { 50 | this._currentOutput.video.clear(); 51 | this._currentOutput.videoFilters.clear(); 52 | this._currentOutput.video('-vn'); 53 | 54 | return this; 55 | }; 56 | 57 | 58 | /** 59 | * Specify video codec 60 | * 61 | * @method FfmpegCommand#videoCodec 62 | * @category Video 63 | * @aliases withVideoCodec 64 | * 65 | * @param {String} codec video codec name 66 | * @return FfmpegCommand 67 | */ 68 | proto.withVideoCodec = 69 | proto.videoCodec = function(codec) { 70 | this._currentOutput.video('-vcodec', codec); 71 | return this; 72 | }; 73 | 74 | 75 | /** 76 | * Specify video bitrate 77 | * 78 | * @method FfmpegCommand#videoBitrate 79 | * @category Video 80 | * @aliases withVideoBitrate 81 | * 82 | * @param {String|Number} bitrate video bitrate in kbps (with an optional 'k' suffix) 83 | * @param {Boolean} [constant=false] enforce constant bitrate 84 | * @return FfmpegCommand 85 | */ 86 | proto.withVideoBitrate = 87 | proto.videoBitrate = function(bitrate, constant) { 88 | bitrate = ('' + bitrate).replace(/k?$/, 'k'); 89 | 90 | this._currentOutput.video('-b:v', bitrate); 91 | if (constant) { 92 | this._currentOutput.video( 93 | '-maxrate', bitrate, 94 | '-minrate', bitrate, 95 | '-bufsize', '3M' 96 | ); 97 | } 98 | 99 | return this; 100 | }; 101 | 102 | 103 | /** 104 | * Specify custom video filter(s) 105 | * 106 | * Can be called both with one or many filters, or a filter array. 107 | * 108 | * @example 109 | * command.videoFilters('filter1'); 110 | * 111 | * @example 112 | * command.videoFilters('filter1', 'filter2=param1=value1:param2=value2'); 113 | * 114 | * @example 115 | * command.videoFilters(['filter1', 'filter2']); 116 | * 117 | * @example 118 | * command.videoFilters([ 119 | * { 120 | * filter: 'filter1' 121 | * }, 122 | * { 123 | * filter: 'filter2', 124 | * options: 'param=value:param=value' 125 | * } 126 | * ]); 127 | * 128 | * @example 129 | * command.videoFilters( 130 | * { 131 | * filter: 'filter1', 132 | * options: ['value1', 'value2'] 133 | * }, 134 | * { 135 | * filter: 'filter2', 136 | * options: { param1: 'value1', param2: 'value2' } 137 | * } 138 | * ); 139 | * 140 | * @method FfmpegCommand#videoFilters 141 | * @category Video 142 | * @aliases withVideoFilter,withVideoFilters,videoFilter 143 | * 144 | * @param {...String|String[]|Object[]} filters video filter strings, string array or 145 | * filter specification array, each with the following properties: 146 | * @param {String} filters.filter filter name 147 | * @param {String|String[]|Object} [filters.options] filter option string, array, or object 148 | * @return FfmpegCommand 149 | */ 150 | proto.withVideoFilter = 151 | proto.withVideoFilters = 152 | proto.videoFilter = 153 | proto.videoFilters = function(filters) { 154 | if (arguments.length > 1) { 155 | filters = [].slice.call(arguments); 156 | } 157 | 158 | if (!Array.isArray(filters)) { 159 | filters = [filters]; 160 | } 161 | 162 | this._currentOutput.videoFilters(utils.makeFilterStrings(filters)); 163 | 164 | return this; 165 | }; 166 | 167 | 168 | /** 169 | * Specify output FPS 170 | * 171 | * @method FfmpegCommand#fps 172 | * @category Video 173 | * @aliases withOutputFps,withOutputFPS,withFpsOutput,withFPSOutput,withFps,withFPS,outputFPS,outputFps,fpsOutput,FPSOutput,FPS 174 | * 175 | * @param {Number} fps output FPS 176 | * @return FfmpegCommand 177 | */ 178 | proto.withOutputFps = 179 | proto.withOutputFPS = 180 | proto.withFpsOutput = 181 | proto.withFPSOutput = 182 | proto.withFps = 183 | proto.withFPS = 184 | proto.outputFPS = 185 | proto.outputFps = 186 | proto.fpsOutput = 187 | proto.FPSOutput = 188 | proto.fps = 189 | proto.FPS = function(fps) { 190 | this._currentOutput.video('-r', fps); 191 | return this; 192 | }; 193 | 194 | 195 | /** 196 | * Only transcode a certain number of frames 197 | * 198 | * @method FfmpegCommand#frames 199 | * @category Video 200 | * @aliases takeFrames,withFrames 201 | * 202 | * @param {Number} frames frame count 203 | * @return FfmpegCommand 204 | */ 205 | proto.takeFrames = 206 | proto.withFrames = 207 | proto.frames = function(frames) { 208 | this._currentOutput.video('-vframes', frames); 209 | return this; 210 | }; 211 | }; 212 | </code></pre> 213 | </article> 214 | </section> 215 | 216 | 217 | 218 | 219 | </div> 220 | 221 | <nav> 222 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 223 | </nav> 224 | 225 | <br clear="both"> 226 | 227 | <footer> 228 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun May 01 2016 12:10:37 GMT+0200 (CEST) 229 | </footer> 230 | 231 | <script> prettyPrint(); </script> 232 | <script src="scripts/linenumber.js"> </script> 233 | </body> 234 | </html> 235 | -------------------------------------------------------------------------------- /doc/output.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/output.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/output.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var utils = require('../utils'); 32 | 33 | 34 | /* 35 | *! Output-related methods 36 | */ 37 | 38 | module.exports = function(proto) { 39 | /** 40 | * Add output 41 | * 42 | * @method FfmpegCommand#output 43 | * @category Output 44 | * @aliases addOutput 45 | * 46 | * @param {String|Writable} target target file path or writable stream 47 | * @param {Object} [pipeopts={}] pipe options (only applies to streams) 48 | * @return FfmpegCommand 49 | */ 50 | proto.addOutput = 51 | proto.output = function(target, pipeopts) { 52 | var isFile = false; 53 | 54 | if (!target && this._currentOutput) { 55 | // No target is only allowed when called from constructor 56 | throw new Error('Invalid output'); 57 | } 58 | 59 | if (target && typeof target !== 'string') { 60 | if (!('writable' in target) || !(target.writable)) { 61 | throw new Error('Invalid output'); 62 | } 63 | } else if (typeof target === 'string') { 64 | var protocol = target.match(/^([a-z]{2,}):/i); 65 | isFile = !protocol || protocol[0] === 'file'; 66 | } 67 | 68 | if (target && !('target' in this._currentOutput)) { 69 | // For backwards compatibility, set target for first output 70 | this._currentOutput.target = target; 71 | this._currentOutput.isFile = isFile; 72 | this._currentOutput.pipeopts = pipeopts || {}; 73 | } else { 74 | if (target && typeof target !== 'string') { 75 | var hasOutputStream = this._outputs.some(function(output) { 76 | return typeof output.target !== 'string'; 77 | }); 78 | 79 | if (hasOutputStream) { 80 | throw new Error('Only one output stream is supported'); 81 | } 82 | } 83 | 84 | this._outputs.push(this._currentOutput = { 85 | target: target, 86 | isFile: isFile, 87 | flags: {}, 88 | pipeopts: pipeopts || {} 89 | }); 90 | 91 | var self = this; 92 | ['audio', 'audioFilters', 'video', 'videoFilters', 'sizeFilters', 'options'].forEach(function(key) { 93 | self._currentOutput[key] = utils.args(); 94 | }); 95 | 96 | if (!target) { 97 | // Call from constructor: remove target key 98 | delete this._currentOutput.target; 99 | } 100 | } 101 | 102 | return this; 103 | }; 104 | 105 | 106 | /** 107 | * Specify output seek time 108 | * 109 | * @method FfmpegCommand#seek 110 | * @category Input 111 | * @aliases seekOutput 112 | * 113 | * @param {String|Number} seek seek time in seconds or as a '[hh:[mm:]]ss[.xxx]' string 114 | * @return FfmpegCommand 115 | */ 116 | proto.seekOutput = 117 | proto.seek = function(seek) { 118 | this._currentOutput.options('-ss', seek); 119 | return this; 120 | }; 121 | 122 | 123 | /** 124 | * Set output duration 125 | * 126 | * @method FfmpegCommand#duration 127 | * @category Output 128 | * @aliases withDuration,setDuration 129 | * 130 | * @param {String|Number} duration duration in seconds or as a '[[hh:]mm:]ss[.xxx]' string 131 | * @return FfmpegCommand 132 | */ 133 | proto.withDuration = 134 | proto.setDuration = 135 | proto.duration = function(duration) { 136 | this._currentOutput.options('-t', duration); 137 | return this; 138 | }; 139 | 140 | 141 | /** 142 | * Set output format 143 | * 144 | * @method FfmpegCommand#format 145 | * @category Output 146 | * @aliases toFormat,withOutputFormat,outputFormat 147 | * 148 | * @param {String} format output format name 149 | * @return FfmpegCommand 150 | */ 151 | proto.toFormat = 152 | proto.withOutputFormat = 153 | proto.outputFormat = 154 | proto.format = function(format) { 155 | this._currentOutput.options('-f', format); 156 | return this; 157 | }; 158 | 159 | 160 | /** 161 | * Add stream mapping to output 162 | * 163 | * @method FfmpegCommand#map 164 | * @category Output 165 | * 166 | * @param {String} spec stream specification string, with optional square brackets 167 | * @return FfmpegCommand 168 | */ 169 | proto.map = function(spec) { 170 | this._currentOutput.options('-map', spec.replace(utils.streamRegexp, '[$1]')); 171 | return this; 172 | }; 173 | 174 | 175 | /** 176 | * Run flvtool2/flvmeta on output 177 | * 178 | * @method FfmpegCommand#flvmeta 179 | * @category Output 180 | * @aliases updateFlvMetadata 181 | * 182 | * @return FfmpegCommand 183 | */ 184 | proto.updateFlvMetadata = 185 | proto.flvmeta = function() { 186 | this._currentOutput.flags.flvmeta = true; 187 | return this; 188 | }; 189 | }; 190 | </code></pre> 191 | </article> 192 | </section> 193 | 194 | 195 | 196 | 197 | </div> 198 | 199 | <nav> 200 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 201 | </nav> 202 | 203 | <br clear="both"> 204 | 205 | <footer> 206 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha5</a> on Tue Jul 08 2014 21:22:19 GMT+0200 (CEST) 207 | </footer> 208 | 209 | <script> prettyPrint(); </script> 210 | <script src="scripts/linenumber.js"> </script> 211 | </body> 212 | </html> 213 | -------------------------------------------------------------------------------- /doc/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (function() { 3 | var source = document.getElementsByClassName('prettyprint source linenums'); 4 | var i = 0; 5 | var lineNumber = 0; 6 | var lineId; 7 | var lines; 8 | var totalLines; 9 | var anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = 'line' + lineNumber; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /doc/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /doc/styles/jsdoc-default.css: -------------------------------------------------------------------------------- 1 | html 2 | { 3 | overflow: auto; 4 | background-color: #fff; 5 | } 6 | 7 | body 8 | { 9 | font: 14px "DejaVu Sans Condensed", "Liberation Sans", "Nimbus Sans L", Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif; 10 | line-height: 130%; 11 | color: #000; 12 | background-color: #fff; 13 | } 14 | 15 | a { 16 | color: #444; 17 | } 18 | 19 | a:visited { 20 | color: #444; 21 | } 22 | 23 | a:active { 24 | color: #444; 25 | } 26 | 27 | header 28 | { 29 | display: block; 30 | padding: 6px 4px; 31 | } 32 | 33 | .class-description { 34 | font-style: italic; 35 | font-family: Palatino, 'Palatino Linotype', serif; 36 | font-size: 130%; 37 | line-height: 140%; 38 | margin-bottom: 1em; 39 | margin-top: 1em; 40 | } 41 | 42 | #main { 43 | float: left; 44 | width: 100%; 45 | } 46 | 47 | section 48 | { 49 | display: block; 50 | 51 | background-color: #fff; 52 | padding: 12px 24px; 53 | border-bottom: 1px solid #ccc; 54 | margin-right: 240px; 55 | } 56 | 57 | .variation { 58 | display: none; 59 | } 60 | 61 | .optional:after { 62 | content: "opt"; 63 | font-size: 60%; 64 | color: #aaa; 65 | font-style: italic; 66 | font-weight: lighter; 67 | } 68 | 69 | nav 70 | { 71 | display: block; 72 | width: 220px; 73 | border-left: 1px solid #ccc; 74 | padding-left: 9px; 75 | 76 | position: fixed; 77 | top: 28px; 78 | right: 0; 79 | } 80 | 81 | nav ul { 82 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; 83 | font-size: 100%; 84 | line-height: 17px; 85 | padding:0; 86 | margin:0; 87 | list-style-type:none; 88 | } 89 | 90 | nav ul ul { 91 | margin-left: 10px; 92 | font-size: 90%; 93 | } 94 | 95 | nav h2 a, nav h2 a:visited { 96 | color: #526492; 97 | text-decoration: none; 98 | } 99 | 100 | nav h3 { 101 | margin-top: 12px; 102 | } 103 | 104 | nav li { 105 | margin-top: 2px; 106 | } 107 | 108 | nav a { 109 | color: #5C5954; 110 | } 111 | 112 | nav a:visited { 113 | color: #5C5954; 114 | } 115 | 116 | nav a:active { 117 | color: #5C5954; 118 | } 119 | 120 | footer { 121 | display: block; 122 | padding: 6px; 123 | margin-top: 12px; 124 | font-style: italic; 125 | font-size: 90%; 126 | } 127 | 128 | h1 129 | { 130 | font-size: 200%; 131 | font-weight: bold; 132 | letter-spacing: -0.01em; 133 | margin: 6px 0 9px 0; 134 | } 135 | 136 | h2 137 | { 138 | font-size: 170%; 139 | font-weight: bold; 140 | letter-spacing: -0.01em; 141 | margin: 50px 0 3px 0; 142 | } 143 | 144 | nav > h2 { 145 | margin-top: 6px; 146 | } 147 | 148 | h3 149 | { 150 | font-size: 150%; 151 | font-weight: bold; 152 | letter-spacing: -0.01em; 153 | margin-top: 16px; 154 | margin: 50px 0 3px 0; 155 | } 156 | 157 | h4 158 | { 159 | font-size: 130%; 160 | font-weight: bold; 161 | letter-spacing: -0.01em; 162 | margin-top: 16px; 163 | margin: 18px 0 3px 0; 164 | color: #526492; 165 | } 166 | 167 | h5, .container-overview .subsection-title 168 | { 169 | font-size: 120%; 170 | font-weight: bold; 171 | letter-spacing: -0.01em; 172 | margin: 8px 0 3px -16px; 173 | } 174 | 175 | h6 176 | { 177 | font-size: 100%; 178 | letter-spacing: -0.01em; 179 | margin: 6px 0 3px 0; 180 | font-style: italic; 181 | } 182 | 183 | article > dl, article > pre { 184 | margin-left: 2em; 185 | } 186 | 187 | .ancestors { color: #999; } 188 | .ancestors a 189 | { 190 | color: #999 !important; 191 | text-decoration: none; 192 | } 193 | 194 | .important 195 | { 196 | font-weight: bold; 197 | color: #950B02; 198 | } 199 | 200 | .yes-def { 201 | text-indent: -1000px; 202 | } 203 | 204 | .type-signature { 205 | color: #aaa; 206 | } 207 | 208 | .name, .signature { 209 | font-family: Consolas, "Lucida Console", Monaco, monospace; 210 | } 211 | 212 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 213 | .details dt { width:100px; float:left; padding-left: 10px; padding-top: 6px; } 214 | .details dd { margin-left: 50px; } 215 | .details ul { margin: 0; } 216 | .details ul { list-style-type: none; } 217 | .details li { margin-left: 30px; padding-top: 6px; } 218 | .details pre.prettyprint { margin: 0 } 219 | .details .object-value { padding-top: 0; } 220 | 221 | .description { 222 | margin-bottom: 1em; 223 | margin-left: -16px; 224 | margin-top: 1em; 225 | } 226 | 227 | .code-caption 228 | { 229 | font-style: italic; 230 | font-family: Palatino, 'Palatino Linotype', serif; 231 | font-size: 107%; 232 | margin: 0; 233 | } 234 | 235 | .prettyprint 236 | { 237 | border: 1px solid #ddd; 238 | width: 80%; 239 | overflow: auto; 240 | } 241 | 242 | .prettyprint.source { 243 | width: inherit; 244 | } 245 | 246 | .prettyprint code 247 | { 248 | font-family: Consolas, 'Lucida Console', Monaco, monospace; 249 | font-size: 100%; 250 | line-height: 18px; 251 | display: block; 252 | padding: 4px 12px; 253 | margin: 0; 254 | background-color: #fff; 255 | color: #000; 256 | } 257 | 258 | .prettyprint code span.line 259 | { 260 | display: inline-block; 261 | } 262 | 263 | .prettyprint.linenums 264 | { 265 | padding-left: 70px; 266 | -webkit-user-select: none; 267 | -moz-user-select: none; 268 | -ms-user-select: none; 269 | user-select: none; 270 | } 271 | 272 | .prettyprint.linenums ol 273 | { 274 | padding-left: 0; 275 | } 276 | 277 | .prettyprint.linenums li 278 | { 279 | border-left: 3px #ddd solid; 280 | } 281 | 282 | .prettyprint.linenums li.selected, 283 | .prettyprint.linenums li.selected * 284 | { 285 | background-color: lightyellow; 286 | } 287 | 288 | .prettyprint.linenums li * 289 | { 290 | -webkit-user-select: text; 291 | -moz-user-select: text; 292 | -ms-user-select: text; 293 | user-select: text; 294 | } 295 | 296 | .params, .props 297 | { 298 | border-spacing: 0; 299 | border: 0; 300 | border-collapse: collapse; 301 | } 302 | 303 | .params .name, .props .name, .name code { 304 | color: #526492; 305 | font-family: Consolas, 'Lucida Console', Monaco, monospace; 306 | font-size: 100%; 307 | } 308 | 309 | .params td, .params th, .props td, .props th 310 | { 311 | border: 1px solid #ddd; 312 | margin: 0px; 313 | text-align: left; 314 | vertical-align: top; 315 | padding: 4px 6px; 316 | display: table-cell; 317 | } 318 | 319 | .params thead tr, .props thead tr 320 | { 321 | background-color: #ddd; 322 | font-weight: bold; 323 | } 324 | 325 | .params .params thead tr, .props .props thead tr 326 | { 327 | background-color: #fff; 328 | font-weight: bold; 329 | } 330 | 331 | .params th, .props th { border-right: 1px solid #aaa; } 332 | .params thead .last, .props thead .last { border-right: 1px solid #ddd; } 333 | 334 | .params td.description > p:first-child 335 | { 336 | margin-top: 0; 337 | padding-top: 0; 338 | } 339 | 340 | .params td.description > p:last-child 341 | { 342 | margin-bottom: 0; 343 | padding-bottom: 0; 344 | } 345 | 346 | .disabled { 347 | color: #454545; 348 | } 349 | -------------------------------------------------------------------------------- /doc/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /doc/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Menlo, Monaco, Consolas, monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /doc/video.js.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: Source: options/video.js</title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title">Source: options/video.js</h1> 21 | 22 | 23 | 24 | 25 | 26 | <section> 27 | <article> 28 | <pre class="prettyprint source linenums"><code>/*jshint node:true*/ 29 | 'use strict'; 30 | 31 | var utils = require('../utils'); 32 | 33 | 34 | /* 35 | *! Video-related methods 36 | */ 37 | 38 | module.exports = function(proto) { 39 | /** 40 | * Disable video in the output 41 | * 42 | * @method FfmpegCommand#noVideo 43 | * @category Video 44 | * @aliases withNoVideo 45 | * 46 | * @return FfmpegCommand 47 | */ 48 | proto.withNoVideo = 49 | proto.noVideo = function() { 50 | this._currentOutput.video.clear(); 51 | this._currentOutput.videoFilters.clear(); 52 | this._currentOutput.video('-vn'); 53 | 54 | return this; 55 | }; 56 | 57 | 58 | /** 59 | * Specify video codec 60 | * 61 | * @method FfmpegCommand#videoCodec 62 | * @category Video 63 | * @aliases withVideoCodec 64 | * 65 | * @param {String} codec video codec name 66 | * @return FfmpegCommand 67 | */ 68 | proto.withVideoCodec = 69 | proto.videoCodec = function(codec) { 70 | this._currentOutput.video('-vcodec', codec); 71 | return this; 72 | }; 73 | 74 | 75 | /** 76 | * Specify video bitrate 77 | * 78 | * @method FfmpegCommand#videoBitrate 79 | * @category Video 80 | * @aliases withVideoBitrate 81 | * 82 | * @param {String|Number} bitrate video bitrate in kbps (with an optional 'k' suffix) 83 | * @param {Boolean} [constant=false] enforce constant bitrate 84 | * @return FfmpegCommand 85 | */ 86 | proto.withVideoBitrate = 87 | proto.videoBitrate = function(bitrate, constant) { 88 | bitrate = ('' + bitrate).replace(/k?$/, 'k'); 89 | 90 | this._currentOutput.video('-b:v', bitrate); 91 | if (constant) { 92 | this._currentOutput.video( 93 | '-maxrate', bitrate, 94 | '-minrate', bitrate, 95 | '-bufsize', '3M' 96 | ); 97 | } 98 | 99 | return this; 100 | }; 101 | 102 | 103 | /** 104 | * Specify custom video filter(s) 105 | * 106 | * Can be called both with one or many filters, or a filter array. 107 | * 108 | * @example 109 | * command.videoFilters('filter1'); 110 | * 111 | * @example 112 | * command.videoFilters('filter1', 'filter2=param1=value1:param2=value2'); 113 | * 114 | * @example 115 | * command.videoFilters(['filter1', 'filter2']); 116 | * 117 | * @example 118 | * command.videoFilters([ 119 | * { 120 | * filter: 'filter1' 121 | * }, 122 | * { 123 | * filter: 'filter2', 124 | * options: 'param=value:param=value' 125 | * } 126 | * ]); 127 | * 128 | * @example 129 | * command.videoFilters( 130 | * { 131 | * filter: 'filter1', 132 | * options: ['value1', 'value2'] 133 | * }, 134 | * { 135 | * filter: 'filter2', 136 | * options: { param1: 'value1', param2: 'value2' } 137 | * } 138 | * ); 139 | * 140 | * @method FfmpegCommand#videoFilters 141 | * @category Video 142 | * @aliases withVideoFilter,withVideoFilters,videoFilter 143 | * 144 | * @param {...String|String[]|Object[]} filters video filter strings, string array or 145 | * filter specification array, each with the following properties: 146 | * @param {String} filters.filter filter name 147 | * @param {String|String[]|Object} [filters.options] filter option string, array, or object 148 | * @return FfmpegCommand 149 | */ 150 | proto.withVideoFilter = 151 | proto.withVideoFilters = 152 | proto.videoFilter = 153 | proto.videoFilters = function(filters) { 154 | if (arguments.length > 1) { 155 | filters = [].slice.call(arguments); 156 | } 157 | 158 | if (!Array.isArray(filters)) { 159 | filters = [filters]; 160 | } 161 | 162 | this._currentOutput.videoFilters(utils.makeFilterStrings(filters)); 163 | 164 | return this; 165 | }; 166 | 167 | 168 | /** 169 | * Specify output FPS 170 | * 171 | * @method FfmpegCommand#fps 172 | * @category Video 173 | * @aliases withOutputFps,withOutputFPS,withFpsOutput,withFPSOutput,withFps,withFPS,outputFPS,outputFps,fpsOutput,FPSOutput,FPS 174 | * 175 | * @param {Number} fps output FPS 176 | * @return FfmpegCommand 177 | */ 178 | proto.withOutputFps = 179 | proto.withOutputFPS = 180 | proto.withFpsOutput = 181 | proto.withFPSOutput = 182 | proto.withFps = 183 | proto.withFPS = 184 | proto.outputFPS = 185 | proto.outputFps = 186 | proto.fpsOutput = 187 | proto.FPSOutput = 188 | proto.fps = 189 | proto.FPS = function(fps) { 190 | this._currentOutput.video('-r', fps); 191 | return this; 192 | }; 193 | 194 | 195 | /** 196 | * Only transcode a certain number of frames 197 | * 198 | * @method FfmpegCommand#frames 199 | * @category Video 200 | * @aliases takeFrames,withFrames 201 | * 202 | * @param {Number} frames frame count 203 | * @return FfmpegCommand 204 | */ 205 | proto.takeFrames = 206 | proto.withFrames = 207 | proto.frames = function(frames) { 208 | this._currentOutput.video('-vframes', frames); 209 | return this; 210 | }; 211 | }; 212 | </code></pre> 213 | </article> 214 | </section> 215 | 216 | 217 | 218 | 219 | </div> 220 | 221 | <nav> 222 | <h2><a href="index.html">Index</a></h2><ul><li><a href="index.html#installation">Installation</a></li><ul></ul><li><a href="index.html#usage">Usage</a></li><ul><li><a href="index.html#prerequisites">Prerequisites</a></li><li><a href="index.html#creating-an-ffmpeg-command">Creating an FFmpeg command</a></li><li><a href="index.html#specifying-inputs">Specifying inputs</a></li><li><a href="index.html#input-options">Input options</a></li><li><a href="index.html#audio-options">Audio options</a></li><li><a href="index.html#video-options">Video options</a></li><li><a href="index.html#video-frame-size-options">Video frame size options</a></li><li><a href="index.html#specifying-multiple-outputs">Specifying multiple outputs</a></li><li><a href="index.html#output-options">Output options</a></li><li><a href="index.html#miscellaneous-options">Miscellaneous options</a></li><li><a href="index.html#setting-event-handlers">Setting event handlers</a></li><li><a href="index.html#starting-ffmpeg-processing">Starting FFmpeg processing</a></li><li><a href="index.html#controlling-the-ffmpeg-process">Controlling the FFmpeg process</a></li><li><a href="index.html#reading-video-metadata">Reading video metadata</a></li><li><a href="index.html#querying-ffmpeg-capabilities">Querying ffmpeg capabilities</a></li><li><a href="index.html#cloning-an-ffmpegcommand">Cloning an FfmpegCommand</a></li></ul><li><a href="index.html#contributing">Contributing</a></li><ul><li><a href="index.html#code-contributions">Code contributions</a></li><li><a href="index.html#documentation-contributions">Documentation contributions</a></li><li><a href="index.html#updating-the-documentation">Updating the documentation</a></li><li><a href="index.html#running-tests">Running tests</a></li></ul><li><a href="index.html#main-contributors">Main contributors</a></li><ul></ul><li><a href="index.html#license">License</a></li><ul></ul></ul><h3>Classes</h3><ul><li><a href="FfmpegCommand.html">FfmpegCommand</a></li><ul><li> <a href="FfmpegCommand.html#audio-methods">Audio methods</a></li><li> <a href="FfmpegCommand.html#capabilities-methods">Capabilities methods</a></li><li> <a href="FfmpegCommand.html#custom-options-methods">Custom options methods</a></li><li> <a href="FfmpegCommand.html#input-methods">Input methods</a></li><li> <a href="FfmpegCommand.html#metadata-methods">Metadata methods</a></li><li> <a href="FfmpegCommand.html#miscellaneous-methods">Miscellaneous methods</a></li><li> <a href="FfmpegCommand.html#other-methods">Other methods</a></li><li> <a href="FfmpegCommand.html#output-methods">Output methods</a></li><li> <a href="FfmpegCommand.html#processing-methods">Processing methods</a></li><li> <a href="FfmpegCommand.html#video-methods">Video methods</a></li><li> <a href="FfmpegCommand.html#video-size-methods">Video size methods</a></li></ul></ul> 223 | </nav> 224 | 225 | <br clear="both"> 226 | 227 | <footer> 228 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha5</a> on Tue Jul 08 2014 21:22:19 GMT+0200 (CEST) 229 | </footer> 230 | 231 | <script> prettyPrint(); </script> 232 | <script src="scripts/linenumber.js"> </script> 233 | </body> 234 | </html> 235 | -------------------------------------------------------------------------------- /examples/any-to-mp4-steam.js: -------------------------------------------------------------------------------- 1 | // The solution based on adding -movflags for mp4 output 2 | // For more movflags details check ffmpeg docs 3 | // https://ffmpeg.org/ffmpeg-formats.html#toc-Options-9 4 | 5 | var fs = require('fs'); 6 | var path = require('path'); 7 | var ffmpeg = require('../index'); 8 | 9 | var pathToSourceFile = path.resolve(__dirname, '../test/assets/testvideo-169.avi'); 10 | var readStream = fs.createReadStream(pathToSourceFile); 11 | var writeStream = fs.createWriteStream('./output.mp4'); 12 | 13 | ffmpeg(readStream) 14 | .addOutputOptions('-movflags +frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov') 15 | .format('mp4') 16 | .pipe(writeStream); 17 | -------------------------------------------------------------------------------- /examples/express-stream.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | ffmpeg = require('../index'); 3 | 4 | var app = express(); 5 | 6 | app.use(express.static(__dirname + '/flowplayer')); 7 | 8 | app.get('/', function(req, res) { 9 | res.send('index.html'); 10 | }); 11 | 12 | app.get('/video/:filename', function(req, res) { 13 | res.contentType('flv'); 14 | // make sure you set the correct path to your video file storage 15 | var pathToMovie = '/path/to/storage/' + req.params.filename; 16 | var proc = ffmpeg(pathToMovie) 17 | // use the 'flashvideo' preset (located in /lib/presets/flashvideo.js) 18 | .preset('flashvideo') 19 | // setup event handlers 20 | .on('end', function() { 21 | console.log('file has been converted succesfully'); 22 | }) 23 | .on('error', function(err) { 24 | console.log('an error happened: ' + err.message); 25 | }) 26 | // save to stream 27 | .pipe(res, {end:true}); 28 | }); 29 | 30 | app.listen(4000); 31 | -------------------------------------------------------------------------------- /examples/flowplayer/flowplayer.controls.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/examples/flowplayer/flowplayer.controls.swf -------------------------------------------------------------------------------- /examples/flowplayer/flowplayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/examples/flowplayer/flowplayer.swf -------------------------------------------------------------------------------- /examples/flowplayer/index.html: -------------------------------------------------------------------------------- 1 | <html> 2 | <head> 3 | <meta http-equiv="content-type" content="text/html; charset=UTF-8"> 4 | <script type="text/javascript" src="/flowplayer.min.js"></script> 5 | <title>node-fluent-ffmpeg</title> 6 | </head> 7 | <body> 8 | 9 | <!-- this A tag is where your Flowplayer will be placed. it can be anywhere --> 10 | <a 11 | href="/video/your_movie.avi" 12 | style="display:block;width:520px;height:330px" 13 | id="player"> 14 | </a> 15 | 16 | <!-- this will install flowplayer inside previous A- tag. --> 17 | <script> 18 | flowplayer("player", "/flowplayer.swf"); 19 | </script> 20 | </body> 21 | </html> -------------------------------------------------------------------------------- /examples/full.js: -------------------------------------------------------------------------------- 1 | var ffmpeg = require('../index'); 2 | 3 | // make sure you set the correct path to your video file 4 | var proc = ffmpeg('/path/to/your_movie.avi') 5 | // set video bitrate 6 | .videoBitrate(1024) 7 | // set target codec 8 | .videoCodec('divx') 9 | // set aspect ratio 10 | .aspect('16:9') 11 | // set size in percent 12 | .size('50%') 13 | // set fps 14 | .fps(24) 15 | // set audio bitrate 16 | .audioBitrate('128k') 17 | // set audio codec 18 | .audioCodec('libmp3lame') 19 | // set number of audio channels 20 | .audioChannels(2) 21 | // set custom option 22 | .addOption('-vtag', 'DIVX') 23 | // set output format to force 24 | .format('avi') 25 | // setup event handlers 26 | .on('end', function() { 27 | console.log('file has been converted succesfully'); 28 | }) 29 | .on('error', function(err) { 30 | console.log('an error happened: ' + err.message); 31 | }) 32 | // save to file 33 | .save('/path/to/your_target.avi'); 34 | -------------------------------------------------------------------------------- /examples/image2video.js: -------------------------------------------------------------------------------- 1 | var ffmpeg = require('fluent-ffmpeg'); 2 | 3 | // make sure you set the correct path to your video file 4 | var proc = ffmpeg('/path/to/your_image.jpg') 5 | // loop for 5 seconds 6 | .loop(5) 7 | // using 25 fps 8 | .fps(25) 9 | // setup event handlers 10 | .on('end', function() { 11 | console.log('file has been converted succesfully'); 12 | }) 13 | .on('error', function(err) { 14 | console.log('an error happened: ' + err.message); 15 | }) 16 | // save to file 17 | .save('/path/to/your_target.m4v'); 18 | -------------------------------------------------------------------------------- /examples/input-stream.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | ffmpeg = require('../index'); 3 | 4 | // open input stream 5 | var infs = fs.createReadStream(__dirname + '/test/assets/testvideo-43.avi'); 6 | 7 | infs.on('error', function(err) { 8 | console.log(err); 9 | }); 10 | 11 | // create new ffmpeg processor instance using input stream 12 | // instead of file path (can be any ReadableStream) 13 | var proc = ffmpeg(infs) 14 | .preset('flashvideo') 15 | // setup event handlers 16 | .on('end', function() { 17 | console.log('done processing input stream'); 18 | }) 19 | .on('error', function(err) { 20 | console.log('an error happened: ' + err.message); 21 | }) 22 | // save to file 23 | .save('/path/to/your_target.flv'); 24 | -------------------------------------------------------------------------------- /examples/livertmp2hls.js: -------------------------------------------------------------------------------- 1 | var ffmpeg = require('../index'); 2 | 3 | // make sure you set the correct path to your video file 4 | var proc = ffmpeg('rtmp://path/to/live/stream', { timeout: 432000 }) 5 | // set video bitrate 6 | .videoBitrate(1024) 7 | // set h264 preset 8 | .addOption('preset','superfast') 9 | // set target codec 10 | .videoCodec('libx264') 11 | // set audio bitrate 12 | .audioBitrate('128k') 13 | // set audio codec 14 | .audioCodec('libfaac') 15 | // set number of audio channels 16 | .audioChannels(2) 17 | // set hls segments time 18 | .addOption('-hls_time', 10) 19 | // include all the segments in the list 20 | .addOption('-hls_list_size',0) 21 | // setup event handlers 22 | .on('end', function() { 23 | console.log('file has been converted succesfully'); 24 | }) 25 | .on('error', function(err) { 26 | console.log('an error happened: ' + err.message); 27 | }) 28 | // save to file 29 | .save('/path/to/your_target.m3u8'); 30 | -------------------------------------------------------------------------------- /examples/mergeVideos.js: -------------------------------------------------------------------------------- 1 | var ffmpeg = require('../index'); 2 | 3 | /* 4 | replicates this sequence of commands: 5 | 6 | ffmpeg -i title.mp4 -qscale:v 1 intermediate1.mpg 7 | ffmpeg -i source.mp4 -qscale:v 1 intermediate2.mpg 8 | ffmpeg -i concat:"intermediate1.mpg|intermediate2.mpg" -c copy intermediate_all.mpg 9 | ffmpeg -i intermediate_all.mpg -qscale:v 2 output.mp4 10 | 11 | Create temporary .mpg files for each video and deletes them after merge is completed. 12 | These files are created by filename pattern like [videoFilename.ext].temp.mpg [outputFilename.ext].temp.merged.mp4 13 | */ 14 | 15 | var firstFile = "title.mp4"; 16 | var secondFile = "source.mp4"; 17 | var thirdFile = "third.mov"; 18 | var outPath = "out.mp4"; 19 | 20 | var proc = ffmpeg(firstFile) 21 | .input(secondFile) 22 | .input(thirdFile) 23 | //.input(fourthFile) 24 | //.input(...) 25 | .on('end', function() { 26 | console.log('files have been merged succesfully'); 27 | }) 28 | .on('error', function(err) { 29 | console.log('an error happened: ' + err.message); 30 | }) 31 | .mergeToFile(outPath); -------------------------------------------------------------------------------- /examples/metadata.js: -------------------------------------------------------------------------------- 1 | var ffmpeg = require('../index'); 2 | 3 | // make sure you set the correct path to your video file 4 | ffmpeg.ffprobe('/path/to/your_movie.avi',function(err, metadata) { 5 | console.log(require('util').inspect(metadata, false, null)); 6 | }); 7 | -------------------------------------------------------------------------------- /examples/preset.js: -------------------------------------------------------------------------------- 1 | var ffmpeg = require('../index'); 2 | 3 | // make sure you set the correct path to your video file 4 | var proc = ffmpeg('/path/to/your_movie.avi') 5 | // use the 'podcast' preset (located in /lib/presets/podcast.js) 6 | .preset('podcast') 7 | // in case you want to override the preset's setting, just keep chaining 8 | .videoBitrate('512k') 9 | // setup event handlers 10 | .on('end', function() { 11 | console.log('file has been converted succesfully'); 12 | }) 13 | .on('error', function(err) { 14 | console.log('an error happened: ' + err.message); 15 | }) 16 | // save to file 17 | .save('/path/to/your_target.m4v'); 18 | -------------------------------------------------------------------------------- /examples/progress.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | ffmpeg = require('../index'); 3 | 4 | // open input stream 5 | var infs = fs.createReadStream(__dirname + '/test/assets/testvideo-43.avi'); 6 | 7 | infs.on('error', function(err) { 8 | console.log(err); 9 | }); 10 | 11 | var proc = ffmpeg(infs) 12 | .preset('flashvideo') 13 | // setup event handlers 14 | .on('progress', function(info) { 15 | console.log('progress ' + info.percent + '%'); 16 | }) 17 | .on('end', function() { 18 | console.log('done processing input stream'); 19 | }) 20 | .on('error', function(err) { 21 | console.log('an error happened: ' + err.message); 22 | }) 23 | .save('/path/to/your_target.flv'); -------------------------------------------------------------------------------- /examples/stream.js: -------------------------------------------------------------------------------- 1 | var ffmpeg = require('../index'), 2 | fs = require('fs'); 3 | 4 | // create the target stream (can be any WritableStream) 5 | var stream = fs.createWriteStream('/path/to/yout_target.flv') 6 | 7 | // make sure you set the correct path to your video file 8 | var proc = ffmpeg('/path/to/your_movie.avi') 9 | // use the 'flashvideo' preset (located in /lib/presets/flashvideo.js) 10 | .preset('flashvideo') 11 | // setup event handlers 12 | .on('end', function() { 13 | console.log('file has been converted succesfully'); 14 | }) 15 | .on('error', function(err) { 16 | console.log('an error happened: ' + err.message); 17 | }) 18 | // save to stream 19 | .pipe(stream, {end:true}); //end = true, close output stream after writing 20 | -------------------------------------------------------------------------------- /examples/thumbnails.js: -------------------------------------------------------------------------------- 1 | var ffmpeg = require('../index'); 2 | 3 | var proc = ffmpeg('/path/to/your_movie.avi') 4 | // setup event handlers 5 | .on('filenames', function(filenames) { 6 | console.log('screenshots are ' + filenames.join(', ')); 7 | }) 8 | .on('end', function() { 9 | console.log('screenshots were saved'); 10 | }) 11 | .on('error', function(err) { 12 | console.log('an error happened: ' + err.message); 13 | }) 14 | // take 2 screenshots at predefined timemarks and size 15 | .takeScreenshots({ count: 2, timemarks: [ '00:00:02.000', '6' ], size: '150x100' }, '/path/to/thumbnail/folder'); 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/fluent-ffmpeg'); 2 | -------------------------------------------------------------------------------- /lib/ffprobe.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true, laxcomma:true*/ 2 | 'use strict'; 3 | 4 | var spawn = require('child_process').spawn; 5 | 6 | 7 | function legacyTag(key) { return key.match(/^TAG:/); } 8 | function legacyDisposition(key) { return key.match(/^DISPOSITION:/); } 9 | 10 | function parseFfprobeOutput(out) { 11 | var lines = out.split(/\r\n|\r|\n/); 12 | 13 | lines = lines.filter(function (line) { 14 | return line.length > 0; 15 | }); 16 | 17 | var data = { 18 | streams: [], 19 | format: {}, 20 | chapters: [] 21 | }; 22 | 23 | function parseBlock(name) { 24 | var data = {}; 25 | 26 | var line = lines.shift(); 27 | while (typeof line !== 'undefined') { 28 | if (line.toLowerCase() == '[/'+name+']') { 29 | return data; 30 | } else if (line.match(/^\[/)) { 31 | line = lines.shift(); 32 | continue; 33 | } 34 | 35 | var kv = line.match(/^([^=]+)=(.*)$/); 36 | if (kv) { 37 | if (!(kv[1].match(/^TAG:/)) && kv[2].match(/^[0-9]+(\.[0-9]+)?$/)) { 38 | data[kv[1]] = Number(kv[2]); 39 | } else { 40 | data[kv[1]] = kv[2]; 41 | } 42 | } 43 | 44 | line = lines.shift(); 45 | } 46 | 47 | return data; 48 | } 49 | 50 | var line = lines.shift(); 51 | while (typeof line !== 'undefined') { 52 | if (line.match(/^\[stream/i)) { 53 | var stream = parseBlock('stream'); 54 | data.streams.push(stream); 55 | } else if (line.match(/^\[chapter/i)) { 56 | var chapter = parseBlock('chapter'); 57 | data.chapters.push(chapter); 58 | } else if (line.toLowerCase() === '[format]') { 59 | data.format = parseBlock('format'); 60 | } 61 | 62 | line = lines.shift(); 63 | } 64 | 65 | return data; 66 | } 67 | 68 | 69 | 70 | module.exports = function(proto) { 71 | /** 72 | * A callback passed to the {@link FfmpegCommand#ffprobe} method. 73 | * 74 | * @callback FfmpegCommand~ffprobeCallback 75 | * 76 | * @param {Error|null} err error object or null if no error happened 77 | * @param {Object} ffprobeData ffprobe output data; this object 78 | * has the same format as what the following command returns: 79 | * 80 | * `ffprobe -print_format json -show_streams -show_format INPUTFILE` 81 | * @param {Array} ffprobeData.streams stream information 82 | * @param {Object} ffprobeData.format format information 83 | */ 84 | 85 | /** 86 | * Run ffprobe on last specified input 87 | * 88 | * @method FfmpegCommand#ffprobe 89 | * @category Metadata 90 | * 91 | * @param {?Number} [index] 0-based index of input to probe (defaults to last input) 92 | * @param {?String[]} [options] array of output options to return 93 | * @param {FfmpegCommand~ffprobeCallback} callback callback function 94 | * 95 | */ 96 | proto.ffprobe = function() { 97 | var input, index = null, options = [], callback; 98 | 99 | // the last argument should be the callback 100 | var callback = arguments[arguments.length - 1]; 101 | 102 | var ended = false 103 | function handleCallback(err, data) { 104 | if (!ended) { 105 | ended = true; 106 | callback(err, data); 107 | } 108 | }; 109 | 110 | // map the arguments to the correct variable names 111 | switch (arguments.length) { 112 | case 3: 113 | index = arguments[0]; 114 | options = arguments[1]; 115 | break; 116 | case 2: 117 | if (typeof arguments[0] === 'number') { 118 | index = arguments[0]; 119 | } else if (Array.isArray(arguments[0])) { 120 | options = arguments[0]; 121 | } 122 | break; 123 | } 124 | 125 | 126 | if (index === null) { 127 | if (!this._currentInput) { 128 | return handleCallback(new Error('No input specified')); 129 | } 130 | 131 | input = this._currentInput; 132 | } else { 133 | input = this._inputs[index]; 134 | 135 | if (!input) { 136 | return handleCallback(new Error('Invalid input index')); 137 | } 138 | } 139 | 140 | // Find ffprobe 141 | this._getFfprobePath(function(err, path) { 142 | if (err) { 143 | return handleCallback(err); 144 | } else if (!path) { 145 | return handleCallback(new Error('Cannot find ffprobe')); 146 | } 147 | 148 | var stdout = ''; 149 | var stdoutClosed = false; 150 | var stderr = ''; 151 | var stderrClosed = false; 152 | 153 | // Spawn ffprobe 154 | var src = input.isStream ? 'pipe:0' : input.source; 155 | var ffprobe = spawn(path, ['-show_streams', '-show_format'].concat(options, src), {windowsHide: true}); 156 | 157 | if (input.isStream) { 158 | // Skip errors on stdin. These get thrown when ffprobe is complete and 159 | // there seems to be no way hook in and close stdin before it throws. 160 | ffprobe.stdin.on('error', function(err) { 161 | if (['ECONNRESET', 'EPIPE', 'EOF'].indexOf(err.code) >= 0) { return; } 162 | handleCallback(err); 163 | }); 164 | 165 | // Once ffprobe's input stream closes, we need no more data from the 166 | // input 167 | ffprobe.stdin.on('close', function() { 168 | input.source.pause(); 169 | input.source.unpipe(ffprobe.stdin); 170 | }); 171 | 172 | input.source.pipe(ffprobe.stdin); 173 | } 174 | 175 | ffprobe.on('error', callback); 176 | 177 | // Ensure we wait for captured streams to end before calling callback 178 | var exitError = null; 179 | function handleExit(err) { 180 | if (err) { 181 | exitError = err; 182 | } 183 | 184 | if (processExited && stdoutClosed && stderrClosed) { 185 | if (exitError) { 186 | if (stderr) { 187 | exitError.message += '\n' + stderr; 188 | } 189 | 190 | return handleCallback(exitError); 191 | } 192 | 193 | // Process output 194 | var data = parseFfprobeOutput(stdout); 195 | 196 | // Handle legacy output with "TAG:x" and "DISPOSITION:x" keys 197 | [data.format].concat(data.streams).forEach(function(target) { 198 | if (target) { 199 | var legacyTagKeys = Object.keys(target).filter(legacyTag); 200 | 201 | if (legacyTagKeys.length) { 202 | target.tags = target.tags || {}; 203 | 204 | legacyTagKeys.forEach(function(tagKey) { 205 | target.tags[tagKey.substr(4)] = target[tagKey]; 206 | delete target[tagKey]; 207 | }); 208 | } 209 | 210 | var legacyDispositionKeys = Object.keys(target).filter(legacyDisposition); 211 | 212 | if (legacyDispositionKeys.length) { 213 | target.disposition = target.disposition || {}; 214 | 215 | legacyDispositionKeys.forEach(function(dispositionKey) { 216 | target.disposition[dispositionKey.substr(12)] = target[dispositionKey]; 217 | delete target[dispositionKey]; 218 | }); 219 | } 220 | } 221 | }); 222 | 223 | handleCallback(null, data); 224 | } 225 | } 226 | 227 | // Handle ffprobe exit 228 | var processExited = false; 229 | ffprobe.on('exit', function(code, signal) { 230 | processExited = true; 231 | 232 | if (code) { 233 | handleExit(new Error('ffprobe exited with code ' + code)); 234 | } else if (signal) { 235 | handleExit(new Error('ffprobe was killed with signal ' + signal)); 236 | } else { 237 | handleExit(); 238 | } 239 | }); 240 | 241 | // Handle stdout/stderr streams 242 | ffprobe.stdout.on('data', function(data) { 243 | stdout += data; 244 | }); 245 | 246 | ffprobe.stdout.on('close', function() { 247 | stdoutClosed = true; 248 | handleExit(); 249 | }); 250 | 251 | ffprobe.stderr.on('data', function(data) { 252 | stderr += data; 253 | }); 254 | 255 | ffprobe.stderr.on('close', function() { 256 | stderrClosed = true; 257 | handleExit(); 258 | }); 259 | }); 260 | }; 261 | }; 262 | -------------------------------------------------------------------------------- /lib/fluent-ffmpeg.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var path = require('path'); 5 | var util = require('util'); 6 | var EventEmitter = require('events').EventEmitter; 7 | 8 | var utils = require('./utils'); 9 | var ARGLISTS = ['_global', '_audio', '_audioFilters', '_video', '_videoFilters', '_sizeFilters', '_complexFilters']; 10 | 11 | 12 | /** 13 | * Create an ffmpeg command 14 | * 15 | * Can be called with or without the 'new' operator, and the 'input' parameter 16 | * may be specified as 'options.source' instead (or passed later with the 17 | * addInput method). 18 | * 19 | * @constructor 20 | * @param {String|ReadableStream} [input] input file path or readable stream 21 | * @param {Object} [options] command options 22 | * @param {Object} [options.logger=<no logging>] logger object with 'error', 'warning', 'info' and 'debug' methods 23 | * @param {Number} [options.niceness=0] ffmpeg process niceness, ignored on Windows 24 | * @param {Number} [options.priority=0] alias for `niceness` 25 | * @param {String} [options.presets="fluent-ffmpeg/lib/presets"] directory to load presets from 26 | * @param {String} [options.preset="fluent-ffmpeg/lib/presets"] alias for `presets` 27 | * @param {String} [options.stdoutLines=100] maximum lines of ffmpeg output to keep in memory, use 0 for unlimited 28 | * @param {Number} [options.timeout=<no timeout>] ffmpeg processing timeout in seconds 29 | * @param {String|ReadableStream} [options.source=<no input>] alias for the `input` parameter 30 | */ 31 | function FfmpegCommand(input, options) { 32 | // Make 'new' optional 33 | if (!(this instanceof FfmpegCommand)) { 34 | return new FfmpegCommand(input, options); 35 | } 36 | 37 | EventEmitter.call(this); 38 | 39 | if (typeof input === 'object' && !('readable' in input)) { 40 | // Options object passed directly 41 | options = input; 42 | } else { 43 | // Input passed first 44 | options = options || {}; 45 | options.source = input; 46 | } 47 | 48 | // Add input if present 49 | this._inputs = []; 50 | if (options.source) { 51 | this.input(options.source); 52 | } 53 | 54 | // Add target-less output for backwards compatibility 55 | this._outputs = []; 56 | this.output(); 57 | 58 | // Create argument lists 59 | var self = this; 60 | ['_global', '_complexFilters'].forEach(function(prop) { 61 | self[prop] = utils.args(); 62 | }); 63 | 64 | // Set default option values 65 | options.stdoutLines = 'stdoutLines' in options ? options.stdoutLines : 100; 66 | options.presets = options.presets || options.preset || path.join(__dirname, 'presets'); 67 | options.niceness = options.niceness || options.priority || 0; 68 | 69 | // Save options 70 | this.options = options; 71 | 72 | // Setup logger 73 | this.logger = options.logger || { 74 | debug: function() {}, 75 | info: function() {}, 76 | warn: function() {}, 77 | error: function() {} 78 | }; 79 | } 80 | util.inherits(FfmpegCommand, EventEmitter); 81 | module.exports = FfmpegCommand; 82 | 83 | 84 | /** 85 | * Clone an ffmpeg command 86 | * 87 | * This method is useful when you want to process the same input multiple times. 88 | * It returns a new FfmpegCommand instance with the exact same options. 89 | * 90 | * All options set _after_ the clone() call will only be applied to the instance 91 | * it has been called on. 92 | * 93 | * @example 94 | * var command = ffmpeg('/path/to/source.avi') 95 | * .audioCodec('libfaac') 96 | * .videoCodec('libx264') 97 | * .format('mp4'); 98 | * 99 | * command.clone() 100 | * .size('320x200') 101 | * .save('/path/to/output-small.mp4'); 102 | * 103 | * command.clone() 104 | * .size('640x400') 105 | * .save('/path/to/output-medium.mp4'); 106 | * 107 | * command.save('/path/to/output-original-size.mp4'); 108 | * 109 | * @method FfmpegCommand#clone 110 | * @return FfmpegCommand 111 | */ 112 | FfmpegCommand.prototype.clone = function() { 113 | var clone = new FfmpegCommand(); 114 | var self = this; 115 | 116 | // Clone options and logger 117 | clone.options = this.options; 118 | clone.logger = this.logger; 119 | 120 | // Clone inputs 121 | clone._inputs = this._inputs.map(function(input) { 122 | return { 123 | source: input.source, 124 | options: input.options.clone() 125 | }; 126 | }); 127 | 128 | // Create first output 129 | if ('target' in this._outputs[0]) { 130 | // We have outputs set, don't clone them and create first output 131 | clone._outputs = []; 132 | clone.output(); 133 | } else { 134 | // No outputs set, clone first output options 135 | clone._outputs = [ 136 | clone._currentOutput = { 137 | flags: {} 138 | } 139 | ]; 140 | 141 | ['audio', 'audioFilters', 'video', 'videoFilters', 'sizeFilters', 'options'].forEach(function(key) { 142 | clone._currentOutput[key] = self._currentOutput[key].clone(); 143 | }); 144 | 145 | if (this._currentOutput.sizeData) { 146 | clone._currentOutput.sizeData = {}; 147 | utils.copy(this._currentOutput.sizeData, clone._currentOutput.sizeData); 148 | } 149 | 150 | utils.copy(this._currentOutput.flags, clone._currentOutput.flags); 151 | } 152 | 153 | // Clone argument lists 154 | ['_global', '_complexFilters'].forEach(function(prop) { 155 | clone[prop] = self[prop].clone(); 156 | }); 157 | 158 | return clone; 159 | }; 160 | 161 | 162 | /* Add methods from options submodules */ 163 | 164 | require('./options/inputs')(FfmpegCommand.prototype); 165 | require('./options/audio')(FfmpegCommand.prototype); 166 | require('./options/video')(FfmpegCommand.prototype); 167 | require('./options/videosize')(FfmpegCommand.prototype); 168 | require('./options/output')(FfmpegCommand.prototype); 169 | require('./options/custom')(FfmpegCommand.prototype); 170 | require('./options/misc')(FfmpegCommand.prototype); 171 | 172 | 173 | /* Add processor methods */ 174 | 175 | require('./processor')(FfmpegCommand.prototype); 176 | 177 | 178 | /* Add capabilities methods */ 179 | 180 | require('./capabilities')(FfmpegCommand.prototype); 181 | 182 | FfmpegCommand.setFfmpegPath = function(path) { 183 | (new FfmpegCommand()).setFfmpegPath(path); 184 | }; 185 | 186 | FfmpegCommand.setFfprobePath = function(path) { 187 | (new FfmpegCommand()).setFfprobePath(path); 188 | }; 189 | 190 | FfmpegCommand.setFlvtoolPath = function(path) { 191 | (new FfmpegCommand()).setFlvtoolPath(path); 192 | }; 193 | 194 | FfmpegCommand.availableFilters = 195 | FfmpegCommand.getAvailableFilters = function(callback) { 196 | (new FfmpegCommand()).availableFilters(callback); 197 | }; 198 | 199 | FfmpegCommand.availableCodecs = 200 | FfmpegCommand.getAvailableCodecs = function(callback) { 201 | (new FfmpegCommand()).availableCodecs(callback); 202 | }; 203 | 204 | FfmpegCommand.availableFormats = 205 | FfmpegCommand.getAvailableFormats = function(callback) { 206 | (new FfmpegCommand()).availableFormats(callback); 207 | }; 208 | 209 | FfmpegCommand.availableEncoders = 210 | FfmpegCommand.getAvailableEncoders = function(callback) { 211 | (new FfmpegCommand()).availableEncoders(callback); 212 | }; 213 | 214 | 215 | /* Add ffprobe methods */ 216 | 217 | require('./ffprobe')(FfmpegCommand.prototype); 218 | 219 | FfmpegCommand.ffprobe = function(file) { 220 | var instance = new FfmpegCommand(file); 221 | instance.ffprobe.apply(instance, Array.prototype.slice.call(arguments, 1)); 222 | }; 223 | 224 | /* Add processing recipes */ 225 | 226 | require('./recipes')(FfmpegCommand.prototype); 227 | -------------------------------------------------------------------------------- /lib/options/audio.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var utils = require('../utils'); 5 | 6 | 7 | /* 8 | *! Audio-related methods 9 | */ 10 | 11 | module.exports = function(proto) { 12 | /** 13 | * Disable audio in the output 14 | * 15 | * @method FfmpegCommand#noAudio 16 | * @category Audio 17 | * @aliases withNoAudio 18 | * @return FfmpegCommand 19 | */ 20 | proto.withNoAudio = 21 | proto.noAudio = function() { 22 | this._currentOutput.audio.clear(); 23 | this._currentOutput.audioFilters.clear(); 24 | this._currentOutput.audio('-an'); 25 | 26 | return this; 27 | }; 28 | 29 | 30 | /** 31 | * Specify audio codec 32 | * 33 | * @method FfmpegCommand#audioCodec 34 | * @category Audio 35 | * @aliases withAudioCodec 36 | * 37 | * @param {String} codec audio codec name 38 | * @return FfmpegCommand 39 | */ 40 | proto.withAudioCodec = 41 | proto.audioCodec = function(codec) { 42 | this._currentOutput.audio('-acodec', codec); 43 | 44 | return this; 45 | }; 46 | 47 | 48 | /** 49 | * Specify audio bitrate 50 | * 51 | * @method FfmpegCommand#audioBitrate 52 | * @category Audio 53 | * @aliases withAudioBitrate 54 | * 55 | * @param {String|Number} bitrate audio bitrate in kbps (with an optional 'k' suffix) 56 | * @return FfmpegCommand 57 | */ 58 | proto.withAudioBitrate = 59 | proto.audioBitrate = function(bitrate) { 60 | this._currentOutput.audio('-b:a', ('' + bitrate).replace(/k?$/, 'k')); 61 | return this; 62 | }; 63 | 64 | 65 | /** 66 | * Specify audio channel count 67 | * 68 | * @method FfmpegCommand#audioChannels 69 | * @category Audio 70 | * @aliases withAudioChannels 71 | * 72 | * @param {Number} channels channel count 73 | * @return FfmpegCommand 74 | */ 75 | proto.withAudioChannels = 76 | proto.audioChannels = function(channels) { 77 | this._currentOutput.audio('-ac', channels); 78 | return this; 79 | }; 80 | 81 | 82 | /** 83 | * Specify audio frequency 84 | * 85 | * @method FfmpegCommand#audioFrequency 86 | * @category Audio 87 | * @aliases withAudioFrequency 88 | * 89 | * @param {Number} freq audio frequency in Hz 90 | * @return FfmpegCommand 91 | */ 92 | proto.withAudioFrequency = 93 | proto.audioFrequency = function(freq) { 94 | this._currentOutput.audio('-ar', freq); 95 | return this; 96 | }; 97 | 98 | 99 | /** 100 | * Specify audio quality 101 | * 102 | * @method FfmpegCommand#audioQuality 103 | * @category Audio 104 | * @aliases withAudioQuality 105 | * 106 | * @param {Number} quality audio quality factor 107 | * @return FfmpegCommand 108 | */ 109 | proto.withAudioQuality = 110 | proto.audioQuality = function(quality) { 111 | this._currentOutput.audio('-aq', quality); 112 | return this; 113 | }; 114 | 115 | 116 | /** 117 | * Specify custom audio filter(s) 118 | * 119 | * Can be called both with one or many filters, or a filter array. 120 | * 121 | * @example 122 | * command.audioFilters('filter1'); 123 | * 124 | * @example 125 | * command.audioFilters('filter1', 'filter2=param1=value1:param2=value2'); 126 | * 127 | * @example 128 | * command.audioFilters(['filter1', 'filter2']); 129 | * 130 | * @example 131 | * command.audioFilters([ 132 | * { 133 | * filter: 'filter1' 134 | * }, 135 | * { 136 | * filter: 'filter2', 137 | * options: 'param=value:param=value' 138 | * } 139 | * ]); 140 | * 141 | * @example 142 | * command.audioFilters( 143 | * { 144 | * filter: 'filter1', 145 | * options: ['value1', 'value2'] 146 | * }, 147 | * { 148 | * filter: 'filter2', 149 | * options: { param1: 'value1', param2: 'value2' } 150 | * } 151 | * ); 152 | * 153 | * @method FfmpegCommand#audioFilters 154 | * @aliases withAudioFilter,withAudioFilters,audioFilter 155 | * @category Audio 156 | * 157 | * @param {...String|String[]|Object[]} filters audio filter strings, string array or 158 | * filter specification array, each with the following properties: 159 | * @param {String} filters.filter filter name 160 | * @param {String|String[]|Object} [filters.options] filter option string, array, or object 161 | * @return FfmpegCommand 162 | */ 163 | proto.withAudioFilter = 164 | proto.withAudioFilters = 165 | proto.audioFilter = 166 | proto.audioFilters = function(filters) { 167 | if (arguments.length > 1) { 168 | filters = [].slice.call(arguments); 169 | } 170 | 171 | if (!Array.isArray(filters)) { 172 | filters = [filters]; 173 | } 174 | 175 | this._currentOutput.audioFilters(utils.makeFilterStrings(filters)); 176 | return this; 177 | }; 178 | }; 179 | -------------------------------------------------------------------------------- /lib/options/custom.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var utils = require('../utils'); 5 | 6 | 7 | /* 8 | *! Custom options methods 9 | */ 10 | 11 | module.exports = function(proto) { 12 | /** 13 | * Add custom input option(s) 14 | * 15 | * When passing a single string or an array, each string containing two 16 | * words is split (eg. inputOptions('-option value') is supported) for 17 | * compatibility reasons. This is not the case when passing more than 18 | * one argument. 19 | * 20 | * @example 21 | * command.inputOptions('option1'); 22 | * 23 | * @example 24 | * command.inputOptions('option1', 'option2'); 25 | * 26 | * @example 27 | * command.inputOptions(['option1', 'option2']); 28 | * 29 | * @method FfmpegCommand#inputOptions 30 | * @category Custom options 31 | * @aliases addInputOption,addInputOptions,withInputOption,withInputOptions,inputOption 32 | * 33 | * @param {...String} options option string(s) or string array 34 | * @return FfmpegCommand 35 | */ 36 | proto.addInputOption = 37 | proto.addInputOptions = 38 | proto.withInputOption = 39 | proto.withInputOptions = 40 | proto.inputOption = 41 | proto.inputOptions = function(options) { 42 | if (!this._currentInput) { 43 | throw new Error('No input specified'); 44 | } 45 | 46 | var doSplit = true; 47 | 48 | if (arguments.length > 1) { 49 | options = [].slice.call(arguments); 50 | doSplit = false; 51 | } 52 | 53 | if (!Array.isArray(options)) { 54 | options = [options]; 55 | } 56 | 57 | this._currentInput.options(options.reduce(function(options, option) { 58 | var split = String(option).split(' '); 59 | 60 | if (doSplit && split.length === 2) { 61 | options.push(split[0], split[1]); 62 | } else { 63 | options.push(option); 64 | } 65 | 66 | return options; 67 | }, [])); 68 | return this; 69 | }; 70 | 71 | 72 | /** 73 | * Add custom output option(s) 74 | * 75 | * @example 76 | * command.outputOptions('option1'); 77 | * 78 | * @example 79 | * command.outputOptions('option1', 'option2'); 80 | * 81 | * @example 82 | * command.outputOptions(['option1', 'option2']); 83 | * 84 | * @method FfmpegCommand#outputOptions 85 | * @category Custom options 86 | * @aliases addOutputOption,addOutputOptions,addOption,addOptions,withOutputOption,withOutputOptions,withOption,withOptions,outputOption 87 | * 88 | * @param {...String} options option string(s) or string array 89 | * @return FfmpegCommand 90 | */ 91 | proto.addOutputOption = 92 | proto.addOutputOptions = 93 | proto.addOption = 94 | proto.addOptions = 95 | proto.withOutputOption = 96 | proto.withOutputOptions = 97 | proto.withOption = 98 | proto.withOptions = 99 | proto.outputOption = 100 | proto.outputOptions = function(options) { 101 | var doSplit = true; 102 | 103 | if (arguments.length > 1) { 104 | options = [].slice.call(arguments); 105 | doSplit = false; 106 | } 107 | 108 | if (!Array.isArray(options)) { 109 | options = [options]; 110 | } 111 | 112 | this._currentOutput.options(options.reduce(function(options, option) { 113 | var split = String(option).split(' '); 114 | 115 | if (doSplit && split.length === 2) { 116 | options.push(split[0], split[1]); 117 | } else { 118 | options.push(option); 119 | } 120 | 121 | return options; 122 | }, [])); 123 | return this; 124 | }; 125 | 126 | 127 | /** 128 | * Specify a complex filtergraph 129 | * 130 | * Calling this method will override any previously set filtergraph, but you can set 131 | * as many filters as needed in one call. 132 | * 133 | * @example <caption>Overlay an image over a video (using a filtergraph string)</caption> 134 | * ffmpeg() 135 | * .input('video.avi') 136 | * .input('image.png') 137 | * .complexFilter('[0:v][1:v]overlay[out]', ['out']); 138 | * 139 | * @example <caption>Overlay an image over a video (using a filter array)</caption> 140 | * ffmpeg() 141 | * .input('video.avi') 142 | * .input('image.png') 143 | * .complexFilter([{ 144 | * filter: 'overlay', 145 | * inputs: ['0:v', '1:v'], 146 | * outputs: ['out'] 147 | * }], ['out']); 148 | * 149 | * @example <caption>Split video into RGB channels and output a 3x1 video with channels side to side</caption> 150 | * ffmpeg() 151 | * .input('video.avi') 152 | * .complexFilter([ 153 | * // Duplicate video stream 3 times into streams a, b, and c 154 | * { filter: 'split', options: '3', outputs: ['a', 'b', 'c'] }, 155 | * 156 | * // Create stream 'red' by cancelling green and blue channels from stream 'a' 157 | * { filter: 'lutrgb', options: { g: 0, b: 0 }, inputs: 'a', outputs: 'red' }, 158 | * 159 | * // Create stream 'green' by cancelling red and blue channels from stream 'b' 160 | * { filter: 'lutrgb', options: { r: 0, b: 0 }, inputs: 'b', outputs: 'green' }, 161 | * 162 | * // Create stream 'blue' by cancelling red and green channels from stream 'c' 163 | * { filter: 'lutrgb', options: { r: 0, g: 0 }, inputs: 'c', outputs: 'blue' }, 164 | * 165 | * // Pad stream 'red' to 3x width, keeping the video on the left, and name output 'padded' 166 | * { filter: 'pad', options: { w: 'iw*3', h: 'ih' }, inputs: 'red', outputs: 'padded' }, 167 | * 168 | * // Overlay 'green' onto 'padded', moving it to the center, and name output 'redgreen' 169 | * { filter: 'overlay', options: { x: 'w', y: 0 }, inputs: ['padded', 'green'], outputs: 'redgreen'}, 170 | * 171 | * // Overlay 'blue' onto 'redgreen', moving it to the right 172 | * { filter: 'overlay', options: { x: '2*w', y: 0 }, inputs: ['redgreen', 'blue']}, 173 | * ]); 174 | * 175 | * @method FfmpegCommand#complexFilter 176 | * @category Custom options 177 | * @aliases filterGraph 178 | * 179 | * @param {String|Array} spec filtergraph string or array of filter specification 180 | * objects, each having the following properties: 181 | * @param {String} spec.filter filter name 182 | * @param {String|Array} [spec.inputs] (array of) input stream specifier(s) for the filter, 183 | * defaults to ffmpeg automatically choosing the first unused matching streams 184 | * @param {String|Array} [spec.outputs] (array of) output stream specifier(s) for the filter, 185 | * defaults to ffmpeg automatically assigning the output to the output file 186 | * @param {Object|String|Array} [spec.options] filter options, can be omitted to not set any options 187 | * @param {Array} [map] (array of) stream specifier(s) from the graph to include in 188 | * ffmpeg output, defaults to ffmpeg automatically choosing the first matching streams. 189 | * @return FfmpegCommand 190 | */ 191 | proto.filterGraph = 192 | proto.complexFilter = function(spec, map) { 193 | this._complexFilters.clear(); 194 | 195 | if (!Array.isArray(spec)) { 196 | spec = [spec]; 197 | } 198 | 199 | this._complexFilters('-filter_complex', utils.makeFilterStrings(spec).join(';')); 200 | 201 | if (Array.isArray(map)) { 202 | var self = this; 203 | map.forEach(function(streamSpec) { 204 | self._complexFilters('-map', streamSpec.replace(utils.streamRegexp, '[$1]')); 205 | }); 206 | } else if (typeof map === 'string') { 207 | this._complexFilters('-map', map.replace(utils.streamRegexp, '[$1]')); 208 | } 209 | 210 | return this; 211 | }; 212 | }; 213 | -------------------------------------------------------------------------------- /lib/options/inputs.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var utils = require('../utils'); 5 | 6 | /* 7 | *! Input-related methods 8 | */ 9 | 10 | module.exports = function(proto) { 11 | /** 12 | * Add an input to command 13 | * 14 | * Also switches "current input", that is the input that will be affected 15 | * by subsequent input-related methods. 16 | * 17 | * Note: only one stream input is supported for now. 18 | * 19 | * @method FfmpegCommand#input 20 | * @category Input 21 | * @aliases mergeAdd,addInput 22 | * 23 | * @param {String|Readable} source input file path or readable stream 24 | * @return FfmpegCommand 25 | */ 26 | proto.mergeAdd = 27 | proto.addInput = 28 | proto.input = function(source) { 29 | var isFile = false; 30 | var isStream = false; 31 | 32 | if (typeof source !== 'string') { 33 | if (!('readable' in source) || !(source.readable)) { 34 | throw new Error('Invalid input'); 35 | } 36 | 37 | var hasInputStream = this._inputs.some(function(input) { 38 | return input.isStream; 39 | }); 40 | 41 | if (hasInputStream) { 42 | throw new Error('Only one input stream is supported'); 43 | } 44 | 45 | isStream = true; 46 | source.pause(); 47 | } else { 48 | var protocol = source.match(/^([a-z]{2,}):/i); 49 | isFile = !protocol || protocol[0] === 'file'; 50 | } 51 | 52 | this._inputs.push(this._currentInput = { 53 | source: source, 54 | isFile: isFile, 55 | isStream: isStream, 56 | options: utils.args() 57 | }); 58 | 59 | return this; 60 | }; 61 | 62 | 63 | /** 64 | * Specify input format for the last specified input 65 | * 66 | * @method FfmpegCommand#inputFormat 67 | * @category Input 68 | * @aliases withInputFormat,fromFormat 69 | * 70 | * @param {String} format input format 71 | * @return FfmpegCommand 72 | */ 73 | proto.withInputFormat = 74 | proto.inputFormat = 75 | proto.fromFormat = function(format) { 76 | if (!this._currentInput) { 77 | throw new Error('No input specified'); 78 | } 79 | 80 | this._currentInput.options('-f', format); 81 | return this; 82 | }; 83 | 84 | 85 | /** 86 | * Specify input FPS for the last specified input 87 | * (only valid for raw video formats) 88 | * 89 | * @method FfmpegCommand#inputFps 90 | * @category Input 91 | * @aliases withInputFps,withInputFPS,withFpsInput,withFPSInput,inputFPS,inputFps,fpsInput 92 | * 93 | * @param {Number} fps input FPS 94 | * @return FfmpegCommand 95 | */ 96 | proto.withInputFps = 97 | proto.withInputFPS = 98 | proto.withFpsInput = 99 | proto.withFPSInput = 100 | proto.inputFPS = 101 | proto.inputFps = 102 | proto.fpsInput = 103 | proto.FPSInput = function(fps) { 104 | if (!this._currentInput) { 105 | throw new Error('No input specified'); 106 | } 107 | 108 | this._currentInput.options('-r', fps); 109 | return this; 110 | }; 111 | 112 | 113 | /** 114 | * Use native framerate for the last specified input 115 | * 116 | * @method FfmpegCommand#native 117 | * @category Input 118 | * @aliases nativeFramerate,withNativeFramerate 119 | * 120 | * @return FfmmegCommand 121 | */ 122 | proto.nativeFramerate = 123 | proto.withNativeFramerate = 124 | proto.native = function() { 125 | if (!this._currentInput) { 126 | throw new Error('No input specified'); 127 | } 128 | 129 | this._currentInput.options('-re'); 130 | return this; 131 | }; 132 | 133 | 134 | /** 135 | * Specify input seek time for the last specified input 136 | * 137 | * @method FfmpegCommand#seekInput 138 | * @category Input 139 | * @aliases setStartTime,seekTo 140 | * 141 | * @param {String|Number} seek seek time in seconds or as a '[hh:[mm:]]ss[.xxx]' string 142 | * @return FfmpegCommand 143 | */ 144 | proto.setStartTime = 145 | proto.seekInput = function(seek) { 146 | if (!this._currentInput) { 147 | throw new Error('No input specified'); 148 | } 149 | 150 | this._currentInput.options('-ss', seek); 151 | 152 | return this; 153 | }; 154 | 155 | 156 | /** 157 | * Loop over the last specified input 158 | * 159 | * @method FfmpegCommand#loop 160 | * @category Input 161 | * 162 | * @param {String|Number} [duration] loop duration in seconds or as a '[[hh:]mm:]ss[.xxx]' string 163 | * @return FfmpegCommand 164 | */ 165 | proto.loop = function(duration) { 166 | if (!this._currentInput) { 167 | throw new Error('No input specified'); 168 | } 169 | 170 | this._currentInput.options('-loop', '1'); 171 | 172 | if (typeof duration !== 'undefined') { 173 | this.duration(duration); 174 | } 175 | 176 | return this; 177 | }; 178 | }; 179 | -------------------------------------------------------------------------------- /lib/options/misc.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var path = require('path'); 5 | 6 | /* 7 | *! Miscellaneous methods 8 | */ 9 | 10 | module.exports = function(proto) { 11 | /** 12 | * Use preset 13 | * 14 | * @method FfmpegCommand#preset 15 | * @category Miscellaneous 16 | * @aliases usingPreset 17 | * 18 | * @param {String|Function} preset preset name or preset function 19 | */ 20 | proto.usingPreset = 21 | proto.preset = function(preset) { 22 | if (typeof preset === 'function') { 23 | preset(this); 24 | } else { 25 | try { 26 | var modulePath = path.join(this.options.presets, preset); 27 | var module = require(modulePath); 28 | 29 | if (typeof module.load === 'function') { 30 | module.load(this); 31 | } else { 32 | throw new Error('preset ' + modulePath + ' has no load() function'); 33 | } 34 | } catch (err) { 35 | throw new Error('preset ' + modulePath + ' could not be loaded: ' + err.message); 36 | } 37 | } 38 | 39 | return this; 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /lib/options/output.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var utils = require('../utils'); 5 | 6 | 7 | /* 8 | *! Output-related methods 9 | */ 10 | 11 | module.exports = function(proto) { 12 | /** 13 | * Add output 14 | * 15 | * @method FfmpegCommand#output 16 | * @category Output 17 | * @aliases addOutput 18 | * 19 | * @param {String|Writable} target target file path or writable stream 20 | * @param {Object} [pipeopts={}] pipe options (only applies to streams) 21 | * @return FfmpegCommand 22 | */ 23 | proto.addOutput = 24 | proto.output = function(target, pipeopts) { 25 | var isFile = false; 26 | 27 | if (!target && this._currentOutput) { 28 | // No target is only allowed when called from constructor 29 | throw new Error('Invalid output'); 30 | } 31 | 32 | if (target && typeof target !== 'string') { 33 | if (!('writable' in target) || !(target.writable)) { 34 | throw new Error('Invalid output'); 35 | } 36 | } else if (typeof target === 'string') { 37 | var protocol = target.match(/^([a-z]{2,}):/i); 38 | isFile = !protocol || protocol[0] === 'file'; 39 | } 40 | 41 | if (target && !('target' in this._currentOutput)) { 42 | // For backwards compatibility, set target for first output 43 | this._currentOutput.target = target; 44 | this._currentOutput.isFile = isFile; 45 | this._currentOutput.pipeopts = pipeopts || {}; 46 | } else { 47 | if (target && typeof target !== 'string') { 48 | var hasOutputStream = this._outputs.some(function(output) { 49 | return typeof output.target !== 'string'; 50 | }); 51 | 52 | if (hasOutputStream) { 53 | throw new Error('Only one output stream is supported'); 54 | } 55 | } 56 | 57 | this._outputs.push(this._currentOutput = { 58 | target: target, 59 | isFile: isFile, 60 | flags: {}, 61 | pipeopts: pipeopts || {} 62 | }); 63 | 64 | var self = this; 65 | ['audio', 'audioFilters', 'video', 'videoFilters', 'sizeFilters', 'options'].forEach(function(key) { 66 | self._currentOutput[key] = utils.args(); 67 | }); 68 | 69 | if (!target) { 70 | // Call from constructor: remove target key 71 | delete this._currentOutput.target; 72 | } 73 | } 74 | 75 | return this; 76 | }; 77 | 78 | 79 | /** 80 | * Specify output seek time 81 | * 82 | * @method FfmpegCommand#seek 83 | * @category Input 84 | * @aliases seekOutput 85 | * 86 | * @param {String|Number} seek seek time in seconds or as a '[hh:[mm:]]ss[.xxx]' string 87 | * @return FfmpegCommand 88 | */ 89 | proto.seekOutput = 90 | proto.seek = function(seek) { 91 | this._currentOutput.options('-ss', seek); 92 | return this; 93 | }; 94 | 95 | 96 | /** 97 | * Set output duration 98 | * 99 | * @method FfmpegCommand#duration 100 | * @category Output 101 | * @aliases withDuration,setDuration 102 | * 103 | * @param {String|Number} duration duration in seconds or as a '[[hh:]mm:]ss[.xxx]' string 104 | * @return FfmpegCommand 105 | */ 106 | proto.withDuration = 107 | proto.setDuration = 108 | proto.duration = function(duration) { 109 | this._currentOutput.options('-t', duration); 110 | return this; 111 | }; 112 | 113 | 114 | /** 115 | * Set output format 116 | * 117 | * @method FfmpegCommand#format 118 | * @category Output 119 | * @aliases toFormat,withOutputFormat,outputFormat 120 | * 121 | * @param {String} format output format name 122 | * @return FfmpegCommand 123 | */ 124 | proto.toFormat = 125 | proto.withOutputFormat = 126 | proto.outputFormat = 127 | proto.format = function(format) { 128 | this._currentOutput.options('-f', format); 129 | return this; 130 | }; 131 | 132 | 133 | /** 134 | * Add stream mapping to output 135 | * 136 | * @method FfmpegCommand#map 137 | * @category Output 138 | * 139 | * @param {String} spec stream specification string, with optional square brackets 140 | * @return FfmpegCommand 141 | */ 142 | proto.map = function(spec) { 143 | this._currentOutput.options('-map', spec.replace(utils.streamRegexp, '[$1]')); 144 | return this; 145 | }; 146 | 147 | 148 | /** 149 | * Run flvtool2/flvmeta on output 150 | * 151 | * @method FfmpegCommand#flvmeta 152 | * @category Output 153 | * @aliases updateFlvMetadata 154 | * 155 | * @return FfmpegCommand 156 | */ 157 | proto.updateFlvMetadata = 158 | proto.flvmeta = function() { 159 | this._currentOutput.flags.flvmeta = true; 160 | return this; 161 | }; 162 | }; 163 | -------------------------------------------------------------------------------- /lib/options/video.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var utils = require('../utils'); 5 | 6 | 7 | /* 8 | *! Video-related methods 9 | */ 10 | 11 | module.exports = function(proto) { 12 | /** 13 | * Disable video in the output 14 | * 15 | * @method FfmpegCommand#noVideo 16 | * @category Video 17 | * @aliases withNoVideo 18 | * 19 | * @return FfmpegCommand 20 | */ 21 | proto.withNoVideo = 22 | proto.noVideo = function() { 23 | this._currentOutput.video.clear(); 24 | this._currentOutput.videoFilters.clear(); 25 | this._currentOutput.video('-vn'); 26 | 27 | return this; 28 | }; 29 | 30 | 31 | /** 32 | * Specify video codec 33 | * 34 | * @method FfmpegCommand#videoCodec 35 | * @category Video 36 | * @aliases withVideoCodec 37 | * 38 | * @param {String} codec video codec name 39 | * @return FfmpegCommand 40 | */ 41 | proto.withVideoCodec = 42 | proto.videoCodec = function(codec) { 43 | this._currentOutput.video('-vcodec', codec); 44 | return this; 45 | }; 46 | 47 | 48 | /** 49 | * Specify video bitrate 50 | * 51 | * @method FfmpegCommand#videoBitrate 52 | * @category Video 53 | * @aliases withVideoBitrate 54 | * 55 | * @param {String|Number} bitrate video bitrate in kbps (with an optional 'k' suffix) 56 | * @param {Boolean} [constant=false] enforce constant bitrate 57 | * @return FfmpegCommand 58 | */ 59 | proto.withVideoBitrate = 60 | proto.videoBitrate = function(bitrate, constant) { 61 | bitrate = ('' + bitrate).replace(/k?$/, 'k'); 62 | 63 | this._currentOutput.video('-b:v', bitrate); 64 | if (constant) { 65 | this._currentOutput.video( 66 | '-maxrate', bitrate, 67 | '-minrate', bitrate, 68 | '-bufsize', '3M' 69 | ); 70 | } 71 | 72 | return this; 73 | }; 74 | 75 | 76 | /** 77 | * Specify custom video filter(s) 78 | * 79 | * Can be called both with one or many filters, or a filter array. 80 | * 81 | * @example 82 | * command.videoFilters('filter1'); 83 | * 84 | * @example 85 | * command.videoFilters('filter1', 'filter2=param1=value1:param2=value2'); 86 | * 87 | * @example 88 | * command.videoFilters(['filter1', 'filter2']); 89 | * 90 | * @example 91 | * command.videoFilters([ 92 | * { 93 | * filter: 'filter1' 94 | * }, 95 | * { 96 | * filter: 'filter2', 97 | * options: 'param=value:param=value' 98 | * } 99 | * ]); 100 | * 101 | * @example 102 | * command.videoFilters( 103 | * { 104 | * filter: 'filter1', 105 | * options: ['value1', 'value2'] 106 | * }, 107 | * { 108 | * filter: 'filter2', 109 | * options: { param1: 'value1', param2: 'value2' } 110 | * } 111 | * ); 112 | * 113 | * @method FfmpegCommand#videoFilters 114 | * @category Video 115 | * @aliases withVideoFilter,withVideoFilters,videoFilter 116 | * 117 | * @param {...String|String[]|Object[]} filters video filter strings, string array or 118 | * filter specification array, each with the following properties: 119 | * @param {String} filters.filter filter name 120 | * @param {String|String[]|Object} [filters.options] filter option string, array, or object 121 | * @return FfmpegCommand 122 | */ 123 | proto.withVideoFilter = 124 | proto.withVideoFilters = 125 | proto.videoFilter = 126 | proto.videoFilters = function(filters) { 127 | if (arguments.length > 1) { 128 | filters = [].slice.call(arguments); 129 | } 130 | 131 | if (!Array.isArray(filters)) { 132 | filters = [filters]; 133 | } 134 | 135 | this._currentOutput.videoFilters(utils.makeFilterStrings(filters)); 136 | 137 | return this; 138 | }; 139 | 140 | 141 | /** 142 | * Specify output FPS 143 | * 144 | * @method FfmpegCommand#fps 145 | * @category Video 146 | * @aliases withOutputFps,withOutputFPS,withFpsOutput,withFPSOutput,withFps,withFPS,outputFPS,outputFps,fpsOutput,FPSOutput,FPS 147 | * 148 | * @param {Number} fps output FPS 149 | * @return FfmpegCommand 150 | */ 151 | proto.withOutputFps = 152 | proto.withOutputFPS = 153 | proto.withFpsOutput = 154 | proto.withFPSOutput = 155 | proto.withFps = 156 | proto.withFPS = 157 | proto.outputFPS = 158 | proto.outputFps = 159 | proto.fpsOutput = 160 | proto.FPSOutput = 161 | proto.fps = 162 | proto.FPS = function(fps) { 163 | this._currentOutput.video('-r', fps); 164 | return this; 165 | }; 166 | 167 | 168 | /** 169 | * Only transcode a certain number of frames 170 | * 171 | * @method FfmpegCommand#frames 172 | * @category Video 173 | * @aliases takeFrames,withFrames 174 | * 175 | * @param {Number} frames frame count 176 | * @return FfmpegCommand 177 | */ 178 | proto.takeFrames = 179 | proto.withFrames = 180 | proto.frames = function(frames) { 181 | this._currentOutput.video('-vframes', frames); 182 | return this; 183 | }; 184 | }; 185 | -------------------------------------------------------------------------------- /lib/presets/divx.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | 'use strict'; 3 | 4 | exports.load = function(ffmpeg) { 5 | ffmpeg 6 | .format('avi') 7 | .videoBitrate('1024k') 8 | .videoCodec('mpeg4') 9 | .size('720x?') 10 | .audioBitrate('128k') 11 | .audioChannels(2) 12 | .audioCodec('libmp3lame') 13 | .outputOptions(['-vtag DIVX']); 14 | }; -------------------------------------------------------------------------------- /lib/presets/flashvideo.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | 'use strict'; 3 | 4 | exports.load = function(ffmpeg) { 5 | ffmpeg 6 | .format('flv') 7 | .flvmeta() 8 | .size('320x?') 9 | .videoBitrate('512k') 10 | .videoCodec('libx264') 11 | .fps(24) 12 | .audioBitrate('96k') 13 | .audioCodec('aac') 14 | .audioFrequency(22050) 15 | .audioChannels(2); 16 | }; 17 | -------------------------------------------------------------------------------- /lib/presets/podcast.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true */ 2 | 'use strict'; 3 | 4 | exports.load = function(ffmpeg) { 5 | ffmpeg 6 | .format('m4v') 7 | .videoBitrate('512k') 8 | .videoCodec('libx264') 9 | .size('320x176') 10 | .audioBitrate('128k') 11 | .audioCodec('aac') 12 | .audioChannels(1) 13 | .outputOptions(['-flags', '+loop', '-cmp', '+chroma', '-partitions','+parti4x4+partp8x8+partb8x8', '-flags2', 14 | '+mixed_refs', '-me_method umh', '-subq 5', '-bufsize 2M', '-rc_eq \'blurCplx^(1-qComp)\'', 15 | '-qcomp 0.6', '-qmin 10', '-qmax 51', '-qdiff 4', '-level 13' ]); 16 | }; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fluent-ffmpeg", 3 | "version": "2.1.3", 4 | "description": "A fluent API to FFMPEG (http://www.ffmpeg.org)", 5 | "keywords": [ 6 | "ffmpeg" 7 | ], 8 | "author": "Stefan Schaermeli <schaermu@gmail.com>", 9 | "contributors": [ 10 | { 11 | "name": "Felix Fichte", 12 | "email": "spruce@space-ships.de" 13 | } 14 | ], 15 | "license": "MIT", 16 | "bugs": { 17 | "mail": "schaermu@gmail.com", 18 | "url": "http://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues" 19 | }, 20 | "repository": "git://github.com/fluent-ffmpeg/node-fluent-ffmpeg.git", 21 | "devDependencies": { 22 | "jsdoc": "^4.0.0", 23 | "mocha": "^10.0.0", 24 | "nyc": "^15.1.0", 25 | "should": "^13.0.0" 26 | }, 27 | "dependencies": { 28 | "async": "^0.2.9", 29 | "which": "^1.1.1" 30 | }, 31 | "engines": { 32 | "node": ">=18" 33 | }, 34 | "main": "index", 35 | "scripts": { 36 | "test": "NODE_ENV=test nyc mocha --require should --reporter spec", 37 | "coverage": "nyc report --reporter=lcov" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/aliases.test.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | /*global describe,it*/ 3 | 'use strict'; 4 | 5 | var Ffmpeg = require('../index'); 6 | 7 | var aliases = { 8 | audio: { 9 | withNoAudio: ['noAudio'], 10 | withAudioCodec: ['audioCodec'], 11 | withAudioBitrate: ['audioBitrate'], 12 | withAudioChannels: ['audioChannels'], 13 | withAudioFrequency: ['audioFrequency'], 14 | withAudioQuality: ['audioQuality'], 15 | withAudioFilter: ['withAudioFilters','audioFilter','audioFilters'] 16 | }, 17 | 18 | custom: { 19 | addInputOption: ['addInputOptions','withInputOption','withInputOptions','inputOption','inputOptions'], 20 | addOutputOption: ['addOutputOptions','addOption','addOptions','withOutputOption','withOutputOptions','withOption','withOptions','outputOption','outputOptions'], 21 | complexFilter: ['filterGraph'] 22 | }, 23 | 24 | inputs: { 25 | addInput: ['input','mergeAdd'], 26 | fromFormat: ['withInputFormat','inputFormat'], 27 | withInputFps: ['withInputFPS','withFpsInput','withFPSInput','inputFPS','inputFps','fpsInput','FPSInput'], 28 | native: ['withNativeFramerate','nativeFramerate'], 29 | setStartTime: ['seekInput'] 30 | }, 31 | 32 | misc: { 33 | usingPreset: ['preset'] 34 | }, 35 | 36 | output: { 37 | addOutput: ['output'], 38 | withDuration: ['duration','setDuration'], 39 | toFormat: ['withOutputFormat','outputFormat','format'], 40 | seek: ['seekOutput'], 41 | updateFlvMetadata: ['flvmeta'] 42 | }, 43 | 44 | video: { 45 | withNoVideo: ['noVideo'], 46 | withVideoCodec: ['videoCodec'], 47 | withVideoBitrate: ['videoBitrate'], 48 | withVideoFilter: ['withVideoFilters','videoFilter','videoFilters'], 49 | withOutputFps: ['withOutputFPS','withFpsOutput','withFPSOutput','withFps','withFPS','outputFPS','outputFps','fpsOutput','FPSOutput','fps','FPS'], 50 | takeFrames: ['withFrames','frames'] 51 | }, 52 | 53 | videosize: { 54 | keepPixelAspect: ['keepDisplayAspect','keepDisplayAspectRatio','keepDAR'], 55 | withSize: ['setSize', 'size'], 56 | withAspect: ['withAspectRatio','setAspect','setAspectRatio','aspect','aspectRatio'], 57 | applyAutopadding: ['applyAutoPadding','applyAutopad','applyAutoPad','withAutopadding','withAutoPadding','withAutopad','withAutoPad','autoPad','autopad'] 58 | }, 59 | 60 | processing: { 61 | saveToFile: ['save'], 62 | writeToStream: ['stream', 'pipe'], 63 | run: ['exec', 'execute'], 64 | concat: ['concatenate', 'mergeToFile'], 65 | screenshots: ['screenshot', 'thumbnails', 'thumbnail', 'takeScreenshots'] 66 | } 67 | }; 68 | 69 | describe('Method aliases', function() { 70 | Object.keys(aliases).forEach(function(category) { 71 | describe(category + ' methods', function() { 72 | Object.keys(aliases[category]).forEach(function(method) { 73 | describe('FfmpegCommand#' + method, function() { 74 | aliases[category][method].forEach(function(alias) { 75 | it('should have a \'' + alias + '\' alias', function() { 76 | var ff = new Ffmpeg(); 77 | 78 | (typeof ff[method]).should.equal('function'); 79 | ff[method].should.equal(ff[alias]); 80 | }); 81 | }); 82 | }); 83 | }); 84 | }); 85 | }); 86 | }); -------------------------------------------------------------------------------- /test/assets/ffserver.conf: -------------------------------------------------------------------------------- 1 | Port 8090 2 | BindAddress 127.0.0.1 3 | RTSPPort 5540 4 | RTSPBindAddress 127.0.0.1 5 | MaxHTTPConnections 1000 6 | MaxClients 10 7 | MaxBandwidth 1000000 8 | 9 | <Stream test.mpg> 10 | ReadOnlyFile teststream.ffm 11 | ACL allow 127.0.0.1 12 | Format mpegts 13 | AudioCodec mp2 14 | VideoCodec libx264 15 | </Stream> 16 | 17 | <Stream test-rtp.mpg> 18 | ReadOnlyFile teststream.ffm 19 | ACL allow 127.0.0.1 20 | Format rtp 21 | </Stream> 22 | 23 | <Stream input.mpg> 24 | File testinput.ffm 25 | ACL allow 127.0.0.1 26 | Format mpegts 27 | AudioCodec mp2 28 | VideoCodec libx264 29 | </Stream> 30 | -------------------------------------------------------------------------------- /test/assets/presets/custompreset.js: -------------------------------------------------------------------------------- 1 | exports.load = function(ffmpeg) { 2 | ffmpeg 3 | .toFormat('m4v') 4 | .withVideoBitrate('512k') 5 | .withVideoCodec('libx264') 6 | .withSize('320x176') 7 | .withAudioBitrate('128k') 8 | .withAudioCodec('aac') 9 | .withAudioChannels(1) 10 | .addOptions(['-flags', '+loop', '-cmp', '+chroma', '-partitions','+parti4x4+partp8x8+partb8x8', '-flags2', 11 | '+mixed_refs', '-me_method umh', '-subq 5', '-bufsize 2M', '-rc_eq \'blurCplx^(1-qComp)\'', 12 | '-qcomp 0.6', '-qmin 10', '-qmax 51', '-qdiff 4', '-level 13' ]); 13 | return ffmpeg; 14 | }; 15 | -------------------------------------------------------------------------------- /test/assets/te[s]t_ video ' _ .flv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/test/assets/te[s]t_ video ' _ .flv -------------------------------------------------------------------------------- /test/assets/testaudio-one.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/test/assets/testaudio-one.wav -------------------------------------------------------------------------------- /test/assets/testaudio-three.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/test/assets/testaudio-three.wav -------------------------------------------------------------------------------- /test/assets/testaudio-two.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/test/assets/testaudio-two.wav -------------------------------------------------------------------------------- /test/assets/teststream.ffm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/test/assets/teststream.ffm -------------------------------------------------------------------------------- /test/assets/testvideo-169.avi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/test/assets/testvideo-169.avi -------------------------------------------------------------------------------- /test/assets/testvideo-43.avi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/test/assets/testvideo-43.avi -------------------------------------------------------------------------------- /test/assets/testvideo-5m.mpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluent-ffmpeg/node-fluent-ffmpeg/7a701f560f6cd0f152cdef22c8a03132c56afc7b/test/assets/testvideo-5m.mpg -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var TestHelpers; 5 | 6 | exports = module.exports = TestHelpers = { 7 | getFfmpegCheck: function() { 8 | var platform = require('os').platform(); 9 | 10 | if (!platform.match(/win(32|64)/)) { 11 | // linux/mac, use which 12 | return 'which ffmpeg'; 13 | } else { 14 | // windows, use where (> windows server 2003 / windows 7) 15 | return 'where /Q ffmpeg'; 16 | } 17 | }, 18 | 19 | logger: { 20 | debug: function(arg) { if (process.env.FLUENTFFMPEG_COV !== '1') console.log(' [DEBUG] ' + arg); }, 21 | info: function(arg) { if (process.env.FLUENTFFMPEG_COV !== '1') console.log(' [INFO] ' + arg); }, 22 | warn: function(arg) { if (process.env.FLUENTFFMPEG_COV !== '1') console.log(' [WARN] ' + arg); }, 23 | error: function(arg) { if (process.env.FLUENTFFMPEG_COV !== '1') console.log(' [ERROR] ' + arg); } 24 | }, 25 | 26 | logArgError: function(err) { 27 | if (err) { 28 | console.log('got error: ' + (err.stack || err)); 29 | if (err.ffmpegOut) { 30 | console.log('---stdout---'); 31 | console.log(err.ffmpegOut); 32 | } 33 | if (err.ffmpegErr) { 34 | console.log('---stderr---'); 35 | console.log(err.ffmpegErr); 36 | } 37 | if (err.spawnErr) { 38 | console.log('---spawn error---'); 39 | console.log(err.spawnErr.stack || err.spawnErr); 40 | } 41 | } 42 | }, 43 | 44 | logError: function(err, stdout, stderr) { 45 | if (err) { 46 | console.log('got error: ' + (err.stack || err)); 47 | if (err.ffmpegOut) { 48 | console.log('---metadata stdout---'); 49 | console.log(err.ffmpegOut); 50 | } 51 | if (err.ffmpegErr) { 52 | console.log('---metadata stderr---'); 53 | console.log(err.ffmpegErr); 54 | } 55 | if (err.spawnErr) { 56 | console.log('---metadata spawn error---'); 57 | console.log(err.spawnErr.stack || err.spawnErr); 58 | } 59 | if (stdout) { 60 | console.log('---stdout---'); 61 | console.log(stdout); 62 | } 63 | if (stderr) { 64 | console.log('---stderr---'); 65 | console.log(stderr); 66 | } 67 | } 68 | }, 69 | 70 | logOutput: function(stdout, stderr) { 71 | if (stdout) { 72 | console.log('---stdout---'); 73 | console.log(stdout); 74 | } 75 | if (stderr) { 76 | console.log('---stderr---'); 77 | console.log(stderr); 78 | } 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /test/metadata.test.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | /*global describe,it,before*/ 3 | 'use strict'; 4 | 5 | var Ffmpeg = require('../index'), 6 | path = require('path'), 7 | fs = require('fs'), 8 | Readable = require('stream').Readable, 9 | assert = require('assert'), 10 | exec = require('child_process').exec, 11 | testhelper = require('./helpers'); 12 | 13 | 14 | describe('Metadata', function() { 15 | before(function(done) { 16 | // check for ffmpeg installation 17 | this.testfile = path.join(__dirname, 'assets', 'testvideo-43.avi'); 18 | 19 | var self = this; 20 | exec(testhelper.getFfmpegCheck(), function(err) { 21 | if (!err) { 22 | // check if file exists 23 | fs.exists(self.testfile, function(exists) { 24 | if (exists) { 25 | done(); 26 | } else { 27 | done(new Error('test video file does not exist, check path (' + self.testfile + ')')); 28 | } 29 | }); 30 | } else { 31 | done(new Error('cannot run test without ffmpeg installed, aborting test...')); 32 | } 33 | }); 34 | }); 35 | 36 | it('should provide an ffprobe entry point', function(done) { 37 | (typeof Ffmpeg.ffprobe).should.equal('function'); 38 | done(); 39 | }); 40 | 41 | it('should return ffprobe data as an object', function(done) { 42 | Ffmpeg.ffprobe(this.testfile, function(err, data) { 43 | testhelper.logError(err); 44 | assert.ok(!err); 45 | 46 | (typeof data).should.equal('object'); 47 | done(); 48 | }); 49 | }); 50 | 51 | it('should provide ffprobe format information', function(done) { 52 | Ffmpeg.ffprobe(this.testfile, function(err, data) { 53 | testhelper.logError(err); 54 | assert.ok(!err); 55 | 56 | ('format' in data).should.equal(true); 57 | (typeof data.format).should.equal('object'); 58 | Number(data.format.duration).should.equal(2); 59 | data.format.format_name.should.equal('avi'); 60 | 61 | done(); 62 | }); 63 | }); 64 | 65 | it('should provide ffprobe stream information', function(done) { 66 | Ffmpeg.ffprobe(this.testfile, function(err, data) { 67 | testhelper.logError(err); 68 | assert.ok(!err); 69 | 70 | ('streams' in data).should.equal(true); 71 | Array.isArray(data.streams).should.equal(true); 72 | data.streams.length.should.equal(1); 73 | data.streams[0].codec_type.should.equal('video'); 74 | data.streams[0].codec_name.should.equal('mpeg4'); 75 | Number(data.streams[0].width).should.equal(1024); 76 | 77 | done(); 78 | }); 79 | }); 80 | 81 | it('should provide ffprobe stream information with units', function(done) { 82 | Ffmpeg.ffprobe(this.testfile, ['-unit'], function(err, data) { 83 | testhelper.logError(err); 84 | assert.ok(!err); 85 | 86 | ('streams' in data).should.equal(true); 87 | Array.isArray(data.streams).should.equal(true); 88 | data.streams.length.should.equal(1); 89 | data.streams[0].bit_rate.should.equal('322427 bit/s'); 90 | done(); 91 | }); 92 | }); 93 | 94 | it('should return ffprobe errors', function(done) { 95 | Ffmpeg.ffprobe('/path/to/missing/file', function(err) { 96 | assert.ok(!!err); 97 | done(); 98 | }); 99 | }); 100 | 101 | it('should enable calling ffprobe on a command with an input file', function(done) { 102 | new Ffmpeg({ source: this.testfile }) 103 | .ffprobe(function(err, data) { 104 | testhelper.logError(err); 105 | assert.ok(!err); 106 | 107 | (typeof data).should.equal('object'); 108 | ('format' in data).should.equal(true); 109 | (typeof data.format).should.equal('object'); 110 | ('streams' in data).should.equal(true); 111 | Array.isArray(data.streams).should.equal(true); 112 | 113 | done(); 114 | }); 115 | }); 116 | 117 | it('should fail calling ffprobe on a command without input', function(done) { 118 | new Ffmpeg().ffprobe(function(err) { 119 | assert.ok(!!err); 120 | err.message.should.match(/No input specified/); 121 | done(); 122 | }); 123 | }); 124 | 125 | it('should allow calling ffprobe on stream input', function(done) { 126 | var stream = fs.createReadStream(this.testfile); 127 | 128 | new Ffmpeg() 129 | .addInput(stream) 130 | .ffprobe(function(err, data) { 131 | assert.ok(!err); 132 | data.streams.length.should.equal(1); 133 | data.format.filename.should.equal('pipe:0'); 134 | done(); 135 | }); 136 | }); 137 | }); 138 | -------------------------------------------------------------------------------- /test/utils.test.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | /*global describe,it*/ 3 | 'use strict'; 4 | 5 | var utils = require('../lib/utils'); 6 | 7 | describe('Utilities', function() { 8 | 9 | describe('Argument list helper', function() { 10 | it('Should add arguments to the list', function() { 11 | var args = utils.args(); 12 | 13 | args('-one'); 14 | args('-two', 'two-param'); 15 | args('-three', 'three-param1', 'three-param2'); 16 | args(['-four', 'four-param', '-five', '-five-param']); 17 | 18 | args.get().length.should.equal(10); 19 | }); 20 | 21 | it('Should return the argument list', function() { 22 | var args = utils.args(); 23 | 24 | args('-one'); 25 | args('-two', 'two-param'); 26 | args('-three', 'three-param1', 'three-param2'); 27 | args(['-four', 'four-param', '-five', '-five-param']); 28 | 29 | var arr = args.get(); 30 | Array.isArray(arr).should.equal(true); 31 | arr.length.should.equal(10); 32 | arr.indexOf('-three').should.equal(3); 33 | arr.indexOf('four-param').should.equal(7); 34 | }); 35 | 36 | it('Should clear the argument list', function() { 37 | var args = utils.args(); 38 | 39 | args('-one'); 40 | args('-two', 'two-param'); 41 | args('-three', 'three-param1', 'three-param2'); 42 | args(['-four', 'four-param', '-five', '-five-param']); 43 | args.clear(); 44 | 45 | args.get().length.should.equal(0); 46 | }); 47 | 48 | it('Should retrieve arguments from the list', function() { 49 | var args = utils.args(); 50 | 51 | args('-one'); 52 | args('-two', 'two-param'); 53 | args('-three', 'three-param1', 'three-param2'); 54 | args(['-four', 'four-param', '-five', '-five-param']); 55 | 56 | var one = args.find('-one'); 57 | Array.isArray(one).should.equal(true); 58 | one.length.should.equal(0); 59 | 60 | var two = args.find('-two', 1); 61 | Array.isArray(two).should.equal(true); 62 | two.length.should.equal(1); 63 | two[0].should.equal('two-param'); 64 | 65 | var three = args.find('-three', 2); 66 | Array.isArray(three).should.equal(true); 67 | three.length.should.equal(2); 68 | three[0].should.equal('three-param1'); 69 | three[1].should.equal('three-param2'); 70 | 71 | var nope = args.find('-nope', 2); 72 | (typeof nope).should.equal('undefined'); 73 | }); 74 | 75 | it('Should remove arguments from the list', function() { 76 | var args = utils.args(); 77 | 78 | args('-one'); 79 | args('-two', 'two-param'); 80 | args('-three', 'three-param1', 'three-param2'); 81 | args(['-four', 'four-param', '-five', '-five-param']); 82 | 83 | args.remove('-four', 1); 84 | var arr = args.get(); 85 | arr.length.should.equal(8); 86 | arr[5].should.equal('three-param2'); 87 | arr[6].should.equal('-five'); 88 | 89 | args.remove('-one'); 90 | arr = args.get(); 91 | arr.length.should.equal(7); 92 | arr[0].should.equal('-two'); 93 | 94 | args.remove('-three', 2); 95 | arr = args.get(); 96 | arr.length.should.equal(4); 97 | arr[1].should.equal('two-param'); 98 | arr[2].should.equal('-five'); 99 | }); 100 | }); 101 | 102 | describe('timemarkToSeconds', function() { 103 | it('should correctly convert a simple timestamp', function() { 104 | utils.timemarkToSeconds('00:02:00.00').should.be.equal(120); 105 | }); 106 | it('should correctly convert a complex timestamp', function() { 107 | utils.timemarkToSeconds('00:08:09.10').should.be.equal(489.1); 108 | }); 109 | it('should correclty convert a simple float string timestamp', function() { 110 | utils.timemarkToSeconds('132.44').should.be.equal(132.44); 111 | }); 112 | it('should correclty convert a simple float timestamp', function() { 113 | utils.timemarkToSeconds(132.44).should.be.equal(132.44); 114 | }); 115 | }); 116 | 117 | describe('Lines ring buffer', function() { 118 | it('should append lines', function() { 119 | var ring = utils.linesRing(100); 120 | ring.append('foo\nbar\nbaz\n'); 121 | ring.append('foo\nbar\nbaz\n'); 122 | ring.get().should.equal('foo\nbar\nbaz\nfoo\nbar\nbaz\n'); 123 | }); 124 | 125 | it('should append partial lines', function() { 126 | var ring = utils.linesRing(100); 127 | ring.append('foo'); 128 | ring.append('bar\nbaz'); 129 | ring.append('moo'); 130 | ring.get().should.equal('foobar\nbazmoo'); 131 | }); 132 | 133 | it('should call line callbacks', function() { 134 | var lines = []; 135 | function cb(l) { 136 | lines.push(l); 137 | } 138 | 139 | var lines2 = []; 140 | function cb2(l) { 141 | lines2.push(l); 142 | } 143 | 144 | var ring = utils.linesRing(100); 145 | ring.callback(cb); 146 | ring.callback(cb2); 147 | 148 | ring.append('foo\nbar\nbaz'); 149 | lines.length.should.equal(2); 150 | lines[0].should.equal('foo'); 151 | lines[1].should.equal('bar'); 152 | 153 | lines2.length.should.equal(2); 154 | lines2[0].should.equal('foo'); 155 | lines2[1].should.equal('bar'); 156 | 157 | ring.append('moo\nmeow\n'); 158 | lines.length.should.equal(4); 159 | lines[2].should.equal('bazmoo'); 160 | lines[3].should.equal('meow'); 161 | 162 | lines2.length.should.equal(4); 163 | lines2[2].should.equal('bazmoo'); 164 | lines2[3].should.equal('meow'); 165 | }); 166 | 167 | it('should close correctly', function() { 168 | var lines = []; 169 | function cb(l) { 170 | lines.push(l); 171 | } 172 | 173 | var ring = utils.linesRing(100); 174 | ring.callback(cb); 175 | 176 | ring.append('foo\nbar\nbaz'); 177 | lines.length.should.equal(2); 178 | lines[0].should.equal('foo'); 179 | lines[1].should.equal('bar'); 180 | 181 | ring.close(); 182 | lines.length.should.equal(3); 183 | lines[2].should.equal('baz'); 184 | 185 | ring.append('moo\nmeow\n'); 186 | lines.length.should.equal(3); 187 | ring.get().should.equal('foo\nbar\nbaz'); 188 | }); 189 | 190 | it('should limit lines', function() { 191 | var ring = utils.linesRing(2); 192 | ring.append('foo\nbar\nbaz'); 193 | ring.get().should.equal('bar\nbaz'); 194 | ring.append('foo\nbar'); 195 | ring.get().should.equal('bazfoo\nbar'); 196 | }); 197 | 198 | it('should allow unlimited lines', function() { 199 | var ring = utils.linesRing(0); 200 | ring.append('foo\nbar\nbaz'); 201 | ring.get().should.equal('foo\nbar\nbaz'); 202 | ring.append('foo\nbar'); 203 | ring.get().should.equal('foo\nbar\nbazfoo\nbar'); 204 | }); 205 | }); 206 | }); 207 | -------------------------------------------------------------------------------- /tools/jsdoc-aliases.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | function createAlias(doclet, alias) { 5 | var clone = {}; 6 | 7 | Object.keys(doclet).forEach(function(key) { 8 | clone[key] = doclet[key]; 9 | }); 10 | 11 | if (alias.indexOf('#') !== -1) { 12 | clone.longname = alias; 13 | clone.memberof = alias.split('#')[0]; 14 | clone.name = alias.split('#')[1]; 15 | } else { 16 | clone.longname = clone.memberof + '#' + alias; 17 | clone.name = alias; 18 | } 19 | 20 | delete clone.returns; 21 | delete clone.examples; 22 | delete clone.meta; 23 | delete clone.aliases; 24 | 25 | clone.isAlias = true; 26 | clone.description = 'Alias for <a href="#' + doclet.name + '">' + doclet.longname + '</a>'; 27 | 28 | return clone; 29 | } 30 | 31 | exports.handlers = { 32 | parseComplete: function(e) { 33 | var doclets = e.doclets.slice(); 34 | 35 | doclets.forEach(function(doclet) { 36 | // Duplicate doclets with aliases 37 | if (doclet.aliases) { 38 | doclet.aliases.forEach(function(alias) { 39 | e.doclets.push(createAlias(doclet, alias)); 40 | }); 41 | } 42 | }); 43 | } 44 | }; 45 | 46 | exports.defineTags = function(dict) { 47 | dict.defineTag('aliases', { 48 | onTagged: function(doclet, tag) { 49 | doclet.aliases = tag.text.split(','); 50 | } 51 | }); 52 | 53 | dict.defineTag('category', { 54 | onTagged: function(doclet, tag) { 55 | doclet.category = tag.text; 56 | } 57 | }); 58 | }; -------------------------------------------------------------------------------- /tools/jsdoc-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "opts": { 3 | "recurse": true, 4 | "verbose": true, 5 | "destination": "doc", 6 | "template": "tools/jsdoc-template" 7 | }, 8 | 9 | "source": { 10 | "include": ["README.md", "lib"], 11 | "excludePattern": "lib\/presets" 12 | }, 13 | 14 | "plugins": [ 15 | "plugins/markdown", 16 | "tools/jsdoc-aliases.js" 17 | ], 18 | 19 | "markdown": { 20 | "parser": "evilstreak", 21 | "dialect": "Markuru" 22 | } 23 | } -------------------------------------------------------------------------------- /tools/jsdoc-template/README.md: -------------------------------------------------------------------------------- 1 | The default template for JSDoc 3 uses: [the Taffy Database library](http://taffydb.com/) and the [Underscore Template library](http://documentcloud.github.com/underscore/#template). 2 | 3 | Updated for node-fluent-ffmpeg to handle @aliases and @category tags. -------------------------------------------------------------------------------- /tools/jsdoc-template/static/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (function() { 3 | var source = document.getElementsByClassName('prettyprint source linenums'); 4 | var i = 0; 5 | var lineNumber = 0; 6 | var lineId; 7 | var lines; 8 | var totalLines; 9 | var anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = 'line' + lineNumber; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /tools/jsdoc-template/static/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /tools/jsdoc-template/static/styles/jsdoc-default.css: -------------------------------------------------------------------------------- 1 | html 2 | { 3 | overflow: auto; 4 | background-color: #fff; 5 | } 6 | 7 | body 8 | { 9 | font: 14px "DejaVu Sans Condensed", "Liberation Sans", "Nimbus Sans L", Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif; 10 | line-height: 130%; 11 | color: #000; 12 | background-color: #fff; 13 | } 14 | 15 | a { 16 | color: #444; 17 | } 18 | 19 | a:visited { 20 | color: #444; 21 | } 22 | 23 | a:active { 24 | color: #444; 25 | } 26 | 27 | header 28 | { 29 | display: block; 30 | padding: 6px 4px; 31 | } 32 | 33 | .class-description { 34 | font-style: italic; 35 | font-family: Palatino, 'Palatino Linotype', serif; 36 | font-size: 130%; 37 | line-height: 140%; 38 | margin-bottom: 1em; 39 | margin-top: 1em; 40 | } 41 | 42 | #main { 43 | float: left; 44 | width: 100%; 45 | } 46 | 47 | section 48 | { 49 | display: block; 50 | 51 | background-color: #fff; 52 | padding: 12px 24px; 53 | border-bottom: 1px solid #ccc; 54 | margin-right: 240px; 55 | } 56 | 57 | .variation { 58 | display: none; 59 | } 60 | 61 | .optional:after { 62 | content: "opt"; 63 | font-size: 60%; 64 | color: #aaa; 65 | font-style: italic; 66 | font-weight: lighter; 67 | } 68 | 69 | nav 70 | { 71 | display: block; 72 | width: 220px; 73 | border-left: 1px solid #ccc; 74 | padding-left: 9px; 75 | 76 | position: fixed; 77 | top: 28px; 78 | right: 0; 79 | } 80 | 81 | nav ul { 82 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; 83 | font-size: 100%; 84 | line-height: 17px; 85 | padding:0; 86 | margin:0; 87 | list-style-type:none; 88 | } 89 | 90 | nav ul ul { 91 | margin-left: 10px; 92 | font-size: 90%; 93 | } 94 | 95 | nav h2 a, nav h2 a:visited { 96 | color: #526492; 97 | text-decoration: none; 98 | } 99 | 100 | nav h3 { 101 | margin-top: 12px; 102 | } 103 | 104 | nav li { 105 | margin-top: 2px; 106 | } 107 | 108 | nav a { 109 | color: #5C5954; 110 | } 111 | 112 | nav a:visited { 113 | color: #5C5954; 114 | } 115 | 116 | nav a:active { 117 | color: #5C5954; 118 | } 119 | 120 | footer { 121 | display: block; 122 | padding: 6px; 123 | margin-top: 12px; 124 | font-style: italic; 125 | font-size: 90%; 126 | } 127 | 128 | h1 129 | { 130 | font-size: 200%; 131 | font-weight: bold; 132 | letter-spacing: -0.01em; 133 | margin: 6px 0 9px 0; 134 | } 135 | 136 | h2 137 | { 138 | font-size: 170%; 139 | font-weight: bold; 140 | letter-spacing: -0.01em; 141 | margin: 50px 0 3px 0; 142 | } 143 | 144 | nav > h2 { 145 | margin-top: 6px; 146 | } 147 | 148 | h3 149 | { 150 | font-size: 150%; 151 | font-weight: bold; 152 | letter-spacing: -0.01em; 153 | margin-top: 16px; 154 | margin: 50px 0 3px 0; 155 | } 156 | 157 | h4 158 | { 159 | font-size: 130%; 160 | font-weight: bold; 161 | letter-spacing: -0.01em; 162 | margin-top: 16px; 163 | margin: 18px 0 3px 0; 164 | color: #526492; 165 | } 166 | 167 | h5, .container-overview .subsection-title 168 | { 169 | font-size: 120%; 170 | font-weight: bold; 171 | letter-spacing: -0.01em; 172 | margin: 8px 0 3px -16px; 173 | } 174 | 175 | h6 176 | { 177 | font-size: 100%; 178 | letter-spacing: -0.01em; 179 | margin: 6px 0 3px 0; 180 | font-style: italic; 181 | } 182 | 183 | article > dl, article > pre { 184 | margin-left: 2em; 185 | } 186 | 187 | .ancestors { color: #999; } 188 | .ancestors a 189 | { 190 | color: #999 !important; 191 | text-decoration: none; 192 | } 193 | 194 | .important 195 | { 196 | font-weight: bold; 197 | color: #950B02; 198 | } 199 | 200 | .yes-def { 201 | text-indent: -1000px; 202 | } 203 | 204 | .type-signature { 205 | color: #aaa; 206 | } 207 | 208 | .name, .signature { 209 | font-family: Consolas, "Lucida Console", Monaco, monospace; 210 | } 211 | 212 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 213 | .details dt { width:100px; float:left; padding-left: 10px; padding-top: 6px; } 214 | .details dd { margin-left: 50px; } 215 | .details ul { margin: 0; } 216 | .details ul { list-style-type: none; } 217 | .details li { margin-left: 30px; padding-top: 6px; } 218 | .details pre.prettyprint { margin: 0 } 219 | .details .object-value { padding-top: 0; } 220 | 221 | .description { 222 | margin-bottom: 1em; 223 | margin-left: -16px; 224 | margin-top: 1em; 225 | } 226 | 227 | .code-caption 228 | { 229 | font-style: italic; 230 | font-family: Palatino, 'Palatino Linotype', serif; 231 | font-size: 107%; 232 | margin: 0; 233 | } 234 | 235 | .prettyprint 236 | { 237 | border: 1px solid #ddd; 238 | width: 80%; 239 | overflow: auto; 240 | } 241 | 242 | .prettyprint.source { 243 | width: inherit; 244 | } 245 | 246 | .prettyprint code 247 | { 248 | font-family: Consolas, 'Lucida Console', Monaco, monospace; 249 | font-size: 100%; 250 | line-height: 18px; 251 | display: block; 252 | padding: 4px 12px; 253 | margin: 0; 254 | background-color: #fff; 255 | color: #000; 256 | } 257 | 258 | .prettyprint code span.line 259 | { 260 | display: inline-block; 261 | } 262 | 263 | .prettyprint.linenums 264 | { 265 | padding-left: 70px; 266 | -webkit-user-select: none; 267 | -moz-user-select: none; 268 | -ms-user-select: none; 269 | user-select: none; 270 | } 271 | 272 | .prettyprint.linenums ol 273 | { 274 | padding-left: 0; 275 | } 276 | 277 | .prettyprint.linenums li 278 | { 279 | border-left: 3px #ddd solid; 280 | } 281 | 282 | .prettyprint.linenums li.selected, 283 | .prettyprint.linenums li.selected * 284 | { 285 | background-color: lightyellow; 286 | } 287 | 288 | .prettyprint.linenums li * 289 | { 290 | -webkit-user-select: text; 291 | -moz-user-select: text; 292 | -ms-user-select: text; 293 | user-select: text; 294 | } 295 | 296 | .params, .props 297 | { 298 | border-spacing: 0; 299 | border: 0; 300 | border-collapse: collapse; 301 | } 302 | 303 | .params .name, .props .name, .name code { 304 | color: #526492; 305 | font-family: Consolas, 'Lucida Console', Monaco, monospace; 306 | font-size: 100%; 307 | } 308 | 309 | .params td, .params th, .props td, .props th 310 | { 311 | border: 1px solid #ddd; 312 | margin: 0px; 313 | text-align: left; 314 | vertical-align: top; 315 | padding: 4px 6px; 316 | display: table-cell; 317 | } 318 | 319 | .params thead tr, .props thead tr 320 | { 321 | background-color: #ddd; 322 | font-weight: bold; 323 | } 324 | 325 | .params .params thead tr, .props .props thead tr 326 | { 327 | background-color: #fff; 328 | font-weight: bold; 329 | } 330 | 331 | .params th, .props th { border-right: 1px solid #aaa; } 332 | .params thead .last, .props thead .last { border-right: 1px solid #ddd; } 333 | 334 | .params td.description > p:first-child 335 | { 336 | margin-top: 0; 337 | padding-top: 0; 338 | } 339 | 340 | .params td.description > p:last-child 341 | { 342 | margin-bottom: 0; 343 | padding-bottom: 0; 344 | } 345 | 346 | .disabled { 347 | color: #454545; 348 | } 349 | -------------------------------------------------------------------------------- /tools/jsdoc-template/static/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /tools/jsdoc-template/static/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Menlo, Monaco, Consolas, monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/aliases.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj || {}; 3 | ?> 4 | <?js if (data.aliases.length > 1) { ?> 5 | <ul> 6 | <?js data.aliases.forEach(function(alias) { ?> 7 | <li><?js= alias ?></li> 8 | <?js }); ?> 9 | </ul> 10 | <?js } else { ?> 11 | <?js= data.aliases[0] ?> 12 | <?js } ?> -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/container.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var self = this; 3 | docs.forEach(function(doc, i) { 4 | ?> 5 | 6 | <?js if (doc.kind === 'mainpage' || (doc.kind === 'package')) { ?> 7 | <?js= self.partial('mainpage.tmpl', doc) ?> 8 | <?js } else if (doc.kind === 'source') { ?> 9 | <?js= self.partial('source.tmpl', doc) ?> 10 | <?js } else { ?> 11 | 12 | <section> 13 | 14 | <header> 15 | <h2><?js if (doc.ancestors && doc.ancestors.length) { ?> 16 | <span class="ancestors"><?js= doc.ancestors.join('') ?></span> 17 | <?js } ?> 18 | <?js= doc.name ?> 19 | <?js if (doc.variation) { ?> 20 | <sup class="variation"><?js= doc.variation ?></sup> 21 | <?js } ?></h2> 22 | <?js if (doc.classdesc) { ?> 23 | <div class="class-description"><?js= doc.classdesc ?></div> 24 | <?js } ?> 25 | </header> 26 | 27 | <article> 28 | <div class="container-overview"> 29 | <?js if (doc.kind === 'module' && doc.module) { ?> 30 | <?js= self.partial('method.tmpl', doc.module) ?> 31 | <?js } ?> 32 | 33 | <?js if (doc.kind === 'class') { ?> 34 | <?js= self.partial('method.tmpl', doc) ?> 35 | <?js } else { ?> 36 | <?js if (doc.description) { ?> 37 | <div class="description"><?js= doc.description ?></div> 38 | <?js } ?> 39 | 40 | <?js= self.partial('details.tmpl', doc) ?> 41 | 42 | <?js if (doc.examples && doc.examples.length) { ?> 43 | <h3>Example<?js= doc.examples.length > 1? 's':'' ?></h3> 44 | <?js= self.partial('examples.tmpl', doc.examples) ?> 45 | <?js } ?> 46 | <?js } ?> 47 | </div> 48 | 49 | <?js if (doc.augments && doc.augments.length) { ?> 50 | <h3 class="subsection-title">Extends</h3> 51 | 52 | <ul><?js doc.augments.forEach(function(a) { ?> 53 | <li><?js= self.linkto(a, a) ?></li> 54 | <?js }); ?></ul> 55 | <?js } ?> 56 | 57 | <?js if (doc.mixes && doc.mixes.length) { ?> 58 | <h3 class="subsection-title">Mixes In</h3> 59 | 60 | <ul><?js doc.mixes.forEach(function(a) { ?> 61 | <li><?js= self.linkto(a, a) ?></li> 62 | <?js }); ?></ul> 63 | <?js } ?> 64 | 65 | <?js if (doc.requires && doc.requires.length) { ?> 66 | <h3 class="subsection-title">Requires</h3> 67 | 68 | <ul><?js doc.requires.forEach(function(r) { ?> 69 | <li><?js= self.linkto(r, r) ?></li> 70 | <?js }); ?></ul> 71 | <?js } ?> 72 | 73 | <?js 74 | var classes = self.find({kind: 'class', memberof: doc.longname}); 75 | if (doc.kind !== 'globalobj' && classes && classes.length) { 76 | ?> 77 | <h3 class="subsection-title">Classes</h3> 78 | 79 | <dl><?js classes.forEach(function(c) { ?> 80 | <dt><?js= self.linkto(c.longname, c.name) ?></dt> 81 | <dd><?js if (c.summary) { ?><?js= c.summary ?><?js } ?></dd> 82 | <?js }); ?></dl> 83 | <?js } ?> 84 | 85 | <?js 86 | var namespaces = self.find({kind: 'namespace', memberof: doc.longname}); 87 | if (doc.kind !== 'globalobj' && namespaces && namespaces.length) { 88 | ?> 89 | <h3 class="subsection-title">Namespaces</h3> 90 | 91 | <dl><?js namespaces.forEach(function(n) { ?> 92 | <dt><a href="namespaces.html#<?js= n.longname ?>"><?js= self.linkto(n.longname, n.name) ?></a></dt> 93 | <dd><?js if (n.summary) { ?><?js= n.summary ?><?js } ?></dd> 94 | <?js }); ?></dl> 95 | <?js } ?> 96 | 97 | <?js 98 | var members = self.find({kind: 'member', memberof: title === 'Global' ? {isUndefined: true} : doc.longname}); 99 | if (members && members.length && members.forEach) { 100 | ?> 101 | <h3 class="subsection-title">Members</h3> 102 | 103 | <dl><?js members.forEach(function(p) { ?> 104 | <?js= self.partial('members.tmpl', p) ?> 105 | <?js }); ?></dl> 106 | <?js } ?> 107 | 108 | <?js 109 | var methods = self.find({kind: 'function', memberof: title === 'Global' ? {isUndefined: true} : doc.longname}); 110 | if (methods && methods.length && methods.forEach) { 111 | // Categorize methods 112 | var categories = {}; 113 | methods.forEach(function(method) { 114 | var category = (method.category || 'Other') + ' methods'; 115 | if (!(category in categories)) { 116 | categories[category] = []; 117 | } 118 | 119 | categories[category].push(method); 120 | }); 121 | 122 | if (Object.keys(categories) === 1) { 123 | categories = { "Methods": categories['Other methods'] }; 124 | } 125 | 126 | Object.keys(categories).sort().forEach(function(category) { 127 | ?> 128 | <h3 class="subsection-title"><a name="<?js= category.toLowerCase().replace(/[^a-z0-9]/gi, '-') ?>"></a><?js= category ?></h3> 129 | 130 | <dl><?js categories[category].forEach(function(m) { ?> 131 | <?js= self.partial('method.tmpl', m) ?> 132 | <?js }); ?></dl> 133 | <?js 134 | }); 135 | } 136 | ?> 137 | 138 | <?js 139 | var typedefs = self.find({kind: 'typedef', memberof: title === 'Global' ? {isUndefined: true} : doc.longname}); 140 | if (typedefs && typedefs.length && typedefs.forEach) { 141 | ?> 142 | <h3 class="subsection-title">Type Definitions</h3> 143 | 144 | <dl><?js typedefs.forEach(function(e) { 145 | if (e.signature) { 146 | ?> 147 | <?js= self.partial('method.tmpl', e) ?> 148 | <?js 149 | } 150 | else { 151 | ?> 152 | <?js= self.partial('members.tmpl', e) ?> 153 | <?js 154 | } 155 | }); ?></dl> 156 | <?js } ?> 157 | 158 | <?js 159 | var events = self.find({kind: 'event', memberof: title === 'Global' ? {isUndefined: true} : doc.longname}); 160 | if (events && events.length && events.forEach) { 161 | ?> 162 | <h3 class="subsection-title">Events</h3> 163 | 164 | <dl><?js events.forEach(function(e) { ?> 165 | <?js= self.partial('method.tmpl', e) ?> 166 | <?js }); ?></dl> 167 | <?js } ?> 168 | </article> 169 | 170 | </section> 171 | <?js } ?> 172 | 173 | <?js }); ?> 174 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/details.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | var defaultObjectClass = ''; 5 | 6 | // Check if the default value is an object, if so, apply code highlighting 7 | if (data.defaultvalue && data.defaultvaluetype === 'object') { 8 | data.defaultvalue = "<pre class=\"prettyprint\"><code>" + data.defaultvalue + "</code></pre>"; 9 | defaultObjectClass = ' class="object-value"'; 10 | } 11 | ?> 12 | <dl class="details"> 13 | <?js 14 | var properties = data.properties; 15 | if (properties && properties.length && properties.forEach) { 16 | ?> 17 | 18 | <h5 class="subsection-title">Properties:</h5> 19 | 20 | <dl><?js= this.partial('properties.tmpl', properties) ?></dl> 21 | 22 | <?js } ?> 23 | 24 | <?js if (data.version) {?> 25 | <dt class="tag-version">Version:</dt> 26 | <dd class="tag-version"><ul class="dummy"><li><?js= version ?></li></ul></dd> 27 | <?js } ?> 28 | 29 | <?js if (data.since) {?> 30 | <dt class="tag-since">Since:</dt> 31 | <dd class="tag-since"><ul class="dummy"><li><?js= since ?></dd> 32 | <?js } ?> 33 | 34 | <?js if (data.inherited && data.inherits) { ?> 35 | <dt class="inherited-from">Inherited From:</dt> 36 | <dd class="inherited-from"><ul class="dummy"><li> 37 | <?js= this.linkto(data.inherits, this.htmlsafe(data.inherits)) ?> 38 | </li></dd> 39 | <?js } ?> 40 | 41 | <?js if (data.deprecated) { ?> 42 | <dt class="important tag-deprecated">Deprecated:</dt><?js 43 | if (data.deprecated === true) { ?><dd class="yes-def tag-deprecated"><ul class="dummy"><li>Yes</li></ul></dd><?js } 44 | else { ?><dd><ul class="dummy"><li><?js= data.deprecated ?></li><ul></dd><?js } 45 | ?> 46 | <?js } ?> 47 | 48 | <?js if (data.author && author.length) {?> 49 | <dt class="tag-author">Author:</dt> 50 | <dd class="tag-author"> 51 | <ul><?js author.forEach(function(a) { ?> 52 | <li><?js= self.resolveAuthorLinks(a) ?></li> 53 | <?js }); ?></ul> 54 | </dd> 55 | <?js } ?> 56 | 57 | <?js if (data.copyright) {?> 58 | <dt class="tag-copyright">Copyright:</dt> 59 | <dd class="tag-copyright"><ul class="dummy"><li><?js= copyright ?></li></ul></dd> 60 | <?js } ?> 61 | 62 | <?js if (data.license) {?> 63 | <dt class="tag-license">License:</dt> 64 | <dd class="tag-license"><ul class="dummy"><li><?js= license ?></li></ul></dd> 65 | <?js } ?> 66 | 67 | <?js if (data.defaultvalue) {?> 68 | <dt class="tag-default">Default Value:</dt> 69 | <dd class="tag-default"><ul class="dummy"> 70 | <li<?js= defaultObjectClass ?>><?js= data.defaultvalue ?></li> 71 | </ul></dd> 72 | <?js } ?> 73 | 74 | <?js if (data.meta && self.outputSourceFiles) {?> 75 | <dt class="tag-source">Source:</dt> 76 | <dd class="tag-source"><ul class="dummy"><li> 77 | <?js= self.linkto(meta.shortpath) ?>, <?js= self.linkto(meta.shortpath, 'line ' + meta.lineno, null, 'line' + meta.lineno) ?> 78 | </li></ul></dd> 79 | <?js } ?> 80 | 81 | <?js if (data.tutorials && tutorials.length) {?> 82 | <dt class="tag-tutorial">Tutorials:</dt> 83 | <dd class="tag-tutorial"> 84 | <ul><?js tutorials.forEach(function(t) { ?> 85 | <li><?js= self.tutoriallink(t) ?></li> 86 | <?js }); ?></ul> 87 | </dd> 88 | <?js } ?> 89 | 90 | <?js if (data.see && see.length) {?> 91 | <dt class="tag-see">See:</dt> 92 | <dd class="tag-see"> 93 | <ul><?js see.forEach(function(s) { ?> 94 | <li><?js= self.linkto(s) ?></li> 95 | <?js }); ?></ul> 96 | </dd> 97 | <?js } ?> 98 | 99 | <?js if (data.todo && todo.length) {?> 100 | <dt class="tag-todo">To Do:</dt> 101 | <dd class="tag-todo"> 102 | <ul><?js todo.forEach(function(t) { ?> 103 | <li><?js= t ?></li> 104 | <?js }); ?></ul> 105 | </dd> 106 | <?js } ?> 107 | </dl> 108 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/example.tmpl: -------------------------------------------------------------------------------- 1 | <?js var data = obj; ?> 2 | <pre><code><?js= data ?></code></pre> 3 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/examples.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | 5 | data.forEach(function(example) { 6 | if (example.caption) { 7 | ?> 8 | <p class="code-caption"><?js= example.caption ?></p> 9 | <?js } ?> 10 | <pre class="prettyprint"><code><?js= self.htmlsafe(example.code) ?></code></pre> 11 | <?js 12 | }); 13 | ?> -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/exceptions.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | ?> 4 | <?js if (data.description && data.type && data.type.names) { ?> 5 | <dl> 6 | <dt> 7 | <div class="param-desc"> 8 | <?js= data.description ?> 9 | </div> 10 | </dt> 11 | <dt> 12 | <dl> 13 | <dt> 14 | Type 15 | </dt> 16 | <dd> 17 | <?js= this.partial('type.tmpl', data.type.names) ?> 18 | </dd> 19 | </dl> 20 | </dt> 21 | </dl> 22 | <?js } else { ?> 23 | <div class="param-desc"> 24 | <?js if (data.description) { ?> 25 | <?js= data.description ?> 26 | <?js } else if (data.type && data.type.names) { ?> 27 | <?js= this.partial('type.tmpl', data.type.names) ?> 28 | <?js } ?> 29 | </div> 30 | <?js } ?> 31 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/layout.tmpl: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: <?js= title ?></title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title"><?js= title ?></h1> 21 | 22 | <?js= content ?> 23 | </div> 24 | 25 | <nav> 26 | <?js= this.nav ?> 27 | </nav> 28 | 29 | <br clear="both"> 30 | 31 | <footer> 32 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc <?js= env.version.number ?></a> on <?js= (new Date()) ?> 33 | </footer> 34 | 35 | <script> prettyPrint(); </script> 36 | <script src="scripts/linenumber.js"> </script> 37 | </body> 38 | </html> 39 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/mainpage.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | ?> 5 | 6 | <?js if (data.kind === 'package') { ?> 7 | <h3><?js= data.name ?> <?js= data.version ?></h3> 8 | <?js } ?> 9 | 10 | <?js if (data.readme) { ?> 11 | <section> 12 | <article><?js= data.readme ?></article> 13 | </section> 14 | <?js } ?> 15 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/members.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | ?> 5 | <dt> 6 | <h4 class="name" id="<?js= id ?>"><?js= data.attribs + name + (data.signature ? data.signature : '') ?></h4> 7 | 8 | <?js if (data.summary) { ?> 9 | <p class="summary"><?js= summary ?></p> 10 | <?js } ?> 11 | </dt> 12 | <dd> 13 | <?js if (data.description) { ?> 14 | <div class="description"> 15 | <?js= data.description ?> 16 | </div> 17 | <?js } ?> 18 | 19 | <?js if (data.type && data.type.names) {?> 20 | <h5>Type:</h5> 21 | <ul> 22 | <li> 23 | <?js= self.partial('type.tmpl', data.type.names) ?> 24 | </li> 25 | </ul> 26 | <?js } ?> 27 | 28 | <?js= this.partial('details.tmpl', data) ?> 29 | 30 | <?js if (data.fires && fires.length) { ?> 31 | <h5>Fires:</h5> 32 | <ul><?js fires.forEach(function(f) { ?> 33 | <li><?js= self.linkto(f) ?></li> 34 | <?js }); ?></ul> 35 | <?js } ?> 36 | 37 | <?js if (data.examples && examples.length) { ?> 38 | <h5>Example<?js= examples.length > 1? 's':'' ?></h5> 39 | <?js= this.partial('examples.tmpl', examples) ?> 40 | <?js } ?> 41 | </dd> 42 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/method.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | ?> 5 | <dt> 6 | <h4 class="name" id="<?js= id ?>"><?js= data.attribs + (kind === 'class' ? 'new ' : '') + name + (kind !== 'event' ? data.signature : '') ?></h4> 7 | 8 | <?js if (data.summary) { ?> 9 | <p class="summary"><?js= summary ?></p> 10 | <?js } ?> 11 | </dt> 12 | <dd> 13 | 14 | <?js if (data.description) { ?> 15 | <div class="description"> 16 | <?js= data.description ?> 17 | </div> 18 | <?js } ?> 19 | 20 | <?js if (kind === 'event' && data.type && data.type.names) {?> 21 | <h5>Type:</h5> 22 | <ul> 23 | <li> 24 | <?js= self.partial('type.tmpl', data.type.names) ?> 25 | </li> 26 | </ul> 27 | <?js } ?> 28 | 29 | <?js if (data['this']) { ?> 30 | <h5>This:</h5> 31 | <ul><li><?js= this.linkto(data['this'], data['this']) ?></li></ul> 32 | <?js } ?> 33 | 34 | <?js if (!data.isAlias && data.params && params.length) { ?> 35 | <h5>Parameters:</h5> 36 | <?js= this.partial('params.tmpl', params) ?> 37 | <?js } ?> 38 | 39 | <?js= this.partial('details.tmpl', data) ?> 40 | 41 | <?js if (data.requires && data.requires.length) { ?> 42 | <h5>Requires:</h5> 43 | <ul><?js data.requires.forEach(function(r) { ?> 44 | <li><?js= self.linkto(r) ?></li> 45 | <?js }); ?></ul> 46 | <?js } ?> 47 | 48 | <?js if (data.fires && fires.length) { ?> 49 | <h5>Fires:</h5> 50 | <ul><?js fires.forEach(function(f) { ?> 51 | <li><?js= self.linkto(f) ?></li> 52 | <?js }); ?></ul> 53 | <?js } ?> 54 | 55 | <?js if (data.listens && listens.length) { ?> 56 | <h5>Listens to Events:</h5> 57 | <ul><?js listens.forEach(function(f) { ?> 58 | <li><?js= self.linkto(f) ?></li> 59 | <?js }); ?></ul> 60 | <?js } ?> 61 | 62 | <?js if (data.listeners && listeners.length) { ?> 63 | <h5>Listeners of This Event:</h5> 64 | <ul><?js listeners.forEach(function(f) { ?> 65 | <li><?js= self.linkto(f) ?></li> 66 | <?js }); ?></ul> 67 | <?js } ?> 68 | 69 | <?js if (data.exceptions && exceptions.length) { ?> 70 | <h5>Throws:</h5> 71 | <?js if (exceptions.length > 1) { ?><ul><?js 72 | exceptions.forEach(function(r) { ?> 73 | <li><?js= self.partial('exceptions.tmpl', r) ?></li> 74 | <?js }); 75 | ?></ul><?js } else { 76 | exceptions.forEach(function(r) { ?> 77 | <?js= self.partial('exceptions.tmpl', r) ?> 78 | <?js }); 79 | } } ?> 80 | 81 | <?js if (data.returns && returns.length) { ?> 82 | <h5>Returns:</h5> 83 | <?js if (returns.length > 1) { ?><ul><?js 84 | returns.forEach(function(r) { ?> 85 | <li><?js= self.partial('returns.tmpl', r) ?></li> 86 | <?js }); 87 | ?></ul><?js } else { 88 | returns.forEach(function(r) { ?> 89 | <?js= self.partial('returns.tmpl', r) ?> 90 | <?js }); 91 | } } ?> 92 | 93 | <?js if (data.examples && examples.length) { ?> 94 | <h5>Example<?js= examples.length > 1? 's':'' ?>:</h5> 95 | <?js= this.partial('examples.tmpl', examples) ?> 96 | <?js } ?> 97 | 98 | <?js if (data.aliases && aliases.length) { ?> 99 | <h5>Alias<?js= aliases.length > 1 ? 'es' : '' ?>:</h5> 100 | <?js= this.partial('aliases.tmpl', { memberof: data.memberof, aliases: aliases }) ?> 101 | <?js } ?> 102 | </dd> 103 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/params.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var params = obj; 3 | 4 | /* sort subparams under their parent params (like opts.classname) */ 5 | var parentParam = null; 6 | params.forEach(function(param, i) { 7 | if (!param) { return; } 8 | if ( parentParam && param.name && param.name.indexOf(parentParam.name + '.') === 0 ) { 9 | param.name = param.name.substr(parentParam.name.length+1); 10 | parentParam.subparams = parentParam.subparams || []; 11 | parentParam.subparams.push(param); 12 | params[i] = null; 13 | } 14 | else { 15 | parentParam = param; 16 | } 17 | }); 18 | 19 | /* determine if we need extra columns, "attributes" and "default" */ 20 | params.hasAttributes = false; 21 | params.hasDefault = false; 22 | params.hasName = false; 23 | 24 | params.forEach(function(param) { 25 | if (!param) { return; } 26 | 27 | if (param.optional || param.nullable || param.variable) { 28 | params.hasAttributes = true; 29 | } 30 | 31 | if (param.name) { 32 | params.hasName = true; 33 | } 34 | 35 | if (typeof param.defaultvalue !== 'undefined') { 36 | params.hasDefault = true; 37 | } 38 | }); 39 | ?> 40 | 41 | <table class="params"> 42 | <thead> 43 | <tr> 44 | <?js if (params.hasName) {?> 45 | <th>Name</th> 46 | <?js } ?> 47 | 48 | <th>Type</th> 49 | 50 | <?js if (params.hasAttributes) {?> 51 | <th>Argument</th> 52 | <?js } ?> 53 | 54 | <?js if (params.hasDefault) {?> 55 | <th>Default</th> 56 | <?js } ?> 57 | 58 | <th class="last">Description</th> 59 | </tr> 60 | </thead> 61 | 62 | <tbody> 63 | <?js 64 | var self = this; 65 | params.forEach(function(param) { 66 | if (!param) { return; } 67 | ?> 68 | 69 | <tr> 70 | <?js if (params.hasName) {?> 71 | <td class="name"><code><?js= param.name ?></code></td> 72 | <?js } ?> 73 | 74 | <td class="type"> 75 | <?js if (param.type && param.type.names) {?> 76 | <?js= self.partial('type.tmpl', param.type.names) ?> 77 | <?js } ?> 78 | </td> 79 | 80 | <?js if (params.hasAttributes) {?> 81 | <td class="attributes"> 82 | <?js if (param.optional) { ?> 83 | <optional><br> 84 | <?js } ?> 85 | 86 | <?js if (param.nullable) { ?> 87 | <nullable><br> 88 | <?js } ?> 89 | 90 | <?js if (param.variable) { ?> 91 | <repeatable><br> 92 | <?js } ?> 93 | </td> 94 | <?js } ?> 95 | 96 | <?js if (params.hasDefault) {?> 97 | <td class="default"> 98 | <?js if (typeof param.defaultvalue !== 'undefined') { ?> 99 | <?js= self.htmlsafe(param.defaultvalue) ?> 100 | <?js } ?> 101 | </td> 102 | <?js } ?> 103 | 104 | <td class="description last"><?js= param.description ?><?js if (param.subparams) { ?> 105 | <h6>Properties</h6> 106 | <?js= self.partial('params.tmpl', param.subparams) ?> 107 | <?js } ?></td> 108 | </tr> 109 | 110 | <?js }); ?> 111 | </tbody> 112 | </table> -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/properties.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var props = obj; 3 | 4 | /* sort subprops under their parent props (like opts.classname) */ 5 | var parentProp = null; 6 | props.forEach(function(prop, i) { 7 | if (!prop) { return; } 8 | if ( parentProp && prop.name && prop.name.indexOf(parentProp.name + '.') === 0 ) { 9 | prop.name = prop.name.substr(parentProp.name.length+1); 10 | parentProp.subprops = parentProp.subprops || []; 11 | parentProp.subprops.push(prop); 12 | props[i] = null; 13 | } 14 | else { 15 | parentProp = prop; 16 | } 17 | }); 18 | 19 | /* determine if we need extra columns, "attributes" and "default" */ 20 | props.hasAttributes = false; 21 | props.hasDefault = false; 22 | props.hasName = false; 23 | 24 | props.forEach(function(prop) { 25 | if (!prop) { return; } 26 | 27 | if (prop.optional || prop.nullable) { 28 | props.hasAttributes = true; 29 | } 30 | 31 | if (prop.name) { 32 | props.hasName = true; 33 | } 34 | 35 | if (typeof prop.defaultvalue !== 'undefined') { 36 | props.hasDefault = true; 37 | } 38 | }); 39 | ?> 40 | 41 | <table class="props"> 42 | <thead> 43 | <tr> 44 | <?js if (props.hasName) {?> 45 | <th>Name</th> 46 | <?js } ?> 47 | 48 | <th>Type</th> 49 | 50 | <?js if (props.hasAttributes) {?> 51 | <th>Argument</th> 52 | <?js } ?> 53 | 54 | <?js if (props.hasDefault) {?> 55 | <th>Default</th> 56 | <?js } ?> 57 | 58 | <th class="last">Description</th> 59 | </tr> 60 | </thead> 61 | 62 | <tbody> 63 | <?js 64 | var self = this; 65 | props.forEach(function(prop) { 66 | if (!prop) { return; } 67 | ?> 68 | 69 | <tr> 70 | <?js if (props.hasName) {?> 71 | <td class="name"><code><?js= prop.name ?></code></td> 72 | <?js } ?> 73 | 74 | <td class="type"> 75 | <?js if (prop.type && prop.type.names) {?> 76 | <?js= self.partial('type.tmpl', prop.type.names) ?> 77 | <?js } ?> 78 | </td> 79 | 80 | <?js if (props.hasAttributes) {?> 81 | <td class="attributes"> 82 | <?js if (prop.optional) { ?> 83 | <optional><br> 84 | <?js } ?> 85 | 86 | <?js if (prop.nullable) { ?> 87 | <nullable><br> 88 | <?js } ?> 89 | </td> 90 | <?js } ?> 91 | 92 | <?js if (props.hasDefault) {?> 93 | <td class="default"> 94 | <?js if (typeof prop.defaultvalue !== 'undefined') { ?> 95 | <?js= self.htmlsafe(prop.defaultvalue) ?> 96 | <?js } ?> 97 | </td> 98 | <?js } ?> 99 | 100 | <td class="description last"><?js= prop.description ?><?js if (prop.subprops) { ?> 101 | <h6>Properties</h6><?js= self.partial('properties.tmpl', prop.subprops) ?> 102 | <?js } ?></td> 103 | </tr> 104 | 105 | <?js }); ?> 106 | </tbody> 107 | </table> -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/returns.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj || {}; 3 | if (data.description) { 4 | ?> 5 | <div class="param-desc"> 6 | <?js= description ?> 7 | </div> 8 | <?js } ?> 9 | 10 | <?js if (data.type && data.type.names) {?> 11 | <dl> 12 | <dt> 13 | Type 14 | </dt> 15 | <dd> 16 | <?js= this.partial('type.tmpl', data.type.names) ?> 17 | </dd> 18 | </dl> 19 | <?js } ?> -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/source.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | ?> 4 | <section> 5 | <article> 6 | <pre class="prettyprint source linenums"><code><?js= data.code ?></code></pre> 7 | </article> 8 | </section> -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/tutorial.tmpl: -------------------------------------------------------------------------------- 1 | <section> 2 | 3 | <header> 4 | <?js if (children.length > 0) { ?> 5 | <ul><?js 6 | var self = this; 7 | children.forEach(function(t) { ?> 8 | <li><?js= self.tutoriallink(t.name) ?></li> 9 | <?js }); ?></ul> 10 | <?js } ?> 11 | 12 | <h2><?js= header ?></h2> 13 | </header> 14 | 15 | <article> 16 | <?js= content ?> 17 | </article> 18 | 19 | </section> 20 | -------------------------------------------------------------------------------- /tools/jsdoc-template/tmpl/type.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | data.forEach(function(name, i) { ?> 5 | <span class="param-type"><?js= self.linkto(name, self.htmlsafe(name)) ?></span> 6 | <?js if (i < data.length-1) { ?>|<?js } ?> 7 | <?js }); ?> --------------------------------------------------------------------------------