From b78003dc008a98a853da83fc8efa5b9aeee51952 Mon Sep 17 00:00:00 2001 From: mjbudd77 Date: Wed, 28 Apr 2021 21:40:46 -0400 Subject: [PATCH] Initial add of avi recording code for Qt GUI. --- src/CMakeLists.txt | 7 +- src/drivers/Qt/AviRecord.cpp | 58 ++++ src/drivers/Qt/AviRecord.h | 9 + src/drivers/Qt/ConsoleWindow.cpp | 15 + src/drivers/Qt/ConsoleWindow.h | 1 + src/drivers/Qt/avi/.gwavi.h.swp | Bin 0 -> 20480 bytes src/drivers/Qt/avi/avi-utils.cpp | 560 +++++++++++++++++++++++++++++++ src/drivers/Qt/avi/fileio.cpp | 91 +++++ src/drivers/Qt/avi/gwavi.cpp | 503 +++++++++++++++++++++++++++ src/drivers/Qt/avi/gwavi.h | 168 ++++++++++ src/drivers/Qt/fceuWrapper.cpp | 4 +- src/drivers/Qt/sdl-video.cpp | 3 +- 12 files changed, 1414 insertions(+), 5 deletions(-) create mode 100644 src/drivers/Qt/AviRecord.cpp create mode 100644 src/drivers/Qt/AviRecord.h create mode 100644 src/drivers/Qt/avi/.gwavi.h.swp create mode 100644 src/drivers/Qt/avi/avi-utils.cpp create mode 100644 src/drivers/Qt/avi/fileio.cpp create mode 100644 src/drivers/Qt/avi/gwavi.cpp create mode 100644 src/drivers/Qt/avi/gwavi.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8c5b685f..da1b8811 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -436,8 +436,7 @@ set(SRC_DRIVERS_COMMON ${CMAKE_CURRENT_SOURCE_DIR}/drivers/common/vidblit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/common/os_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/common/nes_ntsc.c - #${CMAKE_CURRENT_SOURCE_DIR}/drivers/videolog/nesvideos-piece.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/drivers/videolog/rgbtorgb.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/videolog/rgbtorgb.cpp ) set(SRC_DRIVERS_SDL @@ -483,6 +482,10 @@ set(SRC_DRIVERS_SDL ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/sdl-joystick.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/sdl-throttle.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/unix-netplay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/AviRecord.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/avi/avi-utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/avi/fileio.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/avi/gwavi.cpp ) set(SOURCES ${SRC_CORE} ${SRC_DRIVERS_COMMON} ${SRC_DRIVERS_SDL}) diff --git a/src/drivers/Qt/AviRecord.cpp b/src/drivers/Qt/AviRecord.cpp new file mode 100644 index 00000000..776fdaf8 --- /dev/null +++ b/src/drivers/Qt/AviRecord.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#include "Qt/AviRecord.h" +#include "Qt/avi/gwavi.h" +#include "Qt/nes_shm.h" + +static gwavi_t *gwavi = NULL; +static bool recordEnable = false; +//************************************************************************************** +int aviRecordOpenFile( const char *filepath, int format, int width, int height ) +{ + if ( gwavi != NULL ) + { + delete gwavi; gwavi = NULL; + } + + gwavi = new gwavi_t(); + + recordEnable = true; + return 0; +} +//************************************************************************************** +int aviRecordAddFrame( void ) +{ + if ( !recordEnable ) + { + return -1; + } + + if ( gwavi == NULL ) + { + return -1; + } + int numPixels, bufferSize; + + numPixels = nes_shm->video.ncol * nes_shm->video.nrow; + bufferSize = numPixels * sizeof(uint32_t); + + //gwavi-> + + return 0; +} +//************************************************************************************** +int aviRecordClose(void) +{ + recordEnable = false; + + if ( gwavi != NULL ) + { + delete gwavi; gwavi = NULL; + } + + return 0; +} +//************************************************************************************** diff --git a/src/drivers/Qt/AviRecord.h b/src/drivers/Qt/AviRecord.h new file mode 100644 index 00000000..93d2dea0 --- /dev/null +++ b/src/drivers/Qt/AviRecord.h @@ -0,0 +1,9 @@ +// AviRecord.h +// + +int aviRecordOpenFile( const char *filepath, int format, int width, int height ); + +int aviRecordAddFrame( void ); + +int aviRecordClose(void); + diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 3f79ba76..7474bab2 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -63,6 +63,7 @@ #include "Qt/PaletteConf.h" #include "Qt/PaletteEditor.h" #include "Qt/GuiConf.h" +#include "Qt/AviRecord.h" #include "Qt/MoviePlay.h" #include "Qt/MovieOptions.h" #include "Qt/TimingConf.h" @@ -1446,6 +1447,15 @@ void consoleWin_t::createMainMenu(void) movieMenu->addAction(recAsMovAct); + // Movie -> Avi Record + act = new QAction(tr("A&VI Record"), this); + //act->setShortcut( QKeySequence(tr("Shift+F5"))); + act->setCheckable(true); + act->setStatusTip(tr("AVI Record")); + connect(act, SIGNAL(triggered()), this, SLOT(aviOpen(void)) ); + + movieMenu->addAction(act); + //----------------------------------------------------------------------- // Help helpMenu = menubar->addMenu(tr("&Help")); @@ -3084,6 +3094,11 @@ void consoleWin_t::recordMovieAs(void) return; } +void consoleWin_t::aviOpen(void) +{ + aviRecordOpenFile( NULL, 0, 256, 240 ); +} + void consoleWin_t::aboutFCEUX(void) { AboutWindow *aboutWin; diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index fcb78acc..94596cdb 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -357,6 +357,7 @@ class consoleWin_t : public QMainWindow void mainMenuOpen(void); void mainMenuClose(void); void warnAmbiguousShortcut( QShortcut*); + void aviOpen(void); }; diff --git a/src/drivers/Qt/avi/.gwavi.h.swp b/src/drivers/Qt/avi/.gwavi.h.swp new file mode 100644 index 0000000000000000000000000000000000000000..e87ba81e1fcdefb266a2746e3688243794f44102 GIT binary patch literal 20480 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 +#include + +#include "Qt/avi/gwavi.h" + +int +gwavi_t::write_avi_header(FILE *out, struct gwavi_header_t *avi_header) +{ + long marker, t; + + if (write_chars_bin(out, "avih", 4) == -1) { + (void)fprintf(stderr, "write_avi_header: write_chars_bin() " + "failed\n"); + return -1; + } + if ((marker = ftell(out)) == -1) { + perror("write_avi_header (ftell)"); + return -1; + } + if (write_int(out, 0) == -1) + goto write_int_failed; + + if (write_int(out, avi_header->time_delay) == -1) + goto write_int_failed; + if (write_int(out, avi_header->data_rate) == -1) + goto write_int_failed; + if (write_int(out, avi_header->reserved) == -1) + goto write_int_failed; + /* dwFlags */ + if (write_int(out, avi_header->flags) == -1) + goto write_int_failed; + /* dwTotalFrames */ + if (write_int(out, avi_header->number_of_frames) == -1) + goto write_int_failed; + if (write_int(out, avi_header->initial_frames) == -1) + goto write_int_failed; + if (write_int(out, avi_header->data_streams) == -1) + goto write_int_failed; + if (write_int(out, avi_header->buffer_size) == -1) + goto write_int_failed; + if (write_int(out, avi_header->width) == -1) + goto write_int_failed; + if (write_int(out, avi_header->height) == -1) + goto write_int_failed; + if (write_int(out, avi_header->time_scale) == -1) + goto write_int_failed; + if (write_int(out, avi_header->playback_data_rate) == -1) + goto write_int_failed; + if (write_int(out, avi_header->starting_time) == -1) + goto write_int_failed; + if (write_int(out, avi_header->data_length) == -1) + goto write_int_failed; + + if ((t = ftell(out)) == -1) { + perror("write_avi_header (ftell)"); + return -1; + } + if (fseek(out, marker, SEEK_SET) == -1) { + perror("write_avi_header (fseek)"); + return -1; + } + if (write_int(out, (unsigned int)(t - marker - 4)) == -1) + goto write_int_failed; + if (fseek(out, t, SEEK_SET) == -1) { + perror("write_avi_header (fseek)"); + return -1; + } + + return 0; + +write_int_failed: + (void)fprintf(stderr, "write_avi_header: write_int() failed\n"); + return -1; +} + +int +gwavi_t::write_stream_header(FILE *out, struct gwavi_stream_header_t *stream_header) +{ + long marker, t; + + if (write_chars_bin(out, "strh", 4) == -1) + goto write_chars_bin_failed; + if ((marker = ftell(out)) == -1) { + perror("write_stream_header (ftell)"); + return -1; + } + if (write_int(out, 0) == -1) + goto write_int_failed; + + if (write_chars_bin(out, stream_header->data_type, 4) == -1) + goto write_chars_bin_failed; + if (write_chars_bin(out, stream_header->codec, 4) == -1) + goto write_chars_bin_failed; + if (write_int(out, stream_header->flags) == -1) + goto write_int_failed; + if (write_int(out, stream_header->priority) == -1) + goto write_int_failed; + if (write_int(out, stream_header->initial_frames) == -1) + goto write_int_failed; + if (write_int(out, stream_header->time_scale) == -1) + goto write_int_failed; + if (write_int(out, stream_header->data_rate) == -1) + goto write_int_failed; + if (write_int(out, stream_header->start_time) == -1) + goto write_int_failed; + if (write_int(out, stream_header->data_length) == -1) + goto write_int_failed; + if (write_int(out, stream_header->buffer_size) == -1) + goto write_int_failed; + if (write_int(out, stream_header->video_quality) == -1) + goto write_int_failed; + if (write_int(out, stream_header->sample_size) == -1) + goto write_int_failed; + if (write_int(out, 0) == -1) + goto write_int_failed; + if (write_int(out, 0) == -1) + goto write_int_failed; + + if ((t = ftell(out)) == -1) { + perror("write_stream_header (ftell)"); + return -1; + } + if (fseek(out, marker, SEEK_SET) == -1) { + perror("write_stream_header (fseek)"); + return -1; + } + write_int(out, (unsigned int)(t - marker - 4)); + if (fseek(out, t, SEEK_SET) == -1){ + perror("write_stream_header (fseek)"); + return -1; + } + + return 0; + +write_int_failed: + (void)fprintf(stderr, "write_stream_header: write_int() failed\n"); + return -1; + +write_chars_bin_failed: + (void)fprintf(stderr, "write_stream_header: write_chars_bin() failed\n"); + return -1; +} + +int +gwavi_t::write_stream_format_v(FILE *out, struct gwavi_stream_format_v_t *stream_format_v) +{ + long marker,t; + unsigned int i; + + if (write_chars_bin(out, "strf", 4) == -1) { + (void)fprintf(stderr, "write_stream_format_v: write_chars_bin()" + " failed\n"); + return -1; + } + if ((marker = ftell(out)) == -1) { + perror("write_stream_format_v (ftell)"); + return -1; + } + if (write_int(out, 0) == -1) + goto write_int_failed; + + if (write_int(out, stream_format_v->header_size) == -1) + goto write_int_failed; + if (write_int(out, stream_format_v->width) == -1) + goto write_int_failed; + if (write_int(out, stream_format_v->height) == -1) + goto write_int_failed; + if (write_short(out, stream_format_v->num_planes) == -1) { + (void)fprintf(stderr, "write_stream_format_v: write_short() " + "failed\n"); + return -1; + } + if (write_short(out, stream_format_v->bits_per_pixel) == -1) { + (void)fprintf(stderr, "write_stream_format_v: write_short() " + "failed\n"); + return -1; + } + if (write_int(out, stream_format_v->compression_type) == -1) + goto write_int_failed; + if (write_int(out, stream_format_v->image_size) == -1) + goto write_int_failed; + if (write_int(out, stream_format_v->x_pels_per_meter) == -1) + goto write_int_failed; + if (write_int(out, stream_format_v->y_pels_per_meter) == -1) + goto write_int_failed; + if (write_int(out, stream_format_v->colors_used) == -1) + goto write_int_failed; + if (write_int(out, stream_format_v->colors_important) == -1) + goto write_int_failed; + + if (stream_format_v->colors_used != 0) + for (i = 0; i < stream_format_v->colors_used; i++) { + if (fputc(stream_format_v->palette[i] & 255, out) + == EOF) + goto fputc_failed; + if (fputc((stream_format_v->palette[i] >> 8) & 255, out) + == EOF) + goto fputc_failed; + if (fputc((stream_format_v->palette[i] >> 16) & 255, out) + == EOF) + goto fputc_failed; + if (fputc(0, out) == EOF) + goto fputc_failed; + } + + if ((t = ftell(out)) == -1) { + perror("write_stream_format_v (ftell)"); + return -1; + } + if (fseek(out,marker,SEEK_SET) == -1) { + perror("write_stream_format_v (fseek)"); + return -1; + } + if (write_int(out, (unsigned int)(t - marker - 4)) == -1) + goto write_int_failed; + if (fseek(out, t, SEEK_SET) == -1) { + perror("write_stream_format_v (fseek)"); + return -1; + } + + return 0; + +write_int_failed: + (void)fprintf(stderr, "write_stream_format_v: write_int() failed\n"); + return -1; + +fputc_failed: + (void)fprintf(stderr, "write_stream_format_v: fputc() failed\n"); + return -1; +} + +int +gwavi_t::write_stream_format_a(FILE *out, struct gwavi_stream_format_a_t *stream_format_a) +{ + long marker, t; + + if (write_chars_bin(out, "strf", 4) == -1) { + (void)fprintf(stderr, "write_stream_format_a: write_chars_bin()" + " failed\n"); + return -1; + } + if ((marker = ftell(out)) == -1) { + perror("write_stream_format_a (ftell)"); + return -1; + } + if (write_int(out, 0) == -1) + goto write_int_failed; + + if (write_short(out, stream_format_a->format_type) == -1) + goto write_short_failed; + if (write_short(out, stream_format_a->channels) == -1) + goto write_short_failed; + if (write_int(out, stream_format_a->sample_rate) == -1) + goto write_int_failed; + if (write_int(out, stream_format_a->bytes_per_second) == -1) + goto write_int_failed; + if (write_short(out, stream_format_a->block_align) == -1) + goto write_short_failed; + if (write_short(out, stream_format_a->bits_per_sample) == -1) + goto write_short_failed; + if (write_short(out, stream_format_a->size) == -1) + goto write_short_failed; + + if ((t = ftell(out)) == -1) { + perror("write_stream_format_a (ftell)"); + return -1; + } + if (fseek(out, marker, SEEK_SET) == -1) { + perror("write_stream_format_a (fseek)"); + return -1; + } + if (write_int(out, (unsigned int)(t - marker - 4)) == -1) + goto write_int_failed; + if (fseek(out, t, SEEK_SET) == -1) { + perror("write_stream_format_a (fseek)"); + return -1; + } + + return 0; + +write_int_failed: + (void)fprintf(stderr, "write_stream_format_a: write_int() failed\n"); + return -1; + +write_short_failed: + (void)fprintf(stderr, "write_stream_format_a: write_short() failed\n"); + return -1; +} + +int +gwavi_t::write_avi_header_chunk(void) +{ + long marker, t; + long sub_marker; + + if (write_chars_bin(out, "LIST", 4) == -1) + goto write_chars_bin_failed; + if ((marker = ftell(out)) == -1) + goto ftell_failed; + if (write_int(out, 0) == -1) + goto write_int_failed; + if (write_chars_bin(out, "hdrl", 4) == -1) + goto write_chars_bin_failed; + if (write_avi_header(out, &avi_header) == -1) { + (void)fprintf(stderr, "write_avi_header_chunk: " + "write_avi_header() failed\n"); + return -1; + } + + if (write_chars_bin(out, "LIST", 4) == -1) + goto write_chars_bin_failed; + if ((sub_marker = ftell(out)) == -1) + goto ftell_failed; + if (write_int(out, 0) == -1) + goto write_int_failed; + if (write_chars_bin(out, "strl", 4) == -1) + goto write_chars_bin_failed; + if (write_stream_header(out, &stream_header_v) == -1) { + (void)fprintf(stderr, "write_avi_header_chunk: " + "write_stream_header failed\n"); + return -1; + } + if (write_stream_format_v(out, &stream_format_v) == -1) { + (void)fprintf(stderr, "write_avi_header_chunk: " + "write_stream_format_v failed\n"); + return -1; + } + + if ((t = ftell(out)) == -1) + goto ftell_failed; + + if (fseek(out, sub_marker, SEEK_SET) == -1) + goto fseek_failed; + if (write_int(out, (unsigned int)(t - sub_marker - 4)) == -1) + goto write_int_failed; + if (fseek(out, t, SEEK_SET) == -1) + goto fseek_failed; + + if (avi_header.data_streams == 2) { + if (write_chars_bin(out, "LIST", 4) == -1) + goto write_chars_bin_failed; + if ((sub_marker = ftell(out)) == -1) + goto ftell_failed; + if (write_int(out, 0) == -1) + goto write_int_failed; + if (write_chars_bin(out, "strl", 4) == -1) + goto write_chars_bin_failed; + if (write_stream_header(out, &stream_header_a) == -1) { + (void)fprintf(stderr, "write_avi_header_chunk: " + "write_stream_header failed\n"); + return -1; + } + if (write_stream_format_a(out, &stream_format_a) == -1) { + (void)fprintf(stderr, "write_avi_header_chunk: " + "write_stream_format_a failed\n"); + return -1; + } + + if ((t = ftell(out)) == -1) + goto ftell_failed; + if (fseek(out, sub_marker, SEEK_SET) == -1) + goto fseek_failed; + if (write_int(out, (unsigned int)(t - sub_marker - 4)) == -1) + goto write_int_failed; + if (fseek(out, t, SEEK_SET) == -1) + goto fseek_failed; + } + + if ((t = ftell(out)) == -1) + goto ftell_failed; + if (fseek(out, marker, SEEK_SET) == -1) + goto fseek_failed; + if (write_int(out, (unsigned int)(t - marker - 4)) == -1) + goto write_int_failed; + if (fseek(out, t, SEEK_SET) == -1) + goto fseek_failed; + + return 0; + +ftell_failed: + perror("write_avi_header_chunk (ftell)"); + return -1; + +fseek_failed: + perror("write_avi_header_chunk (fseek)"); + return -1; + +write_int_failed: + (void)fprintf(stderr, "write_avi_header_chunk: write_int() failed\n"); + return -1; + +write_chars_bin_failed: + (void)fprintf(stderr, "write_avi_header_chunk: write_chars_bin() failed\n"); + return -1; +} + +int +gwavi_t::write_index(FILE *out, int count, unsigned int *offsets) +{ + long marker, t; + unsigned int offset = 4; + + if (offsets == 0) + return -1; + + if (write_chars_bin(out, "idx1", 4) == -1) { + (void)fprintf(stderr, "write_index: write_chars_bin) failed\n"); + return -1; + } + if ((marker = ftell(out)) == -1) { + perror("write_index (ftell)"); + return -1; + } + if (write_int(out, 0) == -1) + goto write_int_failed; + + for (t = 0; t < count; t++) { + if ((offsets[t] & 0x80000000) == 0) + write_chars(out, "00dc"); + else { + write_chars(out, "01wb"); + offsets[t] &= 0x7fffffff; + } + if (write_int(out, 0x10) == -1) + goto write_int_failed; + if (write_int(out, offset) == -1) + goto write_int_failed; + if (write_int(out, offsets[t]) == -1) + goto write_int_failed; + + offset = offset + offsets[t] + 8; + } + + if ((t = ftell(out)) == -1) { + perror("write_index (ftell)"); + return -1; + } + if (fseek(out, marker, SEEK_SET) == -1) { + perror("write_index (fseek)"); + return -1; + } + if (write_int(out, (unsigned int)(t - marker - 4)) == -1) + goto write_int_failed; + if (fseek(out, t, SEEK_SET) == -1) { + perror("write_index (fseek)"); + return -1; + } + + return 0; + +write_int_failed: + (void)fprintf(stderr, "write_index: write_int() failed\n"); + return -1; +} + +/** + * Return 0 if fourcc is valid, 1 non-valid or -1 in case of errors. + */ +int +gwavi_t::check_fourcc(const char *fourcc) +{ + int ret = 0; + /* list of fourccs from http://fourcc.org/codecs.php */ + const char valid_fourcc[] = + "3IV1 3IV2 8BPS" + "AASC ABYR ADV1 ADVJ AEMI AFLC AFLI AJPG AMPG ANIM AP41 ASLC" + "ASV1 ASV2 ASVX AUR2 AURA AVC1 AVRN" + "BA81 BINK BLZ0 BT20 BTCV BW10 BYR1 BYR2" + "CC12 CDVC CFCC CGDI CHAM CJPG CMYK CPLA CRAM CSCD CTRX CVID" + "CWLT CXY1 CXY2 CYUV CYUY" + "D261 D263 DAVC DCL1 DCL2 DCL3 DCL4 DCL5 DIV3 DIV4 DIV5 DIVX" + "DM4V DMB1 DMB2 DMK2 DSVD DUCK DV25 DV50 DVAN DVCS DVE2 DVH1" + "DVHD DVSD DVSL DVX1 DVX2 DVX3 DX50 DXGM DXTC DXTN" + "EKQ0 ELK0 EM2V ES07 ESCP ETV1 ETV2 ETVC" + "FFV1 FLJP FMP4 FMVC FPS1 FRWA FRWD FVF1" + "GEOX GJPG GLZW GPEG GWLT" + "H260 H261 H262 H263 H264 H265 H266 H267 H268 H269" + "HDYC HFYU HMCR HMRR" + "I263 ICLB IGOR IJPG ILVC ILVR IPDV IR21 IRAW ISME" + "IV30 IV31 IV32 IV33 IV34 IV35 IV36 IV37 IV38 IV39 IV40 IV41" + "IV41 IV43 IV44 IV45 IV46 IV47 IV48 IV49 IV50" + "JBYR JPEG JPGL" + "KMVC" + "L261 L263 LBYR LCMW LCW2 LEAD LGRY LJ11 LJ22 LJ2K LJ44 LJPG" + "LMP2 LMP4 LSVC LSVM LSVX LZO1" + "M261 M263 M4CC M4S2 MC12 MCAM MJ2C MJPG MMES MP2A MP2T MP2V" + "MP42 MP43 MP4A MP4S MP4T MP4V MPEG MPG4 MPGI MR16 MRCA MRLE" + "MSVC MSZH" + "MTX1 MTX2 MTX3 MTX4 MTX5 MTX6 MTX7 MTX8 MTX9" + "MVI1 MVI2 MWV1" + "NAVI NDSC NDSM NDSP NDSS NDXC NDXH NDXP NDXS NHVU NTN1 NTN2" + "NVDS NVHS" + "NVS0 NVS1 NVS2 NVS3 NVS4 NVS5" + "NVT0 NVT1 NVT2 NVT3 NVT4 NVT5" + "PDVC PGVV PHMO PIM1 PIM2 PIMJ PIXL PJPG PVEZ PVMM PVW2" + "QPEG QPEQ" + "RGBT RLE RLE4 RLE8 RMP4 RPZA RT21 RV20 RV30 RV40 S422 SAN3" + "SDCC SEDG SFMC SMP4 SMSC SMSD SMSV SP40 SP44 SP54 SPIG SQZ2" + "STVA STVB STVC STVX STVY SV10 SVQ1 SVQ3" + "TLMS TLST TM20 TM2X TMIC TMOT TR20 TSCC TV10 TVJP TVMJ TY0N" + "TY2C TY2N" + "UCOD ULTI" + "V210 V261 V655 VCR1 VCR2 VCR3 VCR4 VCR5 VCR6 VCR7 VCR8 VCR9" + "VDCT VDOM VDTZ VGPX VIDS VIFP VIVO VIXL VLV1 VP30 VP31 VP40" + "VP50 VP60 VP61 VP62 VP70 VP80 VQC1 VQC2 VQJC VSSV VUUU VX1K" + "VX2K VXSP VYU9 VYUY" + "WBVC WHAM WINX WJPG WMV1 WMV2 WMV3 WMVA WNV1 WVC1" + "X263 X264 XLV0 XMPG XVID" + "XWV0 XWV1 XWV2 XWV3 XWV4 XWV5 XWV6 XWV7 XWV8 XWV9" + "XXAN" + "Y16 Y411 Y41P Y444 Y8 YC12 YUV8 YUV9 YUVP YUY2 YUYV YV12 YV16" + "YV92" + "ZLIB ZMBV ZPEG ZYGO ZYYY"; + + if (!fourcc) { + (void)fputs("fourcc cannot be NULL", stderr); + return -1; + } + if (strchr(fourcc, ' ') || !strstr(valid_fourcc, fourcc)) + ret = 1; + + return ret; +} diff --git a/src/drivers/Qt/avi/fileio.cpp b/src/drivers/Qt/avi/fileio.cpp new file mode 100644 index 00000000..013b7875 --- /dev/null +++ b/src/drivers/Qt/avi/fileio.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2008-2011, Michael Kohn + * Copyright (c) 2013, Robin Hahling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Usefull IO functions. + */ + +#include + +#include "gwavi.h" + +int +gwavi_t::write_int(FILE *out, unsigned int n) +{ + unsigned char buffer[4]; + + buffer[0] = n; + buffer[1] = n >> 8; + buffer[2] = n >> 16; + buffer[3] = n >> 24; + + if (fwrite(buffer, 1, 4, out) != 4) + return -1; + + return 0; +} + +int +gwavi_t::write_short(FILE *out, unsigned int n) +{ + unsigned char buffer[2]; + + buffer[0] = n; + buffer[1] = n >> 8; + + if (fwrite(buffer, 1, 2, out) != 2) + return -1; + + return 0; +} + +int +gwavi_t::write_chars(FILE *out, const char *s) +{ + int t = 0; + + while(s[t] != 0 && t < 255) + if (fputc(s[t++], out) == EOF) + return -1; + + return 0; +} + +int +gwavi_t::write_chars_bin(FILE *out, const char *s, int count) +{ + if (fwrite(s, 1, count, out) != count) + return -1; + + return 0; +} + diff --git a/src/drivers/Qt/avi/gwavi.cpp b/src/drivers/Qt/avi/gwavi.cpp new file mode 100644 index 00000000..e22b47fb --- /dev/null +++ b/src/drivers/Qt/avi/gwavi.cpp @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2008-2011, Michael Kohn + * Copyright (c) 2013, Robin Hahling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is the file containing gwavi library functions. + */ + +#include +#include +#include +#include + +#include "gwavi.h" + +/** + * This is the first function you should call when using gwavi library. + * It allocates memory for a gwavi_t structure and returns it and takes care of + * initializing the AVI header with the provided information. + * + * When you're done creating your AVI file, you should call gwavi_close() + * function to free memory allocated for the gwavi_t structure and properly + * close the output file. + * + * @param filename This is the name of the AVI file which will be generated by + * this library. + * @param width Width of a frame. + * @param height Height of a frame. + * @param fourcc FourCC representing the codec of the video encoded stream. a + * FourCC is a sequence of four chars used to uniquely identify data formats. + * For more information, you can visit www.fourcc.org. + * @param fps Number of frames per second of your video. It needs to be > 0. + * @param audio This parameter is optionnal. It is used for the audio track. If + * you do not want to add an audio track to your AVI file, simply pass NULL for + * this argument. + * + * @return Structure containing required information in order to create the AVI + * file. If an error occured, NULL is returned. + */ + +gwavi_t::gwavi_t(void) +{ + out = NULL; + memset( &avi_header , 0, sizeof(struct gwavi_header_t) ); + memset( &stream_header_v, 0, sizeof(struct gwavi_stream_header_t) ); + memset( &stream_format_v, 0, sizeof(struct gwavi_stream_format_v_t) ); + memset( &stream_header_a, 0, sizeof(struct gwavi_stream_header_t) ); + memset( &stream_format_a, 0, sizeof(struct gwavi_stream_format_a_t) ); + marker = 0; + offsets_ptr = 0; + offsets_len = 0; + offsets_start = 0; + offsets = 0; + offset_count = 0; +} + + +int +gwavi_t::open(const char *filename, unsigned int width, unsigned int height, + const char *fourcc, unsigned int fps, struct gwavi_audio_t *audio) +{ + if (check_fourcc(fourcc) != 0) + (void)fprintf(stderr, "WARNING: given fourcc does not seem to " + "be valid: %s\n", fourcc); + if (fps < 1) + { + return -1; + } + if ((out = fopen(filename, "wb+")) == NULL) + { + perror("gwavi_open: failed to open file for writing"); + return -1; + } + + /* set avi header */ + avi_header.time_delay= 1000000 / fps; + avi_header.data_rate = width * height * 3; + avi_header.flags = 0x10; + + if (audio) + avi_header.data_streams = 2; + else + avi_header.data_streams = 1; + + /* this field gets updated when calling gwavi_close() */ + avi_header.number_of_frames = 0; + avi_header.width = width; + avi_header.height = height; + avi_header.buffer_size = (width * height * 3); + + /* set stream header */ + (void)strcpy(stream_header_v.data_type, "vids"); + (void)memcpy(stream_header_v.codec, fourcc, 4); + stream_header_v.time_scale = 1; + stream_header_v.data_rate = fps; + stream_header_v.buffer_size = (width * height * 3); + stream_header_v.data_length = 0; + + /* set stream format */ + stream_format_v.header_size = 40; + stream_format_v.width = width; + stream_format_v.height = height; + stream_format_v.num_planes = 1; + stream_format_v.bits_per_pixel = 24; + stream_format_v.compression_type = + ((unsigned int)fourcc[3] << 24) + + ((unsigned int)fourcc[2] << 16) + + ((unsigned int)fourcc[1] << 8) + + ((unsigned int)fourcc[0]); + stream_format_v.image_size = width * height * 3; + stream_format_v.colors_used = 0; + stream_format_v.colors_important = 0; + + stream_format_v.palette = 0; + stream_format_v.palette_count = 0; + + if (audio) + { + /* set stream header */ + memcpy(stream_header_a.data_type, "auds", 4); + stream_header_a.codec[0] = 1; + stream_header_a.codec[1] = 0; + stream_header_a.codec[2] = 0; + stream_header_a.codec[3] = 0; + stream_header_a.time_scale = 1; + stream_header_a.data_rate = audio->samples_per_second; + stream_header_a.buffer_size = + audio->channels * (audio->bits / 8) * audio->samples_per_second; + /* when set to -1, drivers use default quality value */ + stream_header_a.audio_quality = -1; + stream_header_a.sample_size = + (audio->bits / 8) * audio->channels; + + /* set stream format */ + stream_format_a.format_type = 1; + stream_format_a.channels = audio->channels; + stream_format_a.sample_rate = audio->samples_per_second; + stream_format_a.bytes_per_second = + audio->channels * (audio->bits / 8) * audio->samples_per_second; + stream_format_a.block_align = + audio->channels * (audio->bits / 8); + stream_format_a.bits_per_sample = audio->bits; + stream_format_a.size = 0; + } + + if (write_chars_bin(out, "RIFF", 4) == -1) + goto write_chars_bin_failed; + if (write_int(out, 0) == -1) { + (void)fprintf(stderr, "gwavi_info: write_int() failed\n"); + return -1; + } + if (write_chars_bin(out, "AVI ", 4) == -1) + goto write_chars_bin_failed; + + if (write_avi_header_chunk() == -1) { + (void)fprintf(stderr, "gwavi_info: write_avi_header_chunk " + "failed\n"); + return -1; + } + + if (write_chars_bin(out, "LIST", 4) == -1) + goto write_chars_bin_failed; + if ((marker = ftell(out)) == -1) { + perror("gwavi_info (ftell)"); + return -1; + } + if (write_int(out, 0) == -1) { + (void)fprintf(stderr, "gwavi_info: write_int() failed\n"); + return -1; + } + if (write_chars_bin(out, "movi", 4) == -1) + goto write_chars_bin_failed; + + offsets_len = 1024; + if ((offsets = (unsigned int *)malloc((size_t)offsets_len * + sizeof(unsigned int))) + == NULL) { + (void)fprintf(stderr, "gwavi_info: could not allocate memory " + "for gwavi offsets table\n"); + return -1; + } + + offsets_ptr = 0; + + return 0; + +write_chars_bin_failed: + (void)fprintf(stderr, "gwavi_open: write_chars_bin() failed\n"); + return -1; +} + +/** + * This function allows you to add an encoded video frame to the AVI file. + * + * @param gwavi Main gwavi structure initialized with gwavi_open()- + * @param buffer Video buffer size. + * @param len Video buffer length. + * + * @return 0 on success, -1 on error. + */ +int +gwavi_t::add_frame( unsigned char *buffer, size_t len) +{ + size_t maxi_pad; /* if your frame is raggin, give it some paddin' */ + size_t t; + + if ( !buffer) { + (void)fputs("gwavi and/or buffer argument cannot be NULL", + stderr); + return -1; + } + if (len < 256) + (void)fprintf(stderr, "WARNING: specified buffer len seems " + "rather small: %d. Are you sure about this?\n", + (int)len); + + offset_count++; + stream_header_v.data_length++; + + maxi_pad = len % 4; + if (maxi_pad > 0) + maxi_pad = 4 - maxi_pad; + + if (offset_count >= offsets_len) { + offsets_len += 1024; + offsets = (unsigned int *)realloc(offsets, + (size_t)offsets_len * + sizeof(unsigned int)); + } + + offsets[offsets_ptr++] = (unsigned int)(len + maxi_pad); + + if (write_chars_bin(out, "00dc", 4) == -1) { + (void)fprintf(stderr, "gwavi_add_frame: write_chars_bin() " + "failed\n"); + return -1; + } + if (write_int(out, (unsigned int)(len + maxi_pad)) == -1) { + (void)fprintf(stderr, "gwavi_add_frame: write_int() failed\n"); + return -1; + } + + if ((t = fwrite(buffer, 1, len, out)) != len) { + (void)fprintf(stderr, "gwavi_add_frame: fwrite() failed\n"); + return -1; + } + + for (t = 0; t < maxi_pad; t++) + { + if (fputc(0, out) == EOF) { + (void)fprintf(stderr, "gwavi_add_frame: fputc() failed\n"); + return -1; + } + } + + return 0; +} + +/** + * This function allows you to add the audio track to your AVI file. + * + * @param gwavi Main gwavi structure initialized with gwavi_open()- + * @param buffer Audio buffer size. + * @param len Audio buffer length. + * + * @return 0 on success, -1 on error. + */ +int +gwavi_t::add_audio( unsigned char *buffer, size_t len) +{ + size_t maxi_pad; /* in case audio bleeds over the 4 byte boundary */ + size_t t; + + if ( !buffer) { + (void)fputs("gwavi and/or buffer argument cannot be NULL", + stderr); + return -1; + } + + offset_count++; + + maxi_pad = len % 4; + if (maxi_pad > 0) + maxi_pad = 4 - maxi_pad; + + if (offset_count >= offsets_len) { + offsets_len += 1024; + offsets = (unsigned int *)realloc(offsets, + (size_t)offsets_len * + sizeof(unsigned int)); + } + + offsets[offsets_ptr++] = + (unsigned int)((len + maxi_pad) | 0x80000000); + + if (write_chars_bin(out,"01wb",4) == -1) { + (void)fprintf(stderr, "gwavi_add_audio: write_chars_bin() " + "failed\n"); + return -1; + } + if (write_int(out,(unsigned int)(len + maxi_pad)) == -1) { + (void)fprintf(stderr, "gwavi_add_audio: write_int() failed\n"); + return -1; + } + + if ((t = fwrite(buffer, 1, len, out)) != len ) { + (void)fprintf(stderr, "gwavi_add_audio: fwrite() failed\n"); + return -1; + } + + for (t = 0; t < maxi_pad; t++) + if (fputc(0,out) == EOF) { + (void)fprintf(stderr, "gwavi_add_audio: fputc() failed\n"); + return -1; + } + + stream_header_a.data_length += (unsigned int)(len + maxi_pad); + + return 0; +} + +/** + * This function should be called when the program is done adding video and/or + * audio frames to the AVI file. It frees memory allocated for gwavi_open() for + * the main gwavi_t structure. It also properly closes the output file. + * + * @param gwavi Main gwavi structure initialized with gwavi_open()- + * + * @return 0 on success, -1 on error. + */ +int +gwavi_t::close(void) +{ + long t; + + if ((t = ftell(out)) == -1) + goto ftell_failed; + if (fseek(out, marker, SEEK_SET) == -1) + goto fseek_failed; + if (write_int(out, (unsigned int)(t - marker - 4)) == -1) { + (void)fprintf(stderr, "gwavi_close: write_int() failed\n"); + return -1; + } + if (fseek(out,t,SEEK_SET) == -1) + goto fseek_failed; + + if (write_index(out, offset_count, offsets) == -1) { + (void)fprintf(stderr, "gwavi_close: write_index() failed\n"); + return -1; + } + + free(offsets); + + /* reset some avi header fields */ + avi_header.number_of_frames = stream_header_v.data_length; + + if ((t = ftell(out)) == -1) + goto ftell_failed; + if (fseek(out, 12, SEEK_SET) == -1) + goto fseek_failed; + if (write_avi_header_chunk() == -1) { + (void)fprintf(stderr, "gwavi_close: write_avi_header_chunk() " + "failed\n"); + return -1; + } + if (fseek(out, t, SEEK_SET) == -1) + goto fseek_failed; + + if ((t = ftell(out)) == -1) + goto ftell_failed; + if (fseek(out, 4, SEEK_SET) == -1) + goto fseek_failed; + if (write_int(out, (unsigned int)(t - 8)) == -1) { + (void)fprintf(stderr, "gwavi_close: write_int() failed\n"); + return -1; + } + if (fseek(out, t, SEEK_SET) == -1) + goto fseek_failed; + + if (stream_format_v.palette != 0) + free(stream_format_v.palette); + + if (fclose(out) == EOF) { + perror("gwavi_close (fclose)"); + return -1; + } + + return 0; + +ftell_failed: + perror("gwavi_close: (ftell)"); + return -1; + +fseek_failed: + perror("gwavi_close (fseek)"); + return -1; +} + +/** + * This function allows you to reset the framerate. In a standard use case, you + * should not need to call it. However, if you need to, you can call it to reset + * the framerate after you are done adding frames to your AVI file and before + * you call gwavi_close(). + * + * @param gwavi Main gwavi structure initialized with gwavi_open()- + * @param fps Number of frames per second of your video. + * + * @return 0 on success, -1 on error. + */ +int +gwavi_t::set_framerate(unsigned int fps) +{ + stream_header_v.data_rate = fps; + avi_header.time_delay = (10000000 / fps); + + return 0; +} + +/** + * This function allows you to reset the video codec. In a standard use case, + * you should not need to call it. However, if you need to, you can call it to + * reset the video codec after you are done adding frames to your AVI file and + * before you call gwavi_close(). + * + * @param gwavi Main gwavi structure initialized with gwavi_open()- + * @param fourcc FourCC representing the codec of the video encoded stream. a + * + * @return 0 on success, -1 on error. + */ +int +gwavi_t::set_codec( const char *fourcc) +{ + if (check_fourcc(fourcc) != 0) + (void)fprintf(stderr, "WARNING: given fourcc does not seem to " + "be valid: %s\n", fourcc); + + memcpy(stream_header_v.codec, fourcc, 4); + stream_format_v.compression_type = + ((unsigned int)fourcc[3] << 24) + + ((unsigned int)fourcc[2] << 16) + + ((unsigned int)fourcc[1] << 8) + + ((unsigned int)fourcc[0]); + + return 0; +} + +/** + * This function allows you to reset the video size. In a standard use case, you + * should not need to call it. However, if you need to, you can call it to reset + * the video height and width set in the AVI file after you are done adding + * frames to your AVI file and before you call gwavi_close(). + * + * @param gwavi Main gwavi structure initialized with gwavi_open()- + * @param width Width of a frame. + * @param height Height of a frame. + * + * @return 0 on success, -1 on error. + */ +int +gwavi_t::set_size( unsigned int width, unsigned int height) +{ + unsigned int size = (width * height * 3); + + avi_header.data_rate = size; + avi_header.width = width; + avi_header.height = height; + avi_header.buffer_size = size; + stream_header_v.buffer_size = size; + stream_format_v.width = width; + stream_format_v.height = height; + stream_format_v.image_size = size; + + return 0; +} + diff --git a/src/drivers/Qt/avi/gwavi.h b/src/drivers/Qt/avi/gwavi.h new file mode 100644 index 00000000..661322f5 --- /dev/null +++ b/src/drivers/Qt/avi/gwavi.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2008-2011, Michael Kohn + * Copyright (c) 2013, Robin Hahling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef H_GWAVI +#define H_GWAVI + +#include /* for size_t */ +#include /* for size_t */ + +/* structures */ +struct gwavi_header_t +{ + unsigned int time_delay; /* dwMicroSecPerFrame */ + unsigned int data_rate; /* dwMaxBytesPerSec */ + unsigned int reserved; + unsigned int flags; /* dwFlags */ + unsigned int number_of_frames; /* dwTotalFrames */ + unsigned int initial_frames; /* dwInitialFrames */ + unsigned int data_streams; /* dwStreams */ + unsigned int buffer_size; /* dwSuggestedBufferSize */ + unsigned int width; /* dwWidth */ + unsigned int height; /* dwHeight */ + unsigned int time_scale; + unsigned int playback_data_rate; + unsigned int starting_time; + unsigned int data_length; +}; + +struct gwavi_stream_header_t +{ + char data_type[5]; /* fccType */ + char codec[5]; /* fccHandler */ + unsigned int flags; /* dwFlags */ + unsigned int priority; + unsigned int initial_frames;/* dwInitialFrames */ + unsigned int time_scale; /* dwScale */ + unsigned int data_rate; /* dwRate */ + unsigned int start_time; /* dwStart */ + unsigned int data_length; /* dwLength */ + unsigned int buffer_size; /* dwSuggestedBufferSize */ + unsigned int video_quality; /* dwQuality */ + /** + * Value between 0-10000. If set to -1, drivers use default quality + * value. + */ + int audio_quality; + unsigned int sample_size; /* dwSampleSize */ +}; + +struct gwavi_stream_format_v_t +{ + unsigned int header_size; + unsigned int width; + unsigned int height; + unsigned short int num_planes; + unsigned short int bits_per_pixel; + unsigned int compression_type; + unsigned int image_size; + unsigned int x_pels_per_meter; + unsigned int y_pels_per_meter; + unsigned int colors_used; + unsigned int colors_important; + unsigned int *palette; + unsigned int palette_count; +}; + +struct gwavi_stream_format_a_t +{ + unsigned short format_type; + unsigned int channels; + unsigned int sample_rate; + unsigned int bytes_per_second; + unsigned int block_align; + unsigned int bits_per_sample; + unsigned short size; +}; + +struct gwavi_audio_t +{ + unsigned int channels; + unsigned int bits; + unsigned int samples_per_second; +}; + +class gwavi_t +{ + public: + gwavi_t(void); + + int open(const char *filename, unsigned int width, + unsigned int height, const char *fourcc, unsigned int fps, + struct gwavi_audio_t *audio); + + int close(void); + + int add_frame( unsigned char *buffer, size_t len); + + int add_audio( unsigned char *buffer, size_t len); + + int set_codec(const char *fourcc); + + int set_size( unsigned int width, unsigned int height); + + int set_framerate(unsigned int fps); + + private: + FILE *out; + struct gwavi_header_t avi_header; + struct gwavi_stream_header_t stream_header_v; + struct gwavi_stream_format_v_t stream_format_v; + struct gwavi_stream_header_t stream_header_a; + struct gwavi_stream_format_a_t stream_format_a; + long marker; + int offsets_ptr; + int offsets_len; + long offsets_start; + unsigned int *offsets; + int offset_count; + + // helper functions + int write_avi_header(FILE *out, struct gwavi_header_t *avi_header); + int write_stream_header(FILE *out, + struct gwavi_stream_header_t *stream_header); + int write_stream_format_v(FILE *out, + struct gwavi_stream_format_v_t *stream_format_v); + int write_stream_format_a(FILE *out, + struct gwavi_stream_format_a_t *stream_format_a); + int write_avi_header_chunk(void); + int write_index(FILE *out, int count, unsigned int *offsets); + int check_fourcc(const char *fourcc); + int write_int(FILE *out, unsigned int n); + int write_short(FILE *out, unsigned int n); + int write_chars(FILE *out, const char *s); + int write_chars_bin(FILE *out, const char *s, int count); + +}; + +#endif /* ndef H_GWAVI */ + diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index 223cf689..04c1cf85 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -939,8 +939,8 @@ int fceuWrapperMemoryCleanup(void) */ void FCEUD_Update(uint8 *XBuf, - int32 *Buffer, - int Count) + int32 *Buffer, + int Count) { int blitDone = 0; //extern int FCEUDnetplay; diff --git a/src/drivers/Qt/sdl-video.cpp b/src/drivers/Qt/sdl-video.cpp index 84e82846..cd522f2d 100644 --- a/src/drivers/Qt/sdl-video.cpp +++ b/src/drivers/Qt/sdl-video.cpp @@ -34,6 +34,7 @@ #include "common/configSys.h" #include "Qt/sdl-video.h" +#include "Qt/AviRecord.h" #include "Qt/fceuWrapper.h" #ifdef CREATE_AVI @@ -478,7 +479,7 @@ BlitScreen(uint8 *XBuf) } nes_shm->blitUpdated = 1; - //guiPixelBufferReDraw(); + aviRecordAddFrame(); #ifdef CREATE_AVI { int fps = FCEUI_GetDesiredFPS();