Skip to content

Commit 55fde93

Browse files
committed
[FragmentLoader] Add com.pivotshare.hls.loader.FragmentLoader
1 parent 32c4eee commit 55fde93

File tree

1 file changed

+130
-2
lines changed

1 file changed

+130
-2
lines changed

src/com/pivotshare/hls/loader/FragmentLoader.as

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,23 @@ package com.pivotshare.hls.loader {
148148
private var _fragLoadStatus : int;
149149
private var _fragSkipping : Boolean;
150150

151+
/*
152+
* Whether _onDemuxProgress event listener has been called at least once
153+
* for current Fragment.
154+
*/
155+
private var _hasDemuxProgressedOnce : Boolean;
156+
157+
/*
158+
* Emergency Fragment to be loaded if current Fragment does not start
159+
* with an IDR.
160+
*/
161+
private var _emergencyFragment : Fragment;
162+
163+
/*
164+
* FragmentDemuxedStream to for emergency Fragment.
165+
*/
166+
private var _emergencyFragmentDemuxedStream : FragmentDemuxedStream;
167+
151168
//
152169
//
153170
//
@@ -182,6 +199,10 @@ package com.pivotshare.hls.loader {
182199
_loadingState = LOADING_STOPPED;
183200
_manifestJustLoaded = false;
184201
_keyLoader = new KeyLoader();
202+
203+
_hasDemuxProgressedOnce = false;
204+
_emergencyFragment = null;
205+
_emergencyFragmentDemuxedStream = null;
185206
};
186207

187208
/**
@@ -1028,8 +1049,57 @@ package com.pivotshare.hls.loader {
10281049
HLSSettings.startFromBitrate !== -1 ||
10291050
_levels.length == 1;
10301051
1052+
var previousLevel : int = _fragPrevious !== null ? _fragPrevious.level : _fragCurrent.level;
1053+
1054+
var mustRecoverFromNonIDRStart : Boolean =
1055+
HLSSettings.recoverFromNonIDRStartFragment &&
1056+
!_fragCurrent.data.starts_with_idr &&
1057+
_fragCurrent.level !== previousLevel; // TODO
1058+
1059+
// If this is the first Progress call for this Fragment
1060+
if (!_hasDemuxProgressedOnce) {
1061+
1062+
_hasDemuxProgressedOnce = true;
1063+
1064+
CONFIG::LOGGING {
1065+
Log.debug2("FragmentLoader#_onDemuxProgress: Fragment[" +
1066+
_fragCurrent.level + "][" + _fragCurrent.seqnum +
1067+
"] starts with IDR? " + _fragCurrent.data.starts_with_idr +
1068+
"@" + _fragCurrent.data.pts_min_video_header);
1069+
}
1070+
1071+
//
1072+
// Check for IDR at beginning of Fragment on Level switch
1073+
//
1074+
// If Fragment does not start with IDR then async stream same
1075+
// sequence at previous Level.
1076+
//
1077+
if (mustRecoverFromNonIDRStart) {
1078+
1079+
CONFIG::LOGGING {
1080+
Log.debug("FragmentLoader#_onDemuxProgress: Fragment["
1081+
+ _fragCurrent.level + "][" + _fragCurrent.seqnum +
1082+
"] DOES NOT start with IDR! Will load same sequence at previous level to recover.");
1083+
}
1084+
1085+
_emergencyFragment = _levels[previousLevel].getFragmentfromSeqNum(_fragCurrent.seqnum);
1086+
1087+
// Initiate loading of same seqnum Fragment on previous level to find IDR
1088+
_keyLoader.load(_emergencyFragment.decrypt_url, function (err : HLSError, keyData : ByteArray) : void {
1089+
_emergencyFragmentDemuxedStream = new FragmentDemuxedStream(_hls.stage);
1090+
_emergencyFragmentDemuxedStream.load(_emergencyFragment, keyData, null);
1091+
});
1092+
}
1093+
}
1094+
1095+
//
1096+
// TODO: We have temporarily disabled progressive buffering, as it
1097+
// does not seem to work with IDR recovery. Seems PTS analysis
1098+
// changes expected states. Needs to be debugged further.
1099+
//
1100+
/*
10311101
// Determine if we can do progressively append to StreamBuffer
1032-
if (_fragmentFirstLoaded || (_manifestJustLoaded && isFirstFragmentLevelDefined)) {
1102+
if (!mustRecoverFromNonIDRStart && (_fragmentFirstLoaded || (_manifestJustLoaded && isFirstFragmentLevelDefined))) {
10331103
10341104
//
10351105
// if audio expected, PTS analysis is done on audio
@@ -1129,7 +1199,7 @@ package com.pivotshare.hls.loader {
11291199
_hasDiscontinuity = false;
11301200
}
11311201
}
1132-
1202+
*/
11331203
}
11341204

