Windows Port: When recording AVIs, video framebuffer conversions and file write operations are performed asynchronously with the main thread. This should greatly increase AVI recording performance.
This commit is contained in:
parent
c7ca122d95
commit
cbe4717d2f
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2006-2017 DeSmuME team
|
Copyright (C) 2006-2018 DeSmuME team
|
||||||
|
|
||||||
This file is free software: you can redistribute it and/or modify
|
This file is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -48,24 +48,33 @@ static void EMU_PrintMessage(const char* msg) {
|
||||||
//extern WAVEFORMATEX wf;
|
//extern WAVEFORMATEX wf;
|
||||||
//extern int soundo;
|
//extern int soundo;
|
||||||
|
|
||||||
#define VIDEO_STREAM 0
|
#define VIDEO_STREAM 0
|
||||||
#define AUDIO_STREAM 1
|
#define AUDIO_STREAM 1
|
||||||
#define MAX_CONVERT_THREADS 16
|
#define MAX_CONVERT_THREADS 32
|
||||||
|
|
||||||
|
#define AUDIO_STREAM_BUFFER_SIZE (DESMUME_SAMPLE_RATE * sizeof(u16) * 2) // 16-bit samples, 2 channels, 1 second duration
|
||||||
|
|
||||||
struct AVIFile;
|
struct AVIFile;
|
||||||
|
|
||||||
struct AVIConversionParam
|
struct AVIConversionParam
|
||||||
{
|
{
|
||||||
AVIFile *avi;
|
AVIFile *avi;
|
||||||
|
size_t bufferIndex;
|
||||||
const void *src;
|
const void *src;
|
||||||
size_t firstLineIndex;
|
size_t firstLineIndex;
|
||||||
size_t lastLineIndex;
|
size_t lastLineIndex;
|
||||||
};
|
};
|
||||||
typedef struct AVIConversionParam AVIConversionParam;
|
typedef struct AVIConversionParam AVIConversionParam;
|
||||||
|
|
||||||
|
struct AVIFileWriteParam
|
||||||
|
{
|
||||||
|
AVIFile *avi;
|
||||||
|
size_t bufferIndex;
|
||||||
|
};
|
||||||
|
typedef struct AVIFileWriteParam AVIFileWriteParam;
|
||||||
|
|
||||||
struct AVIFile
|
struct AVIFile
|
||||||
{
|
{
|
||||||
int valid;
|
|
||||||
int fps;
|
int fps;
|
||||||
int fps_scale;
|
int fps_scale;
|
||||||
|
|
||||||
|
@ -88,6 +97,7 @@ struct AVIFile
|
||||||
int sound_samples;
|
int sound_samples;
|
||||||
|
|
||||||
u8 *convert_buffer;
|
u8 *convert_buffer;
|
||||||
|
size_t videoStreamBufferSize;
|
||||||
int prescaleLevel;
|
int prescaleLevel;
|
||||||
size_t frameWidth;
|
size_t frameWidth;
|
||||||
size_t frameHeight;
|
size_t frameHeight;
|
||||||
|
@ -96,12 +106,16 @@ struct AVIFile
|
||||||
|
|
||||||
long tBytes, ByteBuffer;
|
long tBytes, ByteBuffer;
|
||||||
|
|
||||||
u8 audio_buffer[DESMUME_SAMPLE_RATE*2*2]; // 1 second buffer
|
u8 audio_buffer[AUDIO_STREAM_BUFFER_SIZE * 2];
|
||||||
int audio_buffer_pos;
|
int audio_buffer_pos[2];
|
||||||
|
|
||||||
|
size_t currentBufferIndex;
|
||||||
|
|
||||||
size_t numThreads;
|
size_t numThreads;
|
||||||
|
Task *fileWriteThread;
|
||||||
Task *convertThread[MAX_CONVERT_THREADS];
|
Task *convertThread[MAX_CONVERT_THREADS];
|
||||||
AVIConversionParam convertParam[MAX_CONVERT_THREADS];
|
AVIConversionParam convertParam[MAX_CONVERT_THREADS];
|
||||||
|
AVIFileWriteParam fileWriteParam;
|
||||||
};
|
};
|
||||||
typedef AVIFile AVIFile;
|
typedef AVIFile AVIFile;
|
||||||
|
|
||||||
|
@ -141,39 +155,36 @@ static bool truncate_existing(const char* filename)
|
||||||
|
|
||||||
static int avi_audiosegment_size(struct AVIFile* avi_out)
|
static int avi_audiosegment_size(struct AVIFile* avi_out)
|
||||||
{
|
{
|
||||||
if(!avi_out || !avi_out->valid || !avi_out->sound_added)
|
if (!AVI_IsRecording() || !avi_out->sound_added)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
assert(avi_out->wave_format.nAvgBytesPerSec <= sizeof(avi_out->audio_buffer));
|
assert(avi_out->wave_format.nAvgBytesPerSec <= sizeof(avi_out->audio_buffer));
|
||||||
return avi_out->wave_format.nAvgBytesPerSec;
|
return avi_out->wave_format.nAvgBytesPerSec;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void avi_create(struct AVIFile** avi_out)
|
|
||||||
{
|
|
||||||
*avi_out = (struct AVIFile*)malloc(sizeof(struct AVIFile));
|
|
||||||
memset(*avi_out, 0, sizeof(struct AVIFile));
|
|
||||||
AVIFileInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void avi_destroy(struct AVIFile** avi_out)
|
static void avi_destroy(struct AVIFile** avi_out)
|
||||||
{
|
{
|
||||||
if(!(*avi_out))
|
if (!(*avi_out))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if((*avi_out)->sound_added)
|
const size_t bufferIndex = (*avi_out)->currentBufferIndex;
|
||||||
|
HRESULT error = S_OK;
|
||||||
|
|
||||||
|
if ((*avi_out)->sound_added)
|
||||||
{
|
{
|
||||||
if((*avi_out)->compressed_streams[AUDIO_STREAM])
|
if ((*avi_out)->compressed_streams[AUDIO_STREAM])
|
||||||
{
|
{
|
||||||
if ((*avi_out)->audio_buffer_pos > 0) {
|
if ((*avi_out)->audio_buffer_pos[bufferIndex] > 0)
|
||||||
if(FAILED(AVIStreamWrite(avi_file->compressed_streams[AUDIO_STREAM],
|
{
|
||||||
avi_file->sound_samples, (*avi_out)->audio_buffer_pos / (*avi_out)->wave_format.nBlockAlign,
|
const int frameSampleCount = (*avi_out)->audio_buffer_pos[bufferIndex] / (*avi_out)->wave_format.nBlockAlign;
|
||||||
(*avi_out)->audio_buffer, (*avi_out)->audio_buffer_pos, 0, NULL, &avi_file->ByteBuffer)))
|
|
||||||
{
|
error = AVIStreamWrite(avi_file->compressed_streams[AUDIO_STREAM],
|
||||||
avi_file->valid = 0;
|
avi_file->sound_samples, frameSampleCount,
|
||||||
}
|
(*avi_out)->audio_buffer + (AUDIO_STREAM_BUFFER_SIZE * bufferIndex), (*avi_out)->audio_buffer_pos[bufferIndex], 0, NULL, &avi_file->ByteBuffer);
|
||||||
(*avi_out)->sound_samples += (*avi_out)->audio_buffer_pos / (*avi_out)->wave_format.nBlockAlign;
|
|
||||||
|
(*avi_out)->sound_samples += frameSampleCount;
|
||||||
(*avi_out)->tBytes += avi_file->ByteBuffer;
|
(*avi_out)->tBytes += avi_file->ByteBuffer;
|
||||||
(*avi_out)->audio_buffer_pos = 0;
|
(*avi_out)->audio_buffer_pos[bufferIndex] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LONG test = AVIStreamClose((*avi_out)->compressed_streams[AUDIO_STREAM]);
|
LONG test = AVIStreamClose((*avi_out)->compressed_streams[AUDIO_STREAM]);
|
||||||
|
@ -182,7 +193,7 @@ static void avi_destroy(struct AVIFile** avi_out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((*avi_out)->video_added)
|
if ((*avi_out)->video_added)
|
||||||
{
|
{
|
||||||
if((*avi_out)->compressed_streams[VIDEO_STREAM])
|
if((*avi_out)->compressed_streams[VIDEO_STREAM])
|
||||||
{
|
{
|
||||||
|
@ -197,15 +208,11 @@ static void avi_destroy(struct AVIFile** avi_out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((*avi_out)->avi_file)
|
if ((*avi_out)->avi_file)
|
||||||
{
|
{
|
||||||
AVIFileClose((*avi_out)->avi_file);
|
AVIFileClose((*avi_out)->avi_file);
|
||||||
(*avi_out)->avi_file = NULL;
|
(*avi_out)->avi_file = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_aligned((*avi_out)->convert_buffer);
|
|
||||||
free(*avi_out);
|
|
||||||
*avi_out = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_video_format(const BITMAPINFOHEADER* bitmap_format, struct AVIFile* avi_out)
|
static void set_video_format(const BITMAPINFOHEADER* bitmap_format, struct AVIFile* avi_out)
|
||||||
|
@ -220,46 +227,68 @@ static void set_sound_format(const WAVEFORMATEX* wave_format, struct AVIFile* av
|
||||||
(*avi_out).sound_added = 1;
|
(*avi_out).sound_added = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int avi_open(const char* filename, const BITMAPINFOHEADER* pbmih, const WAVEFORMATEX* pwfex)
|
static bool avi_open(const char* filename, const BITMAPINFOHEADER* pbmih, const WAVEFORMATEX* pwfex, bool isNewSegment)
|
||||||
{
|
{
|
||||||
int error = 1;
|
bool isErrorInFileWrite = false;
|
||||||
int result = 0;
|
bool result = false;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// close existing first
|
if (!truncate_existing(filename))
|
||||||
DRV_AviEnd(false);
|
|
||||||
|
|
||||||
if(!truncate_existing(filename))
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(!pbmih)
|
if (!pbmih)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// create the object
|
if (!isNewSegment)
|
||||||
avi_create(&avi_file);
|
{
|
||||||
|
avi_file = (struct AVIFile*)malloc(sizeof(struct AVIFile));
|
||||||
|
memset(avi_file, 0, sizeof(struct AVIFile));
|
||||||
|
|
||||||
|
avi_file->prescaleLevel = video.prescaleHD;
|
||||||
|
avi_file->frameWidth = GPU_FRAMEBUFFER_NATIVE_WIDTH * video.prescaleHD;
|
||||||
|
avi_file->frameHeight = GPU_FRAMEBUFFER_NATIVE_HEIGHT * video.prescaleHD * 2;
|
||||||
|
avi_file->videoStreamBufferSize = avi_file->frameWidth * avi_file->frameHeight * 3;
|
||||||
|
avi_file->convert_buffer = (u8*)malloc_alignedCacheLine(avi_file->videoStreamBufferSize * 2);
|
||||||
|
|
||||||
|
// create the video stream
|
||||||
|
set_video_format(pbmih, avi_file);
|
||||||
|
|
||||||
|
memset(&avi_file->avi_video_header, 0, sizeof(AVISTREAMINFO));
|
||||||
|
avi_file->avi_video_header.fccType = streamtypeVIDEO;
|
||||||
|
avi_file->avi_video_header.dwScale = 6 * 355 * 263;
|
||||||
|
avi_file->avi_video_header.dwRate = 33513982;
|
||||||
|
avi_file->avi_video_header.dwSuggestedBufferSize = avi_file->bitmap_format.biSizeImage;
|
||||||
|
|
||||||
|
// add audio format
|
||||||
|
if (pwfex)
|
||||||
|
{
|
||||||
|
set_sound_format(pwfex, avi_file);
|
||||||
|
|
||||||
|
memset(&avi_file->avi_sound_header, 0, sizeof(AVISTREAMINFO));
|
||||||
|
avi_file->avi_sound_header.fccType = streamtypeAUDIO;
|
||||||
|
avi_file->avi_sound_header.dwQuality = (DWORD)-1;
|
||||||
|
avi_file->avi_sound_header.dwScale = avi_file->wave_format.nBlockAlign;
|
||||||
|
avi_file->avi_sound_header.dwRate = avi_file->wave_format.nAvgBytesPerSec;
|
||||||
|
avi_file->avi_sound_header.dwSampleSize = avi_file->wave_format.nBlockAlign;
|
||||||
|
avi_file->avi_sound_header.dwInitialFrames = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
avi_file->currentBufferIndex = 0;
|
||||||
|
avi_file->audio_buffer_pos[0] = 0;
|
||||||
|
avi_file->audio_buffer_pos[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVIFileInit();
|
||||||
|
|
||||||
// open the file
|
// open the file
|
||||||
if(FAILED(AVIFileOpen(&avi_file->avi_file, filename, OF_CREATE | OF_WRITE, NULL)))
|
if (FAILED(AVIFileOpen(&avi_file->avi_file, filename, OF_CREATE | OF_WRITE, NULL)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (FAILED(AVIFileCreateStream(avi_file->avi_file, &avi_file->streams[VIDEO_STREAM], &avi_file->avi_video_header)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// create the video stream
|
if (use_prev_options)
|
||||||
set_video_format(pbmih, avi_file);
|
|
||||||
|
|
||||||
memset(&avi_file->avi_video_header, 0, sizeof(AVISTREAMINFO));
|
|
||||||
avi_file->prescaleLevel = video.prescaleHD;
|
|
||||||
avi_file->frameWidth = GPU_FRAMEBUFFER_NATIVE_WIDTH * video.prescaleHD;
|
|
||||||
avi_file->frameHeight = GPU_FRAMEBUFFER_NATIVE_HEIGHT * video.prescaleHD * 2;
|
|
||||||
avi_file->convert_buffer = (u8*)malloc_alignedCacheLine(avi_file->frameWidth * avi_file->frameHeight * 3);
|
|
||||||
|
|
||||||
avi_file->avi_video_header.fccType = streamtypeVIDEO;
|
|
||||||
avi_file->avi_video_header.dwScale = 6*355*263;
|
|
||||||
avi_file->avi_video_header.dwRate = 33513982;
|
|
||||||
avi_file->avi_video_header.dwSuggestedBufferSize = avi_file->bitmap_format.biSizeImage;
|
|
||||||
if(FAILED(AVIFileCreateStream(avi_file->avi_file, &avi_file->streams[VIDEO_STREAM], &avi_file->avi_video_header)))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if(use_prev_options)
|
|
||||||
{
|
{
|
||||||
avi_file->compress_options[VIDEO_STREAM] = saved_avi_info.compress_options[VIDEO_STREAM];
|
avi_file->compress_options[VIDEO_STREAM] = saved_avi_info.compress_options[VIDEO_STREAM];
|
||||||
avi_file->compress_options_ptr[VIDEO_STREAM] = &avi_file->compress_options[0];
|
avi_file->compress_options_ptr[VIDEO_STREAM] = &avi_file->compress_options[0];
|
||||||
|
@ -270,35 +299,24 @@ static int avi_open(const char* filename, const BITMAPINFOHEADER* pbmih, const W
|
||||||
memset(&avi_file->compress_options[VIDEO_STREAM], 0, sizeof(AVICOMPRESSOPTIONS));
|
memset(&avi_file->compress_options[VIDEO_STREAM], 0, sizeof(AVICOMPRESSOPTIONS));
|
||||||
avi_file->compress_options_ptr[VIDEO_STREAM] = &avi_file->compress_options[0];
|
avi_file->compress_options_ptr[VIDEO_STREAM] = &avi_file->compress_options[0];
|
||||||
//retryAviSaveOptions: //mbg merge 7/17/06 removed
|
//retryAviSaveOptions: //mbg merge 7/17/06 removed
|
||||||
error = 0;
|
if (!AVISaveOptions(MainWindow->getHWnd(), 0, 1, &avi_file->streams[VIDEO_STREAM], &avi_file->compress_options_ptr[VIDEO_STREAM]))
|
||||||
if(!AVISaveOptions(MainWindow->getHWnd(), 0, 1, &avi_file->streams[VIDEO_STREAM], &avi_file->compress_options_ptr[VIDEO_STREAM]))
|
|
||||||
break;
|
break;
|
||||||
error = 1;
|
isErrorInFileWrite = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create compressed stream
|
// create compressed stream
|
||||||
if(FAILED(AVIMakeCompressedStream(&avi_file->compressed_streams[VIDEO_STREAM], avi_file->streams[VIDEO_STREAM], &avi_file->compress_options[VIDEO_STREAM], NULL)))
|
if (FAILED(AVIMakeCompressedStream(&avi_file->compressed_streams[VIDEO_STREAM], avi_file->streams[VIDEO_STREAM], &avi_file->compress_options[VIDEO_STREAM], NULL)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// set the stream format
|
// set the stream format
|
||||||
if(FAILED(AVIStreamSetFormat(avi_file->compressed_streams[VIDEO_STREAM], 0, (void*)&avi_file->bitmap_format, avi_file->bitmap_format.biSize)))
|
if (FAILED(AVIStreamSetFormat(avi_file->compressed_streams[VIDEO_STREAM], 0, (void*)&avi_file->bitmap_format, avi_file->bitmap_format.biSize)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// add sound (if requested)
|
// add sound (if requested)
|
||||||
if(pwfex)
|
if (pwfex)
|
||||||
{
|
{
|
||||||
// add audio format
|
|
||||||
set_sound_format(pwfex, avi_file);
|
|
||||||
|
|
||||||
// create the audio stream
|
// create the audio stream
|
||||||
memset(&avi_file->avi_sound_header, 0, sizeof(AVISTREAMINFO));
|
if (FAILED(AVIFileCreateStream(avi_file->avi_file, &avi_file->streams[AUDIO_STREAM], &avi_file->avi_sound_header)))
|
||||||
avi_file->avi_sound_header.fccType = streamtypeAUDIO;
|
|
||||||
avi_file->avi_sound_header.dwQuality = (DWORD)-1;
|
|
||||||
avi_file->avi_sound_header.dwScale = avi_file->wave_format.nBlockAlign;
|
|
||||||
avi_file->avi_sound_header.dwRate = avi_file->wave_format.nAvgBytesPerSec;
|
|
||||||
avi_file->avi_sound_header.dwSampleSize = avi_file->wave_format.nBlockAlign;
|
|
||||||
avi_file->avi_sound_header.dwInitialFrames = 1;
|
|
||||||
if(FAILED(AVIFileCreateStream(avi_file->avi_file, &avi_file->streams[AUDIO_STREAM], &avi_file->avi_sound_header)))
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// AVISaveOptions doesn't seem to work for audio streams
|
// AVISaveOptions doesn't seem to work for audio streams
|
||||||
|
@ -306,7 +324,7 @@ static int avi_open(const char* filename, const BITMAPINFOHEADER* pbmih, const W
|
||||||
avi_file->compressed_streams[AUDIO_STREAM] = avi_file->streams[AUDIO_STREAM];
|
avi_file->compressed_streams[AUDIO_STREAM] = avi_file->streams[AUDIO_STREAM];
|
||||||
|
|
||||||
// set the stream format
|
// set the stream format
|
||||||
if(FAILED(AVIStreamSetFormat(avi_file->compressed_streams[AUDIO_STREAM], 0, (void*)&avi_file->wave_format, sizeof(WAVEFORMATEX))))
|
if (FAILED(AVIStreamSetFormat(avi_file->compressed_streams[AUDIO_STREAM], 0, (void*)&avi_file->wave_format, sizeof(WAVEFORMATEX))))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,30 +333,47 @@ static int avi_open(const char* filename, const BITMAPINFOHEADER* pbmih, const W
|
||||||
avi_file->sound_samples = 0;
|
avi_file->sound_samples = 0;
|
||||||
avi_file->tBytes = 0;
|
avi_file->tBytes = 0;
|
||||||
avi_file->ByteBuffer = 0;
|
avi_file->ByteBuffer = 0;
|
||||||
avi_file->audio_buffer_pos = 0;
|
|
||||||
|
|
||||||
// success
|
// success
|
||||||
error = 0;
|
result = true;
|
||||||
result = 1;
|
|
||||||
avi_file->valid = 1;
|
|
||||||
|
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
if(!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
avi_destroy(&avi_file);
|
avi_destroy(&avi_file);
|
||||||
if(error)
|
|
||||||
|
free_aligned(avi_file->convert_buffer);
|
||||||
|
free(avi_file);
|
||||||
|
avi_file = NULL;
|
||||||
|
|
||||||
|
if (isErrorInFileWrite)
|
||||||
EMU_PrintError("Error writing AVI file");
|
EMU_PrintError("Error writing AVI file");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool AviNextSegment()
|
||||||
|
{
|
||||||
|
char avi_fname[MAX_PATH];
|
||||||
|
strcpy(avi_fname, saved_avi_fname);
|
||||||
|
char avi_fname_temp[MAX_PATH];
|
||||||
|
sprintf(avi_fname_temp, "%s_part%d%s", avi_fname, avi_segnum + 2, saved_avi_ext);
|
||||||
|
saved_avi_info = *avi_file;
|
||||||
|
use_prev_options = 1;
|
||||||
|
avi_segnum++;
|
||||||
|
bool ret = DRV_AviBegin(avi_fname_temp, true);
|
||||||
|
use_prev_options = 0;
|
||||||
|
strcpy(saved_avi_fname, avi_fname);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
//converts 16bpp to 24bpp and flips
|
//converts 16bpp to 24bpp and flips
|
||||||
static void do_video_conversion555(AVIFile* avi, const u16* srcHead, const size_t firstLineIndex, const size_t lastLineIndex)
|
static void do_video_conversion555(AVIFile* avi, const size_t bufferIndex, const u16* srcHead, const size_t firstLineIndex, const size_t lastLineIndex)
|
||||||
{
|
{
|
||||||
const u16* src = srcHead + (avi->frameWidth * firstLineIndex);
|
const u16* src = srcHead + (avi->frameWidth * firstLineIndex);
|
||||||
u8* dst = avi->convert_buffer + (avi->frameWidth * (avi->frameHeight - (firstLineIndex + 1)) * 3);
|
u8* dst = avi->convert_buffer + (avi->videoStreamBufferSize * bufferIndex) + (avi->frameWidth * (avi->frameHeight - (firstLineIndex + 1)) * 3);
|
||||||
|
|
||||||
for (size_t y = firstLineIndex; y <= lastLineIndex; y++)
|
for (size_t y = firstLineIndex; y <= lastLineIndex; y++)
|
||||||
{
|
{
|
||||||
|
@ -349,10 +384,10 @@ static void do_video_conversion555(AVIFile* avi, const u16* srcHead, const size_
|
||||||
}
|
}
|
||||||
|
|
||||||
//converts 32bpp to 24bpp and flips
|
//converts 32bpp to 24bpp and flips
|
||||||
static void do_video_conversion(AVIFile* avi, const u32* srcHead, const size_t firstLineIndex, const size_t lastLineIndex)
|
static void do_video_conversion(AVIFile* avi, const size_t bufferIndex, const u32* srcHead, const size_t firstLineIndex, const size_t lastLineIndex)
|
||||||
{
|
{
|
||||||
const u32* src = srcHead + (avi->frameWidth * firstLineIndex);
|
const u32* src = srcHead + (avi->frameWidth * firstLineIndex);
|
||||||
u8* dst = avi->convert_buffer + (avi->frameWidth * (avi->frameHeight - (firstLineIndex + 1)) * 3);
|
u8* dst = avi->convert_buffer + (avi->videoStreamBufferSize * bufferIndex) + (avi->frameWidth * (avi->frameHeight - (firstLineIndex + 1)) * 3);
|
||||||
|
|
||||||
for (size_t y = firstLineIndex; y <= lastLineIndex; y++)
|
for (size_t y = firstLineIndex; y <= lastLineIndex; y++)
|
||||||
{
|
{
|
||||||
|
@ -362,10 +397,71 @@ static void do_video_conversion(AVIFile* avi, const u32* srcHead, const size_t f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DRV_AviFileWriteExecute(AVIFile *theFile, const size_t bufferIndex)
|
||||||
|
{
|
||||||
|
HRESULT error = S_OK;
|
||||||
|
|
||||||
|
// Write the video stream to file.
|
||||||
|
if (avi_file->video_added)
|
||||||
|
{
|
||||||
|
error = AVIStreamWrite(avi_file->compressed_streams[VIDEO_STREAM],
|
||||||
|
avi_file->video_frames, 1,
|
||||||
|
avi_file->convert_buffer + (avi_file->videoStreamBufferSize * bufferIndex), avi_file->bitmap_format.biSizeImage,
|
||||||
|
AVIIF_KEYFRAME, NULL, &avi_file->ByteBuffer);
|
||||||
|
|
||||||
|
if (FAILED(error))
|
||||||
|
{
|
||||||
|
DRV_AviEnd(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
avi_file->video_frames++;
|
||||||
|
avi_file->tBytes += avi_file->ByteBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the audio stream to file.
|
||||||
|
if (avi_file->sound_added)
|
||||||
|
{
|
||||||
|
const int frameSampleCount = avi_file->audio_buffer_pos[bufferIndex] / avi_file->wave_format.nBlockAlign;
|
||||||
|
|
||||||
|
error = AVIStreamWrite(avi_file->compressed_streams[AUDIO_STREAM],
|
||||||
|
avi_file->sound_samples, frameSampleCount,
|
||||||
|
avi_file->audio_buffer + (AUDIO_STREAM_BUFFER_SIZE * bufferIndex), avi_file->audio_buffer_pos[bufferIndex],
|
||||||
|
0, NULL, &avi_file->ByteBuffer);
|
||||||
|
|
||||||
|
if (FAILED(error))
|
||||||
|
{
|
||||||
|
DRV_AviEnd(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
avi_file->sound_samples += frameSampleCount;
|
||||||
|
avi_file->tBytes += avi_file->ByteBuffer;
|
||||||
|
avi_file->audio_buffer_pos[bufferIndex] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// segment / split AVI when it's almost 2 GB (2000MB, to be precise)
|
||||||
|
//if(!(avi_file->video_frames % 60) && avi_file->tBytes > 2097152000) AviNextSegment();
|
||||||
|
//NOPE: why does it have to break at 1 second?
|
||||||
|
//we need to support dumping HD stuff here; that means 100s of MBs per second
|
||||||
|
//let's roll this back a bit to 1800MB to give us a nice huge 256MB wiggle room, and get rid of the 1 second check
|
||||||
|
if (avi_file->tBytes > (1800 * 1024 * 1024)) AviNextSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRV_AviFileWriteFinish()
|
||||||
|
{
|
||||||
|
if (!AVI_IsRecording())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
avi_file->fileWriteThread->finish();
|
||||||
|
}
|
||||||
|
|
||||||
static void* RunConvertBuffer555XTo888(void *arg)
|
static void* RunConvertBuffer555XTo888(void *arg)
|
||||||
{
|
{
|
||||||
AVIConversionParam *convertParam = (AVIConversionParam *)arg;
|
AVIConversionParam *convertParam = (AVIConversionParam *)arg;
|
||||||
do_video_conversion555(convertParam->avi, (u16 *)convertParam->src, convertParam->firstLineIndex, convertParam->lastLineIndex);
|
do_video_conversion555(convertParam->avi, convertParam->bufferIndex, (u16 *)convertParam->src, convertParam->firstLineIndex, convertParam->lastLineIndex);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -373,24 +469,17 @@ static void* RunConvertBuffer555XTo888(void *arg)
|
||||||
static void* RunConvertBuffer888XTo888(void *arg)
|
static void* RunConvertBuffer888XTo888(void *arg)
|
||||||
{
|
{
|
||||||
AVIConversionParam *convertParam = (AVIConversionParam *)arg;
|
AVIConversionParam *convertParam = (AVIConversionParam *)arg;
|
||||||
do_video_conversion(convertParam->avi, (u32 *)convertParam->src, convertParam->firstLineIndex, convertParam->lastLineIndex);
|
do_video_conversion(convertParam->avi, convertParam->bufferIndex, (u32 *)convertParam->src, convertParam->firstLineIndex, convertParam->lastLineIndex);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AviNextSegment()
|
static void* RunAviFileWrite(void *arg)
|
||||||
{
|
{
|
||||||
char avi_fname[MAX_PATH];
|
AVIFileWriteParam *fileWriteParam = (AVIFileWriteParam *)arg;
|
||||||
strcpy(avi_fname,saved_avi_fname);
|
DRV_AviFileWriteExecute(fileWriteParam->avi, fileWriteParam->bufferIndex);
|
||||||
char avi_fname_temp[MAX_PATH];
|
|
||||||
sprintf(avi_fname_temp, "%s_part%d%s", avi_fname, avi_segnum+2, saved_avi_ext);
|
return NULL;
|
||||||
saved_avi_info=*avi_file;
|
|
||||||
use_prev_options=1;
|
|
||||||
avi_segnum++;
|
|
||||||
bool ret = DRV_AviBegin(avi_fname_temp,true);
|
|
||||||
use_prev_options=0;
|
|
||||||
strcpy(saved_avi_fname,avi_fname);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -398,7 +487,7 @@ bool DRV_AviBegin(const char* fname, bool newsegment)
|
||||||
{
|
{
|
||||||
DRV_AviEnd(newsegment);
|
DRV_AviEnd(newsegment);
|
||||||
|
|
||||||
if(!newsegment)
|
if (!newsegment)
|
||||||
avi_segnum = 0;
|
avi_segnum = 0;
|
||||||
|
|
||||||
BITMAPINFOHEADER bi;
|
BITMAPINFOHEADER bi;
|
||||||
|
@ -412,7 +501,7 @@ bool DRV_AviBegin(const char* fname, bool newsegment)
|
||||||
|
|
||||||
WAVEFORMATEX wf;
|
WAVEFORMATEX wf;
|
||||||
wf.cbSize = sizeof(WAVEFORMATEX);
|
wf.cbSize = sizeof(WAVEFORMATEX);
|
||||||
wf.nAvgBytesPerSec = DESMUME_SAMPLE_RATE * 4;
|
wf.nAvgBytesPerSec = DESMUME_SAMPLE_RATE * sizeof(u16) * 2;
|
||||||
wf.nBlockAlign = 4;
|
wf.nBlockAlign = 4;
|
||||||
wf.nChannels = 2;
|
wf.nChannels = 2;
|
||||||
wf.nSamplesPerSec = DESMUME_SAMPLE_RATE;
|
wf.nSamplesPerSec = DESMUME_SAMPLE_RATE;
|
||||||
|
@ -436,13 +525,13 @@ bool DRV_AviBegin(const char* fname, bool newsegment)
|
||||||
// pwf = 0;
|
// pwf = 0;
|
||||||
|
|
||||||
|
|
||||||
if(!avi_open(fname, &bi, pwf))
|
if (!avi_open(fname, &bi, pwf, newsegment))
|
||||||
{
|
{
|
||||||
saved_avi_fname[0]='\0';
|
saved_avi_fname[0]='\0';
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!avi_segnum)
|
if (avi_segnum == 0)
|
||||||
{
|
{
|
||||||
avi_file->numThreads = CommonSettings.num_cores;
|
avi_file->numThreads = CommonSettings.num_cores;
|
||||||
|
|
||||||
|
@ -476,12 +565,19 @@ bool DRV_AviBegin(const char* fname, bool newsegment)
|
||||||
}
|
}
|
||||||
|
|
||||||
avi_file->convertParam[i].avi = avi_file;
|
avi_file->convertParam[i].avi = avi_file;
|
||||||
|
avi_file->convertParam[i].bufferIndex = avi_file->currentBufferIndex;
|
||||||
avi_file->convertParam[i].src = NULL;
|
avi_file->convertParam[i].src = NULL;
|
||||||
|
|
||||||
avi_file->convertThread[i] = new Task();
|
avi_file->convertThread[i] = new Task();
|
||||||
avi_file->convertThread[i]->start(false);
|
avi_file->convertThread[i]->start(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
avi_file->fileWriteParam.avi = avi_file;
|
||||||
|
avi_file->fileWriteParam.bufferIndex = avi_file->currentBufferIndex;
|
||||||
|
|
||||||
|
avi_file->fileWriteThread = new Task();
|
||||||
|
avi_file->fileWriteThread->start(false);
|
||||||
|
|
||||||
// Don't display at file splits
|
// Don't display at file splits
|
||||||
EMU_PrintMessage("AVI recording started.");
|
EMU_PrintMessage("AVI recording started.");
|
||||||
driver->AddLine("AVI recording started.");
|
driver->AddLine("AVI recording started.");
|
||||||
|
@ -490,7 +586,7 @@ bool DRV_AviBegin(const char* fname, bool newsegment)
|
||||||
strncpy(saved_cur_avi_fnameandext,fname,MAX_PATH);
|
strncpy(saved_cur_avi_fnameandext,fname,MAX_PATH);
|
||||||
strncpy(saved_avi_fname,fname,MAX_PATH);
|
strncpy(saved_avi_fname,fname,MAX_PATH);
|
||||||
char* dot = strrchr(saved_avi_fname, '.');
|
char* dot = strrchr(saved_avi_fname, '.');
|
||||||
if(dot && dot > strrchr(saved_avi_fname, '/') && dot > strrchr(saved_avi_fname, '\\'))
|
if (dot && dot > strrchr(saved_avi_fname, '/') && dot > strrchr(saved_avi_fname, '\\'))
|
||||||
{
|
{
|
||||||
strcpy(saved_avi_ext,dot);
|
strcpy(saved_avi_ext,dot);
|
||||||
dot[0]='\0';
|
dot[0]='\0';
|
||||||
|
@ -499,29 +595,39 @@ bool DRV_AviBegin(const char* fname, bool newsegment)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DRV_AviFrameStart()
|
||||||
|
{
|
||||||
|
if (!AVI_IsRecording())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
avi_file->currentBufferIndex = ((avi_file->currentBufferIndex + 1) % 2);
|
||||||
|
}
|
||||||
|
|
||||||
void DRV_AviVideoUpdate()
|
void DRV_AviVideoUpdate()
|
||||||
{
|
{
|
||||||
if (!avi_file || !avi_file->valid)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const NDSDisplayInfo& dispInfo = GPU->GetDisplayInfo();
|
|
||||||
const void* buffer = dispInfo.masterCustomBuffer;
|
|
||||||
|
|
||||||
//dont do anything if prescale has changed, it's just going to be garbage
|
//dont do anything if prescale has changed, it's just going to be garbage
|
||||||
if (video.prescaleHD != avi_file->prescaleLevel)
|
if (!AVI_IsRecording() || !avi_file->video_added || (video.prescaleHD != avi_file->prescaleLevel))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NDSDisplayInfo &dispInfo = GPU->GetDisplayInfo();
|
||||||
|
const void *buffer = dispInfo.masterCustomBuffer;
|
||||||
|
|
||||||
if (gpu_bpp == 15)
|
if (gpu_bpp == 15)
|
||||||
{
|
{
|
||||||
if (avi_file->numThreads == 0)
|
if (avi_file->numThreads == 0)
|
||||||
{
|
{
|
||||||
do_video_conversion555(avi_file, (u16 *)buffer, 0, avi_file->frameHeight - 1);
|
do_video_conversion555(avi_file, avi_file->currentBufferIndex, (u16 *)buffer, 0, avi_file->frameHeight - 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < avi_file->numThreads; i++)
|
for (size_t i = 0; i < avi_file->numThreads; i++)
|
||||||
{
|
{
|
||||||
avi_file->convertParam[i].src = buffer;
|
avi_file->convertParam[i].src = buffer;
|
||||||
|
avi_file->convertParam[i].bufferIndex = avi_file->currentBufferIndex;
|
||||||
avi_file->convertThread[i]->execute(&RunConvertBuffer555XTo888, &avi_file->convertParam[i]);
|
avi_file->convertThread[i]->execute(&RunConvertBuffer555XTo888, &avi_file->convertParam[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -530,93 +636,80 @@ void DRV_AviVideoUpdate()
|
||||||
{
|
{
|
||||||
if (avi_file->numThreads == 0)
|
if (avi_file->numThreads == 0)
|
||||||
{
|
{
|
||||||
do_video_conversion(avi_file, (u32 *)buffer, 0, avi_file->frameHeight - 1);
|
do_video_conversion(avi_file, avi_file->currentBufferIndex, (u32 *)buffer, 0, avi_file->frameHeight - 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < avi_file->numThreads; i++)
|
for (size_t i = 0; i < avi_file->numThreads; i++)
|
||||||
{
|
{
|
||||||
avi_file->convertParam[i].src = buffer;
|
avi_file->convertParam[i].src = buffer;
|
||||||
|
avi_file->convertParam[i].bufferIndex = avi_file->currentBufferIndex;
|
||||||
avi_file->convertThread[i]->execute(&RunConvertBuffer888XTo888, &avi_file->convertParam[i]);
|
avi_file->convertThread[i]->execute(&RunConvertBuffer888XTo888, &avi_file->convertParam[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < avi_file->numThreads; i++)
|
void DRV_AviSoundUpdate(void *soundData, int soundLen)
|
||||||
{
|
{
|
||||||
avi_file->convertThread[i]->finish();
|
if (!AVI_IsRecording() || !avi_file->sound_added)
|
||||||
}
|
return;
|
||||||
|
|
||||||
if(FAILED(AVIStreamWrite(avi_file->compressed_streams[VIDEO_STREAM],
|
const int soundSize = soundLen * avi_file->wave_format.nBlockAlign;
|
||||||
avi_file->video_frames, 1, avi_file->convert_buffer,
|
memcpy(avi_file->audio_buffer + (AUDIO_STREAM_BUFFER_SIZE * avi_file->currentBufferIndex) + avi_file->audio_buffer_pos[avi_file->currentBufferIndex], soundData, soundSize);
|
||||||
avi_file->bitmap_format.biSizeImage, AVIIF_KEYFRAME,
|
avi_file->audio_buffer_pos[avi_file->currentBufferIndex] += soundSize;
|
||||||
NULL, &avi_file->ByteBuffer)))
|
}
|
||||||
|
|
||||||
|
void DRV_AviFileWrite()
|
||||||
|
{
|
||||||
|
if (!AVI_IsRecording())
|
||||||
{
|
{
|
||||||
DRV_AviEnd(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
avi_file->video_frames++;
|
if (avi_file->video_added)
|
||||||
avi_file->tBytes += avi_file->ByteBuffer;
|
{
|
||||||
|
for (size_t i = 0; i < avi_file->numThreads; i++)
|
||||||
|
{
|
||||||
|
avi_file->convertThread[i]->finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// segment / split AVI when it's almost 2 GB (2000MB, to be precise)
|
DRV_AviFileWriteFinish();
|
||||||
//if(!(avi_file->video_frames % 60) && avi_file->tBytes > 2097152000) AviNextSegment();
|
|
||||||
//NOPE: why does it have to break at 1 second?
|
avi_file->fileWriteParam.bufferIndex = avi_file->currentBufferIndex;
|
||||||
//we need to support dumping HD stuff here; that means 100s of MBs per second
|
avi_file->fileWriteThread->execute(&RunAviFileWrite, &avi_file->fileWriteParam);
|
||||||
//let's roll this back a bit to 1800MB to give us a nice huge 256MB wiggle room, and get rid of the 1 second check
|
|
||||||
if(avi_file->tBytes > (1800*1024*1024)) AviNextSegment();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AVI_IsRecording()
|
bool AVI_IsRecording()
|
||||||
{
|
{
|
||||||
return avi_file && avi_file->valid;
|
return (avi_file != NULL);
|
||||||
}
|
|
||||||
void DRV_AviSoundUpdate(void* soundData, int soundLen)
|
|
||||||
{
|
|
||||||
if(!AVI_IsRecording() || !avi_file->sound_added)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const int audioSegmentSize = avi_audiosegment_size(avi_file);
|
|
||||||
const int samplesPerSegment = audioSegmentSize / avi_file->wave_format.nBlockAlign;
|
|
||||||
const int soundSize = soundLen * avi_file->wave_format.nBlockAlign;
|
|
||||||
int nBytes = soundSize;
|
|
||||||
while (avi_file->audio_buffer_pos + nBytes > audioSegmentSize) {
|
|
||||||
const int bytesToTransfer = audioSegmentSize - avi_file->audio_buffer_pos;
|
|
||||||
memcpy(&avi_file->audio_buffer[avi_file->audio_buffer_pos], &((u8*)soundData)[soundSize - nBytes], bytesToTransfer);
|
|
||||||
nBytes -= bytesToTransfer;
|
|
||||||
|
|
||||||
if(FAILED(AVIStreamWrite(avi_file->compressed_streams[AUDIO_STREAM],
|
|
||||||
avi_file->sound_samples, samplesPerSegment,
|
|
||||||
avi_file->audio_buffer, audioSegmentSize, 0, NULL, &avi_file->ByteBuffer)))
|
|
||||||
{
|
|
||||||
DRV_AviEnd(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
avi_file->sound_samples += samplesPerSegment;
|
|
||||||
avi_file->tBytes += avi_file->ByteBuffer;
|
|
||||||
avi_file->audio_buffer_pos = 0;
|
|
||||||
}
|
|
||||||
memcpy(&avi_file->audio_buffer[avi_file->audio_buffer_pos], &((u8*)soundData)[soundSize - nBytes], nBytes);
|
|
||||||
avi_file->audio_buffer_pos += nBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRV_AviEnd(bool newsegment)
|
void DRV_AviEnd(bool newsegment)
|
||||||
{
|
{
|
||||||
if(!avi_file)
|
if (!AVI_IsRecording())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!newsegment)
|
avi_destroy(&avi_file);
|
||||||
|
|
||||||
|
if (!newsegment)
|
||||||
{
|
{
|
||||||
EMU_PrintMessage("AVI recording ended.");
|
EMU_PrintMessage("AVI recording ended.");
|
||||||
driver->AddLine("AVI recording ended.");
|
driver->AddLine("AVI recording ended.");
|
||||||
|
|
||||||
|
delete avi_file->fileWriteThread;
|
||||||
|
avi_file->fileWriteThread = NULL;
|
||||||
|
|
||||||
for (size_t i = 0; i < avi_file->numThreads; i++)
|
for (size_t i = 0; i < avi_file->numThreads; i++)
|
||||||
{
|
{
|
||||||
delete avi_file->convertThread[i];
|
delete avi_file->convertThread[i];
|
||||||
avi_file->convertThread[i] = NULL;
|
avi_file->convertThread[i] = NULL;
|
||||||
avi_file->numThreads = 0;
|
avi_file->numThreads = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
avi_destroy(&avi_file);
|
free_aligned(avi_file->convert_buffer);
|
||||||
|
free(avi_file);
|
||||||
|
avi_file = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2009-2015 DeSmuME team
|
Copyright (C) 2009-2018 DeSmuME team
|
||||||
|
|
||||||
This file is free software: you can redistribute it and/or modify
|
This file is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -22,8 +22,12 @@
|
||||||
|
|
||||||
bool DRV_AviBegin(const char* fname, bool newsegment = false);
|
bool DRV_AviBegin(const char* fname, bool newsegment = false);
|
||||||
void DRV_AviEnd(bool newsegment = false);
|
void DRV_AviEnd(bool newsegment = false);
|
||||||
void DRV_AviSoundUpdate(void* soundData, int soundLen);
|
|
||||||
bool AVI_IsRecording();
|
bool AVI_IsRecording();
|
||||||
|
|
||||||
|
void DRV_AviFrameStart();
|
||||||
void DRV_AviVideoUpdate();
|
void DRV_AviVideoUpdate();
|
||||||
|
void DRV_AviSoundUpdate(void* soundData, int soundLen);
|
||||||
|
void DRV_AviFileWrite();
|
||||||
|
void DRV_AviFileWriteFinish();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2006 Theo Berkau
|
Copyright (C) 2006 Theo Berkau
|
||||||
Copyright (C) 2006-2017 DeSmuME team
|
Copyright (C) 2006-2018 DeSmuME team
|
||||||
|
|
||||||
This file is free software: you can redistribute it and/or modify
|
This file is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -538,6 +538,21 @@ unsigned short windowSize = 0;
|
||||||
Color::Fuchsia
|
Color::Fuchsia
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
|
class GPUEventHandlerWindows : public GPUEventHandlerDefault
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void DidFrameEnd(bool isFrameSkipped, const NDSDisplayInfo &latestDisplayInfo)
|
||||||
|
{
|
||||||
|
if (isFrameSkipped)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRV_AviVideoUpdate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GPUEventHandlerWindows *WinGPUEvent = NULL;
|
||||||
|
|
||||||
LRESULT CALLBACK HUDFontSettingsDlgProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp);
|
LRESULT CALLBACK HUDFontSettingsDlgProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp);
|
||||||
LRESULT CALLBACK GFX3DSettingsDlgProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp);
|
LRESULT CALLBACK GFX3DSettingsDlgProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp);
|
||||||
|
@ -2209,6 +2224,8 @@ static void StepRunLoop_Core()
|
||||||
NDS_endProcessingInput();
|
NDS_endProcessingInput();
|
||||||
FCEUMOV_HandleRecording();
|
FCEUMOV_HandleRecording();
|
||||||
|
|
||||||
|
DRV_AviFrameStart();
|
||||||
|
|
||||||
inFrameBoundary = false;
|
inFrameBoundary = false;
|
||||||
{
|
{
|
||||||
Lock lock;
|
Lock lock;
|
||||||
|
@ -2217,7 +2234,8 @@ static void StepRunLoop_Core()
|
||||||
win_sound_samplecounter = DESMUME_SAMPLE_RATE/60;
|
win_sound_samplecounter = DESMUME_SAMPLE_RATE/60;
|
||||||
}
|
}
|
||||||
inFrameBoundary = true;
|
inFrameBoundary = true;
|
||||||
DRV_AviVideoUpdate();
|
|
||||||
|
DRV_AviFileWrite();
|
||||||
|
|
||||||
CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION);
|
CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION);
|
||||||
ServiceDisplayThreadInvocations();
|
ServiceDisplayThreadInvocations();
|
||||||
|
@ -2939,6 +2957,7 @@ int _main()
|
||||||
|
|
||||||
driver = new WinDriver();
|
driver = new WinDriver();
|
||||||
CurrentWifiHandler = new WinWifiHandler();
|
CurrentWifiHandler = new WinWifiHandler();
|
||||||
|
WinGPUEvent = new GPUEventHandlerWindows;
|
||||||
|
|
||||||
InitializeCriticalSection(&win_execute_sync);
|
InitializeCriticalSection(&win_execute_sync);
|
||||||
InitializeCriticalSection(&win_backbuffer_sync);
|
InitializeCriticalSection(&win_backbuffer_sync);
|
||||||
|
@ -3272,6 +3291,8 @@ int _main()
|
||||||
|
|
||||||
NDS_Init();
|
NDS_Init();
|
||||||
|
|
||||||
|
GPU->SetEventHandler(WinGPUEvent);
|
||||||
|
|
||||||
CommonSettings.GFX3D_Renderer_TextureScalingFactor = (cmdline.texture_upscale != -1) ? cmdline.texture_upscale : GetPrivateProfileInt("3D", "TextureScalingFactor ", 1, IniName);
|
CommonSettings.GFX3D_Renderer_TextureScalingFactor = (cmdline.texture_upscale != -1) ? cmdline.texture_upscale : GetPrivateProfileInt("3D", "TextureScalingFactor ", 1, IniName);
|
||||||
int newPrescaleHD = (cmdline.gpu_resolution_multiplier != -1) ? cmdline.gpu_resolution_multiplier : GetPrivateProfileInt("3D", "PrescaleHD", 1, IniName);
|
int newPrescaleHD = (cmdline.gpu_resolution_multiplier != -1) ? cmdline.gpu_resolution_multiplier : GetPrivateProfileInt("3D", "PrescaleHD", 1, IniName);
|
||||||
|
|
||||||
|
@ -3494,6 +3515,7 @@ int _main()
|
||||||
|
|
||||||
KillDisplay();
|
KillDisplay();
|
||||||
|
|
||||||
|
DRV_AviFileWriteFinish();
|
||||||
DRV_AviEnd();
|
DRV_AviEnd();
|
||||||
WAV_End();
|
WAV_End();
|
||||||
|
|
||||||
|
@ -3785,6 +3807,7 @@ void SetRotate(HWND hwnd, int rot, bool user)
|
||||||
void AviEnd()
|
void AviEnd()
|
||||||
{
|
{
|
||||||
NDS_Pause();
|
NDS_Pause();
|
||||||
|
DRV_AviFileWriteFinish();
|
||||||
DRV_AviEnd();
|
DRV_AviEnd();
|
||||||
NDS_UnPause();
|
NDS_UnPause();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue