Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolved intermittent crash while seeking, added support for manifest files with CR-LF line breaks #492

Merged
merged 17 commits into from
Apr 15, 2016
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flexLibProperties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flexLibProperties includeAllClasses="false" useMultiPlatformConfig="false" version="3">
<includeClasses>
<classEntry path="org.mangui.hls.utils.StringUtil"/>
<classEntry path="org.mangui.osmf.plugins.HLSDynamicPlugin"/>
<classEntry path="org.mangui.osmf.plugins.HLSMediaElement"/>
<classEntry path="org.mangui.osmf.plugins.HLSPlugin"/>
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,4 @@ crashlytics-build.properties

# Flash Builder
/bin/flashls.swc
/bin-temp
2 changes: 1 addition & 1 deletion src/org/mangui/hls/HLS.as
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ package org.mangui.hls {
public function set audioTrack(val : int) : void {
_audioTrackController.audioTrack = val;
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry I am a bit pedantic, but there are some useless whitespace there :-/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not any more.

/* set stage */
public function set stage(stage : Stage) : void {
_stage = stage;
Expand Down
5 changes: 4 additions & 1 deletion src/org/mangui/hls/controller/AudioTrackController.as
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ 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.<AudioTrack>();
}

public function dispose() : void {
_hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
_hls.removeEventListener(HLSEvent.LEVEL_LOADED, _levelLoadedHandler);
Expand Down
19 changes: 15 additions & 4 deletions src/org/mangui/hls/loader/FragmentLoader.as
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 6 additions & 5 deletions src/org/mangui/hls/playlist/Manifest.as
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you have a sample playlist which is not parsed correctly with current Manifest parser ?
I am just curious to see which kind of parsing issue you were facing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We faced a number of issues. but the primary problem was directives, such as #EXT-X-MEDIA:, being ignored by the parser because ActionScript reported them as starting at indexes of between 2 and 5, rather than 0.

Converting line breaks to LF and trimming whitespace resolved the issues and enabled the parser to parse as normal.

I'm not able to share URLs on a public forum, but here's a problematic playlist: sample.m3u8

};

private function onLoadedData(data : String) : void {
Expand All @@ -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.<Fragment> {
var fragments : Vector.<Fragment> = new Vector.<Fragment>();
var lines : Array = data.split("\n");
var lines : Vector.<String> = StringUtil.toLines(data);
// fragment seqnum
var seqnum : int = 0;
// fragment start time (in sec)
Expand Down Expand Up @@ -313,7 +314,7 @@ package org.mangui.hls.playlist {
var levels : Vector.<Level> = new Vector.<Level>();
var bitrateDictionary : Dictionary = new Dictionary();
var level : Level;
var lines : Array = data.split("\n");
var lines : Vector.<String> = StringUtil.toLines(data);
var level_found : Boolean = false;
var i : int = 0;
while (i < lines.length) {
Expand Down Expand Up @@ -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.<AltAudioTrack> {
var altAudioTracks : Vector.<AltAudioTrack> = new Vector.<AltAudioTrack>();
var lines : Array = data.split("\n");
var lines : Vector.<String> = StringUtil.toLines(data);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this trim the Level / Alt Audio track name ?
it is really common to have space in Quality Level, track Name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No: it trims whitespace characters from the ends of the lines, but has now affect on anything else, including labels

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok got it

var i : int = 0;
while (i < lines.length) {
var line : String = lines[i++];
Expand Down Expand Up @@ -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.<String> = StringUtil.toLines(data);
var i : int = 0;
var targetduration : Number = 0;

Expand Down
94 changes: 94 additions & 0 deletions src/org/mangui/hls/utils/StringUtil.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.mangui.hls.utils
{
public class StringUtil
{
/**
* Returns <code>true</code> 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 <code>true</code> 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.<String>
{
var lines:Array = toLF(str).split("\n");
var i:uint;
var length:uint = lines.length;

for (i=0; i<length; ++i)
{
lines[i] = trim(lines[i]);
}

return Vector.<String>(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");
}
}
}