private void MakeOpenDMLIndexes() { for (int iStream = 0; iStream < _streamList.Count; iStream++) { const byte AVI_INDEX_OF_INDEXES = 0x00; AVIStream stream = _streamList[iStream]; List <StreamChunkInfo> chunkList = stream.ChunkList; AVISUPERINDEX header = new AVISUPERINDEX(); AVISUPERINDEXENTRY entry; int entriesPerChunk, entryOffset, chunksLeft, buffPos; uint indexChunkID = AVIHelper.StreamFourCC(AVIHelper.StreamID(stream.FourCC, false), "ix", true); byte[] buff; buffPos = 0; buff = new byte[OpenDMLSuperIndexSize(MaxOpenDMLSuperIndexEntries)]; entriesPerChunk = (int)CalculateOpenDMLStandardIndexEntryCount(chunkList); entryOffset = 0; chunksLeft = chunkList.Count; header.wLongsPerEntry = 4; header.bIndexSubType = 0; header.bIndexType = AVI_INDEX_OF_INDEXES; header.nEntriesInUse = (uint)((chunksLeft + (entriesPerChunk - 1)) / entriesPerChunk); header.dwChunkID = stream.FourCC; if (header.nEntriesInUse > MaxOpenDMLSuperIndexEntries) { throw new Exception("Too many super-index entries."); } buffPos += StructHelper <AVISUPERINDEX> .ToBytes(header, buff, buffPos, false); while (chunksLeft > 0) { int length = Math.Min(entriesPerChunk, chunksLeft); byte[] tmp = MakeOpenDMLStandardIndexChunk(chunkList, entryOffset, length, stream.FourCC); CheckExtend((uint)tmp.Length); entry.qwOffset = (ulong)WriteChunk(indexChunkID, tmp); entry.dwSize = (uint)tmp.Length + 8; entry.dwDuration = (stream.Type == AVIStreamType.Video) ? (uint)length : CalculateDuration(chunkList, entryOffset, length); BitConverterLE.WriteBytes(entry.qwOffset, buff, buffPos); BitConverterLE.WriteBytes(entry.dwSize, buff, buffPos + 8); BitConverterLE.WriteBytes(entry.dwDuration, buff, buffPos + 12); buffPos += 16; entryOffset += length; chunksLeft -= length; } stream.OpenDMLSuperIndex = buff; } }
private void ParseOldIndex(byte[] data) { const uint AVIIF_KEYFRAME = 0x10; int dataPos = 0; StreamChunkInfo chunkInfo = new StreamChunkInfo(); AVIOLDINDEXENTRY entry; int streamID; long firstVideoChunkOffset = -1; long offsetCorrection; while (dataPos <= (data.Length - 16)) { entry.dwChunkID = BitConverterLE.ToUInt32(data, dataPos); entry.dwFlags = BitConverterLE.ToUInt32(data, dataPos + 4); entry.dwOffset = BitConverterLE.ToUInt32(data, dataPos + 8); entry.dwSize = BitConverterLE.ToUInt32(data, dataPos + 12); dataPos += 16; streamID = AVIHelper.StreamID(entry.dwChunkID, false); if ((streamID == -1) || (streamID >= _streamList.Count)) { continue; } if (_streamList[streamID].FourCC == 0) { _streamList[streamID].FourCC = entry.dwChunkID; } chunkInfo.Offset = (long)entry.dwOffset; chunkInfo.Size = entry.dwSize; chunkInfo.IsKeyFrame = (entry.dwFlags & AVIIF_KEYFRAME) != 0; if ((firstVideoChunkOffset == -1) && (streamID == _videoStreamID)) { firstVideoChunkOffset = chunkInfo.Offset; } _streamList[streamID].ChunkList.Add(chunkInfo); } if ((_firstVideoChunkOffset == -1) || (firstVideoChunkOffset == -1)) { throw new Exception("Video stream not found."); } // Add 8 because the offset needs to point to the start of the data offsetCorrection = (_firstVideoChunkOffset - firstVideoChunkOffset) + 8; foreach (AVIStream stream in _streamList) { for (int i = 0; i < stream.ChunkList.Count; i++) { chunkInfo = stream.ChunkList[i]; chunkInfo.Offset += offsetCorrection; stream.ChunkList[i] = chunkInfo; } } }
private void ParseOpenDMLStandardIndex(byte[] data) { AVISTDINDEX header; AVISTDINDEXENTRY entry; int dataPos = 0; int streamID; List <StreamChunkInfo> chunkList; StreamChunkInfo ci = new StreamChunkInfo(); header = StructHelper <AVISTDINDEX> .FromBytes(data, 0, false); dataPos += StructHelper <AVISTDINDEX> .SizeOf; streamID = AVIHelper.StreamID(header.dwChunkID, false); if ((streamID == -1) || (streamID >= _streamList.Count)) { throw new Exception("Invalid chunk ID in OpenDML standard index."); } if (_streamList[streamID].FourCC == 0) { _streamList[streamID].FourCC = header.dwChunkID; } chunkList = _streamList[streamID].ChunkList; while (dataPos <= (data.Length - 8)) { if (header.nEntriesInUse == 0) { break; } entry.dwOffset = BitConverterLE.ToUInt32(data, dataPos); entry.dwSize = BitConverterLE.ToUInt32(data, dataPos + 4); dataPos += 8; ci.Offset = (long)(header.qwBaseOffset + entry.dwOffset); ci.Size = entry.dwSize & 0x7FFFFFFF; ci.IsKeyFrame = ((entry.dwSize & 0x80000000) == 0); chunkList.Add(ci); header.nEntriesInUse--; } }
private bool NextChunkByMOVI(out int streamID, out StreamChunkInfo chunkInfo) { uint chunkID, dataSize, listType; streamID = -1; chunkInfo = new StreamChunkInfo(); Seek(_nextChunkOffset); while (true) { chunkInfo.Offset = ReadChunkHeader(out chunkID, out dataSize, out listType); if (chunkInfo.Offset == -1) { return(false); } chunkInfo.Offset += 8; if ((chunkID == ckIDLIST) || (chunkID == ckIDRIFF)) { continue; } streamID = AVIHelper.StreamID(chunkID, false); if ((streamID != -1) && (streamID < _streamList.Count)) { if (_streamList[streamID].FourCC == 0) { _streamList[streamID].FourCC = chunkID; } chunkInfo.Size = dataSize; chunkInfo.IsKeyFrame = _streamList[streamID].IsFirstChunk || (_streamList[streamID].Type != AVIStreamType.Video); _streamList[streamID].IsFirstChunk = false; _nextChunkOffset = _fileOffset + ((dataSize + 1) & 0xFFFFFFFE); return(true); } SkipChunkData(dataSize); } }
private void ReadHeaders() { uint chunkID, dataSize, listType; long chunkOffset, firstRIFFEnd, moviEnd; byte[] data; bool inMOVI; moviEnd = -1; chunkOffset = ReadChunkHeader(out chunkID, out dataSize, out listType); if ((chunkOffset == -1) || (chunkID != AVIHelper.FourCC("RIFF")) || (listType != AVIHelper.FourCC("AVI "))) { throw new Exception("File isn't an AVI."); } firstRIFFEnd = _fileOffset + dataSize; while (_fileOffset < firstRIFFEnd) { chunkOffset = ReadChunkHeader(out chunkID, out dataSize, out listType); if (chunkOffset == -1) { break; } if (chunkID == ckIDLIST) { if (listType == AVIHelper.FourCC("movi")) { if (_videoStreamID == -1) { throw new Exception("Video stream not found."); } _moviOffset = chunkOffset; moviEnd = _fileOffset + dataSize; } continue; } inMOVI = (_moviOffset != -1) && (chunkOffset >= _moviOffset) && (chunkOffset < moviEnd); if (!inMOVI) { data = ReadChunkData(dataSize); if (data.Length < dataSize) { break; } } else { SkipChunkData(dataSize); data = null; } if (chunkID == AVIHelper.FourCC("strh")) { AVISTREAMHEADER strH = StructHelper <AVISTREAMHEADER> .FromBytes(data, 0, false); AVIStream s; if (strH.fccType == AVIHelper.FourCC("vids")) { s = new AVIStream(AVIStreamType.Video); if (_videoStreamID == -1) { _videoStreamID = _streamList.Count; } } else if (strH.fccType == AVIHelper.FourCC("auds")) { s = new AVIStream(AVIStreamType.Audio); } else { s = new AVIStream(AVIStreamType.Other); } s.Header = strH; _streamList.Add(s); } if (chunkID == AVIHelper.FourCC("strf")) { AVIStream stream = _streamList[_streamList.Count - 1]; int fmtSize = 0; int fmtExtraSize; if (stream.Type == AVIStreamType.Video) { stream.VideoFormat = StructHelper <BITMAPINFOHEADER> .FromBytes(data, 0, false); fmtSize = StructHelper <BITMAPINFOHEADER> .SizeOf; } else if (stream.Type == AVIStreamType.Audio) { stream.AudioFormat = StructHelper <WAVEFORMATEX> .FromBytes(data, 0, false); fmtSize = StructHelper <WAVEFORMATEX> .SizeOf; } else { fmtSize = 0; } fmtExtraSize = data.Length - fmtSize; if (fmtExtraSize > 0) { stream.FormatExtra = new byte[fmtExtraSize]; Buffer.BlockCopy(data, fmtSize, stream.FormatExtra, 0, fmtExtraSize); } } if (chunkID == AVIHelper.FourCC("strn")) { _streamList[_streamList.Count - 1].STRNData = data; } if (inMOVI && (AVIHelper.StreamID(chunkID, false) == _videoStreamID)) { _firstVideoChunkOffset = chunkOffset; Seek(moviEnd); } if (chunkID == AVIHelper.FourCC("indx")) { _isOpenDML = true; _foundIndex = ParseOpenDMLIndex(data); } if (chunkID == AVIHelper.FourCC("idx1")) { if (!_isOpenDML) { ParseOldIndex(data); _foundIndex = true; } } } if (_moviOffset == -1) { throw new Exception("\"movi\" list not found."); } SeekToStart(); }