public FlvReader(FileInfo file, bool generateMetadata) { _file = file; FileStream fs = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 65536); //_reader = new AMFReader(file.Open(FileMode.Open)); _reader = new AMFReader(fs); _generateMetadata = generateMetadata; if (GetRemainingBytes() >= 9) DecodeHeader(); _keyframeMeta = AnalyzeKeyFrames(); }
public int Seek(int ts) { lock (_syncLock) { if (_keyFrameMeta == null) { if (!(_reader is IKeyFrameDataAnalyzer)) { // Seeking not supported return ts; } _keyFrameMeta = (_reader as IKeyFrameDataAnalyzer).AnalyzeKeyFrames(); } if (_keyFrameMeta.Positions.Length == 0) { // no video keyframe metainfo, it's an audio-only FLV // we skip the seek for now. // TODO add audio-seek capability return ts; } if (ts >= _keyFrameMeta.Duration) { // Seek at or beyond EOF _reader.Position = long.MaxValue; return (int) _keyFrameMeta.Duration; } int frame = 0; for (int i = 0; i < _keyFrameMeta.Positions.Length; i++) { if (_keyFrameMeta.Timestamps[i] > ts) { break; } frame = i; } _reader.Position = _keyFrameMeta.Positions[frame]; return _keyFrameMeta.Timestamps[frame]; } }
/// <summary> /// Key frames analysis may be used as a utility method so synchronize it. /// </summary> /// <returns></returns> public KeyFrameMeta AnalyzeKeyFrames() { lock (this.SyncRoot) { if (_keyframeMeta != null) return _keyframeMeta; // Lists of video positions and timestamps List<long> positionList = new List<long>(); List<int> timestampList = new List<int>(); // Lists of audio positions and timestamps List<long> audioPositionList = new List<long>(); List<int> audioTimestampList = new List<int>(); long origPos = GetCurrentPosition(); // point to the first tag SetCurrentPosition(9); // Maps positions to tags _posTagMap = new Dictionary<long,int>(); int idx = 0; bool audioOnly = true; while (this.HasMoreTags()) { long pos = GetCurrentPosition(); _posTagMap.Add(pos, idx++); // Read tag header and duration ITag tmpTag = this.ReadTagHeader(); _duration = tmpTag.Timestamp; if (tmpTag.DataType == IOConstants.TYPE_VIDEO) { if (audioOnly) { audioOnly = false; audioPositionList.Clear(); audioTimestampList.Clear(); } if (_firstVideoTag == -1) { _firstVideoTag = pos; } // Grab Frame type byte frametype = _reader.ReadByte(); if (((frametype & IOConstants.MASK_VIDEO_FRAMETYPE) >> 4) == IOConstants.FLAG_FRAMETYPE_KEYFRAME) { positionList.Add(pos); timestampList.Add(tmpTag.Timestamp); } } else if (tmpTag.DataType == IOConstants.TYPE_AUDIO) { if (_firstAudioTag == -1) { _firstAudioTag = pos; } if (audioOnly) { audioPositionList.Add(pos); audioTimestampList.Add(tmpTag.Timestamp); } } // This properly handles damaged FLV files - as far as duration/size is concerned long newPosition = pos + tmpTag.BodySize + 15; if (newPosition >= GetTotalBytes()) { #if !SILVERLIGHT log.Info("New position exceeds limit"); if (log.IsDebugEnabled) { log.Debug("Keyframe analysis"); log.Debug(" data type=" + tmpTag.DataType + " bodysize=" + tmpTag.BodySize); log.Debug(" remaining=" + GetRemainingBytes() + " limit=" + GetTotalBytes() + " new pos=" + newPosition + " pos=" + pos); } #endif break; } else { SetCurrentPosition(newPosition); } } // restore the pos SetCurrentPosition(origPos); _keyframeMeta = new KeyFrameMeta(); _keyframeMeta.Duration = _duration; _posTimeMap = new Dictionary<long,long>(); if (audioOnly) { // The flv only contains audio tags, use their lists to support pause and seeking positionList = audioPositionList; timestampList = audioTimestampList; } _keyframeMeta.AudioOnly = audioOnly; _keyframeMeta.Positions = new long[positionList.Count]; _keyframeMeta.Timestamps = new int[timestampList.Count]; for (int i = 0; i < _keyframeMeta.Positions.Length; i++) { _keyframeMeta.Positions[i] = (long)positionList[i]; _keyframeMeta.Timestamps[i] = (int)timestampList[i]; _posTimeMap.Add((long)positionList[i], (long)((int)timestampList[i])); } return _keyframeMeta; } }