/** * mux.js * * Copyright (c) Brightcove * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE */ 'use strict'; var Stream = require('../utils/stream.js'); var ONE_SECOND_IN_TS = require('../utils/clock').ONE_SECOND_IN_TS; var _AdtsStream; var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; /* * Accepts a ElementaryStream and emits data events with parsed * AAC Audio Frames of the individual packets. Input audio in ADTS * format is unpacked and re-emitted as AAC frames. * * @see http://wiki.multimedia.cx/index.php?title=ADTS * @see http://wiki.multimedia.cx/?title=Understanding_AAC */ _AdtsStream = function AdtsStream(handlePartialSegments) { var buffer, frameNum = 0; _AdtsStream.prototype.init.call(this); this.skipWarn_ = function (start, end) { this.trigger('log', { level: 'warn', message: "adts skiping bytes " + start + " to " + end + " in frame " + frameNum + " outside syncword" }); }; this.push = function (packet) { var i = 0, frameLength, protectionSkipBytes, frameEnd, oldBuffer, sampleCount, adtsFrameDuration; if (!handlePartialSegments) { frameNum = 0; } if (packet.type !== 'audio') { // ignore non-audio data return; } // Prepend any data in the buffer to the input data so that we can parse // aac frames the cross a PES packet boundary if (buffer && buffer.length) { oldBuffer = buffer; buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength); buffer.set(oldBuffer); buffer.set(packet.data, oldBuffer.byteLength); } else { buffer = packet.data; } // unpack any ADTS frames which have been fully received // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS var skip; // We use i + 7 here because we want to be able to parse the entire header. // If we don't have enough bytes to do that, then we definitely won't have a full frame. while (i + 7 < buffer.length) { // Look for the start of an ADTS header.. if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) { if (typeof skip !== 'number') { skip = i; } // If a valid header was not found, jump one forward and attempt to // find a valid ADTS header starting at the next byte i++; continue; } if (typeof skip === 'number') { this.skipWarn_(skip, i); skip = null; } // The protection skip bit tells us if we have 2 bytes of CRC data at the // end of the ADTS header protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2; // Frame length is a 13 bit integer starting 16 bits from the // end of the sync sequence // NOTE: frame length includes the size of the header frameLength = (buffer[i + 3] & 0x03) << 11 | buffer[i + 4] << 3 | (buffer[i + 5] & 0xe0) >> 5; sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024; adtsFrameDuration = sampleCount * ONE_SECOND_IN_TS / ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2]; // If we don't have enough data to actually finish this ADTS frame, // then we have to wait for more data if (buffer.byteLength - i < frameLength) { break; } // Otherwise, deliver the complete AAC frame this.trigger('data', { pts: packet.pts + frameNum * adtsFrameDuration, dts: packet.dts + frameNum * adtsFrameDuration, sampleCount: sampleCount, audioobjecttype: (buffer[i + 2] >>> 6 & 0x03) + 1, channelcount: (buffer[i + 2] & 1) << 2 | (buffer[i + 3] & 0xc0) >>> 6, samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2], samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2, // assume ISO/IEC 14496-12 AudioSampleEntry default of 16 samplesize: 16, // data is the frame without it's header data: buffer.subarray(i + 7 + protectionSkipBytes, i + frameLength) }); frameNum++; i += frameLength; } if (typeof skip === 'number') { this.skipWarn_(skip, i); skip = null; } // remove processed bytes from the buffer. buffer = buffer.subarray(i); }; this.flush = function () { frameNum = 0; this.trigger('done'); }; this.reset = function () { buffer = void 0; this.trigger('reset'); }; this.endTimeline = function () { buffer = void 0; this.trigger('endedtimeline'); }; }; _AdtsStream.prototype = new Stream(); module.exports = _AdtsStream;