private static AviStreamIndex ParseOldIndex(long idx1Offset, AtomicBinaryReader abr, int size, uint streamId, long idx1EntryOffset) { int count = (int)(size / 16); var index = new AviStreamIndex(); index.streamId = streamId; index.entries.Capacity = count; // less memory allocation, more used temporarily long p = idx1Offset; var uintBuf = new uint[count * 4]; abr.Read(ref p, uintBuf, 0, count * 4); for (int i = 0; i < count; i++) { uint ckid = uintBuf [i * 4]; if (ckid == streamId || (ckid == AviDemux.ID_00db && streamId == AviDemux.ID_00dc)) { var entry = new AviStreamIndex.Entry(); entry.isKeyframe = (uintBuf [i * 4 + 1] & 0x00000010) != 0; entry.chunkOffset = idx1EntryOffset + uintBuf [i * 4 + 2]; entry.chunkLength = (int)uintBuf [i * 4 + 3]; index.entries.Add(entry); } } return(index); }
public override void Init(Stream dstStream, VideoStreamInfo videoStreamInfo, AudioStreamInfo audioStreamInfo) { if (dstStream == null || videoStreamInfo == null) { throw new ArgumentException("At least destination stream and video stream info is needed"); } base.Init(dstStream, videoStreamInfo, audioStreamInfo); usingMultipleRiffs = false; totalFramesOld = 0; totalFrames = 0; totalSamples = 0; writer = new RiffWriter(dstStream); writer.BeginRiff(AviDemux.ID_AVI_); writer.BeginList(AviDemux.ID_hdrl); // main header offsets.avih = WriteMainHeader(writer, videoStreamInfo, hasAudioStream); // video stream header writer.BeginList(AviDemux.ID_strl); offsets.videoStrh = WriteVideoStreamHeader(writer, videoStreamInfo); WriteVideoFormatHeader(writer, videoStreamInfo); offsets.videoIndx = WriteDummySuperIndex(writer, AviDemux.ID_00dc, maxSuperindexEntries); videoSuperIndexEntryCount = 0; writer.EndList(); // end of strl videoIndex = new AviStreamIndex(); videoIndex.streamId = AviDemux.ID_00dc; if (hasAudioStream) { // audio stream header writer.BeginList(AviDemux.ID_strl); offsets.audioStrh = WriteAudioStreamHeader(writer, audioStreamInfo); WriteAudioFormatHeader(writer, audioStreamInfo); offsets.audioIndx = WriteDummySuperIndex(writer, AviDemux.ID_01wb, maxSuperindexEntries); audioSuperIndexEntryCount = 0; writer.EndList(); // end of strl audioIndex = new AviStreamIndex(); audioIndex.streamId = AviDemux.ID_01wb; } // odml header writer.BeginList(AviDemux.ID_odml); offsets.dmlh = WriteDmlhHeader(writer, videoStreamInfo.frameCount); writer.EndList(); writer.EndList(); // end of hdrl writer.BeginList(AviDemux.ID_movi); offsets.indexBase = writer.binaryWriter.Seek(0, SeekOrigin.Current); }
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(); }
private static AviStreamIndex ParseOdmlIndex(AtomicBinaryReader reader, long p, out uint streamId) { ushort wLongsPerEntry = reader.ReadUInt16(ref p); byte bSubIndexType = reader.ReadByte(ref p); byte bIndexType = reader.ReadByte(ref p); uint nEntriesInUse = reader.ReadUInt32(ref p); streamId = reader.ReadUInt32(ref p); var index = new AviStreamIndex(); index.streamId = streamId; // if there is AVI_INDEX_OF_CHUNKS (superindex) in this element if (bIndexType == (byte)AviStreamIndex.Type.SUPERINDEX) { p += 3 * 4; // not caring about reserved bytes #if MP_DEBUG //Debug.Log("Parsing superindex for " + RiffParser.FromFourCC(streamId)); #endif // sanity check if (bSubIndexType != 0 || wLongsPerEntry != 4) { #if MP_DEBUG Debug.LogWarning("Broken superindex for stream " + RiffParser.FromFourCC(streamId) + ", but trying to continue. " + bSubIndexType + " " + wLongsPerEntry); #endif } for (uint i = 0; i < nEntriesInUse; i++) { long qwOffset = reader.ReadInt64(ref p); int dwSize = reader.ReadInt32(ref p); reader.ReadInt32(ref p); // dwDuration. don't care if (qwOffset != 0) { long currentStreamPos = p; p = qwOffset; // reduce memory allocations by (over)estimating entry count from index size in bytes index.entries.Capacity += dwSize / 8; ParseChunkIndex(reader, p, ref index); p = currentStreamPos; } } } // if there is AVI_INDEX_OF_CHUNKS (chunk index) in here else if (bIndexType == (byte)AviStreamIndex.Type.CHUNKS) { // seek back to the beginning of this chunk (12bytes read here, 8bytes read by RiffParser) ParseChunkIndex(reader, p - 20, ref index); } else { throw new MpException("Unsupported index type " + bIndexType + " encountered for stream " + RiffParser.FromFourCC(streamId)); } index.entries.TrimExcess(); return(index); }
public override void Init(Stream dstStream, VideoStreamInfo videoStreamInfo, AudioStreamInfo audioStreamInfo) { if (dstStream == null || videoStreamInfo == null) { throw new ArgumentException ("At least destination stream and video stream info is needed"); } base.Init (dstStream, videoStreamInfo, audioStreamInfo); usingMultipleRiffs = false; totalFramesOld = 0; totalFrames = 0; totalSamples = 0; writer = new RiffWriter (dstStream); writer.BeginRiff (AviDemux.ID_AVI_); writer.BeginList (AviDemux.ID_hdrl); // main header offsets.avih = WriteMainHeader (writer, videoStreamInfo, hasAudioStream); // video stream header writer.BeginList (AviDemux.ID_strl); offsets.videoStrh = WriteVideoStreamHeader (writer, videoStreamInfo); WriteVideoFormatHeader (writer, videoStreamInfo); offsets.videoIndx = WriteDummySuperIndex (writer, AviDemux.ID_00dc, maxSuperindexEntries); videoSuperIndexEntryCount = 0; writer.EndList (); // end of strl videoIndex = new AviStreamIndex (); videoIndex.streamId = AviDemux.ID_00dc; if (hasAudioStream) { // audio stream header writer.BeginList (AviDemux.ID_strl); offsets.audioStrh = WriteAudioStreamHeader (writer, audioStreamInfo); WriteAudioFormatHeader (writer, audioStreamInfo); offsets.audioIndx = WriteDummySuperIndex (writer, AviDemux.ID_01wb, maxSuperindexEntries); audioSuperIndexEntryCount = 0; writer.EndList (); // end of strl audioIndex = new AviStreamIndex (); audioIndex.streamId = AviDemux.ID_01wb; } // odml header writer.BeginList (AviDemux.ID_odml); offsets.dmlh = WriteDmlhHeader (writer, videoStreamInfo.frameCount); writer.EndList (); writer.EndList (); // end of hdrl writer.BeginList (AviDemux.ID_movi); offsets.indexBase = writer.binaryWriter.Seek (0, SeekOrigin.Current); }
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 (); }