/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program 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 3 of the License, or (at your option) any later version. This program 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, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include "plAVIWriter.h" #include "hsTypes.h" #include "hsWindows.h" #include #include "hsTimer.h" #include "plMipmap.h" #include "../plMessage/plRenderMsg.h" #include "plPipeline.h" #include "../pnDispatch/plDispatch.h" #include "../pnKeyedObject/plFixedKey.h" bool plAVIWriter::fInitialized = false; class plAVIWriterImp : public plAVIWriter { protected: PAVIFILE fFileHandle; PAVISTREAM fStreamHandle; PAVISTREAM fCompressedHandle; BITMAPINFOHEADER fBitmapInfo; hsBool fOldRealTime; hsScalar fOldFrameTimeInc; double fStartTime; void IFillStreamInfo(AVISTREAMINFO* inf, plPipeline* pipeline); void IFillBitmapInfo(BITMAPINFOHEADER* inf, plPipeline* pipeline); bool ICaptureFrame(plPipeline* pipeline); public: plAVIWriterImp(); virtual ~plAVIWriterImp(); virtual hsBool MsgReceive(plMessage* msg); virtual void Shutdown(); virtual bool Open(const char* fileName, plPipeline* pipeline); virtual void Close(); }; plAVIWriter::~plAVIWriter() { } plAVIWriter& plAVIWriter::Instance() { static plAVIWriterImp theInstance; if (!fInitialized) { theInstance.RegisterAs(kAVIWriter_KEY); fInitialized = true; } return theInstance; } //////////////////////////////////////////////////////////////////////////////// plAVIWriterImp::plAVIWriterImp() : fStartTime(0), fOldRealTime(false), fStreamHandle(nil), fCompressedHandle(nil), fFileHandle(nil) { AVIFileInit(); } plAVIWriterImp::~plAVIWriterImp() { } void plAVIWriterImp::Shutdown() { Close(); UnRegisterAs(kAVIWriter_KEY); SetKey(nil); } #include "plProfile.h" plProfile_CreateTimer("AviCapture", "RenderSetup", AviCapture); hsBool plAVIWriterImp::MsgReceive(plMessage* msg) { plRenderMsg* renderMsg = plRenderMsg::ConvertNoRef(msg); if (renderMsg) { plProfile_BeginTiming(AviCapture); ICaptureFrame(renderMsg->Pipeline()); plProfile_EndTiming(AviCapture); } return hsKeyedObject::MsgReceive(msg); } static const int kFramesPerSec = 30; bool plAVIWriterImp::Open(const char* fileName, plPipeline* pipeline) { // Already writing, fail if (fStreamHandle) return false; fStartTime = hsTimer::GetSysSeconds(); // If we're running in real time, set to frame time fOldRealTime = hsTimer::IsRealTime(); if (fOldRealTime) { hsTimer::SetRealTime(false); hsTimer::SetFrameTimeInc(1.f / kFramesPerSec); } // Open AVI file HRESULT err; err = AVIFileOpen( &fFileHandle, // returned file pointer fileName, // file name OF_WRITE | OF_CREATE, // mode to open file with NULL); // use handler determined hsAssert(err == AVIERR_OK, "Error creating AVI file in plAVIWriter::Open"); if (err != AVIERR_OK) { Close(); return false; } AVISTREAMINFO streamInfo; IFillStreamInfo(&streamInfo, pipeline); // Create a video stream in the file err = AVIFileCreateStream( fFileHandle, // file pointer &fStreamHandle, // returned stream pointer &streamInfo ); // stream header hsAssert(err == AVIERR_OK, "Error creating video stream in plAVIWriter::Open"); if (err != AVIERR_OK) { Close(); return false; } do { AVICOMPRESSOPTIONS opts; AVICOMPRESSOPTIONS FAR * aopts[1] = {&opts}; memset(&opts, 0, sizeof(opts)); BOOL bErr = AVISaveOptions(NULL, ICMF_CHOOSE_DATARATE, 1, &fStreamHandle, (LPAVICOMPRESSOPTIONS FAR*)&aopts); hsAssert(bErr, "Error saving stream options in plAVIWriter::Open"); if (!bErr) { Close(); return false; } err = AVIMakeCompressedStream(&fCompressedHandle, fStreamHandle, &opts, NULL); hsAssert(err == AVIERR_OK, "Error creating compressed stream in plAVIWriter::Open"); if (err != AVIERR_OK) { Close(); return false; } IFillBitmapInfo(&fBitmapInfo, pipeline); err = AVIStreamSetFormat( fCompressedHandle, 0, &fBitmapInfo, // stream format fBitmapInfo.biSize); } while (err != AVIERR_OK && hsMessageBox("Codec unavailable, try again?", "AVI Writer", hsMessageBoxYesNo) == hsMBoxYes); if (err != AVIERR_OK) { Close(); return false; } plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); return true; } void plAVIWriterImp::Close() { plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); hsTimer::SetRealTime(fOldRealTime); if (fStreamHandle) { AVIStreamClose(fStreamHandle); fStreamHandle = nil; } if (fCompressedHandle) { AVIStreamClose(fCompressedHandle); fCompressedHandle = nil; } if (fFileHandle) { AVIFileClose(fFileHandle); fFileHandle = nil; } AVIFileExit(); } void plAVIWriterImp::IFillStreamInfo(AVISTREAMINFO* inf, plPipeline* pipeline) { memset(inf, 0, sizeof(AVISTREAMINFO)); inf->fccType = streamtypeVIDEO; inf->fccHandler = 0; inf->dwScale = 1; inf->dwRate = kFramesPerSec; SetRect(&inf->rcFrame, 0,0, pipeline->Width(), pipeline->Height()); } void plAVIWriterImp::IFillBitmapInfo(BITMAPINFOHEADER* inf, plPipeline* pipeline) { memset(inf,0,sizeof(BITMAPINFOHEADER)); inf->biSize = sizeof(BITMAPINFOHEADER); inf->biPlanes = 1; inf->biBitCount = 32; inf->biCompression = BI_RGB; inf->biSizeImage = 0; inf->biXPelsPerMeter = 0; inf->biYPelsPerMeter = 0; inf->biClrUsed = 0; inf->biClrImportant = 0; inf->biWidth = pipeline->Width(); inf->biHeight = pipeline->Height(); } bool plAVIWriterImp::ICaptureFrame(plPipeline* pipeline) { plMipmap frame; pipeline->CaptureScreen(&frame, true); double time = hsTimer::GetSysSeconds() - fStartTime; time *= kFramesPerSec; HRESULT err; err = AVIStreamWrite( fCompressedHandle, int(time), 1, (LPBYTE)frame.GetAddr32(0,0), frame.GetTotalSize(), AVIIF_KEYFRAME, NULL, NULL); return (err == AVIERR_OK); }