mirror of
https://github.com/libretro/Mesen-S.git
synced 2026-11-07 04:16:00 -10:00
Cleanups
This commit is contained in:
1 parent
3c2900cfd6
commit
b0b53409ee
8 files changed
+2
-1033
No files matched your search
+2
-32
@@ -5,9 +5,6 @@
|
||||
#include "Console.h"
|
||||
#include "EmuSettings.h"
|
||||
#include "MessageManager.h"
|
||||
#include "../Utilities/IVideoRecorder.h"
|
||||
#include "../Utilities/AviRecorder.h"
|
||||
#include "../Utilities/GifRecorder.h"
|
||||
|
||||
VideoRenderer::VideoRenderer(shared_ptr<Console> console)
|
||||
{
|
||||
@@ -62,11 +59,6 @@ void VideoRenderer::RenderThread()
|
||||
|
||||
void VideoRenderer::UpdateFrame(void* frameBuffer, uint32_t width, uint32_t height)
|
||||
{
|
||||
shared_ptr<IVideoRecorder> recorder = _recorder;
|
||||
if(recorder) {
|
||||
recorder->AddFrame(frameBuffer, width, height, _console->GetFps());
|
||||
}
|
||||
|
||||
if(_renderer) {
|
||||
_renderer->UpdateFrame(frameBuffer, width, height);
|
||||
_waitForRender.Signal();
|
||||
@@ -89,39 +81,17 @@ void VideoRenderer::UnregisterRenderingDevice(IRenderingDevice *renderer)
|
||||
|
||||
void VideoRenderer::StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel)
|
||||
{
|
||||
FrameInfo frameInfo = _console->GetVideoDecoder()->GetFrameInfo();
|
||||
|
||||
shared_ptr<IVideoRecorder> recorder;
|
||||
if(codec == VideoCodec::GIF) {
|
||||
recorder.reset(new GifRecorder());
|
||||
} else {
|
||||
recorder.reset(new AviRecorder(codec, compressionLevel));
|
||||
}
|
||||
|
||||
if(recorder->StartRecording(filename, frameInfo.Width, frameInfo.Height, 4, _console->GetSettings()->GetAudioConfig().SampleRate, _console->GetFps())) {
|
||||
_recorder = recorder;
|
||||
MessageManager::DisplayMessage("VideoRecorder", "VideoRecorderStarted", filename);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoRenderer::AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
|
||||
{
|
||||
shared_ptr<IVideoRecorder> recorder = _recorder;
|
||||
if(recorder) {
|
||||
recorder->AddSound(soundBuffer, sampleCount, sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoRenderer::StopRecording()
|
||||
{
|
||||
shared_ptr<IVideoRecorder> recorder = _recorder;
|
||||
if(recorder) {
|
||||
MessageManager::DisplayMessage("VideoRecorder", "VideoRecorderStopped", recorder->GetOutputFile());
|
||||
}
|
||||
_recorder.reset();
|
||||
}
|
||||
|
||||
bool VideoRenderer::IsRecording()
|
||||
{
|
||||
return _recorder != nullptr && _recorder->IsRecording();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -131,8 +131,6 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
|
||||
$(CORE_DIR)/WaveRecorder.cpp \
|
||||
$(UTIL_DIR)/ArchiveReader.cpp \
|
||||
$(UTIL_DIR)/AutoResetEvent.cpp \
|
||||
$(UTIL_DIR)/AviRecorder.cpp \
|
||||
$(UTIL_DIR)/AviWriter.cpp \
|
||||
$(UTIL_DIR)/blip_buf.cpp \
|
||||
$(UTIL_DIR)/BpsPatcher.cpp \
|
||||
$(UTIL_DIR)/CamstudioCodec.cpp \
|
||||
@@ -160,7 +158,6 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
|
||||
$(UTIL_DIR)/VirtualFile.cpp \
|
||||
$(UTIL_DIR)/ZipReader.cpp \
|
||||
$(UTIL_DIR)/ZipWriter.cpp \
|
||||
$(UTIL_DIR)/ZmbvCodec.cpp \
|
||||
$(UTIL_DIR)/HQX/hq2x.cpp \
|
||||
$(UTIL_DIR)/HQX/hq3x.cpp \
|
||||
$(UTIL_DIR)/HQX/hq4x.cpp \
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "AviRecorder.h"
|
||||
|
||||
AviRecorder::AviRecorder(VideoCodec codec, uint32_t compressionLevel)
|
||||
{
|
||||
_recording = false;
|
||||
_stopFlag = false;
|
||||
_frameBuffer = nullptr;
|
||||
_frameBufferLength = 0;
|
||||
_sampleRate = 0;
|
||||
_codec = codec;
|
||||
_compressionLevel = compressionLevel;
|
||||
}
|
||||
|
||||
AviRecorder::~AviRecorder()
|
||||
{
|
||||
if(_recording) {
|
||||
StopRecording();
|
||||
}
|
||||
|
||||
if(_frameBuffer) {
|
||||
delete[] _frameBuffer;
|
||||
_frameBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AviRecorder::StartRecording(string filename, uint32_t width, uint32_t height, uint32_t bpp, uint32_t audioSampleRate, double fps)
|
||||
{
|
||||
if(!_recording) {
|
||||
_outputFile = filename;
|
||||
_sampleRate = audioSampleRate;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_fps = fps;
|
||||
_frameBufferLength = height * width * bpp;
|
||||
_frameBuffer = new uint8_t[_frameBufferLength];
|
||||
|
||||
_aviWriter.reset(new AviWriter());
|
||||
if(!_aviWriter->StartWrite(filename, _codec, width, height, bpp, (uint32_t)(_fps * 1000000), audioSampleRate, _compressionLevel)) {
|
||||
_aviWriter.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
_aviWriterThread = std::thread([=]() {
|
||||
while(!_stopFlag) {
|
||||
_waitFrame.Wait();
|
||||
if(_stopFlag) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto lock = _lock.AcquireSafe();
|
||||
_aviWriter->AddFrame(_frameBuffer);
|
||||
}
|
||||
});
|
||||
|
||||
_recording = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AviRecorder::StopRecording()
|
||||
{
|
||||
if(_recording) {
|
||||
_recording = false;
|
||||
|
||||
_stopFlag = true;
|
||||
_waitFrame.Signal();
|
||||
_aviWriterThread.join();
|
||||
|
||||
_aviWriter->EndWrite();
|
||||
_aviWriter.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AviRecorder::AddFrame(void* frameBuffer, uint32_t width, uint32_t height, double fps)
|
||||
{
|
||||
if(_recording) {
|
||||
if(_width != width || _height != height || _fps != fps) {
|
||||
StopRecording();
|
||||
} else {
|
||||
auto lock = _lock.AcquireSafe();
|
||||
memcpy(_frameBuffer, frameBuffer, _frameBufferLength);
|
||||
_waitFrame.Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AviRecorder::AddSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
|
||||
{
|
||||
if(_recording) {
|
||||
if(_sampleRate != sampleRate) {
|
||||
auto lock = _lock.AcquireSafe();
|
||||
StopRecording();
|
||||
} else {
|
||||
_aviWriter->AddSound(soundBuffer, sampleCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AviRecorder::IsRecording()
|
||||
{
|
||||
return _recording;
|
||||
}
|
||||
|
||||
string AviRecorder::GetOutputFile()
|
||||
{
|
||||
return _outputFile;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <thread>
|
||||
#include "AutoResetEvent.h"
|
||||
#include "AviWriter.h"
|
||||
#include "SimpleLock.h"
|
||||
#include "IVideoRecorder.h"
|
||||
|
||||
class Console;
|
||||
|
||||
class AviRecorder : public IVideoRecorder
|
||||
{
|
||||
private:
|
||||
std::thread _aviWriterThread;
|
||||
|
||||
unique_ptr<AviWriter> _aviWriter;
|
||||
|
||||
string _outputFile;
|
||||
SimpleLock _lock;
|
||||
AutoResetEvent _waitFrame;
|
||||
|
||||
atomic<bool> _stopFlag;
|
||||
bool _recording;
|
||||
uint8_t* _frameBuffer;
|
||||
uint32_t _frameBufferLength;
|
||||
uint32_t _sampleRate;
|
||||
|
||||
double _fps;
|
||||
uint32_t _width;
|
||||
uint32_t _height;
|
||||
|
||||
VideoCodec _codec;
|
||||
uint32_t _compressionLevel;
|
||||
|
||||
public:
|
||||
AviRecorder(VideoCodec codec, uint32_t compressionLevel);
|
||||
virtual ~AviRecorder();
|
||||
|
||||
bool StartRecording(string filename, uint32_t width, uint32_t height, uint32_t bpp, uint32_t audioSampleRate, double fps) override;
|
||||
void StopRecording() override;
|
||||
|
||||
void AddFrame(void* frameBuffer, uint32_t width, uint32_t height, double fps) override;
|
||||
void AddSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate) override;
|
||||
|
||||
bool IsRecording() override;
|
||||
string GetOutputFile() override;
|
||||
};
|
||||
@@ -1,273 +0,0 @@
|
||||
// This file is a part of Mesen
|
||||
// It is a heavily modified version of the hardware.h/cpp file found in DOSBox's code.
|
||||
|
||||
/*
|
||||
* Copyright (C) 2002-2011 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include "AviWriter.h"
|
||||
#include "BaseCodec.h"
|
||||
#include "RawCodec.h"
|
||||
#include "ZmbvCodec.h"
|
||||
#include "CamstudioCodec.h"
|
||||
|
||||
void AviWriter::WriteAviChunk(const char *tag, uint32_t size, void *data, uint32_t flags)
|
||||
{
|
||||
uint8_t chunk[8] = { (uint8_t)tag[0], (uint8_t)tag[1], (uint8_t)tag[2], (uint8_t)tag[3] };
|
||||
host_writed(&chunk[4], size);
|
||||
_file.write((char*)chunk, 8);
|
||||
|
||||
uint32_t writesize = (size + 1)&~1;
|
||||
_file.write((char*)data, writesize);
|
||||
|
||||
uint32_t pos = _written + 4;
|
||||
_written += writesize + 8;
|
||||
|
||||
_aviIndex.push_back(tag[0]);
|
||||
_aviIndex.push_back(tag[1]);
|
||||
_aviIndex.push_back(tag[2]);
|
||||
_aviIndex.push_back(tag[3]);
|
||||
_aviIndex.insert(_aviIndex.end(), 12, 0);
|
||||
host_writed(_aviIndex.data() + _aviIndex.size() - 12, flags);
|
||||
host_writed(_aviIndex.data() + _aviIndex.size() - 8, pos);
|
||||
host_writed(_aviIndex.data() + _aviIndex.size() - 4, size);
|
||||
}
|
||||
|
||||
void AviWriter::host_writew(uint8_t* buffer, uint16_t value)
|
||||
{
|
||||
buffer[0] = value & 0xFF;
|
||||
buffer[1] = value >> 8;
|
||||
}
|
||||
|
||||
void AviWriter::host_writed(uint8_t* buffer, uint32_t value)
|
||||
{
|
||||
buffer[0] = value;
|
||||
buffer[1] = value >> 8;
|
||||
buffer[2] = value >> 16;
|
||||
buffer[3] = value >> 24;
|
||||
}
|
||||
|
||||
bool AviWriter::StartWrite(string filename, VideoCodec codec, uint32_t width, uint32_t height, uint32_t bpp, uint32_t fps, uint32_t audioSampleRate, uint32_t compressionLevel)
|
||||
{
|
||||
_codecType = codec;
|
||||
_file.open(filename, std::ios::out | std::ios::binary);
|
||||
if(!_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(_codecType) {
|
||||
default:
|
||||
case VideoCodec::None: _codec.reset(new RawCodec()); break;
|
||||
case VideoCodec::ZMBV: _codec.reset(new ZmbvCodec()); break;
|
||||
case VideoCodec::CSCD: _codec.reset(new CamstudioCodec()); break;
|
||||
}
|
||||
|
||||
if(!_codec->SetupCompress(width, height, compressionLevel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_frameBuffer = new uint8_t[width*height*bpp];
|
||||
|
||||
_aviIndex.clear();
|
||||
_aviIndex.insert(_aviIndex.end(), 8, 0);
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
_bpp = bpp;
|
||||
_fps = fps;
|
||||
|
||||
_audiorate = audioSampleRate;
|
||||
|
||||
for(int i = 0; i < AviWriter::AviHeaderSize; i++) {
|
||||
_file.put(0);
|
||||
}
|
||||
_frames = 0;
|
||||
_written = 0;
|
||||
_audioPos = 0;
|
||||
_audiowritten = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AviWriter::EndWrite()
|
||||
{
|
||||
/* Close the video */
|
||||
uint8_t avi_header[AviWriter::AviHeaderSize];
|
||||
uint32_t main_list;
|
||||
uint32_t header_pos = 0;
|
||||
#define AVIOUT4(_S_) memcpy(&avi_header[header_pos],_S_,4);header_pos+=4;
|
||||
#define AVIOUTw(_S_) host_writew(&avi_header[header_pos], _S_);header_pos+=2;
|
||||
#define AVIOUTd(_S_) host_writed(&avi_header[header_pos], _S_);header_pos+=4;
|
||||
/* Try and write an avi header */
|
||||
AVIOUT4("RIFF"); // Riff header
|
||||
AVIOUTd(AviWriter::AviHeaderSize + _written - 8 + (uint32_t)_aviIndex.size());
|
||||
AVIOUT4("AVI ");
|
||||
AVIOUT4("LIST"); // List header
|
||||
main_list = header_pos;
|
||||
AVIOUTd(0); // TODO size of list
|
||||
AVIOUT4("hdrl");
|
||||
|
||||
AVIOUT4("avih");
|
||||
AVIOUTd(56); /* # of bytes to follow */
|
||||
AVIOUTd((uint32_t)(1000000 / _fps)); /* Microseconds per frame */
|
||||
AVIOUTd(0);
|
||||
AVIOUTd(0); /* PaddingGranularity (whatever that might be) */
|
||||
AVIOUTd(0x110); /* Flags,0x10 has index, 0x100 interleaved */
|
||||
AVIOUTd(_frames); /* TotalFrames */
|
||||
AVIOUTd(0); /* InitialFrames */
|
||||
AVIOUTd(2); /* Stream count */
|
||||
AVIOUTd(0); /* SuggestedBufferSize */
|
||||
AVIOUTd(_width); /* Width */
|
||||
AVIOUTd(_height); /* Height */
|
||||
AVIOUTd(0); /* TimeScale: Unit used to measure time */
|
||||
AVIOUTd(0); /* DataRate: Data rate of playback */
|
||||
AVIOUTd(0); /* StartTime: Starting time of AVI data */
|
||||
AVIOUTd(0); /* DataLength: Size of AVI data chunk */
|
||||
|
||||
/* Video stream list */
|
||||
AVIOUT4("LIST");
|
||||
AVIOUTd(4 + 8 + 56 + 8 + 40); /* Size of the list */
|
||||
AVIOUT4("strl");
|
||||
/* video stream header */
|
||||
AVIOUT4("strh");
|
||||
AVIOUTd(56); /* # of bytes to follow */
|
||||
AVIOUT4("vids"); /* Type */
|
||||
AVIOUT4(_codec->GetFourCC()); /* Handler */
|
||||
AVIOUTd(0); /* Flags */
|
||||
AVIOUTd(0); /* Reserved, MS says: wPriority, wLanguage */
|
||||
AVIOUTd(0); /* InitialFrames */
|
||||
AVIOUTd(1000000); /* Scale */
|
||||
AVIOUTd(_fps); /* Rate: Rate/Scale == samples/second */
|
||||
AVIOUTd(0); /* Start */
|
||||
AVIOUTd(_frames); /* Length */
|
||||
AVIOUTd(0); /* SuggestedBufferSize */
|
||||
AVIOUTd(~0); /* Quality */
|
||||
AVIOUTd(0); /* SampleSize */
|
||||
AVIOUTd(0); /* Frame */
|
||||
AVIOUTd(0); /* Frame */
|
||||
/* The video stream format */
|
||||
AVIOUT4("strf");
|
||||
AVIOUTd(40); /* # of bytes to follow */
|
||||
AVIOUTd(40); /* Size */
|
||||
AVIOUTd(_width); /* Width */
|
||||
AVIOUTd(_height); /* Height */
|
||||
// OUTSHRT(1); OUTSHRT(24); /* Planes, Count */
|
||||
AVIOUTw(1); //number of planes
|
||||
AVIOUTw(24); //bits for colors
|
||||
AVIOUT4(_codec->GetFourCC()); /* Compression */
|
||||
AVIOUTd(_width * _height * 4); /* SizeImage (in bytes?) */
|
||||
AVIOUTd(0); /* XPelsPerMeter */
|
||||
AVIOUTd(0); /* YPelsPerMeter */
|
||||
AVIOUTd(0); /* ClrUsed: Number of colors used */
|
||||
AVIOUTd(0); /* ClrImportant: Number of colors important */
|
||||
|
||||
/* Audio stream list */
|
||||
AVIOUT4("LIST");
|
||||
AVIOUTd(4 + 8 + 56 + 8 + 16); /* Length of list in bytes */
|
||||
AVIOUT4("strl");
|
||||
/* The audio stream header */
|
||||
AVIOUT4("strh");
|
||||
AVIOUTd(56); /* # of bytes to follow */
|
||||
AVIOUT4("auds");
|
||||
AVIOUTd(0); /* Format (Optionally) */
|
||||
AVIOUTd(0); /* Flags */
|
||||
AVIOUTd(0); /* Reserved, MS says: wPriority, wLanguage */
|
||||
AVIOUTd(0); /* InitialFrames */
|
||||
AVIOUTd(4); /* Scale */
|
||||
AVIOUTd(_audiorate * 4); /* Rate, actual rate is scale/rate */
|
||||
AVIOUTd(0); /* Start */
|
||||
if(!_audiorate)
|
||||
_audiorate = 1;
|
||||
AVIOUTd(_audiowritten / 4); /* Length */
|
||||
AVIOUTd(0); /* SuggestedBufferSize */
|
||||
AVIOUTd(~0); /* Quality */
|
||||
AVIOUTd(4); /* SampleSize */
|
||||
AVIOUTd(0); /* Frame */
|
||||
AVIOUTd(0); /* Frame */
|
||||
/* The audio stream format */
|
||||
AVIOUT4("strf");
|
||||
AVIOUTd(16); /* # of bytes to follow */
|
||||
AVIOUTw(1); /* Format, WAVE_ZMBV_FORMAT_PCM */
|
||||
AVIOUTw(2); /* Number of channels */
|
||||
AVIOUTd(_audiorate); /* SamplesPerSec */
|
||||
AVIOUTd(_audiorate * 4); /* AvgBytesPerSec*/
|
||||
AVIOUTw(4); /* BlockAlign */
|
||||
AVIOUTw(16); /* BitsPerSample */
|
||||
int nmain = header_pos - main_list - 4;
|
||||
/* Finish stream list, i.e. put number of bytes in the list to proper pos */
|
||||
|
||||
int njunk = AviWriter::AviHeaderSize - 8 - 12 - header_pos;
|
||||
AVIOUT4("JUNK");
|
||||
AVIOUTd(njunk);
|
||||
/* Fix the size of the main list */
|
||||
header_pos = main_list;
|
||||
AVIOUTd(nmain);
|
||||
header_pos = AviWriter::AviHeaderSize - 12;
|
||||
AVIOUT4("LIST");
|
||||
AVIOUTd(_written + 4); /* Length of list in bytes */
|
||||
AVIOUT4("movi");
|
||||
/* First add the index table to the end */
|
||||
memcpy(_aviIndex.data(), "idx1", 4);
|
||||
host_writed(_aviIndex.data() + 4, (uint32_t)_aviIndex.size() - 8);
|
||||
|
||||
_file.write((char*)_aviIndex.data(), _aviIndex.size());
|
||||
_file.seekp(std::ios::beg);
|
||||
_file.write((char*)avi_header, AviWriter::AviHeaderSize);
|
||||
_file.close();
|
||||
}
|
||||
|
||||
void AviWriter::AddFrame(uint8_t *frameData)
|
||||
{
|
||||
if(!_file) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool isKeyFrame = (_frames % 120 == 0) ? 1 : 0;
|
||||
|
||||
uint8_t* compressedData = nullptr;
|
||||
int written = _codec->CompressFrame(isKeyFrame, frameData, &compressedData);
|
||||
if(written < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_codecType == VideoCodec::None) {
|
||||
isKeyFrame = true;
|
||||
}
|
||||
WriteAviChunk(_codecType == VideoCodec::None ? "00db" : "00dc", written, compressedData, isKeyFrame ? 0x10 : 0);
|
||||
_frames++;
|
||||
|
||||
if(_audioPos) {
|
||||
auto lock = _audioLock.AcquireSafe();
|
||||
WriteAviChunk("01wb", _audioPos, _audiobuf, 0);
|
||||
_audiowritten += _audioPos;
|
||||
_audioPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AviWriter::AddSound(int16_t *data, uint32_t sampleCount)
|
||||
{
|
||||
if(!_file) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = _audioLock.AcquireSafe();
|
||||
memcpy(_audiobuf+_audioPos/2, data, sampleCount * 4);
|
||||
_audioPos += sampleCount * 4;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// This file is a part of Mesen
|
||||
// It is a heavily modified version of the hardware.h/cpp file found in DOSBox's code.
|
||||
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "SimpleLock.h"
|
||||
#include "BaseCodec.h"
|
||||
|
||||
enum class VideoCodec
|
||||
{
|
||||
None = 0,
|
||||
ZMBV = 1,
|
||||
CSCD = 2,
|
||||
GIF = 3
|
||||
};
|
||||
|
||||
class AviWriter
|
||||
{
|
||||
private:
|
||||
static constexpr int WaveBufferSize = 16 * 1024;
|
||||
static constexpr int AviHeaderSize = 500;
|
||||
|
||||
std::unique_ptr<BaseCodec> _codec;
|
||||
ofstream _file;
|
||||
|
||||
VideoCodec _codecType;
|
||||
|
||||
int16_t _audiobuf[WaveBufferSize];
|
||||
uint32_t _audioPos = 0;
|
||||
uint32_t _audiorate = 0;
|
||||
uint32_t _audiowritten = 0;
|
||||
|
||||
uint32_t _frames = 0;
|
||||
uint32_t _width = 0;
|
||||
uint32_t _height = 0;
|
||||
uint32_t _bpp = 0;
|
||||
uint32_t _written = 0;
|
||||
uint32_t _fps = 0;
|
||||
|
||||
uint8_t* _frameBuffer = nullptr;
|
||||
|
||||
vector<uint8_t> _aviIndex;
|
||||
|
||||
SimpleLock _audioLock;
|
||||
|
||||
private:
|
||||
void host_writew(uint8_t* buffer, uint16_t value);
|
||||
void host_writed(uint8_t* buffer, uint32_t value);
|
||||
void WriteAviChunk(const char * tag, uint32_t size, void * data, uint32_t flags);
|
||||
|
||||
public:
|
||||
void AddFrame(uint8_t* frameData);
|
||||
void AddSound(int16_t * data, uint32_t sampleCount);
|
||||
|
||||
bool StartWrite(string filename, VideoCodec codec, uint32_t width, uint32_t height, uint32_t bpp, uint32_t fps, uint32_t audioSampleRate, uint32_t compressionLevel);
|
||||
void EndWrite();
|
||||
};
|
||||
@@ -1,398 +0,0 @@
|
||||
// This file is a part of Mesen
|
||||
// It is a heavily modified version of the zmbv.h/cpp file found in DOSBox's code.
|
||||
|
||||
/*
|
||||
* Copyright (C) 2002-2011 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "miniz.h"
|
||||
#include "ZmbvCodec.h"
|
||||
|
||||
#define DBZV_VERSION_HIGH 0
|
||||
#define DBZV_VERSION_LOW 1
|
||||
|
||||
#define COMPRESSION_NONE 0
|
||||
#define COMPRESSION_ZLIB 1
|
||||
|
||||
#define MAX_VECTOR 16
|
||||
|
||||
#define Mask_KeyFrame 0x01
|
||||
#define Mask_DeltaPalette 0x02
|
||||
|
||||
int ZmbvCodec::NeededSize( int _width, int _height, zmbv_format_t _format) {
|
||||
int f;
|
||||
switch (_format) {
|
||||
case ZMBV_FORMAT_8BPP:f = 1;break;
|
||||
case ZMBV_FORMAT_15BPP:f = 2;break;
|
||||
case ZMBV_FORMAT_16BPP:f = 2;break;
|
||||
case ZMBV_FORMAT_32BPP:f = 4;break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
f = f*_width*_height + 2*(1+(_width/8)) * (1+(_height/8))+1024;
|
||||
return f + f/1000;
|
||||
}
|
||||
|
||||
bool ZmbvCodec::SetupBuffers(zmbv_format_t _format, int blockwidth, int blockheight) {
|
||||
FreeBuffers();
|
||||
palsize = 0;
|
||||
switch (_format) {
|
||||
case ZMBV_FORMAT_8BPP:
|
||||
pixelsize = 1;
|
||||
palsize = 256;
|
||||
break;
|
||||
case ZMBV_FORMAT_15BPP:
|
||||
pixelsize = 2;
|
||||
break;
|
||||
case ZMBV_FORMAT_16BPP:
|
||||
pixelsize = 2;
|
||||
break;
|
||||
case ZMBV_FORMAT_32BPP:
|
||||
pixelsize = 4;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
bufsize = (height+2*MAX_VECTOR)*pitch*pixelsize+2048;
|
||||
|
||||
buf1 = new unsigned char[bufsize];
|
||||
buf2 = new unsigned char[bufsize];
|
||||
work = new unsigned char[bufsize];
|
||||
|
||||
int xblocks = (width/blockwidth);
|
||||
int xleft = width % blockwidth;
|
||||
if (xleft) xblocks++;
|
||||
int yblocks = (height/blockheight);
|
||||
int yleft = height % blockheight;
|
||||
if (yleft) yblocks++;
|
||||
blockcount=yblocks*xblocks;
|
||||
blocks=new FrameBlock[blockcount];
|
||||
|
||||
if (!buf1 || !buf2 || !work || !blocks) {
|
||||
FreeBuffers();
|
||||
return false;
|
||||
}
|
||||
int y,x,i;
|
||||
i=0;
|
||||
for (y=0;y<yblocks;y++) {
|
||||
for (x=0;x<xblocks;x++) {
|
||||
blocks[i].start=((y*blockheight)+MAX_VECTOR)*pitch+
|
||||
(x*blockwidth)+MAX_VECTOR;
|
||||
if (xleft && x==(xblocks-1)) {
|
||||
blocks[i].dx=xleft;
|
||||
} else {
|
||||
blocks[i].dx=blockwidth;
|
||||
}
|
||||
if (yleft && y==(yblocks-1)) {
|
||||
blocks[i].dy=yleft;
|
||||
} else {
|
||||
blocks[i].dy=blockheight;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
memset(buf1,0,bufsize);
|
||||
memset(buf2,0,bufsize);
|
||||
memset(work,0,bufsize);
|
||||
oldframe=buf1;
|
||||
newframe=buf2;
|
||||
format = _format;
|
||||
|
||||
_bufSize = NeededSize(width, height, format);
|
||||
_buf = new uint8_t[_bufSize];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZmbvCodec::CreateVectorTable(void) {
|
||||
int x,y,s;
|
||||
VectorCount=1;
|
||||
|
||||
VectorTable[0].x=VectorTable[0].y=0;
|
||||
for (s=1;s<=10;s++) {
|
||||
for (y=0-s;y<=0+s;y++) for (x=0-s;x<=0+s;x++) {
|
||||
if (abs(x)==s || abs(y)==s) {
|
||||
VectorTable[VectorCount].x=x;
|
||||
VectorTable[VectorCount].y=y;
|
||||
VectorCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class P>
|
||||
INLINE int ZmbvCodec::PossibleBlock(int vx,int vy,FrameBlock * block) {
|
||||
int ret=0;
|
||||
P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
|
||||
P * pnew=((P*)newframe)+block->start;;
|
||||
for (int y=0;y<block->dy;y+=4) {
|
||||
for (int x=0;x<block->dx;x+=4) {
|
||||
int test=0-((pold[x]-pnew[x])&0x00ffffff);
|
||||
ret-=(test>>31);
|
||||
}
|
||||
pold+=pitch*4;
|
||||
pnew+=pitch*4;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class P>
|
||||
INLINE int ZmbvCodec::CompareBlock(int vx,int vy,FrameBlock * block) {
|
||||
int ret=0;
|
||||
P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
|
||||
P * pnew=((P*)newframe)+block->start;;
|
||||
for (int y=0;y<block->dy;y++) {
|
||||
for (int x=0;x<block->dx;x++) {
|
||||
int test=0-((pold[x]-pnew[x])&0x00ffffff);
|
||||
ret-=(test>>31);
|
||||
}
|
||||
pold+=pitch;
|
||||
pnew+=pitch;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class P>
|
||||
INLINE void ZmbvCodec::AddXorBlock(int vx,int vy,FrameBlock * block) {
|
||||
P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
|
||||
P * pnew=((P*)newframe)+block->start;
|
||||
for (int y=0;y<block->dy;y++) {
|
||||
for (int x=0;x<block->dx;x++) {
|
||||
*((P*)&work[workUsed])=pnew[x] ^ pold[x];
|
||||
workUsed+=sizeof(P);
|
||||
}
|
||||
pold+=pitch;
|
||||
pnew+=pitch;
|
||||
}
|
||||
}
|
||||
|
||||
template<class P>
|
||||
void ZmbvCodec::AddXorFrame(void) {
|
||||
signed char * vectors=(signed char*)&work[workUsed];
|
||||
/* Align the following xor data on 4 byte boundary*/
|
||||
workUsed=(workUsed + blockcount*2 +3) & ~3;
|
||||
for (int b=0;b<blockcount;b++) {
|
||||
FrameBlock * block=&blocks[b];
|
||||
int bestvx = 0;
|
||||
int bestvy = 0;
|
||||
int bestchange=CompareBlock<P>(0,0, block);
|
||||
int possibles=64;
|
||||
for (int v=0;v<VectorCount && possibles;v++) {
|
||||
if (bestchange<4) break;
|
||||
int vx = VectorTable[v].x;
|
||||
int vy = VectorTable[v].y;
|
||||
if (PossibleBlock<P>(vx, vy, block) < 4) {
|
||||
possibles--;
|
||||
int testchange=CompareBlock<P>(vx,vy, block);
|
||||
if (testchange<bestchange) {
|
||||
bestchange=testchange;
|
||||
bestvx = vx;
|
||||
bestvy = vy;
|
||||
}
|
||||
}
|
||||
}
|
||||
vectors[b*2+0]=(bestvx << 1);
|
||||
vectors[b*2+1]=(bestvy << 1);
|
||||
if (bestchange) {
|
||||
vectors[b*2+0]|=1;
|
||||
AddXorBlock<P>(bestvx, bestvy, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ZmbvCodec::SetupCompress( int _width, int _height, uint32_t compressionLevel ) {
|
||||
width = _width;
|
||||
height = _height;
|
||||
pitch = _width + 2*MAX_VECTOR;
|
||||
format = ZMBV_FORMAT_NONE;
|
||||
if (deflateInit (&zstream, compressionLevel) != Z_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZmbvCodec::PrepareCompressFrame(int flags, zmbv_format_t _format, char * pal)
|
||||
{
|
||||
int i;
|
||||
unsigned char *firstByte;
|
||||
|
||||
if (_format != format) {
|
||||
if (!SetupBuffers( _format, 16, 16))
|
||||
return false;
|
||||
flags|=1; //Force a keyframe
|
||||
}
|
||||
/* replace oldframe with new frame */
|
||||
unsigned char *copyFrame = newframe;
|
||||
newframe = oldframe;
|
||||
oldframe = copyFrame;
|
||||
|
||||
compressInfo.linesDone = 0;
|
||||
compressInfo.writeSize = _bufSize;
|
||||
compressInfo.writeDone = 1;
|
||||
compressInfo.writeBuf = (unsigned char *)_buf;
|
||||
/* Set a pointer to the first byte which will contain info about this frame */
|
||||
firstByte = compressInfo.writeBuf;
|
||||
*firstByte = 0;
|
||||
//Reset the work buffer
|
||||
workUsed = 0;workPos = 0;
|
||||
if (flags & 1) {
|
||||
/* Make a keyframe */
|
||||
*firstByte |= Mask_KeyFrame;
|
||||
KeyframeHeader * header = (KeyframeHeader *)(compressInfo.writeBuf + compressInfo.writeDone);
|
||||
header->high_version = DBZV_VERSION_HIGH;
|
||||
header->low_version = DBZV_VERSION_LOW;
|
||||
header->compression = COMPRESSION_ZLIB;
|
||||
header->format = format;
|
||||
header->blockwidth = 16;
|
||||
header->blockheight = 16;
|
||||
compressInfo.writeDone += sizeof(KeyframeHeader);
|
||||
/* Copy the new frame directly over */
|
||||
if (palsize) {
|
||||
if (pal)
|
||||
memcpy(&palette, pal, sizeof(palette));
|
||||
else
|
||||
memset(&palette,0, sizeof(palette));
|
||||
/* keyframes get the full palette */
|
||||
for (i=0;i<palsize;i++) {
|
||||
work[workUsed++] = palette[i*4+0];
|
||||
work[workUsed++] = palette[i*4+1];
|
||||
work[workUsed++] = palette[i*4+2];
|
||||
}
|
||||
}
|
||||
/* Restart deflate */
|
||||
deflateReset(&zstream);
|
||||
} else {
|
||||
if (palsize && pal && memcmp(pal, palette, palsize * 4)) {
|
||||
*firstByte |= Mask_DeltaPalette;
|
||||
for(i=0;i<palsize;i++) {
|
||||
work[workUsed++]=palette[i*4+0] ^ pal[i*4+0];
|
||||
work[workUsed++]=palette[i*4+1] ^ pal[i*4+1];
|
||||
work[workUsed++]=palette[i*4+2] ^ pal[i*4+2];
|
||||
}
|
||||
memcpy(&palette,pal, palsize * 4);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZmbvCodec::CompressLines(int lineCount, void *lineData[])
|
||||
{
|
||||
int linePitch = pitch * pixelsize;
|
||||
int lineWidth = width * pixelsize;
|
||||
int i = 0;
|
||||
unsigned char *destStart = newframe + pixelsize*(MAX_VECTOR+(compressInfo.linesDone+MAX_VECTOR)*pitch);
|
||||
while ( i < lineCount && (compressInfo.linesDone < height)) {
|
||||
memcpy(destStart, lineData[i], lineWidth );
|
||||
destStart += linePitch;
|
||||
i++; compressInfo.linesDone++;
|
||||
}
|
||||
}
|
||||
|
||||
int ZmbvCodec::FinishCompressFrame(uint8_t** compressedData)
|
||||
{
|
||||
unsigned char firstByte = *compressInfo.writeBuf;
|
||||
if (firstByte & Mask_KeyFrame) {
|
||||
int i;
|
||||
/* Add the full frame data */
|
||||
unsigned char * readFrame = newframe + pixelsize*(MAX_VECTOR+MAX_VECTOR*pitch);
|
||||
for (i=0;i<height;i++) {
|
||||
memcpy(&work[workUsed], readFrame, width*pixelsize);
|
||||
readFrame += pitch*pixelsize;
|
||||
workUsed += width*pixelsize;
|
||||
}
|
||||
} else {
|
||||
/* Add the delta frame data */
|
||||
switch (format) {
|
||||
case ZMBV_FORMAT_8BPP:
|
||||
AddXorFrame<int8_t>();
|
||||
break;
|
||||
case ZMBV_FORMAT_15BPP:
|
||||
case ZMBV_FORMAT_16BPP:
|
||||
AddXorFrame<int16_t>();
|
||||
break;
|
||||
|
||||
default:
|
||||
case ZMBV_FORMAT_32BPP:
|
||||
AddXorFrame<int32_t>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Create the actual frame with compression */
|
||||
zstream.next_in = (Bytef *)work;
|
||||
zstream.avail_in = workUsed;
|
||||
zstream.total_in = 0;
|
||||
|
||||
zstream.next_out = (Bytef *)(compressInfo.writeBuf + compressInfo.writeDone);
|
||||
zstream.avail_out = compressInfo.writeSize - compressInfo.writeDone;
|
||||
zstream.total_out = 0;
|
||||
|
||||
deflate(&zstream, Z_SYNC_FLUSH);
|
||||
|
||||
*compressedData = _buf;
|
||||
|
||||
return compressInfo.writeDone + zstream.total_out;
|
||||
}
|
||||
|
||||
void ZmbvCodec::FreeBuffers()
|
||||
{
|
||||
delete[] blocks;
|
||||
blocks = nullptr;
|
||||
delete[] buf1;
|
||||
buf1 = nullptr;
|
||||
delete[] buf2;
|
||||
buf2 = nullptr;
|
||||
delete[] work;
|
||||
work = nullptr;
|
||||
delete[] _buf;
|
||||
_buf = nullptr;
|
||||
}
|
||||
|
||||
ZmbvCodec::ZmbvCodec()
|
||||
{
|
||||
CreateVectorTable();
|
||||
blocks = nullptr;
|
||||
buf1 = nullptr;
|
||||
buf2 = nullptr;
|
||||
work = nullptr;
|
||||
memset( &zstream, 0, sizeof(zstream));
|
||||
}
|
||||
|
||||
int ZmbvCodec::CompressFrame(bool isKeyFrame, uint8_t *frameData, uint8_t** compressedData)
|
||||
{
|
||||
if(!PrepareCompressFrame(isKeyFrame ? 1 : 0, ZMBV_FORMAT_32BPP, nullptr)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(int i = 0; i < height; i++) {
|
||||
void * rowPointer = frameData + i*width*4;
|
||||
CompressLines(1, &rowPointer);
|
||||
}
|
||||
|
||||
return FinishCompressFrame(compressedData);
|
||||
}
|
||||
|
||||
const char* ZmbvCodec::GetFourCC()
|
||||
{
|
||||
return "ZMBV";
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// This file is a part of Mesen
|
||||
// It is a heavily modified version of the zmbv.h/cpp file found in DOSBox's code.
|
||||
|
||||
/*
|
||||
* Copyright (C) 2002-2011 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseCodec.h"
|
||||
#include "miniz.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define INLINE __forceinline
|
||||
#else
|
||||
#define INLINE inline
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ZMBV_FORMAT_NONE = 0x00,
|
||||
ZMBV_FORMAT_1BPP = 0x01,
|
||||
ZMBV_FORMAT_2BPP = 0x02,
|
||||
ZMBV_FORMAT_4BPP = 0x03,
|
||||
ZMBV_FORMAT_8BPP = 0x04,
|
||||
ZMBV_FORMAT_15BPP = 0x05,
|
||||
ZMBV_FORMAT_16BPP = 0x06,
|
||||
ZMBV_FORMAT_24BPP = 0x07,
|
||||
ZMBV_FORMAT_32BPP = 0x08
|
||||
} zmbv_format_t;
|
||||
|
||||
class ZmbvCodec : public BaseCodec
|
||||
{
|
||||
private:
|
||||
struct FrameBlock {
|
||||
int start = 0;
|
||||
int dx = 0,dy = 0;
|
||||
};
|
||||
struct CodecVector {
|
||||
int x = 0,y = 0;
|
||||
int slot = 0;
|
||||
};
|
||||
struct KeyframeHeader {
|
||||
unsigned char high_version = 0;
|
||||
unsigned char low_version = 0;
|
||||
unsigned char compression = 0;
|
||||
unsigned char format = 0;
|
||||
unsigned char blockwidth = 0,blockheight = 0;
|
||||
};
|
||||
|
||||
struct {
|
||||
int linesDone = 0;
|
||||
int writeSize = 0;
|
||||
int writeDone = 0;
|
||||
unsigned char *writeBuf = nullptr;
|
||||
} compressInfo;
|
||||
|
||||
CodecVector VectorTable[512] = {};
|
||||
int VectorCount = 0;
|
||||
|
||||
unsigned char *oldframe=nullptr, *newframe=nullptr;
|
||||
unsigned char *buf1=nullptr, *buf2=nullptr, *work=nullptr;
|
||||
int bufsize = 0;
|
||||
|
||||
int blockcount = 0;
|
||||
FrameBlock * blocks = nullptr;
|
||||
|
||||
int workUsed = 0, workPos = 0;
|
||||
|
||||
int palsize = 0;
|
||||
char palette[256*4] = {};
|
||||
int height = 0, width = 0, pitch = 0;
|
||||
zmbv_format_t format = zmbv_format_t::ZMBV_FORMAT_NONE;
|
||||
int pixelsize = 0;
|
||||
|
||||
uint8_t* _buf = nullptr;
|
||||
uint32_t _bufSize = 0;
|
||||
|
||||
z_stream zstream = {};
|
||||
|
||||
// methods
|
||||
void FreeBuffers(void);
|
||||
void CreateVectorTable(void);
|
||||
bool SetupBuffers(zmbv_format_t format, int blockwidth, int blockheight);
|
||||
|
||||
template<class P> void AddXorFrame(void);
|
||||
template<class P> INLINE int PossibleBlock(int vx,int vy,FrameBlock * block);
|
||||
template<class P> INLINE int CompareBlock(int vx,int vy,FrameBlock * block);
|
||||
template<class P> INLINE void AddXorBlock(int vx,int vy,FrameBlock * block);
|
||||
|
||||
int NeededSize(int _width, int _height, zmbv_format_t _format);
|
||||
|
||||
void CompressLines(int lineCount, void *lineData[]);
|
||||
bool PrepareCompressFrame(int flags, zmbv_format_t _format, char * pal);
|
||||
int FinishCompressFrame(uint8_t** compressedData);
|
||||
|
||||
public:
|
||||
ZmbvCodec();
|
||||
bool SetupCompress(int _width, int _height, uint32_t compressionLevel) override;
|
||||
int CompressFrame(bool isKeyFrame, uint8_t *frameData, uint8_t** compressedData) override;
|
||||
const char* GetFourCC() override;
|
||||
};
|
||||
Reference in new issue
Block a user