Skip to content

Commit

Permalink
Add support for multiple EXT-X-MAP tags video-dev#2279 video-dev#1990
Browse files Browse the repository at this point in the history
  • Loading branch information
elv-peter committed Aug 10, 2020
1 parent b1041aa commit 1582855
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 33 deletions.
15 changes: 7 additions & 8 deletions src/controller/audio-stream-controller.js
Expand Up @@ -199,11 +199,7 @@ class AudioStreamController extends BaseStreamController {
}
}
}
if (trackDetails.initSegment && !trackDetails.initSegment.data) {
frag = trackDetails.initSegment;
} // eslint-disable-line brace-style
// if bufferEnd before start of playlist, load first fragment
else if (bufferEnd <= start) {
if (bufferEnd <= start) {
frag = fragments[0];
if (this.videoTrackCC !== null && frag.cc !== this.videoTrackCC) {
// Ensure we find a fragment which matches the continuity of the video track
Expand Down Expand Up @@ -262,6 +258,9 @@ class AudioStreamController extends BaseStreamController {
} else {
// only load if fragment is not loaded or if in audio switch
// we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch
if (trackDetails.initSegments[frag.initSegment] && !trackDetails.initSegments[frag.initSegment].data) {
frag = trackDetails.initSegments[frag.initSegment].fragment;
}
this.fragCurrent = frag;
if (audioSwitch || this.fragmentTracker.getState(frag) === FragmentState.NOT_LOADED) {
logger.log(`Loading ${frag.sn}, cc: ${frag.cc} of [${trackDetails.startSN} ,${trackDetails.endSN}],track ${trackId}, ${
Expand Down Expand Up @@ -500,7 +499,7 @@ class AudioStreamController extends BaseStreamController {
this.state = State.IDLE;

stats.tparsed = stats.tbuffered = performance.now();
details.initSegment.data = data.payload;
details.initSegments[data.frag.relurl].data = data.payload;
this.hls.trigger(Event.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'audio' });
this.tick();
} else {
Expand All @@ -514,8 +513,8 @@ class AudioStreamController extends BaseStreamController {
// Check if we have video initPTS
// If not we need to wait for it
let initPTS = this.initPTS[cc];
let initSegmentData = details.initSegment ? details.initSegment.data : [];
if (initPTS !== undefined) {
const initSegmentData = details.initSegments[fragCurrent.initSegment] ? details.initSegments[fragCurrent.initSegment].data : [];
if (details.initSegments[fragCurrent.initSegment] || initPTS !== undefined) {
this.pendingBuffering = true;
logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],track ${trackId}`);
// time Offset is accurate if level PTS is known, or if playlist is not sliding (not live)
Expand Down
10 changes: 8 additions & 2 deletions src/controller/level-helper.js
Expand Up @@ -114,9 +114,15 @@ export function updateFragPTSDTS (details, frag, startPTS, endPTS, startDTS, end
}

export function mergeDetails (oldDetails, newDetails) {
// potentially retrieve cached initsegment
// potentially retrieve cached initsegments
if (newDetails.initSegment && oldDetails.initSegment) {
newDetails.initSegment = oldDetails.initSegment;
if (newDetails.initSegments && oldDetails.initSegments) {
newDetails.initSegment = oldDetails.initSegment;
newDetails.initSegments = {
...newDetails.initSegments,
...oldDetails.initSegments
};
}
}

// check if old/new playlists have fragments in common
Expand Down
43 changes: 21 additions & 22 deletions src/controller/stream-controller.js
Expand Up @@ -246,34 +246,33 @@ class StreamController extends BaseStreamController {
bufferEnd = bufferInfo.end,
frag;

if (levelDetails.initSegment && !levelDetails.initSegment.data) {
frag = levelDetails.initSegment;
} else {
// in case of live playlist we need to ensure that requested position is not located before playlist start
if (levelDetails.live) {
let initialLiveManifestSize = this.config.initialLiveManifestSize;
if (fragLen < initialLiveManifestSize) {
logger.warn(`Can not start playback of a level, reason: not enough fragments ${fragLen} < ${initialLiveManifestSize}`);
return;
}
// in case of live playlist we need to ensure that requested position is not located before playlist start
if (levelDetails.live) {
let initialLiveManifestSize = this.config.initialLiveManifestSize;
if (fragLen < initialLiveManifestSize) {
logger.warn(`Can not start playback of a level, reason: not enough fragments ${fragLen} < ${initialLiveManifestSize}`);
return;
}

frag = this._ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments);
// if it explicitely returns null don't load any fragment and exit function now
if (frag === null) {
return;
}
} else {
// VoD playlist: if bufferEnd before start of playlist, load first fragment
if (bufferEnd < start) {
frag = fragments[0];
}
frag = this._ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments);
// if it explicitely returns null don't load any fragment and exit function now
if (frag === null) {
return;
}
} else {
// VoD playlist: if bufferEnd before start of playlist, load first fragment
if (bufferEnd < start) {
frag = fragments[0];
}
}
if (!frag) {
frag = this._findFragment(start, fragPrevious, fragLen, fragments, bufferEnd, end, levelDetails);
}

if (frag) {
if (levelDetails.initSegments[frag.initSegment] && !levelDetails.initSegments[frag.initSegment].data) {
frag = levelDetails.initSegments[frag.initSegment].fragment;
}
if (frag.encrypted) {
this._loadKey(frag, levelDetails);
} else {
Expand Down Expand Up @@ -879,7 +878,7 @@ class StreamController extends BaseStreamController {
} else if (fragLoaded.sn === 'initSegment') {
this.state = State.IDLE;
stats.tparsed = stats.tbuffered = window.performance.now();
details.initSegment.data = data.payload;
details.initSegments[data.frag.relurl].data = data.payload;
hls.trigger(Event.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'main' });
this.tick();
} else {
Expand All @@ -899,7 +898,7 @@ class StreamController extends BaseStreamController {

// time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) and if media is not seeking (this is to overcome potential timestamp drifts between playlists and fragments)
const accurateTimeOffset = !(media && media.seeking) && (details.PTSKnown || !details.live);
const initSegmentData = details.initSegment ? details.initSegment.data : [];
const initSegmentData = details.initSegments[fragCurrent.initSegment] ? details.initSegments[fragCurrent.initSegment].data : [];
const audioCodec = this._getAudioCodec(currentLevel);

// transmux the MPEG-TS data to ISO-BMFF segments
Expand Down
4 changes: 4 additions & 0 deletions src/loader/fragment.ts
Expand Up @@ -57,6 +57,10 @@ export default class Fragment {
// _decryptdata will set the IV for this segment based on the segment number in the fragment
public levelkey?: LevelKey;

// initSegment is the relurl of the initsegment associated with the fragment which can be found
// on level details
public initSegment?: string;

// TODO(typescript-xhrloader)
public loader: any;

Expand Down
10 changes: 10 additions & 0 deletions src/loader/init-segment.ts
@@ -0,0 +1,10 @@
import Fragment from './fragment';

export default class InitSegment {
public fragment: Fragment;
public data: ArrayBuffer | null = null;

constructor (fragment: Fragment) {
this.fragment = fragment;
}
}
2 changes: 1 addition & 1 deletion src/loader/level.js
Expand Up @@ -4,7 +4,7 @@ export default class Level {
this.endCC = 0;
this.endSN = 0;
this.fragments = [];
this.initSegment = null;
this.initSegments = {};
this.live = true;
this.needSidxRanges = false;
this.startCC = 0;
Expand Down
8 changes: 8 additions & 0 deletions src/loader/m3u8-parser.ts
@@ -1,6 +1,7 @@
import * as URLToolkit from 'url-toolkit';

import Fragment from './fragment';
import InitSegment from './init-segment';
import Level from './level';
import LevelKey from './level-key';

Expand Down Expand Up @@ -173,6 +174,7 @@ export default class M3U8Parser {
let frag: Fragment | null = new Fragment();
let result: RegExpExecArray | RegExpMatchArray | null;
let i: number;
let initSegment: InitSegment | null = null;
let levelkey: LevelKey | undefined;

let firstPdtIndex = null;
Expand Down Expand Up @@ -200,6 +202,9 @@ export default class M3U8Parser {
frag.cc = discontinuityCounter;
frag.urlId = levelUrlId;
frag.baseurl = baseurl;
if (initSegment) {
frag.initSegment = initSegment.fragment.relurl;
}
// avoid sliced strings https://github.com/video-dev/hls.js/issues/939
frag.relurl = (' ' + result[3]).slice(1);
assignProgramDateTime(frag, prevFrag);
Expand Down Expand Up @@ -314,6 +319,9 @@ export default class M3U8Parser {
frag.type = type;
frag.sn = 'initSegment';
level.initSegment = frag;
frag.cc = discontinuityCounter;
initSegment = new InitSegment(frag);
level.initSegments[frag.relurl] = initSegment;
frag = new Fragment();
frag.rawProgramDateTime = level.initSegment.rawProgramDateTime;
break;
Expand Down

0 comments on commit 1582855

Please sign in to comment.