Video: Added "Video Recorder" to record AVI files
This commit is contained in:
parent
1c5a44c20e
commit
0f5c7fe78b
35 changed files with 1701 additions and 68 deletions
98
Core/AviRecorder.cpp
Normal file
98
Core/AviRecorder.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include "stdafx.h"
|
||||
#include "AviRecorder.h"
|
||||
#include "MessageManager.h"
|
||||
|
||||
AviRecorder::AviRecorder()
|
||||
{
|
||||
_recording = false;
|
||||
_stopFlag = false;
|
||||
_frameBuffer = nullptr;
|
||||
_frameBufferLength = 0;
|
||||
_sampleRate = 0;
|
||||
}
|
||||
|
||||
AviRecorder::~AviRecorder()
|
||||
{
|
||||
if(_recording) {
|
||||
StopRecording();
|
||||
}
|
||||
|
||||
if(_frameBuffer) {
|
||||
delete[] _frameBuffer;
|
||||
_frameBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AviRecorder::StartRecording(string filename, VideoCodec codec, uint32_t width, uint32_t height, uint32_t bpp, uint32_t fps, uint32_t audioSampleRate)
|
||||
{
|
||||
if(!_recording) {
|
||||
_outputFile = filename;
|
||||
_sampleRate = audioSampleRate;
|
||||
_frameBufferLength = height * width * bpp;
|
||||
_frameBuffer = new uint8_t[_frameBufferLength];
|
||||
|
||||
_aviWriter.reset(new AviWriter());
|
||||
if(!_aviWriter->StartWrite(filename, codec, width, height, bpp, fps, audioSampleRate)) {
|
||||
_aviWriter.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
_aviWriterThread = std::thread([=]() {
|
||||
while(!_stopFlag) {
|
||||
_waitFrame.Wait();
|
||||
if(_stopFlag) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto lock = _lock.AcquireSafe();
|
||||
_aviWriter->AddFrame(_frameBuffer);
|
||||
}
|
||||
});
|
||||
|
||||
MessageManager::DisplayMessage("VideoRecorder", "VideoRecorderStarted", _outputFile);
|
||||
_recording = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AviRecorder::StopRecording()
|
||||
{
|
||||
if(_recording) {
|
||||
_recording = false;
|
||||
|
||||
_stopFlag = true;
|
||||
_waitFrame.Signal();
|
||||
_aviWriterThread.join();
|
||||
|
||||
_aviWriter->EndWrite();
|
||||
_aviWriter.reset();
|
||||
|
||||
MessageManager::DisplayMessage("VideoRecorder", "VideoRecorderStopped", _outputFile);
|
||||
}
|
||||
}
|
||||
|
||||
void AviRecorder::AddFrame(void* frameBuffer)
|
||||
{
|
||||
if(_recording) {
|
||||
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;
|
||||
}
|
37
Core/AviRecorder.h
Normal file
37
Core/AviRecorder.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <thread>
|
||||
#include "../Utilities/AutoResetEvent.h"
|
||||
#include "../Utilities/AviWriter.h"
|
||||
#include "../Utilities/SimpleLock.h"
|
||||
|
||||
class AviRecorder
|
||||
{
|
||||
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;
|
||||
|
||||
public:
|
||||
AviRecorder();
|
||||
virtual ~AviRecorder();
|
||||
|
||||
bool StartRecording(string filename, VideoCodec codec, uint32_t width, uint32_t height, uint32_t bpp, uint32_t fps, uint32_t audioSampleRate);
|
||||
void StopRecording();
|
||||
|
||||
void AddFrame(void* frameBuffer);
|
||||
void AddSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
|
||||
void SendAudio();
|
||||
|
||||
bool IsRecording();
|
||||
};
|
|
@ -406,6 +406,7 @@
|
|||
<ClInclude Include="ArkanoidController.h" />
|
||||
<ClInclude Include="AutoRomTest.h" />
|
||||
<ClInclude Include="AutoSaveManager.h" />
|
||||
<ClInclude Include="AviRecorder.h" />
|
||||
<ClInclude Include="Ax5705.h" />
|
||||
<ClInclude Include="Bandai74161_7432.h" />
|
||||
<ClInclude Include="BandaiFcg.h" />
|
||||
|
@ -745,6 +746,7 @@
|
|||
<ClCompile Include="ArkanoidController.cpp" />
|
||||
<ClCompile Include="AutoRomTest.cpp" />
|
||||
<ClCompile Include="AutoSaveManager.cpp" />
|
||||
<ClCompile Include="AviRecorder.cpp" />
|
||||
<ClCompile Include="BaseControlDevice.cpp" />
|
||||
<ClCompile Include="BaseMapper.cpp" />
|
||||
<ClCompile Include="BisqwitNtscFilter.cpp" />
|
||||
|
|
|
@ -1108,6 +1108,9 @@
|
|||
<ClInclude Include="BisqwitNtscFilter.h">
|
||||
<Filter>VideoDecoder</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AviRecorder.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
@ -1299,5 +1302,8 @@
|
|||
<ClCompile Include="BisqwitNtscFilter.cpp">
|
||||
<Filter>VideoDecoder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AviRecorder.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -20,6 +20,7 @@ std::unordered_map<string, string> MessageManager::_enResources = {
|
|||
{ "ScreenshotSaved", u8"Screenshot Saved" },
|
||||
{ "SoundRecorder", u8"Sound Recorder" },
|
||||
{ "Test", u8"Test" },
|
||||
{ "VideoRecorder", u8"Video Recorder" },
|
||||
|
||||
{ "ApplyingIps", u8"Applying patch: %1" },
|
||||
{ "CheatApplied", u8"1 cheat applied." },
|
||||
|
@ -61,6 +62,8 @@ std::unordered_map<string, string> MessageManager::_enResources = {
|
|||
{ "SoundRecorderStopped", u8"Recording saved to: %1" },
|
||||
{ "TestFileSavedTo", u8"Test file saved to: %1" },
|
||||
{ "UnsupportedMapper", u8"Unsupported mapper (%1), cannot load game." },
|
||||
{ "VideoRecorderStarted", u8"Recording to: %1" },
|
||||
{ "VideoRecorderStopped", u8"Recording saved to: %1" },
|
||||
|
||||
{ "GoogleDrive", u8"Google Drive" },
|
||||
{ "SynchronizationStarted", u8"Synchronization started." },
|
||||
|
@ -84,6 +87,7 @@ std::unordered_map<string, string> MessageManager::_frResources = {
|
|||
{ "ScreenshotSaved", u8"Capture d'écran" },
|
||||
{ "SoundRecorder", u8"Enregistreur audio" },
|
||||
{ "Test", u8"Test" },
|
||||
{ "VideoRecorder", u8"Enregistreur vidéo" },
|
||||
|
||||
{ "ApplyingIps", u8"Fichier IPS appliqué : %1" },
|
||||
{ "CheatApplied", u8"%1 code activé." },
|
||||
|
@ -125,6 +129,8 @@ std::unordered_map<string, string> MessageManager::_frResources = {
|
|||
{ "SoundRecorderStopped", u8"Enregistrement audio sauvegardé : %1" },
|
||||
{ "TestFileSavedTo", u8"Test sauvegardé : %1" },
|
||||
{ "UnsupportedMapper", u8"Ce mapper (%1) n'est pas encore supporté - le jeu ne peut pas être démarré." },
|
||||
{ "VideoRecorderStarted", u8"En cours d'enregistrement : %1" },
|
||||
{ "VideoRecorderStopped", u8"Enregistrement audio sauvegardé : %1" },
|
||||
|
||||
{ "GoogleDrive", u8"Google Drive" },
|
||||
{ "SynchronizationStarted", u8"Synchronisation en cours." },
|
||||
|
@ -148,6 +154,7 @@ std::unordered_map<string, string> MessageManager::_jaResources = {
|
|||
{ "ScreenshotSaved", u8"スクリーンショット" },
|
||||
{ "SoundRecorder", u8"サウンドレコーダー" },
|
||||
{ "Test", u8"テスト" },
|
||||
{ "VideoRecorder", u8"動画レコーダー" },
|
||||
|
||||
{ "ApplyingIps", u8"パッチファイルを適用しました: %1" },
|
||||
{ "CheatApplied", u8"チートコード%1個を有効にしました。" },
|
||||
|
@ -189,6 +196,8 @@ std::unordered_map<string, string> MessageManager::_jaResources = {
|
|||
{ "SoundRecorderStopped", u8"録音を終了しました: %1" },
|
||||
{ "TestFileSavedTo", u8"Test file saved to: %1" },
|
||||
{ "UnsupportedMapper", u8"このMapper (%1)を使うゲームはロードできません。" },
|
||||
{ "VideoRecorderStarted", u8"%1に録画しています。" },
|
||||
{ "VideoRecorderStopped", u8"録画を終了しました: %1" },
|
||||
|
||||
{ "GoogleDrive", u8"Google Drive" },
|
||||
{ "SynchronizationStarted", u8"同期中。" },
|
||||
|
@ -212,6 +221,7 @@ std::unordered_map<string, string> MessageManager::_ruResources = {
|
|||
{ "ScreenshotSaved", u8"Скриншот сохранён" },
|
||||
{ "SoundRecorder", u8"Запись звука" },
|
||||
{ "Test", u8"Тест" },
|
||||
{ "VideoRecorder", u8"Video Recorder" },
|
||||
|
||||
{ "ApplyingIps", u8"Применён патч: %1" },
|
||||
{ "CheatApplied", u8"1 Чит применён." },
|
||||
|
@ -253,6 +263,8 @@ std::unordered_map<string, string> MessageManager::_ruResources = {
|
|||
{ "SoundRecorderStopped", u8"Запись сохранена: %1" },
|
||||
{ "TestFileSavedTo", u8"Тест сохранён: %1" },
|
||||
{ "UnsupportedMapper", u8"Неподдерживаемый mapper (%1), игра не загружена." },
|
||||
{ "VideoRecorderStarted", u8"Запись начата to: %1" },
|
||||
{ "VideoRecorderStopped", u8"Запись сохранена: %1" },
|
||||
|
||||
{ "GoogleDrive", u8"Google Drive" },
|
||||
{ "SynchronizationStarted", u8"Синхронизация начата." },
|
||||
|
@ -276,6 +288,7 @@ std::unordered_map<string, string> MessageManager::_esResources = {
|
|||
{ "ScreenshotSaved", u8"Captura Guardada" },
|
||||
{ "SoundRecorder", u8"Grabadora de Sonido" },
|
||||
{ "Test", u8"Test" },
|
||||
{ "VideoRecorder", u8"Video Recorder" },
|
||||
|
||||
{ "ApplyingIps", u8"Aplicando parche: %1" },
|
||||
{ "CheatApplied", u8"1 truco aplicado." },
|
||||
|
@ -317,6 +330,9 @@ std::unordered_map<string, string> MessageManager::_esResources = {
|
|||
{ "SoundRecorderStopped", u8"Grabación guardada en: %1" },
|
||||
{ "TestFileSavedTo", u8"Archivo test guardado en: %1" },
|
||||
{ "UnsupportedMapper", u8"Mapa (%1) no soportado, no se puede cargar el juego." },
|
||||
{ "VideoRecorderStarted", u8"Grabando en: %1" },
|
||||
{ "VideoRecorderStopped", u8"Grabación guardada en: %1" },
|
||||
|
||||
|
||||
{ "GoogleDrive", u8"Google Drive" },
|
||||
{ "SynchronizationStarted", u8"Sincronización iniciada." },
|
||||
|
@ -340,6 +356,7 @@ std::unordered_map<string, string> MessageManager::_ukResources = {
|
|||
{ "ScreenshotSaved", u8"Скріншот збережений" },
|
||||
{ "SoundRecorder", u8"Запис звуку" },
|
||||
{ "Test", u8"Тест" },
|
||||
{ "VideoRecorder", u8"Video Recorder" },
|
||||
|
||||
{ "ApplyingIps", u8"Застосовано патч: %1" },
|
||||
{ "CheatApplied", u8"1 Чiт застосований." },
|
||||
|
@ -381,6 +398,8 @@ std::unordered_map<string, string> MessageManager::_ukResources = {
|
|||
{ "SoundRecorderStopped", u8"Запис збережена: %1" },
|
||||
{ "TestFileSavedTo", u8"Тест збережений: %1" },
|
||||
{ "UnsupportedMapper", u8"Непідтримуваний mapper (%1), гра не завантажена." },
|
||||
{ "VideoRecorderStarted", u8"Запис розпочато to: %1" },
|
||||
{ "VideoRecorderStopped", u8"Запис збережена: %1" },
|
||||
|
||||
{ "Google Диск", u8"Google Drive" },
|
||||
{ "SynchronizationStarted", u8"Синхронізацію розпочато." },
|
||||
|
@ -404,6 +423,7 @@ std::unordered_map<string, string> MessageManager::_ptResources = {
|
|||
{ "ScreenshotSaved", u8"Screenshot salva" },
|
||||
{ "SoundRecorder", u8"Gravador de Som" },
|
||||
{ "Test", u8"Teste" },
|
||||
{ "VideoRecorder", u8"Video Recorder" },
|
||||
|
||||
{ "ApplyingIps", u8"Aplicando patch: %1" },
|
||||
{ "CheatApplied", u8"1 cheat aplicado." },
|
||||
|
@ -445,6 +465,8 @@ std::unordered_map<string, string> MessageManager::_ptResources = {
|
|||
{ "SoundRecorderStopped", u8"Gravação salva em: %1" },
|
||||
{ "TestFileSavedTo", u8"Arquivo teste salvo em: %1" },
|
||||
{ "UnsupportedMapper", u8"Mapa (%1) não suportado, não se pode carregar o jogo." },
|
||||
{ "VideoRecorderStarted", u8"Gravando em: %1" },
|
||||
{ "VideoRecorderStopped", u8"Gravação salva em: %1" },
|
||||
|
||||
{ "GoogleDrive", u8"Google Drive" },
|
||||
{ "SynchronizationStarted", u8"Sincronização iniciada." },
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "SoundMixer.h"
|
||||
#include "APU.h"
|
||||
#include "CPU.h"
|
||||
#include "VideoDecoder.h"
|
||||
|
||||
IAudioDevice* SoundMixer::AudioDevice = nullptr;
|
||||
unique_ptr<WaveRecorder> SoundMixer::_waveRecorder;
|
||||
|
@ -85,41 +86,44 @@ void SoundMixer::PlayAudioBuffer(uint32_t time)
|
|||
size_t sampleCount = blip_read_samples(_blipBufLeft, _outputBuffer, SoundMixer::MaxSamplesPerFrame, 1);
|
||||
blip_read_samples(_blipBufRight, _outputBuffer + 1, SoundMixer::MaxSamplesPerFrame, 1);
|
||||
|
||||
//Apply low pass filter/volume reduction when in background (based on options)
|
||||
if(!VideoDecoder::GetInstance()->IsRecording() && !_waveRecorder && !EmulationSettings::CheckFlag(EmulationFlags::NsfPlayerEnabled) && EmulationSettings::CheckFlag(EmulationFlags::InBackground)) {
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::MuteSoundInBackground)) {
|
||||
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 0, 0);
|
||||
} else if(EmulationSettings::CheckFlag(EmulationFlags::ReduceSoundInBackground)) {
|
||||
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 6, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
if(EmulationSettings::GetReverbStrength() > 0) {
|
||||
_reverbFilter.ApplyFilter(_outputBuffer, sampleCount, _sampleRate, EmulationSettings::GetReverbStrength(), EmulationSettings::GetReverbDelay());
|
||||
} else {
|
||||
_reverbFilter.ResetFilter();
|
||||
}
|
||||
|
||||
switch(EmulationSettings::GetStereoFilter()) {
|
||||
case StereoFilter::Delay: _stereoDelay.ApplyFilter(_outputBuffer, sampleCount, _sampleRate); break;
|
||||
case StereoFilter::Panning: _stereoPanning.ApplyFilter(_outputBuffer, sampleCount); break;
|
||||
}
|
||||
|
||||
if(EmulationSettings::GetCrossFeedRatio() > 0) {
|
||||
_crossFeedFilter.ApplyFilter(_outputBuffer, sampleCount, EmulationSettings::GetCrossFeedRatio());
|
||||
}
|
||||
|
||||
if(SoundMixer::AudioDevice && !EmulationSettings::IsPaused()) {
|
||||
//Apply low pass filter/volume reduction when in background (based on options)
|
||||
if(!_waveRecorder && !EmulationSettings::CheckFlag(EmulationFlags::NsfPlayerEnabled) && EmulationSettings::CheckFlag(EmulationFlags::InBackground)) {
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::MuteSoundInBackground)) {
|
||||
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 0, 0);
|
||||
} else if(EmulationSettings::CheckFlag(EmulationFlags::ReduceSoundInBackground)) {
|
||||
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 6, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
if(EmulationSettings::GetReverbStrength() > 0) {
|
||||
_reverbFilter.ApplyFilter(_outputBuffer, sampleCount, _sampleRate, EmulationSettings::GetReverbStrength(), EmulationSettings::GetReverbDelay());
|
||||
} else {
|
||||
_reverbFilter.ResetFilter();
|
||||
}
|
||||
|
||||
switch(EmulationSettings::GetStereoFilter()) {
|
||||
case StereoFilter::Delay: _stereoDelay.ApplyFilter(_outputBuffer, sampleCount, _sampleRate); break;
|
||||
case StereoFilter::Panning: _stereoPanning.ApplyFilter(_outputBuffer, sampleCount); break;
|
||||
}
|
||||
|
||||
if(EmulationSettings::GetCrossFeedRatio() > 0) {
|
||||
_crossFeedFilter.ApplyFilter(_outputBuffer, sampleCount, EmulationSettings::GetCrossFeedRatio());
|
||||
}
|
||||
|
||||
SoundMixer::AudioDevice->PlayBuffer(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true);
|
||||
}
|
||||
|
||||
if(_waveRecorder) {
|
||||
auto lock = _waveRecorderLock.AcquireSafe();
|
||||
if(_waveRecorder) {
|
||||
auto lock = _waveRecorderLock.AcquireSafe();
|
||||
if(_waveRecorder) {
|
||||
if(!_waveRecorder->WriteSamples(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true)) {
|
||||
_waveRecorder.reset();
|
||||
}
|
||||
if(!_waveRecorder->WriteSamples(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true)) {
|
||||
_waveRecorder.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VideoDecoder::GetInstance()->AddRecordingSound(_outputBuffer, (uint32_t)sampleCount, _sampleRate);
|
||||
|
||||
if(EmulationSettings::GetSampleRate() != _sampleRate) {
|
||||
//Update sample rate for next frame if setting changed
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "stdafx.h"
|
||||
#include "AviRecorder.h"
|
||||
#include "IRenderingDevice.h"
|
||||
#include "VideoDecoder.h"
|
||||
#include "EmulationSettings.h"
|
||||
|
@ -90,6 +91,10 @@ void VideoDecoder::UpdateVideoFilter()
|
|||
|
||||
case VideoFilterType::HdPack: _videoFilter.reset(new HdVideoFilter()); break;
|
||||
}
|
||||
|
||||
if(_aviRecorder) {
|
||||
StopRecording();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,13 +107,18 @@ void VideoDecoder::DecodeFrame()
|
|||
}
|
||||
_videoFilter->SendFrame(_ppuOutputBuffer);
|
||||
|
||||
shared_ptr<AviRecorder> aviRecorder = _aviRecorder;
|
||||
if(aviRecorder) {
|
||||
aviRecorder->AddFrame(_videoFilter->GetOutputBuffer());
|
||||
}
|
||||
|
||||
FrameInfo frameInfo = _videoFilter->GetFrameInfo();
|
||||
if(_previousScale != EmulationSettings::GetVideoScale() || frameInfo.Height != _previousFrameInfo.Height || frameInfo.Width != _previousFrameInfo.Width) {
|
||||
MessageManager::SendNotification(ConsoleNotificationType::ResolutionChanged);
|
||||
}
|
||||
_previousScale = EmulationSettings::GetVideoScale();
|
||||
_previousFrameInfo = frameInfo;
|
||||
|
||||
|
||||
_frameChanged = false;
|
||||
|
||||
VideoRenderer::GetInstance()->UpdateFrame(_videoFilter->GetOutputBuffer(), frameInfo.Width, frameInfo.Height);
|
||||
|
@ -126,7 +136,7 @@ void VideoDecoder::DecodeThread()
|
|||
//This thread will decode the PPU's output (color ID to RGB, intensify r/g/b and produce a HD version of the frame if needed)
|
||||
while(!_stopFlag.load()) {
|
||||
//DecodeFrame returns the final ARGB frame we want to display in the emulator window
|
||||
if(!_frameChanged) {
|
||||
while(!_frameChanged) {
|
||||
_waitForFrame.Wait();
|
||||
if(_stopFlag.load()) {
|
||||
return;
|
||||
|
@ -151,7 +161,7 @@ void VideoDecoder::UpdateFrame(void *ppuOutputBuffer, HdPpuPixelInfo *hdPixelInf
|
|||
}
|
||||
//At this point, we are sure that the decode thread is no longer busy
|
||||
}
|
||||
|
||||
|
||||
_hdScreenTiles = hdPixelInfo;
|
||||
_ppuOutputBuffer = (uint16_t*)ppuOutputBuffer;
|
||||
_frameChanged = true;
|
||||
|
@ -202,4 +212,34 @@ void VideoDecoder::TakeScreenshot()
|
|||
if(_videoFilter) {
|
||||
_videoFilter->TakeScreenshot();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoDecoder::StartRecording(string filename, VideoCodec codec)
|
||||
{
|
||||
if(_videoFilter) {
|
||||
shared_ptr<AviRecorder> recorder(new AviRecorder());
|
||||
|
||||
FrameInfo frameInfo = _videoFilter->GetFrameInfo();
|
||||
if(recorder->StartRecording(filename, codec, frameInfo.Width, frameInfo.Height, frameInfo.BitsPerPixel, 60098814, EmulationSettings::GetSampleRate())) {
|
||||
_aviRecorder = recorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoDecoder::AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
|
||||
{
|
||||
shared_ptr<AviRecorder> aviRecorder = _aviRecorder;
|
||||
if(aviRecorder) {
|
||||
aviRecorder->AddSound(soundBuffer, sampleCount, sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoDecoder::StopRecording()
|
||||
{
|
||||
_aviRecorder.reset();
|
||||
}
|
||||
|
||||
bool VideoDecoder::IsRecording()
|
||||
{
|
||||
return _aviRecorder != nullptr && _aviRecorder->IsRecording();
|
||||
}
|
|
@ -5,10 +5,12 @@ using std::thread;
|
|||
|
||||
#include "../Utilities/SimpleLock.h"
|
||||
#include "../Utilities/AutoResetEvent.h"
|
||||
#include "../Utilities/AviWriter.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "HdNesPack.h"
|
||||
#include "FrameInfo.h"
|
||||
|
||||
class AviRecorder;
|
||||
class BaseVideoFilter;
|
||||
class IRenderingDevice;
|
||||
|
||||
|
@ -28,6 +30,7 @@ private:
|
|||
HdPpuPixelInfo *_hdScreenTiles = nullptr;
|
||||
|
||||
unique_ptr<thread> _decodeThread;
|
||||
shared_ptr<AviRecorder> _aviRecorder;
|
||||
|
||||
AutoResetEvent _waitForFrame;
|
||||
|
||||
|
@ -66,4 +69,9 @@ public:
|
|||
bool IsRunning();
|
||||
void StartThread();
|
||||
void StopThread();
|
||||
|
||||
void StartRecording(string filename, VideoCodec codec);
|
||||
void AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
|
||||
void StopRecording();
|
||||
bool IsRecording();
|
||||
};
|
|
@ -83,11 +83,23 @@ namespace Mesen.GUI.Config
|
|||
{
|
||||
get
|
||||
{
|
||||
string waveFoler = Path.Combine(ConfigManager.HomeFolder, "Wave");
|
||||
if(!Directory.Exists(waveFoler)) {
|
||||
Directory.CreateDirectory(waveFoler);
|
||||
string waveFolder = Path.Combine(ConfigManager.HomeFolder, "Wave");
|
||||
if(!Directory.Exists(waveFolder)) {
|
||||
Directory.CreateDirectory(waveFolder);
|
||||
}
|
||||
return waveFoler;
|
||||
return waveFolder;
|
||||
}
|
||||
}
|
||||
|
||||
public static string AviFolder
|
||||
{
|
||||
get
|
||||
{
|
||||
string aviFolder = Path.Combine(ConfigManager.HomeFolder, "Avi");
|
||||
if(!Directory.Exists(aviFolder)) {
|
||||
Directory.CreateDirectory(aviFolder);
|
||||
}
|
||||
return aviFolder;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<Message ID="FilterAll">All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterMovie">Movie files (*.mmo)|*.mmo|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterWave">Wave files (*.wav)|*.wav|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterAvi">Avi files (*.avi)|*.avi|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterPalette">Palette Files (*.pal)|*.pal|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterRom">All supported formats (*.nes, *.zip, *.7z, *.nsf, *.nsfe, *.fds, *.unf)|*.NES;*.ZIP;*.7z;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|All (*.*)|*.*</Message>
|
||||
<Message ID="FilterRomIps">All supported formats (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips)|*.NES;*.ZIP;*.7z;*.IPS;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|IPS Patches (*.ips)|*.IPS|All (*.*)|*.*</Message>
|
||||
|
|
|
@ -71,6 +71,9 @@
|
|||
<Control ID="mnuSoundRecorder">Grabar sonido</Control>
|
||||
<Control ID="mnuWaveRecord">Grabar...</Control>
|
||||
<Control ID="mnuWaveStop">Detener</Control>
|
||||
<Control ID="mnuVideoRecorder">Video Recorder</Control>
|
||||
<Control ID="mnuAviRecord">Grabar...</Control>
|
||||
<Control ID="mnuAviStop">Detener</Control>
|
||||
<Control ID="mnuCheats">Trucos</Control>
|
||||
<Control ID="mnuTests">Tests</Control>
|
||||
<Control ID="mnuTestRun">Iniciar...</Control>
|
||||
|
@ -477,11 +480,19 @@
|
|||
<Control ID="btnOK">Ok</Control>
|
||||
<Control ID="lblSearch">Buscar</Control>
|
||||
</Form>
|
||||
<Form ID="frmRecordAvi" Title="Video Recorder">
|
||||
<Control ID="lblAviFile">Save to:</Control>
|
||||
<Control ID="chkUseCompression">Use ZMBV compression</Control>
|
||||
<Control ID="btnBrowse">Browse...</Control>
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Cancelar</Control>
|
||||
</Form>
|
||||
</Forms>
|
||||
<Messages>
|
||||
<Message ID="FilterAll">Todos los tipos de archivo (*.*)|*.*</Message>
|
||||
<Message ID="FilterMovie">Videos (*.mmo)|*.mmo|Todos los archivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterWave">Archivos wave (*.wav)|*.wav|Todos los archivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterAvi">Archivos avi (*.avi)|*.avi|Todos los archivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterPalette">Archivos pal (*.pal)|*.pal|Todos los archivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterRom">Todos los formatos soportados (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf)|*.NES;*.ZIP;*.7z;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES (*.nes, *.unf)|*.NES;*.UNF|Roms de Famicom Disk System (*.fds)|*.FDS|Archivos NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Archivos ZIP (*.zip)|*.ZIP|Archivos 7-Zip (*.7z)|*.7z|Todos los archivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterRomIps">Todos los formatos soportados (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips)|*.NES;*.ZIP;*.7z;*.IPS;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES(*.nes, *.unf)|*.NES;*.UNF|Roms de Famicom Disk System (*.fds)|*.FDS|Archivos NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Archivos ZIP (*.zip)|*.ZIP|Archivos 7-Zip (*.7z)|*.7z|Archivos IPS (*.ips)|*.IPS|Todos los archivos (*.*)|*.*</Message>
|
||||
|
|
|
@ -71,6 +71,9 @@
|
|||
<Control ID="mnuSoundRecorder">Enregistreur audio</Control>
|
||||
<Control ID="mnuWaveRecord">Enregistrer...</Control>
|
||||
<Control ID="mnuWaveStop">Arrêter</Control>
|
||||
<Control ID="mnuVideoRecorder">Enregistreur vidéo</Control>
|
||||
<Control ID="mnuAviRecord">Enregistrer...</Control>
|
||||
<Control ID="mnuAviStop">Arrêter</Control>
|
||||
<Control ID="mnuCheats">Codes</Control>
|
||||
<Control ID="mnuTests">Tests</Control>
|
||||
<Control ID="mnuTestRun">Run...</Control>
|
||||
|
@ -490,11 +493,19 @@
|
|||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Annuler</Control>
|
||||
</Form>
|
||||
<Form ID="frmRecordAvi" Title="Enregistreur vidéo">
|
||||
<Control ID="lblAviFile">Enregistrer sous:</Control>
|
||||
<Control ID="chkUseCompression">Utiliser la compression ZMBV</Control>
|
||||
<Control ID="btnBrowse">Parcourir...</Control>
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Annuler</Control>
|
||||
</Form>
|
||||
</Forms>
|
||||
<Messages>
|
||||
<Message ID="FilterAll">Tous les fichiers (*.*)|*.*</Message>
|
||||
<Message ID="FilterMovie">Films (*.mmo)|*.mmo|Tous les fichiers (*.*)|*.*</Message>
|
||||
<Message ID="FilterWave">Fichiers wave (*.wav)|*.wav|Tous les fichiers (*.*)|*.*</Message>
|
||||
<Message ID="FilterAvi">Fichiers avi (*.avi)|*.avi|Tous les fichiers (*.*)|*.*</Message>
|
||||
<Message ID="FilterPalette">Fichier de palette (*.pal)|*.pal|Tous les fichiers (*.*)|*.*</Message>
|
||||
<Message ID="FilterRom">Tous les formats supportés (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf)|*.NES;*.ZIP;*.7z;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES (*.nes, *.unf)|*.NES;*.UNF|Roms du Famicom Disk System (*.fds)|*.FDS|Fichiers NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Fichiers ZIP (*.zip)|*.ZIP|Fichiers 7-Zip (*.7z)|*.7z|Tous les fichiers (*.*)|*.*</Message>
|
||||
<Message ID="FilterRomIps">Tous les formats supportés (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips)|*.NES;*.ZIP;*.7z;*.IPS;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES (*.nes, *.unf)|*.NES;*.UNF|Roms du Famicom Disk System (*.fds)|*.FDS|Fichiers NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Fichiers ZIP (*.zip)|*.ZIP|Fichiers 7-Zip (*.7z)|*.7z|Fichiers IPS (*.ips)|*.IPS|Tous les fichiers (*.*)|*.*</Message>
|
||||
|
|
|
@ -71,6 +71,9 @@
|
|||
<Control ID="mnuSoundRecorder">サウンドレコーダー</Control>
|
||||
<Control ID="mnuWaveRecord">録音</Control>
|
||||
<Control ID="mnuWaveStop">停止</Control>
|
||||
<Control ID="mnuVideoRecorder">動画レコーダー</Control>
|
||||
<Control ID="mnuAviRecord">録画</Control>
|
||||
<Control ID="mnuAviStop">停止</Control>
|
||||
<Control ID="mnuCheats">チートコード</Control>
|
||||
<Control ID="mnuTests">テスト</Control>
|
||||
<Control ID="mnuTestRun">Run...</Control>
|
||||
|
@ -472,11 +475,19 @@
|
|||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">キャンセル</Control>
|
||||
</Form>
|
||||
<Form ID="frmRecordAvi" Title="動画レコーダー">
|
||||
<Control ID="lblAviFile">保存先:</Control>
|
||||
<Control ID="chkUseCompression">ZMBV圧縮を利用する</Control>
|
||||
<Control ID="btnBrowse">参照...</Control>
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">キャンセル</Control>
|
||||
</Form>
|
||||
</Forms>
|
||||
<Messages>
|
||||
<Message ID="FilterAll">すべてのファイル (*.*)|*.*</Message>
|
||||
<Message ID="FilterMovie">動画 (*.mmo)|*.mmo|すべてのファイル (*.*)|*.*</Message>
|
||||
<Message ID="FilterWave">WAVファイル (*.wav)|*.wav|すべてのファイル (*.*)|*.*</Message>
|
||||
<Message ID="FilterAvi">AVIファイル (*.avi)|*.avi|すべてのファイル (*.*)|*.*</Message>
|
||||
<Message ID="FilterPalette">パレットファイル (*.pal)|*.pal|すべてのファイル (*.*)|*.*</Message>
|
||||
<Message ID="FilterRom">対応するすべてのファイル (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf)|*.NES;*.ZIP;*.FDS;*.7z;*.NSF;*.NSFE;*.UNF|ファミコンゲーム (*.nes, *.unf)|*.NES;*.UNF|ファミコンディスクシステムのゲーム (*.fds)|*.FDS|NSFファイル (*.nsf, *.nsfe)|*.NSF;*.NSFE|ZIPファイル (*.zip)|*.ZIP|7-Zipファイル (*.7z)|*.7z|すべてのファイル (*.*)|*.*</Message>
|
||||
<Message ID="FilterRomIps">対応するすべてのファイル (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips)|*.NES;*.ZIP;*.7z;*.IPS;*.FDS;*.NSF;*.NSFE;*.UNF|ファミコンゲーム (*.nes, *.unf)|*.NES;*.UNF|ファミコンディスクシステムのゲーム (*.fds)|*.FDS|NSFファイル (*.nsf, *.nsfe)|*.NSF;*.NSFE|ZIPファイル (*.zip)|*.ZIP|7-Zipファイル (*.7z)|*.7z|IPSファイル (*.ips)|*.IPS|すべてのファイル (*.*)|*.*</Message>
|
||||
|
|
|
@ -68,6 +68,9 @@
|
|||
<Control ID="mnuRecordFromStart">Do início do jogo</Control>
|
||||
<Control ID="mnuRecordFromNow">Do estado atual</Control>
|
||||
<Control ID="mnuStopMovie">Parar</Control>
|
||||
<Control ID="mnuVideoRecorder">Video Recorder</Control>
|
||||
<Control ID="mnuAviRecord">Gravar...</Control>
|
||||
<Control ID="mnuAviStop">Parar</Control>
|
||||
<Control ID="mnuSoundRecorder">Gravar som</Control>
|
||||
<Control ID="mnuWaveRecord">Gravar...</Control>
|
||||
<Control ID="mnuWaveStop">Parar</Control>
|
||||
|
@ -477,11 +480,19 @@
|
|||
<Control ID="btnOK">Ok</Control>
|
||||
<Control ID="lblSearch">Buscar</Control>
|
||||
</Form>
|
||||
<Form ID="frmRecordAvi" Title="Video Recorder">
|
||||
<Control ID="lblAviFile">Save to:</Control>
|
||||
<Control ID="chkUseCompression">Use ZMBV compression</Control>
|
||||
<Control ID="btnBrowse">Browse...</Control>
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Cancelar</Control>
|
||||
</Form>
|
||||
</Forms>
|
||||
<Messages>
|
||||
<Message ID="FilterAll">Todos os tipos de arquivo (*.*)|*.*</Message>
|
||||
<Message ID="FilterMovie">Vídeos (*.mmo)|*.mmo|Todos os arquivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterWave">Arquivos wave (*.wav)|*.wav|Todos os arquivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterAvi">Arquivos avi (*.avi)|*.avi|Todos os arquivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterPalette">Arquivos pal (*.pal)|*.pal|Todos os arquivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterRom">Todos os formatos suportados (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf)|*.NES;*.ZIP;*.7z;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES (*.nes, *.unf)|*.NES;*.UNF|Roms de Famicom Disk System (*.fds)|*.FDS|Arquivos NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Arquivos ZIP (*.zip)|*.ZIP|Arquivos 7-Zip (*.7z)|*.7z|Todos os arquivos (*.*)|*.*</Message>
|
||||
<Message ID="FilterRomIps">Todos os formatos suportados (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips)|*.NES;*.ZIP;*.7z;*.IPS;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES(*.nes, *.unf)|*.NES;*.UNF|Roms de Famicom Disk System (*.fds)|*.FDS|Arquivos NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Arquivos ZIP (*.zip)|*.ZIP|Arquivos 7-Zip (*.7z)|*.7z|Arquivos IPS (*.ips)|*.IPS|Todos os arquivos (*.*)|*.*</Message>
|
||||
|
|
|
@ -71,6 +71,9 @@
|
|||
<Control ID="mnuSoundRecorder">Запись звука</Control>
|
||||
<Control ID="mnuWaveRecord">Начать</Control>
|
||||
<Control ID="mnuWaveStop">Остановить</Control>
|
||||
<Control ID="mnuVideoRecorder">Video Recorder</Control>
|
||||
<Control ID="mnuAviRecord">Начать</Control>
|
||||
<Control ID="mnuAviStop">Остановить</Control>
|
||||
<Control ID="mnuCheats">Читы</Control>
|
||||
<Control ID="mnuTests">Тесты</Control>
|
||||
<Control ID="mnuTestRun">Запустить...</Control>
|
||||
|
@ -481,11 +484,19 @@
|
|||
<Control ID="btnOK">ОК</Control>
|
||||
<Control ID="btnCancel">Отмена</Control>
|
||||
</Form>
|
||||
<Form ID="frmRecordAvi" Title="Video Recorder">
|
||||
<Control ID="lblAviFile">Save to:</Control>
|
||||
<Control ID="chkUseCompression">Use ZMBV compression</Control>
|
||||
<Control ID="btnBrowse">Browse...</Control>
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Отмена</Control>
|
||||
</Form>
|
||||
</Forms>
|
||||
<Messages>
|
||||
<Message ID="FilterAll">Все файлы (*.*)|*.*</Message>
|
||||
<Message ID="FilterMovie">Записи (*.mmo)|*.mmo|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterWave">Wave файлы (*.wav)|*.wav|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterAvi">Avi файлы (*.avi)|*.avi|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterPalette">Файлы палитры (*.pal)|*.pal|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterRom">Все поддерживаемые форматы (*.nes, *.zip, *.7z, *.nsf, *.nsfe, *.fds, *.unf)|*.NES;*.ZIP;*.7z;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|All (*.*)|*.*</Message>
|
||||
<Message ID="FilterRomIps">Все поддерживаемые форматы (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips)|*.NES;*.ZIP;*.7z;*.IPS;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|IPS Patches (*.ips)|*.IPS|All (*.*)|*.*</Message>
|
||||
|
|
|
@ -71,6 +71,9 @@
|
|||
<Control ID="mnuSoundRecorder">Запис звуку</Control>
|
||||
<Control ID="mnuWaveRecord">Почати</Control>
|
||||
<Control ID="mnuWaveStop">Зупинити</Control>
|
||||
<Control ID="mnuVideoRecorder">Video Recorder</Control>
|
||||
<Control ID="mnuAviRecord">Почати</Control>
|
||||
<Control ID="mnuAviStop">Зупинити</Control>
|
||||
<Control ID="mnuCheats">Чити</Control>
|
||||
<Control ID="mnuTests">Тести</Control>
|
||||
<Control ID="mnuTestRun">Запустити...</Control>
|
||||
|
@ -194,7 +197,7 @@
|
|||
<Control ID="tpgSet2">Контролер #2</Control>
|
||||
<Control ID="tpgSet3">Контролер #3</Control>
|
||||
<Control ID="tpgSet4">Контролер #4</Control>
|
||||
|
||||
|
||||
<Control ID="btnSelectPreset">Пресети</Control>
|
||||
<Control ID="mnuKeyboard">Клавіатура</Control>
|
||||
<Control ID="mnuWasdLayout">WSAD розкладка</Control>
|
||||
|
@ -210,7 +213,7 @@
|
|||
<Control ID="mnuSnes30Controller">SNES30 геймпад</Control>
|
||||
<Control ID="mnuSnes30Layout1">Контролер #1</Control>
|
||||
<Control ID="mnuSnes30Layout2">Контролер #2</Control>
|
||||
|
||||
|
||||
<Control ID="btnClear">Очистити</Control>
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Відміна</Control>
|
||||
|
@ -254,7 +257,7 @@
|
|||
<Control ID="trkYFilterLength">Y Filter (Horizontal Blur)</Control>
|
||||
<Control ID="trkIFilterLength">I Filter (Horizontal Bleed)</Control>
|
||||
<Control ID="trkQFilterLength">Q Filter (Horizontal Bleed)</Control>
|
||||
|
||||
|
||||
<Control ID="grpScanlines">Scanlines</Control>
|
||||
<Control ID="trkScanlines">Scanlines</Control>
|
||||
<Control ID="btnResetPictureSettings">Скидання</Control>
|
||||
|
@ -268,7 +271,7 @@
|
|||
<Control ID="chkDisableSprites">Відключити sprites</Control>
|
||||
<Control ID="chkForceBackgroundFirstColumn">Force background display in first column</Control>
|
||||
<Control ID="chkForceSpritesFirstColumn">Force sprite display in first column</Control>
|
||||
|
||||
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Відміна</Control>
|
||||
</Form>
|
||||
|
@ -474,17 +477,25 @@
|
|||
</Form>
|
||||
<Form ID="frmGetKey" Title="Налаштування клавіш...">
|
||||
<Control ID="lblSetKeyMessage">Натисніть будь-яку клавішу на клавіатурі або контролері.</Control>...">
|
||||
</Form>
|
||||
<Form ID="frmPlayerProfile" Title="Профіль">
|
||||
<Control ID="lblName">Iм'я :</Control>
|
||||
<Control ID="btnOK">ОК</Control>
|
||||
<Control ID="btnCancel">Вiдмiна</Control>
|
||||
</Form>
|
||||
</Form>
|
||||
<Form ID="frmPlayerProfile" Title="Профіль">
|
||||
<Control ID="lblName">Iм'я :</Control>
|
||||
<Control ID="btnOK">ОК</Control>
|
||||
<Control ID="btnCancel">Вiдмiна</Control>
|
||||
</Form>
|
||||
<Form ID="frmRecordAvi" Title="Video Recorder">
|
||||
<Control ID="lblAviFile">Save to:</Control>
|
||||
<Control ID="chkUseCompression">Use ZMBV compression</Control>
|
||||
<Control ID="btnBrowse">Browse...</Control>
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Вiдмiна</Control>
|
||||
</Form>
|
||||
</Forms>
|
||||
<Messages>
|
||||
<Message ID="FilterAll">Всi файли (*.*)|*.*</Message>
|
||||
<Message ID="FilterMovie">Записи (*.mmo)|*.mmo|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterWave">Wave файли (*.wav)|*.wav|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterAvi">Avi файли (*.avi)|*.avi|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterPalette">Файли палiтр (*.pal)|*.pal|All Files (*.*)|*.*</Message>
|
||||
<Message ID="FilterRom">Всі підтримувані формати (*.nes, *.zip, *.7z, *.nsf, *.nsfe, *.fds, *.unf)|*.NES;*.ZIP;*.7z;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|All (*.*)|*.*</Message>
|
||||
<Message ID="FilterRomIps">Всі підтримувані формати (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips)|*.NES;*.ZIP;*.7z;*.IPS;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|IPS Patches (*.ips)|*.IPS|All (*.*)|*.*</Message>
|
||||
|
|
83
GUI.NET/Forms/frmMain.Designer.cs
generated
83
GUI.NET/Forms/frmMain.Designer.cs
generated
|
@ -145,10 +145,14 @@ namespace Mesen.GUI.Forms
|
|||
this.mnuRecordFromStart = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuRecordFromNow = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuStopMovie = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuCheats = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem22 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuVideoRecorder = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuAviRecord = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuAviStop = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuSoundRecorder = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuWaveRecord = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuWaveStop = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuCheats = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.mnuTests = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuTestRun = new System.Windows.Forms.ToolStripMenuItem();
|
||||
|
@ -453,7 +457,7 @@ namespace Mesen.GUI.Forms
|
|||
this.mnuShowFPS});
|
||||
this.mnuEmulationSpeed.Image = global::Mesen.GUI.Properties.Resources.Speed;
|
||||
this.mnuEmulationSpeed.Name = "mnuEmulationSpeed";
|
||||
this.mnuEmulationSpeed.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuEmulationSpeed.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuEmulationSpeed.Text = "Speed";
|
||||
this.mnuEmulationSpeed.DropDownOpening += new System.EventHandler(this.mnuEmulationSpeed_DropDownOpening);
|
||||
//
|
||||
|
@ -555,7 +559,7 @@ namespace Mesen.GUI.Forms
|
|||
this.mnuFullscreen});
|
||||
this.mnuVideoScale.Image = global::Mesen.GUI.Properties.Resources.Fullscreen;
|
||||
this.mnuVideoScale.Name = "mnuVideoScale";
|
||||
this.mnuVideoScale.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuVideoScale.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuVideoScale.Text = "Video Size";
|
||||
//
|
||||
// mnuScale1x
|
||||
|
@ -667,7 +671,7 @@ namespace Mesen.GUI.Forms
|
|||
this.toolStripMenuItem19,
|
||||
this.mnuBilinearInterpolation});
|
||||
this.mnuVideoFilter.Name = "mnuVideoFilter";
|
||||
this.mnuVideoFilter.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuVideoFilter.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuVideoFilter.Text = "Video Filter";
|
||||
//
|
||||
// mnuNoneFilter
|
||||
|
@ -876,7 +880,7 @@ namespace Mesen.GUI.Forms
|
|||
this.mnuRegionDendy});
|
||||
this.mnuRegion.Image = global::Mesen.GUI.Properties.Resources.Globe;
|
||||
this.mnuRegion.Name = "mnuRegion";
|
||||
this.mnuRegion.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuRegion.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuRegion.Text = "Region";
|
||||
//
|
||||
// mnuRegionAuto
|
||||
|
@ -910,13 +914,13 @@ namespace Mesen.GUI.Forms
|
|||
// toolStripMenuItem10
|
||||
//
|
||||
this.toolStripMenuItem10.Name = "toolStripMenuItem10";
|
||||
this.toolStripMenuItem10.Size = new System.Drawing.Size(149, 6);
|
||||
this.toolStripMenuItem10.Size = new System.Drawing.Size(132, 6);
|
||||
//
|
||||
// mnuAudioConfig
|
||||
//
|
||||
this.mnuAudioConfig.Image = global::Mesen.GUI.Properties.Resources.Audio;
|
||||
this.mnuAudioConfig.Name = "mnuAudioConfig";
|
||||
this.mnuAudioConfig.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuAudioConfig.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuAudioConfig.Text = "Audio";
|
||||
this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click);
|
||||
//
|
||||
|
@ -924,7 +928,7 @@ namespace Mesen.GUI.Forms
|
|||
//
|
||||
this.mnuInput.Image = global::Mesen.GUI.Properties.Resources.Controller;
|
||||
this.mnuInput.Name = "mnuInput";
|
||||
this.mnuInput.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuInput.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuInput.Text = "Input";
|
||||
this.mnuInput.Click += new System.EventHandler(this.mnuInput_Click);
|
||||
//
|
||||
|
@ -932,7 +936,7 @@ namespace Mesen.GUI.Forms
|
|||
//
|
||||
this.mnuVideoConfig.Image = global::Mesen.GUI.Properties.Resources.Video;
|
||||
this.mnuVideoConfig.Name = "mnuVideoConfig";
|
||||
this.mnuVideoConfig.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuVideoConfig.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuVideoConfig.Text = "Video";
|
||||
this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click);
|
||||
//
|
||||
|
@ -940,20 +944,20 @@ namespace Mesen.GUI.Forms
|
|||
//
|
||||
this.mnuEmulationConfig.Image = global::Mesen.GUI.Properties.Resources.DipSwitches;
|
||||
this.mnuEmulationConfig.Name = "mnuEmulationConfig";
|
||||
this.mnuEmulationConfig.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuEmulationConfig.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuEmulationConfig.Text = "Emulation";
|
||||
this.mnuEmulationConfig.Click += new System.EventHandler(this.mnuEmulationConfig_Click);
|
||||
//
|
||||
// toolStripMenuItem11
|
||||
//
|
||||
this.toolStripMenuItem11.Name = "toolStripMenuItem11";
|
||||
this.toolStripMenuItem11.Size = new System.Drawing.Size(149, 6);
|
||||
this.toolStripMenuItem11.Size = new System.Drawing.Size(132, 6);
|
||||
//
|
||||
// mnuPreferences
|
||||
//
|
||||
this.mnuPreferences.Image = global::Mesen.GUI.Properties.Resources.Cog;
|
||||
this.mnuPreferences.Name = "mnuPreferences";
|
||||
this.mnuPreferences.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuPreferences.Size = new System.Drawing.Size(135, 22);
|
||||
this.mnuPreferences.Text = "Preferences";
|
||||
this.mnuPreferences.Click += new System.EventHandler(this.mnuPreferences_Click);
|
||||
//
|
||||
|
@ -962,8 +966,10 @@ namespace Mesen.GUI.Forms
|
|||
this.mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.mnuNetPlay,
|
||||
this.mnuMovies,
|
||||
this.mnuSoundRecorder,
|
||||
this.mnuCheats,
|
||||
this.toolStripMenuItem22,
|
||||
this.mnuSoundRecorder,
|
||||
this.mnuVideoRecorder,
|
||||
this.toolStripMenuItem12,
|
||||
this.mnuTests,
|
||||
this.mnuDebugger,
|
||||
|
@ -1127,6 +1133,45 @@ namespace Mesen.GUI.Forms
|
|||
this.mnuStopMovie.Text = "Stop";
|
||||
this.mnuStopMovie.Click += new System.EventHandler(this.mnuStopMovie_Click);
|
||||
//
|
||||
// mnuCheats
|
||||
//
|
||||
this.mnuCheats.Image = global::Mesen.GUI.Properties.Resources.CheatCode;
|
||||
this.mnuCheats.Name = "mnuCheats";
|
||||
this.mnuCheats.Size = new System.Drawing.Size(231, 22);
|
||||
this.mnuCheats.Text = "Cheats";
|
||||
this.mnuCheats.Click += new System.EventHandler(this.mnuCheats_Click);
|
||||
//
|
||||
// toolStripMenuItem22
|
||||
//
|
||||
this.toolStripMenuItem22.Name = "toolStripMenuItem22";
|
||||
this.toolStripMenuItem22.Size = new System.Drawing.Size(228, 6);
|
||||
//
|
||||
// mnuVideoRecorder
|
||||
//
|
||||
this.mnuVideoRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.mnuAviRecord,
|
||||
this.mnuAviStop});
|
||||
this.mnuVideoRecorder.Image = global::Mesen.GUI.Properties.Resources.VideoRecorder;
|
||||
this.mnuVideoRecorder.Name = "mnuVideoRecorder";
|
||||
this.mnuVideoRecorder.Size = new System.Drawing.Size(231, 22);
|
||||
this.mnuVideoRecorder.Text = "Video Recorder";
|
||||
//
|
||||
// mnuAviRecord
|
||||
//
|
||||
this.mnuAviRecord.Image = global::Mesen.GUI.Properties.Resources.Record;
|
||||
this.mnuAviRecord.Name = "mnuAviRecord";
|
||||
this.mnuAviRecord.Size = new System.Drawing.Size(155, 22);
|
||||
this.mnuAviRecord.Text = "Record...";
|
||||
this.mnuAviRecord.Click += new System.EventHandler(this.mnuAviRecord_Click);
|
||||
//
|
||||
// mnuAviStop
|
||||
//
|
||||
this.mnuAviStop.Image = global::Mesen.GUI.Properties.Resources.Stop;
|
||||
this.mnuAviStop.Name = "mnuAviStop";
|
||||
this.mnuAviStop.Size = new System.Drawing.Size(155, 22);
|
||||
this.mnuAviStop.Text = "Stop Recording";
|
||||
this.mnuAviStop.Click += new System.EventHandler(this.mnuAviStop_Click);
|
||||
//
|
||||
// mnuSoundRecorder
|
||||
//
|
||||
this.mnuSoundRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
|
@ -1153,14 +1198,6 @@ namespace Mesen.GUI.Forms
|
|||
this.mnuWaveStop.Text = "Stop Recording";
|
||||
this.mnuWaveStop.Click += new System.EventHandler(this.mnuWaveStop_Click);
|
||||
//
|
||||
// mnuCheats
|
||||
//
|
||||
this.mnuCheats.Image = global::Mesen.GUI.Properties.Resources.CheatCode;
|
||||
this.mnuCheats.Name = "mnuCheats";
|
||||
this.mnuCheats.Size = new System.Drawing.Size(231, 22);
|
||||
this.mnuCheats.Text = "Cheats";
|
||||
this.mnuCheats.Click += new System.EventHandler(this.mnuCheats_Click);
|
||||
//
|
||||
// toolStripMenuItem12
|
||||
//
|
||||
this.toolStripMenuItem12.Name = "toolStripMenuItem12";
|
||||
|
@ -1496,6 +1533,10 @@ namespace Mesen.GUI.Forms
|
|||
private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitHalfFilter;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitFullFilter;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitQuarterFilter;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem22;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuVideoRecorder;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuAviRecord;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuAviStop;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -619,6 +619,10 @@ namespace Mesen.GUI.Forms
|
|||
mnuWaveRecord.Enabled = _emuThread != null && !waveRecording;
|
||||
mnuWaveStop.Enabled = _emuThread != null && waveRecording;
|
||||
|
||||
bool aviRecording = InteropEmu.AviIsRecording();
|
||||
mnuAviRecord.Enabled = _emuThread != null && !aviRecording;
|
||||
mnuAviStop.Enabled = _emuThread != null && aviRecording;
|
||||
|
||||
bool testRecording = InteropEmu.RomTestRecording();
|
||||
mnuTestRun.Enabled = !netPlay && !moviePlaying && !movieRecording;
|
||||
mnuTestStopRecording.Enabled = _emuThread != null && testRecording;
|
||||
|
@ -934,7 +938,6 @@ namespace Mesen.GUI.Forms
|
|||
RecordMovie(false);
|
||||
}
|
||||
|
||||
|
||||
private void mnuWaveRecord_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
|
@ -951,6 +954,20 @@ namespace Mesen.GUI.Forms
|
|||
InteropEmu.WaveStop();
|
||||
}
|
||||
|
||||
private void mnuAviRecord_Click(object sender, EventArgs e)
|
||||
{
|
||||
using(frmRecordAvi frm = new frmRecordAvi()) {
|
||||
if(frm.ShowDialog(mnuVideoRecorder) == DialogResult.OK) {
|
||||
InteropEmu.AviRecord(frm.Filename, frm.UseCompression ? VideoCodec.ZMBV : VideoCodec.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuAviStop_Click(object sender, EventArgs e)
|
||||
{
|
||||
InteropEmu.AviStop();
|
||||
}
|
||||
|
||||
private void mnuTestRun_Click(object sender, EventArgs e)
|
||||
{
|
||||
OpenFileDialog ofd = new OpenFileDialog();
|
||||
|
|
131
GUI.NET/Forms/frmRecordAvi.Designer.cs
generated
Normal file
131
GUI.NET/Forms/frmRecordAvi.Designer.cs
generated
Normal file
|
@ -0,0 +1,131 @@
|
|||
namespace Mesen.GUI.Forms
|
||||
{
|
||||
partial class frmRecordAvi
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.lblAviFile = new System.Windows.Forms.Label();
|
||||
this.txtFilename = new System.Windows.Forms.TextBox();
|
||||
this.btnBrowse = new System.Windows.Forms.Button();
|
||||
this.chkUseCompression = new System.Windows.Forms.CheckBox();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// baseConfigPanel
|
||||
//
|
||||
this.baseConfigPanel.Location = new System.Drawing.Point(0, 66);
|
||||
this.baseConfigPanel.Size = new System.Drawing.Size(375, 29);
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 3;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel1.Controls.Add(this.lblAviFile, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.txtFilename, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.btnBrowse, 2, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.chkUseCompression, 0, 1);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 3;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(375, 95);
|
||||
this.tableLayoutPanel1.TabIndex = 0;
|
||||
//
|
||||
// lblAviFile
|
||||
//
|
||||
this.lblAviFile.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lblAviFile.AutoSize = true;
|
||||
this.lblAviFile.Location = new System.Drawing.Point(3, 8);
|
||||
this.lblAviFile.Name = "lblAviFile";
|
||||
this.lblAviFile.Size = new System.Drawing.Size(47, 13);
|
||||
this.lblAviFile.TabIndex = 0;
|
||||
this.lblAviFile.Text = "Save to:";
|
||||
//
|
||||
// txtFilename
|
||||
//
|
||||
this.txtFilename.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.txtFilename.Location = new System.Drawing.Point(56, 3);
|
||||
this.txtFilename.Name = "txtFilename";
|
||||
this.txtFilename.ReadOnly = true;
|
||||
this.txtFilename.Size = new System.Drawing.Size(235, 20);
|
||||
this.txtFilename.TabIndex = 1;
|
||||
//
|
||||
// btnBrowse
|
||||
//
|
||||
this.btnBrowse.Location = new System.Drawing.Point(297, 3);
|
||||
this.btnBrowse.Name = "btnBrowse";
|
||||
this.btnBrowse.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnBrowse.TabIndex = 2;
|
||||
this.btnBrowse.Text = "Browse...";
|
||||
this.btnBrowse.UseVisualStyleBackColor = true;
|
||||
this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
|
||||
//
|
||||
// chkUseCompression
|
||||
//
|
||||
this.chkUseCompression.AutoSize = true;
|
||||
this.chkUseCompression.Checked = true;
|
||||
this.chkUseCompression.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.chkUseCompression, 3);
|
||||
this.chkUseCompression.Location = new System.Drawing.Point(3, 32);
|
||||
this.chkUseCompression.Name = "chkUseCompression";
|
||||
this.chkUseCompression.Size = new System.Drawing.Size(140, 17);
|
||||
this.chkUseCompression.TabIndex = 3;
|
||||
this.chkUseCompression.Text = "Use ZMBV compression";
|
||||
this.chkUseCompression.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// frmRecordAvi
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(375, 95);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Name = "frmRecordAvi";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Video Recording Options";
|
||||
this.Controls.SetChildIndex(this.tableLayoutPanel1, 0);
|
||||
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label lblAviFile;
|
||||
private System.Windows.Forms.TextBox txtFilename;
|
||||
private System.Windows.Forms.Button btnBrowse;
|
||||
private System.Windows.Forms.CheckBox chkUseCompression;
|
||||
}
|
||||
}
|
48
GUI.NET/Forms/frmRecordAvi.cs
Normal file
48
GUI.NET/Forms/frmRecordAvi.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Mesen.GUI.Config;
|
||||
|
||||
namespace Mesen.GUI.Forms
|
||||
{
|
||||
public partial class frmRecordAvi : BaseConfigForm
|
||||
{
|
||||
public frmRecordAvi()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public string Filename { get; internal set; }
|
||||
public bool UseCompression { get; internal set; }
|
||||
|
||||
protected override bool ValidateInput()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(txtFilename.Text);
|
||||
}
|
||||
|
||||
protected override void OnFormClosed(FormClosedEventArgs e)
|
||||
{
|
||||
base.OnFormClosed(e);
|
||||
|
||||
this.Filename = txtFilename.Text;
|
||||
this.UseCompression = chkUseCompression.Checked;
|
||||
}
|
||||
|
||||
private void btnBrowse_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.SetFilter(ResourceHelper.GetMessage("FilterAvi"));
|
||||
sfd.InitialDirectory = ConfigManager.AviFolder;
|
||||
sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".avi";
|
||||
if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
||||
txtFilename.Text = sfd.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
GUI.NET/Forms/frmRecordAvi.resx
Normal file
123
GUI.NET/Forms/frmRecordAvi.resx
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
|
@ -562,6 +562,12 @@
|
|||
<Compile Include="Forms\frmLogWindow.Designer.cs">
|
||||
<DependentUpon>frmLogWindow.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Forms\frmRecordAvi.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Forms\frmRecordAvi.Designer.cs">
|
||||
<DependentUpon>frmRecordAvi.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Forms\frmSelectRom.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
@ -773,6 +779,9 @@
|
|||
<EmbeddedResource Include="Forms\frmMain.resx">
|
||||
<DependentUpon>frmMain.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\frmRecordAvi.resx">
|
||||
<DependentUpon>frmRecordAvi.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\frmSelectRom.resx">
|
||||
<DependentUpon>frmSelectRom.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
@ -882,6 +891,7 @@
|
|||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Icon.ico" />
|
||||
<None Include="Resources\VideoRecorder.png" />
|
||||
<None Include="Resources\Donate.png" />
|
||||
<None Include="Resources\Dice.png" />
|
||||
<None Include="Resources\Warning.png" />
|
||||
|
|
|
@ -91,6 +91,10 @@ namespace Mesen.GUI
|
|||
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool MoviePlaying();
|
||||
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool MovieRecording();
|
||||
|
||||
[DllImport(DLLPath)] public static extern void AviRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename, VideoCodec codec);
|
||||
[DllImport(DLLPath)] public static extern void AviStop();
|
||||
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool AviIsRecording();
|
||||
|
||||
[DllImport(DLLPath)] public static extern void WaveRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename);
|
||||
[DllImport(DLLPath)] public static extern void WaveStop();
|
||||
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool WaveIsRecording();
|
||||
|
@ -1084,6 +1088,12 @@ namespace Mesen.GUI
|
|||
Sunsoft5B = 10
|
||||
}
|
||||
|
||||
public enum VideoCodec
|
||||
{
|
||||
None = 0,
|
||||
ZMBV = 1,
|
||||
}
|
||||
|
||||
public enum VideoFilterType
|
||||
{
|
||||
None = 0,
|
||||
|
|
10
GUI.NET/Properties/Resources.Designer.cs
generated
10
GUI.NET/Properties/Resources.Designer.cs
generated
|
@ -470,6 +470,16 @@ namespace Mesen.GUI.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap VideoRecorder {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("VideoRecorder", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
|
|
@ -244,4 +244,7 @@
|
|||
<data name="Donate" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Donate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="VideoRecorder" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\VideoRecorder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
BIN
GUI.NET/Resources/VideoRecorder.png
Normal file
BIN
GUI.NET/Resources/VideoRecorder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 233 B |
|
@ -17,6 +17,7 @@
|
|||
#include "../Core/NsfMapper.h"
|
||||
#include "../Core/IRenderingDevice.h"
|
||||
#include "../Core/IAudioDevice.h"
|
||||
#include "../Utilities/AviWriter.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include "../Windows/Renderer.h"
|
||||
|
@ -299,6 +300,10 @@ namespace InteropEmu {
|
|||
DllExport bool __stdcall MoviePlaying() { return Movie::Playing(); }
|
||||
DllExport bool __stdcall MovieRecording() { return Movie::Recording(); }
|
||||
|
||||
DllExport void __stdcall AviRecord(char* filename, VideoCodec codec) { VideoDecoder::GetInstance()->StartRecording(filename, codec); }
|
||||
DllExport void __stdcall AviStop() { VideoDecoder::GetInstance()->StopRecording(); }
|
||||
DllExport bool __stdcall AviIsRecording() { return VideoDecoder::GetInstance()->IsRecording(); }
|
||||
|
||||
DllExport void __stdcall WaveRecord(char* filename) { SoundMixer::StartRecording(filename); }
|
||||
DllExport void __stdcall WaveStop() { SoundMixer::StopRecording(); }
|
||||
DllExport bool __stdcall WaveIsRecording() { return SoundMixer::IsRecording(); }
|
||||
|
|
270
Utilities/AviWriter.cpp
Normal file
270
Utilities/AviWriter.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
// 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 "AviWriter.h"
|
||||
#include "BaseCodec.h"
|
||||
#include "RawCodec.h"
|
||||
#include "ZmbvCodec.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)
|
||||
{
|
||||
_codecType = codec;
|
||||
_file.open(filename, std::ios::out | std::ios::binary);
|
||||
if(!_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_codecType == VideoCodec::ZMBV) {
|
||||
_codec.reset(new ZmbvCodec());
|
||||
} else {
|
||||
_codec.reset(new RawCodec());
|
||||
}
|
||||
|
||||
if(!_codec->SetupCompress(width, height)) {
|
||||
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;
|
||||
}
|
57
Utilities/AviWriter.h
Normal file
57
Utilities/AviWriter.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// 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,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
int _bufSize = 0;
|
||||
void *_buf = 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);
|
||||
void EndWrite();
|
||||
};
|
10
Utilities/BaseCodec.h
Normal file
10
Utilities/BaseCodec.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
class BaseCodec
|
||||
{
|
||||
public:
|
||||
virtual bool SetupCompress(int width, int height) = 0;
|
||||
virtual int CompressFrame(bool isKeyFrame, uint8_t *frameData, uint8_t** compressedData) = 0;
|
||||
virtual const char* GetFourCC() = 0;
|
||||
};
|
50
Utilities/RawCodec.h
Normal file
50
Utilities/RawCodec.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseCodec.h"
|
||||
|
||||
class RawCodec : public BaseCodec
|
||||
{
|
||||
private:
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
uint32_t _bufferSize = 0;
|
||||
uint8_t* _buffer = nullptr;
|
||||
|
||||
public:
|
||||
virtual bool SetupCompress(int width, int height) override
|
||||
{
|
||||
_height = height;
|
||||
_width = width;
|
||||
|
||||
_bufferSize = width * height * 3;
|
||||
_buffer = new uint8_t[(_bufferSize + 1) & ~1];
|
||||
memset(_buffer, 0, (_bufferSize + 1) & ~1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int CompressFrame(bool isKeyFrame, uint8_t *frameData, uint8_t** compressedData) override
|
||||
{
|
||||
*compressedData = _buffer;
|
||||
|
||||
//Convert raw frame to BMP/DIB format (row order is reversed)
|
||||
uint8_t* buffer = _buffer;
|
||||
frameData += (_height - 1) * _width * 4;
|
||||
for(int y = 0; y < _height; y++) {
|
||||
for(int x = 0; x < _width; x++) {
|
||||
buffer[0] = frameData[0];
|
||||
buffer[1] = frameData[1];
|
||||
buffer[2] = frameData[2];
|
||||
frameData += 4;
|
||||
buffer += 3;
|
||||
}
|
||||
frameData -= _width * 2 * 4;
|
||||
}
|
||||
return _bufferSize;
|
||||
}
|
||||
|
||||
virtual const char* GetFourCC() override
|
||||
{
|
||||
return "\0\0\0\0";
|
||||
}
|
||||
};
|
|
@ -322,6 +322,7 @@
|
|||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ArchiveReader.h" />
|
||||
<ClInclude Include="AviWriter.h" />
|
||||
<ClInclude Include="blip_buf.h" />
|
||||
<ClInclude Include="CRC32.h" />
|
||||
<ClInclude Include="FolderUtilities.h" />
|
||||
|
@ -337,8 +338,10 @@
|
|||
<ClInclude Include="nes_ntsc.h" />
|
||||
<ClInclude Include="nes_ntsc_config.h" />
|
||||
<ClInclude Include="nes_ntsc_impl.h" />
|
||||
<ClInclude Include="BaseCodec.h" />
|
||||
<ClInclude Include="PlatformUtilities.h" />
|
||||
<ClInclude Include="PNGHelper.h" />
|
||||
<ClInclude Include="RawCodec.h" />
|
||||
<ClInclude Include="Scale2x\scale2x.h" />
|
||||
<ClInclude Include="Scale2x\scale3x.h" />
|
||||
<ClInclude Include="Scale2x\scalebit.h" />
|
||||
|
@ -354,9 +357,11 @@
|
|||
<ClInclude Include="xBRZ\xbrz.h" />
|
||||
<ClInclude Include="ZipReader.h" />
|
||||
<ClInclude Include="ZipWriter.h" />
|
||||
<ClInclude Include="ZmbvCodec.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ArchiveReader.cpp" />
|
||||
<ClCompile Include="AviWriter.cpp" />
|
||||
<ClCompile Include="blip_buf.cpp" />
|
||||
<ClCompile Include="CRC32.cpp" />
|
||||
<ClCompile Include="FolderUtilities.cpp" />
|
||||
|
@ -496,6 +501,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="ZipReader.cpp" />
|
||||
<ClCompile Include="ZipWriter.cpp" />
|
||||
<ClCompile Include="ZmbvCodec.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
<Filter Include="KreedSaiEagle">
|
||||
<UniqueIdentifier>{8e159744-fb91-4e16-aa82-8d8703ba2762}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Avi">
|
||||
<UniqueIdentifier>{8b0e23bf-1bd9-4cc1-8046-784fd01e8688}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
|
@ -122,6 +125,18 @@
|
|||
<ClInclude Include="HexUtilities.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ZmbvCodec.h">
|
||||
<Filter>Avi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AviWriter.h">
|
||||
<Filter>Avi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BaseCodec.h">
|
||||
<Filter>Avi</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RawCodec.h">
|
||||
<Filter>Avi</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
@ -220,5 +235,11 @@
|
|||
<ClCompile Include="HexUtilities.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZmbvCodec.cpp">
|
||||
<Filter>Avi</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AviWriter.cpp">
|
||||
<Filter>Avi</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
410
Utilities/ZmbvCodec.cpp
Normal file
410
Utilities/ZmbvCodec.cpp
Normal file
|
@ -0,0 +1,410 @@
|
|||
// 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) {
|
||||
int written=0;
|
||||
int lastvector=0;
|
||||
signed char * vectors=(signed char*)&work[workUsed];
|
||||
/* Align the following xor data on 4 byte boundary*/
|
||||
workUsed=(workUsed + blockcount*2 +3) & ~3;
|
||||
int totalx=0;
|
||||
int totaly=0;
|
||||
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--;
|
||||
// if (!possibles) Msg("Ran out of possibles, at %d of %d best %d\n",v,VectorCount,bestchange);
|
||||
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 ) {
|
||||
width = _width;
|
||||
height = _height;
|
||||
pitch = _width + 2*MAX_VECTOR;
|
||||
format = ZMBV_FORMAT_NONE;
|
||||
if (deflateInit (&zstream, 4) != 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<char>();
|
||||
break;
|
||||
case ZMBV_FORMAT_15BPP:
|
||||
case ZMBV_FORMAT_16BPP:
|
||||
AddXorFrame<short>();
|
||||
break;
|
||||
case ZMBV_FORMAT_32BPP:
|
||||
AddXorFrame<long>();
|
||||
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;
|
||||
int res = deflate(&zstream, Z_SYNC_FLUSH);
|
||||
|
||||
*compressedData = _buf;
|
||||
|
||||
return compressInfo.writeDone + zstream.total_out;
|
||||
}
|
||||
|
||||
void ZmbvCodec::FreeBuffers()
|
||||
{
|
||||
if (blocks) {
|
||||
delete[] blocks;
|
||||
blocks= nullptr;
|
||||
}
|
||||
if (buf1) {
|
||||
delete[] buf1;
|
||||
buf1= nullptr;
|
||||
}
|
||||
if (buf2) {
|
||||
delete[] buf2;
|
||||
buf2= nullptr;
|
||||
}
|
||||
if (work) {
|
||||
delete[] work;
|
||||
work= nullptr;
|
||||
}
|
||||
if(_buf) {
|
||||
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";
|
||||
}
|
115
Utilities/ZmbvCodec.h
Normal file
115
Utilities/ZmbvCodec.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
// 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;
|
||||
int dx,dy;
|
||||
};
|
||||
struct CodecVector {
|
||||
int x,y;
|
||||
int slot;
|
||||
};
|
||||
struct KeyframeHeader {
|
||||
unsigned char high_version;
|
||||
unsigned char low_version;
|
||||
unsigned char compression;
|
||||
unsigned char format;
|
||||
unsigned char blockwidth,blockheight;
|
||||
};
|
||||
|
||||
struct {
|
||||
int linesDone;
|
||||
int writeSize;
|
||||
int writeDone;
|
||||
unsigned char *writeBuf;
|
||||
} compressInfo;
|
||||
|
||||
CodecVector VectorTable[512];
|
||||
int VectorCount;
|
||||
|
||||
unsigned char *oldframe, *newframe;
|
||||
unsigned char *buf1, *buf2, *work;
|
||||
int bufsize;
|
||||
|
||||
int blockcount;
|
||||
FrameBlock * blocks;
|
||||
|
||||
int workUsed, workPos;
|
||||
|
||||
int palsize;
|
||||
char palette[256*4];
|
||||
int height, width, pitch;
|
||||
zmbv_format_t format;
|
||||
int pixelsize;
|
||||
|
||||
uint8_t* _buf;
|
||||
uint32_t _bufSize;
|
||||
|
||||
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) override;
|
||||
int CompressFrame(bool isKeyFrame, uint8_t *frameData, uint8_t** compressedData) override;
|
||||
const char* GetFourCC() override;
|
||||
};
|
Loading…
Add table
Reference in a new issue