public VideoPacket?GetNearestStreamDataLessThanEqual(PopTimeline.TimeUnit Time)
        {
            var Blocks = this.Blocks;

            if (Blocks.Count == 0)
            {
                return(null);
            }

            System.Func <int, VideoPacket> GetAt = (Index) =>
            {
                return(Blocks[Index]);
            };
            System.Func <VideoPacket, BinaryChop.CompareDirection> Compare = (OtherBlock) =>
            {
                return(OtherBlock.GetTimeDirection(Time));
            };
            int?Match;
            int?Nearest;

            BinaryChop.Search(0, Blocks.Count - 1, GetAt, Compare, out Nearest, out Match);

            //	nothing at/before this time
            if (Nearest.Value < 0)
            {
                return(null);
            }

            return(GetAt(Nearest.Value));
        }
    public override void GetTimeRange(out PopTimeline.TimeUnit Min, out PopTimeline.TimeUnit Max)
    {
        int?MinTime = null;
        int?MaxTime = null;

        Pop.AllocIfNull(ref VideoStreams);


        foreach (var bs in VideoStreams)
        {
            var Blocks = bs.Blocks;
            if (Blocks == null || Blocks.Count == 0)
            {
                continue;
            }
            var bsmin = Blocks[0].GetStartTime().Time;
            var bsmax = Blocks[Blocks.Count - 1].GetEndTime().Time;

            MinTime = MinTime.HasValue ? Mathf.Min(MinTime.Value, bsmin) : bsmin;
            MaxTime = MaxTime.HasValue ? Mathf.Max(MaxTime.Value, bsmax) : bsmax;
        }

        if (!MinTime.HasValue)
        {
            throw new System.Exception("No time range (no data)");
        }

        Min = new PopTimeline.TimeUnit(MinTime.Value);
        Max = new PopTimeline.TimeUnit(MaxTime.Value);
    }
 //	is this time before,inside,or after this block
 public BinaryChop.CompareDirection GetTimeDirection(PopTimeline.TimeUnit Time)
 {
     if (Time.Time < StartTimeMs)
     {
         return(BinaryChop.CompareDirection.Before);
     }
     if (Time.Time > StartTimeMs + DurationMs)
     {
         return(BinaryChop.CompareDirection.After);
     }
     return(BinaryChop.CompareDirection.Inside);
 }
        public VideoPacket?GetStreamData(PopTimeline.TimeUnit Time)
        {
            int?Index;
            int?NearestIndex;

            GetStreamDataIndex(Time, out Index, out NearestIndex);
            if (!Index.HasValue)
            {
                return(null);
            }
            return(Blocks[Index.Value]);
        }
        void GetStreamDataIndex(PopTimeline.TimeUnit Time, out int?Match, out int?Nearest)
        {
            var Blocks = this.Blocks;

            if (Blocks.Count == 0)
            {
                Match   = null;
                Nearest = null;
                return;
            }

            System.Func <int, VideoPacket> GetAt = (Index) =>
            {
                return(Blocks[Index]);
            };
            System.Func <VideoPacket, BinaryChop.CompareDirection> Compare = (OtherBlock) =>
            {
                return(OtherBlock.GetTimeDirection(Time));
            };
            BinaryChop.Search(0, Blocks.Count - 1, GetAt, Compare, out Nearest, out Match);
        }
    void OnTimeSelected(PopTimeline.TimeUnit SelectedTime)
    {
        //	find keyframe closest to time
        var VideoStream = Sink.GetVideoStream(VideoStreamName);

        var Packet = VideoStream.GetNearestStreamDataLessThanEqual(SelectedTime);
        //	todo: avoid infinite loop properly
        var InfLoopCheck = 1000;

        while (Packet.HasValue && --InfLoopCheck > 0)
        {
            if (Packet.Value.Sample.IsKeyframe)
            {
                break;
            }

            //	look for previous
            var PrevTime = new PopTimeline.TimeUnit(Packet.Value.GetStartTime().Time - 1);
            Packet = VideoStream.GetNearestStreamDataLessThanEqual(PrevTime);
        }

        if (InfLoopCheck <= 0)
        {
            Packet = null;
        }

        if (Packet == null)
        {
            throw new System.Exception("Failed to find keyframe before " + SelectedTime);
        }

        var HeaderData = VideoStream.TrackMeta.SampleDescriptions[0].Data;
        var PacketData = Sink.GetFileData(Packet.Value.Sample.DataPosition, Packet.Value.Sample.DataSize);

        //	just h264 current expects annexb format. Mp4 is probably in AVCC (see meta!)
        HeaderData = PopX.H264.GetH264AnnexB(HeaderData);
        PacketData = PopX.H264.GetH264AnnexB(PacketData);
        OnH264Data.Invoke(HeaderData, PacketData);
    }
        public VideoPacket?GetNearestStreamDataGreaterThanEqual(PopTimeline.TimeUnit Time)
        {
            var Blocks = this.Blocks;

            if (Blocks.Count == 0)
            {
                return(null);
            }

            System.Func <int, VideoPacket> GetAt = (Index) =>
            {
                return(Blocks[Index]);
            };
            System.Func <VideoPacket, BinaryChop.CompareDirection> Compare = (OtherBlock) =>
            {
                return(OtherBlock.GetTimeDirection(Time));
            };
            int?Match;
            int?Nearest;

            BinaryChop.Search(0, Blocks.Count - 1, GetAt, Compare, out Nearest, out Match);

            if (Match.HasValue)
            {
                return(GetAt(Match.Value));
            }

            //	next must the one after prev
            var Next = Nearest.Value + 1;

            if (Next >= Blocks.Count)
            {
                return(null);
            }

            return(GetAt(Next));
        }
    public override PopTimeline.StreamDataItem GetNearestOrNextStreamData(PopTimeline.DataStreamMeta _StreamMeta, ref PopTimeline.TimeUnit Time)
    {
        var StreamMeta  = (VideoStreamMeta)_StreamMeta;
        var BlockStream = VideoStreams[StreamMeta.StreamIndex];
        var NextData    = BlockStream.GetNearestStreamDataGreaterThanEqual(Time);
        var Next        = NextData.Value;

        Time = Next.GetStartTime();
        return(Next);
    }
    public override PopTimeline.StreamDataItem GetNearestOrPrevStreamData(PopTimeline.DataStreamMeta _StreamMeta, ref PopTimeline.TimeUnit Time)
    {
        var StreamMeta  = (VideoStreamMeta)_StreamMeta;
        var BlockStream = VideoStreams[StreamMeta.StreamIndex];
        var PrevData    = BlockStream.GetNearestStreamDataLessThanEqual(Time);
        var Prev        = PrevData.Value;

        Time = Prev.GetStartTime();
        return(Prev);
    }
    public override List <PopTimeline.StreamDataItem> GetStreamData(PopTimeline.DataStreamMeta _StreamMeta, PopTimeline.TimeUnit MinTime, PopTimeline.TimeUnit MaxTime)
    {
        var StreamMeta  = (VideoStreamMeta)_StreamMeta;
        var BlockStream = VideoStreams[StreamMeta.StreamIndex];
        var Data        = new List <PopTimeline.StreamDataItem>();
        var Blocks      = BlockStream.Blocks;

        if (Blocks.Count == 0)
        {
            return(Data);
        }

        //	find start with binary chop.
        //	find TAIL and work backwards as nearestprev will be last time, but if we do min we can start out of range
        System.Func <int, VideoPacket> GetAt = (Index) =>
        {
            return(Blocks[Index]);
        };
        System.Func <VideoPacket, BinaryChop.CompareDirection> Compare = (OtherBlock) =>
        {
            return(OtherBlock.GetTimeDirection(MaxTime));
        };
        int?MaxMatch;
        int?MaxNearestPrev;

        BinaryChop.Search(0, Blocks.Count - 1, GetAt, Compare, out MaxNearestPrev, out MaxMatch);
        int Last = MaxNearestPrev.Value;

        //	go earlier
        for (int b = Last; b >= 0; b--)
        {
            var Block      = Blocks[b];
            var CompareDir = Block.GetTimeDirection(MinTime);
            //	if min time is AFTER block, block is before min
            if (CompareDir == BinaryChop.CompareDirection.After)
            {
                break;
            }
            Data.Insert(0, Block);
        }

        return(Data);
    }