First successful test of VFW avi recording on Qt GUI.
This commit is contained in:
parent
22523076de
commit
33d645f911
|
@ -34,7 +34,7 @@ if(WIN32)
|
||||||
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/drivers/win/zlib )
|
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/drivers/win/zlib )
|
||||||
set( OPENGL_LDFLAGS OpenGL::GL )
|
set( OPENGL_LDFLAGS OpenGL::GL )
|
||||||
set( SDL2_LDFLAGS ${SDL_INSTALL_PREFIX}/SDL2/lib/x64/SDL2.lib )
|
set( SDL2_LDFLAGS ${SDL_INSTALL_PREFIX}/SDL2/lib/x64/SDL2.lib )
|
||||||
set( SYS_LIBS wsock32 ws2_32 )
|
set( SYS_LIBS wsock32 ws2_32 vfw32 )
|
||||||
else(WIN32)
|
else(WIN32)
|
||||||
# Non Windows System
|
# Non Windows System
|
||||||
# UNIX (Linux or Mac OSX)
|
# UNIX (Linux or Mac OSX)
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include <vfw.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
#include "common/os_utils.h"
|
#include "common/os_utils.h"
|
||||||
|
|
||||||
|
@ -13,6 +18,7 @@
|
||||||
#include "Qt/AviRecord.h"
|
#include "Qt/AviRecord.h"
|
||||||
#include "Qt/avi/gwavi.h"
|
#include "Qt/avi/gwavi.h"
|
||||||
#include "Qt/nes_shm.h"
|
#include "Qt/nes_shm.h"
|
||||||
|
#include "Qt/ConsoleWindow.h"
|
||||||
#include "Qt/ConsoleUtilities.h"
|
#include "Qt/ConsoleUtilities.h"
|
||||||
#include "Qt/fceuWrapper.h"
|
#include "Qt/fceuWrapper.h"
|
||||||
|
|
||||||
|
@ -26,31 +32,48 @@ static int abufTail = 0;
|
||||||
static int abufSize = 0;
|
static int abufSize = 0;
|
||||||
static uint32_t *rawVideoBuf = NULL;
|
static uint32_t *rawVideoBuf = NULL;
|
||||||
static int16_t *rawAudioBuf = NULL;
|
static int16_t *rawAudioBuf = NULL;
|
||||||
static int videoFormat = 2;
|
static int videoFormat = AVI_VFW;
|
||||||
static int audioSampleRate = 48000;
|
static int audioSampleRate = 48000;
|
||||||
//**************************************************************************************
|
//**************************************************************************************
|
||||||
|
|
||||||
static void convertRgb_32_to_24( const unsigned char *src, unsigned char *dest, int w, int h, int nPix )
|
static void convertRgb_32_to_24( const unsigned char *src, unsigned char *dest, int w, int h, int nPix, bool verticalFlip )
|
||||||
{
|
{
|
||||||
int i=0, j=0, x, y;
|
int i=0, j=0, x, y;
|
||||||
|
|
||||||
// Uncompressed RGB needs to be flipped vertically
|
if ( verticalFlip )
|
||||||
y = h-1;
|
|
||||||
|
|
||||||
while ( y >= 0 )
|
|
||||||
{
|
{
|
||||||
x = 0;
|
// Uncompressed RGB needs to be flipped vertically
|
||||||
i = y*w*4;
|
y = h-1;
|
||||||
|
|
||||||
while ( x < w )
|
while ( y >= 0 )
|
||||||
{
|
{
|
||||||
dest[j] = src[i]; i++; j++;
|
x = 0;
|
||||||
dest[j] = src[i]; i++; j++;
|
i = y*w*4;
|
||||||
dest[j] = src[i]; i++; j++;
|
|
||||||
i++;
|
while ( x < w )
|
||||||
x++;
|
{
|
||||||
|
dest[j] = src[i]; i++; j++;
|
||||||
|
dest[j] = src[i]; i++; j++;
|
||||||
|
dest[j] = src[i]; i++; j++;
|
||||||
|
i++;
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
y--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
|
||||||
|
size = nPix*4;
|
||||||
|
i=0;
|
||||||
|
while ( i < size )
|
||||||
|
{
|
||||||
|
dest[j] = src[i]; i++; j++;
|
||||||
|
dest[j] = src[i]; i++; j++;
|
||||||
|
dest[j] = src[i]; i++; j++;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
y--;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//**************************************************************************************
|
//**************************************************************************************
|
||||||
|
@ -260,6 +283,127 @@ static int close(void)
|
||||||
}; // End X264 namespace
|
}; // End X264 namespace
|
||||||
#endif
|
#endif
|
||||||
//**************************************************************************************
|
//**************************************************************************************
|
||||||
|
// Windows VFW Interface
|
||||||
|
#ifdef WIN32
|
||||||
|
namespace VFW
|
||||||
|
{
|
||||||
|
static bool cmpSet = false;
|
||||||
|
static COMPVARS cmpvars;
|
||||||
|
static DWORD dwFlags = 0;
|
||||||
|
static BITMAPINFOHEADER bmapIn;
|
||||||
|
static LPBITMAPINFOHEADER bmapOut = NULL;
|
||||||
|
static DWORD frameNum = 0;
|
||||||
|
static DWORD dwQuality = 0;
|
||||||
|
static LPVOID outBuf = NULL;
|
||||||
|
|
||||||
|
static int chooseConfig(int width, int height)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
if ( cmpSet )
|
||||||
|
{
|
||||||
|
ICCompressorFree( &cmpvars );
|
||||||
|
cmpSet = false;
|
||||||
|
}
|
||||||
|
memset( &cmpvars, 0, sizeof(COMPVARS));
|
||||||
|
cmpvars.cbSize = sizeof(COMPVARS);
|
||||||
|
ret = ICCompressorChoose( HWND(consoleWindow->winId()), ICMF_CHOOSE_ALLCOMPRESSORS,
|
||||||
|
0, NULL, &cmpvars, 0);
|
||||||
|
|
||||||
|
//printf("hic:%i\n", cmpvars.hic);
|
||||||
|
printf("FCC:%08X %c%c%c%c \n", cmpvars.fccHandler,
|
||||||
|
(cmpvars.fccHandler & 0x000000FF) ,
|
||||||
|
(cmpvars.fccHandler & 0x0000FF00) >> 8,
|
||||||
|
(cmpvars.fccHandler & 0x00FF0000) >> 16,
|
||||||
|
(cmpvars.fccHandler & 0xFF000000) >> 24 );
|
||||||
|
|
||||||
|
|
||||||
|
if ( ret )
|
||||||
|
{
|
||||||
|
cmpSet = true;
|
||||||
|
}
|
||||||
|
return (cmpSet == false) ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init( int width, int height )
|
||||||
|
{
|
||||||
|
void *h;
|
||||||
|
DWORD dwFormatSize, dwCompressBufferSize;
|
||||||
|
|
||||||
|
memset( &bmapIn, 0, sizeof(bmapIn));
|
||||||
|
bmapIn.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
bmapIn.biWidth = width;
|
||||||
|
bmapIn.biHeight = height;
|
||||||
|
bmapIn.biPlanes = 1;
|
||||||
|
bmapIn.biBitCount = 24;
|
||||||
|
bmapIn.biCompression = BI_RGB;
|
||||||
|
bmapIn.biSizeImage = width * height * 3;
|
||||||
|
|
||||||
|
dwFormatSize = ICCompressGetFormatSize( cmpvars.hic, &bmapIn );
|
||||||
|
|
||||||
|
printf("Format Size:%i %zi\n", dwFormatSize, sizeof(BITMAPINFOHEADER));
|
||||||
|
|
||||||
|
h = GlobalAlloc(GHND, dwFormatSize);
|
||||||
|
bmapOut = (LPBITMAPINFOHEADER)GlobalLock(h);
|
||||||
|
memset( bmapOut, 0, sizeof(bmapOut));
|
||||||
|
bmapOut->biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
ICCompressGetFormat( cmpvars.hic, &bmapIn, bmapOut);
|
||||||
|
|
||||||
|
// Find the worst-case buffer size.
|
||||||
|
dwCompressBufferSize = ICCompressGetSize( cmpvars.hic, &bmapIn, bmapOut);
|
||||||
|
|
||||||
|
printf("Worst Case Compress Buffer Size: %i\n", dwCompressBufferSize );
|
||||||
|
|
||||||
|
// Allocate a buffer and get lpOutput to point to it.
|
||||||
|
h = GlobalAlloc(GHND, dwCompressBufferSize);
|
||||||
|
outBuf = (LPVOID)GlobalLock(h);
|
||||||
|
|
||||||
|
dwQuality = ICGetDefaultQuality( cmpvars.hic );
|
||||||
|
|
||||||
|
ICCompressBegin( cmpvars.hic, &bmapIn, bmapOut );
|
||||||
|
//msleep(5000);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int close(void)
|
||||||
|
{
|
||||||
|
ICCompressEnd( cmpvars.hic);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int encode_frame( unsigned char *inBuf, int width, int height )
|
||||||
|
{
|
||||||
|
DWORD ret;
|
||||||
|
DWORD flagsOut = 0, reserved = 0;
|
||||||
|
int bytesWritten = 0;
|
||||||
|
|
||||||
|
ret = ICCompress(
|
||||||
|
cmpvars.hic,
|
||||||
|
dwFlags,
|
||||||
|
bmapOut,
|
||||||
|
outBuf,
|
||||||
|
&bmapIn,
|
||||||
|
inBuf,
|
||||||
|
&reserved,
|
||||||
|
&flagsOut,
|
||||||
|
frameNum++,
|
||||||
|
0,
|
||||||
|
dwQuality,
|
||||||
|
NULL, NULL );
|
||||||
|
|
||||||
|
if ( ret == ICERR_OK )
|
||||||
|
{
|
||||||
|
//printf("Compressing Frame:%i Size:%i\n", frameNum, bmapOut->biSizeImage);
|
||||||
|
bytesWritten = bmapOut->biSizeImage;
|
||||||
|
gwavi->add_frame( (unsigned char*)outBuf, bytesWritten );
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End namespace VFW
|
||||||
|
#endif
|
||||||
|
//**************************************************************************************
|
||||||
int aviRecordOpenFile( const char *filepath )
|
int aviRecordOpenFile( const char *filepath )
|
||||||
{
|
{
|
||||||
char fourcc[8];
|
char fourcc[8];
|
||||||
|
@ -267,6 +411,18 @@ int aviRecordOpenFile( const char *filepath )
|
||||||
unsigned int fps;
|
unsigned int fps;
|
||||||
char fileName[1024];
|
char fileName[1024];
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
if ( videoFormat == AVI_VFW )
|
||||||
|
{
|
||||||
|
if ( !VFW::cmpSet )
|
||||||
|
{
|
||||||
|
if ( VFW::chooseConfig( nes_shm->video.ncol, nes_shm->video.nrow ) )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( filepath != NULL )
|
if ( filepath != NULL )
|
||||||
{
|
{
|
||||||
|
@ -318,15 +474,23 @@ int aviRecordOpenFile( const char *filepath )
|
||||||
|
|
||||||
memset( fourcc, 0, sizeof(fourcc) );
|
memset( fourcc, 0, sizeof(fourcc) );
|
||||||
|
|
||||||
|
if ( videoFormat == AVI_I420 )
|
||||||
if ( videoFormat == 1 )
|
|
||||||
{
|
{
|
||||||
strcpy( fourcc, "I420");
|
strcpy( fourcc, "I420");
|
||||||
}
|
}
|
||||||
else if ( videoFormat == 2 )
|
#ifdef _USE_X264
|
||||||
|
else if ( videoFormat == AVI_X264 )
|
||||||
{
|
{
|
||||||
strcpy( fourcc, "X264");
|
strcpy( fourcc, "X264");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
else if ( videoFormat == AVI_VFW )
|
||||||
|
{
|
||||||
|
memcpy( fourcc, &VFW::cmpvars.fccHandler, 4);
|
||||||
|
printf("Set VFW FourCC: %s", fourcc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
gwavi = new gwavi_t();
|
gwavi = new gwavi_t();
|
||||||
|
|
||||||
|
@ -473,8 +637,8 @@ void AviRecordDiskThread_t::run(void)
|
||||||
int numPixels, width, height, numPixelsReady = 0;
|
int numPixels, width, height, numPixelsReady = 0;
|
||||||
int fps = 60, numSamples = 0;
|
int fps = 60, numSamples = 0;
|
||||||
unsigned char *rgb24;
|
unsigned char *rgb24;
|
||||||
int16_t audioOut[48000];
|
int16_t *audioOut;
|
||||||
uint32_t videoOut[1048576];
|
uint32_t *videoOut;
|
||||||
char writeAudio = 1;
|
char writeAudio = 1;
|
||||||
int avgAudioPerFrame, localVideoFormat;
|
int avgAudioPerFrame, localVideoFormat;
|
||||||
|
|
||||||
|
@ -506,11 +670,20 @@ void AviRecordDiskThread_t::run(void)
|
||||||
localVideoFormat = videoFormat;
|
localVideoFormat = videoFormat;
|
||||||
|
|
||||||
#ifdef _USE_X264
|
#ifdef _USE_X264
|
||||||
if ( localVideoFormat == 2)
|
if ( localVideoFormat == AVI_X264)
|
||||||
{
|
{
|
||||||
X264::init( width, height );
|
X264::init( width, height );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
if ( localVideoFormat == AVI_VFW)
|
||||||
|
{
|
||||||
|
VFW::init( width, height );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
audioOut = (int16_t *)malloc(48000);
|
||||||
|
videoOut = (uint32_t*)malloc(1048576);
|
||||||
|
|
||||||
while ( !isInterruptionRequested() )
|
while ( !isInterruptionRequested() )
|
||||||
{
|
{
|
||||||
|
@ -528,22 +701,30 @@ void AviRecordDiskThread_t::run(void)
|
||||||
|
|
||||||
writeAudio = 1;
|
writeAudio = 1;
|
||||||
|
|
||||||
if ( localVideoFormat == 1)
|
if ( localVideoFormat == AVI_I420)
|
||||||
{
|
{
|
||||||
Convert_4byte_To_I420Frame<4>(videoOut,rgb24,numPixels,width);
|
Convert_4byte_To_I420Frame<4>(videoOut,rgb24,numPixels,width);
|
||||||
gwavi->add_frame( rgb24, (numPixels*3)/2 );
|
gwavi->add_frame( rgb24, (numPixels*3)/2 );
|
||||||
}
|
}
|
||||||
#ifdef _USE_X264
|
#ifdef _USE_X264
|
||||||
else if ( localVideoFormat == 2)
|
else if ( localVideoFormat == AVI_X264)
|
||||||
{
|
{
|
||||||
Convert_4byte_To_I420Frame<4>(videoOut,rgb24,numPixels,width);
|
Convert_4byte_To_I420Frame<4>(videoOut,rgb24,numPixels,width);
|
||||||
X264::encode_frame( rgb24, width, height );
|
X264::encode_frame( rgb24, width, height );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
else if ( localVideoFormat == AVI_VFW)
|
||||||
|
{
|
||||||
|
convertRgb_32_to_24( (const unsigned char*)videoOut, rgb24,
|
||||||
|
width, height, numPixels, true );
|
||||||
|
writeAudio = VFW::encode_frame( rgb24, width, height ) > 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
convertRgb_32_to_24( (const unsigned char*)videoOut, rgb24,
|
convertRgb_32_to_24( (const unsigned char*)videoOut, rgb24,
|
||||||
width, height, numPixels );
|
width, height, numPixels, true );
|
||||||
gwavi->add_frame( rgb24, numPixels*3 );
|
gwavi->add_frame( rgb24, numPixels*3 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,13 +764,22 @@ void AviRecordDiskThread_t::run(void)
|
||||||
free(rgb24);
|
free(rgb24);
|
||||||
|
|
||||||
#ifdef _USE_X264
|
#ifdef _USE_X264
|
||||||
if ( localVideoFormat == 2)
|
if ( localVideoFormat == AVI_X264)
|
||||||
{
|
{
|
||||||
X264::close();
|
X264::close();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
if ( localVideoFormat == AVI_VFW)
|
||||||
|
{
|
||||||
|
VFW::close();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
aviRecordClose();
|
aviRecordClose();
|
||||||
|
|
||||||
|
free(audioOut);
|
||||||
|
free(videoOut);
|
||||||
|
|
||||||
printf("AVI Record Disk Exit\n");
|
printf("AVI Record Disk Exit\n");
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,19 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
enum aviEncoderList
|
||||||
|
{
|
||||||
|
AVI_RGB24 = 0,
|
||||||
|
AVI_I420,
|
||||||
|
#ifdef _USE_X264
|
||||||
|
AVI_X264,
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
AVI_VFW,
|
||||||
|
#endif
|
||||||
|
AVI_NUM_ENC
|
||||||
|
};
|
||||||
|
|
||||||
int aviRecordOpenFile( const char *filepath );
|
int aviRecordOpenFile( const char *filepath );
|
||||||
|
|
||||||
int aviRecordAddFrame( void );
|
int aviRecordAddFrame( void );
|
||||||
|
|
|
@ -83,6 +83,13 @@ int main( int argc, char *argv[] )
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
//const char *styleSheetEnv = NULL;
|
//const char *styleSheetEnv = NULL;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
||||||
|
{
|
||||||
|
freopen("CONOUT$", "w", stdout);
|
||||||
|
freopen("CONOUT$", "w", stderr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
//app.setStyle( new MenuStyle() );
|
//app.setStyle( new MenuStyle() );
|
||||||
|
|
||||||
//styleSheetEnv = ::getenv("FCEUX_QT_STYLESHEET");
|
//styleSheetEnv = ::getenv("FCEUX_QT_STYLESHEET");
|
||||||
|
|
Loading…
Reference in New Issue