Skip to content

Commit

Permalink
Do not flush audio on audio track switch when both tracks are main va…
Browse files Browse the repository at this point in the history
…riant streams

video-dev#2837
  • Loading branch information
Rob Walch authored and alangdm committed Sep 15, 2020
1 parent dc62d83 commit 98aabf9
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 107 deletions.
1 change: 1 addition & 0 deletions demo/index.html
Expand Up @@ -169,6 +169,7 @@ <h3>
</p>
<p>
<span>
<button type="button" class="btn btn-xs btn-default" onclick="$('#streamSelect')[0].selectedIndex++;$('#streamSelect').change()">Next video</button>
<button type="button" class="btn btn-xs btn-default btn-dump" title="Save dumped audio mp4 appends" onclick="createfMP4('audio');">Create audio-fmp4</button>
<button type="button" class="btn btn-xs btn-default btn-dump" title="Save dumped video mp4 appends" onclick="createfMP4('video')">Create video-fmp4</button>
</span>
Expand Down
5 changes: 4 additions & 1 deletion demo/main.js
Expand Up @@ -58,10 +58,13 @@ $(document).ready(function () {

chart = setupTimelineChart();

Object.keys(testStreams).forEach((key) => {
Object.keys(testStreams).forEach((key, index) => {
const stream = testStreams[key];
const option = new Option(stream.description, key);
$('#streamSelect').append(option);
if (stream.url === sourceURL) {
$('#streamSelect')[0].selectedIndex = index + 1;
}
});

$('#streamSelect').change(function () {
Expand Down
11 changes: 7 additions & 4 deletions src/controller/audio-stream-controller.js
Expand Up @@ -58,7 +58,7 @@ class AudioStreamController extends BaseStreamController {
// Can change due level switch
this.initPTS[cc] = initPTS;
this.videoTrackCC = cc;
logger.log(`InitPTS for cc: ${cc} found from video track: ${initPTS}`);
logger.log(`InitPTS for cc: ${cc} found from main: ${initPTS}`);

// If we are waiting we need to demux/remux the waiting frag
// With the new initPTS
Expand Down Expand Up @@ -264,7 +264,9 @@ class AudioStreamController extends BaseStreamController {
// we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch
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}, currentTime:${pos},bufferEnd:${bufferEnd.toFixed(3)}`);
logger.log(`Loading ${frag.sn}, cc: ${frag.cc} of [${trackDetails.startSN} ,${trackDetails.endSN}],track ${trackId}, ${
this.loadedmetadata ? 'currentTime' : 'nextLoadPosition'
}: ${pos}, bufferEnd: ${bufferEnd.toFixed(3)}`);

if (frag.sn !== 'initSegment') {
this.startFragRequested = true;
Expand Down Expand Up @@ -593,9 +595,10 @@ class AudioStreamController extends BaseStreamController {
logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb}`);
LevelHelper.updateFragPTSDTS(track.details, fragCurrent, data.startPTS, data.endPTS);

let audioSwitch = this.audioSwitch, media = this.media, appendOnBufferFlush = false;
const media = this.media;
let appendOnBufferFlush = false;
// Only flush audio from old audio tracks when PTS is known on new audio track
if (audioSwitch) {
if (this.audioSwitch) {
if (media && media.readyState) {
let currentTime = media.currentTime;
logger.log('switching audio track : currentTime:' + currentTime);
Expand Down
26 changes: 18 additions & 8 deletions src/controller/stream-controller.js
Expand Up @@ -426,7 +426,7 @@ class StreamController extends BaseStreamController {
}

_loadKey (frag, levelDetails) {
logger.log(`Loading key for ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${this.level}`);
logger.log(`Loading key for ${frag.sn} of [${levelDetails.startSN}-${levelDetails.endSN}], level ${this.level}`);
this.state = State.KEY_LOADING;
this.hls.trigger(Event.KEY_LOADING, { frag });
}
Expand All @@ -449,7 +449,9 @@ class StreamController extends BaseStreamController {
frag.autoLevel = this.hls.autoLevelEnabled;
frag.bitrateTest = this.bitrateTest;

logger.log(`Loading ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${this.level}, currentTime:${pos.toFixed(3)},bufferEnd:${bufferEnd.toFixed(3)}`);
logger.log(`Loading ${frag.sn} of [${levelDetails.startSN}-${levelDetails.endSN}], level ${this.level}, ${
this.loadedmetadata ? 'currentTime' : 'nextLoadPosition'
}: ${parseFloat(pos.toFixed(3))}, bufferEnd: ${parseFloat(bufferEnd.toFixed(3))}`);

this.hls.trigger(Event.FRAG_LOADING, { frag });
// lazy demuxer init, as this could take some time ... do it during frag loading
Expand Down Expand Up @@ -1085,8 +1087,9 @@ class StreamController extends BaseStreamController {

onAudioTrackSwitching (data) {
// if any URL found on new audio track, it is an alternate audio track
let altAudio = !!data.url,
trackId = data.id;
const fromAltAudio = this.altAudio;
const altAudio = !!data.url;
const trackId = data.id;
// if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered
// don't do anything if we switch to alt audio: audio stream controller is handling it.
// we will just have to change buffer scheduling on audioTrackSwitched
Expand All @@ -1111,10 +1114,17 @@ class StreamController extends BaseStreamController {
this.state = State.IDLE;
}
let hls = this.hls;
// switching to main audio, flush all audio and trigger track switched
hls.trigger(Event.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' });
hls.trigger(Event.AUDIO_TRACK_SWITCHED, { id: trackId });
this.altAudio = false;
// If switching from alt to main audio, flush all audio and trigger track switched
if (fromAltAudio) {
hls.trigger(Event.BUFFER_FLUSHING, {
startOffset: 0,
endOffset: Number.POSITIVE_INFINITY,
type: 'audio'
});
}
hls.trigger(Event.AUDIO_TRACK_SWITCHED, {
id: trackId
});
}
}

Expand Down
169 changes: 75 additions & 94 deletions tests/test-streams.js
Expand Up @@ -35,116 +35,89 @@ function createTestStreamWithConfig (target, config) {
}

module.exports = {
bbb: createTestStreamWithConfig({
bbb: {
url: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8',
description: 'Big Buck Bunny - adaptive qualities'
description: 'Big Buck Bunny - adaptive qualities',
abr: true
},
{
// try to workaround test failing because of slow seek on Chrome/Win10
nudgeMaxRetry: 5
}
),
fdr: {
url: 'https://cdn.jwplayer.com/manifests/pZxWPRg4.m3u8',
description: 'FDR - CDN packaged, 4s segments, 180p - 1080p',
live: false,
abr: true
},
bigBuckBunny480p: {
'url': 'https://test-streams.mux.dev/x36xhzz/url_6/193039199_mp4_h264_aac_hq_7.m3u8',
'description': 'Big Buck Bunny - 480p only',
'live': false,
'abr': false,
'blacklist_ua': ['internet explorer']
url: 'https://test-streams.mux.dev/x36xhzz/url_6/193039199_mp4_h264_aac_hq_7.m3u8',
description: 'Big Buck Bunny - 480p only',
abr: false,
blacklist_ua: ['internet explorer']
},
arte: {
'url': 'https://test-streams.mux.dev/test_001/stream.m3u8',
'description': 'ARTE China,ABR',
'live': false,
'abr': true
url: 'https://test-streams.mux.dev/test_001/stream.m3u8',
description: 'ARTE China,ABR',
abr: true
},
deltatreDAI: {
'url': 'https://test-streams.mux.dev/dai-discontinuity-deltatre/manifest.m3u8',
'description': 'Ad-insertion in event stream',
'live': false,
'abr': false,
'blacklist_ua': ['internet explorer']
url: 'https://test-streams.mux.dev/dai-discontinuity-deltatre/manifest.m3u8',
description: 'Ad-insertion in event stream',
abr: false,
blacklist_ua: ['internet explorer']
},
issue666: {
'url': 'https://playertest.longtailvideo.com/adaptive/issue666/playlists/cisq0gim60007xzvi505emlxx.m3u8',
'description': 'Surveillance footage - https://github.com/video-dev/hls.js/issues/666',
'live': false,
'abr': false,
'blacklist_ua': ['internet explorer']
},
/* // went offline for us :( would be good to replace this for regression test with something mimicking the issue
issue649: {
'url': 'https://cdn3.screen9.com/media/c/W/cW87csHkxsgu5TV1qs78aA_auto_hls.m3u8?auth=qlUjeCtbVdtkDfZYrtveTIVUXX1yuSqgF8wfWabzKpX72r-d5upW88-FHuyRRdnZA_1PKRTGAtTt_6Z-aj22kw',
'description': 'hls.js/issues/649',
'live': false,
'abr': false
url: 'https://playertest.longtailvideo.com/adaptive/issue666/playlists/cisq0gim60007xzvi505emlxx.m3u8',
description: 'Surveillance footage - https://github.com/video-dev/hls.js/issues/666',
abr: false,
blacklist_ua: ['internet explorer']
},
*/
closedCaptions: {
'url': 'https://playertest.longtailvideo.com/adaptive/captions/playlist.m3u8',
'description': 'CNN special report, with CC',
'live': false,
'abr': false
url: 'https://playertest.longtailvideo.com/adaptive/captions/playlist.m3u8',
description: 'CNN special report, with CC',
abr: false
},
customIvBadDts: {
'url': 'https://playertest.longtailvideo.com/adaptive/customIV/prog_index.m3u8',
'description': 'Custom IV with bad PTS DTS',
'live': false,
'abr': false,
'blacklist_ua': ['safari']
url: 'https://playertest.longtailvideo.com/adaptive/customIV/prog_index.m3u8',
description: 'Custom IV with bad PTS DTS',
abr: false
},
oceansAES: {
'url': 'https://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8',
'description': 'AES encrypted,ABR',
'live': false,
'abr': true
url: 'https://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8',
description: 'AES encrypted,ABR',
abr: true
},
/*
bbbAES: {
'url': 'https://test-streams.mux.dev/bbbAES/playlists/sample_aes/index.m3u8',
'description': 'SAMPLE-AES encrypted',
'live': false,
'abr': false
url: 'https://test-streams.mux.dev/bbbAES/playlists/sample_aes/index.m3u8',
description: 'SAMPLE-AES encrypted',
live: false,
abr: false
},
*/
mp3Audio: {
'url': 'https://playertest.longtailvideo.com/adaptive/vod-with-mp3/manifest.m3u8',
'description': 'MP3 VOD demo',
'live': false,
'abr': false,
'blacklist_ua': ['safari']
url: 'https://playertest.longtailvideo.com/adaptive/vod-with-mp3/manifest.m3u8',
description: 'MP3 VOD demo',
abr: false
},
mpegAudioOnly: {
'url': 'https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8',
'description': 'MPEG Audio Only demo',
'live': false,
'abr': false,
'blacklist_ua': ['internet explorer', 'MicrosoftEdge', 'safari', 'firefox']
url: 'https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8',
description: 'MPEG Audio Only demo',
abr: false,
blacklist_ua: ['internet explorer', 'MicrosoftEdge', 'firefox']
},
fmp4: {
'url': 'https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8',
'description': 'HLS fMP4 Angel-One multiple audio-tracks',
'live': false,
'abr': false,
'blacklist_ua': ['safari', 'internet explorer']
url: 'https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8',
description: 'HLS fMP4 Angel-One multiple audio-tracks',
abr: true,
blacklist_ua: ['internet explorer']
},
fmp4Bitmovin: {
'url': 'https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s-fmp4/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8',
'description': 'HLS fMP4 by Bitmovin',
'live': false,
'abr': true,
'blacklist_ua': ['safari', 'internet explorer']
url: 'https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s-fmp4/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8',
description: 'HLS fMP4 by Bitmovin',
abr: true,
blacklist_ua: ['internet explorer']
},
offset_pts: {
'url': 'https://test-streams.mux.dev/pts_shift/master.m3u8',
'description': 'DK Turntable, PTS shifted by 2.3s',
'live': false,
'abr': false
url: 'https://test-streams.mux.dev/pts_shift/master.m3u8',
description: 'DK Turntable, PTS shifted by 2.3s',
abr: true
},
/*
uspHLSAteam: createTestStream(
Expand All @@ -155,59 +128,67 @@ module.exports = {
angelOneShakaWidevine: createTestStreamWithConfig({
url: 'https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine-hls/hls.m3u8',
description: 'Shaka-packager Widevine DRM (EME) HLS-fMP4 - Angel One Demo',
abr: true,
blacklist_ua: ['firefox', 'safari', 'internet explorer']
},
{
}, {
widevineLicenseUrl: 'http://cwip-shaka-proxy.appspot.com/no_auth',
emeEnabled: true
}
),
}),
audioOnlyMultipleLevels: {
'url': 'https://s3.amazonaws.com/qa.jwplayer.com/~alex/121628/new_master.m3u8',
'description': 'Multiple non-alternate audio levels',
'live': false,
'abr': false
url: 'https://s3.amazonaws.com/qa.jwplayer.com/~alex/121628/new_master.m3u8',
description: 'Multiple non-alternate audio levels',
abr: true
},
pdtDuplicate: {
url: 'https://playertest.longtailvideo.com/adaptive/artbeats/manifest.m3u8',
description: 'Stream with duplicate sequential PDT values'
description: 'Stream with duplicate sequential PDT values',
abr: false
},
pdtLargeGap: {
url: 'https://playertest.longtailvideo.com/adaptive/boxee/playlist.m3u8',
description: 'PDTs with large gaps following discontinuities'
description: 'PDTs with large gaps following discontinuities',
abr: false
},
pdtBadValues: {
url: 'https://playertest.longtailvideo.com/adaptive/progdatime/playlist2.m3u8',
description: 'PDTs with bad values'
description: 'PDTs with bad values',
abr: false
},
pdtOneValue: {
url: 'https://playertest.longtailvideo.com/adaptive/aviion/manifest.m3u8',
description: 'One PDT, no discontinuities'
description: 'One PDT, no discontinuities',
abr: false
},
noTrackIntersection: {
noTrackIntersection: createTestStreamWithConfig({
url: 'https://s3.amazonaws.com/qa.jwplayer.com/~alex/123633/new_master.m3u8',
description: 'Audio/video track PTS values do not intersect; 10 second start gap',
abr: false
}, {
avBufferOffset: 10.5
},
}),
// altAudioNoVideoCodecSignaled: {
// url: 'https://d35u71x3nb8v2y.cloudfront.net/4b711b97-513c-4d36-ad29-298ab23a2e5e/3cbf1114-b2f4-4320-afb3-f0f7eeeb8630/playlist.m3u8',
// description: 'Alternate audio track, but no video codec is signaled in the master manifest'
// },
altAudioAndTracks: {
url: 'https://wowzaec2demo.streamlock.net/vod-multitrack/_definst_/smil:ElephantsDream/elephantsdream2.smil/playlist.m3u',
description: 'Alternate audio tracks, and multiple VTT tracks'
description: 'Alternate audio tracks, and multiple VTT tracks',
abr: true
},
altAudioAudioOnly: {
url: 'https://playertest.longtailvideo.com/adaptive/alt-audio-no-video/sintel/playlist.m3u8',
description: 'Audio only with alternate audio track (Sintel)'
description: 'Audio only with alternate audio track (Sintel)',
abr: false
},
altAudioMultiAudioOnly: {
url: 'https://playertest.longtailvideo.com/adaptive/alt-audio-no-video/angel-one.m3u8',
description: 'Audio only with multiple alternate audio tracks (Angel One)'
description: 'Audio only with multiple alternate audio tracks (Angel One)',
abr: false
},
muxedFmp4: {
url: 'https://s3.amazonaws.com/qa.jwplayer.com/hlsjs/muxed-fmp4/hls.m3u8',
description: 'Muxed av fmp4 - appended to "audiovideo" SourceBuffer'
description: 'Muxed av fmp4 - appended to "audiovideo" SourceBuffer',
abr: false
},
altAudioWithPdtAndStartGap: {
url: 'https://playertest.longtailvideo.com/adaptive/hls-test-streams/test-audio-pdt/playlist.m3u8',
Expand Down

0 comments on commit 98aabf9

Please sign in to comment.