Files
Zos/Skills/@be/be/node_modules/icecast/lib/writer.js

148 lines
3.4 KiB
JavaScript

/**
* Module dependencies.
*/
var assert = require('assert');
var Parser = require('stream-parser');
var stringify = require('./stringify');
var inherits = require('util').inherits;
var Transform = require('stream').Transform;
var debug = require('debug')('icecast:writer');
// for node v0.8.x support, remove after v0.12.x
if (!Transform) Transform = require('readable-stream/transform');
/**
* Module exports.
*/
module.exports = Writer;
/**
* The value of the metabyte on each pass is
* "ceil(metadata.length / META_BLOCK_SIZE)".
*
* @api private
*/
var META_BLOCK_SIZE = 16;
/**
* The maximum byte length of a metadata chunk.
*
* @api private
*/
var MAX_LENGTH = META_BLOCK_SIZE * 255;
/**
* Usually, the "metabyte" will just be 1 NUL byte, because there is no metadata
* to be sent. Create that 1-byte buffer up-front.
*
* @api private
*/
var NO_METADATA = new Buffer([0]);
/**
* The `Writer` class is a duplex stream that accepts raw audio/video data and
* passes it through untouched. It also has a `queue()` function that will
* queue the Writer to inject the metadata into the stream at the next "metaint"
* interval.
*
* @param {Number} metaint the number of raw bytes that should be placed in between "metadata" blocks.
* @param {Object} opts optional options object
* @api public
*/
function Writer (metaint, opts) {
if (!(this instanceof Writer)) {
return new Writer(metaint, opts);
}
if (!isFinite(metaint)) {
throw new Error('Writer requires a "metaint" number');
}
Transform.call(this, opts);
this.metaint = +metaint;
this._queue = [];
this._passthrough(this.metaint, this._inject);
}
inherits(Writer, Transform);
/**
* Mixin `Parser`.
*/
Parser(Writer.prototype);
/**
* Queues a piece of metadata to be sent along with the stream.
* `metadata` may be a String and be any title (up to 4066 chars),
* or may be an Object containing at least a "StreamTitle" key, with a String
* value. The serialized metadata payload must be <= 4080 bytes.
*
* @param {String|Object} metadata
* @api public
*/
Writer.prototype.queue = function (metadata) {
if ('string' == typeof metadata) {
// string title
metadata = { StreamTitle: metadata };
} else if (metadata && Object(metadata) === metadata) {
// an object
} else {
throw new TypeError('don\'t know how to format metadata: ' + metadata);
}
if (!('StreamTitle' in metadata)) {
throw new TypeError('a "StreamTitle" property is required for metadata');
}
var str = stringify(metadata);
var len = Buffer.byteLength(str);
if (len > MAX_LENGTH) {
throw new Error('metadata must be <= 4080, got: ' + len);
}
var meta = Math.ceil(len / META_BLOCK_SIZE);
var buf = new Buffer(meta * META_BLOCK_SIZE + 1);
buf[0] = meta;
var written = buf.write(str, 1);
for (var i = written + 1; i < buf.length; i++) {
// pad with NULL bytes
buf[i] = 0;
}
this._queue.push(buf);
};
/**
* Backwards-compat.
*
* @api private
*/
Writer.prototype.queueMetadata = Writer.prototype.queue;
/**
* Writes the next "metabyte" to the output stream.
*
* @param {Function} write output callback function
* @api private
*/
Writer.prototype._inject = function (write) {
var buffer;
if (0 === this._queue.length) {
buffer = NO_METADATA;
} else {
buffer = this._queue.shift();
}
write(buffer);
// passthrough "metaint" bytes before injecting the next metabyte
this._passthrough(this.metaint, this._inject);
};