private bool NextChunkByIndex(out int streamID, out StreamChunkInfo chunkInfo) { long minOffset = Int64.MaxValue; int minStreamID = -1; for (int i = 0; i < _streamList.Count; i++) { AVIStream stream = _streamList[i]; if (stream.ChunkIndex < stream.ChunkList.Count) { long thisOffset = stream.ChunkList[stream.ChunkIndex].Offset; if (thisOffset < minOffset) { minOffset = thisOffset; minStreamID = i; } } } if (minStreamID != -1) { streamID = minStreamID; chunkInfo = _streamList[streamID].ChunkList[_streamList[streamID].ChunkIndex]; _streamList[streamID].ChunkIndex++; return(true); } else { streamID = -1; chunkInfo = new StreamChunkInfo(); return(false); } }
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 WriteHeaders() { const uint AVIF_HASINDEX = 0x00000010; const uint AVIF_ISINTERLEAVED = 0x00000100; AVIMAINHEADER aviH = new AVIMAINHEADER(); AVIStream vidStr = _streamList[_videoStreamID]; AVISTREAMHEADER vidStrH = vidStr.Header; BITMAPINFOHEADER vidStrF = vidStr.VideoFormat; if (vidStrH.dwRate != 0) { aviH.dwMicroSecPerFrame = Convert.ToUInt32(((double)vidStrH.dwScale / vidStrH.dwRate) * 1000000.0); } aviH.dwFlags = AVIF_HASINDEX | AVIF_ISINTERLEAVED; aviH.dwTotalFrames = (uint)vidStr.ChunksInFirstMOVI; aviH.dwStreams = (uint)_streamList.Count; aviH.dwWidth = (uint)vidStrF.biWidth; aviH.dwHeight = (uint)Math.Abs(vidStrF.biHeight); Seek(_avihOffset); WriteChunk("avih", StructHelper <AVIMAINHEADER> .ToBytes(aviH, false)); for (int i = 0; i < _streamList.Count; i++) { AVIStream s = _streamList[i]; AVISTREAMHEADER sHeader = s.Header; sHeader.dwLength = (s.Type == AVIStreamType.Video) ? (uint)s.ChunkList.Count : CalculateDuration(s.ChunkList, 0, s.ChunkList.Count); sHeader.dwSuggestedBufferSize = FindLargestChunk(s.ChunkList); Seek(s.STRHOffset); WriteChunk("strh", StructHelper <AVISTREAMHEADER> .ToBytes(sHeader, false)); Seek(s.STRFOffset); WriteChunk("strf", s.MakeSTRFChunk()); if (s.OpenDMLSuperIndex != null) { Seek(s.INDXOffset); WriteChunk("indx", s.OpenDMLSuperIndex); } } Seek(_dmlhOffset); WriteChunk("dmlh", MakeDMLHChunk((uint)vidStr.ChunkList.Count)); }
private void StartFile() { if (_videoStreamID == -1) { throw new Exception("AVI must have a video stream."); } _isFileStarted = true; _riffOffset = StartList("RIFF", "AVI "); StartList("LIST", "hdrl"); _avihOffset = WriteChunk("avih", (uint)StructHelper <AVIMAINHEADER> .SizeOf); for (int i = 0; i < _streamList.Count; i++) { AVIStream s = _streamList[i]; StartList("LIST", "strl"); s.STRHOffset = WriteChunk("strh", (uint)StructHelper <AVISTREAMHEADER> .SizeOf); s.STRFOffset = WriteChunk("strf", s.MakeSTRFChunk()); if (s.STRNData != null) { WriteChunk("strn", s.STRNData); } s.INDXOffset = WriteChunk("JUNK", OpenDMLSuperIndexSize(MaxOpenDMLSuperIndexEntries)); EndList(); } StartList("LIST", "odml"); _dmlhOffset = WriteChunk("dmlh", MakeDMLHChunk(0)); EndList(); EndList(); // 'hdrl' // Pad to a multiple of 2K (8K at least) if ((_fileOffset < 8192) || ((_fileOffset % 2048) != 0)) { int paddingStart = (int)_fileOffset + 8; int toNext2K = (2048 - (paddingStart % 2048)) % 2048; int to8K = 8192 - paddingStart; WriteChunk("JUNK", (uint)Math.Max(to8K, toNext2K)); } _moviOffset = StartList("LIST", "movi"); }
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(); }
private byte[] MakeOldIndexChunk() { const uint AVIIF_KEYFRAME = 0x10; List <List <StreamChunkInfo> > cilList = new List <List <StreamChunkInfo> >(); List <int> cilIndex = new List <int>(); List <int> cilLength = new List <int>(); List <uint> cilFourCC = new List <uint>(); AVIOLDINDEXENTRY entry; byte[] buff; int i, entryCount, buffPos, u; entryCount = 0; for (i = 0; i < _streamList.Count; i++) { AVIStream s = _streamList[i]; if (s.ChunkList.Count > 0) { cilList.Add(s.ChunkList); cilIndex.Add(0); cilLength.Add(s.ChunksInFirstMOVI); cilFourCC.Add(s.FourCC); entryCount += s.ChunksInFirstMOVI; } } buffPos = 0; buff = new byte[entryCount * 16]; while (entryCount > 0) { // Find the chunk with the lowest offset u = 0; for (i = 1; i < cilList.Count; i++) { if (cilList[i][cilIndex[i]].Offset < cilList[u][cilIndex[u]].Offset) { u = i; } } entry.dwChunkID = cilFourCC[u]; entry.dwFlags = cilList[u][cilIndex[u]].IsKeyFrame ? AVIIF_KEYFRAME : 0; entry.dwOffset = (uint)(cilList[u][cilIndex[u]].Offset - (_moviOffset + 8)); entry.dwSize = cilList[u][cilIndex[u]].Size; BitConverterLE.WriteBytes(entry.dwChunkID, buff, buffPos); BitConverterLE.WriteBytes(entry.dwFlags, buff, buffPos + 4); BitConverterLE.WriteBytes(entry.dwOffset, buff, buffPos + 8); BitConverterLE.WriteBytes(entry.dwSize, buff, buffPos + 12); buffPos += 16; // If all the chunks from this stream have been written in the index, // stop checking this stream cilIndex[u]++; if (cilIndex[u] >= cilLength[u]) { cilList.RemoveAt(u); cilIndex.RemoveAt(u); cilLength.RemoveAt(u); cilFourCC.RemoveAt(u); } entryCount--; } return(buff); }