Skip to content

Commit

Permalink
Base class aac and mp3 demuxers and modularize mpeg audio demux funct…
Browse files Browse the repository at this point in the history
…ions
  • Loading branch information
Rob Walch committed Sep 24, 2019
1 parent 0e3c862 commit 8d45a12
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 335 deletions.
121 changes: 17 additions & 104 deletions src/demux/aacdemuxer.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,41 @@
/**
* AAC demuxer
*/
import BaseAudioDemuxer from './base-audio-demuxer';
import * as ADTS from './adts';
import { logger } from '../utils/logger';
import ID3 from '../demux/id3';
import { DemuxerResult, Demuxer, DemuxedTrack } from '../types/demuxer';
import { dummyTrack } from './dummy-demuxed-track';
import { appendUint8Array } from '../utils/mp4-tools';

class AACDemuxer implements Demuxer {
class AACDemuxer extends BaseAudioDemuxer {
private observer: any;
private config: any;
private _audioTrack!: any;
private _id3Track!: DemuxedTrack;
private frameIndex: number = 0;
private cachedData: Uint8Array = new Uint8Array();
private initPTS: number | null = null;
static readonly minProbeByteLength: number = 9;

constructor (observer, config) {
super();
this.observer = observer;
this.config = config;
}

resetInitSegment (audioCodec, videoCodec, duration) {
this._audioTrack = { container: 'audio/adts', type: 'audio', id: 0, sequenceNumber: 0, isAAC: true, samples: [], len: 0, manifestCodec: audioCodec, duration: duration, inputTimeScale: 90000 };
this._id3Track = {
type: 'id3',
super.resetInitSegment(audioCodec, videoCodec, duration);
this._audioTrack = {
container: 'audio/adts',
type: 'audio',
id: 0,
pid: -1,
inputTimeScale: 90000,
sequenceNumber: 0,
isAAC: true,
samples: [],
manifestCodec: audioCodec,
duration: duration,
inputTimeScale: 90000,
dropped: 0
};
}

resetTimeStamp () {
}

resetContiguity (): void {
}

// Source for probe info - https://wiki.multimedia.cx/index.php?title=ADTS
static probe (data) {
static probe (data): boolean {
if (!data) {
return false;
}
Expand All @@ -64,92 +56,13 @@ class AACDemuxer implements Demuxer {
return false;
}

// feed incoming data to the front of the parsing pipeline
demux (data, timeOffset): DemuxerResult {
if (this.cachedData.length) {
data = appendUint8Array(this.cachedData, data);
this.cachedData = new Uint8Array();
}
let id3Data = ID3.getID3Data(data, 0) || [];
let offset = id3Data.length;
let lastDataIndex;
let pts;
const track = this._audioTrack;
const id3Track = this._id3Track;
const timestamp = ID3.getTimeStamp(id3Data);
const length = data.length;

if (this.initPTS === null) {
this.initPTS = Number.isFinite(timestamp) ? timestamp * 90 : timeOffset * 90000;
}

if (id3Data.length) {
id3Track.samples.push({ pts: this.initPTS, dts: this.initPTS, data: id3Data });
}

pts = this.initPTS;

// Iteratively parse data for ADTS Headers and ID3 headers
while (offset < length) {
// Only begin parsing if able.
if (ADTS.canParse(data, offset)) {
ADTS.initTrackConfig(track, this.observer, data, offset, track.manifestCodec);
const frame = ADTS.appendFrame(track, data, offset, this.initPTS, this.frameIndex);
if (frame) {
this.frameIndex++;
pts = frame.sample.pts;
offset += frame.length;
lastDataIndex = offset;
} else {
logger.log('Unable to parse AAC frame');
offset = length;
}
} else if (ID3.canParse(data, offset)) {
id3Data = ID3.getID3Data(data, offset);
id3Track.samples.push({ pts: pts, dts: pts, data: id3Data });
offset += id3Data.length;
lastDataIndex = offset;
} else {
offset++;
}
// At end of fragment, if there is remaining data, append everything since last useable data to cache.
if (offset === length && lastDataIndex !== length) {
const partialData = data.slice(lastDataIndex);
this.cachedData = appendUint8Array(this.cachedData, partialData);
}
}

return {
audioTrack: track,
avcTrack: dummyTrack(),
id3Track,
textTrack: dummyTrack()
};
}

demuxSampleAes (data: Uint8Array, decryptData: Uint8Array, timeOffset: number): Promise<DemuxerResult> {
return Promise.reject(new Error('The AAC demuxer does not support Sample-AES decryption'));
}

flush (timeOffset): DemuxerResult {
// Parse cache in case of remaining frames.
if (this.cachedData) {
this.demux(this.cachedData, 0);
}

this.frameIndex = 0;
this.cachedData = new Uint8Array();
this.initPTS = null;

return {
audioTrack: this._audioTrack,
avcTrack: dummyTrack(),
id3Track: this._id3Track,
textTrack: dummyTrack()
};
canParse (data, offset) {
return ADTS.canParse(data, offset);
}

destroy () {
appendFrame (track, data, offset) {
ADTS.initTrackConfig(track, this.observer, data, offset, track.manifestCodec);
return ADTS.appendFrame(track, data, offset, this.initPTS, this.frameIndex);
}
}

Expand Down
123 changes: 123 additions & 0 deletions src/demux/base-audio-demuxer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import ID3 from '../demux/id3';
import { DemuxerResult, Demuxer, DemuxedTrack, DemuxedAudioTrack } from '../types/demuxer';
import { dummyTrack } from './dummy-demuxed-track';
import { appendUint8Array } from '../utils/mp4-tools';

class BaseAudioDemuxer implements Demuxer {
protected _audioTrack!: DemuxedAudioTrack;
protected _id3Track!: DemuxedTrack;
protected frameIndex: number = 0;
protected cachedData: Uint8Array = new Uint8Array();
protected initPTS: number | null = null;

resetInitSegment (audioCodec: string, videoCodec: string, duration: number) {
this._id3Track = {
type: 'id3',
id: 0,
pid: -1,
inputTimeScale: 90000,
sequenceNumber: 0,
samples: [],
dropped: 0
};
}

resetTimeStamp () {
}

resetContiguity (): void {
}

canParse (data: Uint8Array, offset: number): boolean {
return false;
}

appendFrame (track: DemuxedAudioTrack, data: Uint8Array, offset: number): { sample, length } | undefined {
return undefined;
}

// feed incoming data to the front of the parsing pipeline
demux (data: Uint8Array, timeOffset: number): DemuxerResult {
if (this.cachedData.length) {
data = appendUint8Array(this.cachedData, data);
this.cachedData = new Uint8Array();
}

let id3Data = ID3.getID3Data(data, 0) || [];
let offset = id3Data.length;
let lastDataIndex;
let pts;
const track = this._audioTrack;
const id3Track = this._id3Track;
const timestamp = ID3.getTimeStamp(id3Data);
const length = data.length;

if (this.initPTS === null) {
this.initPTS = Number.isFinite(timestamp) ? timestamp * 90 : timeOffset * 90000;
}

if (id3Data.length) {
id3Track.samples.push({ pts: this.initPTS, dts: this.initPTS, data: id3Data });
}

pts = this.initPTS;

while (offset < length) {
if (this.canParse(data, offset)) {
const frame = this.appendFrame(track, data, offset);
if (frame) {
this.frameIndex++;
pts = frame.sample.pts;
offset += frame.length;
lastDataIndex = offset;
} else {
offset = length;
}
} else if (ID3.canParse(data, offset)) {
id3Data = ID3.getID3Data(data, offset);
id3Track.samples.push({ pts: pts, dts: pts, data: id3Data });
offset += id3Data.length;
lastDataIndex = offset;
} else {
offset++;
}
if (offset === length && lastDataIndex !== length) {
const partialData = data.slice(lastDataIndex);
this.cachedData = appendUint8Array(this.cachedData, partialData);
}
}

return {
audioTrack: track,
avcTrack: dummyTrack(),
id3Track,
textTrack: dummyTrack()
};
}

demuxSampleAes (data: Uint8Array, decryptData: Uint8Array, timeOffset: number): Promise<DemuxerResult> {
return Promise.reject(new Error(`[${this}] This demuxer does not support Sample-AES decryption`));
}

flush (timeOffset: number): DemuxerResult {
// Parse cache in case of remaining frames.
if (this.cachedData) {
this.demux(this.cachedData, 0);
}

this.frameIndex = 0;
this.initPTS = null;
this.cachedData = new Uint8Array();

return {
audioTrack: this._audioTrack,
avcTrack: dummyTrack(),
id3Track: this._id3Track,
textTrack: dummyTrack()
};
}

destroy () {}
}

export default BaseAudioDemuxer;

0 comments on commit 8d45a12

Please sign in to comment.