diff --git a/.flexLibProperties b/.flexLibProperties index fe59c629..fbf40870 100644 --- a/.flexLibProperties +++ b/.flexLibProperties @@ -1,6 +1,7 @@ + diff --git a/.gitignore b/.gitignore index 25a04e37..9ae3fae8 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ crashlytics-build.properties # Flash Builder /bin/flashls.swc +/bin-temp diff --git a/src/org/mangui/hls/controller/AudioTrackController.as b/src/org/mangui/hls/controller/AudioTrackController.as index fa6c4d00..55f24dd0 100644 --- a/src/org/mangui/hls/controller/AudioTrackController.as +++ b/src/org/mangui/hls/controller/AudioTrackController.as @@ -29,6 +29,9 @@ package org.mangui.hls.controller { _hls = hls; _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler); _hls.addEventListener(HLSEvent.LEVEL_LOADED, _levelLoadedHandler); + + // Prevents crash when reading hls.audioTracks.length before audio tracks are initialized + _audioTracks = new Vector.(); } public function dispose() : void { diff --git a/src/org/mangui/hls/loader/FragmentLoader.as b/src/org/mangui/hls/loader/FragmentLoader.as index 376fe6ba..5f64f9d1 100644 --- a/src/org/mangui/hls/loader/FragmentLoader.as +++ b/src/org/mangui/hls/loader/FragmentLoader.as @@ -1021,21 +1021,32 @@ package org.mangui.hls.loader { levelObj.updateFragment(_fragCurrent.seqnum, true, fragData.pts_min, fragData.pts_min + _fragCurrent.duration * 1000); /* in case we are probing PTS, retrieve PTS info and synchronize playlist PTS / sequence number */ CONFIG::LOGGING { - Log.debug("analyzed PTS " + _fragCurrent.seqnum + " of [" + (levelObj.start_seqnum) + "," + (levelObj.end_seqnum) + "],level " + _hls.loadLevel + " m PTS:" + fragData.pts_min); + Log.debug("analyzed PTS " + _fragCurrent.seqnum + " of [" + (levelObj.start_seqnum) + "," + (levelObj.end_seqnum) + "],level " + _hls.loadLevel + " m PTS:" + fragData.pts_min); } /* check if fragment loaded for PTS analysis is the next one if this is the expected one, then continue if not, then cancel current fragment loading, next call to loadnextfragment() will load the right seqnum */ - var next_pts : Number = _fragPrevious.data.pts_start_computed + 1000*_fragPrevious.duration; - var next_seqnum : Number = levelObj.getSeqNumNearestPTS(next_pts, _fragCurrent.continuity); + var next_pts:Number; + var next_seqnum:Number; + + // Resolves intermittent issue that causes the player to crash due to missing previous fragment data while seeking + if (_fragPrevious && _fragPrevious.data) { + next_pts = _fragPrevious.data.pts_start_computed + 1000*_fragPrevious.duration; + next_seqnum = levelObj.getSeqNumNearestPTS(next_pts, _fragCurrent.continuity); + } else { + CONFIG::LOGGING { + Log.debug("Previous fragment data not found while analyzing PTS!"); + } + } + CONFIG::LOGGING { Log.debug("analyzed PTS : getSeqNumNearestPTS(level,pts,cc:" + _hls.loadLevel + "," + next_pts + "," + _fragCurrent.continuity + ")=" + next_seqnum); } // CONFIG::LOGGING { // Log.info("seq/next:"+ _seqnum+"/"+ next_seqnum); // } - if (next_seqnum != _fragCurrent.seqnum) { + if (next_seqnum !== _fragCurrent.seqnum) { // stick to same level after PTS analysis _levelNext = _hls.loadLevel; CONFIG::LOGGING { diff --git a/src/org/mangui/hls/playlist/Manifest.as b/src/org/mangui/hls/playlist/Manifest.as index 6e7c13ea..cdce7d01 100644 --- a/src/org/mangui/hls/playlist/Manifest.as +++ b/src/org/mangui/hls/playlist/Manifest.as @@ -20,6 +20,7 @@ package org.mangui.hls.playlist { import org.mangui.hls.model.Level; import org.mangui.hls.utils.DateUtil; import org.mangui.hls.utils.Hex; + import org.mangui.hls.utils.StringUtil; CONFIG::LOGGING { import org.mangui.hls.utils.Log; @@ -127,7 +128,7 @@ package org.mangui.hls.playlist { /** loading complete handler **/ private function _loadCompleteHandler(event : Event) : void { _metrics.loading_end_time = getTimer(); - onLoadedData(String(_urlloader.data)); + onLoadedData(StringUtil.toLF(String(_urlloader.data))); }; private function onLoadedData(data : String) : void { @@ -144,7 +145,7 @@ package org.mangui.hls.playlist { /** Extract fragments from playlist data. **/ public static function getFragments(data : String, base : String, level : int) : Vector. { var fragments : Vector. = new Vector.(); - var lines : Array = data.split("\n"); + var lines : Vector. = StringUtil.toLines(data); // fragment seqnum var seqnum : int = 0; // fragment start time (in sec) @@ -313,7 +314,7 @@ package org.mangui.hls.playlist { var levels : Vector. = new Vector.(); var bitrateDictionary : Dictionary = new Dictionary(); var level : Level; - var lines : Array = data.split("\n"); + var lines : Vector. = StringUtil.toLines(data); var level_found : Boolean = false; var i : int = 0; while (i < lines.length) { @@ -387,7 +388,7 @@ package org.mangui.hls.playlist { /** Extract Alternate Audio Tracks from manifest data. **/ public static function extractAltAudioTracks(data : String, base : String = '') : Vector. { var altAudioTracks : Vector. = new Vector.(); - var lines : Array = data.split("\n"); + var lines : Vector. = StringUtil.toLines(data); var i : int = 0; while (i < lines.length) { var line : String = lines[i++]; @@ -490,7 +491,7 @@ package org.mangui.hls.playlist { }; public static function getTargetDuration(data : String) : Number { - var lines : Array = data.split("\n"); + var lines : Vector. = StringUtil.toLines(data); var i : int = 0; var targetduration : Number = 0; diff --git a/src/org/mangui/hls/utils/StringUtil.as b/src/org/mangui/hls/utils/StringUtil.as new file mode 100644 index 00000000..1016fc0d --- /dev/null +++ b/src/org/mangui/hls/utils/StringUtil.as @@ -0,0 +1,94 @@ +package org.mangui.hls.utils +{ + public class StringUtil + { + /** + * Returns true if the specified string is + * a single space, tab, carriage return, newline, or formfeed character. + * + * @param str The String that is is being queried. + * + * @return true if the specified string is + * a single space, tab, carriage return, newline, or formfeed character. + */ + public static function isWhitespace(character:String):Boolean + { + switch (character) + { + case " ": + case "\t": + case "\r": + case "\n": + case "\f": + case "\u00A0": // non breaking space + case "\u2028": // line seperator + case "\u2029": // paragraph seperator + case "\u3000": // ideographic space + return true; + } + + return false; + } + + /** + * Removes all whitespace characters from the beginning and end + * of the specified string. + * + * @param str The String whose whitespace should be trimmed. + * + * @return Updated String where whitespace was removed from the + * beginning and end. + */ + public static function trim(str:String):String + { + if (str == null) return ''; + + var startIndex:int = 0; + while (isWhitespace(str.charAt(startIndex))) + ++startIndex; + + var endIndex:int = str.length - 1; + while (isWhitespace(str.charAt(endIndex))) + --endIndex; + + if (endIndex >= startIndex) + return str.slice(startIndex, endIndex + 1); + else + return ""; + } + + /** + * Splits a String into an Vector of nicely trimmed strings, where each + * item represents a single line of the original String data + * + * @param str The String to be split into an Array + * @return Vector of type String + */ + public static function toLines(str:String):Vector. + { + var lines:Array = toLF(str).split("\n"); + var i:uint; + var length:uint = lines.length; + + for (i=0; i(lines); + } + + /** + * Converts strings containing Windows (CR-LF), MacOS (CR) and other + * non-standard line breaks (LF-CR) into strings using only Linux-style + * line breaks (LF). + * + * @param str String containing non-Linux line breaks + * @returns String containly only Linux-style line breaks + */ + public static function toLF(str:String):String + { + return (str || "").replace(/\r\n|\n\r|\r/g, "\n"); + } + } +} \ No newline at end of file