/// <summary>
        /// Flushes all buffers and caches last samples.
        /// </summary>
        public void Flush()
        {
            if (_byteBufferSize > 0)
            {
                // Handle any remaining data.
                HandleData(_byteBuffer, 0, _byteBufferSize);

                // If anything is still unhandled, append it to last sample.
                // This is useful for H.264 HAL units which can have zero padding
                // at the end.
                if (_byteBufferSize > 0)
                {
                    if (_currentSample != null)
                        _currentSample.AddData(_byteBuffer, 0, _byteBufferSize);
                    _byteBufferSize = 0;
                }
            }

            // Finish and enqueue last sample of the stream.
            if (_currentSample != null)
            {
                if (_currentSample.DataStream == null)
                    throw new InvalidOperationException("invalid sample");

                lock (_outputBuffer)
                {
                    _outputBuffer.Enqueue(_currentSample);
                }

                _currentSample = null;
            }

            _bytesToRead = 0;

            _PTSTimestampList.Clear();
        }
        /// <summary>
        /// Finalizes current sample, if any, and creates new sample. Should only
        /// be called from ParseData method.
        /// </summary>
        /// <param name="count">Size of new sample in bytes</param>
        /// <param name="duration">Sample duration in HNS</param>
        /// <param name="timestamp">Timestamp of new sample in HNS</param>
        protected void BeginSample(int count, long duration, long timeStamp)
        {
            if (count <= 0)
                throw new ArgumentOutOfRangeException("count");

            if (_bytesToRead != 0)
                throw new InvalidOperationException("invalid call");

            if (_currentSample != null)
            {
                // Finish and enqueue previous current sample. It must have
                // collected some data at this point.
                Debug.Assert(_bytesToRead == 0);
                if (_currentSample.DataStream == null)
                    throw new InvalidOperationException("invalid sample");

                lock (_outputBuffer)
                {
                    Debug.Assert(_currentSample.DataStream.Length > 0);
                    _outputBuffer.Enqueue(_currentSample);
                }
            }

            _currentSample = _outputBuffer.AllocSample(timeStamp, duration, _bitrate, _hlsStream, count);
            _bytesToRead = count;
        }
 /// <summary>
 /// Discards current sample and any unused data. This method is called
 /// instead of Flush at the end of stream when caller is unable to verify
 /// integrity of last data. Typically it happens for any discontinuities in
 /// transport stream, like bitrate change, skipped segments, etc.
 /// </summary>
 public void DiscardLastData()
 {
     _byteBufferSize = 0;
     _bytesToRead = 0;
     if (null != _currentSample)
     {
         _currentSample.Discard();
     }
     _currentSample = null;
 }
        /// <summary>
        /// Produces next sample to play.
        /// </summary>
        /// <returns></returns>
        private Sample DequeueSample()
        {
            Sample sample = null;

            lock (this)
            {
                if (_samples.Count == 0)
                    return null;

                sample = _samples.Dequeue();

                _totalSizeOfSamples -= (int)sample.DataStream.Length;
                _bufferLevel -= sample.Duration;

                if (sample == _lastSample)
                    _lastSample = null;

                HLSTrace.WriteLineLow("DequeueSample from {0} to {1} timeline {2}", sample.Timestamp / ConvertHelper.MSInHNS, sample.AdjustedTimeStamp / ConvertHelper.MSInHNS, sample.TimelineIndex);

                if (null == _timelineTrackingInfo)
                {
                    _timelineTrackingInfo = new TimelineTrackingInfo(sample.TimelineIndex, sample.AdjustedTimeStamp, sample.AdjustedTimeStamp, null);
                }
                else
                {
                    if (sample.TimelineIndex != _timelineTrackingInfo.currentTimelineIndex)
                    {
                        _timelineTrackingInfo.previousTimeline = new TimelineTrackingInfo(_timelineTrackingInfo.currentTimelineIndex, _timelineTrackingInfo.timelineStartTimeStampHNS, _timelineTrackingInfo.timelineEndTimeStampHNS, null);
                        _timelineTrackingInfo.currentTimelineIndex = sample.TimelineIndex;
                        _timelineTrackingInfo.timelineStartTimeStampHNS = sample.AdjustedTimeStamp;
                        _timelineTrackingInfo.timelineEndTimeStampHNS = sample.AdjustedTimeStamp;
                    }
                    else
                    {
                        _timelineTrackingInfo.timelineEndTimeStampHNS = sample.AdjustedTimeStamp;
                    }
                }
            }

            return sample;
        }
        public void RecycleSample(Sample sample)
        {
            lock (this)
            {
                if (null != sample)
                {
                    if (null != sample.PoolItem)
                    {
                        // Return raw sample buffer to fifo pool
                        sample.PoolItem.Close();
                    }

                    // return sample wrapper to wrapper pool
                    _sampleWrapperPool.Enqueue(sample);
                    // If fifo pool has shrinked, shrink wrapper pool as well
                    int poolFreeSampleCount = _pool.PoolFreeCount;
                    while (poolFreeSampleCount < _sampleWrapperPool.Count)
                    {
                        _sampleWrapperPool.Dequeue();
                    }
                }
            }
        }
        /// <summary>
        /// Adds a new sample to the buffer.
        /// </summary>
        /// <param name="sample"></param>
        public void Enqueue(Sample sample)
        {
            lock (this)
            {
                Debug.Assert(_timelineInfoList.Count > sample.TimelineIndex);

                if (-1 == _timelineInfoList[sample.TimelineIndex]._timelineStartOffsetHNS)
                {
                    sample.AdjustedTimeStamp = sample.Timestamp;
                }
                else
                {
                    if (-1 == _timelineInfoList[sample.TimelineIndex]._timelineStartTimeStamp)
                    {
                        _timelineInfoList[sample.TimelineIndex]._timelineStartTimeStamp = sample.Timestamp;
                        sample.AdjustedTimeStamp = _timelineInfoList[sample.TimelineIndex]._timelineStartOffsetHNS;
                    }
                    else
                    {
                        sample.AdjustedTimeStamp = sample.Timestamp - _timelineInfoList[sample.TimelineIndex]._timelineStartTimeStamp + _timelineInfoList[sample.TimelineIndex]._timelineStartOffsetHNS;
                    }
                }

                if (null == _timelineTrackingInfo)
                {
                    _timelineTrackingInfo = new TimelineTrackingInfo(sample.TimelineIndex, sample.AdjustedTimeStamp, sample.AdjustedTimeStamp, null);
                }
                _timelineTrackingInfo.lastTimeStamp = sample.Timestamp;
                _timelineTrackingInfo.lastAdjustedTimeStamp = sample.AdjustedTimeStamp;
                _samples.Enqueue(sample);
                _totalSizeOfSamples += (int)sample.DataStream.Length;
                _bufferLevel += sample.Duration;
                _lastSample = sample;
                if (_hnsSegmentStart == -1 )
                {
                    _hnsSegmentStart = sample.AdjustedTimeStamp;
                }

                SegmentProgramDateTime segmentTime =sample.HLSStream.SegmentProgramTime;
                if ( segmentTime!= null && segmentTime.programTime.TsStartTime == DateTime.MinValue)
                {
                    segmentTime.programTime.TsStartTime = segmentTime.programTime.startTime + segmentTime.offset - TimeSpan.FromTicks(sample.AdjustedTimeStamp);
                }
            }

            HLSTrace.WriteLineLow("Enqueue sample timestamp {0} adjust to {1}, size {2}, timeline {3}", sample.Timestamp / ConvertHelper.MSInHNS, sample.AdjustedTimeStamp / ConvertHelper.MSInHNS, sample.DataStream.Length, sample.TimelineIndex);
        }
 public Sample AllocSample(long timestamp, long duration, uint bitrate, HLSStream hlsStream, int dataSize)
 {
     lock (this)
     {
         if (0 == _sampleWrapperPool.Count)
         {
             Sample sample = new Sample(timestamp, duration, bitrate, hlsStream, dataSize, _msd, this);
             return sample;
         }
         else
         {
             Sample sample = _sampleWrapperPool.Dequeue();
             sample.Reset(timestamp, duration, bitrate, hlsStream, dataSize, _msd, this);
             return sample;
         }
     }
 }