shithub: treason

ref: 3f03592d6961c9f33b681b3269803739766b0c4b
dir: /stream_ivf.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "stream.h"

static int
Bu16le(Biobuf *b, u16int *o)
{
	int x;

	x = Bgetc(b);
	x |= Bgetc(b)<<8;
	*o = x;
	if(x < 0)
		werrstr("failed to read 2 bytes");

	return x < 0 ? -1 : 0;
}

static int
Bu32le(Biobuf *b, u32int *o)
{
	int x, i;

	*o = 0;
	for(i = 0; i < 4; *o |= x<<(i*8), i++){
		if((x = Bgetc(b)) < 0){
			werrstr("failed to read 4 bytes");
			return -1;
		}
	}

	return 0;
}

static int
Bu64le(Biobuf *b, u64int *o)
{
	int x, i;

	*o = 0;
	for(i = 0; i < 8; *o |= x<<(i*8), i++){
		if((x = Bgetc(b)) < 0){
			werrstr("failed to read 8 bytes");
			return -1;
		}
	}

	return 0;
}

static Stream *
ivfopen(char *filename, int *num, int *failed)
{
	Biobuf *b;
	Stream *s;
	u16int hlen, w, h, fmt;
	u32int tbdenum, tbnum;
	char tmp[6];

	s = nil;
	if((b = Bopen(filename, OREAD)) == nil)
		return nil;
	if(Bread(b, tmp, 6) != 6 || memcmp(tmp, "DKIF", 4) != 0 || Bu16le(b, &hlen) < 0){
		Bterm(b);
		return nil;
	}
	if(hlen < 0x20 || Bread(b, tmp, 4) != 4){
		werrstr("invalid header: hlen=%d", hlen);
		goto err;
	}

	if(memcmp(tmp, "AV01", 4) == 0)
		fmt = FmtAV1;
	else if(memcmp(tmp, "VP90", 4) == 0)
		fmt = FmtVP9;
	else if(memcmp(tmp, "VP80", 4) == 0)
		fmt = FmtVP8;
	else{
		werrstr("unsupported format %.*s", 4, tmp);
		goto err;
	}

	if(Bu16le(b, &w) < 0 || Bu16le(b, &h) < 0 || Bu32le(b, &tbdenum) < 0 || Bu32le(b, &tbnum) < 0){
		werrstr("invalid header: %r");
		goto err;
	}
	if(Bseek(b, hlen, 0) != hlen){
		werrstr("invalid IVF stream");
		goto err;
	}

	if((s = calloc(1, sizeof(*s))) == nil)
		goto err;
	*num = 1;
	s->type = Svideo;
	s->fmt = fmt;
	s->video.w = w;
	s->video.h = h;
	s->timebase.denum = tbdenum;
	s->timebase.num = tbnum;
	s->b = b;

	return s;
err:
	*failed = 1;
	Bterm(b);
	free(s);
	return nil;
}

static void
ivfclose(Stream *s)
{
	Bterm(s->b);
	free(s->buf);
	free(s);
}

static int
ivfread(Stream *s, Streamframe *f)
{
	u64int timestamp;
	u32int sz;
	u8int *buf;

	f->offset = Boffset(s->b);
	if(Bu32le(s->b, &sz) < 0 || Bu64le(s->b, &timestamp) || (int)sz < 0)
		return -1;
	buf = s->buf;
	if(s->ops.alloc != nil)
		buf = s->ops.alloc(s->ops.aux, sz);
	else if(sz > s->bufsz){
		if((buf = realloc(s->buf, sz)) == nil){
			werrstr("frame is too big: %d bytes", sz);
			return -1;
		}
		s->buf = buf;
	}
	if(Bread(s->b, buf, sz) != sz){
		werrstr("short read");
		return -1;
	}
	f->buf = buf;
	f->sz = sz;
	f->timestamp = timestamp;

	return 0;
}

static vlong
ivfoffset(Stream *s)
{
	return Boffset(s->b);
}

Streamops ivfops = {
	.open = ivfopen,
	.close = ivfclose,
	.read = ivfread,
	.offset = ivfoffset,
};