@@ -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