shithub: rtmp

Download patch

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;