709 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			709 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			HTML
		
	
	
	
<!DOCTYPE html>
 | 
						|
<html lang="en">
 | 
						|
<head>
 | 
						|
    <meta charset="utf-8">
 | 
						|
    <title>JSDoc: Source: processor.js</title>
 | 
						|
 | 
						|
    <script src="scripts/prettify/prettify.js"> </script>
 | 
						|
    <script src="scripts/prettify/lang-css.js"> </script>
 | 
						|
    <!--[if lt IE 9]>
 | 
						|
      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
 | 
						|
    <![endif]-->
 | 
						|
    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
 | 
						|
    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
 | 
						|
</head>
 | 
						|
 | 
						|
<body>
 | 
						|
 | 
						|
<div id="main">
 | 
						|
 | 
						|
    <h1 class="page-title">Source: processor.js</h1>
 | 
						|
 | 
						|
    
 | 
						|
 | 
						|
 | 
						|
    
 | 
						|
    <section>
 | 
						|
        <article>
 | 
						|
            <pre class="prettyprint source linenums"><code>/*jshint node:true*/
 | 
						|
'use strict';
 | 
						|
 | 
						|
var spawn = require('child_process').spawn;
 | 
						|
var path = require('path');
 | 
						|
var fs = require('fs');
 | 
						|
var async = require('async');
 | 
						|
var utils = require('./utils');
 | 
						|
 | 
						|
var nlRegexp = /\r\n|\r|\n/g;
 | 
						|
 | 
						|
