312 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			HTML
		
	
	
	
<!DOCTYPE html>
 | 
						|
<html lang="en">
 | 
						|
<head>
 | 
						|
    <meta charset="utf-8">
 | 
						|
    <title>JSDoc: Source: ffprobe.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: ffprobe.js</h1>
 | 
						|
 | 
						|
    
 | 
						|
 | 
						|
 | 
						|
    
 | 
						|
    <section>
 | 
						|
        <article>
 | 
						|
            <pre class="prettyprint source linenums"><code>/*jshint node:true, laxcomma:true*/
 | 
						|
'use strict';
 | 
						|
 | 
						|
var spawn = require('child_process').spawn;
 | 
						|
 | 
						|
 | 
						|
function legacyTag(key) { return key.match(/^TAG:/); }
 | 
						|
function legacyDisposition(key) { return key.match(/^DISPOSITION:/); }
 | 
						|
 | 
						|
function parseFfprobeOutput(out) {
 | 
						|
  var lines = out.split(/\r\n|\r|\n/);
 | 
						|
 | 
						|
  lines = lines.filter(function (line) {
 | 
						|
    return line.length > 0;
 | 
						|
  });
 | 
						|
 | 
						|
  var data = {
 | 
						|
    streams: [],
 | 
						|
    format: {},
 | 
						|
    chapters: []
 | 
						|
  };
 | 
						|
 | 
						|
  function parseBlock(name) {
 | 
						|
    var data = {};
 | 
						|
 | 
						|
    var line = lines.shift();
 | 
						|
    while (typeof line !== 'undefined') {
 | 
						|
      if (line.toLowerCase() == '[/'+name+']') {
 | 
						|
        return data;
 | 
						|
      } else if (line.match(/^\[/)) {
 | 
						|
        line = lines.shift();
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      var kv = line.match(/^([^=]+)=(.*)$/);
 | 
						|
      if (kv) {
 | 
						|
        if (!(kv[1].match(/^TAG:/)) && kv[2].match(/^[0-9]+(\.[0-9]+)?$/)) {
 | 
						|
          data[kv[1]] = Number(kv[2]);
 | 
						|
        } else {
 | 
						|
          data[kv[1]] = kv[2];
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      line = lines.shift();
 | 
						|
    }
 | 
						|
 | 
						|
    return data;
 | 
						|
  }
 | 
						|
 | 
						|
  var line = lines.shift();
 | 
						|
  while (typeof line !== 'undefined') {
 | 
						|
    if (line.match(/^\[stream/i)) {
 | 
						|
      var stream = parseBlock('stream');
 | 
						|
      data.streams.push(stream);
 | 
						|
    } else if (line.match(/^\[chapter/i)) {
 | 
						|
      var chapter = parseBlock('chapter');
 | 
						|
      data.chapters.push(chapter);
 | 
						|
    } else if (line.toLowerCase() === '[format]') {
 | 
						|
      data.format = parseBlock('format');
 | 
						|
    }
 | 
						|
 | 
						|
    line = lines.shift();
 | 
						|
  }
 | 
						|
 | 
						|
  return data;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
module.exports = function(proto) {
 | 
						|
  /**
 | 
						|
   * A callback passed to the {@link FfmpegCommand#ffprobe} method.
 | 
						|
   *
 | 
						|
   * @callback FfmpegCommand~ffprobeCallback
 | 
						|
   *
 | 
						|
   * @param {Error|null} err error object or null if no error happened
 | 
						|
   * @param {Object} ffprobeData ffprobe output data; this object
 | 
						|
   *   has the same format as what the following command returns:
 | 
						|
   *
 | 
						|
   *     `ffprobe -print_format json -show_streams -show_format INPUTFILE`
 | 
						|
   * @param {Array} ffprobeData.streams stream information
 | 
						|
   * @param {Object} ffprobeData.format format information
 | 
						|
   */
 | 
						|
 | 
						|
  /**
 | 
						|
   * Run ffprobe on last specified input
 | 
						|
   *
 | 
						|
   * @method FfmpegCommand#ffprobe
 | 
						|
   * @category Metadata
 | 
						|
   *
 | 
						|
   * @param {?Number} [index] 0-based index of input to probe (defaults to last input)
 | 
						|
   * @param {?String[]} [options] array of output options to return
 | 
						|
   * @param {FfmpegCommand~ffprobeCallback} callback callback function
 | 
						|
   *
 | 
						|
   */
 | 
						|
  proto.ffprobe = function() {
 | 
						|
    var input, index = null, options = [], callback;
 | 
						|
 | 
						|
    // the last argument should be the callback
 | 
						|
    var callback = arguments[arguments.length - 1];
 | 
						|
 | 
						|
    var ended = false
 | 
						|
    function handleCallback(err, data) {
 | 
						|
      if (!ended) {
 | 
						|
        ended = true;
 | 
						|
        callback(err, data);
 | 
						|
      }
 | 
						|
    };
 | 
						|
 | 
						|
    // map the arguments to the correct variable names
 | 
						|
    switch (arguments.length) {
 | 
						|
      case 3:
 | 
						|
        index = arguments[0];
 | 
						|
        options = arguments[1];
 | 
						|
        break;
 | 
						|
      case 2:
 | 
						|
        if (typeof arguments[0] === 'number') {
 | 
						|
          index = arguments[0];
 | 
						|
        } else if (Array.isArray(arguments[0])) {
 | 
						|
          options = arguments[0];
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    if (index === null) {
 | 
						|
      if (!this._currentInput) {
 | 
						|
        return handleCallback(new Error('No input specified'));
 | 
						|
      }
 | 
						|
 | 
						|
      input = this._currentInput;
 | 
						|
    } else {
 | 
						|
      input = this._inputs[index];
 | 
						|
 | 
						|
      if (!input) {
 | 
						|
        return handleCallback(new Error('Invalid input index'));
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Find ffprobe
 | 
						|
    this._getFfprobePath(function(err, path) {
 | 
						|
      if (err) {
 | 
						|
        return handleCallback(err);
 | 
						|
      } else if (!path) {
 | 
						|
        return handleCallback(new Error('Cannot find ffprobe'));
 | 
						|
      }
 | 
						|
 | 
						|
      var stdout = '';
 | 
						|
      var stdoutClosed = false;
 | 
						|
      var stderr = '';
 | 
						|
      var stderrClosed = false;
 | 
						|
 | 
						|
      // Spawn ffprobe
 | 
						|
      var src = input.isStream ? 'pipe:0' : input.source;
 | 
						|
      var ffprobe = spawn(path, ['-show_streams', '-show_format'].concat(options, src));
 | 
						|
 | 
						|
      if (input.isStream) {
 | 
						|
        // Skip errors on stdin. These get thrown when ffprobe is complete and
 | 
						|
        // there seems to be no way hook in and close stdin before it throws.
 | 
						|
        ffprobe.stdin.on('error', function(err) {
 | 
						|
          if (['ECONNRESET', 'EPIPE'].indexOf(err.code) >= 0) { return; }
 | 
						|
          handleCallback(err);
 | 
						|
        });
 | 
						|
 | 
						|
        // Once ffprobe's input stream closes, we need no more data from the
 | 
						|
        // input
 | 
						|
        ffprobe.stdin.on('close', function() {
 | 
						|
            input.source.pause();
 | 
						|
            input.source.unpipe(ffprobe.stdin);
 | 
						|
        });
 | 
						|
 | 
						|
        input.source.pipe(ffprobe.stdin);
 | 
						|
      }
 | 
						|
 | 
						|
      ffprobe.on('error', callback);
 | 
						|
 | 
						|
      // Ensure we wait for captured streams to end before calling callback
 | 
						|
      var exitError = null;
 | 
						|
      function handleExit(err) {
 | 
						|
        if (err) {
 | 
						|
          exitError = err;
 | 
						|
        }
 | 
						|
 | 
						|
        if (processExited && stdoutClosed && stderrClosed) {
 | 
						|
          if (exitError) {
 | 
						|
            if (stderr) {
 | 
						|
              exitError.message += '\n' + stderr;
 | 
						|
            }
 | 
						|
 | 
						|
            return handleCallback(exitError);
 | 
						|
          }
 | 
						|
 | 
						|
          // Process output
 | 
						|
          var data = parseFfprobeOutput(stdout);
 | 
						|
 | 
						|
          // Handle legacy output with "TAG:x" and "DISPOSITION:x" keys
 | 
						|
          [data.format].concat(data.streams).forEach(function(target) {
 | 
						|
            if (target) {
 | 
						|
              var legacyTagKeys = Object.keys(target).filter(legacyTag);
 | 
						|
 | 
						|
              if (legacyTagKeys.length) {
 | 
						|
                target.tags = target.tags || {};
 | 
						|
 | 
						|
                legacyTagKeys.forEach(function(tagKey) {
 | 
						|
                  target.tags[tagKey.substr(4)] = target[tagKey];
 | 
						|
                  delete target[tagKey];
 | 
						|
                });
 | 
						|
              }
 | 
						|
 | 
						|
              var legacyDispositionKeys = Object.keys(target).filter(legacyDisposition);
 | 
						|
 | 
						|
              if (legacyDispositionKeys.length) {
 | 
						|
                target.disposition = target.disposition || {};
 | 
						|
 | 
						|
                legacyDispositionKeys.forEach(function(dispositionKey) {
 | 
						|
                  target.disposition[dispositionKey.substr(12)] = target[dispositionKey];
 | 
						|
                  delete target[dispositionKey];
 | 
						|
                });
 | 
						|
              }
 | 
						|
            }
 | 
						|
          });
 | 
						|
 | 
						|
          handleCallback(null, data);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      // Handle ffprobe exit
 | 
						|
      var processExited = false;
 | 
						|
      ffprobe.on('exit', function(code, signal) {
 | 
						|
        processExited = true;
 | 
						|
 | 
						|
        if (code) {
 | 
						|
          handleExit(new Error('ffprobe exited with code ' + code));
 | 
						|
        } else if (signal) {
 | 
						|
          handleExit(new Error('ffprobe was killed with signal ' + signal));
 | 
						|
        } else {
 | 
						|
          handleExit();
 | 
						|
        }
 | 
						|
      });
 | 
						|
 | 
						|
      // Handle stdout/stderr streams
 | 
						|
      ffprobe.stdout.on('data', function(data) {
 | 
						|
        stdout += data;
 | 
						|
      });
 | 
						|
 | 
						|
      ffprobe.stdout.on('close', function() {
 | 
						|
        stdoutClosed = true;
 | 
						|
        handleExit();
 | 
						|
      });
 | 
						|
 | 
						|
      ffprobe.stderr.on('data', function(data) {
 | 
						|
        stderr += data;
 | 
						|
      });
 | 
						|
 | 
						|
      ffprobe.stderr.on('close', function() {
 | 
						|
        stderrClosed = true;
 | 
						|
        handleExit();
 | 
						|
      });
 | 
						|
    });
 | 
						|
  };
 | 
						|
};
 | 
						|
</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>
 |