/// <summary> /// calls f for each set of fragments advertised by the FRT. f will be passed /// an Object argument (arg) with 6 fields. /// /// arg.fdp will be the entry corresponding to the fragment range. /// arg.isLast will be true if this is the last entry in the table /// arg.startFragmentId will be the id of the first fragment in the interval /// arg.endFragmentId will be the id of the last fragment in the interval + 1 /// arg.startTime will be the start time of the first fragment in the interval /// arg.endTime will be the end time of the last fragment in the interval /// /// if f returns false, iteration will halt /// if f returns true, iteration will continue /// /// if will be called in ascending startFragmentId order. /// </summary> private void forEachInterval(Func <FragmentDurationPair, bool, uint, uint, uint, uint, bool> f) { // search for gaps, then check if the desired time is in that gap for (int i = 0; i < fragmentDurationPairs.Count; ++i) { FragmentDurationPair fdp = fragmentDurationPairs[i]; if (fdp.duration == 0) { // some kind of discontinuity continue; } uint startFragmentId = fdp.firstFragment; uint startTime = (uint)fdp.durationAccrued; // find the valid entry or the next skip, gap, or skip+gap bool isLast = true; int j; for (j = i + 1; j < fragmentDurationPairs.Count; ++j) { if (fragmentDurationPairs[j].duration != 0 || // next is valid entry fragmentDurationPairs[j].discontinuityIndicator == 1 || // next is skip fragmentDurationPairs[j].discontinuityIndicator == 2 || // next is gap fragmentDurationPairs[j].discontinuityIndicator == 3) // next is skip+gap { isLast = false; break; } else { // eof or some unknown kind of discontinuity } } uint endFragmentId; uint endTime; if (isLast) { // there's no next entry endFragmentId = 0; endTime = 0; } else { endFragmentId = fragmentDurationPairs[j].firstFragment; if (startFragmentId > endFragmentId) // very uncommon case: fragment numbers are out of order { continue; } endTime = startTime + (endFragmentId - startFragmentId) * fdp.duration; } bool shouldContinue = f(fdp, isLast, startFragmentId, endFragmentId, startTime, endTime); if (!shouldContinue || isLast) { return; } } }
public void adjustEndEntryDurationAccrued(uint value) { FragmentDurationPair fdp = fragmentDurationPairs[fragmentDurationPairs.Count - 1]; if (fdp.duration == 0) { fdp.durationAccrued = value; } }
/// <summary> /// return whether the fragment table is complete. /// </summary> public bool tableComplete() { if (fragmentDurationPairs == null || fragmentDurationPairs.Count <= 0) { return(false); } FragmentDurationPair fdp = fragmentDurationPairs[fragmentDurationPairs.Count - 1] as FragmentDurationPair; return(fdp.duration == 0 && fdp.discontinuityIndicator == 0); }
/// <summary> /// return the fragment information for the first fragment in the FRT whose fragment number /// is greater than or equal to fragment id. special cases: /// /// if fragmentId is in a gap, the first fragment after the gap will be returned. /// if fragmentId is in a skip, the first fragment after the skip will be returned. /// if fragmentId is before the first fragment-duration-pair, the first fragment will be returned. /// if fragmentId is after the last fragment-duration-pair, it will be assumed to exist. /// (in other words, the live point is ignored). /// /// if there are no valid entries in the FRT, returns null. this is the only situation that returns null. /// </summary> public FragmentAccessInformation getFragmentWithIdGreq(uint fragmentId) { FragmentDurationPair desiredFdp = null; uint desiredFragmentId = 0; forEachInterval(delegate(FragmentDurationPair fdp, bool isLast, uint startFragmentId, uint endFragmentId, uint startTime, uint endTime) { if (fragmentId < startFragmentId) { // before the given interval desiredFdp = fdp; desiredFragmentId = startFragmentId; return(false); // stop iterating } else if (isLast) { // catch all in the last entry desiredFdp = fdp; desiredFragmentId = fragmentId; return(false); } else if (fragmentId < endFragmentId) { // between the start and end of this interval desiredFdp = fdp; desiredFragmentId = fragmentId; return(false); // stop iterating } else { // beyond this interval, but not the last entry return(true); // keep iterating } }); if (desiredFdp == null) { // no fragment entries case return(null); } if (desiredFragmentId < desiredFdp.firstFragment) { // probably won't ever hit this // just make sure that we're before the start desiredFragmentId = desiredFdp.firstFragment; } FragmentAccessInformation fai = new FragmentAccessInformation(); fai.fragId = desiredFragmentId; fai.fragDuration = desiredFdp.duration; fai.fragmentEndTime = (uint)desiredFdp.durationAccrued + (desiredFragmentId - desiredFdp.firstFragment + 1) * desiredFdp.duration; return(fai); }
/// <summary> /// Given a fragment id, return the number of fragments after the /// fragment with the id given. /// </summary> public uint fragmentsLeft(uint fragId, uint currentMediaTime) { if (fragmentDurationPairs == null || fragmentDurationPairs.Count == 0) { return(0); } FragmentDurationPair fdp = fragmentDurationPairs[fragmentDurationPairs.Count - 1] as FragmentDurationPair; uint fragments = (currentMediaTime - (uint)fdp.durationAccrued) / fdp.duration + fdp.firstFragment - fragId - 1; return(fragments); }
/// <summary> /// return the first FragmentDurationPair whose index >= i that is not a discontinuity, /// or null if no such FragmentDurationPair exists. /// </summary> private FragmentDurationPair findNextValidFragmentDurationPair(int index) { for (int i = index; i < fragmentDurationPairs.Count; ++i) { FragmentDurationPair fdp = fragmentDurationPairs[i]; if (fdp.duration > 0) { return(fdp); } } return(null); }
private uint calculateFragmentId(FragmentDurationPair fdp, uint time) { if (fdp.duration <= 0) { return(fdp.firstFragment); } uint deltaTime = time - (uint)fdp.durationAccrued; uint count = (deltaTime > 0)? deltaTime / fdp.duration : 1; if ((deltaTime % fdp.duration) > 0) { count++; } return(fdp.firstFragment + count - 1); }
/// <summary> /// return the fragment information for the first fragment in the FRT that contains a time /// greater than or equal to fragment time. special cases: /// /// if time is in a gap, the first fragment after the gap will be returned. /// if time is in a skip, the first fragment after the skip will be returned. /// if time is before the first fragment-duration-pair, the first fragment will be returned. /// if time is after the last fragment-duration-pair, it will be assumed to exist. /// (in other words, the live point is ignored). /// /// if there are no valid entries in the FRT, returns null. this is the only situation that returns null. /// </summary> public FragmentAccessInformation getFragmentWithTimeGreq(uint fragmentTime) { FragmentDurationPair desiredFdp = null; uint desiredFragmentStartTime = 0; forEachInterval(delegate(FragmentDurationPair fdp, bool isLast, uint startFragmentId, uint endFragmentId, uint startTime, uint endTime) { if (fragmentTime < startTime) { // before the given interval desiredFdp = fdp; desiredFragmentStartTime = startTime; return(false); // stop iterating } else if (isLast) { // catch all in the last entry desiredFdp = fdp; desiredFragmentStartTime = fragmentTime; return(false); } else if (fragmentTime < endTime) { // between the start and end of this interval desiredFdp = fdp; desiredFragmentStartTime = fragmentTime; return(false); // stop iterating } else { // beyond this interval, but not the last entry return(true); // keep iterating } }); if (desiredFdp == null) { // no fragment entries case return(null); } uint desiredFragmentId = calculateFragmentId(desiredFdp, desiredFragmentStartTime); FragmentAccessInformation fai = new FragmentAccessInformation(); fai.fragId = desiredFragmentId; fai.fragDuration = desiredFdp.duration; fai.fragmentEndTime = (uint)desiredFdp.durationAccrued + (desiredFragmentId - desiredFdp.firstFragment + 1) * desiredFdp.duration; return(fai); }
/// <summary> /// calls f for each true gap (discontinuity of type 2) found within the FRT. f will be passed /// an Object argument (arg) with 3 fields. /// /// arg.fdp will be the discontinuity entry. /// arg.prevFdp will be the previous non-discontinuity entry /// arg.nextFdp will be the next non-discontinuity entry /// /// if f returns false, iteration will halt /// if f returns true, iteration will continue /// </summary> private void forEachGap(Func <FragmentDurationPair, FragmentDurationPair, FragmentDurationPair, bool> f) { if (fragmentDurationPairs.Count <= 0) { return; } // search for gaps, then check if the desired time is in that gap for (int i = 0; i < fragmentDurationPairs.Count; ++i) { FragmentDurationPair fdp = fragmentDurationPairs[i]; if (fdp.duration != 0 || fdp.discontinuityIndicator != 2) { // skip until we find a discontinuity of type 2 continue; } // gaps should only be present in the middle of content, // so there should always be a previous valid entry and // a next valid entry. // figure out the previous valid entry FragmentDurationPair prevFdp = findPrevValidFragmentDurationPair(i); if (prevFdp == null || // very uncommon case: there are no non-discontinuities before the discontinuity prevFdp.firstFragment > fdp.firstFragment) // very uncommon case: fragment numbers are out of order { continue; } // search forwards for the first non-discontinuity FragmentDurationPair nextFdp = findNextValidFragmentDurationPair(i + 1); if (nextFdp == null || // very uncommon case: there are no valid fragments after the discontinuity fdp.firstFragment > nextFdp.firstFragment) // very uncommon case: fragment numbers are out of order { continue; } bool shouldContinue = f(fdp, prevFdp, nextFdp); if (!shouldContinue) { return; } } }
/// <summary> /// return the first FragmentDurationPair whose index less i that is not a discontinuity, /// or null if no such FragmentDurationPair exists. /// </summary> private FragmentDurationPair findPrevValidFragmentDurationPair(int index) { int i = index; if (i > fragmentDurationPairs.Count) { i = fragmentDurationPairs.Count; } for (; i > 0; --i) { FragmentDurationPair fdp = fragmentDurationPairs[i - 1]; if (fdp.duration > 0) { return(fdp); } } return(null); }
private FragmentAccessInformation getNextValidFragment(int startIdx) { FragmentAccessInformation fai = null; for (int i = startIdx; i < fragmentDurationPairs.Count; i++) { FragmentDurationPair fdp = fragmentDurationPairs[i]; if (fdp.duration > 0) { fai = new FragmentAccessInformation(); fai.fragId = fdp.firstFragment; fai.fragDuration = fdp.duration; fai.fragmentEndTime = (uint)fdp.durationAccrued + fdp.duration; break; } } return(fai); }
/// <summary> /// Given a time spot in terms of the time scale used by the fragment table, returns the corresponding /// Id of the fragment that contains the time spot. /// </summary> public FragmentAccessInformation findFragmentIdByTime(uint time, uint totalDuration, bool live = false) { if (fragmentDurationPairs.Count <= 0) { return(null); } FragmentDurationPair fdp = null; for (int i = 1; i < fragmentDurationPairs.Count; i++) { fdp = fragmentDurationPairs[i]; if (fdp.durationAccrued >= time) { return(validateFragment(calculateFragmentId(fragmentDurationPairs[i - 1], time), totalDuration, live)); } } return(validateFragment(calculateFragmentId(fragmentDurationPairs[fragmentDurationPairs.Count - 1], time), totalDuration, live)); }
public override void Parse(BoxInfo bi, HDSBinaryReader br) { base.Parse(bi, br); timeScale = br.ReadUInt32(); qualitySegmentURLModifiers.Clear(); uint qualityEntryCount = br.ReadByte(); for (uint i = 0; i < qualityEntryCount; i++) { qualitySegmentURLModifiers.Add(br.ReadString()); } uint entryCount = br.ReadUInt32(); for (uint i = 0; i < entryCount; i++) { FragmentDurationPair fdp = new FragmentDurationPair(); fdp.Parse(br); fragmentDurationPairs.Add(fdp); } }
///<summary>The total number of fragments in the movie.</summary> public uint GetFragmentsCount() { AdobeFragmentRunTable lastFragmentTable = fragmentRunTables[fragmentRunTables.Count - 1]; List <FragmentDurationPair> fdps = lastFragmentTable.fragmentDurationPairs; if (fdps.Count < 1) { SegmentFragmentPair lastSegment = GetLastSegment(); return(lastSegment.fragmentsAccrued + lastSegment.fragmentsPerSegment - 1); } FragmentDurationPair lastValidFdp = fdps[fdps.Count - 1]; if (lastValidFdp.duration == 0) { lastValidFdp = fdps[fdps.Count - 2]; } int deltaTime = (int)(currentMediaTime - lastValidFdp.durationAccrued); uint fragCount = (uint)((deltaTime <= lastValidFdp.duration) ? 1 : (deltaTime / lastValidFdp.duration)); return(lastValidFdp.firstFragment + fragCount - 1); }
/// <summary> /// Given a fragment id, check whether the current fragment is valid or a discontinuity. /// If the latter, skip to the nearest fragment and return the new fragment id. /// /// return the Id of the fragment that is valid. /// </summary> public FragmentAccessInformation validateFragment(uint fragId, ulong totalDuration, bool live = false) { int size = fragmentDurationPairs.Count - 1; FragmentAccessInformation fai = null; uint timeResidue, timeDistance, fragStartTime; for (int i = 0; i < size; i++) { FragmentDurationPair curFdp = fragmentDurationPairs[i]; FragmentDurationPair nextFdp = fragmentDurationPairs[i + 1]; if ((curFdp.firstFragment <= fragId) && (fragId < nextFdp.firstFragment)) { if (curFdp.duration <= 0) { fai = getNextValidFragment(i + 1); } else { fai = new FragmentAccessInformation(); fai.fragId = fragId; fai.fragDuration = curFdp.duration; fai.fragmentEndTime = (uint)curFdp.durationAccrued + curFdp.duration * (fragId - curFdp.firstFragment + 1); } break; } else if ((curFdp.firstFragment <= fragId) && endOfStreamEntry(nextFdp)) { if (curFdp.duration > 0) { timeResidue = (uint)(totalDuration - curFdp.durationAccrued); timeDistance = (fragId - curFdp.firstFragment + 1) * curFdp.duration; fragStartTime = (fragId - curFdp.firstFragment) * curFdp.duration; if (timeResidue > fragStartTime) { if (!live || ((fragStartTime + curFdp.duration + curFdp.durationAccrued) <= totalDuration)) { fai = new FragmentAccessInformation(); fai.fragId = fragId; fai.fragDuration = curFdp.duration; if (timeResidue >= timeDistance) { fai.fragmentEndTime = (uint)curFdp.durationAccrued + timeDistance; } else { fai.fragmentEndTime = (uint)curFdp.durationAccrued + timeResidue; } break; } } } } } if (fai == null) { FragmentDurationPair lastFdp = fragmentDurationPairs[size]; if (lastFdp.duration > 0 && fragId >= lastFdp.firstFragment) { timeResidue = (uint)(totalDuration - lastFdp.durationAccrued); timeDistance = (fragId - lastFdp.firstFragment + 1) * lastFdp.duration; fragStartTime = (fragId - lastFdp.firstFragment) * lastFdp.duration; if (timeResidue > fragStartTime) { if (!live || ((fragStartTime + lastFdp.duration + lastFdp.durationAccrued) <= totalDuration)) { fai = new FragmentAccessInformation(); fai.fragId = fragId; fai.fragDuration = lastFdp.duration; if (timeResidue >= timeDistance) { fai.fragmentEndTime = (uint)lastFdp.durationAccrued + timeDistance; } else { fai.fragmentEndTime = (uint)lastFdp.durationAccrued + timeResidue; } } } } } return(fai); }
private bool endOfStreamEntry(FragmentDurationPair fdp) { return(fdp.duration == 0 && fdp.discontinuityIndicator == 0); }