no commit message

This commit is contained in:
niandeng
2020-09-17 18:40:01 +08:00
parent 4382915d09
commit d957abceaa
283 changed files with 86935 additions and 63 deletions
@@ -0,0 +1,23 @@
/* 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.osmf.plugins {
import flash.display.Sprite;
import flash.system.Security;
import org.osmf.media.PluginInfo;
public class HLSDynamicPlugin extends Sprite {
private var _pluginInfo : PluginInfo;
public function HLSDynamicPlugin() {
super();
Security.allowDomain("*");
_pluginInfo = new HLSPlugin();
}
public function get pluginInfo() : PluginInfo {
return _pluginInfo;
}
}
}
@@ -0,0 +1,180 @@
/* 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.osmf.plugins {
import flash.media.Video;
import flash.net.NetStream;
import org.mangui.hls.HLS;
import org.mangui.hls.event.HLSEvent;
import org.mangui.osmf.plugins.loader.HLSNetLoader;
import org.mangui.osmf.plugins.traits.*;
import org.mangui.osmf.plugins.utils.ErrorManager;
import org.osmf.media.LoadableElementBase;
import org.osmf.media.MediaElement;
import org.osmf.media.MediaResourceBase;
import org.osmf.media.videoClasses.VideoSurface;
import org.osmf.net.NetStreamAudioTrait;
import org.osmf.net.StreamType;
import org.osmf.net.StreamingURLResource;
import org.osmf.traits.AudioTrait;
import org.osmf.traits.BufferTrait;
import org.osmf.traits.DVRTrait;
import org.osmf.traits.LoadTrait;
import org.osmf.traits.LoaderBase;
import org.osmf.traits.MediaTraitType;
import org.osmf.traits.PlayTrait;
import org.osmf.traits.SeekTrait;
import org.osmf.traits.TimeTrait;
import org.osmf.utils.OSMFSettings;
import org.osmf.events.MediaError;
import org.osmf.events.MediaErrorEvent;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSMediaElement extends LoadableElementBase {
private var _hls : HLS;
private var _stream : NetStream;
private var _defaultduration : Number;
private var videoSurface : VideoSurface;
private var _smoothing : Boolean;
public function HLSMediaElement(resource : MediaResourceBase, hls : HLS, duration : Number) {
_hls = hls;
_defaultduration = duration;
super(resource, new HLSNetLoader(hls));
_hls.addEventListener(HLSEvent.ERROR, _errorHandler);
}
protected function createVideo() : Video {
return new Video();
}
override protected function createLoadTrait(resource : MediaResourceBase, loader : LoaderBase) : LoadTrait {
return new HLSNetStreamLoadTrait(_hls, _defaultduration, loader, resource);
}
override protected function processLoadingState() : void {
CONFIG::LOGGING {
Log.debug("HLSMediaElement:processLoadingState");
}
}
override protected function processReadyState() : void {
CONFIG::LOGGING {
Log.debug("HLSMediaElement:processReadyState");
}
initTraits();
}
override protected function processUnloadingState() : void {
CONFIG::LOGGING {
Log.debug("HLSMediaElement:processUnloadingState");
}
removeTrait(MediaTraitType.AUDIO);
removeTrait(MediaTraitType.BUFFER);
removeTrait(MediaTraitType.TIME);
removeTrait(MediaTraitType.DISPLAY_OBJECT);
removeTrait(MediaTraitType.PLAY);
removeTrait(MediaTraitType.SEEK);
removeTrait(MediaTraitType.DYNAMIC_STREAM);
removeTrait(MediaTraitType.DVR);
removeTrait(MediaTraitType.ALTERNATIVE_AUDIO);
if (videoSurface != null) {
videoSurface.attachNetStream(null);
videoSurface = null;
}
}
/**
* Specifies whether the video should be smoothed (interpolated) when it is scaled.
* For smoothing to work, the runtime must be in high-quality mode (the default).
* The default value is false (no smoothing). Set this property to true to take
* advantage of mipmapping image optimization.
*
* @see flash.media.Video
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
**/
public function get smoothing() : Boolean {
return _smoothing;
}
public function set smoothing(value : Boolean) : void {
_smoothing = value;
if (videoSurface != null) {
videoSurface.smoothing = value;
}
}
private function initTraits() : void {
_stream = _hls.stream;
// Set the video's dimensions so that it doesn't appear at the wrong size.
// We'll set the correct dimensions once the metadata is loaded. (FM-206)
videoSurface = new VideoSurface(OSMFSettings.enableStageVideo && OSMFSettings.supportsStageVideo, createVideo);
videoSurface.smoothing = true;
videoSurface.deblocking = 1;
videoSurface.width = videoSurface.height = 0;
videoSurface.attachNetStream(_stream);
var audioTrait : AudioTrait = new NetStreamAudioTrait(_stream);
addTrait(MediaTraitType.AUDIO, audioTrait);
var ccTrait : HLSClosedCaptionsTrait = new HLSClosedCaptionsTrait(_hls);
addTrait(HLSMediaTraitType.CLOSED_CAPTIONS, ccTrait);
var bufferTrait : BufferTrait = new HLSBufferTrait(_hls);
addTrait(MediaTraitType.BUFFER, bufferTrait);
var timeTrait : TimeTrait = new HLSTimeTrait(_hls, _defaultduration);
addTrait(MediaTraitType.TIME, timeTrait);
var displayObjectTrait : HLSDisplayObjectTrait = new HLSDisplayObjectTrait(_hls,videoSurface, NaN, NaN);
addTrait(MediaTraitType.DISPLAY_OBJECT, displayObjectTrait);
// setup seek trait
var seekTrait : SeekTrait = new HLSSeekTrait(_hls, timeTrait);
addTrait(MediaTraitType.SEEK, seekTrait);
if (_hls.levels.length > 1) {
// setup dynamic stream trait
var dsTrait : HLSDynamicStreamTrait = new HLSDynamicStreamTrait(_hls);
addTrait(MediaTraitType.DYNAMIC_STREAM, dsTrait);
}
// retrieve stream type
var streamType : String = (resource as StreamingURLResource).streamType;
if (streamType == StreamType.DVR) {
// add DvrTrait
var dvrTrait : DVRTrait = new DVRTrait(true);
addTrait(MediaTraitType.DVR, dvrTrait);
}
// setup drm trait
// addTrait(MediaTraitType.DRM, drmTrait);
// setup alternative audio trait
var alternateAudioTrait : HLSAlternativeAudioTrait = new HLSAlternativeAudioTrait(_hls, this as MediaElement);
addTrait(MediaTraitType.ALTERNATIVE_AUDIO, alternateAudioTrait);
var playTrait : PlayTrait = new HLSPlayTrait(_hls);
addTrait(MediaTraitType.PLAY, playTrait);
}
private function _errorHandler(event : HLSEvent) : void {
var errorCode : int = ErrorManager.getMediaErrorCode(event);
var errorMsg : String = ErrorManager.getMediaErrorMessage(event);
CONFIG::LOGGING {
Log.warn("HLS Error event received, dispatching MediaError " + errorCode + "," + errorMsg);
}
dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(errorCode, errorMsg)));
}
}
}
@@ -0,0 +1,61 @@
/* 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.osmf.plugins {
import org.mangui.hls.utils.Params2Settings;
import org.mangui.osmf.plugins.loader.HLSLoaderBase;
import org.mangui.osmf.plugins.loader.HLSLoadFromDocumentElement;
import org.osmf.media.MediaElement;
import org.osmf.media.MediaFactoryItem;
import org.osmf.media.MediaFactoryItemType;
import org.osmf.media.MediaResourceBase;
import org.osmf.media.PluginInfo;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSPlugin extends PluginInfo {
public function HLSPlugin(items : Vector.<MediaFactoryItem>=null, elementCreatedNotification : Function = null) {
items = new Vector.<MediaFactoryItem>();
items.push(new MediaFactoryItem('org.mangui.osmf.plugins.HLSPlugin', canHandleResource, createMediaElement, MediaFactoryItemType.STANDARD));
super(items, elementCreatedNotification);
}
/**
* Called from super class when plugin has been initialized with the MediaFactory from which it was loaded.
* Used for customize HLSSettings with values provided in resource metadata (that was set eg. in flash vars)
*
* @param resource Provides acces to the resource used to load the plugin and any associated metadata
*
*/
override public function initializePlugin(resource : MediaResourceBase) : void {
CONFIG::LOGGING {
Log.debug("OSMF HLSPlugin init");
}
metadataParamsToHLSSettings(resource);
}
private function canHandleResource(resource : MediaResourceBase) : Boolean {
return HLSLoaderBase.canHandle(resource);
}
private function createMediaElement() : MediaElement {
return new HLSLoadFromDocumentElement(null, new HLSLoaderBase());
}
private function metadataParamsToHLSSettings(resource : MediaResourceBase) : void {
if (resource == null) {
return;
}
var metadataNamespaceURLs : Vector.<String> = resource.metadataNamespaceURLs;
// set all legal params values to HLSSetings properties
for each (var key : String in metadataNamespaceURLs) {
Params2Settings.set(key, resource.getMetadataValue(key));
}
}
}
}
@@ -0,0 +1,135 @@
/* 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.osmf.plugins.loader {
import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait;
import org.osmf.events.LoadEvent;
import org.osmf.media.MediaResourceBase;
import org.osmf.traits.LoadState;
import org.osmf.traits.LoadTrait;
import org.osmf.traits.LoaderBase;
import org.osmf.traits.MediaTraitType;
import org.osmf.utils.OSMFStrings;
import org.osmf.elements.ProxyElement;
/**
* LoadFromDocumentElement is the base class for MediaElements that load documents
* that contain information about the real MediaElement to expose. For example, the
* contents of a SMIL document can be represented as a MediaElement, but until the
* SMIL document is loaded, it's impossible to know what MediaElement the SMIL
* document represents.
*
* <p>Because of the dynamic nature of this operation, a LoadFromDocumentElement extends
* ProxyElement. When the document has been loaded, this class will set the
* <code>proxiedElement</code> property to the MediaElement that was generated from
* the document.</p>
*
* <p>This is an abstract base class, and must be subclassed.</p>
*
* <p>Note: It is simplest to use the MediaPlayer class in conjunction with subclasses of
* the LoadFromDocumentElement. If you work directly with a LoadFromDocumentElement, then
* it's important to listen for events related to traits being added and removed. If you
* use the MediaPlayer class with a LoadFromDocumentElement, then the MediaPlayer will
* automatically listen for these events for you.</p>
*
* @see org.osmf.elements.ProxyElement
* @see org.osmf.media.MediaElement
* @see org.osmf.media.MediaPlayer
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public class HLSLoadFromDocumentElement extends ProxyElement {
/**
* Constructor.
*
* @param resource The resource associated with this element.
* @param loader The LoaderBase used to load the resource. Cannot be null.
*
* @throws ArgumentError If loader is null.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function HLSLoadFromDocumentElement(resource : MediaResourceBase = null, loader : LoaderBase = null) {
super(null);
this.loader = loader;
this.resource = resource;
if (loader == null) {
throw new ArgumentError(OSMFStrings.getString(OSMFStrings.NULL_PARAM));
}
}
/**
* @private
*
* Overriding is necessary because there is a null proxiedElement.
*/
override public function set resource(value : MediaResourceBase) : void {
if (_resource != value && value != null) {
_resource = value;
loadTrait = new LoadFromDocumentLoadTrait(loader, resource);
loadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onLoadStateChange, false, int.MAX_VALUE);
if (super.getTrait(MediaTraitType.LOAD) != null) {
super.removeTrait(MediaTraitType.LOAD);
}
super.addTrait(MediaTraitType.LOAD, loadTrait);
}
}
/**
* @private
*/
override public function get resource() : MediaResourceBase {
return _resource;
}
private function onLoadStateChange(event : LoadEvent) : void {
if (event.loadState == LoadState.READY) {
event.stopImmediatePropagation();
// Remove the temporary LoadTrait.
removeTrait(MediaTraitType.LOAD);
// Tell the soon-to-be proxied element to load itself.
// Note that we do this before setting it as the proxied
// element, so as to avoid dispatching a second LOADING
// event.
// Set up a listener so that we can prevent the dispatch
// of a second LOADING event.
var proxiedLoadTrait : LoadTrait = loadTrait.mediaElement.getTrait(MediaTraitType.LOAD) as LoadTrait;
proxiedLoadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onProxiedElementLoadStateChange, false, int.MAX_VALUE);
// Expose the proxied element.
proxiedElement = loadTrait.mediaElement;
// If our proxied element hasn't started loading yet, we should
// initiate the load.
if (proxiedLoadTrait.loadState == LoadState.UNINITIALIZED) {
proxiedLoadTrait.load();
}
function onProxiedElementLoadStateChange(event : LoadEvent) : void {
if (event.loadState == LoadState.LOADING) {
event.stopImmediatePropagation();
} else {
proxiedLoadTrait.removeEventListener(LoadEvent.LOAD_STATE_CHANGE, onProxiedElementLoadStateChange);
}
}
}
}
private var _resource : MediaResourceBase;
private var loadTrait : LoadFromDocumentLoadTrait;
private var loader : LoaderBase;
}
}
@@ -0,0 +1,155 @@
/* 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.osmf.plugins.loader {
import org.mangui.hls.HLS;
import org.mangui.hls.event.HLSEvent;
import org.mangui.hls.model.Level;
import org.mangui.hls.constant.HLSTypes;
import org.mangui.osmf.plugins.HLSMediaElement;
import org.mangui.osmf.plugins.utils.ErrorManager;
import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait;
import org.osmf.events.MediaError;
import org.osmf.events.MediaErrorEvent;
import org.osmf.media.MediaElement;
import org.osmf.media.MediaResourceBase;
import org.osmf.media.URLResource;
import org.osmf.net.DynamicStreamingItem;
import org.osmf.net.DynamicStreamingResource;
import org.osmf.net.StreamType;
import org.osmf.net.StreamingURLResource;
import org.osmf.traits.LoadState;
import org.osmf.traits.LoadTrait;
import org.osmf.traits.LoaderBase;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
/**
* Loader for .m3u8 playlist file.
* Works like a F4MLoader
*/
public class HLSLoaderBase extends LoaderBase {
private var _loadTrait : LoadTrait;
/** Reference to the framework. **/
private var _hls : HLS = null;
public function HLSLoaderBase() {
super();
}
public static function canHandle(resource : MediaResourceBase) : Boolean {
if (resource !== null && resource is URLResource) {
var urlResource : URLResource = URLResource(resource);
// check for m3u/m3u8
if (urlResource.url.search(/(https?|file)\:\/\/.*?\m3u(\?.*)?/i) !== -1) {
return true;
}
var contentType : Object = urlResource.getMetadataValue("content-type");
if (contentType && contentType is String) {
// If the filename doesn't include a .m3u or m3u8 extension, but
// explicit content-type metadata is found on the
// URLResource, we can handle it. Must be either of:
// - "application/x-mpegURL"
// - "vnd.apple.mpegURL"
if ((contentType as String).search(/(application\/x-mpegURL|vnd.apple.mpegURL)/i) !== -1) {
return true;
}
}
}
return false;
}
override public function canHandleResource(resource : MediaResourceBase) : Boolean {
return canHandle(resource);
}
override protected function executeLoad(loadTrait : LoadTrait) : void {
_loadTrait = loadTrait;
updateLoadTrait(loadTrait, LoadState.LOADING);
if (_hls != null) {
_hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
_hls.removeEventListener(HLSEvent.ERROR, _errorHandler);
_hls.dispose();
_hls = null;
}
_hls = new HLS();
_hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
_hls.addEventListener(HLSEvent.ERROR, _errorHandler);
/* load playlist */
_hls.load(URLResource(loadTrait.resource).url);
}
override protected function executeUnload(loadTrait : LoadTrait) : void {
updateLoadTrait(loadTrait, LoadState.UNINITIALIZED);
}
/** Update video A/R on manifest load. **/
private function _manifestLoadedHandler(event : HLSEvent) : void {
var resource : MediaResourceBase = URLResource(_loadTrait.resource);
// retrieve stream type
var streamType : String = (resource as StreamingURLResource).streamType;
if (streamType == null || streamType == StreamType.LIVE_OR_RECORDED) {
if (_hls.type == HLSTypes.LIVE) {
streamType = StreamType.LIVE;
} else {
streamType = StreamType.RECORDED;
}
}
var levels : Vector.<Level> = _hls.levels;
var nbLevel : int = levels.length;
var urlRes : URLResource = resource as URLResource;
var dynamicRes : DynamicStreamingResource = new DynamicStreamingResource(urlRes.url);
var streamItems : Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();
for (var i : int = 0; i < nbLevel; i++) {
if (levels[i].width) {
streamItems.push(new DynamicStreamingItem(level2label(levels[i]), levels[i].bitrate / 1024, levels[i].width, levels[i].height));
} else {
streamItems.push(new DynamicStreamingItem(level2label(levels[i]), levels[i].bitrate / 1024));
}
}
dynamicRes.streamItems = streamItems;
dynamicRes.initialIndex = _hls.startLevel;
resource = dynamicRes;
// set Stream Type
var streamUrlRes : StreamingURLResource = resource as StreamingURLResource;
streamUrlRes.streamType = streamType;
try {
var loadedElem : MediaElement = new HLSMediaElement(resource, _hls, event.levels[_hls.startLevel].duration);
LoadFromDocumentLoadTrait(_loadTrait).mediaElement = loadedElem;
updateLoadTrait(_loadTrait, LoadState.READY);
} catch(e : Error) {
updateLoadTrait(_loadTrait, LoadState.LOAD_ERROR);
_loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(e.errorID, e.message)));
}
};
private function level2label(level : Level) : String {
if (level.name) {
return level.name;
} else {
if (level.height) {
return(level.height + 'p / ' + Math.round(level.bitrate / 1024) + 'kb');
} else {
return(Math.round(level.bitrate / 1024) + 'kb');
}
}
}
private function _errorHandler(event : HLSEvent) : void {
var errorCode : int = ErrorManager.getMediaErrorCode(event);
var errorMsg : String = ErrorManager.getMediaErrorMessage(event);
CONFIG::LOGGING {
Log.warn("HLS Error event received, dispatching MediaError " + errorCode + "," + errorMsg);
}
updateLoadTrait(_loadTrait, LoadState.LOAD_ERROR);
_loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(errorCode, errorMsg)));
}
}
}
@@ -0,0 +1,28 @@
/* 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.osmf.plugins.loader {
import flash.net.NetStream;
import flash.net.NetConnection;
import org.osmf.net.NetLoader;
import org.osmf.media.URLResource;
import org.mangui.hls.HLS;
public class HLSNetLoader extends NetLoader {
private var _hls : HLS;
private var _connection : NetConnection;
private var _resource : URLResource;
public function HLSNetLoader(hls : HLS) {
_hls = hls;
super();
}
override protected function createNetStream(connection : NetConnection, resource : URLResource) : NetStream {
_connection = connection;
_resource = resource;
return _hls.stream;
}
}
}
@@ -0,0 +1,119 @@
/* 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.osmf.plugins.traits {
import org.mangui.hls.HLS;
import org.mangui.hls.model.AudioTrack;
import org.mangui.hls.event.HLSEvent;
import org.osmf.events.AlternativeAudioEvent;
import org.osmf.media.MediaElement;
import org.osmf.net.StreamingItem;
import org.osmf.traits.AlternativeAudioTrait;
import org.osmf.utils.OSMFStrings;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSAlternativeAudioTrait extends AlternativeAudioTrait {
private var _hls : HLS;
private var _media : MediaElement;
private var _audioTrackList : Vector.<AudioTrack>;
private var _numAlternativeAudioStreams : int;
private var _activeTransitionIndex : int = DEFAULT_TRANSITION_INDEX;
private var _lastTransitionIndex : int = INVALID_TRANSITION_INDEX;
public function HLSAlternativeAudioTrait(hls : HLS, media : MediaElement) {
CONFIG::LOGGING {
Log.debug("HLSAlternativeAudioTrait()");
}
_hls = hls;
_audioTrackList = _hls.audioTracks;
_numAlternativeAudioStreams = _audioTrackList.length - 1;
super(_numAlternativeAudioStreams);
_media = media;
_hls.addEventListener(HLSEvent.AUDIO_TRACK_SWITCH, _audioTrackChangedHandler);
_hls.addEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTrackListChangedHandler);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSAlternativeAudioTrait:dispose");
}
_hls.removeEventListener(HLSEvent.AUDIO_TRACK_SWITCH, _audioTrackChangedHandler);
_hls.removeEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTrackListChangedHandler);
super.dispose();
}
override public function get numAlternativeAudioStreams() : int {
CONFIG::LOGGING {
Log.debug("HLSAlternativeAudioTrait:numAlternativeAudioStreams:" + _numAlternativeAudioStreams);
}
return _numAlternativeAudioStreams;
}
override public function getItemForIndex(index : int) : StreamingItem {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:getItemForIndex(" + index + ")");
}
if (index <= INVALID_TRANSITION_INDEX || index >= numAlternativeAudioStreams) {
throw new RangeError(OSMFStrings.getString(OSMFStrings.ALTERNATIVEAUDIO_INVALID_INDEX));
}
if (index == DEFAULT_TRANSITION_INDEX) {
return null;
}
var name : String = _audioTrackList[index + 1].title;
var streamItem : StreamingItem = new StreamingItem("AUDIO", name);
streamItem.info.label = name;
return streamItem;
}
override protected function endSwitching(index : int) : void {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:endSwitching(" + index + ")");
}
if (switching) {
executeSwitching(_indexToSwitchTo);
}
super.endSwitching(index);
}
protected function executeSwitching(indexToSwitchTo : int) : void {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:executeSwitching(" + indexToSwitchTo + ")");
}
if (_lastTransitionIndex != indexToSwitchTo) {
_activeTransitionIndex = indexToSwitchTo;
_hls.audioTrack = indexToSwitchTo + 1;
}
}
private function _audioTrackChangedHandler(event : HLSEvent) : void {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:_audioTrackChangedHandler");
}
setSwitching(false, _activeTransitionIndex);
_lastTransitionIndex = _activeTransitionIndex;
}
private function _audioTrackListChangedHandler(event : HLSEvent) : void {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:_audioTrackListChangedHandler");
}
_audioTrackList = _hls.audioTracks;
if (_audioTrackList.length > 0) {
// try to change default Audio Track Title for GrindPlayer ...
if (_audioTrackList[0].title.indexOf("TS/") == -1) {
CONFIG::LOGGING {
Log.debug("default audio track title:" + _audioTrackList[0].title);
}
_media.resource.addMetadataValue("defaultAudioLabel", _audioTrackList[0].title);
}
}
_numAlternativeAudioStreams = _audioTrackList.length - 1;
if (_numAlternativeAudioStreams > 0) {
dispatchEvent(new AlternativeAudioEvent(AlternativeAudioEvent.NUM_ALTERNATIVE_AUDIO_STREAMS_CHANGE, false, false, false));
}
}
}
}
@@ -0,0 +1,57 @@
/* 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.osmf.plugins.traits {
import org.mangui.hls.HLS;
import org.mangui.hls.event.HLSEvent;
import org.mangui.hls.constant.HLSPlayStates;
import org.osmf.traits.BufferTrait;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSBufferTrait extends BufferTrait {
private var _hls : HLS;
public function HLSBufferTrait(hls : HLS) {
CONFIG::LOGGING {
Log.debug("HLSBufferTrait()");
}
super();
_hls = hls;
_hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateChangedHandler);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSBufferTrait:dispose");
}
_hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateChangedHandler);
super.dispose();
}
override public function get bufferLength() : Number {
return _hls.stream.bufferLength;
}
/** state changed handler **/
private function _stateChangedHandler(event : HLSEvent) : void {
switch(event.state) {
case HLSPlayStates.PLAYING_BUFFERING:
case HLSPlayStates.PAUSED_BUFFERING:
CONFIG::LOGGING {
Log.debug("HLSBufferTrait:_stateChangedHandler:setBuffering(true)");
}
setBuffering(true);
break;
default:
CONFIG::LOGGING {
Log.debug("HLSBufferTrait:_stateChangedHandler:setBuffering(false)");
}
setBuffering(false);
break;
}
}
}
}
@@ -0,0 +1,9 @@
package org.mangui.osmf.plugins.traits
{
public final class HLSClosedCaptionsState
{
public static const YES:String = "yes";
public static const NO:String = "no";
public static const UNKNOWN:String = "unknown";
}
}
@@ -0,0 +1,58 @@
/* 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.osmf.plugins.traits {
import org.mangui.hls.HLS;
import org.mangui.hls.event.HLSEvent;
import org.mangui.hls.model.Level;
import org.osmf.traits.MediaTraitBase;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSClosedCaptionsTrait extends MediaTraitBase {
private var _hls : HLS;
private var _hasClosedCapations : String;
public function HLSClosedCaptionsTrait(hls : HLS, closed_captions : String = HLSClosedCaptionsState.UNKNOWN) {
CONFIG::LOGGING {
Log.debug("HLSClosedCaptionsTrait()");
}
super(HLSMediaTraitType.CLOSED_CAPTIONS);
_hasClosedCapations = closed_captions;
_hls = hls;
_hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSClosedCaptionsTrait:dispose");
}
_hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
super.dispose();
}
public function hasClosedCaptions() : String {
return _hasClosedCapations;
}
/** Update playback position/duration **/
private function _levelSwitchHandler(event : HLSEvent) : void {
var cc : String = (_hls.levels[event.level] as Level).closed_captions;
if (cc && cc === "NONE")
{
// manifest told us to ignore any 608/708 binary
_hasClosedCapations = HLSClosedCaptionsState.NO;
}
else
{
_hasClosedCapations = HLSClosedCaptionsState.UNKNOWN;
}
// YES only happens for WebVTT, which isn't supported.
}
}
}
@@ -0,0 +1,65 @@
/* 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.osmf.plugins.traits {
import flash.display.DisplayObject;
import flash.display.Stage;
import flash.events.Event;
import org.mangui.hls.HLS;
import org.osmf.media.videoClasses.VideoSurface;
import org.osmf.traits.DisplayObjectTrait;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSDisplayObjectTrait extends DisplayObjectTrait {
private var videoSurface : VideoSurface;
private var _hls : HLS;
public function HLSDisplayObjectTrait(hls : HLS, videoSurface : DisplayObject, mediaWidth : int = 0, mediaHeight : int = 0) {
CONFIG::LOGGING {
Log.debug("HLSDisplayObjectTrait()");
}
_hls = hls;
super(videoSurface, mediaWidth, mediaHeight);
this.videoSurface = videoSurface as VideoSurface;
if (this.videoSurface is VideoSurface)
this.videoSurface.addEventListener(Event.ADDED_TO_STAGE, onStage);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSDisplayObjectTrait:dispose");
}
videoSurface.removeEventListener(Event.ENTER_FRAME, onFrame);
super.dispose();
}
private function onStage(event : Event) : void {
_hls.stage = event.target.stage as Stage;
videoSurface.removeEventListener(Event.ADDED_TO_STAGE, onStage);
videoSurface.addEventListener(Event.ENTER_FRAME, onFrame);
}
private function onFrame(event : Event) : void {
var newWidth : int = videoSurface.videoWidth;
var newHeight : int = videoSurface.videoHeight;
if (newWidth != 0 && newHeight != 0 && (newWidth != mediaWidth || newHeight != mediaHeight)) {
// If there is no layout, set as no scale.
if (videoSurface.width == 0 && videoSurface.height == 0) {
videoSurface.width = newWidth;
videoSurface.height = newHeight;
}
CONFIG::LOGGING {
Log.info("HLSDisplayObjectTrait:setMediaSize(" + newWidth + "," + newHeight + ")");
}
setMediaSize(newWidth, newHeight);
}
// videoSurface.removeEventListener(Event.ENTER_FRAME, onFrame);
}
}
}
@@ -0,0 +1,86 @@
/* 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.osmf.plugins.traits {
import org.mangui.hls.HLS;
import org.mangui.hls.event.HLSEvent;
import org.osmf.traits.DynamicStreamTrait;
import org.osmf.utils.OSMFStrings;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSDynamicStreamTrait extends DynamicStreamTrait {
private var _hls : HLS;
public function HLSDynamicStreamTrait(hls : HLS) {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait()");
}
_hls = hls;
_hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
super(true, _hls.startLevel, hls.levels.length);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:dispose");
}
_hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
super.dispose();
}
override public function getBitrateForIndex(index : int) : Number {
if (index > numDynamicStreams - 1 || index < 0) {
throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
}
var bitrate : Number = _hls.levels[index].bitrate / 1000;
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:getBitrateForIndex(" + index + ")=" + bitrate);
}
return bitrate;
}
override public function switchTo(index : int) : void {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:switchTo(" + index + ")/max:" + maxAllowedIndex);
}
if (index < 0 || index > maxAllowedIndex) {
throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
}
autoSwitch = false;
if (!switching) {
setSwitching(true, index);
}
}
override protected function autoSwitchChangeStart(value : Boolean) : void {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:autoSwitchChangeStart:" + value);
}
if (value == true && _hls.autoLevel == false) {
_hls.nextLevel = -1;
}
}
override protected function switchingChangeStart(newSwitching : Boolean, index : int) : void {
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:switchingChangeStart(newSwitching/index):" + newSwitching + "/" + index);
}
if (newSwitching) {
_hls.currentLevel = index;
}
}
/** Update playback position/duration **/
private function _levelSwitchHandler(event : HLSEvent) : void {
var newLevel : int = event.level;
CONFIG::LOGGING {
Log.debug("HLSDynamicStreamTrait:_qualitySwitchHandler:" + newLevel);
}
setCurrentIndex(newLevel);
setSwitching(false, newLevel);
};
}
}
@@ -0,0 +1,7 @@
package org.mangui.osmf.plugins.traits
{
public final class HLSMediaTraitType
{
public static const CLOSED_CAPTIONS:String = "hlsClosedCaptions";
}
}
@@ -0,0 +1,71 @@
/* 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.osmf.plugins.traits {
import org.mangui.hls.HLS;
import org.mangui.hls.event.HLSEvent;
import org.osmf.media.MediaResourceBase;
import org.osmf.net.NetStreamLoadTrait;
import org.osmf.traits.LoaderBase;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSNetStreamLoadTrait extends NetStreamLoadTrait {
private var _hls : HLS;
private var _timeLoaded : Number;
private var _timeTotal : Number;
public function HLSNetStreamLoadTrait(hls : HLS, duration : Number, loader : LoaderBase, resource : MediaResourceBase) {
CONFIG::LOGGING {
Log.debug("HLSNetStreamLoadTrait()");
}
super(loader, resource);
_hls = hls;
_timeLoaded = 0;
_timeTotal = duration;
super.netStream = _hls.stream;
_hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSNetStreamLoadTrait:dispose");
}
_hls.removeEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
super.dispose();
}
override public function get bytesLoaded() : Number {
return _timeLoaded;
}
override public function get bytesTotal() : Number {
return _timeTotal;
}
public function get hls() : HLS {
return _hls;
}
/** **/
private function _mediaTimeHandler(event : HLSEvent) : void {
var timeTotal : Number = Math.round(10 * event.mediatime.duration) / 10;
var timeLoaded : Number = Math.max(0,Math.round(10 * (event.mediatime.position + event.mediatime.buffer)) / 10);
if (_timeTotal != timeTotal) {
if (timeTotal < _timeLoaded || timeTotal < 0) {
timeTotal = NaN;
}
_timeTotal = timeTotal;
setBytesTotal(timeTotal);
}
if (_timeLoaded != timeLoaded && timeLoaded <= timeTotal) {
_timeLoaded = timeLoaded;
setBytesLoaded(timeLoaded);
}
};
}
}
@@ -0,0 +1,82 @@
/* 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.osmf.plugins.traits {
import org.mangui.hls.HLS;
import org.mangui.hls.constant.HLSPlayStates;
import org.mangui.hls.event.HLSEvent;
import org.osmf.traits.PlayState;
import org.osmf.traits.PlayTrait;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSPlayTrait extends PlayTrait {
private var _hls : HLS;
private var streamStarted : Boolean = false;
public function HLSPlayTrait(hls : HLS) {
CONFIG::LOGGING {
Log.debug("HLSPlayTrait()");
}
super();
_hls = hls;
_hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateChangedHandler);
_hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _playbackComplete);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSPlayTrait:dispose");
}
_hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateChangedHandler);
_hls.removeEventListener(HLSEvent.PLAYBACK_COMPLETE, _playbackComplete);
super.dispose();
}
override protected function playStateChangeStart(newPlayState:String):void {
CONFIG::LOGGING {
Log.info("HLSPlayTrait:playStateChangeStart:" + newPlayState);
}
switch (newPlayState) {
case PlayState.PLAYING:
if (!streamStarted) {
_hls.stream.play();
streamStarted = true;
}
else {
_hls.stream.resume();
}
break;
case PlayState.PAUSED:
_hls.stream.pause();
break;
case PlayState.STOPPED:
streamStarted = false;
_hls.stream.close();
break;
}
}
/** state changed handler **/
private function _stateChangedHandler(event:HLSEvent):void {
switch (event.state) {
case HLSPlayStates.PLAYING:
CONFIG::LOGGING {
Log.debug("HLSPlayTrait:_stateChangedHandler:setBuffering(true)");
}
if (!streamStarted) {
streamStarted = true;
play();
}
default:
}
}
/** playback complete handler **/
private function _playbackComplete(event : HLSEvent) : void {
stop();
}
}
}
@@ -0,0 +1,61 @@
/* 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.osmf.plugins.traits {
import org.mangui.hls.HLS;
import org.mangui.hls.event.HLSEvent;
import org.mangui.hls.constant.HLSSeekStates;
import org.osmf.traits.SeekTrait;
import org.osmf.traits.TimeTrait;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSSeekTrait extends SeekTrait {
private var _hls : HLS;
public function HLSSeekTrait(hls : HLS, timeTrait : TimeTrait) {
CONFIG::LOGGING {
Log.debug("HLSSeekTrait()");
}
super(timeTrait);
_hls = hls;
_hls.addEventListener(HLSEvent.SEEK_STATE, _stateChangedHandler);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSSeekTrait:dispose");
}
_hls.removeEventListener(HLSEvent.SEEK_STATE, _stateChangedHandler);
super.dispose();
}
/**
* @private
* Communicates a <code>seeking</code> change to the media through the NetStream.
* @param newSeeking New <code>seeking</code> value.
* @param time Time to seek to, in seconds.
*/
override protected function seekingChangeStart(newSeeking : Boolean, time : Number) : void {
if (newSeeking) {
CONFIG::LOGGING {
Log.info("HLSSeekTrait:seekingChangeStart(newSeeking/time):(" + newSeeking + "/" + time + ")");
}
_hls.stream.seek(time);
}
super.seekingChangeStart(newSeeking, time);
}
/** state changed handler **/
private function _stateChangedHandler(event : HLSEvent) : void {
if (seeking && event.state != HLSSeekStates.SEEKING) {
CONFIG::LOGGING {
Log.debug("HLSSeekTrait:setSeeking(false);");
}
setSeeking(false, timeTrait.currentTime);
}
}
}
}
@@ -0,0 +1,60 @@
/* 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.osmf.plugins.traits {
import org.mangui.hls.HLS;
import org.mangui.hls.constant.HLSTypes;
import org.mangui.hls.event.HLSEvent;
import org.osmf.traits.TimeTrait;
import org.osmf.events.TimeEvent;
CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
}
public class HLSTimeTrait extends TimeTrait {
private var _hls : HLS;
public function HLSTimeTrait(hls : HLS, duration : Number = 0) {
CONFIG::LOGGING {
Log.debug("HLSTimeTrait()");
}
super(duration);
setCurrentTime(0);
_hls = hls;
_hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
_hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _playbackComplete);
}
override public function dispose() : void {
CONFIG::LOGGING {
Log.debug("HLSTimeTrait:dispose");
}
_hls.removeEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
_hls.removeEventListener(HLSEvent.PLAYBACK_COMPLETE, _playbackComplete);
super.dispose();
}
override protected function signalComplete():void
{
// live streams shouldn't end based on TimeTrait
if (_hls.type !== HLSTypes.LIVE)
{
dispatchEvent(new TimeEvent(TimeEvent.COMPLETE));
}
}
/** Update playback position/duration **/
private function _mediaTimeHandler(event : HLSEvent) : void {
var newDuration : Number = event.mediatime.duration;
var newPosition : Number = Math.max(0, event.mediatime.position);
setDuration(newDuration);
setCurrentTime(newPosition);
};
/** playback complete handler **/
private function _playbackComplete(event : HLSEvent) : void {
signalComplete();
}
}
}
@@ -0,0 +1,42 @@
/* 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.osmf.plugins.utils {
import org.mangui.hls.event.HLSError;
import org.mangui.hls.event.HLSEvent;
import org.osmf.events.MediaErrorCodes;
public class ErrorManager {
public static function getMediaErrorCode(event : HLSEvent) : int {
var errorCode : int = MediaErrorCodes.NETSTREAM_PLAY_FAILED;
if (event && event.error) {
switch (event.error.code) {
case HLSError.FRAGMENT_LOADING_ERROR:
case HLSError.KEY_LOADING_ERROR:
case HLSError.MANIFEST_LOADING_IO_ERROR:
errorCode = MediaErrorCodes.IO_ERROR;
break;
case HLSError.FRAGMENT_LOADING_CROSSDOMAIN_ERROR:
case HLSError.KEY_LOADING_CROSSDOMAIN_ERROR:
case HLSError.MANIFEST_LOADING_CROSSDOMAIN_ERROR:
errorCode = MediaErrorCodes.SECURITY_ERROR
break;
case org.mangui.hls.event.HLSError.FRAGMENT_PARSING_ERROR:
case org.mangui.hls.event.HLSError.KEY_PARSING_ERROR:
case org.mangui.hls.event.HLSError.MANIFEST_PARSING_ERROR:
errorCode = MediaErrorCodes.NETSTREAM_FILE_STRUCTURE_INVALID;
break;
case org.mangui.hls.event.HLSError.TAG_APPENDING_ERROR:
errorCode = MediaErrorCodes.ARGUMENT_ERROR;
break;
}
}
return errorCode;
};
public static function getMediaErrorMessage(event : HLSEvent) : String {
return (event && event.error) ? event.error.msg : "Unknown error";
};
};
}