ref: b93aad5650ddf5cd776a0e7c827eaeae750b251c
parent: 6fb80473c5f8b70a64b6209fdd1c6aa071e8529f
author: Sigrid Solveig Haflínudóttir <[email protected]>
date: Fri Aug 6 07:02:22 EDT 2021
audio WIP
--- a/README.md
+++ b/README.md
@@ -10,3 +10,7 @@
provide. All you need is the correct RTMP URL. Preferably of a
server that is close to you, for lower latency, see [the list of
ingest endpoints](https://stream.twitch.tv/ingests).
+
+## Audio
+
+This is still WIP. It will not work properly.
--- /dev/null
+++ b/adts.c
@@ -1,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "adts.h"
+#include "util.h"
+
+int
+adtsread(Biobuf *b, ADTSFrame *f)
+{
+ u8int h[7];
+
+ if(Bread(b, h, 7) != 7)
+ goto err;
+ if(h[0] != 0xff || (h[1]&0xf0) != 0xf0){
+ werrstr("bad sync word");
+ goto err;
+ }
+
+ f->sz = h[4]<<3 | h[5]>>5;
+ if(f->sz < 7){
+ werrstr("frame too small (%d bytes)", f->sz);
+ goto err;
+ }
+ if(f->sz > f->bufsz){
+ f->bufsz = f->sz*2;
+ f->buf = erealloc(f->buf, f->bufsz);
+ }
+
+ memmove(f->buf, h, 7);
+ if(Bread(b, f->buf+7, f->sz-7) != f->sz-7)
+ goto err;
+
+ return 0;
+
+err:
+ werrstr("adtsread: %r");
+ return -1;
+}
--- /dev/null
+++ b/adts.h
@@ -1,0 +1,9 @@
+typedef struct ADTSFrame ADTSFrame;
+
+struct ADTSFrame {
+ u8int *buf;
+ int bufsz;
+ int sz;
+};
+
+int adtsread(Biobuf *b, ADTSFrame *f);
--- a/main.c
+++ b/main.c
@@ -2,6 +2,7 @@
#include <libc.h>
#include <thread.h>
#include <bio.h>
+#include "adts.h"
#include "ivf.h"
#include "rtmp.h"
#include "util.h"
@@ -20,9 +21,10 @@
threadmain(int argc, char **argv)
{
Biobuf *a, v;
+ ADTSFrame af;
+ IVFrame vf;
u64int ns;
ulong sid;
- IVFrame f;
IVF ivf;
RTMP *r;
@@ -52,18 +54,31 @@
if(rtmpstream(r, &sid) != 0 ||
rtmppublish(r, sid, PubLive, nil) != 0 ||
- rtmpmeta(r, sid, VcodecH264, ivf.w, ivf.h, -1) != 0){
+ rtmpmeta(r, sid, VcodecH264, ivf.w, ivf.h, a != nil ? AcodecAAC : -1) != 0){
sysfatal("%r");
}
- memset(&f, 0, sizeof(f));
+ memset(&af, 0, sizeof(af));
+ memset(&vf, 0, sizeof(vf));
for(;;){
- if(ivfread(&v, &f) != 0)
+ if(ivfread(&v, &vf) != 0)
sysfatal("%r");
- if(f.sz == 0)
+ if(vf.sz == 0)
break;
- ns = ivfns(&ivf, f.ts)/1000000ULL;
- if(rtmpdata(r, sid, ns, Tvideo, f.buf, f.sz) != 0){
+ ns = ivfns(&ivf, vf.ts)/1000000ULL;
+ if(rtmpdata(r, sid, ns, Tvideo, vf.buf, vf.sz) != 0){
+ fprint(2, "%r\n");
+ break;
+ }
+
+ /* FIXME obviously this has to run in a separate frame (same for video, actually) */
+ if(a == nil)
+ continue;
+ if(adtsread(a, &af) != 0)
+ sysfatal("%r");
+ if(af.sz == 0)
+ break;
+ if(rtmpdata(r, sid, ns, Taudio, af.buf, af.sz) != 0){
fprint(2, "%r\n");
break;
}
--- a/mkfile
+++ b/mkfile
@@ -4,6 +4,7 @@
TARG=rtmp
HFILES=\
+ adts.h\
amf0.h\
ivf.h\
rtmp.h\
@@ -10,6 +11,7 @@
util.h\
OFILES=\
+ adts.$O\
amf0.$O\
ivf.$O\
main.$O\
--- a/rtmp.c
+++ b/rtmp.c
@@ -16,8 +16,8 @@
ChunkDefault = 128,
ChunkDesired = 65536,
- FlKey = 1<<0,
- FlHdr = 1<<1,
+ DataHdr = 0,
+ DataFrame,
Type0 = 0,
Type1,
@@ -125,6 +125,7 @@
Command *w;
}cmds;
int sps;
+ int aacpd;
u8int biobuf[Biobufsz];
};
@@ -847,11 +848,11 @@
if(spssz > 0 && ppssz > 0 && !r->sps){
newmsg(r, Video, Type0, CSData);
- r->o.msg.ts = dt;
+ r->o.msg.ts = 0;
r->o.msg.sid = sid;
putbyte(0x10 | VcodecH264);
- putbyte(0);
+ putbyte(DataHdr);
puti24(0);
p = ps;
@@ -884,7 +885,7 @@
r->o.msg.sid = sid;
putbyte((key ? 0x10 : 0x20) | VcodecH264);
- putbyte(1);
+ putbyte(DataFrame);
puti24(0);
for(p = p₀, sz = sz₀; sz > 0; p += nsz, sz -= nsz){
@@ -900,6 +901,69 @@
return rtmpsend(r);
}
+static int
+aacdata(RTMP *r, ulong sid, u32int dt, u8int *p, int sz)
+{
+ int chanc, ratei, objt;
+ u16int x;
+
+ if(sz < 7){
+ werrstr("aac frame too small");
+ return -1;
+ }
+
+ if(!r->aacpd){
+ newmsg(r, Audio, Type0, CSData);
+ r->o.msg.ts = 0;
+ r->o.msg.sid = sid;
+
+ putbyte(AcodecAAC<<4 | 0xf);
+ putbyte(DataHdr);
+
+ objt = (p[2]>>6) + 1;
+ ratei = (p[2]>>2) & 7;
+ chanc = (p[2]&3)<<2 | p[3]>>6;
+
+ if(chanc > 7){
+ werrstr("channel config out of range: %d", chanc);
+ return -1;
+ }else if(ratei > 12){
+ werrstr("invalid rate config: %d", ratei);
+ return -1;
+ }else if(objt > 4){
+ werrstr("object type out of range: %d", objt);
+ return -1;
+ }
+
+ x = chanc<<3 | ratei<<7 | objt<<11;
+ putbyte(x>>8);
+ putbyte(x);
+
+ /* FIXME wtf */
+ putbyte(0x56);
+ putbyte(0xe5);
+ putbyte(0x00);
+
+ if(rtmpsend(r) < 0)
+ return -1;
+
+ r->aacpd = 1;
+ }
+
+ bextend(&r->o, sz);
+
+ newmsg(r, Audio, Type0, CSData);
+ r->o.msg.ts = dt;
+ r->o.msg.sid = sid;
+
+ putbyte(AcodecAAC<<4 | 0xf);
+ putbyte(DataFrame);
+
+ putdata(p+7, sz-7);
+
+ return rtmpsend(r);
+}
+
int
rtmpdata(RTMP *r, ulong sid, u32int dt, int type, void *p, int sz)
{
@@ -908,9 +972,7 @@
assert(type == Taudio || type == Tvideo);
qlock(r);
- res = 0;
- if(type == Tvideo)
- res = h264data(r, sid, dt, p, sz);
+ res = (type == Tvideo ? h264data : aacdata)(r, sid, dt, p, sz);
qunlock(r);
return res;