diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c9edf32..3a9f5d72 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,7 +34,7 @@ if(WIN32) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/drivers/win/zlib ) set( OPENGL_LDFLAGS OpenGL::GL ) 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) # Non Windows System # UNIX (Linux or Mac OSX) diff --git a/src/drivers/Qt/AviRecord.cpp b/src/drivers/Qt/AviRecord.cpp index f3ec919c..f05c2206 100644 --- a/src/drivers/Qt/AviRecord.cpp +++ b/src/drivers/Qt/AviRecord.cpp @@ -3,6 +3,11 @@ #include #include +#ifdef WIN32 +#include +#include +#endif + #include "driver.h" #include "common/os_utils.h" @@ -13,6 +18,7 @@ #include "Qt/AviRecord.h" #include "Qt/avi/gwavi.h" #include "Qt/nes_shm.h" +#include "Qt/ConsoleWindow.h" #include "Qt/ConsoleUtilities.h" #include "Qt/fceuWrapper.h" @@ -26,31 +32,48 @@ static int abufTail = 0; static int abufSize = 0; static uint32_t *rawVideoBuf = NULL; static int16_t *rawAudioBuf = NULL; -static int videoFormat = 2; +static int videoFormat = AVI_VFW; 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; - // Uncompressed RGB needs to be flipped vertically - y = h-1; - - while ( y >= 0 ) + if ( verticalFlip ) { - x = 0; - i = y*w*4; + // Uncompressed RGB needs to be flipped vertically + y = h-1; - while ( x < w ) + while ( y >= 0 ) { - dest[j] = src[i]; i++; j++; - dest[j] = src[i]; i++; j++; - dest[j] = src[i]; i++; j++; - i++; - x++; + x = 0; + i = y*w*4; + + while ( x < w ) + { + 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 #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 ) { char fourcc[8]; @@ -267,6 +411,18 @@ int aviRecordOpenFile( const char *filepath ) unsigned int fps; 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 ) { @@ -318,15 +474,23 @@ int aviRecordOpenFile( const char *filepath ) memset( fourcc, 0, sizeof(fourcc) ); - - if ( videoFormat == 1 ) + if ( videoFormat == AVI_I420 ) { strcpy( fourcc, "I420"); } - else if ( videoFormat == 2 ) + #ifdef _USE_X264 + else if ( videoFormat == AVI_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(); @@ -473,8 +637,8 @@ void AviRecordDiskThread_t::run(void) int numPixels, width, height, numPixelsReady = 0; int fps = 60, numSamples = 0; unsigned char *rgb24; - int16_t audioOut[48000]; - uint32_t videoOut[1048576]; + int16_t *audioOut; + uint32_t *videoOut; char writeAudio = 1; int avgAudioPerFrame, localVideoFormat; @@ -506,11 +670,20 @@ void AviRecordDiskThread_t::run(void) localVideoFormat = videoFormat; #ifdef _USE_X264 - if ( localVideoFormat == 2) + if ( localVideoFormat == AVI_X264) { X264::init( width, height ); } #endif +#ifdef WIN32 + if ( localVideoFormat == AVI_VFW) + { + VFW::init( width, height ); + } +#endif + + audioOut = (int16_t *)malloc(48000); + videoOut = (uint32_t*)malloc(1048576); while ( !isInterruptionRequested() ) { @@ -528,22 +701,30 @@ void AviRecordDiskThread_t::run(void) writeAudio = 1; - if ( localVideoFormat == 1) + if ( localVideoFormat == AVI_I420) { Convert_4byte_To_I420Frame<4>(videoOut,rgb24,numPixels,width); gwavi->add_frame( rgb24, (numPixels*3)/2 ); } #ifdef _USE_X264 - else if ( localVideoFormat == 2) + else if ( localVideoFormat == AVI_X264) { Convert_4byte_To_I420Frame<4>(videoOut,rgb24,numPixels,width); X264::encode_frame( rgb24, width, height ); } #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 { convertRgb_32_to_24( (const unsigned char*)videoOut, rgb24, - width, height, numPixels ); + width, height, numPixels, true ); gwavi->add_frame( rgb24, numPixels*3 ); } @@ -583,13 +764,22 @@ void AviRecordDiskThread_t::run(void) free(rgb24); #ifdef _USE_X264 - if ( localVideoFormat == 2) + if ( localVideoFormat == AVI_X264) { X264::close(); } +#endif +#ifdef WIN32 + if ( localVideoFormat == AVI_VFW) + { + VFW::close(); + } #endif aviRecordClose(); + free(audioOut); + free(videoOut); + printf("AVI Record Disk Exit\n"); emit finished(); } diff --git a/src/drivers/Qt/AviRecord.h b/src/drivers/Qt/AviRecord.h index 485672df..25c8aedf 100644 --- a/src/drivers/Qt/AviRecord.h +++ b/src/drivers/Qt/AviRecord.h @@ -7,6 +7,19 @@ #include #include +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 aviRecordAddFrame( void ); diff --git a/src/drivers/Qt/main.cpp b/src/drivers/Qt/main.cpp index 7d874ebc..468ab2ed 100644 --- a/src/drivers/Qt/main.cpp +++ b/src/drivers/Qt/main.cpp @@ -83,6 +83,13 @@ int main( int argc, char *argv[] ) QApplication app(argc, argv); //const char *styleSheetEnv = NULL; + #ifdef WIN32 + if (AttachConsole(ATTACH_PARENT_PROCESS)) + { + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + } + #endif //app.setStyle( new MenuStyle() ); //styleSheetEnv = ::getenv("FCEUX_QT_STYLESHEET");