ref: 4812d7f4d60a30404a8f0d0cb1f244ae288387d5
parent: 3f03592d6961c9f33b681b3269803739766b0c4b
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Thu Sep 10 08:28:42 EDT 2020
support MP4 container via mcfs
--- a/main.c
+++ b/main.c
@@ -95,7 +95,11 @@
[Cnum] = { nil, nil, CHANEND },
};
+ debug = 0;
ARGBEGIN{
+ case 'd':
+ debug++;
+ break;
}ARGEND
if(argc < 1)
@@ -102,6 +106,8 @@
sysfatal("usage");
nproc = atoi((s = getenv("NPROC")) != nil ? s : "1");
+ if(nproc < 1)
+ nproc = 1;
srand(nanosec());
if(initdraw(nil, nil, "treason") < 0)
--- a/misc.c
+++ b/misc.c
@@ -1,8 +1,28 @@
#include <u.h>
#include <libc.h>
#include <tos.h>
+#include "misc.h"
+#include "stream.h"
-int nproc;
+int nproc, debug;
+
+static char *fmts[] = {
+ [FmtAV1] = "av01",
+ [FmtVP9] = "vp09",
+ [FmtVP8] = "vp08",
+ [FmtAAC] = "mp4a",
+ [FmtOpus] = "opus",
+};
+
+int
+str2fmt(char *s)
+{
+ int i;
+
+ for(i = 0; i < nelem(fmts) && strcmp(fmts[i], s) != 0; i++);
+
+ return i < nelem(fmts) ? i : -1;
+}
uvlong
nanosec(void)
--- a/misc.h
+++ b/misc.h
@@ -1,3 +1,4 @@
+int str2fmt(char *s);
uvlong nanosec(void);
void yuv420_rgb24(
u32int width, u32int height,
@@ -5,3 +6,4 @@
u8int *RGB, u32int RGB_stride);
extern int nproc;
+extern int debug;
--- a/stream.c
+++ b/stream.c
@@ -9,8 +9,8 @@
char *name;
Streamops *o;
}ops[] = {
- {"ivf", &ivfops},
{"mp4", &mp4ops},
+ {"ivf", &ivfops},
};
Stream *
@@ -21,10 +21,8 @@
for(i = 0; i < nelem(ops); i++){
failed = 0;
- if((s = ops[i].o->open(filename, &failed, num)) != nil){
- memmove(&s->ops, ops[i].o, sizeof(Streamops));
+ if((s = ops[i].o->open(filename, &failed, num)) != nil)
return s;
- }
if(failed){
werrstr("%s: %r", ops[i].name);
return nil;
--- a/stream.h
+++ b/stream.h
@@ -26,7 +26,7 @@
};
struct Streamops {
- Stream *(*open)(char *filename, int *num, int *failed);
+ Stream *(*open)(char *path, int *num, int *failed);
u8int *(*alloc)(void *aux, int sz);
void (*close)(Stream *s);
vlong (*offset)(Stream *s);
@@ -69,3 +69,5 @@
vlong Soffset(Stream *s);
void Sclose(Stream *s);
int Sread(Stream *s, Streamframe *f);
+
+int ivfopenb(void *bio, Stream *s, int *failed);
--- a/stream_ivf.c
+++ b/stream_ivf.c
@@ -3,6 +3,8 @@
#include <bio.h>
#include "stream.h"
+extern Streamops ivfops;
+
static int
Bu16le(Biobuf *b, u16int *o)
{
@@ -49,23 +51,28 @@
return 0;
}
-static Stream *
-ivfopen(char *filename, int *num, int *failed)
+int
+ivfopenb(void *bio, Stream *s, 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(bio == nil){
+ werrstr("nil bio");
+ return -1;
}
- if(hlen < 0x20 || Bread(b, tmp, 4) != 4){
+ 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;
}
@@ -81,18 +88,15 @@
goto err;
}
- if(Bu16le(b, &w) < 0 || Bu16le(b, &h) < 0 || Bu32le(b, &tbdenum) < 0 || Bu32le(b, &tbnum) < 0){
+ 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(b, hlen, 0) != hlen){
+ if(Bseek(bio, 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;
@@ -99,14 +103,34 @@
s->video.h = h;
s->timebase.denum = tbdenum;
s->timebase.num = tbnum;
- s->b = b;
+ s->b = bio;
+ memmove(&s->ops, &ivfops, sizeof(ivfops));
- return s;
+ return 0;
err:
*failed = 1;
- Bterm(b);
+ Bterm(bio);
free(s);
- return nil;
+ 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), s, failed) != 0){
+ free(s);
+ s = nil;
+ }
+
+ return s;
}
static void
--- a/stream_mp4.c
+++ b/stream_mp4.c
@@ -2,37 +2,108 @@
#include <libc.h>
#include <bio.h>
#include "stream.h"
+#include "misc.h"
-static Stream *
-mp4open(char *filename, int *num, int *failed)
+enum {
+ Maxstreams = 2,
+};
+
+static int
+mcfs(char **argv, int *pipefd)
{
- USED(filename); USED(num); USED(failed);
- return nil;
+ int p[2], pid, fd;
+
+ pipe(p);
+ if((pid = rfork(RFPROC|RFFDG|RFREND)) == 0){
+ close(0);
+ dup(p[1], 1); close(p[1]);
+ if(!debug){
+ dup(fd = open("/dev/null", OWRITE), 2);
+ close(fd);
+ }
+ exec("/bin/mcfs", argv);
+ sysfatal("exec: %r");
+ }
+ close(p[1]);
+ if(pid < 0)
+ close(p[0]);
+ *pipefd = p[0];
+
+ return pid;
}
-static void
-mp4close(Stream *s)
+static Stream *
+mp4open(char *filename, int *num, int *failed)
{
- free(s);
-}
+ Waitmsg *w;
+ char *line;
+ int p, pid, n, ns;
+ Biobuf b;
+ char *argv[] = {
+ "mcfs",
+ "-i",
+ filename,
+ nil,
+ nil,
+ };
+ Stream *streams;
+ int nvideo, naudio, sp;
+ char *v[8];
-static int
-mp4read(Stream *s, Streamframe *f)
-{
- USED(s); USED(f);
- return -1;
-}
+ if((pid = mcfs(argv, &p)) < 0)
+ return nil;
+ if((streams = calloc(Maxstreams, sizeof(Stream))) == nil)
+ return nil;
-static vlong
-mp4offset(Stream *s)
-{
- USED(s);
- return -1;
+ Binit(&b, p, OREAD);
+ for(ns = naudio = nvideo = 0; ns < Maxstreams && (line = Brdstr(&b, '\n', 1)) != nil;){
+ n = tokenize(line, v, nelem(v));
+ if(n > 4 && str2fmt(v[2]) >= 0){
+ argv[1] = "-t";
+ argv[2] = v[0]; /* stream id */
+ argv[3] = filename;
+
+ if(nvideo < 1 && strcmp(v[1], "video") == 0){
+ if(mcfs(argv, &sp) > 0 && ivfopenb(Bfdopen(sp, OREAD), streams+ns, failed) == 0){
+ nvideo++;
+ ns++;
+ }
+ }else if(naudio < 1 && strcmp(v[1], "audio") == 0){
+ /* FIXME add audio streams */
+ }
+ }
+ free(line);
+ }
+ Bterm(&b);
+
+ *num = ns;
+ if(ns < 1){
+ werrstr("no streams");
+ *failed = 1;
+
+ free(streams);
+ streams = nil;
+
+ while((w = wait()) != nil){
+ if(w->pid == pid){
+ if(w->msg[0] != 0){
+ werrstr("%s", w->msg);
+ goto err;
+ }
+ free(w);
+ break;
+ }
+ free(w);
+ }
+ }
+
+ return streams;
+err:
+ free(w);
+ free(streams);
+ return nil;
}
Streamops mp4ops = {
.open = mp4open,
- .close = mp4close,
- .read = mp4read,
- .offset = mp4offset,
};