ref: d696956ababd5fdec5bcb51373896468c310161d
dir: /stream_ivf.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "stream.h" extern Streamops ivfops; 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; } int ivfopenb(void *bio, Stream *s, int *failed) { u16int hlen, w, h, fmt; u32int tbdenum, tbnum; char tmp[6]; if(bio == nil){ werrstr("nil bio"); return -1; } if(Bread(bio, tmp, 6) != 6 || Bu16le(bio, &hlen) < 0){ werrstr("header read failed"); Bterm(bio); return -1; } if(memcmp(tmp, "DKIF", 4) != 0){ werrstr("expected DKIF, got %02x%02x%02x%02x", tmp[0], tmp[1], tmp[2], tmp[3]); Bterm(bio); return -1; } if(hlen < 0x20 || Bread(bio, 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 if(memcmp(tmp, "AVC1", 4) == 0) /* FIXME that's non-standard */ fmt = FmtH264; else{ werrstr("unsupported format %.*s", 4, tmp); goto err; } if(Bu16le(bio, &w) < 0 || Bu16le(bio, &h) < 0 || Bu32le(bio, &tbdenum) < 0 || Bu32le(bio, &tbnum) < 0){ werrstr("invalid header: %r"); goto err; } if(Bseek(bio, hlen, 0) != hlen){ werrstr("invalid IVF stream"); goto err; } s->type = Svideo; s->fmt = fmt; s->video.w = w; s->video.h = h; s->timebase.denum = tbdenum; s->timebase.num = tbnum; s->b = bio; memmove(&s->ops, &ivfops, sizeof(ivfops)); return 0; err: *failed = 1; Bterm(bio); free(s); return -1; } static Stream * ivfopen(char *path, int *num, int *failed) { Stream *s; if((s = calloc(1, sizeof(*s))) == nil){ *failed = 1; return nil; } *num = 1; if(ivfopenb(Bopen(path, OREAD|OCEXEC), s, failed) != 0){ free(s); s = nil; } return s; } static int ivfread(Stream *s, Streamframe *f) { u64int timestamp; u32int sz; u8int *buf; int n; if(Bu32le(s->b, &sz) < 0 || Bu64le(s->b, ×tamp) < 0 || (int)sz < 0){ /* eof */ f->sz = 0; return 0; } 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); goto err; } s->buf = buf; } if((n = Bread(s->b, buf, sz)) != sz){ werrstr("short read (%d < %d)", n, sz); goto err; } f->buf = buf; f->sz = sz; f->timestamp = timestamp; return 0; err: werrstr("ivf: %r"); return -1; } static void ivfclose(Stream *s) { if(s->b != nil){ Bterm(s->b); s->b = nil; } free(s->buf); s->buf = nil; } Streamops ivfops = { .open = ivfopen, .read = ivfread, .close = ivfclose, };