bool Encode(float *input, UINT numInputFrames, DataPacket &packet, QWORD ×tamp) { if(bFirstFrame) { curEncodeTimestamp = timestamp; bFirstFrame = false; } //------------------------------------------------ UINT lastSampleSize = frameCounter; frameCounter += numInputFrames; if(frameCounter > outputFrameSize) { frameCounter -= outputFrameSize; bufferedTimestamps << curEncodeTimestamp; curEncodeTimestamp = timestamp + ((outputFrameSize-lastSampleSize)*1000/App->GetSampleRateHz()); } int ret = lame_encode_buffer_interleaved_ieee_float(lgf, (float*)input, numInputFrames, MP3OutputBuffer.Array()+1, dwMP3MaxSize); if(ret < 0) { AppWarning(TEXT("MP3 encode failed")); return false; } if(ret > 0) { if(bFirstPacket) { header.CopyArray(MP3OutputBuffer.Array(), ret); bFirstPacket = false; ret = 0; } else { packet.lpPacket = MP3OutputBuffer.Array(); packet.size = ret+1; timestamp = bufferedTimestamps[0]; bufferedTimestamps.Remove(0); } } return ret > 0; }
~MP4FileStream() { if(!bStreamOpened) return; App->EnableSceneSwitching(false); //--------------------------------------------------- //HWND hwndProgressDialog = CreateDialog(hinstMain, MAKEINTRESOURCE(IDD_BUILDINGMP4), hwndMain, (DLGPROC)MP4ProgressDialogProc); //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETRANGE32, 0, 100); mdatStop = fileOut.GetPos(); BufferOutputSerializer output(endBuffer); //set a reasonable initial buffer size endBuffer.SetSize((videoFrames.Num() + audioFrames.Num()) * 20 + 131072); UINT64 audioFrameSize = App->GetAudioEncoder()->GetFrameSize(); DWORD macTime = fastHtonl(DWORD(GetMacTime())); UINT videoDuration = fastHtonl(lastVideoTimestamp + App->GetFrameTime()); UINT audioDuration = fastHtonl(lastVideoTimestamp + DWORD(double(audioFrameSize)*1000.0/double(App->GetSampleRateHz()))); UINT width, height; App->GetOutputSize(width, height); LPCSTR lpVideoTrack = "Video Media Handler"; LPCSTR lpAudioTrack = "Sound Media Handler"; const char videoCompressionName[31] = "AVC Coding"; //------------------------------------------- // get video headers DataPacket videoHeaders; App->GetVideoHeaders(videoHeaders); List<BYTE> SPS, PPS; LPBYTE lpHeaderData = videoHeaders.lpPacket+11; SPS.CopyArray(lpHeaderData+2, fastHtons(*(WORD*)lpHeaderData)); lpHeaderData += SPS.Num()+3; PPS.CopyArray(lpHeaderData+2, fastHtons(*(WORD*)lpHeaderData)); //------------------------------------------- // get AAC headers if using AAC List<BYTE> AACHeader; if(!bMP3) { DataPacket data; App->GetAudioHeaders(data); AACHeader.CopyArray(data.lpPacket+2, data.size-2); } //------------------------------------------- EndChunkInfo(videoChunks, videoSampleToChunk, curVideoChunkOffset, numVideoSamples); EndChunkInfo(audioChunks, audioSampleToChunk, curAudioChunkOffset, numAudioSamples); if (numVideoSamples > 1) GetVideoDecodeTime(videoFrames.Last(), true); if (numAudioSamples > 1) GetAudioDecodeTime(audioFrames.Last(), true); UINT audioUnitDuration = fastHtonl(UINT(lastAudioTimeVal)); //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 25, 0); //------------------------------------------- // sound descriptor thingy. this part made me die a little inside admittedly. UINT maxBitRate = fastHtonl(App->GetAudioEncoder()->GetBitRate()*1000); List<BYTE> esDecoderDescriptor; BufferOutputSerializer esDecoderOut(esDecoderDescriptor); esDecoderOut.OutputByte(bMP3 ? 107 : 64); esDecoderOut.OutputByte(0x15); //stream/type flags. always 0x15 for my purposes. esDecoderOut.OutputByte(0); //buffer size, just set it to 1536 for both mp3 and aac esDecoderOut.OutputWord(WORD_BE(0x600)); esDecoderOut.OutputDword(maxBitRate); //max bit rate (cue bill 'o reily meme for these two) esDecoderOut.OutputDword(maxBitRate); //avg bit rate if(!bMP3) //if AAC, put in headers { esDecoderOut.OutputByte(0x5); //decoder specific descriptor type /*esDecoderOut.OutputByte(0x80); //some stuff that no one should probably care about esDecoderOut.OutputByte(0x80); esDecoderOut.OutputByte(0x80);*/ esDecoderOut.OutputByte(AACHeader.Num()); esDecoderOut.Serialize((LPVOID)AACHeader.Array(), AACHeader.Num()); } List<BYTE> esDescriptor; BufferOutputSerializer esOut(esDescriptor); esOut.OutputWord(0); //es id esOut.OutputByte(0); //stream priority esOut.OutputByte(4); //descriptor type /*esOut.OutputByte(0x80); //some stuff that no one should probably care about esOut.OutputByte(0x80); esOut.OutputByte(0x80);*/ esOut.OutputByte(esDecoderDescriptor.Num()); esOut.Serialize((LPVOID)esDecoderDescriptor.Array(), esDecoderDescriptor.Num()); esOut.OutputByte(0x6); //config descriptor type /*esOut.OutputByte(0x80); //some stuff that no one should probably care about esOut.OutputByte(0x80); esOut.OutputByte(0x80);*/ esOut.OutputByte(1); //len esOut.OutputByte(2); //SL value(? always 2) //------------------------------------------- PushBox(output, DWORD_BE('moov')); //------------------------------------------------------ // header PushBox(output, DWORD_BE('mvhd')); output.OutputDword(0); //version and flags (none) output.OutputDword(macTime); //creation time output.OutputDword(macTime); //modified time output.OutputDword(DWORD_BE(1000)); //time base (milliseconds, so 1000) output.OutputDword(videoDuration); //duration (in time base units) output.OutputDword(DWORD_BE(0x00010000)); //fixed point playback speed 1.0 output.OutputWord(WORD_BE(0x0100)); //fixed point vol 1.0 output.OutputQword(0); //reserved (10 bytes) output.OutputWord(0); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0) output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0) output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0) output.OutputDword(0); //prevew start time (time base units) output.OutputDword(0); //prevew duration (time base units) output.OutputDword(0); //still poster frame (timestamp of frame) output.OutputDword(0); //selection(?) start time (time base units) output.OutputDword(0); //selection(?) duration (time base units) output.OutputDword(0); //current time (0, time base units) output.OutputDword(DWORD_BE(3)); //next free track id (1-based rather than 0-based) PopBox(output); //mvhd //------------------------------------------------------ // audio track PushBox(output, DWORD_BE('trak')); PushBox(output, DWORD_BE('tkhd')); //track header output.OutputDword(DWORD_BE(0x00000007)); //version (0) and flags (0xF) output.OutputDword(macTime); //creation time output.OutputDword(macTime); //modified time output.OutputDword(DWORD_BE(1)); //track ID output.OutputDword(0); //reserved output.OutputDword(audioDuration); //duration (in time base units) output.OutputQword(0); //reserved output.OutputWord(0); //video layer (0) output.OutputWord(WORD_BE(0)); //quicktime alternate track id output.OutputWord(WORD_BE(0x0100)); //volume output.OutputWord(0); //reserved output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0) output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0) output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0) output.OutputDword(0); //width (fixed point) output.OutputDword(0); //height (fixed point) PopBox(output); //tkhd /*PushBox(output, DWORD_BE('edts')); PushBox(output, DWORD_BE('elst')); output.OutputDword(0); //version and flags (none) output.OutputDword(DWORD_BE(1)); //count output.OutputDword(audioDuration); //duration output.OutputDword(0); //start time output.OutputDword(DWORD_BE(0x00010000)); //playback speed (1.0) PopBox(); //elst PopBox(); //tdst*/ PushBox(output, DWORD_BE('mdia')); PushBox(output, DWORD_BE('mdhd')); output.OutputDword(0); //version and flags (none) output.OutputDword(macTime); //creation time output.OutputDword(macTime); //modified time output.OutputDword(DWORD_BE(App->GetSampleRateHz())); //time scale output.OutputDword(audioUnitDuration); output.OutputDword(bMP3 ? DWORD_BE(0x55c40000) : DWORD_BE(0x15c70000)); PopBox(output); //mdhd PushBox(output, DWORD_BE('hdlr')); output.OutputDword(0); //version and flags (none) output.OutputDword(0); //quicktime type (none) output.OutputDword(DWORD_BE('soun')); //media type output.OutputDword(0); //manufacturer reserved output.OutputDword(0); //quicktime component reserved flags output.OutputDword(0); //quicktime component reserved mask output.Serialize((LPVOID)lpAudioTrack, (DWORD)strlen(lpAudioTrack)+1); //track name PopBox(output); //hdlr PushBox(output, DWORD_BE('minf')); PushBox(output, DWORD_BE('smhd')); output.OutputDword(0); //version and flags (none) output.OutputDword(0); //balance (fixed point) PopBox(output); //vdhd PushBox(output, DWORD_BE('dinf')); PushBox(output, DWORD_BE('dref')); output.OutputDword(0); //version and flags (none) output.OutputDword(DWORD_BE(1)); //count PushBox(output, DWORD_BE('url ')); output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1) PopBox(output); //url PopBox(output); //dref PopBox(output); //dinf PushBox(output, DWORD_BE('stbl')); PushBox(output, DWORD_BE('stsd')); output.OutputDword(0); //version and flags (none) output.OutputDword(DWORD_BE(1)); //count PushBox(output, DWORD_BE('mp4a')); output.OutputDword(0); //reserved (6 bytes) output.OutputWord(0); output.OutputWord(WORD_BE(1)); //dref index output.OutputWord(0); //quicktime encoding version output.OutputWord(0); //quicktime encoding revision output.OutputDword(0); //quicktime audio encoding vendor output.OutputWord(0); //channels (ignored) output.OutputWord(WORD_BE(16)); //sample size output.OutputWord(0); //quicktime audio compression id output.OutputWord(0); //quicktime audio packet size output.OutputDword(DWORD_BE(App->GetSampleRateHz()<<16)); //sample rate (fixed point) PushBox(output, DWORD_BE('esds')); output.OutputDword(0); //version and flags (none) output.OutputByte(3); //ES descriptor type /*output.OutputByte(0x80); output.OutputByte(0x80); output.OutputByte(0x80);*/ output.OutputByte(esDescriptor.Num()); output.Serialize((LPVOID)esDescriptor.Array(), esDescriptor.Num()); PopBox(output); PopBox(output); PopBox(output); //stsd PushBox(output, DWORD_BE('stts')); //list of keyframe (i-frame) IDs output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(audioDecodeTimes.Num())); for(UINT i=0; i<audioDecodeTimes.Num(); i++) { output.OutputDword(fastHtonl(audioDecodeTimes[i].count)); output.OutputDword(fastHtonl(audioDecodeTimes[i].val)); } PopBox(output); //stss PushBox(output, DWORD_BE('stsc')); //sample to chunk list output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(audioSampleToChunk.Num())); for(UINT i=0; i<audioSampleToChunk.Num(); i++) { SampleToChunk &stc = audioSampleToChunk[i]; output.OutputDword(fastHtonl(stc.firstChunkID)); output.OutputDword(fastHtonl(stc.samplesPerChunk)); output.OutputDword(DWORD_BE(1)); } PopBox(output); //stsc //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 30, 0); //ProcessEvents(); PushBox(output, DWORD_BE('stsz')); //sample sizes output.OutputDword(0); //version and flags (none) output.OutputDword(0); //block size for all (0 if differing sizes) output.OutputDword(fastHtonl(audioFrames.Num())); for(UINT i=0; i<audioFrames.Num(); i++) output.OutputDword(fastHtonl(audioFrames[i].size)); PopBox(output); //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 40, 0); //ProcessEvents(); if(audioChunks.Num() && audioChunks.Last() > 0xFFFFFFFFLL) { PushBox(output, DWORD_BE('co64')); //chunk offsets output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(audioChunks.Num())); for(UINT i=0; i<audioChunks.Num(); i++) output.OutputQword(fastHtonll(audioChunks[i])); PopBox(output); //co64 } else { PushBox(output, DWORD_BE('stco')); //chunk offsets output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(audioChunks.Num())); for(UINT i=0; i<audioChunks.Num(); i++) output.OutputDword(fastHtonl((DWORD)audioChunks[i])); PopBox(output); //stco } PopBox(output); //stbl PopBox(output); //minf PopBox(output); //mdia PopBox(output); //trak //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 50, 0); //ProcessEvents(); //------------------------------------------------------ // video track PushBox(output, DWORD_BE('trak')); PushBox(output, DWORD_BE('tkhd')); //track header output.OutputDword(DWORD_BE(0x00000007)); //version (0) and flags (0x7) output.OutputDword(macTime); //creation time output.OutputDword(macTime); //modified time output.OutputDword(DWORD_BE(2)); //track ID output.OutputDword(0); //reserved output.OutputDword(videoDuration); //duration (in time base units) output.OutputQword(0); //reserved output.OutputWord(0); //video layer (0) output.OutputWord(0); //quicktime alternate track id (0) output.OutputWord(0); //track audio volume (this is video, so 0) output.OutputWord(0); //reserved output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0) output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0) output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0) output.OutputDword(fastHtonl(width<<16)); //width (fixed point) output.OutputDword(fastHtonl(height<<16)); //height (fixed point) PopBox(output); //tkhd /*PushBox(output, DWORD_BE('edts')); PushBox(output, DWORD_BE('elst')); output.OutputDword(0); //version and flags (none) output.OutputDword(DWORD_BE(1)); //count output.OutputDword(videoDuration); //duration output.OutputDword(0); //start time output.OutputDword(DWORD_BE(0x00010000)); //playback speed (1.0) PopBox(); //elst PopBox(); //tdst*/ PushBox(output, DWORD_BE('mdia')); PushBox(output, DWORD_BE('mdhd')); output.OutputDword(0); //version and flags (none) output.OutputDword(macTime); //creation time output.OutputDword(macTime); //modified time output.OutputDword(DWORD_BE(1000)); //time scale output.OutputDword(videoDuration); output.OutputDword(DWORD_BE(0x55c40000)); PopBox(output); //mdhd PushBox(output, DWORD_BE('hdlr')); output.OutputDword(0); //version and flags (none) output.OutputDword(0); //quicktime type (none) output.OutputDword(DWORD_BE('vide')); //media type output.OutputDword(0); //manufacturer reserved output.OutputDword(0); //quicktime component reserved flags output.OutputDword(0); //quicktime component reserved mask output.Serialize((LPVOID)lpVideoTrack, (DWORD)strlen(lpVideoTrack)+1); //track name PopBox(output); //hdlr PushBox(output, DWORD_BE('minf')); PushBox(output, DWORD_BE('vmhd')); output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1) output.OutputWord(0); //quickdraw graphic mode (copy = 0) output.OutputWord(0); //quickdraw red value output.OutputWord(0); //quickdraw green value output.OutputWord(0); //quickdraw blue value PopBox(output); //vdhd PushBox(output, DWORD_BE('dinf')); PushBox(output, DWORD_BE('dref')); output.OutputDword(0); //version and flags (none) output.OutputDword(DWORD_BE(1)); //count PushBox(output, DWORD_BE('url ')); output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1) PopBox(output); //url PopBox(output); //dref PopBox(output); //dinf PushBox(output, DWORD_BE('stbl')); PushBox(output, DWORD_BE('stsd')); output.OutputDword(0); //version and flags (none) output.OutputDword(DWORD_BE(1)); //count PushBox(output, DWORD_BE('avc1')); output.OutputDword(0); //reserved 6 bytes output.OutputWord(0); output.OutputWord(WORD_BE(1)); //index output.OutputWord(0); //encoding version output.OutputWord(0); //encoding revision level output.OutputDword(0); //encoding vendor output.OutputDword(0); //temporal quality output.OutputDword(0); //spatial quality output.OutputWord(fastHtons(width)); //width output.OutputWord(fastHtons(height)); //height output.OutputDword(DWORD_BE(0x00480000)); //fixed point width pixel resolution (72.0) output.OutputDword(DWORD_BE(0x00480000)); //fixed point height pixel resolution (72.0) output.OutputDword(0); //quicktime video data size output.OutputWord(WORD_BE(1)); //frame count(?) output.OutputByte((BYTE)strlen(videoCompressionName)); //compression name length output.Serialize(videoCompressionName, 31); //31 bytes for the name output.OutputWord(WORD_BE(24)); //bit depth output.OutputWord(0xFFFF); //quicktime video color table id (none = -1) PushBox(output, DWORD_BE('avcC')); output.OutputByte(1); //version output.OutputByte(100); //h264 profile ID output.OutputByte(0); //h264 compatible profiles output.OutputByte(0x1f); //h264 level output.OutputByte(0xff); //reserved output.OutputByte(0xe1); //first half-byte = no clue. second half = sps count output.OutputWord(fastHtons(SPS.Num())); //sps size output.Serialize(SPS.Array(), SPS.Num()); //sps data output.OutputByte(1); //pps count output.OutputWord(fastHtons(PPS.Num())); //pps size output.Serialize(PPS.Array(), PPS.Num()); //pps data PopBox(output); //avcC PopBox(output); //avc1 PopBox(output); //stsd PushBox(output, DWORD_BE('stts')); //frame times output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(videoDecodeTimes.Num())); for(UINT i=0; i<videoDecodeTimes.Num(); i++) { output.OutputDword(fastHtonl(videoDecodeTimes[i].count)); output.OutputDword(fastHtonl(videoDecodeTimes[i].val)); } PopBox(output); //stts //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 60, 0); //ProcessEvents(); if (IFrameIDs.Num()) { PushBox(output, DWORD_BE('stss')); //list of keyframe (i-frame) IDs output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(IFrameIDs.Num())); output.Serialize(IFrameIDs.Array(), IFrameIDs.Num()*sizeof(UINT)); PopBox(output); //stss } PushBox(output, DWORD_BE('ctts')); //list of composition time offsets output.OutputDword(0); //version (0) and flags (none) //output.OutputDword(DWORD_BE(0x01000000)); //version (1) and flags (none) output.OutputDword(fastHtonl(compositionOffsets.Num())); for(UINT i=0; i<compositionOffsets.Num(); i++) { output.OutputDword(fastHtonl(compositionOffsets[i].count)); output.OutputDword(fastHtonl(compositionOffsets[i].val)); } PopBox(output); //ctts //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 70, 0); //ProcessEvents(); PushBox(output, DWORD_BE('stsc')); //sample to chunk list output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(videoSampleToChunk.Num())); for(UINT i=0; i<videoSampleToChunk.Num(); i++) { SampleToChunk &stc = videoSampleToChunk[i]; output.OutputDword(fastHtonl(stc.firstChunkID)); output.OutputDword(fastHtonl(stc.samplesPerChunk)); output.OutputDword(DWORD_BE(1)); } PopBox(output); //stsc PushBox(output, DWORD_BE('stsz')); //sample sizes output.OutputDword(0); //version and flags (none) output.OutputDword(0); //block size for all (0 if differing sizes) output.OutputDword(fastHtonl(videoFrames.Num())); for(UINT i=0; i<videoFrames.Num(); i++) output.OutputDword(fastHtonl(videoFrames[i].size)); PopBox(output); if(videoChunks.Num() && videoChunks.Last() > 0xFFFFFFFFLL) { PushBox(output, DWORD_BE('co64')); //chunk offsets output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(videoChunks.Num())); for(UINT i=0; i<videoChunks.Num(); i++) output.OutputQword(fastHtonll(videoChunks[i])); PopBox(output); //co64 } else { PushBox(output, DWORD_BE('stco')); //chunk offsets output.OutputDword(0); //version and flags (none) output.OutputDword(fastHtonl(videoChunks.Num())); for(UINT i=0; i<videoChunks.Num(); i++) output.OutputDword(fastHtonl((DWORD)videoChunks[i])); PopBox(output); //stco } PopBox(output); //stbl PopBox(output); //minf PopBox(output); //mdia PopBox(output); //trak //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 80, 0); //ProcessEvents(); //------------------------------------------------------ // info thingy PushBox(output, DWORD_BE('udta')); PushBox(output, DWORD_BE('meta')); output.OutputDword(0); //version and flags (none) PushBox(output, DWORD_BE('hdlr')); output.OutputDword(0); //version and flags (none) output.OutputDword(0); //quicktime type output.OutputDword(DWORD_BE('mdir')); //metadata type output.OutputDword(DWORD_BE('appl')); //quicktime manufacturer reserved thingy output.OutputDword(0); //quicktime component reserved flag output.OutputDword(0); //quicktime component reserved flag mask output.OutputByte(0); //null string PopBox(output); //hdlr PushBox(output, DWORD_BE('ilst')); PushBox(output, DWORD_BE('\xa9too')); PushBox(output, DWORD_BE('data')); output.OutputDword(DWORD_BE(1)); //version (1) + flags (0) output.OutputDword(0); //reserved LPSTR lpVersion = OBS_VERSION_STRING_ANSI; output.Serialize(lpVersion, (DWORD)strlen(lpVersion)); PopBox(output); //data PopBox(output); //@too PopBox(output); //ilst PopBox(output); //meta PopBox(output); //udta PopBox(output); //moov fileOut.Serialize(endBuffer.Array(), (DWORD)output.GetPos()); fileOut.Close(); XFile file; if(file.Open(strFile, XFILE_WRITE, XFILE_OPENEXISTING)) { #ifdef USE_64BIT_MP4 file.SetPos((INT64)mdatStart+8, XFILE_BEGIN); UINT64 size = fastHtonll(mdatStop-mdatStart); file.Write(&size, 8); #else file.SetPos((INT64)mdatStart, XFILE_BEGIN); UINT size = fastHtonl((DWORD)(mdatStop-mdatStart)); file.Write(&size, 4); #endif file.Close(); } App->EnableSceneSwitching(true); //DestroyWindow(hwndProgressDialog); }
void SkinMesh::LoadAnimations(CTSTR lpName) { traceIn(SkinMesh::LoadAnimations); assert(lpName); DWORD i, j, k; String strMeshPath; if(!Engine::ConvertResourceName(lpName, TEXT("models"), strMeshPath)) return; String strFilePath; strFilePath << GetPathWithoutExtension(strMeshPath) << TEXT(".xan"); //----------------------------------------------- XFileInputSerializer animData; if(!animData.Open(strFilePath)) { AppWarning(TEXT("Could not open animation file \"%s\""), (TSTR)strFilePath); return; } DWORD ver; animData << ver; if(ver != ANIMFILE_VER && ver != 0x100) { AppWarning(TEXT("'%s': Bad animation file version"), strFilePath); animData.Close(); return; } //----------------------------------------------- unsigned int numSequences; animData << numSequences; SequenceList.SetSize(numSequences); for(i=0; i<numSequences; i++) animData << SequenceList[i]; DWORD nBones; animData << nBones; BoneList.SetSize(nBones); for(i=0; i<nBones; i++) { Bone &bone = BoneList[i]; List<DWORD> RigidVerts; List<VWeight> BlendedVerts; DWORD nRigid, nBlended; //---------------------------- animData << bone.Pos << bone.Rot << nRigid << nBlended << bone.flags << bone.idParent; animData << RigidVerts; animData << BlendedVerts; if(bone.idParent != 0xFFFFFFFF) { bone.Parent = BoneList+bone.idParent; bone.LocalPos = bone.Pos - BoneList[bone.idParent].Pos; BoneList[bone.idParent].Children << &bone; } else { bone.Parent = NULL; bone.LocalPos = bone.Pos; bone.flags |= BONE_ROOT; } //---------------------------- bone.Weights.SetSize(nRigid); for(j=0; j<RigidVerts.Num(); j++) { VWeight &weight = bone.Weights[j]; weight.vert = RigidVerts[j]; weight.weight = 1.0f; } bone.Weights.AppendList(BlendedVerts); //---------------------------- DWORD nAnim; animData << nAnim; bone.seqKeys.SetSize(nAnim); for(j=0; j<nAnim; j++) { SeqKeys &keys = bone.seqKeys[j]; DWORD nPosKeys, nRotKeys; //---------------------------- animData << nRotKeys; keys.hasRotKeys = nRotKeys > 0; if(keys.hasRotKeys) { keys.lpRotKeys = (Quat*)Allocate(nRotKeys*sizeof(Quat)); animData.Serialize(keys.lpRotKeys, nRotKeys*sizeof(Quat)); keys.lpRotTans = (Quat*)Allocate(nRotKeys*sizeof(Quat)); for(k=0; k<nRotKeys; k++) { DWORD kp1 = (k == (nRotKeys-1)) ? k : (k+1); DWORD km1 = (k == 0) ? 0 : k-1; Quat &qk = keys.lpRotKeys[k]; Quat &qkp1 = keys.lpRotKeys[kp1]; Quat &qkm1 = keys.lpRotKeys[km1]; keys.lpRotTans[k] = qk.GetInterpolationTangent(qkm1, qkp1); } } //---------------------------- animData << nPosKeys; keys.hasPosKeys = nPosKeys > 0; if(keys.hasPosKeys) { keys.lpPosKeys = (Vect*)Allocate(nPosKeys*sizeof(Vect)); Vect::SerializeArray(animData, keys.lpPosKeys, nPosKeys); keys.lpPosTans = (Vect*)Allocate(nPosKeys*sizeof(Vect)); for(k=0; k<nPosKeys; k++) { DWORD kp1 = (k == (nPosKeys-1)) ? k : (k+1); DWORD km1 = (k == 0) ? 0 : k-1; Vect &pk = keys.lpPosKeys[k]; Vect &pkp1 = keys.lpPosKeys[kp1]; Vect &pkm1 = keys.lpPosKeys[km1]; keys.lpPosTans[k] = pk.GetInterpolationTangent(pkm1, pkp1); } } } } //----------------------------------------------- int num; animData << num; BoneExtensions.SetSize(num); BoneExtensionNames.SetSize(num); for(int i=0; i<num; i++) { animData << BoneExtensionNames[i]; animData << BoneExtensions[i]; } //----------------------------------------------- AnimatedSections.SetSize(nSections); if(ver == 0x100) //remove { UINT *indices = (UINT*)IdxBuffer->GetData(); List<UINT> adjustedIndices; adjustedIndices.CopyArray(indices, IdxBuffer->NumIndices()); VBData *vbd = VertBuffer->GetData(); List<Vect> &verts = vbd->VertList; //--------- // get vert data List<VertAnimInfo> vertInfo; vertInfo.SetSize(nVerts); for(int i=0; i<nBones; i++) { Bone &bone = BoneList[i]; for(int j=0; j<bone.Weights.Num(); j++) { VWeight &vertWeight = bone.Weights[j]; if(!vertInfo[vertWeight.vert].bones.HasValue(i)) { vertInfo[vertWeight.vert].bones << i; vertInfo[vertWeight.vert].weights << vertWeight.weight; } } } //--------- // remove excess bone influence from verts (let just set the max to 3 for this) /*for(int i=0; i<vertInfo.Num(); i++) { VertAnimInfo &vert = vertInfo[i]; while(vert.bones.Num() > 3) { float weakestWeight = M_INFINITE; UINT weakestID; for(int j=0; j<vert.bones.Num(); j++) { if(vert.weights[j] < weakestWeight) { weakestID = j; weakestWeight = vert.weights[j]; } } float weightAdjust = 1.0f/(1.0f-weakestWeight); vert.weights.Remove(weakestID); vert.bones.Remove(weakestID); for(int j=0; j<vert.weights.Num(); j++) vert.weights[j] *= weightAdjust; } }*/ for(int i=0; i<vertInfo.Num(); i++) { VertAnimInfo &vert = vertInfo[i]; for(int j=0; j<vert.bones.Num(); j++) { if(vert.weights[j] <= 0.15f) { float weightAdjust = 1.0f/(1.0f-vert.weights[j]); vert.weights.Remove(j); vert.bones.Remove(j); for(int k=0; k<vert.weights.Num(); k++) vert.weights[k] *= weightAdjust; --j; } } } //--------- // remove excess bone influence from tris (can only have 4 bones influencing any triangle) for(int i=0; i<nSections; i++) { DrawSection §ion = SectionList[i]; if(!section.numFaces) continue; for(int j=0; j<section.numFaces; j++) { UINT *triVertIDs = &indices[(section.startFace+j)*3]; List<UINT> bones; List<float> bestVertWeights; for(int k=0; k<3; k++) { VertAnimInfo &info = vertInfo[triVertIDs[k]]; for(int l=0; l<info.bones.Num(); l++) { UINT id = bones.FindValueIndex(info.bones[l]); if(id == INVALID) { bones.Add(info.bones[l]); bestVertWeights.Add(info.weights[l]); } else bestVertWeights[id] = MAX(bestVertWeights[id], info.weights[l]); } } while(bones.Num() > 4) { int removeBone, removeBoneID; float bestWeight = M_INFINITE; for(int k=0; k<bones.Num(); k++) { if(bestVertWeights[k] < bestWeight) { removeBone = bones[k]; removeBoneID = k; bestWeight = bestVertWeights[k]; } } for(int k=0; k<3; k++) { VertAnimInfo &info = vertInfo[triVertIDs[k]]; UINT id = info.bones.FindValueIndex(removeBone); if(id == INVALID) continue; float weightAdjust = 1.0f/(1.0f-info.weights[id]); info.weights.Remove(id); info.bones.Remove(id); for(int l=0; l<info.weights.Num(); l++) info.weights[l] *= weightAdjust; } bones.Remove(removeBoneID); bestVertWeights.Remove(removeBoneID); } } } //--------- // sort out sections of triangles that are influenced up to a max of 4 bones // also, duplicate shared verts VBData *newVBD = new VBData; newVBD->CopyList(*vbd); newVBD->TVList.SetSize(2); newVBD->TVList[1].SetWidth(4); newVBD->TVList[1].SetSize(nVerts); List<SubSectionInfo> newSubSections; for(int i=0; i<nSections; i++) { List<TriBoneInfo> triInfo; DrawSection §ion = SectionList[i]; if(!section.numFaces) continue; for(int j=0; j<section.numFaces; j++) { UINT *triVertIDs = &indices[(section.startFace+j)*3]; TriBoneInfo &newTri = *triInfo.CreateNew(); for(int k=0; k<3; k++) { VertAnimInfo &info = vertInfo[triVertIDs[k]]; for(int l=0; l<info.bones.Num(); l++) newTri.bones.SafeAdd(info.bones[l]); } } BitList UsedTris; UsedTris.SetSize(section.numFaces); DWORD nUsedTris = 0; while(nUsedTris != section.numFaces) { DWORD triSectionID = newSubSections.Num(); SubSectionInfo *curSubSecInfo = newSubSections.CreateNew(); curSubSecInfo->section = i; for(int j=0; j<triInfo.Num(); j++) { if(UsedTris[j]) continue; TriBoneInfo &tri = triInfo[j]; List<UINT> secBones; secBones.CopyList(curSubSecInfo->bones); BOOL bBadTri = FALSE; for(int k=0; k<tri.bones.Num(); k++) { secBones.SafeAdd(tri.bones[k]); if(secBones.Num() > 4) break; } if(secBones.Num() > 4) continue; DWORD triID = section.startFace+j; curSubSecInfo->bones.CopyList(secBones); curSubSecInfo->tris << triID; UINT *triVertIDs = &indices[triID*3]; for(int k=0; k<3; k++) { VertAnimInfo *info = &vertInfo[triVertIDs[k]]; UINT id = info->sections.FindValueIndex(triSectionID); if(id == INVALID) { UINT vertID; if(info->sections.Num() >= 1) //duplicate vertex { vertID = newVBD->DuplicateVertex(triVertIDs[k]); adjustedIndices[(triID*3)+k] = vertID; VertAnimInfo &newVertInfo = *vertInfo.CreateNew(); info = &vertInfo[triVertIDs[k]]; //reset pointer newVertInfo.bones.CopyList(info->bones); newVertInfo.weights.CopyList(info->weights); newVertInfo.sections << triSectionID; } else vertID = triVertIDs[k]; info->sections << triSectionID; info->sectionVertIDs << vertID; List<Vect4> &vertWeights = *newVBD->TVList[1].GetV4(); for(int l=0; l<4; l++) { if(l >= curSubSecInfo->bones.Num()) vertWeights[vertID].ptr[l] = 0.0f; else { UINT boneID = curSubSecInfo->bones[l]; UINT weightID = info->bones.FindValueIndex(boneID); if(weightID == INVALID) vertWeights[vertID].ptr[l] = 0.0f; else vertWeights[vertID].ptr[l] = info->weights[weightID]; } } } else adjustedIndices[(triID*3)+k] = info->sectionVertIDs[id]; } UsedTris.Set(j); ++nUsedTris; } } for(int j=0; j<triInfo.Num(); j++) triInfo[j].FreeData(); } //--------- // create animated draw sections and create new index buffer DWORD curTriID = 0; List<UINT> newIndices; for(int i=0; i<newSubSections.Num(); i++) { SubSectionInfo &subSecInfo = newSubSections[i]; AnimSection &animSection = AnimatedSections[subSecInfo.section]; AnimSubSection &subSection = *animSection.SubSections.CreateNew(); subSection.numBones = subSecInfo.bones.Num(); for(int j=0; j<subSecInfo.bones.Num(); j++) subSection.bones[j] = subSecInfo.bones[j]; subSection.startFace = curTriID; for(int j=0; j<subSecInfo.tris.Num(); j++) { UINT *tri = &adjustedIndices[subSecInfo.tris[j]*3]; newIndices << tri[0] << tri[1] << tri[2]; ++curTriID; } subSection.numFaces = curTriID-subSection.startFace; subSecInfo.FreeData(); } //rebuild original bone data for(int i=0; i<BoneList.Num(); i++) BoneList[i].Weights.Clear(); for(int i=0; i<vertInfo.Num(); i++) { for(int j=0; j<vertInfo[i].bones.Num(); j++) { VWeight weight; weight.vert = i; weight.weight = vertInfo[i].weights[j]; BoneList[vertInfo[i].bones[j]].Weights << weight; } vertInfo[i].FreeData(); } delete VertBuffer; delete IdxBuffer; UINT numIndices; UINT *indexArray; newIndices.TransferTo(indexArray, numIndices); nVerts = newVBD->VertList.Num(); IdxBuffer = CreateIndexBuffer(GS_UNSIGNED_LONG, indexArray, numIndices); VertBuffer = CreateVertexBuffer(newVBD); } else { for(int i=0; i<nSections; i++) animData << AnimatedSections[i].SubSections; } animData.Close(); traceOut; }
void OBS::MainAudioLoop() { DWORD taskID = 0; HANDLE hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskID); bPushToTalkOn = false; micMax = desktopMax = VOL_MIN; micPeak = desktopPeak = VOL_MIN; UINT audioFramesSinceMeterUpdate = 0; UINT audioFramesSinceMicMaxUpdate = 0; UINT audioFramesSinceDesktopMaxUpdate = 0; List<float> mixedLatestDesktopSamples; List<float> blank10msSample; blank10msSample.SetSize(882); QWORD lastAudioTime = 0; while(TRUE) { OSSleep(5); //screw it, just run it every 5ms if(!bRunning) break; //----------------------------------------------- float *desktopBuffer, *micBuffer; UINT desktopAudioFrames = 0, micAudioFrames = 0; UINT latestDesktopAudioFrames = 0, latestMicAudioFrames = 0; curDesktopVol = desktopVol * desktopBoost; if(bUsingPushToTalk) curMicVol = bPushToTalkOn ? micVol : 0.0f; else curMicVol = micVol; curMicVol *= micBoost; bool bDesktopMuted = (curDesktopVol < EPSILON); bool bMicEnabled = (micAudio != NULL); QWORD timestamp; while(QueryNewAudio(timestamp)) { if (!lastAudioTime) lastAudioTime = App->GetSceneTimestamp(); if (lastAudioTime < timestamp) { while ((lastAudioTime+=10) < timestamp) EncodeAudioSegment(blank10msSample.Array(), 441, lastAudioTime); } //---------------------------------------------------------------------------- // get latest sample for calculating the volume levels float *latestDesktopBuffer = NULL, *latestMicBuffer = NULL; desktopAudio->GetBuffer(&desktopBuffer, &desktopAudioFrames, timestamp-10); desktopAudio->GetNewestFrame(&latestDesktopBuffer, &latestDesktopAudioFrames); UINT totalFloats = desktopAudioFrames*2; if(bDesktopMuted) { // Clearing the desktop audio buffer before mixing in the auxiliary audio sources. zero(desktopBuffer, sizeof(*desktopBuffer)*totalFloats); } if(micAudio != NULL) { micAudio->GetBuffer(&micBuffer, &micAudioFrames, timestamp-10); micAudio->GetNewestFrame(&latestMicBuffer, &latestMicAudioFrames); } //---------------------------------------------------------------------------- // get latest aux volume level samples and mix OSEnterMutex(hAuxAudioMutex); mixedLatestDesktopSamples.CopyArray(latestDesktopBuffer, latestDesktopAudioFrames*2); for(UINT i=0; i<auxAudioSources.Num(); i++) { float *latestAuxBuffer; if(auxAudioSources[i]->GetNewestFrame(&latestAuxBuffer, &latestDesktopAudioFrames)) MixAudio(mixedLatestDesktopSamples.Array(), latestAuxBuffer, latestDesktopAudioFrames*2, false); } //---------------------------------------------------------------------------- // mix output aux sound samples with the desktop for(UINT i=0; i<auxAudioSources.Num(); i++) { float *auxBuffer; if(auxAudioSources[i]->GetBuffer(&auxBuffer, &desktopAudioFrames, timestamp-10)) MixAudio(desktopBuffer, auxBuffer, desktopAudioFrames*2, false); } OSLeaveMutex(hAuxAudioMutex); //---------------------------------------------------------------------------- //UINT totalFloats = desktopAudioFrames*2; //---------------------------------------------------------------------------- /*multiply samples by volume and compute RMS and max of samples*/ float desktopRMS = 0, micRMS = 0, desktopMx = 0, micMx = 0; // Use 1.0f instead of curDesktopVol, since aux audio sources already have their volume set, and shouldn't be boosted anyway. if(latestDesktopBuffer) CalculateVolumeLevels(mixedLatestDesktopSamples.Array(), latestDesktopAudioFrames*2, 1.0f, desktopRMS, desktopMx); if(bMicEnabled && latestMicBuffer) CalculateVolumeLevels(latestMicBuffer, latestMicAudioFrames*2, curMicVol, micRMS, micMx); /*convert RMS and Max of samples to dB*/ desktopRMS = toDB(desktopRMS); micRMS = toDB(micRMS); desktopMx = toDB(desktopMx); micMx = toDB(micMx); /* update max if sample max is greater or after 1 second */ float maxAlpha = 0.15f; UINT peakMeterDelayFrames = 44100 * 3; if(micMx > micMax) { micMax = micMx; } else { micMax = maxAlpha * micMx + (1.0f - maxAlpha) * micMax; } if(desktopMx > desktopMax) { desktopMax = desktopMx; } else { desktopMax = maxAlpha * desktopMx + (1.0f - maxAlpha) * desktopMax; } /*update delayed peak meter*/ if(micMax > micPeak || audioFramesSinceMicMaxUpdate > peakMeterDelayFrames) { micPeak = micMax; audioFramesSinceMicMaxUpdate = 0; } else { audioFramesSinceMicMaxUpdate += desktopAudioFrames; } if(desktopMax > desktopPeak || audioFramesSinceDesktopMaxUpdate > peakMeterDelayFrames) { desktopPeak = desktopMax; audioFramesSinceDesktopMaxUpdate = 0; } else { audioFramesSinceDesktopMaxUpdate += desktopAudioFrames; } /*low pass the level sampling*/ float rmsAlpha = 0.15f; desktopMag = rmsAlpha * desktopRMS + desktopMag * (1.0f - rmsAlpha); micMag = rmsAlpha * micRMS + micMag * (1.0f - rmsAlpha); /*update the meter about every 50ms*/ audioFramesSinceMeterUpdate += desktopAudioFrames; if(audioFramesSinceMeterUpdate >= 2205) { PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_MICVOLUMEMETER, VOLN_METERED), 0); audioFramesSinceMeterUpdate = 0; } //---------------------------------------------------------------------------- // mix mic and desktop sound, using SSE2 if available // also, it's perfectly fine to just mix into the returned buffer if(bMicEnabled) MixAudio(desktopBuffer, micBuffer, totalFloats, bForceMicMono); EncodeAudioSegment(desktopBuffer, totalFloats>>1, lastAudioTime); } //----------------------------------------------- if(!bRecievedFirstAudioFrame && pendingAudioFrames.Num()) bRecievedFirstAudioFrame = true; } desktopMag = desktopMax = desktopPeak = VOL_MIN; micMag = micMax = micPeak = VOL_MIN; PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_MICVOLUMEMETER, VOLN_METERED), 0); for(UINT i=0; i<pendingAudioFrames.Num(); i++) pendingAudioFrames[i].audioData.Clear(); AvRevertMmThreadCharacteristics(hTask); }