/// <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);
            }
        }
Example #14
0
        ///<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);
 }