public bool WriteLookbackVideoFrame(int frame) { // if given frame number is negative, use it as relative lookback index // (useful when writing frames sequentially using WriteNextVideoFrame) if (frame < 0) { frame = totalFrames - frame; } int i = frame - videoIndex.globalOffset; if (i < 0 || i >= videoIndex.entries.Count) { // can't look up that frame, it's in different RIFF block return(false); } var lookbackEntry = videoIndex.entries [i]; var entry = new AviStreamIndex.Entry(); entry.chunkOffset = lookbackEntry.chunkOffset; entry.chunkLength = lookbackEntry.chunkLength; videoIndex.entries.Add(entry); totalFrames++; if (!usingMultipleRiffs) { totalFramesOld++; } return(true); }
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 WriteNextVideoFrame(byte[] frameBytes, int size = -1) { // the 'movi' element is getting too big (1Gb+). // close it and start new RIFF AVIX element if (writer.currentElementSize > maxRiffElementSize) { StartNewRiff(); } if (size < 0) { size = frameBytes.Length; } var entry = new AviStreamIndex.Entry(); entry.chunkOffset = writer.binaryWriter.Seek(0, SeekOrigin.Current) + 8; entry.chunkLength = size; videoIndex.entries.Add(entry); writer.WriteChunk(AviDemux.ID_00dc, frameBytes, size); totalFrames++; if (!usingMultipleRiffs) { totalFramesOld++; } }
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); } }
// NB! always provide a multiple of sampleSize bytes! public override void WriteNextAudioSamples(byte[] sampleBytes, int size = -1) { // the 'movi' element is getting too big (1Gb+). // close it and start new RIFF AVIX element if (writer.currentElementSize > maxRiffElementSize) { StartNewRiff(); } if (size < 0) { size = sampleBytes.Length; } var entry = new AviStreamIndex.Entry(); entry.chunkOffset = writer.binaryWriter.Seek(0, SeekOrigin.Current) + 8; entry.chunkLength = size; audioIndex.entries.Add(entry); writer.WriteChunk(AviDemux.ID_01wb, sampleBytes); totalSamples += size / audioStreamInfo.sampleSize; // expecting this to be exact }
public override int ReadAudioSamples(out byte[] targetBuf, int sampleCount) { // usually 1, 2 or 4 for PCM audio (eg 16bit*2channels=4) int audioSampleSize = audioStreamInfo.sampleSize; int count = sampleCount * audioSampleSize; if (rawAudioBuf == null || rawAudioBuf.Length < count) { rawAudioBuf = new byte[count]; } long position = nextAudioSample * audioSampleSize; nextAudioSample += sampleCount; // sanity check if (rawAudioBuf.Length < count) { //count = rawAudioBuf.Length; throw new ArgumentException("array.Length < count"); } // find the <pos> where <chunk> contains the first byte requested. <chunk>=index[audioByteIndex[pos]] //int pos = audioByteIndex.BinarySearch (position); int pos = Array.BinarySearch(audioByteIndex, position); if (pos == -1) { // Sanity check, this should never happen. // Only happens when this.position<audioByteIndex[0], but audioByteIndex[0] is always 0 and this.position>=0 throw new MpException("audioByteIndex is corrupted"); } // BinarySearch returns negative values if the match was not exact. Convert it to positive. if (pos < 0) { pos = -pos - 2; } // now read all the requested bytes into target rawAudioBuf int writeOffset = 0; long readOffset = position; int bytesLeftToRead = count; int totalBytesActuallyRead = 0; int offsetInChunk, bytesToRead, bytesActuallyRead; do { AviStreamIndex.Entry chunkIndex = avi.audioIndex.entries [pos]; offsetInChunk = (int)(readOffset - audioByteIndex [pos]); bytesToRead = chunkIndex.chunkLength - offsetInChunk; if (bytesToRead > bytesLeftToRead) { bytesToRead = bytesLeftToRead; } //UnityEngine.Debug.Log ("READ " + offsetInChunk+"("+(chunkIndex.chunkOffset + offsetInChunk)+"):"+bytesToRead+"@chunk_"+pos + // " into " + writeOffset + ":" + bytesToRead + "@buffer"); long offs = chunkIndex.chunkOffset + offsetInChunk; bytesActuallyRead = reader.Read(ref offs, rawAudioBuf, writeOffset, bytesToRead); totalBytesActuallyRead += bytesActuallyRead; //Debug.Log ("Actually read " + bytesActuallyRead); bytesLeftToRead -= bytesActuallyRead; offsetInChunk += bytesActuallyRead; writeOffset += bytesActuallyRead; readOffset += bytesActuallyRead; // if current chunk is fully read, only then advance to next index chunk if (offsetInChunk >= (int)chunkIndex.chunkLength) { pos++; } } while(bytesLeftToRead > 0 && bytesActuallyRead == bytesToRead && pos < audioByteIndex.Length); targetBuf = rawAudioBuf; return(totalBytesActuallyRead / audioSampleSize); }
public override void WriteNextVideoFrame(byte[] frameBytes, int size = -1) { // the 'movi' element is getting too big (1Gb+). // close it and start new RIFF AVIX element if (writer.currentElementSize > maxRiffElementSize) StartNewRiff (); if (size < 0) size = frameBytes.Length; var entry = new AviStreamIndex.Entry (); entry.chunkOffset = writer.binaryWriter.Seek (0, SeekOrigin.Current) + 8; entry.chunkLength = size; videoIndex.entries.Add (entry); writer.WriteChunk (AviDemux.ID_00dc, frameBytes, size); totalFrames++; if (!usingMultipleRiffs) totalFramesOld++; }
// NB! always provide a multiple of sampleSize bytes! public override void WriteNextAudioSamples(byte[] sampleBytes, int size = -1) { // the 'movi' element is getting too big (1Gb+). // close it and start new RIFF AVIX element if (writer.currentElementSize > maxRiffElementSize) StartNewRiff (); if (size < 0) size = sampleBytes.Length; var entry = new AviStreamIndex.Entry (); entry.chunkOffset = writer.binaryWriter.Seek (0, SeekOrigin.Current) + 8; entry.chunkLength = size; audioIndex.entries.Add (entry); writer.WriteChunk (AviDemux.ID_01wb, sampleBytes); totalSamples += size / audioStreamInfo.sampleSize; // expecting this to be exact }
public bool WriteLookbackVideoFrame(int frame) { // if given frame number is negative, use it as relative lookback index // (useful when writing frames sequentially using WriteNextVideoFrame) if (frame < 0) frame = totalFrames - frame; int i = frame - videoIndex.globalOffset; if (i < 0 || i >= videoIndex.entries.Count) { // can't look up that frame, it's in different RIFF block return false; } var lookbackEntry = videoIndex.entries [i]; var entry = new AviStreamIndex.Entry (); entry.chunkOffset = lookbackEntry.chunkOffset; entry.chunkLength = lookbackEntry.chunkLength; videoIndex.entries.Add (entry); totalFrames++; if (!usingMultipleRiffs) totalFramesOld++; return true; }