New utility ivfrate(.exe) and a couple of small VP8 changes.

The command-line utility can query and set the frame rate of IVF files, since
apparently encoders don't care too much about setting proper values in the IVF
header.  Also, add the utility to the synthesis build.

On the playback side in EDuke32, get rid of the 1/(2*fps) "correction" if the
FPS numerator is <1000 (presumably used in older encoders) and properly print
the frame rate's fractional part.

git-svn-id: https://svn.eduke32.com/eduke32@3131 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2012-11-08 20:17:23 +00:00
parent 17bac8cb55
commit b2162f554d
5 changed files with 158 additions and 29 deletions

View File

@ -79,6 +79,8 @@ endif
EDUKE32 ?= eduke32$(EXESUFFIX) EDUKE32 ?= eduke32$(EXESUFFIX)
MAPSTER32 ?= mapster32$(EXESUFFIX) MAPSTER32 ?= mapster32$(EXESUFFIX)
IVFRATE ?= ivfrate$(EXESUFFIX)
EDUKE32_TARGET:=$(EDUKE32) EDUKE32_TARGET:=$(EDUKE32)
ifneq ($(PLATFORM),WII) ifneq ($(PLATFORM),WII)
MAPSTER32_TARGET:=$(MAPSTER32) MAPSTER32_TARGET:=$(MAPSTER32)
@ -440,6 +442,11 @@ $(OBJ)/%.$o: $(SRC)/%.c
$(COMPILE_STATUS) $(COMPILE_STATUS)
if $(COMPILER) $(OURCFLAGS) -c $< -o $@; then $(COMPILE_OK); else $(COMPILE_FAILED); fi 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 #### Lunatic
# Create object files directly with luajit # Create object files directly with luajit

View File

@ -26,29 +26,22 @@ const char *animvpx_read_ivf_header_errmsg[] = {
"unrecognized IVF version, expected 0", "unrecognized IVF version, expected 0",
"only VP8 video stream supported", "only VP8 video stream supported",
"invalid framerate numerator or denominator after correction, must not be 0", "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) int32_t animvpx_read_ivf_header(int32_t inhandle, animvpx_ivf_header_t *hdr)
{ {
if (sizeof(animvpx_ivf_header_t) != 32) int32_t err;
return 6;
Bassert(sizeof(animvpx_ivf_header_t) == 32);
if (kread(inhandle, hdr, sizeof(animvpx_ivf_header_t)) != sizeof(animvpx_ivf_header_t)) if (kread(inhandle, hdr, sizeof(animvpx_ivf_header_t)) != sizeof(animvpx_ivf_header_t))
return 1; // "couldn't read header" return 1; // "couldn't read header"
if (Bmemcmp(hdr,"DKIF",4)) err = animvpx_check_header(hdr);
return 2; // "not an IVF file" if (err)
return err;
hdr->version = B_LITTLE16(hdr->version);
if (hdr->version != 0)
return 3; // "unrecognized IVF version"
hdr->hdrlen = B_LITTLE16(hdr->hdrlen); 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->width = B_LITTLE16(hdr->width);
hdr->height = B_LITTLE16(hdr->height); 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() // 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) if (hdr->fpsnumer < 1000)
{ {
/* Correct for the factor of 2 applied to the timebase in the // NOTE: We got rid of the 1/(2*fps) correction from libvpx's vpxdec.c,
* encoder. // users are encouraged to use the "ivfrate" utility from the source/
*/ // directory instead.
if (hdr->fpsnumer&1)
hdr->fpsdenom <<= 1;
else
hdr->fpsnumer >>= 1;
if (hdr->fpsdenom==0 || hdr->fpsnumer==0) if (hdr->fpsdenom==0 || hdr->fpsnumer==0)
return 5; // "invalid framerate numerator or denominator" 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); hdr->fpsnumer, hdr->fpsdenom, (double)hdr->fpsnumer/hdr->fpsdenom);
} }
else 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", initprintf("animvpx: set rate to 30 fps (header says %d frames / %d seconds = %.03f fps).\n",
hdr->fpsnumer, hdr->fpsdenom, fps); hdr->fpsnumer, hdr->fpsdenom, fps);

View File

@ -86,5 +86,19 @@ int32_t animvpx_render_frame(animvpx_codec_ctx *codec);
void animvpx_print_stats(const 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 #endif // !defined ANIM_VPX_H

View File

@ -0,0 +1,124 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#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 <file.ivf> [<fpsnumerator> <fpsdenominator> [-force]]\n"
" Without -force, <fpsnumerator> must be < 1000.\n"
" If <fpsnumerator> 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;
}

View File

@ -9,8 +9,8 @@ make=( make PLATFORM=WINDOWS CC='wine gcc' CXX='wine g++' AS='wine nasm' RC='win
clean=veryclean clean=veryclean
# the following file paths are relative to $source # the following file paths are relative to $source
targets=( eduke32.exe mapster32.exe ) targets=( eduke32.exe mapster32.exe ivfrate.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/* ) 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 ) not_src_packaged=( psd source/jaudiolib/third-party/vorbis.framework/Versions/A/vorbis Apple/lib )
# group that owns the resulting packages # group that owns the resulting packages