/*
 | 
						|
 *! Processor methods
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Run ffprobe asynchronously and store data in command
 | 
						|
 *
 | 
						|
 * @param {FfmpegCommand} command
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function runFfprobe(command) {
 | 
						|
  command.ffprobe(0, function(err, data) {
 | 
						|
    command._ffprobeData = data;
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
module.exports = function(proto) {
 | 
						|
  /**
 | 
						|
   * Emitted just after ffmpeg has been spawned.
 | 
						|
   *
 | 
						|
   * @event FfmpegCommand#start
 | 
						|
   * @param {String} command ffmpeg command line
 | 
						|
   */
 | 
						|
 | 
						|
  /**
 | 
						|
   * Emitted when ffmpeg reports progress information
 | 
						|
   *
 | 
						|
   * @event FfmpegCommand#progress
 | 
						|
   * @param {Object} progress progress object
 | 
						|
   * @param {Number} progress.frames number of frames transcoded
 | 
						|
   * @param {Number} progress.currentFps current processing speed in frames per second
 | 
						|
   * @param {Number} progress.currentKbps current output generation speed in kilobytes per second
 | 
						|
   * @param {Number} progress.targetSize current output file size
 | 
						|
   * @param {String} progress.timemark current video timemark
 | 
						|
   * @param {Number} [progress.percent] processing progress (may not be available depending on input)
 | 
						|
   */
 | 
						|
 | 
						|
  /**
 | 
						|
   * Emitted when ffmpeg outputs to stderr
 | 
						|
   *
 | 
						|
   * @event FfmpegCommand#stderr
 | 
						|
   * @param {String} line stderr output line
 | 
						|
   */
 | 
						|
 | 
						|
  /**
 | 
						|
   * Emitted when ffmpeg reports input codec data
 | 
						|
   *
 | 
						|
   * @event FfmpegCommand#codecData
 | 
						|
   * @param {Object} codecData codec data object
 | 
						|
   * @param {String} codecData.format input format name
 | 
						|
   * @param {String} codecData.audio input audio codec name
 | 
						|
   * @param {String} codecData.audio_details input audio codec parameters
 | 
						|
   * @param {String} codecData.video input video codec name
 | 
						|
   * @param {String} codecData.video_details input video codec parameters
 | 
						|
   */
 | 
						|
 | 
						|
  /**
 | 
						|
   * Emitted when an error happens when preparing or running a command
 | 
						|
   *
 | 
						|
   * @event FfmpegCommand#error
 | 
						|
   * @param {Error} error error object
 | 
						|
   * @param {String|null} stdout ffmpeg stdout, unless outputting to a stream
 | 
						|
   * @param {String|null} stderr ffmpeg stderr
 | 
						|
   */
 | 
						|
 | 
						|
  /**
 | 
						|
   * Emitted when a command finishes processing
 | 
						|
   *
 | 
						|
   * @event FfmpegCommand#end
 | 
						|
   * @param {Array|String|null} [filenames|stdout] generated filenames when taking screenshots, ffmpeg stdout when not outputting to a stream, null otherwise
 | 
						|
   * @param {String|null} stderr ffmpeg stderr
 | 
						|
   */
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Spawn an ffmpeg process
 | 
						|
   *
 | 
						|
   * The 'options' argument may contain the following keys:
 | 
						|
   * - 'niceness': specify process niceness, ignored on Windows (default: 0)
 | 
						|
   * - `cwd`: change working directory
 | 
						|
   * - 'captureStdout': capture stdout and pass it to 'endCB' as its 2nd argument (default: false)
 | 
						|
   * - 'stdoutLines': override command limit (default: use command limit)
 | 
						|
   *
 | 
						|
   * The 'processCB' callback, if present, is called as soon as the process is created and
 | 
						|
   * receives a nodejs ChildProcess object.  It may not be called at all if an error happens
 | 
						|
   * before spawning the process.
 | 
						|
   *
 | 
						|
   * The 'endCB' callback is called either when an error occurs or when the ffmpeg process finishes.
 | 
						|
   *
 | 
						|
   * @method FfmpegCommand#_spawnFfmpeg
 | 
						|
   * @param {Array} args ffmpeg command line argument list
 | 
						|
   * @param {Object} [options] spawn options (see above)
 | 
						|
   * @param {Function} [processCB] callback called with process object and stdout/stderr ring buffers when process has been created
 | 
						|
   * @param {Function} endCB callback called with error (if applicable) and stdout/stderr ring buffers when process finished
 | 
						|
   * @private
 | 
						|
   */
 | 
						|
  proto._spawnFfmpeg = function(args, options, processCB, endCB) {
 | 
						|
    // Enable omitting options
 | 
						|
    if (typeof options === 'function') {
 | 
						|
      endCB = processCB;
 | 
						|
      processCB = options;
 | 
						|
      options = {};
 | 
						|
    }
 | 
						|
 | 
						|
    // Enable omitting processCB
 | 
						|
    if (typeof endCB === 'undefined') {
 | 
						|
      endCB = processCB;
 | 
						|
      processCB = function() {};
 | 
						|
    }
 | 
						|
 | 
						|
    var maxLines = 'stdoutLines' in options ? options.stdoutLines : this.options.stdoutLines;
 | 
						|
 | 
						|
    // Find ffmpeg
 | 
						|
    this._getFfmpegPath(function(err, command) {
 | 
						|
      if (err) {
 | 
						|
        return endCB(err);
 | 
						|
      } else if (!command || command.length === 0) {
 | 
						|
        return endCB(new Error('Cannot find ffmpeg'));
 | 
						|
      }
 | 
						|
 | 
						|
      // Apply niceness
 | 
						|
      if (options.niceness && options.niceness !== 0 && !utils.isWindows) {
 | 
						|
        args.unshift('-n', options.niceness, command);
 | 
						|
        command = 'nice';
 | 
						|
      }
 | 
						|
 | 
						|
      var stdoutRing = utils.linesRing(maxLines);
 | 
						|
      var stdoutClosed = false;
 | 
						|
 | 
						|
      var stderrRing = utils.linesRing(maxLines);
 | 
						|
      var stderrClosed = false;
 | 
						|
 | 
						|
      // Spawn process
 | 
						|
      var ffmpegProc = spawn(command, args, options);
 | 
						|
 | 
						|
      if (ffmpegProc.stderr) {
 | 
						|
        ffmpegProc.stderr.setEncoding('utf8');
 | 
						|
      }
 | 
						|
 | 
						|
      ffmpegProc.on('error', function(err) {
 | 
						|
        endCB(err);
 | 
						|
      });
 | 
						|
 | 
						|
      // Ensure we wait for captured streams to end before calling endCB
 | 
						|
      var exitError = null;
 | 
						|
      function handleExit(err) {
 | 
						|
        if (err) {
 | 
						|
          exitError = err;
 | 
						|
        }
 | 
						|
 | 
						|
        if (processExited && (stdoutClosed || !options.captureStdout) && stderrClosed) {
 | 
						|
          endCB(exitError, stdoutRing, stderrRing);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      // Handle process exit
 | 
						|
      var processExited = false;
 | 
						|
      ffmpegProc.on('exit', function(code, signal) {
 | 
						|
        processExited = true;
 | 
						|
 | 
						|
        if (signal) {
 | 
						|
          handleExit(new Error('ffmpeg was killed with signal ' + signal));
 | 
						|
        } else if (code) {
 | 
						|
          handleExit(new Error('ffmpeg exited with code ' + code));
 | 
						|
        } else {
 | 
						|
          handleExit();
 | 
						|
        }
 | 
						|
      });
 | 
						|
 | 
						|
      // Capture stdout if specified
 | 
						|
      if (options.captureStdout) {
 | 
						|
        ffmpegProc.stdout.on('data', function(data) {
 | 
						|
          stdoutRing.append(data);
 | 
						|
        });
 | 
						|
 | 
						|
        ffmpegProc.stdout.on('close', function() {
 | 
						|
          stdoutRing.close();
 | 
						|
          stdoutClosed = true;
 | 
						|
          handleExit();
 | 
						|
        });
 | 
						|
      }
 | 
						|
 | 
						|
      // Capture stderr if specified
 | 
						|
      ffmpegProc.stderr.on('data', function(data) {
 | 
						|
        stderrRing.append(data);
 | 
						|
      });
 | 
						|
 | 
						|
      ffmpegProc.stderr.on('close', function() {
 | 
						|
        stderrRing.close();
 | 
						|
        stderrClosed = true;
 | 
						|
        handleExit();
 | 
						|
      });
 | 
						|
 | 
						|
      // Call process callback
 | 
						|
      processCB(ffmpegProc, stdoutRing, stderrRing);
 | 
						|
    });
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Build the argument list for an ffmpeg command
 | 
						|
   *
 | 
						|
   * @method FfmpegCommand#_getArguments
 | 
						|
   * @return argument list
 | 
						|
   * @private
 | 
						|
   */
 | 
						|
  proto._getArguments = function() {
 | 
						|
    var complexFilters = this._complexFilters.get();
 | 
						|
 | 
						|
    var fileOutput = this._outputs.some(function(output) {
 | 
						|
      return output.isFile;
 | 
						|
    });
 | 
						|
 | 
						|
    return [].concat(
 | 
						|
        // Inputs and input options
 | 
						|
        this._inputs.reduce(function(args, input) {
 | 
						|
          var source = (typeof input.source === 'string') ? input.source : 'pipe:0';
 | 
						|
 | 
						|
          // For each input, add input options, then '-i <source>'
 | 
						|
          return args.concat(
 | 
						|
            input.options.get(),
 | 
						|
            ['-i', source]
 | 
						|
          );
 | 
						|
        }, []),
 | 
						|
 | 
						|
        // Global options
 | 
						|
        this._global.get(),
 | 
						|
 | 
						|
        // Overwrite if we have file outputs
 | 
						|
        fileOutput ? ['-y'] : [],
 | 
						|
 | 
						|
        // Complex filters
 | 
						|
        complexFilters,
 | 
						|
 | 
						|
        // Outputs, filters and output options
 | 
						|
        this._outputs.reduce(function(args, output) {
 | 
						|
          var sizeFilters = utils.makeFilterStrings(output.sizeFilters.get());
 | 
						|
          var audioFilters = output.audioFilters.get();
 | 
						|
          var videoFilters = output.videoFilters.get().concat(sizeFilters);
 | 
						|
          var outputArg;
 | 
						|
 | 
						|
          if (!output.target) {
 | 
						|
            outputArg = [];
 | 
						|
          } else if (typeof output.target === 'string') {
 | 
						|
            outputArg = [output.target];
 | 
						|
          } else {
 | 
						|
            outputArg = ['pipe:1'];
 | 
						|
          }
 | 
						|
 | 
						|
          return args.concat(
 | 
						|
            output.audio.get(),
 | 
						|
            audioFilters.length ? ['-filter:a', audioFilters.join(',')] : [],
 | 
						|
            output.video.get(),
 | 
						|
            videoFilters.length ? ['-filter:v', videoFilters.join(',')] : [],
 | 
						|
            output.options.get(),
 | 
						|
            outputArg
 | 
						|
          );
 | 
						|
        }, [])
 | 
						|
      );
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Prepare execution of an ffmpeg command
 | 
						|
   *
 | 
						|
   * Checks prerequisites for the execution of the command (codec/format availability, flvtool...),
 | 
						|
   * then builds the argument list for ffmpeg and pass them to 'callback'.
 | 
						|
   *
 | 
						|
   * @method FfmpegCommand#_prepare
 | 
						|
   * @param {Function} callback callback with signature (err, args)
 | 
						|
   * @param {Boolean} [readMetadata=false] read metadata before processing
 | 
						|
   * @private
 | 
						|
   */
 | 
						|
  proto._prepare = function(callback, readMetadata) {
 | 
						|
    var self = this;
 | 
						|
 | 
						|
    async.waterfall([
 | 
						|
      // Check codecs and formats
 | 
						|
      function(cb) {
 | 
						|
        self._checkCapabilities(cb);
 | 
						|
      },
 | 
						|
 | 
						|
      // Read metadata if required
 | 
						|
      function(cb) {
 | 
						|
        if (!readMetadata) {
 | 
						|
          return cb();
 | 
						|
        }
 | 
						|
 | 
						|
        self.ffprobe(0, function(err, data) {
 | 
						|
          if (!err) {
 | 
						|
            self._ffprobeData = data;
 | 
						|
          }
 | 
						|
 | 
						|
          cb();
 | 
						|
        });
 | 
						|
      },
 | 
						|
 | 
						|
      // Check for flvtool2/flvmeta if necessary
 | 
						|
      function(cb) {
 | 
						|
        var flvmeta = self._outputs.some(function(output) {
 | 
						|
          // Remove flvmeta flag on non-file output
 | 
						|
          if (output.flags.flvmeta && !output.isFile) {
 | 
						|
            self.logger.warn('Updating flv metadata is only supported for files');
 | 
						|
            output.flags.flvmeta = false;
 | 
						|
          }
 | 
						|
 | 
						|
          return output.flags.flvmeta;
 | 
						|
        });
 | 
						|
 | 
						|
        if (flvmeta) {
 | 
						|
          self._getFlvtoolPath(function(err) {
 | 
						|
            cb(err);
 | 
						|
          });
 | 
						|
        } else {
 | 
						|
          cb();
 | 
						|
        }
 | 
						|
      },
 | 
						|
 | 
						|
      // Build argument list
 | 
						|
      function(cb) {
 | 
						|
        var args;
 | 
						|
        try {
 | 
						|
          args = self._getArguments();
 | 
						|
        } catch(e) {
 | 
						|
          return cb(e);
 | 
						|
        }
 | 
						|
 | 
						|
        cb(null, args);
 | 
						|
      },
 | 
						|
 | 
						|
      // Add "-strict experimental" option where needed
 | 
						|
      function(args, cb) {
 | 
						|
        self.availableEncoders(function(err, encoders) {
 | 
						|
          for (var i = 0; i < args.length; i++) {
 | 
						|
            if (args[i] === '-acodec' || args[i] === '-vcodec') {
 | 
						|
              i++;
 | 
						|
 | 
						|
              if ((args[i] in encoders) && encoders[args[i]].experimental) {
 | 
						|
                args.splice(i + 1, 0, '-strict', 'experimental');
 | 
						|
                i += 2;
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          cb(null, args);
 | 
						|
        });
 | 
						|
      }
 | 
						|
    ], callback);
 | 
						|
 | 
						|
    if (!readMetadata) {
 | 
						|
      // Read metadata as soon as 'progress' listeners are added
 | 
						|
 | 
						|
      if (this.listeners('progress').length > 0) {
 | 
						|
        // Read metadata in parallel
 | 
						|
        runFfprobe(this);
 | 
						|
      } else {
 | 
						|
        // Read metadata as soon as the first 'progress' listener is added
 | 
						|
        this.once('newListener', function(event) {
 | 
						|
          if (event === 'progress') {
 | 
						|
            runFfprobe(this);
 | 
						|
          }
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Run ffmpeg command
 | 
						|
   *
 | 
						|
   * @method FfmpegCommand#run
 | 
						|
   * @category Processing
 | 
						|
   * @aliases exec,execute
 | 
						|
   */
 | 
						|
  proto.exec =
 | 
						|
  proto.execute =
 | 
						|
  proto.run = function() {
 | 
						|
    var self = this;
 | 
						|
 | 
						|
    // Check if at least one output is present
 | 
						|
    var outputPresent = this._outputs.some(function(output) {
 | 
						|
      return 'target' in output;
 | 
						|
    });
 | 
						|
 | 
						|
    if (!outputPresent) {
 | 
						|
      throw new Error('No output specified');
 | 
						|
    }
 | 
						|
 | 
						|
    // Get output stream if any
 | 
						|
    var outputStream = this._outputs.filter(function(output) {
 | 
						|
      return typeof output.target !== 'string';
 | 
						|
    })[0];
 | 
						|
 | 
						|
    // Get input stream if any
 | 
						|
    var inputStream = this._inputs.filter(function(input) {
 | 
						|
      return typeof input.source !== 'string';
 | 
						|
    })[0];
 | 
						|
 | 
						|
    // Ensure we send 'end' or 'error' only once
 | 
						|
    var ended = false;
 | 
						|
    function emitEnd(err, stdout, stderr) {
 | 
						|
      if (!ended) {
 | 
						|
        ended = true;
 | 
						|
 | 
						|
        if (err) {
 | 
						|
          self.emit('error', err, stdout, stderr);
 | 
						|
        } else {
 | 
						|
          self.emit('end', stdout, stderr);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    self._prepare(function(err, args) {
 | 
						|
      if (err) {
 | 
						|
        return emitEnd(err);
 | 
						|
      }
 | 
						|
 | 
						|
      // Run ffmpeg
 | 
						|
      self._spawnFfmpeg(
 | 
						|
        args,
 | 
						|
        {
 | 
						|
          captureStdout: !outputStream,
 | 
						|
          niceness: self.options.niceness,
 | 
						|
          cwd: self.options.cwd
 | 
						|
        },
 | 
						|
 | 
						|
        function processCB(ffmpegProc, stdoutRing, stderrRing) {
 | 
						|
          self.ffmpegProc = ffmpegProc;
 | 
						|
          self.emit('start', 'ffmpeg ' + args.join(' '));
 | 
						|
 | 
						|
          // Pipe input stream if any
 | 
						|
          if (inputStream) {
 | 
						|
            inputStream.source.on('error', function(err) {
 | 
						|
              emitEnd(new Error('Input stream error: ' + err.message));
 | 
						|
              ffmpegProc.kill();
 | 
						|
            });
 | 
						|
 | 
						|
            inputStream.source.resume();
 | 
						|
            inputStream.source.pipe(ffmpegProc.stdin);
 | 
						|
 | 
						|
            // Set stdin error handler on ffmpeg (prevents nodejs catching the error, but
 | 
						|
            // ffmpeg will fail anyway, so no need to actually handle anything)
 | 
						|
            ffmpegProc.stdin.on('error', function() {});
 | 
						|
          }
 | 
						|
 | 
						|
          // Setup timeout if requested
 | 
						|
          var processTimer;
 | 
						|
          if (self.options.timeout) {
 | 
						|
            processTimer = setTimeout(function() {
 | 
						|
              var msg = 'process ran into a timeout (' + self.options.timeout + 's)';
 | 
						|
 | 
						|
              emitEnd(new Error(msg), stdoutRing.get(), stderrRing.get());
 | 
						|
              ffmpegProc.kill();
 | 
						|
            }, self.options.timeout * 1000);
 | 
						|
          }
 | 
						|
 | 
						|
 | 
						|
          if (outputStream) {
 | 
						|
            // Pipe ffmpeg stdout to output stream
 | 
						|
            ffmpegProc.stdout.pipe(outputStream.target, outputStream.pipeopts);
 | 
						|
 | 
						|
            // Handle output stream events
 | 
						|
            outputStream.target.on('close', function() {
 | 
						|
              self.logger.debug('Output stream closed, scheduling kill for ffmpgeg process');
 | 
						|
 | 
						|
              // Don't kill process yet, to give a chance to ffmpeg to
 | 
						|
              // terminate successfully first  This is necessary because
 | 
						|
              // under load, the process 'exit' event sometimes happens
 | 
						|
              // after the output stream 'close' event.
 | 
						|
              setTimeout(function() {
 | 
						|
                emitEnd(new Error('Output stream closed'));
 | 
						|
                ffmpegProc.kill();
 | 
						|
              }, 20);
 | 
						|
            });
 | 
						|
 | 
						|
            outputStream.target.on('error', function(err) {
 | 
						|
              self.logger.debug('Output stream error, killing ffmpgeg process');
 | 
						|
              emitEnd(new Error('Output stream error: ' + err.message), stdoutRing.get(), stderrRing.get());
 | 
						|
              ffmpegProc.kill();
 | 
						|
            });
 | 
						|
          }
 | 
						|
 | 
						|
          // Setup stderr handling
 | 
						|
          if (stderrRing) {
 | 
						|
 | 
						|
            // 'stderr' event
 | 
						|
            if (self.listeners('stderr').length) {
 | 
						|
              stderrRing.callback(function(line) {
 | 
						|
                self.emit('stderr', line);
 | 
						|
              });
 | 
						|
            }
 | 
						|
 | 
						|
            // 'codecData' event
 | 
						|
            if (self.listeners('codecData').length) {
 | 
						|
              var codecDataSent = false;
 | 
						|
              var codecObject = {};
 | 
						|
 | 
						|
              stderrRing.callback(function(line) {
 | 
						|
                if (!codecDataSent)
 | 
						|
                  codecDataSent = utils.extractCodecData(self, line, codecObject);
 | 
						|
              });
 | 
						|
            }
 | 
						|
 | 
						|
            // 'progress' event
 | 
						|
            if (self.listeners('progress').length) {
 | 
						|
              var duration = 0;
 | 
						|
 | 
						|
              if (self._ffprobeData && self._ffprobeData.format && self._ffprobeData.format.duration) {
 | 
						|
                duration = Number(self._ffprobeData.format.duration);
 | 
						|
              }
 | 
						|
 | 
						|
              stderrRing.callback(function(line) {
 | 
						|
                utils.extractProgress(self, line, duration);
 | 
						|
              });
 | 
						|
            }
 | 
						|
          }
 | 
						|
        },
 | 
						|
 | 
						|
        function endCB(err, stdoutRing, stderrRing) {
 | 
						|
          delete self.ffmpegProc;
 | 
						|
 | 
						|
          if (err) {
 | 
						|
            if (err.message.match(/ffmpeg exited with code/)) {
 | 
						|
              // Add ffmpeg error message
 | 
						|
              err.message += ': ' + utils.extractError(stderrRing.get());
 | 
						|
            }
 | 
						|
 | 
						|
            emitEnd(err, stdoutRing.get(), stderrRing.get());
 | 
						|
          } else {
 | 
						|
            // Find out which outputs need flv metadata
 | 
						|
            var flvmeta = self._outputs.filter(function(output) {
 | 
						|
              return output.flags.flvmeta;
 | 
						|
            });
 | 
						|
 | 
						|
            if (flvmeta.length) {
 | 
						|
              self._getFlvtoolPath(function(err, flvtool) {
 | 
						|
                if (err) {
 | 
						|
                  return emitEnd(err);
 | 
						|
                }
 | 
						|
 | 
						|
                async.each(
 | 
						|
                  flvmeta,
 | 
						|
                  function(output, cb) {
 | 
						|
                    spawn(flvtool, ['-U', output.target])
 | 
						|
                      .on('error', function(err) {
 | 
						|
                        cb(new Error('Error running ' + flvtool + ' on ' + output.target + ': ' + err.message));
 | 
						|
                      })
 | 
						|
                      .on('exit', function(code, signal) {
 | 
						|
                        if (code !== 0 || signal) {
 | 
						|
                          cb(
 | 
						|
                            new Error(flvtool + ' ' +
 | 
						|
                              (signal ? 'received signal ' + signal
 | 
						|
                                      : 'exited with code ' + code)) +
 | 
						|
                              ' when running on ' + output.target
 | 
						|
                          );
 | 
						|
                        } else {
 | 
						|
                          cb();
 | 
						|
                        }
 | 
						|
                      });
 | 
						|
                  },
 | 
						|
                  function(err) {
 | 
						|
                    if (err) {
 | 
						|
                      emitEnd(err);
 | 
						|
                    } else {
 | 
						|
                      emitEnd(null, stdoutRing.get(), stderrRing.get());
 | 
						|
                    }
 | 
						|
                  }
 | 
						|
                );
 | 
						|
              });
 | 
						|
            } else {
 | 
						|
              emitEnd(null, stdoutRing.get(), stderrRing.get());
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      );
 | 
						|
    });
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Renice current and/or future ffmpeg processes
 | 
						|
   *
 | 
						|
   * Ignored on Windows platforms.
 | 
						|
   *
 | 
						|
   * @method FfmpegCommand#renice
 | 
						|
   * @category Processing
 | 
						|
   *
 | 
						|
   * @param {Number} [niceness=0] niceness value between -20 (highest priority) and 20 (lowest priority)
 | 
						|
   * @return FfmpegCommand
 | 
						|
   */
 | 
						|
  proto.renice = function(niceness) {
 | 
						|
    if (!utils.isWindows) {
 | 
						|
      niceness = niceness || 0;
 | 
						|
 | 
						|
      if (niceness < -20 || niceness > 20) {
 | 
						|
        this.logger.warn('Invalid niceness value: ' + niceness + ', must be between -20 and 20');
 | 
						|
      }
 | 
						|
 | 
						|
      niceness = Math.min(20, Math.max(-20, niceness));
 | 
						|
      this.options.niceness = niceness;
 | 
						|
 | 
						|
      if (this.ffmpegProc) {
 | 
						|
        var logger = this.logger;
 | 
						|
        var pid = this.ffmpegProc.pid;
 | 
						|
        var renice = spawn('renice', [niceness, '-p', pid]);
 | 
						|
 | 
						|
        renice.on('error', function(err) {
 | 
						|
          logger.warn('could not renice process ' + pid + ': ' + err.message);
 | 
						|
        });
 | 
						|
 | 
						|
        renice.on('exit', function(code, signal) {
 | 
						|
          if (signal) {
 | 
						|
            logger.warn('could not renice process ' + pid + ': renice was killed by signal ' + signal);
 | 
						|
          } else if (code) {
 | 
						|
            logger.warn('could not renice process ' + pid + ': renice exited with ' + code);
 | 
						|
          } else {
 | 
						|
            logger.info('successfully reniced process ' + pid + ' to ' + niceness + ' niceness');
 | 
						|
          }
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return this;
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Kill current ffmpeg process, if any
 | 
						|
   *
 | 
						|
   * @method FfmpegCommand#kill
 | 
						|
   * @category Processing
 | 
						|
   *
 | 
						|
   * @param {String} [signal=SIGKILL] signal name
 | 
						|
   * @return FfmpegCommand
 | 
						|
   */
 | 
						|
  proto.kill = function(signal) {
 | 
						|
    if (!this.ffmpegProc) {
 | 
						|
      this.logger.warn('No running ffmpeg process, cannot send signal');
 | 
						|
    } else {
 | 
						|
      this.ffmpegProc.kill(signal || 'SIGKILL');
 | 
						|
    }
 | 
						|
 | 
						|
    return this;
 | 
						|
  };
 | 
						|
};
 | 
						|
</code></pre>
 | 
						|
        </article>
 | 
						|
    </section>
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
</div>
 | 
						|
 | 
						|
<nav>
 | 
						|
    <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>
 | 
						|
</nav>
 | 
						|
 | 
						|
<br clear="both">
 | 
						|
 | 
						|
<footer>
 | 
						|
    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)
 | 
						|
</footer>
 | 
						|
 | 
						|
<script> prettyPrint(); </script>
 | 
						|
<script src="scripts/linenumber.js"> </script>
 | 
						|
</body>
 | 
						|
</html>
 |