private static void ParseChunkIndex(AtomicBinaryReader reader, long p, ref AviStreamIndex index) { // read ix.. chunk id and size. do sanity check uint ixChunkFCC = reader.ReadUInt32(ref p); uint ixChunkFCCb = (ixChunkFCC & 0x0000FFFF) | 0x20200000; if (ixChunkFCCb != RiffParser.ToFourCC("ix ") && ixChunkFCC != RiffParser.ToFourCC("indx")) { throw new MpException("Unexpected chunk id for index " + RiffParser.FromFourCC(ixChunkFCC) + " for stream " + RiffParser.FromFourCC(index.streamId)); } uint ixChunkSize = reader.ReadUInt32(ref p); // read index data header and do sanity check ushort wLongsPerEntry = reader.ReadUInt16(ref p); byte bSubIndexType = reader.ReadByte(ref p); byte bIndexType = reader.ReadByte(ref p); uint nEntriesInUse = reader.ReadUInt32(ref p); uint streamId = reader.ReadUInt32(ref p); #if MP_DEBUG //Debug.Log("Parsing index for " + RiffParser.FromFourCC(index.streamId)); #endif if (bIndexType != (int)AviStreamIndex.Type.CHUNKS || bSubIndexType != 0 || streamId != index.streamId || wLongsPerEntry != 2 || ixChunkSize < 4 * wLongsPerEntry * nEntriesInUse + 24) { throw new MpException("Broken or unsupported index for stream " + RiffParser.FromFourCC(streamId) + ". " + streamId + "!=" + index.streamId + ", wLongsPerEntry=" + wLongsPerEntry + ", bIndexType=" + bIndexType + ", bSubIndexType=" + bSubIndexType); } long qwBaseOffset = reader.ReadInt64(ref p); p += 4; // not caring about reserved bytes // reading it all at once is about 10x faster than reading individual uints. // the index chunk is not that big, so it's ok for GC too. var uintBuf = new uint[nEntriesInUse * 2]; reader.Read(ref p, uintBuf, 0, (int)nEntriesInUse * 2); for (int i = 0; i < nEntriesInUse; i++) { var entry = new AviStreamIndex.Entry(); entry.chunkOffset = qwBaseOffset + uintBuf [2 * i]; uint len = uintBuf [2 * i + 1]; entry.chunkLength = (int)(len & 0x7FFFFFFF); if ((len & 0x80000000) == 0) { entry.isKeyframe = true; } index.entries.Add(entry); } }
private static void WriteChunkIndex(RiffWriter rw, AviStreamIndex index, int superIndexChunkOffset, ref int superIndexEntryCount, long indexBaseOffset, int maxSuperindexEntries) { var bw = rw.binaryWriter; // the offset where this index will be written long streamIndexOffset = bw.Seek(0, SeekOrigin.Current); // update stream superindex superIndexEntryCount++; if (superIndexEntryCount > maxSuperindexEntries) { throw new MpException("Not enough space was reserved for superindex. Please increase maxSuperindexEntries"); } bw.Seek(superIndexChunkOffset + 1 * 4, SeekOrigin.Begin); bw.Write(superIndexEntryCount); // overwrite nEntriesInUse bw.Seek(superIndexChunkOffset + 6 * 4 + (superIndexEntryCount - 1) * 16, SeekOrigin.Begin); bw.Write(streamIndexOffset); bw.Write(32 + 8 * index.entries.Count); // dwSize bw.Write(index.entries.Count); // dwDuration in stream ticks. @todo this is OK only for video, for audio stream this should be ??? // write stream chunk index // @xxx MSDN suggests not seeking BaseStream when using BinaryWriter, but there are no Seek(long) // in BinaryWriter. According to this forum post, BinaryWriter.Seek is just a wrapper // to BinaryWriter.BaseStream.Seek, so all should be ok. // http://www.pcreview.co.uk/forums/binarywriter-seek-vs-binarywriter-basestream-seek-t1223754.html bw.BaseStream.Seek(streamIndexOffset, SeekOrigin.Begin); rw.BeginChunk((RiffParser.ToFourCC("ix__") & 0x0000FFFF) | ((index.streamId << 16) & 0xFFFF0000)); bw.Write((short)2); // wLongsPerEntry is always 2 here bw.Write((byte)0); // bIndexSubType is always 0 here bw.Write((byte)AviStreamIndex.Type.CHUNKS); // bIndexType = AVI_INDEX_OF_CHUNKS bw.Write(index.entries.Count); // nEntriesInUse. bw.Write(index.streamId); // dwChunkId ("##dc" and similar) bw.Write(indexBaseOffset); // qwBaseOffset bw.Write((int)0); // dwReserved3 foreach (var entry in index.entries) { long offset = entry.chunkOffset - indexBaseOffset; if (offset > int.MaxValue) { throw new MpException("Internal error. Can't write index, because chunk offset won't fit into 31 bits: " + offset); } bw.Write((uint)offset); // bit31==0 indicating that this is a keyframe bw.Write(entry.chunkLength); } rw.EndChunk(); index.globalOffset += index.entries.Count; index.entries.Clear(); }