/// <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; } } }