From b7029dac06104e878de445a12cdb6b6fa4621e93 Mon Sep 17 00:00:00 2001 From: Patryk Obara Date: Sat, 8 Feb 2020 14:52:48 +0100 Subject: [PATCH] =?UTF-8?q?Revert=20"Rewrite=20video=20capturing=20and=20(?= =?UTF-8?q?=E2=80=A6)"=20r4314?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 987a48600deeeec7733a7f01e418064c49dd6eb9. --- configure.ac | 27 +- include/hardware.h | 4 +- src/hardware/hardware.cpp | 982 ++++++++++++---------------------- src/libs/zmbv/zmbv.cpp | 32 +- src/libs/zmbv/zmbv.h | 3 +- src/platform/visualc/config.h | 2 - 6 files changed, 379 insertions(+), 671 deletions(-) diff --git a/configure.ac b/configure.ac index a01d7107..e993b759 100644 --- a/configure.ac +++ b/configure.ac @@ -439,12 +439,12 @@ else fi AH_TEMPLATE(C_SSHOT,[Define to 1 to enable screenshots, requires libpng]) -AC_ARG_ENABLE(screenshots,AC_HELP_STRING([--disable-screenshots],[Disable screenshots]),enable_screenshots=no,enable_screenshots=yes) -AC_CHECK_HEADER(png.h,have_png_h=yes,have_png_h=no) -AC_CHECK_LIB(png, png_get_io_ptr, have_png_lib=yes, have_png_lib=no,-lz) +AC_ARG_ENABLE(screenshots,AC_HELP_STRING([--disable-screenshots],[Disable screenshots and movie recording]),,enable_screenshots=yes) +AC_CHECK_HEADER(png.h,have_png_h=yes,) +AC_CHECK_LIB(png, png_get_io_ptr, have_png_lib=yes, ,-lz) AC_MSG_CHECKING([whether screenshots will be enabled]) if test x$enable_screenshots = xyes; then - if test x$have_png_lib = xyes -a x$have_png_h = xyes ; then + if test x$have_png_lib = xyes -a x$have_png_h = xyes ; then LIBS="$LIBS -lpng -lz" AC_DEFINE(C_SSHOT,1) AC_MSG_RESULT([yes]) @@ -455,25 +455,6 @@ else AC_MSG_RESULT([no]) fi -AH_TEMPLATE(C_SRECORD,[Define to 1 to enable movie recording, requires zlib built without Z_SOLO]) -AC_ARG_ENABLE(recording,AC_HELP_STRING([--disable-recording],[Disable movie recording]),,enable_recording=yes) -AC_CHECK_HEADER(zlib.h,have_zlib_h=yes) -AC_CHECK_LIB(z,compress,have_z_lib=yes,,) -AC_MSG_CHECKING([whether recording will be enabled]) -if test x$enable_recording = xyes; then - if test x$have_z_lib = xyes -a x$have_zlib_h = xyes ; then - if test x$enable_screenshots = xno -o x$have_png_h = xno -o x$have_png_lib = xno ; then - LIBS="$LIBS -lz" - fi - AC_DEFINE(C_SRECORD,1) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no, can't find zlib.]) - fi -else - AC_MSG_RESULT([no]) -fi - AH_TEMPLATE(C_MODEM,[Define to 1 to enable internal modem support, requires SDL_net]) AH_TEMPLATE(C_IPX,[Define to 1 to enable IPX over Internet networking, requires SDL_net]) AC_CHECK_HEADER(SDL_net.h,have_sdl_net_h=yes,) diff --git a/include/hardware.h b/include/hardware.h index a2e86ba0..3a960868 100644 --- a/include/hardware.h +++ b/include/hardware.h @@ -45,10 +45,10 @@ bool TS_Get_Address(Bitu& tsaddr, Bitu& tsirq, Bitu& tsdma); extern Bit8u adlib_commandreg; FILE * OpenCaptureFile(const char * type,const char * ext); -void CAPTURE_AddWave(Bit32u freq, Bitu len, Bit16s * data); +void CAPTURE_AddWave(Bit32u freq, Bit32u len, Bit16s * data); #define CAPTURE_FLAG_DBLW 0x1 #define CAPTURE_FLAG_DBLH 0x2 -void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, float fps, const Bit8u * data, const Bit8u * pal); +void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, float fps, Bit8u * data, Bit8u * pal); void CAPTURE_AddMidi(bool sysex, Bitu len, Bit8u * data); #endif diff --git a/src/hardware/hardware.cpp b/src/hardware/hardware.cpp index c8281001..2f800b75 100644 --- a/src/hardware/hardware.cpp +++ b/src/hardware/hardware.cpp @@ -17,7 +17,6 @@ */ -#include #include #include #include @@ -33,8 +32,6 @@ #if (C_SSHOT) #include -#endif -#if (C_SRECORD) #include "../libs/zmbv/zmbv.cpp" #endif @@ -42,386 +39,14 @@ static std::string capturedir; extern const char* RunningProgram; Bitu CaptureState; -static const char version_text[] = "DOSBox " VERSION; - #define WAVE_BUF 16*1024 #define MIDI_BUF 4*1024 - -#if (C_SRECORD) - -#define AVII_KEYFRAME (1<<4) -#define AVIF_HASINDEX (1<<4) -#define AVIF_ISINTERLEAVED (1<<8) - -#define AVI_MAX_SIZE 0x7FFFFFFF - -struct AVITag { - char sTag[4]; - AVITag(const char *n) { - memcpy(sTag, n, sizeof(sTag)); - } -}; - -struct AVIChunk : AVITag { - Bit32u dwSize; - AVIChunk(const char *n, size_t s=AVI_MAX_SIZE) - : AVITag(n) { - var_write(&dwSize, (Bit32u)s); - } -}; - -// utility class to set dwSize automatically -template -struct AVIChunkT : AVIChunk { - AVIChunkT(const char *n) - : AVIChunk(n,sizeof(T)-sizeof(AVIChunk)) {} -}; - -struct AVIStreamFormatAudio { - AVIChunkT ck; - Bit16u wFormatTag; - Bit16u nChannels; - Bit32u nSamplesPerSec; - Bit32u nAvgBytesPerSec; - Bit16u nBlockAlign; - Bit16u wBitsPerSample; - AVIStreamFormatAudio() : ck("strf") { - var_write(&wFormatTag, 1); - var_write(&nChannels, 2); - var_write(&nBlockAlign, 4); - var_write(&wBitsPerSample, 16); - } -}; - -struct AVIStreamFormatVideo { - AVIChunkT ck; - Bit32u biSize; - Bit32u biWidth; - Bit32u biHeight; - Bit16u biPlanes; - Bit16u biBitCount; - Bit32u biCompression; - Bit32u biSizeImage; - Bit32u biXPelsPerMeter; - Bit32u biYPelsPerMeter; - Bit32u biClrUsed; - Bit32u biClrImportant; - AVIStreamFormatVideo() : ck("strf") { - biSize = ck.dwSize; - biPlanes = 0; - biBitCount = 0; - memcpy(&biCompression, CODEC_4CC, 4); - biXPelsPerMeter = 0; - biYPelsPerMeter = 0; - biClrUsed = 0; - biClrImportant = 0; - } -}; - -struct AVIStreamHeader { - AVIChunkT ck; - AVITag sFCC; - Bit32u sFCCHandler; - Bit32u dwFlags; - Bit16u wPriority; - Bit16u wLanguage; - Bit32u dwInitialFrames; - Bit32u dwScale; - Bit32u dwRate; - Bit32u dwStart; - Bit32u dwLength; - Bit32u dwSuggestedBufferSize; - Bit32u dwQuality; - Bit32u dwSampleSize; - Bit16u wLeft,wTop,wRight,wBottom; - AVIStreamHeader(const char *n) : ck("strh"), sFCC(n) { - dwFlags = 0; - wPriority = 0; - wLanguage = 0; - dwInitialFrames = 0; - dwStart = 0; - dwLength = 0xFFFFFFFF; - dwSuggestedBufferSize = 0; - dwQuality = 0xFFFFFFFF; - wLeft = wTop = 0; - } -}; - -struct AVIStreamHeaderVideo : AVIStreamHeader { - AVIStreamHeaderVideo() : AVIStreamHeader("vids") { - memcpy(&sFCCHandler, CODEC_4CC, 4); - var_write(&dwScale, 1<<24); - dwSampleSize = 0; - } -}; - -struct AVIStreamHeaderAudio : AVIStreamHeader { - AVIStreamHeaderAudio() : AVIStreamHeader("auds") { - sFCCHandler = 0; - var_write(&dwScale, 1); - var_write(&dwSampleSize, 4); - wRight = wBottom = 0; - } -}; - -struct AVIMainHeader { - AVIChunkT ck; - Bit32u dwMicroSecPerFrame; - Bit32u dwMaxBytesPerSec; - Bit32u dwPaddingGranularity; - Bit32u dwFlags; - Bit32u dwTotalFrames; - Bit32u dwInitialFrames; - Bit32u dwStreams; - Bit32u dwSuggestedBufferSize; - Bit32u dwWidth; - Bit32u dwHeight; - Bit32u dwReserved[4]; - AVIMainHeader() : ck("avih") { - dwMaxBytesPerSec = 0; - dwPaddingGranularity = 0; - var_write(&dwFlags, AVIF_ISINTERLEAVED); - dwTotalFrames = 0xFFFFFFFF; - dwInitialFrames = 0; - var_write(&dwStreams, 2); - dwSuggestedBufferSize = 0; - dwReserved[0]=dwReserved[1]=dwReserved[2]=dwReserved[3]=0; - } -}; - -template -struct AVIList { - AVIChunkT ck; - AVITag sName; - AVIList(const char *n) : ck("LIST"), sName(n) {} -}; - -/* movi list - special case, initial size is MAX */ -struct AVIListMovi : AVIChunk { - AVITag sName; - AVIListMovi() : AVIChunk("LIST"),sName("movi") {} -}; - -template -struct AVIListStream { - AVIList lst; - htype hdr; - ftype fmt; - AVIListStream() : lst("strl") {} -}; - -struct AVIListHeader { - AVIList lst; - AVIMainHeader hdr; - AVIListStream vid; - AVIListStream aud; - AVIListHeader() : lst("hdrl") {} -}; - -/* Interesting metadata tags: - * "ICMT" Comment/Description - * "ICRD" Date - * "INAM" Title - * "ISFT" Sofware/Encoder (not read by many programs, so using ICMT here instead) - * "IPRD" Product - * "ISBJ" Subject - * "ISRC" Source - */ -template -struct AVIMeta { - AVIChunkT ck; - char aTag[len]; - AVIMeta(const char *n, const char *s) : ck(n) { - Bitu i; - for (i=0; i < sizeof(aTag)-1; i++) { - aTag[i] = *s; - if (*s) ++s; - } - aTag[i] = '\0'; - } -}; - -struct AVIListInfo { - AVIList lst; - // multiple of 4 to keep dword alignment - AVIMeta<(sizeof(version_text)+3)&~3> comment; - AVIListInfo() : lst("INFO"), - comment("ICMT",version_text) {} -}; - -struct AVIRIFF : AVIChunk { - AVITag sName; - AVIListHeader hdr; - AVIListInfo info; - AVIListMovi movi; - AVIRIFF() : AVIChunk("RIFF"), sName("AVI ") {} -}; - -struct AVIIndexEntry { - AVITag dwChunkId; - Bit32u dwFlags; - Bit32u dwOffset; - Bit32u dwSize; - AVIIndexEntry(const AVIChunk &ck, Bit32u flags, size_t offset) - : dwChunkId(ck) { - var_write(&dwFlags, flags); - var_write(&dwOffset, (Bit32u)offset); - dwSize = ck.dwSize; - } -}; - -class AVIFILE { -private: - AVIRIFF riff; - std::vector idx; - size_t data_length; - FILE *handle; - Bitu samples; - Bit32u freq; - size_t buffer_size[2]; - /* set new sampling rate, returns true if changed */ - bool SetFreq(Bit32u newfreq) { - if (freq == newfreq) return false; - freq = newfreq; - var_write(&riff.hdr.aud.hdr.dwRate, freq); - riff.hdr.aud.fmt.nSamplesPerSec = riff.hdr.aud.hdr.dwRate; - var_write(&riff.hdr.aud.fmt.nAvgBytesPerSec, 4*freq); - return true; - } - bool AddChunk(const AVIChunk &ck, const AVIIndexEntry &entry, const void *data, size_t length) { - long pos = ftell(handle); // in case writing fails - if (fwrite(&ck,sizeof(ck),1,handle)==1 && \ - fwrite(data,length,1,handle)==1) - { - if (length&1) { // chunks must be aligned to 2-bytes - fseek(handle,1,SEEK_CUR); - length++; - } else fflush(handle); - idx.push_back(entry); - data_length += length+sizeof(ck); - return true; - } - - fseek(handle, pos, SEEK_SET); - return false; - } -public: - Bit32u frames; - - AVIFILE(FILE* _handle, Bitu width, Bitu height, float fps) : handle(_handle) { - data_length = sizeof(AVITag); // "movi" tag - frames = 0; - samples = 0; - freq = 0; - buffer_size[0]=buffer_size[1] = 0; - idx.reserve(4096); - - var_write(&riff.hdr.hdr.dwMicroSecPerFrame, (Bit32u)(1000000/fps)); - var_write(&riff.hdr.hdr.dwWidth, (Bit32u)width); - var_write(&riff.hdr.hdr.dwHeight, (Bit32u)height); - var_write(&riff.hdr.vid.hdr.dwRate, (Bit32u)((1<<24)*fps)); - var_write(&riff.hdr.vid.hdr.wRight, (Bit16u)width); - var_write(&riff.hdr.vid.hdr.wBottom, (Bit16u)height); - riff.hdr.vid.fmt.biWidth = riff.hdr.hdr.dwWidth; - riff.hdr.vid.fmt.biHeight = riff.hdr.hdr.dwHeight; - var_write(&riff.hdr.vid.fmt.biSizeImage, (Bit32u)(width*height*4)); - - SetFreq(44100); // guess, don't know the frequency until first audio block is written - fwrite(&riff, sizeof(riff), 1, handle); - } - ~AVIFILE() { - AVIChunk idx1("idx1",idx.size()*sizeof(AVIIndexEntry)); - - var_write(&riff.movi.dwSize, (Bit32u)data_length); - var_write(&riff.hdr.hdr.dwTotalFrames, frames); - riff.hdr.vid.hdr.dwLength = riff.hdr.hdr.dwTotalFrames; - var_write(&riff.hdr.vid.hdr.dwSuggestedBufferSize, (Bit32u)buffer_size[0]); - var_write(&riff.hdr.aud.hdr.dwLength, (Bit32u)samples); - var_write(&riff.hdr.aud.hdr.dwSuggestedBufferSize, (Bit32u)buffer_size[1]); - - // attempt to write index - if (fwrite(&idx1,sizeof(idx1),1,handle)==1 && \ - fwrite(&idx[0],sizeof(AVIIndexEntry),idx.size(),handle)==idx.size()) { - // index was added, set HASINDEX flag - var_write(&riff.hdr.hdr.dwFlags, AVIF_ISINTERLEAVED|AVIF_HASINDEX); - // accumulate index size - data_length += sizeof(AVIChunk)+sizeof(AVIIndexEntry)*idx.size(); - } - - var_write(&riff.dwSize, (Bit32u)(sizeof(riff)+data_length-sizeof(AVIChunk)-sizeof(AVITag))); - - fseek(handle, 0, SEEK_SET); - fwrite(&riff, sizeof(riff), 1, handle); - fclose(handle); - } - - /* want to add s bytes of data, fails if new size exceeds 2GB */ - bool CanAdd(size_t s) { - /* calculate maximum possible data size (all constants here)*/ - size_t max_size = AVI_MAX_SIZE; - /* minus headers (excluding "movi" tag) */ - max_size -= (sizeof(riff)-sizeof(AVITag)); - /* minus index chunk header + data chunk header */ - max_size -= 2*sizeof(AVIChunk); - /* minus index entry */ - max_size -= sizeof(AVIIndexEntry); - - /* round up to multiple of 2 */ - s += s&1; - /* add all data already written */ - s += data_length; - /* add existing index entries */ - s += idx.size()*sizeof(AVIIndexEntry); - - return s < max_size; - } - - /* add video data */ - bool AddVideo(const void *data, size_t length, Bit32u flags) { - AVIChunk ck("00dc",length); - AVIIndexEntry entry(ck, flags, data_length); - - if (AddChunk(ck, entry, data, length)) { - if (length > buffer_size[0]) - buffer_size[0] = length; - frames++; - return true; - } - return false; - } - - /* add audio data */ - bool AddAudio(const void *data, Bitu _samples, Bit32u new_freq) { - size_t length = _samples*4; - AVIChunk ck("01wb",length); - /* Every audio block marked as keyframe, just in case */ - AVIIndexEntry entry(ck, AVII_KEYFRAME, data_length); - - if (!CanAdd(length)) return false; - - if (SetFreq(new_freq)) { - /* rewrite audio headers */ - long pos = ftell(handle); - fseek(handle, (long)(riff.hdr.aud.lst.ck.sTag-riff.sTag), SEEK_SET); - fwrite(&riff.hdr.aud, sizeof(riff.hdr.aud), 1, handle); - fseek(handle, pos, SEEK_SET); - } - - if (AddChunk(ck, entry, data, length)) { - if (length > buffer_size[1]) - buffer_size[1] = length; - samples += _samples; - return true; - } - return false; - } -}; -#endif // C_SRECORD +#define AVI_HEADER_SIZE 500 static struct { struct { FILE * handle; - Bit16u buf[WAVE_BUF][2]; + Bit16s buf[WAVE_BUF][2]; Bitu used; Bit32u length; Bit32u freq; @@ -435,17 +60,22 @@ static struct { struct { Bitu rowlen; } image; -#if (C_SRECORD) +#if (C_SSHOT) struct { - AVIFILE *avi_out; + FILE *handle; + Bitu frames; Bit16s audiobuf[WAVE_BUF][2]; Bitu audioused; - Bit32u audiorate; + Bitu audiorate; + Bitu audiowritten; VideoCodec *codec; Bitu width, height, bpp; + Bitu written; float fps; int bufSize; void *buf; + Bit8u *index; + Bitu indexsize, indexused; } video; #endif } capture; @@ -488,7 +118,7 @@ FILE * OpenCaptureFile(const char * type,const char * ext) { } close_directory( dir ); char file_name[CROSS_LEN]; - sprintf(file_name,"%s%c%s%03" sBitfs(u) "%s",capturedir.c_str(),CROSS_FILESPLIT,file_start,last,ext); + sprintf(file_name,"%s%c%s%03d%s",capturedir.c_str(),CROSS_FILESPLIT,file_start,last,ext); /* Open the actual file */ FILE * handle=fopen(file_name,"wb"); if (handle) { @@ -499,39 +129,177 @@ FILE * OpenCaptureFile(const char * type,const char * ext) { return handle; } -#if (C_SRECORD) +#if (C_SSHOT) +static void CAPTURE_AddAviChunk(const char * tag, Bit32u size, void * data, Bit32u flags) { + Bit8u chunk[8];Bit8u *index;Bit32u pos, writesize; + + chunk[0] = tag[0];chunk[1] = tag[1];chunk[2] = tag[2];chunk[3] = tag[3]; + host_writed(&chunk[4], size); + /* Write the actual data */ + fwrite(chunk,1,8,capture.video.handle); + writesize = (size+1)&~1; + fwrite(data,1,writesize,capture.video.handle); + pos = capture.video.written + 4; + capture.video.written += writesize + 8; + if (capture.video.indexused + 16 >= capture.video.indexsize ) { + capture.video.index = (Bit8u*)realloc( capture.video.index, capture.video.indexsize + 16 * 4096); + if (!capture.video.index) + E_Exit("Ran out of memory during AVI capturing"); + capture.video.indexsize += 16*4096; + } + index = capture.video.index+capture.video.indexused; + capture.video.indexused += 16; + index[0] = tag[0]; + index[1] = tag[1]; + index[2] = tag[2]; + index[3] = tag[3]; + host_writed(index+4, flags); + host_writed(index+8, pos); + host_writed(index+12, size); +} +#endif + +#if (C_SSHOT) static void CAPTURE_VideoEvent(bool pressed) { if (!pressed) return; if (CaptureState & CAPTURE_VIDEO) { - /* Flush remaining audio */ - if ( capture.video.audioused ) { - if (capture.video.avi_out) { - /* if it can't be added, leave it to be written to the next segment */ - if (capture.video.avi_out->AddAudio(capture.video.audiobuf, capture.video.audioused, capture.video.audiorate)) { - capture.video.audioused = 0; - } - } - } /* Close the video */ CaptureState &= ~CAPTURE_VIDEO; - LOG_MSG("Stopped capturing video."); + LOG_MSG("Stopped capturing video."); - delete capture.video.avi_out; - capture.video.avi_out = NULL; + Bit8u avi_header[AVI_HEADER_SIZE]; + Bitu main_list; + Bitu 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(AVI_HEADER_SIZE + capture.video.written - 8 + capture.video.indexused); + 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((Bit32u)(1000000 / capture.video.fps)); /* Microseconds per frame */ + AVIOUTd(0); + AVIOUTd(0); /* PaddingGranularity (whatever that might be) */ + AVIOUTd(0x110); /* Flags,0x10 has index, 0x100 interleaved */ + AVIOUTd(capture.video.frames); /* TotalFrames */ + AVIOUTd(0); /* InitialFrames */ + AVIOUTd(2); /* Stream count */ + AVIOUTd(0); /* SuggestedBufferSize */ + AVIOUTd(capture.video.width); /* Width */ + AVIOUTd(capture.video.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_4CC); /* Handler */ + AVIOUTd(0); /* Flags */ + AVIOUTd(0); /* Reserved, MS says: wPriority, wLanguage */ + AVIOUTd(0); /* InitialFrames */ + AVIOUTd(1000000); /* Scale */ + AVIOUTd((Bit32u)(1000000 * capture.video.fps)); /* Rate: Rate/Scale == samples/second */ + AVIOUTd(0); /* Start */ + AVIOUTd(capture.video.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(capture.video.width); /* Width */ + AVIOUTd(capture.video.height); /* Height */ +// OUTSHRT(1); OUTSHRT(24); /* Planes, Count */ + AVIOUTd(0); + AVIOUT4(CODEC_4CC); /* Compression */ + AVIOUTd(capture.video.width * capture.video.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(capture.video.audiorate*4); /* Rate, actual rate is scale/rate */ + AVIOUTd(0); /* Start */ + if (!capture.video.audiorate) + capture.video.audiorate = 1; + AVIOUTd(capture.video.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(capture.video.audiorate); /* SamplesPerSec */ + AVIOUTd(capture.video.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 = AVI_HEADER_SIZE - 8 - 12 - header_pos; + AVIOUT4("JUNK"); + AVIOUTd(njunk); + /* Fix the size of the main list */ + header_pos = main_list; + AVIOUTd(nmain); + header_pos = AVI_HEADER_SIZE - 12; + AVIOUT4("LIST"); + AVIOUTd(capture.video.written+4); /* Length of list in bytes */ + AVIOUT4("movi"); + /* First add the index table to the end */ + memcpy(capture.video.index, "idx1", 4); + host_writed( capture.video.index+4, capture.video.indexused - 8 ); + fwrite( capture.video.index, 1, capture.video.indexused, capture.video.handle); + fseek(capture.video.handle, 0, SEEK_SET); + fwrite(&avi_header, 1, AVI_HEADER_SIZE, capture.video.handle); + fclose( capture.video.handle ); + free( capture.video.index ); free( capture.video.buf ); - capture.video.buf = NULL; delete capture.video.codec; - capture.video.codec = NULL; + capture.video.handle = 0; } else { CaptureState |= CAPTURE_VIDEO; - capture.video.audioused=0; } } #endif -void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, float fps, const Bit8u * data, const Bit8u * pal) { +void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, float fps, Bit8u * data, Bit8u * pal) { +#if (C_SSHOT) Bitu i; Bit8u doubleRow[SCALER_MAXWIDTH*4]; Bitu countWidth = width; @@ -545,183 +313,161 @@ void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, return; if (width > SCALER_MAXWIDTH) return; -#if (C_SSHOT) + if (CaptureState & CAPTURE_IMAGE) { - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; + png_structp png_ptr; + png_infop info_ptr; png_color palette[256]; CaptureState &= ~CAPTURE_IMAGE; /* Open the actual file */ FILE * fp=OpenCaptureFile("Screenshot",".png"); - if (fp) { - /* First try to allocate the png structures */ - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL, NULL); - if (png_ptr) info_ptr = png_create_info_struct(png_ptr); + if (!fp) goto skip_shot; + /* First try to allocate the png structures */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL, NULL); + if (!png_ptr) goto skip_shot; + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr,(png_infopp)NULL); + goto skip_shot; } - if (info_ptr) { - /* Finalize the initing of png library */ - png_init_io(png_ptr, fp); - png_set_compression_level(png_ptr,Z_BEST_COMPRESSION); - - /* set other zlib parameters */ - png_set_compression_mem_level(png_ptr, 8); - png_set_compression_strategy(png_ptr,Z_DEFAULT_STRATEGY); - png_set_compression_window_bits(png_ptr, 15); - png_set_compression_method(png_ptr, 8); - png_set_compression_buffer_size(png_ptr, 8192); + + /* Finalize the initing of png library */ + png_init_io(png_ptr, fp); + png_set_compression_level(png_ptr,Z_BEST_COMPRESSION); - if (bpp==8) { - png_set_IHDR(png_ptr, info_ptr, width, height, - 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - for (i=0;i<256;i++) { - palette[i].red=pal[i*4+0]; - palette[i].green=pal[i*4+1]; - palette[i].blue=pal[i*4+2]; - } - png_set_PLTE(png_ptr, info_ptr, palette,256); - } else { - png_set_bgr( png_ptr ); - png_set_IHDR(png_ptr, info_ptr, width, height, - 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + /* set other zlib parameters */ + png_set_compression_mem_level(png_ptr, 8); + png_set_compression_strategy(png_ptr,Z_DEFAULT_STRATEGY); + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_method(png_ptr, 8); + png_set_compression_buffer_size(png_ptr, 8192); + + if (bpp==8) { + png_set_IHDR(png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + for (i=0;i<256;i++) { + palette[i].red=pal[i*4+0]; + palette[i].green=pal[i*4+1]; + palette[i].blue=pal[i*4+2]; } - -#ifdef PNG_TEXT_SUPPORTED - char ptext[] = "DOSBox" VERSION; - char software[] = "Software"; - png_text text; - text.compression = PNG_TEXT_COMPRESSION_NONE; - text.key = software; - text.text = ptext; - png_set_text(png_ptr, info_ptr, &text, 1); -#endif - png_write_info(png_ptr, info_ptr); - - for (i=0;i> 1)*pitch); - else - srcLine=(data+(i >> 0)*pitch); - rowPointer=srcLine; - switch (bpp) { - case 8: - if (flags & CAPTURE_FLAG_DBLW) { - for (Bitu x=0;x> 10; - doubleRow[x*6+1] = doubleRow[x*6+4] = (((pixel&0xe000)|((pixel&0x0003)<<16)) * 0x21) >> 15; - doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0x007c) * 0x21) >> 4; -#else - doubleRow[x*6+0] = doubleRow[x*6+3] = ((pixel& 0x001f) * 0x21) >> 2; - doubleRow[x*6+1] = doubleRow[x*6+4] = ((pixel& 0x03e0) * 0x21) >> 7; - doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0x7c00) * 0x21) >> 12; -#endif - } - } else { - for (Bitu x=0;x> 10; - doubleRow[x*3+1] = (((pixel&0xe000)|((pixel&0x0003)<<16)) * 0x21) >> 15; - doubleRow[x*3+2] = ((pixel& 0x007c) * 0x21) >> 4; -#else - doubleRow[x*3+0] = ((pixel& 0x001f) * 0x21) >> 2; - doubleRow[x*3+1] = ((pixel& 0x03e0) * 0x21) >> 7; - doubleRow[x*3+2] = ((pixel& 0x7c00) * 0x21) >> 12; -#endif - } - } - rowPointer = doubleRow; - break; - case 16: - if (flags & CAPTURE_FLAG_DBLW) { - for (Bitu x=0;x> 10; - doubleRow[x*6+1] = doubleRow[x*6+4] = (((pixel&0xe000)|((pixel&0x0007)<<16)) * 0x41) >> 17; - doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0x00f8) * 0x21) >> 5; -#else - doubleRow[x*6+0] = doubleRow[x*6+3] = ((pixel& 0x001f) * 0x21) >> 2; - doubleRow[x*6+1] = doubleRow[x*6+4] = ((pixel& 0x07e0) * 0x41) >> 9; - doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0xf800) * 0x21) >> 13; -#endif - } - } else { - for (Bitu x=0;x> 10; - doubleRow[x*3+1] = (((pixel&0xe000)|((pixel&0x0007)<<16)) * 0x41) >> 17; - doubleRow[x*3+2] = ((pixel& 0x00f8) * 0x21) >> 5; -#else - doubleRow[x*3+0] = ((pixel& 0x001f) * 0x21) >> 2; - doubleRow[x*3+1] = ((pixel& 0x07e0) * 0x41) >> 9; - doubleRow[x*3+2] = ((pixel& 0xf800) * 0x21) >> 13; -#endif - } - } - rowPointer = doubleRow; - break; - case 32: - if (flags & CAPTURE_FLAG_DBLW) { - for (Bitu x=0;x> 1)*pitch); + else + srcLine=(data+(i >> 0)*pitch); + rowPointer=srcLine; + switch (bpp) { + case 8: + if (flags & CAPTURE_FLAG_DBLW) { + for (Bitu x=0;x> 2; + doubleRow[x*6+1] = doubleRow[x*6+4] = ((pixel& 0x03e0) * 0x21) >> 7; + doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0x7c00) * 0x21) >> 12; + } + } else { + for (Bitu x=0;x> 2; + doubleRow[x*3+1] = ((pixel& 0x03e0) * 0x21) >> 7; + doubleRow[x*3+2] = ((pixel& 0x7c00) * 0x21) >> 12; + } + } + rowPointer = doubleRow; + break; + case 16: + if (flags & CAPTURE_FLAG_DBLW) { + for (Bitu x=0;x> 2; + doubleRow[x*6+1] = doubleRow[x*6+4] = ((pixel& 0x07e0) * 0x41) >> 9; + doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0xf800) * 0x21) >> 13; + } + } else { + for (Bitu x=0;x> 2; + doubleRow[x*3+1] = ((pixel& 0x07e0) * 0x41) >> 9; + doubleRow[x*3+2] = ((pixel& 0xf800) * 0x21) >> 13; + } + } + rowPointer = doubleRow; + break; + case 32: + if (flags & CAPTURE_FLAG_DBLW) { + for (Bitu x=0;xCanAdd(4*capture.video.audioused+capture.video.bufSize)) { - // need to switch files - CAPTURE_VideoEvent(true); - LOG_MSG("CAPTURE: Beginning new video segment"); - CaptureState |= CAPTURE_VIDEO; - } + if (capture.video.handle && ( + capture.video.width != width || + capture.video.height != height || + capture.video.bpp != bpp || + capture.video.fps != fps)) + { + CAPTURE_VideoEvent(true); } + CaptureState &= ~CAPTURE_VIDEO; switch (bpp) { case 8:format = ZMBV_FORMAT_8BPP;break; case 15:format = ZMBV_FORMAT_15BPP;break; @@ -730,42 +476,53 @@ void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, default: goto skip_video; } - if (!capture.video.avi_out) { - FILE *f = OpenCaptureFile("Video",".avi"); - if (f==NULL) + if (!capture.video.handle) { + capture.video.handle = OpenCaptureFile("Video",".avi"); + if (!capture.video.handle) goto skip_video; - capture.video.avi_out = new AVIFILE(f, width, height, fps); capture.video.codec = new VideoCodec(); if (!capture.video.codec) goto skip_video; - if (!capture.video.codec->SetupCompress( (int)width, (int)height)) + if (!capture.video.codec->SetupCompress( width, height)) goto skip_video; - capture.video.bufSize = capture.video.codec->NeededSize((int)width, (int)height, format); + capture.video.bufSize = capture.video.codec->NeededSize(width, height, format); capture.video.buf = malloc( capture.video.bufSize ); if (!capture.video.buf) goto skip_video; + capture.video.index = (Bit8u*)malloc( 16*4096 ); + if (!capture.video.index) + goto skip_video; + capture.video.indexsize = 16*4096; + capture.video.indexused = 8; capture.video.width = width; capture.video.height = height; capture.video.bpp = bpp; capture.video.fps = fps; + for (i=0;iframes % 300 == 0) + if (capture.video.frames % 300 == 0) codecFlags = 1; else codecFlags = 0; if (!capture.video.codec->PrepareCompressFrame( codecFlags, format, (char *)pal, capture.video.buf, capture.video.bufSize)) goto skip_video; for (i=0;i> 1)*pitch); - else - srcLine=(data+(i >> 0)*pitch); + void * rowPointer; if (flags & CAPTURE_FLAG_DBLW) { + void *srcLine; Bitu x; Bitu countWidth = width >> 1; + if (flags & CAPTURE_FLAG_DBLH) + srcLine=(data+(i >> 1)*pitch); + else + srcLine=(data+(i >> 0)*pitch); switch ( bpp) { case 8: for (x=0;x> 1)*pitch); + else + rowPointer=(data+(i >> 0)*pitch); } - if (flags & CAPTURE_FLAG_DBLH) { - const void *rowPointer[2]; - rowPointer[0]=rowPointer[1]=srcLine; - capture.video.codec->CompressLines(2, rowPointer); - i++; - } else - capture.video.codec->CompressLines(1, &srcLine); + capture.video.codec->CompressLines( 1, &rowPointer ); } int written = capture.video.codec->FinishCompressFrame(); if (written < 0) goto skip_video; - if (capture.video.avi_out->AddVideo(capture.video.buf, written, codecFlags & 1 ? AVII_KEYFRAME : 0)) { -// LOG_MSG("Frame %d video %d audio %d",capture.video.avi_out->frames, written, capture.video.audioused *4 ); - if (!capture.video.audioused) return; - Bitu samples = capture.video.audioused; + CAPTURE_AddAviChunk( "00dc", written, capture.video.buf, codecFlags & 1 ? 0x10 : 0x0); + capture.video.frames++; +// LOG_MSG("Frame %d video %d audio %d",capture.video.frames, written, capture.video.audioused *4 ); + if ( capture.video.audioused ) { + CAPTURE_AddAviChunk( "01wb", capture.video.audioused * 4, capture.video.audiobuf, 0); + capture.video.audiowritten = capture.video.audioused*4; capture.video.audioused = 0; - if (capture.video.avi_out->AddAudio(capture.video.audiobuf, samples, capture.video.audiorate)) - return; - LOG_MSG("Failed to write audio data"); - } else LOG_MSG("Failed to write video data"); - } else return; + } + + /* Everything went okay, set flag again for next frame */ + CaptureState |= CAPTURE_VIDEO; + } skip_video: - /* something went wrong, shut it down */ - CAPTURE_VideoEvent(true); #endif return; } @@ -834,33 +590,14 @@ static Bit8u wavheader[]={ 0x0,0x0,0x0,0x0, /* Bit32u data size */ }; -void CAPTURE_AddWave(Bit32u freq, Bitu len, Bit16s * data) { -#if (C_SRECORD) +void CAPTURE_AddWave(Bit32u freq, Bit32u len, Bit16s * data) { +#if (C_SSHOT) if (CaptureState & CAPTURE_VIDEO) { Bitu left = WAVE_BUF - capture.video.audioused; - if (len > WAVE_BUF) - LOG_MSG("CAPTURE: WAVE_BUF too small"); - /* if framerate is very low (and audiorate is high) the audiobuffer may overflow */ - if (left < len && capture.video.avi_out) { - if (capture.video.avi_out->AddAudio(capture.video.audiobuf, capture.video.audioused, capture.video.audiorate)) { - capture.video.audioused = 0; - left = WAVE_BUF; - } - } if (left > len) left = len; - // samples are native format, need to be little endian - Bit16u *buf = (Bit16u*)capture.video.audiobuf[capture.video.audioused]; - Bit16u *read = (Bit16u*)data; + memcpy( &capture.video.audiobuf[capture.video.audioused], data, left*4); capture.video.audioused += left; -#ifdef WORDS_BIGENDIAN - do { - var_write(buf++, *read++); - var_write(buf++, *read++); - } while (--left); -#else - memcpy(buf, read, left*4); -#endif capture.video.audiorate = freq; } #endif @@ -876,7 +613,7 @@ void CAPTURE_AddWave(Bit32u freq, Bitu len, Bit16s * data) { capture.wave.freq = freq; fwrite(wavheader,1,sizeof(wavheader),capture.wave.handle); } - Bit16u * read = (Bit16u*)data; + Bit16s * read = data; while (len > 0 ) { Bitu left = WAVE_BUF - capture.wave.used; if (!left) { @@ -887,17 +624,10 @@ void CAPTURE_AddWave(Bit32u freq, Bitu len, Bit16s * data) { } if (left > len) left = len; - Bit16u *buf = capture.wave.buf[capture.wave.used]; + memcpy( &capture.wave.buf[capture.wave.used], read, left*4); capture.wave.used += left; + read += left*2; len -= left; -#ifdef WORDS_BIGENDIAN - do { - var_write(buf++, *read++); - var_write(buf++, *read++); - } while (--left); -#else - memcpy(buf, read, left*4); -#endif } } } @@ -1024,14 +754,12 @@ public: MAPPER_AddHandler(CAPTURE_MidiEvent,MK_f8,MMOD1|MMOD2,"caprawmidi","Cap MIDI"); #if (C_SSHOT) MAPPER_AddHandler(CAPTURE_ScreenShotEvent,MK_f5,MMOD1,"scrshot","Screenshot"); -#endif -#if (C_SRECORD) MAPPER_AddHandler(CAPTURE_VideoEvent,MK_f5,MMOD1|MMOD2,"video","Video"); #endif } ~HARDWARE(){ -#if (C_SRECORD) - if (capture.video.avi_out) CAPTURE_VideoEvent(true); +#if (C_SSHOT) + if (capture.video.handle) CAPTURE_VideoEvent(true); #endif if (capture.wave.handle) CAPTURE_WaveEvent(true); if (capture.midi.handle) CAPTURE_MidiEvent(true); diff --git a/src/libs/zmbv/zmbv.cpp b/src/libs/zmbv/zmbv.cpp index 5cbe77a6..dc2c0478 100644 --- a/src/libs/zmbv/zmbv.cpp +++ b/src/libs/zmbv/zmbv.cpp @@ -153,8 +153,7 @@ INLINE int VideoCodec::PossibleBlock(int vx,int vy,FrameBlock * block) { P * pnew=((P*)newframe)+block->start;; for (int y=0;ydy;y+=4) { for (int x=0;xdx;x+=4) { - int test=pold[x]-pnew[x]; - test |= -test; + int test=0-((pold[x]-pnew[x])&0x00ffffff); ret-=(test>>31); } pold+=pitch*4; @@ -170,8 +169,7 @@ INLINE int VideoCodec::CompareBlock(int vx,int vy,FrameBlock * block) { P * pnew=((P*)newframe)+block->start;; for (int y=0;ydy;y++) { for (int x=0;xdx;x++) { - int test=pold[x]-pnew[x]; - test |= -test; + int test=0-((pold[x]-pnew[x])&0x00ffffff); ret-=(test>>31); } pold+=pitch; @@ -316,7 +314,7 @@ bool VideoCodec::PrepareCompressFrame(int flags, zmbv_format_t _format, char * return true; } -void VideoCodec::CompressLines(int lineCount, const void *lineData[]) { +void VideoCodec::CompressLines(int lineCount, void *lineData[]) { int linePitch = pitch * pixelsize; int lineWidth = width * pixelsize; int i = 0; @@ -350,7 +348,7 @@ int VideoCodec::FinishCompressFrame( void ) { AddXorFrame(); break; case ZMBV_FORMAT_32BPP: - AddXorFrame(); + AddXorFrame(); break; } } @@ -471,7 +469,7 @@ bool VideoCodec::DecompressFrame(void * framedata, int size) { UnXorFrame(); break; case ZMBV_FORMAT_32BPP: - UnXorFrame(); + UnXorFrame(); break; } } @@ -526,10 +524,18 @@ void VideoCodec::Output_UpsideDown_24(void *output) { } void VideoCodec::FreeBuffers(void) { - delete[] blocks;blocks=NULL; - delete[] buf1;buf1=NULL; - delete[] buf2;buf2=NULL; - delete[] work;work=NULL; + if (blocks) { + delete[] blocks;blocks=0; + } + if (buf1) { + delete[] buf1;buf1=0; + } + if (buf2) { + delete[] buf2;buf2=0; + } + if (work) { + delete[] work;work=0; + } } @@ -541,7 +547,3 @@ VideoCodec::VideoCodec() { work = 0; memset( &zstream, 0, sizeof(zstream)); } - -VideoCodec::~VideoCodec() { - FreeBuffers(); -} diff --git a/src/libs/zmbv/zmbv.h b/src/libs/zmbv/zmbv.h index 6a46702b..7a6705c0 100644 --- a/src/libs/zmbv/zmbv.h +++ b/src/libs/zmbv/zmbv.h @@ -105,13 +105,12 @@ private: INLINE void CopyBlock(int vx, int vy,FrameBlock * block); public: VideoCodec(); - ~VideoCodec(); bool SetupCompress( int _width, int _height); bool SetupDecompress( int _width, int _height); zmbv_format_t BPPFormat( int bpp ); int NeededSize( int _width, int _height, zmbv_format_t _format); - void CompressLines(int lineCount, const void *lineData[]); + void CompressLines(int lineCount, void *lineData[]); bool PrepareCompressFrame(int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize); int FinishCompressFrame( void ); bool DecompressFrame(void * framedata, int size); diff --git a/src/platform/visualc/config.h b/src/platform/visualc/config.h index fbdd1b14..cd95e71f 100644 --- a/src/platform/visualc/config.h +++ b/src/platform/visualc/config.h @@ -8,8 +8,6 @@ /* Define to 1 to enable screenshots, requires libpng */ #define C_SSHOT 1 -/* Define to 1 to enable movie recording, requires zlib built without Z_SOLO */ -#define C_SRECORD 1 /* Define to 1 to use opengl display output support */ #define C_OPENGL 1