ref: 3f03592d6961c9f33b681b3269803739766b0c4b
parent: 6d7bf12a173f770c54c290c608a6f80bd0d0c83e
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Wed Sep 9 12:23:23 EDT 2020
prepare for multiple streams support
--- a/README.md
+++ b/README.md
@@ -20,9 +20,10 @@
cd /tmp
git/clone https://git.sr.ht/~ft/dav1d
git/clone https://git.sr.ht/~ft/treason
+ git/clone https://git.sr.ht/~ft/mcfs
cd dav1d/src && mk
- cd ../../treason
- mk install
+ cd ../../treason && mk install
+ cd ../mcfs && mk install
## Gotchas
--- a/dec_av1.c
+++ /dev/null
@@ -1,148 +1,0 @@
-#include <dav1d.h>
-#include "frame.h"
-#include "stream.h"
-#include "decoder.h"
-#include "misc.h"
-
-#pragma lib "../dav1d/src/libdav1d.$M.a"
-
-typedef struct Aux Aux;
-
-struct Aux {
- Dav1dContext *c;
-};
-
-struct Stream {
- Streamhdr;
-};
-
-static char *layout[] = {
- [DAV1D_PIXEL_LAYOUT_I400] = "i400",
- [DAV1D_PIXEL_LAYOUT_I420] = "i420",
- [DAV1D_PIXEL_LAYOUT_I422] = "i422",
- [DAV1D_PIXEL_LAYOUT_I444] = "i444",
-};
-
-static u8int *
-allocdata(void *data, int sz)
-{
- return dav1d_data_create(data, sz);
-}
-
-static int
-readframe(Stream *s, Dav1dData *data)
-{
- Streamframe f;
-
- s->ops.aux = data;
- if(Sread(s, &f) != 0)
- return -1;
- data->m.offset = f.offset;
- data->m.timestamp = f.timestamp;
-
- return 0;
-}
-
-static void
-decode(void *x)
-{
- uvlong lasttimestamp;
- Dav1dPicture pic;
- Dav1dData data;
- Decoder *d;
- Frame *f;
- Aux *a;
- int res;
-
- d = x;
- a = d->aux;
- lasttimestamp = 0;
- memset(&pic, 0, sizeof(pic));
- for(res = 0, data.sz = 0; data.sz > 0 || (res = readframe(d->s, &data)) == 0;){
- if(nbrecvp(d->stop) != 0){
- print("received stop\n");
- break;
- }
-
- res = dav1d_get_picture(a->c, &pic);
- if(res < 0){
- if(res != DAV1D_ERR(EAGAIN)){
- werrstr("dav1d_get_picture: %d", res);
- break;
- }
- }else{
- if((f = malloc(sizeof(*f) + pic.p.w*pic.p.h*3)) != nil){
- f->w = pic.p.w;
- f->h = pic.p.h;
- yuv420_rgb24(f->w, f->h, pic.data[0], pic.data[1], pic.data[2], pic.stride[0], pic.stride[1], f->rgb, f->w*3);
- f->dt = (pic.m.timestamp - lasttimestamp) * d->timebase * 1000000000ULL;
- lasttimestamp = pic.m.timestamp;
- dav1d_picture_unref(&pic);
-
- if(sendp(d->frames, f) < 0){
- free(f);
- break;
- }
- }
- }
-
- res = dav1d_send_data(a->c, &data);
- if(res < 0){
- if(res != DAV1D_ERR(EAGAIN)){
- werrstr("dav1d_send_data: %d", res);
- break;
- }
- }
- }
- if(res != 0)
- fprint(2, "av1: %r\n");
-
- if(data.sz > 0)
- dav1d_data_unref(&data);
-
- /* drain */
- while(dav1d_get_picture(a->c, &pic) >= 0)
- dav1d_picture_unref(&pic);
-
- dav1d_close(&a->c);
- sendp(d->finished, nil);
- free(a);
-
- threadexits(nil);
-}
-
-static int
-av1open(Decoder *d)
-{
- Dav1dSettings av1s;
- Aux *a;
- int res;
-
- a = calloc(1, sizeof(*a));
-
- dav1d_default_settings(&av1s);
- av1s.n_frame_threads = nproc;
- av1s.n_tile_threads = nproc;
-
- if((res = dav1d_open(&a->c, &av1s)) != 0){
- werrstr("dav1d_open: %d", res);
- free(a);
- return -1;
- }
- d->aux = a;
- d->s->ops.alloc = allocdata;
- proccreate(decode, d, 16384);
-
- return 0;
-}
-
-static void
-av1close(Decoder *d)
-{
- USED(d);
-}
-
-Decoderops av1ops = {
- .open = av1open,
- .close = av1close,
-};
--- a/decoder.c
+++ b/decoder.c
@@ -16,7 +16,7 @@
};
Decoder *
-Dopen(Stream *s, Streaminfo *info)
+Dopen(Stream *s)
{
Decoder *d;
int i;
@@ -24,15 +24,14 @@
if((d = calloc(1, sizeof(*d))) == nil)
return nil;
- d->timebase = (double)info->timebase.num/(double)info->timebase.denum;
+ d->timebase = (double)s->timebase.num/(double)s->timebase.denum;
d->s = s;
d->frames = chancreate(sizeof(Frame*), 4);
d->finished = chancreate(sizeof(void*), 1);
d->stop = chancreate(sizeof(void*), 1);
- memmove(&d->info, info, sizeof(info));
for(i = 0; i < nelem(ops); i++){
- if(ops[i].fmt == info->fmt){
+ if(ops[i].fmt == s->fmt){
if(ops[i].o->open(d) < 0){
werrstr("%s: %r", ops[i].name);
goto err;
--- a/decoder.h
+++ b/decoder.h
@@ -6,7 +6,6 @@
Channel *frames;
Channel *finished;
Channel *stop;
- Streaminfo info;
Decoderops *ops;
double timebase;
@@ -18,5 +17,5 @@
void (*close)(Decoder *d);
};
-Decoder *Dopen(Stream *s, Streaminfo *info);
+Decoder *Dopen(Stream *s);
void Dclose(Decoder *d);
--- /dev/null
+++ b/decoder_av1.c
@@ -1,0 +1,152 @@
+#include <dav1d.h>
+#include "frame.h"
+#include "stream.h"
+#include "decoder.h"
+#include "misc.h"
+
+#pragma lib "../dav1d/src/libdav1d.$M.a"
+
+typedef struct Aux Aux;
+
+struct Aux {
+ Dav1dContext *c;
+};
+
+static char *layouts[] = {
+ [DAV1D_PIXEL_LAYOUT_I400] = "i400",
+ [DAV1D_PIXEL_LAYOUT_I420] = "i420",
+ [DAV1D_PIXEL_LAYOUT_I422] = "i422",
+ [DAV1D_PIXEL_LAYOUT_I444] = "i444",
+};
+
+static u8int *
+allocdata(void *data, int sz)
+{
+ return dav1d_data_create(data, sz);
+}
+
+static int
+readframe(Stream *s, Dav1dData *data)
+{
+ Streamframe f;
+
+ s->ops.aux = data;
+ if(Sread(s, &f) != 0)
+ return -1;
+ data->m.offset = f.offset;
+ data->m.timestamp = f.timestamp;
+
+ return 0;
+}
+
+static void
+decode(void *x)
+{
+ uvlong lasttimestamp;
+ Dav1dPicture pic;
+ Dav1dData data;
+ Decoder *d;
+ Frame *f;
+ Aux *a;
+ int res;
+
+ d = x;
+ a = d->aux;
+ lasttimestamp = 0;
+ memset(&pic, 0, sizeof(pic));
+ for(res = 0, data.sz = 0; data.sz > 0 || (res = readframe(d->s, &data)) == 0;){
+ if(nbrecvp(d->stop) != 0)
+ break;
+
+ res = dav1d_get_picture(a->c, &pic);
+ if(res < 0){
+ if(res != DAV1D_ERR(EAGAIN)){
+ werrstr("dav1d_get_picture: %d", res);
+ break;
+ }
+ }else if(pic.p.layout != DAV1D_PIXEL_LAYOUT_I420){
+ if(pic.p.layout >= 0 && pic.p.layout < nelem(layouts))
+ werrstr("%s", layouts[pic.p.layout]);
+ else
+ werrstr("??? (%d)", pic.p.layout);
+ werrstr("unsupported pixel layout: %r");
+ res = -1;
+ break;
+ }else if(pic.p.bpc != 8){
+ werrstr("unsupported bits per component: %d", pic.p.bpc);
+ res = -1;
+ break;
+ }else if((f = malloc(sizeof(*f) + pic.p.w*pic.p.h*3)) != nil){
+ f->w = pic.p.w;
+ f->h = pic.p.h;
+ yuv420_rgb24(f->w, f->h, pic.data[0], pic.data[1], pic.data[2], pic.stride[0], pic.stride[1], f->rgb, f->w*3);
+ f->dt = (pic.m.timestamp - lasttimestamp) * d->timebase * 1000000000ULL;
+ lasttimestamp = pic.m.timestamp;
+ dav1d_picture_unref(&pic);
+
+ if(sendp(d->frames, f) < 0){
+ free(f);
+ break;
+ }
+ }
+
+ res = dav1d_send_data(a->c, &data);
+ if(res < 0){
+ if(res != DAV1D_ERR(EAGAIN)){
+ werrstr("dav1d_send_data: %d", res);
+ break;
+ }
+ }
+ }
+ if(res != 0)
+ fprint(2, "av1: %r\n");
+
+ if(data.sz > 0)
+ dav1d_data_unref(&data);
+
+ /* drain */
+ while(dav1d_get_picture(a->c, &pic) >= 0)
+ dav1d_picture_unref(&pic);
+
+ dav1d_close(&a->c);
+ sendp(d->finished, nil);
+ free(a);
+
+ threadexits(nil);
+}
+
+static int
+av1open(Decoder *d)
+{
+ Dav1dSettings av1s;
+ Aux *a;
+ int res;
+
+ a = calloc(1, sizeof(*a));
+
+ dav1d_default_settings(&av1s);
+ av1s.n_frame_threads = nproc;
+ av1s.n_tile_threads = nproc;
+
+ if((res = dav1d_open(&a->c, &av1s)) != 0){
+ werrstr("dav1d_open: %d", res);
+ free(a);
+ return -1;
+ }
+ d->aux = a;
+ d->s->ops.alloc = allocdata;
+ proccreate(decode, d, 16384);
+
+ return 0;
+}
+
+static void
+av1close(Decoder *d)
+{
+ USED(d);
+}
+
+Decoderops av1ops = {
+ .open = av1open,
+ .close = av1close,
+};
--- a/ivf.c
+++ /dev/null
@@ -1,157 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include "stream.h"
-
-struct Stream {
- Streamhdr;
- Biobuf *b;
- u8int *buf;
- int bufsz;
-};
-
-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, Streaminfo *info, int *failed)
-{
- Biobuf *b;
- Stream *s;
- u16int hlen, w, h;
- u32int tbdenum, tbnum;
- char tmp[6];
-
- 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((s = calloc(1, sizeof(*s))) == nil)
- goto err;
- if(hlen < 0x20 || Bread(b, tmp, 4) != 4){
- werrstr("invalid header: hlen=%d", hlen);
- goto err;
- }
- if(memcmp(tmp, "AV01", 4) != 0){ /* nothing else is supported yet */
- werrstr("unsupported format %.*s", 4, tmp);
- goto err;
- }
- info->fmt = FmtAV1;
- if(Bu16le(b, &w) < 0 || Bu16le(b, &h) < 0 || Bu32le(b, &tbdenum) < 0 || Bu32le(b, &tbnum) < 0){
- werrstr("invalid header: %r");
- goto err;
- }
- info->w = w;
- info->h = h;
- info->timebase.denum = tbdenum;
- info->timebase.num = tbnum;
- if(Bseek(b, hlen, 0) != hlen){
- werrstr("invalid IVF stream");
- goto err;
- }
- 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, ×tamp) || (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,
-};
--- a/main.c
+++ b/main.c
@@ -83,9 +83,8 @@
Mouse m;
char *s;
Stream *stream;
- Streaminfo info;
Decoder *d;
- int i, end, done, res;
+ int i, end, done, res, nstreams;
Alt a[Cnum+1] =
{
[Cframe] = { nil, &frame, CHANRCV },
@@ -121,9 +120,9 @@
for(end = i = 0; !end && i < argc; i++){
draw(screen, screen->r, display->black, nil, ZP);
- if((stream = Sopen(argv[i], &info)) == nil)
+ if((stream = Sopen(argv[i], &nstreams)) == nil)
sysfatal("%r");
- if((d = Dopen(stream, &info)) == nil)
+ if((d = Dopen(stream)) == nil)
sysfatal("%r");
a[Cframe].c = d->frames;
@@ -163,7 +162,8 @@
}
Dclose(d);
- Sclose(stream);
+ for(i = 0; i < nstreams; i++)
+ Sclose(stream+i);
}
threadexitsall(nil);
--- a/mkfile
+++ b/mkfile
@@ -11,12 +11,13 @@
stream.h\
OFILES=\
- dec_av1.$O\
decoder.$O\
- ivf.$O\
+ decoder_av1.$O\
main.$O\
misc.$O\
stream.$O\
+ stream_ivf.$O\
+ stream_mp4.$O\
yuv.$O\
default:V: all
--- a/stream.c
+++ b/stream.c
@@ -2,11 +2,8 @@
#include <libc.h>
#include "stream.h"
-struct Stream {
- Streamops ops;
-};
-
extern Streamops ivfops;
+extern Streamops mp4ops;
static struct {
char *name;
@@ -13,10 +10,11 @@
Streamops *o;
}ops[] = {
{"ivf", &ivfops},
+ {"mp4", &mp4ops},
};
Stream *
-Sopen(char *filename, Streaminfo *info)
+Sopen(char *filename, int *num)
{
int i, failed;
Stream *s;
@@ -23,7 +21,7 @@
for(i = 0; i < nelem(ops); i++){
failed = 0;
- if((s = ops[i].o->open(filename, info, &failed)) != nil){
+ if((s = ops[i].o->open(filename, &failed, num)) != nil){
memmove(&s->ops, ops[i].o, sizeof(Streamops));
return s;
}
--- a/stream.h
+++ b/stream.h
@@ -6,7 +6,16 @@
#pragma incomplete Stream
enum {
- FmtAV1,
+ Svideo,
+ Saudio,
+
+ /* video */
+ FmtAV1 = 0,
+ FmtVP9,
+ FmtVP8,
+ /* audio */
+ FmtAAC,
+ FmtOpus,
};
struct Streamframe {
@@ -17,7 +26,7 @@
};
struct Streamops {
- Stream *(*open)(char *filename, Streaminfo *info, int *failed);
+ Stream *(*open)(char *filename, int *num, int *failed);
u8int *(*alloc)(void *aux, int sz);
void (*close)(Stream *s);
vlong (*offset)(Stream *s);
@@ -26,20 +35,37 @@
void *aux;
};
-struct Streamhdr {
- Streamops ops;
-};
-
struct Streaminfo {
+ int type;
+ int fmt;
+
struct {
uvlong denum, num;
}timebase;
- int fmt;
- int w;
- int h;
+
+ union {
+ struct {
+ int w, h;
+ }video;
+
+ struct {
+ int nchan;
+ int srate;
+ }audio;
+ };
};
-Stream *Sopen(char *filename, Streaminfo *info);
+struct Stream {
+ Streaminfo;
+ Streamops ops;
+
+ /* private stuff */
+ void *b;
+ u8int *buf;
+ int bufsz;
+};
+
+Stream *Sopen(char *filename, int *num);
vlong Soffset(Stream *s);
void Sclose(Stream *s);
int Sread(Stream *s, Streamframe *f);
--- /dev/null
+++ b/stream_ivf.c
@@ -1,0 +1,162 @@
+#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, ×tamp) || (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,
+};
--- /dev/null
+++ b/stream_mp4.c
@@ -1,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "stream.h"
+
+static Stream *
+mp4open(char *filename, int *num, int *failed)
+{
+ USED(filename); USED(num); USED(failed);
+ return nil;
+}
+
+static void
+mp4close(Stream *s)
+{
+ free(s);
+}
+
+static int
+mp4read(Stream *s, Streamframe *f)
+{
+ USED(s); USED(f);
+ return -1;
+}
+
+static vlong
+mp4offset(Stream *s)
+{
+ USED(s);
+ return -1;
+}
+
+Streamops mp4ops = {
+ .open = mp4open,
+ .close = mp4close,
+ .read = mp4read,
+ .offset = mp4offset,
+};