Files
moeplayer/source/org/mangui/hls/loader/AltAudioLevelLoader.as
T
2020-09-17 18:40:01 +08:00

169 lines
7.5 KiB
ActionScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.loader {
import flash.events.ErrorEvent;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.utils.clearTimeout;
import flash.utils.getTimer;
import flash.utils.setTimeout;
import org.mangui.hls.constant.HLSPlayStates;
import org.mangui.hls.event.HLSError;
import org.mangui.hls.event.HLSEvent;
import org.mangui.hls.event.HLSLoadMetrics;
import org.mangui.hls.HLS;
import org.mangui.hls.HLSSettings;
import org.mangui.hls.model.AudioTrack;
import org.mangui.hls.model.Fragment;
import org.mangui.hls.model.Level;
import org.mangui.hls.playlist.AltAudioTrack;
import org.mangui.hls.playlist.Manifest;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class AltAudioLevelLoader {
/** Reference to the hls framework controller. **/
private var _hls : HLS;
/** Link to the M3U8 file. **/
private var _url : String;
/** Timeout ID for reloading live playlists. **/
private var _timeoutID : uint;
/** last reload manifest time **/
private var _reloadPlaylistTimer : uint;
/** current audio level **/
private var _currentTrack : int;
/** reference to manifest being loaded **/
private var _manifestLoading : Manifest;
/** is this loader closed **/
private var _closed : Boolean = false;
/* playlist retry timeout */
private var _retryTimeout : Number;
private var _retryCount : int;
/** Setup the loader. **/
public function AltAudioLevelLoader(hls : HLS) {
_hls = hls;
_hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
_hls.addEventListener(HLSEvent.AUDIO_TRACK_SWITCH, _audioTrackSwitchHandler);
};
public function dispose() : void {
_close();
_hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
_hls.removeEventListener(HLSEvent.AUDIO_TRACK_SWITCH, _audioTrackSwitchHandler);
}
/** Loading failed; return errors. **/
private function _errorHandler(event : ErrorEvent) : void {
var txt : String;
var code : int;
if (event is SecurityErrorEvent) {
code = HLSError.MANIFEST_LOADING_CROSSDOMAIN_ERROR;
txt = "Cannot load M3U8: crossdomain access denied:" + event.text;
} else if (event is IOErrorEvent && (HLSSettings.manifestLoadMaxRetry == -1 || _retryCount < HLSSettings.manifestLoadMaxRetry)) {
CONFIG::LOGGING {
Log.warn("I/O Error while trying to load Playlist, retry in " + _retryTimeout + " ms");
}
_timeoutID = setTimeout(_loadAudioLevelPlaylist, _retryTimeout);
/* exponential increase of retry timeout, capped to manifestLoadMaxRetryTimeout */
_retryTimeout = Math.min(HLSSettings.manifestLoadMaxRetryTimeout, 2 * _retryTimeout);
_retryCount++;
return;
} else {
code = HLSError.MANIFEST_LOADING_IO_ERROR;
txt = "Cannot load M3U8: " + event.text;
}
var hlsError : HLSError = new HLSError(code, _url, txt);
_hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
};
/** parse a playlist **/
private function _parseAudioPlaylist(string : String, url : String, level : int, metrics : HLSLoadMetrics) : void {
if (string != null && string.length != 0) {
CONFIG::LOGGING {
Log.debug("audio level " + level + " playlist:\n" + string);
}
var frags : Vector.<Fragment> = Manifest.getFragments(string, url, level);
// set fragment and update sequence number range
var audioTrack : AudioTrack = _hls.audioTracks[_currentTrack];
var audioLevel : Level = audioTrack.level;
if(audioLevel == null) {
audioLevel = audioTrack.level = new Level();
}
audioLevel.updateFragments(frags);
audioLevel.targetduration = Manifest.getTargetDuration(string);
// if stream is live, arm a timer to periodically reload playlist
if (!Manifest.hasEndlist(string)) {
var timeout : int = Math.max(100, _reloadPlaylistTimer + 1000 * audioLevel.averageduration - getTimer());
CONFIG::LOGGING {
Log.debug("Alt Audio Level Live Playlist parsing finished: reload in " + timeout + " ms");
}
_timeoutID = setTimeout(_loadAudioLevelPlaylist, timeout);
}
}
metrics.id = audioLevel.start_seqnum;
metrics.id2 = audioLevel.end_seqnum;
_hls.dispatchEvent(new HLSEvent(HLSEvent.AUDIO_LEVEL_LOADED, metrics));
_manifestLoading = null;
};
/** load/reload active M3U8 playlist **/
private function _loadAudioLevelPlaylist() : void {
if (_closed) {
return;
}
_reloadPlaylistTimer = getTimer();
var altAudioTrack : AltAudioTrack = _hls.altAudioTracks[_hls.audioTracks[_currentTrack].id];
_manifestLoading = new Manifest();
_manifestLoading.loadPlaylist(_hls,altAudioTrack.url, _parseAudioPlaylist, _errorHandler, _currentTrack, _hls.type, HLSSettings.flushLiveURLCache);
_hls.dispatchEvent(new HLSEvent(HLSEvent.AUDIO_LEVEL_LOADING, _currentTrack));
};
/** When audio track switch occurs, assess the need of loading audio level playlist **/
private function _audioTrackSwitchHandler(event : HLSEvent) : void {
_currentTrack = event.audioTrack;
var audioTrack : AudioTrack = _hls.audioTracks[_currentTrack];
if (audioTrack.source == AudioTrack.FROM_PLAYLIST) {
var altAudioTrack : AltAudioTrack = _hls.altAudioTracks[audioTrack.id];
if (altAudioTrack.url && audioTrack.level == null) {
CONFIG::LOGGING {
Log.debug("switch to audio track " + _currentTrack + ", load Playlist");
}
_retryTimeout = 1000;
_retryCount = 0;
_closed = false;
if(_manifestLoading) {
_manifestLoading.close();
_manifestLoading = null;
}
clearTimeout(_timeoutID);
_timeoutID = setTimeout(_loadAudioLevelPlaylist, 0);
}
}
};
private function _close() : void {
CONFIG::LOGGING {
Log.debug("cancel any audio level load in progress");
}
_closed = true;
clearTimeout(_timeoutID);
try {
if (_manifestLoading) {
_manifestLoading.close();
}
} catch(e : Error) {
}
}
/** When the framework idles out, stop reloading manifest **/
private function _stateHandler(event : HLSEvent) : void {
if (event.state == HLSPlayStates.IDLE) {
_close();
}
};
}
}