175 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * 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(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;
 | 
