diff --git a/polymer/eduke32/Makefile b/polymer/eduke32/Makefile index 0694cafe9..173ece225 100644 --- a/polymer/eduke32/Makefile +++ b/polymer/eduke32/Makefile @@ -79,6 +79,8 @@ endif EDUKE32 ?= eduke32$(EXESUFFIX) MAPSTER32 ?= mapster32$(EXESUFFIX) +IVFRATE ?= ivfrate$(EXESUFFIX) + EDUKE32_TARGET:=$(EDUKE32) ifneq ($(PLATFORM),WII) MAPSTER32_TARGET:=$(MAPSTER32) @@ -440,6 +442,11 @@ $(OBJ)/%.$o: $(SRC)/%.c $(COMPILE_STATUS) if $(COMPILER) $(OURCFLAGS) -c $< -o $@; then $(COMPILE_OK); else $(COMPILE_FAILED); fi +#### Utilities + +$(IVFRATE): $(SRC)/ivfrate.c + if $(COMPILER) -Wall -Wextra $< -o $@; then $(COMPILE_OK); else $(COMPILE_FAILED); fi + #### Lunatic # Create object files directly with luajit diff --git a/polymer/eduke32/source/animvpx.c b/polymer/eduke32/source/animvpx.c index 6b15f3992..3397898a5 100644 --- a/polymer/eduke32/source/animvpx.c +++ b/polymer/eduke32/source/animvpx.c @@ -26,29 +26,22 @@ const char *animvpx_read_ivf_header_errmsg[] = { "unrecognized IVF version, expected 0", "only VP8 video stream supported", "invalid framerate numerator or denominator after correction, must not be 0", - "INTERNAL ERROR, IVF header size wrong" }; int32_t animvpx_read_ivf_header(int32_t inhandle, animvpx_ivf_header_t *hdr) { - if (sizeof(animvpx_ivf_header_t) != 32) - return 6; + int32_t err; + + Bassert(sizeof(animvpx_ivf_header_t) == 32); if (kread(inhandle, hdr, sizeof(animvpx_ivf_header_t)) != sizeof(animvpx_ivf_header_t)) return 1; // "couldn't read header" - if (Bmemcmp(hdr,"DKIF",4)) - return 2; // "not an IVF file" - - hdr->version = B_LITTLE16(hdr->version); - if (hdr->version != 0) - return 3; // "unrecognized IVF version" + err = animvpx_check_header(hdr); + if (err) + return err; hdr->hdrlen = B_LITTLE16(hdr->hdrlen); - // fourcc is left as-is - - if (Bmemcmp(hdr->fourcc, "VP80", 4)) - return 4; // "only VP8 supported" hdr->width = B_LITTLE16(hdr->width); hdr->height = B_LITTLE16(hdr->height); @@ -59,30 +52,21 @@ int32_t animvpx_read_ivf_header(int32_t inhandle, animvpx_ivf_header_t *hdr) // the rest is based on vpxdec.c --> file_is_ivf() - /* Some versions of vpxenc used 1/(2*fps) for the timebase, so - * we can guess the framerate using only the timebase in this - * case. Other files would require reading ahead to guess the - * timebase, like we do for webm. - */ if (hdr->fpsnumer < 1000) { - /* Correct for the factor of 2 applied to the timebase in the - * encoder. - */ - if (hdr->fpsnumer&1) - hdr->fpsdenom <<= 1; - else - hdr->fpsnumer >>= 1; + // NOTE: We got rid of the 1/(2*fps) correction from libvpx's vpxdec.c, + // users are encouraged to use the "ivfrate" utility from the source/ + // directory instead. if (hdr->fpsdenom==0 || hdr->fpsnumer==0) return 5; // "invalid framerate numerator or denominator" - initprintf("animvpx: rate is %d frames / %d seconds (%.03f fps) after 1/(2*fps) correction.\n", + initprintf("animvpx: rate is %d frames / %d seconds (%.03f fps).\n", hdr->fpsnumer, hdr->fpsdenom, (double)hdr->fpsnumer/hdr->fpsdenom); } else { - double fps = (hdr->fpsdenom==0) ? 0.0 : hdr->fpsnumer/hdr->fpsdenom; + double fps = (hdr->fpsdenom==0) ? 0.0 : (double)hdr->fpsnumer/hdr->fpsdenom; initprintf("animvpx: set rate to 30 fps (header says %d frames / %d seconds = %.03f fps).\n", hdr->fpsnumer, hdr->fpsdenom, fps); diff --git a/polymer/eduke32/source/animvpx.h b/polymer/eduke32/source/animvpx.h index ce05960af..3ca774f29 100644 --- a/polymer/eduke32/source/animvpx.h +++ b/polymer/eduke32/source/animvpx.h @@ -86,5 +86,19 @@ int32_t animvpx_render_frame(animvpx_codec_ctx *codec); void animvpx_print_stats(const animvpx_codec_ctx *codec); +static inline int32_t animvpx_check_header(const animvpx_ivf_header_t *hdr) +{ + if (Bmemcmp(hdr->magic,"DKIF",4)) + return 2; // "not an IVF file" + + if (hdr->version != 0) + return 3; // "unrecognized IVF version" + + // fourcc is left as-is + if (Bmemcmp(hdr->fourcc, "VP80", 4)) + return 4; // "only VP8 supported" + + return 0; +} #endif // !defined ANIM_VPX_H diff --git a/polymer/eduke32/source/ivfrate.c b/polymer/eduke32/source/ivfrate.c new file mode 100644 index 000000000..8f58e07c9 --- /dev/null +++ b/polymer/eduke32/source/ivfrate.c @@ -0,0 +1,124 @@ + +#include +#include +#include +#include + +#include +#include + +#include +#define Bmemcmp memcmp +#define USE_OPENGL +#include "animvpx.h" + + +static void print_info(const char *prefix, const animvpx_ivf_header_t *hdr) +{ + printf("%s%d x %d, %d frames @ %d frames / %d seconds (%.3f fps%s)\n", prefix, + hdr->width, hdr->height, hdr->numframes, hdr->fpsnumer, hdr->fpsdenom, + (hdr->fpsdenom==0 ? 0 : (double)hdr->fpsnumer/hdr->fpsdenom), + hdr->fpsnumer>=1000 ? " --> 30 fps" : ""); +} + + +int main(int argc, char **argv) +{ + int fd, dowrite, err; + animvpx_ivf_header_t hdr; + + union { uint16_t i; char c[2]; } u; + u.c[0] = 1; + u.c[1] = 0; + if (u.i != 1) + { + fprintf(stderr, "This program is only for little-endian machines.\n"); + return 255; + } + + if (argc != 2 && argc != 4 && argc != 5) + { + fprintf(stderr, "Usage: %s [ [-force]]\n" + " Without -force, must be < 1000.\n" + " If is >= 1000, the actual frame rate\n" + " is set to 30 fps on playback.\n", + argv[0]); + return 1; + } + + dowrite = (argc >= 4); + + fd = open(argv[1], dowrite ? O_RDWR : O_RDONLY); + if (fd < 0) + { + fprintf(stderr, "Could't open \"%s\" for: %s\n", + dowrite ? "reading/writing":"reading", strerror(errno)); + return 2; + } + + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) + { + fprintf(stderr, "Couldn't read IVF header: %s\n", strerror(errno)); + return 3; + } + + err = animvpx_check_header(&hdr); + if (err) + { + fprintf(stderr, "Header check failed with code %d (not an IVF file?)\n", err); + return 4; + } + + if (!dowrite) + { + print_info("", &hdr); + } + else + { + unsigned long numer = strtoul(argv[2], NULL, 10); + unsigned long denom = strtoul(argv[3], NULL, 10); + uint32_t numer32=numer, denom32=denom; + const int NUMER_OFS = offsetof(animvpx_ivf_header_t, fpsnumer); + + if (denom == 0) + { + fprintf(stderr, "FPS denominator must not be zero!\n"); + return 4; + } + + if (numer >= 1000 && (argc!=5 || strcmp(argv[4], "-force"))) + { + fprintf(stderr, "FPS numerator must be < 1000, or -force must be passed as 5th arg.\n"); + return 5; + } + + if (numer32 != numer || denom32 != denom) + { + fprintf(stderr, "Out of range number passed.\n"); + return 6; + } + + print_info("Old: ", &hdr); + hdr.fpsnumer = numer32; + hdr.fpsdenom = denom32; + print_info("New: ", &hdr); + + if (lseek(fd, NUMER_OFS, SEEK_SET) != NUMER_OFS) + { + fprintf(stderr, "lseek failed: %s\n", strerror(errno)); + return 7; + } + + err = 0; + err |= (write(fd, &numer32, 4) != 4); + err |= (write(fd, &denom32, 4) != 4); + + if (err) + { + fprintf(stderr, "Warning: data not fully written.\n"); + return 8; + } + } + + return 0; +} diff --git a/polymer/synthesis.sh b/polymer/synthesis.sh index 3c4d414ae..a17093fd3 100755 --- a/polymer/synthesis.sh +++ b/polymer/synthesis.sh @@ -9,8 +9,8 @@ make=( make PLATFORM=WINDOWS CC='wine gcc' CXX='wine g++' AS='wine nasm' RC='win clean=veryclean # the following file paths are relative to $source -targets=( eduke32.exe mapster32.exe ) -bin_packaged=( eduke32.exe eduke32.debug.exe mapster32.exe mapster32.debug.exe ebacktrace1.dll SEHELP.HLP STHELP.HLP names.h buildlic.txt GNU.TXT m32help.hlp nedmalloc.dll tiles.cfg samples/* ) +targets=( eduke32.exe mapster32.exe ivfrate.exe ) +bin_packaged=( eduke32.exe eduke32.debug.exe mapster32.exe mapster32.debug.exe ivfrate.exe ebacktrace1.dll SEHELP.HLP STHELP.HLP names.h buildlic.txt GNU.TXT m32help.hlp nedmalloc.dll tiles.cfg samples/* ) not_src_packaged=( psd source/jaudiolib/third-party/vorbis.framework/Versions/A/vorbis Apple/lib ) # group that owns the resulting packages