celestia/src/celestia/avicapture.cpp

225 lines
5.0 KiB
C++

// avicapture.cpp
//
// Copyright (C) 2001-2008, Chris Laurel <claurel@shatters.net>
//
// 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 2
// of the License, or (at your option) any later version.
#include <cmath>
#include <windowsx.h>
#include <celutil/debug.h>
#include "avicapture.h"
using namespace std;
AVICapture::AVICapture(const Renderer *r) :
MovieCapture(r)
{
AVIFileInit();
}
AVICapture::~AVICapture()
{
cleanup();
AVIFileExit();
}
bool AVICapture::start(const string& filename,
int w, int h,
float fps)
{
if (capturing)
return false;
width = w;
height = h;
frameRate = fps;
if (HIWORD(VideoForWindowsVersion()) < 0x010a)
{
// We need to be running on version 1.1 or later
return false;
}
// Compute the width of a row in bytes; pad so that rows are aligned on
// 4 byte boundaries.
int rowBytes = (width * 3 + 3) & ~0x3;
image = new unsigned char[rowBytes * height];
HRESULT hr = AVIFileOpenA(&aviFile,
filename.c_str(),
OF_WRITE | OF_CREATE,
nullptr);
if (hr != AVIERR_OK)
{
DPRINTF(0, "Erroring creating avi file for capture.\n");
return false;
}
AVISTREAMINFO info;
ZeroMemory(&info, sizeof info);
info.fccType = streamtypeVIDEO;
info.fccHandler = 0;
info.dwScale = 1;
info.dwRate = (DWORD) floor(frameRate + 0.5f);
info.dwSuggestedBufferSize = rowBytes * height;
SetRect(&info.rcFrame, 0, 0, width, height);
hr = AVIFileCreateStream(aviFile, &aviStream, &info);
if (hr != AVIERR_OK)
{
DPRINTF(0, "Error %08x creating AVI stream.\n", hr);
cleanup();
return false;
}
// Display a dialog to allow the user to select compression options
AVICOMPRESSOPTIONS options;
AVICOMPRESSOPTIONS* arrOptions[1] = { &options };
ZeroMemory(&options, sizeof options);
if (!AVISaveOptions(nullptr, 0, 1, &aviStream,
(LPAVICOMPRESSOPTIONS*) &arrOptions))
{
// The user either clicked on cancel or there was an error
cleanup();
return false;
}
hr = AVIMakeCompressedStream(&compAviStream, aviStream, &options, nullptr);
if (hr != AVIERR_OK)
{
DPRINTF(0, "Error %08x creating compressed AVI stream.\n", hr);
cleanup();
return false;
}
BITMAPINFOHEADER bi;
ZeroMemory(&bi, sizeof bi);
bi.biSize = sizeof bi;
bi.biWidth = width;
bi.biHeight = height;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = rowBytes * height;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
hr = AVIStreamSetFormat(compAviStream, 0, &bi, sizeof bi);
if (hr != AVIERR_OK)
{
DPRINTF(0, "AVIStreamSetFormat failed: %08x\n", hr);
cleanup();
return false;
}
capturing = true;
frameCounter = 0;
return true;
}
bool AVICapture::end()
{
capturing = false;
cleanup();
return true;
}
bool AVICapture::captureFrame()
{
if (!capturing)
return false;
// Get the dimensions of the current viewport
int x, y, w, h;
renderer->getViewport(&x, &y, &w, &h);
x += (w - width) / 2;
y += (h - height) / 2;
renderer->captureFrame(x, y, width, height,
Renderer::PixelFormat::BGR_EXT,
image);
int rowBytes = (width * 3 + 3) & ~0x3;
LONG samplesWritten = 0;
LONG bytesWritten = 0;
HRESULT hr = AVIStreamWrite(compAviStream,
frameCounter,
1,
image,
rowBytes * height,
AVIIF_KEYFRAME,
&samplesWritten,
&bytesWritten);
if (hr != AVIERR_OK)
{
DPRINTF(0, "AVIStreamWrite failed on frame %d\n", frameCounter);
return false;
}
// fmt::printf("Writing frame: %d %d => %d bytes\n",
// frameCounter, rowBytes * height, bytesWritten);
frameCounter++;
return true;
}
void AVICapture::cleanup()
{
if (aviStream != nullptr)
{
AVIStreamRelease(aviStream);
aviStream = nullptr;
}
if (compAviStream != nullptr)
{
AVIStreamRelease(compAviStream);
compAviStream = nullptr;
}
if (aviFile != nullptr)
{
AVIFileRelease(aviFile);
aviFile = nullptr;
}
if (image != nullptr)
{
delete[] image;
image = nullptr;
}
}
int AVICapture::getWidth() const
{
return width;
}
int AVICapture::getHeight() const
{
return height;
}
float AVICapture::getFrameRate() const
{
return frameRate;
}
int AVICapture::getFrameCount() const
{
return frameCounter;
}