www.pudn.com > mediator15src.zip > AVIOutput.cpp
/* * AVIOutput.cpp * Copyright (C) 1998-2001 Avery Lee * * This file is part of MPEG Mediator, a free MPEG stream converter. * * MPEG Mediator is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * MPEG Mediator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include#include "stdafx.h" #include #include "error.h" #include "AVIIndex.h" #include "AVIOutput.h" typedef __int64 QUADWORD; // The following comes from the OpenDML 1.0 spec for extended AVI files // bIndexType codes // #define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex // array points to an index chunk #define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex // array points to a chunk in the file #define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is // really the data // bIndexSubtype codes for INDEX_OF_CHUNKS #define AVI_INDEX_2FIELD 0x01 // when fields within frames // are also indexed struct _avisuperindex_entry { QUADWORD qwOffset; // absolute file offset, offset 0 is // unused entry?? DWORD dwSize; // size of index chunk at this offset DWORD dwDuration; // time span in stream ticks }; struct _avistdindex_entry { DWORD dwOffset; // qwBaseOffset + this is absolute file offset DWORD dwSize; // bit 31 is set if this is NOT a keyframe }; #pragma pack(push) #pragma pack(2) typedef struct _avisuperindex_chunk { FOURCC fcc; // ’ix##’ DWORD cb; // size of this structure WORD wLongsPerEntry; // must be 4 (size of each entry in aIndex array) BYTE bIndexSubType; // must be 0 or AVI_INDEX_2FIELD BYTE bIndexType; // must be AVI_INDEX_OF_INDEXES DWORD nEntriesInUse; // number of entries in aIndex array that // are used DWORD dwChunkId; // ’##dc’ or ’##db’ or ’##wb’, etc DWORD dwReserved[3]; // must be 0 struct _avisuperindex_entry aIndex[]; } AVISUPERINDEX, * PAVISUPERINDEX; typedef struct _avistdindex_chunk { FOURCC fcc; // ’ix##’ DWORD cb; WORD wLongsPerEntry; // must be sizeof(aIndex[0])/sizeof(DWORD) BYTE bIndexSubType; // must be 0 BYTE bIndexType; // must be AVI_INDEX_OF_CHUNKS DWORD nEntriesInUse; // DWORD dwChunkId; // ’##dc’ or ’##db’ or ’##wb’ etc.. QUADWORD qwBaseOffset; // all dwOffsets in aIndex array are // relative to this DWORD dwReserved3; // must be 0 struct _avistdindex_entry aIndex[]; } AVISTDINDEX, * PAVISTDINDEX; #pragma pack(pop) /////////////////////////////////////////// AVIOutputStream::AVIOutputStream(class AVIOutput *output) { this->output = output; format = NULL; } AVIOutputStream::~AVIOutputStream() { delete format; } BOOL AVIOutputStream::_write(FOURCC ckid, LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer) { output->writeIndexedChunk(ckid, dwIndexFlags, lpBuffer, cbBuffer); // output->writeIndexedChunk('bd00', dwIndexFlags, lpBuffer, cbBuffer); return TRUE; } BOOL AVIOutputStream::finalize() { _RPT0(0,"AVIOutputStream: finalize()\n"); return TRUE; } //////////////////////////////////// AVIAudioOutputStream::AVIAudioOutputStream(class AVIOutput *out) : AVIOutputStream(out) { lTotalBytesWritten = 0; } BOOL AVIAudioOutputStream::write(LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer, LONG lSamples) { BOOL success; success = _write(mmioFOURCC('0','1','w','b'), dwIndexFlags, lpBuffer, cbBuffer); if (success) lTotalBytesWritten += cbBuffer; return success; } BOOL AVIAudioOutputStream::finalize() { _RPT0(0,"AVIAudioOutputStream: finalize()\n"); if (!lTotalBytesWritten) streamInfo.dwLength = 1; else streamInfo.dwLength = lTotalBytesWritten / (__int64)getWaveFormat()->nBlockAlign ; streamInfo.dwRate = getWaveFormat()->nAvgBytesPerSec; streamInfo.dwScale = getWaveFormat()->nBlockAlign; return TRUE; } BOOL AVIAudioOutputStream::flush() { return TRUE; } //////////////////////////////////// AVIVideoOutputStream::AVIVideoOutputStream(class AVIOutput *out) : AVIOutputStream(out) { lTotalSamplesWritten = 0; } BOOL AVIVideoOutputStream::write(LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer, LONG lSamples) { BOOL success; success = _write(id, dwIndexFlags, lpBuffer, cbBuffer); if (success) lTotalSamplesWritten += lSamples; return success; } BOOL AVIVideoOutputStream::finalize() { _RPT0(0,"AVIVideoOutputStream: finalize()\n"); if (!lTotalSamplesWritten) streamInfo.dwLength = 1; else streamInfo.dwLength = lTotalSamplesWritten; return TRUE; } //////////////////////////////////// char AVIOutput::szME[]="AVIOutput"; AVIOutput::AVIOutput() { audioOut = NULL; videoOut = NULL; } AVIOutput::~AVIOutput() { _RPT0(0,"AVIOutput::~AVIOutput()\n"); delete audioOut; delete videoOut; } AVIOutputFile::AVIOutputFile() { hFile = NULL; index = NULL; index_audio = NULL; index_video = NULL; fCaching = TRUE; i64FilePosition = 0; i64XBufferLevel = 0; xblock = 0; fExtendedAVI = true; lAVILimit = 0x7F000000L; fCaptureMode = false; iPadOffset = 0; pSegmentHint = NULL; cbSegmentHint = 0; fInitComplete = false; pHeaderBlock = new char[65536]; nHeaderLen = 0; i64FarthestWritePoint = 0; lLargestIndexDelta[0] = 0; lLargestIndexDelta[1] = 0; i64FirstIndexedChunk[0] = 0; i64FirstIndexedChunk[1] = 0; i64LastIndexedChunk[0] = 0; i64LastIndexedChunk[1] = 0; lIndexedChunkCount[0] = 0; lIndexedChunkCount[1] = 0; lIndexSize = 0; fPreemptiveExtendFailed = false; } AVIOutputFile::~AVIOutputFile() { delete index; delete index_audio; delete index_video; delete pSegmentHint; delete []pHeaderBlock; _RPT0(0,"AVIOutputFile: destructor called\n"); if (hFile) { LONG lHi = (LONG)(i64FarthestWritePoint>>32); DWORD dwError; if (0xFFFFFFFF != SetFilePointer(hFile, (LONG)i64FarthestWritePoint, &lHi, FILE_BEGIN) || (dwError = GetLastError()) != NO_ERROR) { SetEndOfFile(hFile); } } if (hFile) CloseHandle(hFile); } ////////////////////////////////// BOOL AVIOutputFile::initOutputStreams() { if (!(audioOut = new AVIAudioOutputStream(this))) return FALSE; if (!(videoOut = new AVIVideoOutputStream(this))) return FALSE; return TRUE; } void AVIOutputFile::disable_os_caching() { fCaching = FALSE; lChunkSize = 0; } void AVIOutputFile::disable_extended_avi() { fExtendedAVI = false; } void AVIOutputFile::set_1Gb_limit() { lAVILimit = 0x3F000000L; } void AVIOutputFile::set_chunk_size(long l) { lChunkSize = l; } void AVIOutputFile::set_capture_mode(bool b) { fCaptureMode = b; } void AVIOutputFile::setSegmentHintBlock(bool fIsFinal, const char *pszNextPath, int cbBlock) { if (!pSegmentHint) if (!(pSegmentHint = new char[cbBlock])) throw MyError("AVIOutputFile::setSegmentHintBlock - Error allocating memory"); cbSegmentHint = cbBlock; memset(pSegmentHint, 0, cbBlock); pSegmentHint[0] = !fIsFinal; if (pszNextPath) strcpy(pSegmentHint+1, pszNextPath); } // I don't like to bitch about other programs (well, okay, so I do), but // Windows Media Player deserves special attention here. The ActiveMovie // implementation of OpenDML hierarchial indexing >2Gb *SUCKS*. It can't // cope with a JUNK chunk at the end of the hdrl chunk (even though // the Microsoft documentation in AVIRIFF.H shows one), requires that // all standard indexes be the same size except for the last one, and // requires buffer size information for streams. NONE of this is required // by ActiveMovie when an extended index is absent (easily verified by // changing the 'indx' chunks to JUNK chunks). While diagnosing these // problems I got an interesting array of error messages from WMP, // including: // // o Downloading codec from activex.microsoft.com // (Because of an extended index!?) // o "Cannot allocate memory because no size has been set" // ??? // o "The file format is invalid." // Detail: "The file format is invalid. (Error=8004022F)" // Gee, that clears everything up. // o My personal favorite: recursion of the above until the screen // has 100+ dialogs and WMP crashes with a stack fault. // // Basically, supporting WMP (or as I like to call it, WiMP) was an // absolute 100% pain in the ass. BOOL AVIOutputFile::init(const char *szFile, LONG xSize, LONG ySize, BOOL videoIn, BOOL audioIn, LONG bufferSize, BOOL is_interleaved) { return _init(szFile, xSize, ySize, videoIn, audioIn, bufferSize, is_interleaved, true); } BOOL AVIOutputFile::_init(const char *szFile, LONG xSize, LONG ySize, BOOL videoIn, BOOL audioIn, LONG bufferSize, BOOL is_interleaved, bool fThreaded) { AVISUPERINDEX asi; struct _avisuperindex_entry asie_dumb[MAX_SUPERINDEX_ENTRIES]; fLimitTo4Gb = IsFilenameOnFATVolume(szFile); if (audioIn) { if (!audioOut) return FALSE; } else { delete audioOut; audioOut = NULL; } if (!videoOut) return FALSE; // Allocate indexes if (!(index = new AVIIndex())) return FALSE; if (fExtendedAVI) { if (!(index_audio = new AVIIndex())) return FALSE; if (!(index_video = new AVIIndex())) return FALSE; } // Initialize main AVI header (avih) memset(&avihdr, 0, sizeof avihdr); avihdr.dwMicroSecPerFrame = MulDiv(videoOut->streamInfo.dwScale, 1000000L, videoOut->streamInfo.dwRate); avihdr.dwMaxBytesPerSec = 0; avihdr.dwPaddingGranularity = 0; avihdr.dwFlags = AVIF_HASINDEX | (is_interleaved ? AVIF_ISINTERLEAVED : 0); avihdr.dwTotalFrames = videoOut->streamInfo.dwLength; avihdr.dwInitialFrames = 0; avihdr.dwStreams = audioIn ? 2 : 1; avihdr.dwSuggestedBufferSize = 0; avihdr.dwWidth = xSize; avihdr.dwHeight = ySize; // Initialize file if (!fCaching) { hFile = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hFile) throw MyError("AVIOutputFile::_init - error opening file"); } else { hFile = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN /*|FILE_FLAG_WRITE_THROUGH*/, NULL); if (INVALID_HANDLE_VALUE == hFile) throw MyError("AVIOutputFile::_init - error opening file"); } i64FilePosition = 0; ////////// Initialize the first 'AVI ' chunk ////////// __int64 hdrl_pos; __int64 odml_pos; DWORD dw[64]; // start RIFF chunk dw[0] = FOURCC_RIFF; dw[1] = 0; dw[2] = formtypeAVI; _writeHdr(dw, 12); // start header chunk hdrl_pos = _beginList(listtypeAVIHEADER); // write out main AVI header main_hdr_pos = _writeHdrChunk(ckidAVIMAINHDR, &avihdr, sizeof avihdr); _RPT1(0,"Main header is at %08lx\n", main_hdr_pos); // start video stream headers strl_pos = _beginList(listtypeSTREAMHEADER); // write out video stream header and format video_hdr_pos = _writeHdrChunk(ckidSTREAMHEADER, &videoOut->streamInfo, sizeof videoOut->streamInfo); _writeHdrChunk(ckidSTREAMFORMAT, videoOut->getFormat(), videoOut->getFormatLen()); _RPT1(0,"Video header is at %08lx\n", video_hdr_pos); // write out video superindex (but make it a JUNK chunk for now). if (fExtendedAVI) { memset(asie_dumb, 0, sizeof asie_dumb); video_indx_pos = _getPosition(); asi.fcc = ckidAVIPADDING; asi.cb = (sizeof asi)-8 + MAX_SUPERINDEX_ENTRIES*sizeof(_avisuperindex_entry); _writeHdr(&asi, sizeof asi); _writeHdr(asie_dumb, MAX_SUPERINDEX_ENTRIES*sizeof(_avisuperindex_entry)); } // finish video stream header _closeList(strl_pos); videoOut->streamInfo.dwSuggestedBufferSize = 0; // if there is audio... if (audioIn) { // start audio stream headers strl_pos = _beginList(listtypeSTREAMHEADER); // write out audio stream header and format audio_hdr_pos = _writeHdrChunk(ckidSTREAMHEADER, &audioOut->streamInfo, sizeof audioOut->streamInfo); audio_format_pos = _writeHdrChunk(ckidSTREAMFORMAT, audioOut->getFormat(), audioOut->getFormatLen()); _RPT1(0,"Audio header is at %08lx\n", audio_hdr_pos); // write out audio superindex (but make it a JUNK chunk for now). if (fExtendedAVI) { audio_indx_pos = _getPosition(); asi.fcc = ckidAVIPADDING; asi.cb = (sizeof asi)-8 + MAX_SUPERINDEX_ENTRIES*sizeof(_avisuperindex_entry); _writeHdr(&asi, sizeof asi); _writeHdr(asie_dumb, MAX_SUPERINDEX_ENTRIES*sizeof(_avisuperindex_entry)); } // finish audio stream header _closeList(strl_pos); audioOut->streamInfo.dwSuggestedBufferSize = 0; } // write out dmlh header (indicates real # of frames) if (fExtendedAVI) { odml_pos = _beginList('lmdo'); memset(dw, 0, sizeof dw); dmlh_pos = _writeHdrChunk('hlmd', dw, 62*4); _closeList(odml_pos); } // write out segment hint block if (pSegmentHint) seghint_pos = _writeHdrChunk('mges', pSegmentHint, cbSegmentHint); _closeList(hdrl_pos); // _flushHdr(); // pad out to a multiple of 2048 bytes // // WARNING: ActiveMovie/WMP can't handle a trailing JUNK chunk in hdrl // if an extended index is in use. It says the file format // is invalid! { char *s; long pad; pad = (2048 - ((_getPosition()+8)&2047))&2047; if (pad) { if (!(s = new char[pad])) return FALSE; memset(s,0,pad); if (pad > 80) sprintf(s, "MPEG Mediator OpenDML AVI Plugin %s", #ifdef _DEBUG "debug" #else "release" #endif ); _writeHdrChunk(ckidAVIPADDING, s, pad); delete []s; } // // If we are using a fast path, sync the fast path to the slow path // if (fastIO) // fastIO->Seek(i64FilePosition); } _flushHdr(); // If we're using the fast path, we're aligned to a sector boundary. // Write out the 12 header bytes. _openXblock(); { DWORD dwLo, dwHi; dwLo = GetFileSize(hFile, &dwHi); if (dwLo != 0xFFFFFFFF || GetLastError()==NO_ERROR) i64EndOfFile = dwLo | ((__int64)dwHi << 32); else i64EndOfFile = 0; } fInitComplete = true; return TRUE; } BOOL AVIOutputFile::finalize() { AVISUPERINDEX asi_video; AVISUPERINDEX asi_audio; struct _avisuperindex_entry asie_video[MAX_SUPERINDEX_ENTRIES]; struct _avisuperindex_entry asie_audio[MAX_SUPERINDEX_ENTRIES]; DWORD dw; int i; if (!fInitComplete) return TRUE; if (videoOut) if (!videoOut->finalize()) return FALSE; if (audioOut) if (!audioOut->finalize()) return FALSE; // fast path: clean it up and resync slow path. // create extended indices if (fExtendedAVI && xblock != 0) { _createNewIndices(index_video, &asi_video, asie_video, false); if (audioOut) _createNewIndices(index_audio, &asi_audio, asie_audio, true); } // finish last Xblock _closeXblock(); // truncate file SetEndOfFile(hFile); _RPT0(0,"AVIOutputFile: Writing main AVI header...\n"); _seekHdr(main_hdr_pos+8); avihdr.dwSuggestedBufferSize = videoOut->streamInfo.dwSuggestedBufferSize; _writeHdr(&avihdr, sizeof avihdr); _RPT0(0,"AVIOutputFile: Writing video header...\n"); _seekHdr(video_hdr_pos+8); _writeHdr(&videoOut->streamInfo, sizeof(AVIStreamHeader_fixed)); if (audioOut) { _RPT0(0,"AVIOutputFile: Writing audio header...\n"); _seekHdr(audio_hdr_pos+8); _writeHdr(&audioOut->streamInfo, sizeof(AVIStreamHeader_fixed)); // we have to rewrite the audio format, in case someone // fixed fields in the format afterward (MPEG-1/L3) _RPT0(0,"AVIOutputFile: Writing audio format...\n"); _seekHdr(audio_format_pos+8); _writeHdr(audioOut->getFormat(), audioOut->getFormatLen()); } if (fExtendedAVI) { _RPT0(0,"AVIOutputFile: writing dmlh header...\n"); _seekHdr(dmlh_pos+8); dw = videoOut->streamInfo.dwLength; _writeHdr(&dw, 4); if (xblock > 1) { _RPT0(0,"AVIOutputFile: writing video superindex...\n"); _seekHdr(video_indx_pos); _writeHdr(&asi_video, sizeof asi_video); _writeHdr(asie_video, sizeof(_avisuperindex_entry)*MAX_SUPERINDEX_ENTRIES); if (audioOut) { _seekHdr(audio_indx_pos); _writeHdr(&asi_audio, sizeof asi_audio); _writeHdr(asie_audio, sizeof(_avisuperindex_entry)*MAX_SUPERINDEX_ENTRIES); } } } if (pSegmentHint) { _seekHdr(seghint_pos+8); _writeHdr(pSegmentHint, cbSegmentHint); } _flushHdr(); _RPT0(0,"AVIOutputFile: closing RIFF and movi chunks...\n"); for(i=0; i i64FarthestWritePoint) i64FarthestWritePoint = i64FilePosition; } __int64 AVIOutputFile::_getPosition() { return i64FilePosition; } void AVIOutputFile::_seekHdr(__int64 i64NewPos) { i64FilePosition = i64NewPos; } void AVIOutputFile::_seekDirect(__int64 i64NewPos) { LONG lHi = (LONG)(i64NewPos>>32); DWORD dwError; // _RPT1(0,"Seeking to %I64d\n", i64NewPos); if (0xFFFFFFFF == SetFilePointer(hFile, (LONG)i64NewPos, &lHi, FILE_BEGIN)) if ((dwError = GetLastError()) != NO_ERROR) throw MyWin32Error("%s: %%s", dwError, szME); i64FilePosition = i64NewPos; } __int64 AVIOutputFile::_writeDirect(void *data, long len) { DWORD dwActual; if (!WriteFile(hFile, data, len, &dwActual, NULL) || dwActual != len) throw MyWin32Error("%s: %%s", GetLastError(), szME); i64FilePosition += len; if (i64FilePosition > i64FarthestWritePoint) i64FarthestWritePoint = i64FilePosition; return i64FilePosition - len; } bool AVIOutputFile::_extendFile(__int64 i64NewPoint) { bool fSuccess; // Have we already extended the file past that point? if (i64NewPoint < i64EndOfFile) return true; // Attempt to extend the file. __int64 i64Save = i64FilePosition; _seekDirect(i64NewPoint); fSuccess = !!SetEndOfFile(hFile); _seekDirect(i64Save); if (fSuccess) { i64EndOfFile = i64NewPoint; // _RPT1(0,"Successfully extended file to %I64d bytes\n", i64EndOfFile); } else { // _RPT1(0,"Failed to extend file to %I64d bytes\n", i64NewPoint); } return fSuccess; } void AVIOutputFile::_write(void *data, int len) { if (!fPreemptiveExtendFailed && i64FilePosition + len + lIndexSize > i64EndOfFile - 8388608) { fPreemptiveExtendFailed = !_extendFile((i64FilePosition + len + lIndexSize + 16777215) & -8388608); } _writeDirect(data, len); } void AVIOutputFile::writeIndexedChunk(FOURCC ckid, LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer) { AVIIndexEntry2 avie; long buf[5]; static char zero = 0; long siz; bool fOpenNewBlock = false; int nStream = 0; if ((ckid&0xffff) > (int)'00') nStream = 1; // Determine if we need to open another RIFF block (xblock). siz = cbBuffer + (cbBuffer&1) + 16; // The original AVI format can't accommodate RIFF chunks >4Gb due to the // use of 32-bit size fields. Most RIFF parsers don't handle >2Gb because // of inappropriate use of signed variables. And to top it all off, // stupid mistakes in the MCI RIFF parser prevent processing beyond the // 1Gb mark. // // To be save, we keep the first RIFF AVI chunk below 1Gb, and subsequent // RIFF AVIX chunks below 2Gb. We have to leave ourselves a little safety // margin (16Mb in this case) for index blocks. if (fExtendedAVI) if (i64XBufferLevel + siz > (xblock ? 0x7F000000 : lAVILimit)) fOpenNewBlock = true; // Check available disk space. // // Take the largest separation between data blocks, __int64 chunkloc; int idxblocksize; int idxblocks; __int64 maxpoint; chunkloc = i64FilePosition; if (fOpenNewBlock) chunkloc += 24; if (!i64FirstIndexedChunk[nStream]) i64FirstIndexedChunk[nStream] = chunkloc; if ((long)(chunkloc - i64LastIndexedChunk[nStream]) > lLargestIndexDelta[nStream]) lLargestIndexDelta[nStream] = (long)(chunkloc - i64LastIndexedChunk[nStream]); ++lIndexedChunkCount[nStream]; // compute how much total space we need to close the file idxblocks = 0; if (lLargestIndexDelta[0]) { idxblocksize = (int)(0x100000000i64 / lLargestIndexDelta[0]); if (idxblocksize > MAX_INDEX_ENTRIES) idxblocksize = MAX_INDEX_ENTRIES; idxblocks = (lIndexedChunkCount[0] + idxblocksize - 1) / idxblocksize; } if (lLargestIndexDelta[1]) { idxblocksize = (int)(0x100000000i64 / lLargestIndexDelta[1]); if (idxblocksize > MAX_INDEX_ENTRIES) idxblocksize = MAX_INDEX_ENTRIES; idxblocks += (lIndexedChunkCount[1] + idxblocksize - 1) / idxblocksize; } lIndexSize = 0; if (fExtendedAVI) lIndexSize = idxblocks*sizeof(AVISTDINDEX); lIndexSize += 8 + 16*(lIndexedChunkCount[0]+lIndexedChunkCount[1]); // Give ourselves ~4K of headroom... maxpoint = (chunkloc + cbBuffer + 1 + 8 + 14 + 2047 + lIndexSize + 4096) & -2048i64; if (fLimitTo4Gb && maxpoint >= 0x100000000i64) { _RPT1(0,"overflow detected! maxpoint=%I64d\n", maxpoint); _RPT2(0,"lIndexSize = %08lx (%ld index blocks)\n", lIndexSize, idxblocks); _RPT2(0,"sample counts = %ld, %ld\n", lIndexedChunkCount[0], lIndexedChunkCount[1]); throw MyError("Out of file space: Files cannot exceed 4 gigabytes on a FAT32 partition."); } if (!_extendFile(maxpoint)) throw MyError("Not enough disk space to write additional data."); i64LastIndexedChunk[nStream] = chunkloc; // If we need to open a new Xblock, do so. if (fOpenNewBlock) { _closeXblock(); _openXblock(); } // Write the chunk. avie.ckid = ckid; avie.pos = i64FilePosition - (avi_movi_pos[0]+8); //chunkMisc.dwDataOffset - 2064; avie.size = cbBuffer; if (dwIndexFlags & AVIIF_KEYFRAME) avie.size |= 0x80000000L; buf[0] = ckid; buf[1] = cbBuffer; _write(buf, 8); i64XBufferLevel += siz; // ActiveMovie/WMP requires a non-zero dwSuggestedBufferSize for // hierarchial indexing (piece of sh*t player). So we continually // bump it up to the largest chunk size; if ((unsigned short)ckid == '10') { if (cbBuffer > audioOut->streamInfo.dwSuggestedBufferSize) audioOut->streamInfo.dwSuggestedBufferSize = cbBuffer; if (fExtendedAVI) if (!index_audio->add(&avie)) throw MyError("%s error: couldn't add audio chunk to index",szME); } else { if (cbBuffer > videoOut->streamInfo.dwSuggestedBufferSize) videoOut->streamInfo.dwSuggestedBufferSize = cbBuffer; if (fExtendedAVI) if (!index_video->add(&avie)) throw MyError("%s error: couldn't add video chunk to index",szME); } if (index) if (!index->add(&avie)) throw MyError("%s error: couldn't add chunk to index",szME); _write(lpBuffer, cbBuffer); // Align to 8-byte boundary, not 2-byte, in capture mode. if (fCaptureMode) { char *pp; int offset = (cbBuffer + iPadOffset) & 7; // offset=0: no action // offset=1/2: [00] 'JUNK' 6 <6 bytes> // offset=3/4: [00] 'JUNK' 4 <4 bytes> // offset=5/6: [00] 'JUNK' 2 <2 bytes> // offset=7: 00 if (offset) { buf[0] = 0; buf[1] = 'KNUJ'; buf[2] = (-offset) & 6; buf[3] = 0; buf[4] = 0; pp = (char *)&buf[1]; if (offset & 1) --pp; _write(pp, (offset & 1) + (((offset+1)&7) ? 8+buf[2] : 0)); } } else { // Standard AVI: use 2 bytes if (cbBuffer & 1) _write(&zero, 1); } } void AVIOutputFile::_closeXblock() { avi_movi_len[xblock] = i64FilePosition - (avi_movi_pos[xblock]+8); if (!xblock) { avihdr.dwTotalFrames = videoOut->lTotalSamplesWritten; _writeLegacyIndex(true); } avi_riff_len[xblock] = i64FilePosition - (avi_riff_pos[xblock]+8); ++xblock; i64XBufferLevel = 0; } void AVIOutputFile::_openXblock() { DWORD dw[8]; if (xblock >= MAX_AVIBLOCKS) throw MyError("%s: Exceeded maximum RIFF count (%d)", szME, MAX_AVIBLOCKS); // If we're in capture mode, keep this stuff aligned to 8-byte boundaries! if (xblock != 0) { avi_riff_pos[xblock] = i64FilePosition; dw[0] = FOURCC_RIFF; dw[1] = 0x7F000000; dw[2] = xblock ? 'XIVA' : ' IVA'; dw[3] = FOURCC_LIST; dw[4] = 0x7F000000; dw[5] = 'ivom'; // movi _write(dw,24); avi_movi_pos[xblock] = i64FilePosition - 12; } else { avi_riff_pos[xblock] = 0; avi_movi_pos[xblock] = i64FilePosition; dw[0] = FOURCC_LIST; dw[1] = 0x7FFFFFFF; dw[2] = 'ivom'; // movi if (fCaptureMode) iPadOffset = 4; _write(dw, 12); } // WARNING: For AVIFile to parse the index correctly, it assumes that the // first chunk in an index starts right after the movi chunk! // dw[0] = ckidAVIPADDING; // dw[1] = 4; // dw[2] = 0; // _write(dw, 12); } void AVIOutputFile::_writeLegacyIndex(bool use_fastIO) { if (!index) return; if (!index->makeIndex()) throw MyMemoryError(); // if (use_fastIO && fastIO) { DWORD dw[2]; dw[0] = ckidAVINEWINDEX; dw[1] = index->indexLen() * sizeof(AVIINDEXENTRY); _write(dw, 8); _write(index->indexPtr(), index->indexLen() * sizeof(AVIINDEXENTRY)); // } else { // _writeHdrChunk(ckidAVINEWINDEX, index->indexPtr(), index->indexLen() * sizeof(AVIINDEXENTRY)); delete index; index = NULL; } void AVIOutputFile::_createNewIndices(AVIIndex *index, AVISUPERINDEX *asi, _avisuperindex_entry *asie, bool is_audio) { AVIIndexEntry2 *asie2; int size; int actual; int indexnum=0; int blocksize; if (!index || !index->size()) return; if (!index->makeIndex2()) throw MyMemoryError(); size = index->indexLen(); asie2 = index->index2Ptr(); memset(asie, 0, sizeof(_avisuperindex_entry)*MAX_SUPERINDEX_ENTRIES); // Now we run into a bit of a problem. DirectShow's AVI2 parser requires // that all index blocks have the same # of entries (except the last), // which is a problem since we also have to guarantee that each block // has offsets <4Gb. // For now, use a O(n^2) algorithm to find the optimal size. This isn't // really a problem because this routine won't ever be called in time // critical circumstances, like streaming video capture. blocksize = MAX_INDEX_ENTRIES; { while(blocksize > 1) { int i; int nextblock = 0; __int64 offset; for(i=0; i = offset + 0x100000000i64) break; } if (i >= size) break; --blocksize; } } // Write out the actual index blocks. while(size > 0) { if (indexnum >= MAX_SUPERINDEX_ENTRIES) throw MyError("Maximum number of extended AVI indices exceeded (%d)\n", MAX_SUPERINDEX_ENTRIES); actual = _writeNewIndex(&asie[indexnum], asie2, min(size, blocksize), is_audio ? '10xi' : '00xi', is_audio ? 'bw10' : videoOut->id, is_audio ? audioOut->streamInfo.dwSampleSize : videoOut->streamInfo.dwSampleSize); asie2 += actual; size -= actual; ++indexnum; } memset(asi, 0, sizeof(AVISUPERINDEX)); asi->fcc = 'xdni'; asi->cb = sizeof(AVISUPERINDEX)-8 + sizeof(_avisuperindex_entry)*MAX_SUPERINDEX_ENTRIES; asi->wLongsPerEntry = 4; asi->bIndexSubType = 0; asi->bIndexType = AVI_INDEX_OF_INDEXES; asi->nEntriesInUse = indexnum; asi->dwChunkId = is_audio ? 'bw10' : videoOut->id; } int AVIOutputFile::_writeNewIndex(struct _avisuperindex_entry *asie, AVIIndexEntry2 *avie2, int size, FOURCC fcc, DWORD dwChunkId, DWORD dwSampleSize) { AVISTDINDEX asi; AVIIndexEntry3 asie3[64]; __int64 offset = avie2->pos; int tc; int i; int size0; // Scan, ascertain how many we can handle without exceeding 4Gb offset for(i=0; i = 0x100000000i64) break; } size0 = size = i; // Check to see if we need to open a new AVIX block if (i64XBufferLevel + sizeof(AVISTDINDEX) + size*sizeof(_avistdindex_entry) > (xblock ? 0x7F000000 : lAVILimit)) { _closeXblock(); _openXblock(); } // setup superindex entry asie->qwOffset = i64FilePosition; asie->dwSize = sizeof(AVISTDINDEX) + size*sizeof(_avistdindex_entry); if (dwSampleSize) { __int64 total_bytes = 0; for(int i=0; i size & 0x7FFFFFFF; asie->dwDuration = (DWORD)(total_bytes / audioOut->streamInfo.dwSampleSize); } else asie->dwDuration = size; asi.fcc = ((dwChunkId & 0xFFFF)<<16) | 'xi'; asi.cb = asie->dwSize - 8; asi.wLongsPerEntry = 2; asi.bIndexSubType = 0; asi.bIndexType = AVI_INDEX_OF_CHUNKS; asi.nEntriesInUse = size; asi.dwChunkId = dwChunkId; asi.qwBaseOffset = offset; asi.dwReserved3 = 0; _write(&asi, sizeof asi); while(size > 0) { tc = size; if (tc>64) tc=64; for(i=0; i pos - offset + avi_movi_pos[0] + 16); asie3[i].dwSizeKeyframe = avie2->size; ++avie2; } _write(asie3, tc*sizeof(AVIIndexEntry3)); size -= tc; } return size0; }