Skip to content

Commit

Permalink
demuxer: support multiple instances
Browse files Browse the repository at this point in the history
add a demuxer id, propagate this id in fragment parsing events
related to https://github.com/dailymotion/hls.js/issues/30
  • Loading branch information
mangui committed Apr 28, 2016
1 parent 2eaa6c6 commit ad8aa80
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 59 deletions.
10 changes: 5 additions & 5 deletions API.md
Expand Up @@ -633,15 +633,15 @@ full list of Events available below :
- `Hls.Events.FRAG_LOADED` - fired when a fragment loading is completed
- data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length}}
- `Hls.Events.FRAG_PARSING_INIT_SEGMENT` - fired when Init Segment has been extracted from fragment
- data: { moov : moov MP4 box, codecs : codecs found while parsing fragment}
- data: { id: demuxer id, moov : moov MP4 box, codecs : codecs found while parsing fragment}
- `Hls.Events.FRAG_PARSING_METADATA` - fired when parsing id3 is completed
- data: { samples : [ id3 pes - pts and dts timestamp are relative, values are in seconds]}
- data: { id: demuxer id, samples : [ id3 pes - pts and dts timestamp are relative, values are in seconds]}
- `Hls.Events.FRAG_PARSING_DATA` - fired when moof/mdat have been extracted from fragment
- data: { moof : moof MP4 box, mdat : mdat MP4 box, startPTS : PTS of first sample, endPTS : PTS of last sample, startDTS : DTS of first sample, endDTS : DTS of last sample, type : stream type (audio or video), nb : number of samples}
- data: { id: demuxer id, moof : moof MP4 box, mdat : mdat MP4 box, startPTS : PTS of first sample, endPTS : PTS of last sample, startDTS : DTS of first sample, endDTS : DTS of last sample, type : stream type (audio or video), nb : number of samples}
- `Hls.Events.FRAG_PARSED` - fired when fragment parsing is completed
- data: undefined
- data: { id: demuxer id}
- `Hls.Events.FRAG_BUFFERED` - fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer
- data: { frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length} }
- data: { id: demuxer id, frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length} }
- `Hls.Events.FRAG_CHANGED` - fired when fragment matching with current video position is changing
- data: { frag : fragment object }
- `Hls.Events.FPS_DROP` - triggered when FPS drop in last monitoring period is higher than given threshold
Expand Down
14 changes: 7 additions & 7 deletions src/controller/stream-controller.js
Expand Up @@ -65,7 +65,7 @@ class StreamController extends EventHandler {
if (this.levels) {
var media = this.media, lastCurrentTime = this.lastCurrentTime;
this.stopLoad();
this.demuxer = new Demuxer(this.hls);
this.demuxer = new Demuxer(this.hls,0);
if (!this.timer) {
this.timer = setInterval(this.ontick, 100);
}
Expand Down Expand Up @@ -744,7 +744,7 @@ class StreamController extends EventHandler {
this.state = State.IDLE;
this.fragBitrateTest = false;
data.stats.tparsed = data.stats.tbuffered = performance.now();
this.hls.trigger(Event.FRAG_BUFFERED, {stats: data.stats, frag: fragCurrent});
this.hls.trigger(Event.FRAG_BUFFERED, {stats: data.stats, frag: fragCurrent, id : 0});
} else {
this.state = State.PARSING;
// transmux the MPEG-TS data to ISO-BMFF segments
Expand Down Expand Up @@ -778,7 +778,7 @@ class StreamController extends EventHandler {
}

onFragParsingInitSegment(data) {
if (this.state === State.PARSING) {
if (data.id === 0 && this.state === State.PARSING) {
var tracks = data.tracks, trackName, track;

// include levelCodec in audio and video tracks
Expand Down Expand Up @@ -858,7 +858,7 @@ class StreamController extends EventHandler {
}

onFragParsingData(data) {
if (this.state === State.PARSING) {
if (data.id === 0 && this.state === State.PARSING) {
this.tparse2 = Date.now();
var level = this.levels[this.level],
frag = this.fragCurrent;
Expand Down Expand Up @@ -886,8 +886,8 @@ class StreamController extends EventHandler {
}
}

onFragParsed() {
if (this.state === State.PARSING) {
onFragParsed(data) {
if (data.id === 0 && this.state === State.PARSING) {
this.stats.tparsed = performance.now();
this.state = State.PARSED;
this._checkAppendedParsed();
Expand All @@ -914,7 +914,7 @@ class StreamController extends EventHandler {
this.fragPrevious = frag;
stats.tbuffered = performance.now();
this.fragLastKbps = Math.round(8 * stats.length / (stats.tbuffered - stats.tfirst));
this.hls.trigger(Event.FRAG_BUFFERED, {stats: stats, frag: frag});
this.hls.trigger(Event.FRAG_BUFFERED, {stats: stats, frag: frag, id : 0});
logger.log(`media buffered : ${this.timeRangesToString(this.media.buffered)}`);
this.state = State.IDLE;
}
Expand Down
14 changes: 8 additions & 6 deletions src/demux/demuxer-inline.js
Expand Up @@ -11,8 +11,9 @@ import PassThroughRemuxer from '../remux/passthrough-remuxer';

class DemuxerInline {

constructor(hls,typeSupported) {
constructor(hls,id,typeSupported) {
this.hls = hls;
this.id = id;
this.typeSupported = typeSupported;
}

Expand All @@ -26,18 +27,19 @@ class DemuxerInline {
push(data, audioCodec, videoCodec, timeOffset, cc, level, sn, duration) {
var demuxer = this.demuxer;
if (!demuxer) {
var hls = this.hls;
let hls = this.hls,
id = this.id;
// probe for content type
if (TSDemuxer.probe(data)) {
if (this.typeSupported.mp2t === true) {
demuxer = new TSDemuxer(hls,PassThroughRemuxer);
demuxer = new TSDemuxer(hls,id,PassThroughRemuxer);
} else {
demuxer = new TSDemuxer(hls,MP4Remuxer);
demuxer = new TSDemuxer(hls,id,MP4Remuxer);
}
} else if(AACDemuxer.probe(data)) {
demuxer = new AACDemuxer(hls,MP4Remuxer);
demuxer = new AACDemuxer(hls,id,MP4Remuxer);
} else {
hls.trigger(Event.ERROR, {type : ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: 'no demux matching with content found'});
hls.trigger(Event.ERROR, {type : ErrorTypes.MEDIA_ERROR, id : id, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: 'no demux matching with content found'});
return;
}
this.demuxer = demuxer;
Expand Down
16 changes: 8 additions & 8 deletions src/demux/demuxer-worker.js
Expand Up @@ -22,7 +22,7 @@ var DemuxerWorker = function (self) {
//console.log('demuxer cmd:' + data.cmd);
switch (data.cmd) {
case 'init':
self.demuxer = new DemuxerInline(observer, data.typeSupported);
self.demuxer = new DemuxerInline(observer, data.id, data.typeSupported);
break;
case 'demux':
self.demuxer.push(new Uint8Array(data.data), data.audioCodec, data.videoCodec, data.timeOffset, data.cc, data.level, data.sn, data.duration);
Expand All @@ -34,30 +34,30 @@ var DemuxerWorker = function (self) {

// listen to events triggered by Demuxer
observer.on(Event.FRAG_PARSING_INIT_SEGMENT, function(ev, data) {
self.postMessage({event: ev, tracks : data.tracks, unique : data.unique });
self.postMessage({event: ev, id : data.id, tracks : data.tracks, unique : data.unique });
});

observer.on(Event.FRAG_PARSING_DATA, function(ev, data) {
var objData = {event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb};
var objData = {event: ev, id : data.id, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb};
// pass data1/data2 as transferable object (no copy)
self.postMessage(objData, [objData.data1, objData.data2]);
});

observer.on(Event.FRAG_PARSED, function(event) {
self.postMessage({event: event});
observer.on(Event.FRAG_PARSED, function(event, data) {
self.postMessage({event: event, data : data});
});

observer.on(Event.ERROR, function(event, data) {
self.postMessage({event: event, data: data});
self.postMessage({event: event, data: data });
});

observer.on(Event.FRAG_PARSING_METADATA, function(event, data) {
var objData = {event: event, samples: data.samples};
var objData = {event: event, id : data.id, samples: data.samples};
self.postMessage(objData);
});

observer.on(Event.FRAG_PARSING_USERDATA, function(event, data) {
var objData = {event: event, samples: data.samples};
var objData = {event: event, id : data.id, samples: data.samples};
self.postMessage(objData);
});

Expand Down
30 changes: 17 additions & 13 deletions src/demux/demuxer.js
Expand Up @@ -6,8 +6,9 @@ import Decrypter from '../crypt/decrypter';

class Demuxer {

constructor(hls) {
constructor(hls, id) {
this.hls = hls;
this.id = id;
var typeSupported = {
mp4 : MediaSource.isTypeSupported('video/mp4'),
mp2t : hls.config.enableMP2TPassThrough && MediaSource.isTypeSupported('video/mp2t')
Expand All @@ -19,13 +20,13 @@ class Demuxer {
this.w = work(DemuxerWorker);
this.onwmsg = this.onWorkerMessage.bind(this);
this.w.addEventListener('message', this.onwmsg);
this.w.postMessage({cmd: 'init', typeSupported : typeSupported});
this.w.postMessage({cmd: 'init', typeSupported : typeSupported, id : id});
} catch(err) {
logger.error('error while initializing DemuxerWorker, fallback on DemuxerInline');
this.demuxer = new DemuxerInline(hls,typeSupported);
this.demuxer = new DemuxerInline(hls,id,typeSupported);
}
} else {
this.demuxer = new DemuxerInline(hls,typeSupported);
this.demuxer = new DemuxerInline(hls,id,typeSupported);
}
this.demuxInitialized = true;
}
Expand Down Expand Up @@ -70,17 +71,18 @@ class Demuxer {
}

onWorkerMessage(ev) {
var data = ev.data;
let data = ev.data,
hls = this.hls,
id = data.id;

//console.log('onWorkerMessage:' + data.event);
switch(data.event) {
case Event.FRAG_PARSING_INIT_SEGMENT:
var obj = {};
obj.tracks = data.tracks;
obj.unique = data.unique;
this.hls.trigger(Event.FRAG_PARSING_INIT_SEGMENT, obj);
hls.trigger(Event.FRAG_PARSING_INIT_SEGMENT, {id : id, tracks : data.tracks, unique : data.unique});
break;
case Event.FRAG_PARSING_DATA:
this.hls.trigger(Event.FRAG_PARSING_DATA,{
hls.trigger(Event.FRAG_PARSING_DATA,{
id : id,
data1: new Uint8Array(data.data1),
data2: new Uint8Array(data.data2),
startPTS: data.startPTS,
Expand All @@ -92,17 +94,19 @@ class Demuxer {
});
break;
case Event.FRAG_PARSING_METADATA:
this.hls.trigger(Event.FRAG_PARSING_METADATA, {
hls.trigger(Event.FRAG_PARSING_METADATA, {
id : id,
samples: data.samples
});
break;
case Event.FRAG_PARSING_USERDATA:
this.hls.trigger(Event.FRAG_PARSING_USERDATA, {
hls.trigger(Event.FRAG_PARSING_USERDATA, {
id : id,
samples: data.samples
});
break;
default:
this.hls.trigger(data.event, data.data);
hls.trigger(data.event, data.data);
break;
}
}
Expand Down
9 changes: 5 additions & 4 deletions src/demux/tsdemuxer.js
Expand Up @@ -18,11 +18,12 @@

class TSDemuxer {

constructor(observer,remuxerClass) {
constructor(observer,id,remuxerClass) {
this.observer = observer;
this.id = id;
this.remuxerClass = remuxerClass;
this.lastCC = 0;
this.remuxer = new this.remuxerClass(observer);
this.remuxer = new this.remuxerClass(observer,id);
}

static probe(data) {
Expand Down Expand Up @@ -172,7 +173,7 @@
}
}
} else {
this.observer.trigger(Event.ERROR, {type : ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'TS packet did not start with 0x47'});
this.observer.trigger(Event.ERROR, {type : ErrorTypes.MEDIA_ERROR, id : this.id, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'TS packet did not start with 0x47'});
}
}
// parse last PES packet
Expand Down Expand Up @@ -586,7 +587,7 @@
reason = 'no ADTS header found in AAC PES';
fatal = true;
}
this.observer.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: fatal, reason: reason});
this.observer.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, id : this.id, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: fatal, reason: reason});
if (fatal) {
return;
}
Expand Down
14 changes: 7 additions & 7 deletions src/events.js
Expand Up @@ -53,19 +53,19 @@ module.exports = {
FRAG_LOAD_EMERGENCY_ABORTED: 'hlsFragLoadEmergencyAborted',
// fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length}}
FRAG_LOADED: 'hlsFragLoaded',
// fired when Init Segment has been extracted from fragment - data: { moov : moov MP4 box, codecs : codecs found while parsing fragment}
// fired when Init Segment has been extracted from fragment - data: { id : demuxer id, moov : moov MP4 box, codecs : codecs found while parsing fragment}
FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
// fired when parsing sei text is completed - data: { samples : [ sei samples pes ] }
// fired when parsing sei text is completed - data: { id : demuxer id, samples : [ sei samples pes ] }
FRAG_PARSING_USERDATA: 'hlsFragParsingUserdata',
// fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] }
// fired when parsing id3 is completed - data: { id : demuxer id, samples : [ id3 samples pes ] }
FRAG_PARSING_METADATA: 'hlsFragParsingMetadata',
// fired when data have been extracted from fragment - data: { data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null}
// fired when data have been extracted from fragment - data: { id : demuxer id, data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null}
FRAG_PARSING_DATA: 'hlsFragParsingData',
// fired when fragment parsing is completed - data: undefined
// fired when fragment parsing is completed - data: { id : demuxer id}
FRAG_PARSED: 'hlsFragParsed',
// fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length} }
// fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id,frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length} }
FRAG_BUFFERED: 'hlsFragBuffered',
// fired when fragment matching with current media position is changing - data : { frag : fragment object }
// fired when fragment matching with current media position is changing - data : { id : demuxer id, frag : fragment object }
FRAG_CHANGED: 'hlsFragChanged',
// Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames}
FPS_DROP: 'hlsFpsDrop',
Expand Down
4 changes: 2 additions & 2 deletions src/remux/dummy-remuxer.js
Expand Up @@ -3,9 +3,9 @@
*/

class DummyRemuxer {
constructor(observer) {
this.PES_TIMESCALE = 90000;
constructor(observer, id) {
this.observer = observer;
this.id = id;
}

get passthrough() {
Expand Down
13 changes: 9 additions & 4 deletions src/remux/mp4-remuxer.js
Expand Up @@ -9,8 +9,9 @@ import MP4 from '../remux/mp4-generator';
import {ErrorTypes, ErrorDetails} from '../errors';

class MP4Remuxer {
constructor(observer) {
constructor(observer,id) {
this.observer = observer;
this.id = id;
this.ISGenerated = false;
this.PES2MP4SCALEFACTOR = 4;
this.PES_TIMESCALE = 90000;
Expand Down Expand Up @@ -56,7 +57,7 @@ class MP4Remuxer {
this.remuxText(textTrack,timeOffset);
}
//notify end of parsing
this.observer.trigger(Event.FRAG_PARSED);
this.observer.trigger(Event.FRAG_PARSED, { id : this.id });
}

generateIS(audioTrack,videoTrack,timeOffset) {
Expand All @@ -65,7 +66,7 @@ class MP4Remuxer {
videoSamples = videoTrack.samples,
pesTimeScale = this.PES_TIMESCALE,
tracks = {},
data = { tracks : tracks, unique : false },
data = { id : this.id, tracks : tracks, unique : false },
computePTSDTS = (this._initPTS === undefined),
initPTS, initDTS;

Expand Down Expand Up @@ -127,7 +128,7 @@ class MP4Remuxer {
this._initDTS = initDTS;
}
} else {
observer.trigger(Event.ERROR, {type : ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'no audio/video samples found'});
observer.trigger(Event.ERROR, {type : ErrorTypes.MEDIA_ERROR, id : this.id, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'no audio/video samples found'});
}
}

Expand Down Expand Up @@ -282,6 +283,7 @@ class MP4Remuxer {
moof = MP4.moof(track.sequenceNumber++, firstDTS / pes2mp4ScaleFactor, track);
track.samples = [];
this.observer.trigger(Event.FRAG_PARSING_DATA, {
id : this.id,
data1: moof,
data2: mdat,
startPTS: firstPTS / pesTimeScale,
Expand Down Expand Up @@ -409,6 +411,7 @@ class MP4Remuxer {
moof = MP4.moof(track.sequenceNumber++, firstDTS / pes2mp4ScaleFactor, track);
track.samples = [];
this.observer.trigger(Event.FRAG_PARSING_DATA, {
id : this.id,
data1: moof,
data2: mdat,
startPTS: firstPTS / pesTimeScale,
Expand All @@ -433,6 +436,7 @@ class MP4Remuxer {
sample.dts = ((sample.dts - this._initDTS) / this.PES_TIMESCALE);
}
this.observer.trigger(Event.FRAG_PARSING_METADATA, {
id : this.id,
samples:track.samples
});
}
Expand All @@ -456,6 +460,7 @@ class MP4Remuxer {
sample.pts = ((sample.pts - this._initPTS) / this.PES_TIMESCALE);
}
this.observer.trigger(Event.FRAG_PARSING_USERDATA, {
id : this.id,
samples:track.samples
});
}
Expand Down

0 comments on commit ad8aa80

Please sign in to comment.