11351205
/**
@@ -1175,6 +1245,50 @@ package com.pivotshare.hls.loader {
11751245
}
11761246
}
11771247

1248+
//
1249+
// Handle possible IDR Problem here
1250+
// TODO: We should still have have simple removal of frames as option
1251+
//
1252+
if (_emergencyFragment) {
1253+
1254+
_emergencyFragment = _emergencyFragmentDemuxedStream.getFragment();
1255+
1256+
// Return all tags (audio/video) unless it is Video tag that precedes IDR
1257+
var isNotBadVideoTag : Function = function (tag:FLVTag, index:int, vector:Vector.<FLVTag>):Boolean {
1258+
return tag.type !== FLVTag.AVC_NALU || tag.pts >= _fragCurrent.data.pts_min_video_header;
1259+
}
1260+
1261+
// Return only Video tags that precede and IDR
1262+
var isGoodVideoTag : Function = function (tag:FLVTag, index:int, vector:Vector.<FLVTag>):Boolean {
1263+
return tag.type == FLVTag.AVC_NALU && tag.pts < _fragCurrent.data.pts_min_video_header;
1264+
}
1265+
1266+
// It's already been found
1267+
if (!isNaN(_emergencyFragment.data.pts_min_video_header)) {
1268+
CONFIG::LOGGING {
1269+
Log.debug("FragmentLoader#_fragParsingCompleteHandler: Will fix non-IDR right now!");
1270+
}
1271+
1272+
var tagsExceptBadVideo : Vector.<FLVTag> = _fragCurrent.data.tags.filter(isNotBadVideoTag);
1273+
var goodNonIDRTags : Vector.<FLVTag> = _emergencyFragment.data.tags.filter(isGoodVideoTag);
1274+
1275+
_fragCurrent.data.tags = goodNonIDRTags.concat(tagsExceptBadVideo);
1276+
1277+
_emergencyFragment = null;
1278+
// TODO: Remove listener?
1279+
_emergencyFragmentDemuxedStream.close();
1280+
_emergencyFragmentDemuxedStream.removeEventListener(Event.COMPLETE, _onEmergencyDemuxedStreamComplete);
1281+
_emergencyFragmentDemuxedStream = null;
1282+
1283+
} else { // bail leaving _emergencyFragment callback to recall this callback :(
1284+
CONFIG::LOGGING {
1285+
Log.debug("FragmentLoader#_fragParsingCompleteHandler: Cannot fix this non-IDR Fragment yet.");
1286+
}
1287+
_emergencyFragmentDemuxedStream.addEventListener(Event.COMPLETE, _onEmergencyDemuxedStreamComplete);
1288+
return;
1289+
}
1290+
}
1291+
11781292
//
11791293
// Finish calculating processing metrics
11801294
// TODO: What else should this include, e.g. fragment fixing?
@@ -1282,6 +1396,9 @@ package com.pivotshare.hls.loader {
12821396
_hls.dispatchEvent(new HLSEvent(HLSEvent.TAGS_LOADED, _metrics));
12831397
_fragCurrent.data.shiftTags();
12841398
_hasDiscontinuity = false;
1399+
1400+
_hasDemuxProgressedOnce = false;
1401+
_emergencyFragment = null;
12851402
}
12861403

12871404
} else {
@@ -1350,5 +1467,16 @@ package com.pivotshare.hls.loader {
13501467
private function _onDemuxID3TagFound(id3_tags : Vector.<ID3Tag>) : void {
13511468
_fragCurrent.data.id3_tags = id3_tags;
13521469
}
1470+
1471+
/**
1472+
* Called when emergency Fragment has finished loading and demuxing.
1473+
*
1474+
* @method _onEmergencyDemuxedStreamComplete
1475+
* @param {Event} evt
1476+
*/
1477+
private function _onEmergencyDemuxedStreamComplete (evt : Event) : void {
1478+
_emergencyFragment = _emergencyFragmentDemuxedStream.getFragment();
1479+
_onDemuxComplete();
1480+
}
13531481
}
13541482
}

0 commit comments

Comments
 (0)