shithub: rtmp

Download patch

ref: 09b812b49d237cfdcd53273b33dea9ccc355fe9a
parent: 36986cc948a8f422bed5631bf3582ce5657bcde3
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Tue Aug 3 08:26:34 EDT 2021

send video data

--- a/amf0.c
+++ b/amf0.c
@@ -77,6 +77,17 @@
 }
 
 u8int *
+a₀i32le(u8int *p, u8int *e, s32int i)
+{
+	atleast("i32le", 4);
+	*p++ = i;
+	*p++ = i >> 8;
+	*p++ = i >> 16;
+	*p++ = i >> 24;
+	return p;
+}
+
+u8int *
 a₀num(u8int *p, u8int *e, double v)
 {
 	union {
--- a/amf0.h
+++ b/amf0.h
@@ -33,6 +33,7 @@
 u8int *a₀byte(u8int *p, u8int *e, u8int byte);
 u8int *a₀i24(u8int *p, u8int *e, s32int i);
 u8int *a₀i32(u8int *p, u8int *e, s32int i);
+u8int *a₀i32le(u8int *p, u8int *e, s32int i);
 u8int *a₀num(u8int *p, u8int *e, double v);
 u8int *a₀str(u8int *p, u8int *e, char *s);
 u8int *a₀arr(u8int *p, u8int *e);
--- a/main.c
+++ b/main.c
@@ -59,9 +59,9 @@
 
 	if(rtmpstream(r, &sid) == 0){
 		fprint(2, "stream: %lud\n", sid);
-		if(rtmppublish(r, sid, PubLive, "live") == 0){
+		if(rtmppublish(r, sid, PubLive, "???") == 0){
 			fprint(2, "stream published\n");
-			if(rtmpmeta(r, sid, VcodecH264, 1920, 1080, -1) == 0)
+			if(rtmpmeta(r, sid, VcodecH264, 1920, 1088, -1) == 0)
 				fprint(2, "metadata sent\n");
 			else
 				fprint(2, "metadata failed: %r\n");
@@ -79,7 +79,7 @@
 		if(f.sz == 0)
 			break;
 		ns = ivfns(&ivf, f.ts);
-		if(rtmpdata(r, sid, (ns - ons) / 1000000ULL, Tvideo, FlHdr, f.buf, f.sz) != 0)
+		if(rtmpdata(r, sid, (ns - ons) / 1000000ULL, Tvideo, f.buf, f.sz) != 0)
 			sysfatal("video: flvdata: %r");
 		ons = ns;
 	}
--- a/rtmp.c
+++ b/rtmp.c
@@ -14,8 +14,11 @@
 	Port = 1935,
 	CSsz = 1536,
 	ChunkDefault = 128,
-	ChunkDesired = 4096,
+	ChunkDesired = 65536,
 
+	FlKey = 1<<0,
+	FlHdr = 1<<1,
+
 	Type0 = 0,
 	Type1,
 	Type2,
@@ -23,6 +26,7 @@
 
 	CSUserCtl = 2,
 	CSCtl = 3,
+	CSData = 4,
 
 	CbCommand = 0,
 	CbTransID,
@@ -76,6 +80,7 @@
 	void (*cb)(RTMP *r, int ok, A₀ *a[NumCb], void *aux);
 	void *aux;
 	int tid;
+	int cs;
 
 	Command *prev, *next;
 };
@@ -117,6 +122,13 @@
 		int tid;
 		Command *w;
 	}cmds;
+	struct {
+		int hsent;
+		int spssz;
+		int ppssz;
+		u8int sps[128];
+		u8int pps[128];
+	}v;
 	u8int biobuf[Biobufsz];
 };
 
@@ -142,6 +154,11 @@
 	r->o.msg.cmd.cb = cb_; \
 }while(0)
 
+#define putcall(name) do { \
+	putstr(name); \
+	putnum(r->o.msg.cmd.tid); \
+}while(0)
+
 static int szs[] = {
 	[Type3] = 0,
 	[Type2] = 3,
@@ -204,6 +221,7 @@
 		r->o.msg.cmd.tid = ++r->cmds.tid;
 	else
 		r->o.msg.cmd.tid = 0;
+	r->o.msg.cmd.cs = cs;
 }
 
 static void
@@ -328,12 +346,12 @@
 	hsz = szs[m->fmt];
 	e = h + hsz;
 	if(hsz >= szs[Type2]){
-		h = a₀i24(h, e, 0); /* FIXME put actual timestamps? */
+		h = a₀i24(h, e, m->ts);
 		if(hsz >= szs[Type1]){
 			h = a₀i24(h, e, m->sz);
 			h = a₀byte(h, e, m->type);
 			if(hsz >= szs[Type0])
-				h = a₀i32(h, e, m->sid);
+				h = a₀i32le(h, e, m->sid);
 		}
 	}
 	assert(h != nil);
@@ -487,7 +505,14 @@
 						werrstr("transaction ID is not a number");
 						goto err;
 					}
-					for(c = r->cmds.w; c != nil && c->tid != a[n]->num; c = c->next);
+					for(c = r->cmds.w; c != nil; c = c->next){
+						/* transaction id match */
+						if(c->tid == a[n]->num)
+							break;
+						/* no transaction id, but the chunk stream matches */
+						if(a[n]->num == 0 && c->cs == m->cs)
+							break;
+					}
 					if(c == nil)
 						fprint(2, "response to non-existent transaction %d", (int)a[n]->num);
 					break;
@@ -648,8 +673,7 @@
 	qlock(r);
 
 	newmsg(r, AMF0Command, Type0, CSCtl);
-	putstr("createStream");
-	putnum(r->o.msg.cmd.tid);
+	putcall("createStream");
 	putnull();
 
 	r->o.msg.cmd.cb = streamcreated;
@@ -698,10 +722,8 @@
 
 	qlock(r);
 
-	newmsg(r, AMF0Command, Type0, CSCtl);
-	notransaction(r);
-	putstr("publish");
-	putnum(0);
+	newmsg(r, AMF0Command, Type0, CSData);
+	putcall("publish");
 	putnull();
 	putstr(name);
 	putstr(pubtype2s[type]);
@@ -735,12 +757,13 @@
 
 	qlock(r);
 
-	newmsg(r, AMF0Metadata, Type0, CSCtl);
-	r->o.msg.sid = sid;
+	newmsg(r, AMF0Metadata, Type0, CSData);
 
+	putstr("@setDataFrame");
+
 	putstr("onMetaData");
-	putnum(0);
-	putobj();
+	putarr();
+	puti32(1 + (vcodec < 0 ? 0 : 3) + (acodec < 0 ? 0 : 1));
 	putkvnum("duration", 0.0);
 	if(vcodec >= 0){
 		putkvnum("videocodecid", vcodec);
@@ -750,6 +773,7 @@
 	if(acodec >= 0)
 		putkvnum("audiocodecid", acodec);
 	putend();
+	r->o.msg.sid = sid;
 
 	res = rtmpsend(r);
 
@@ -758,18 +782,59 @@
 	return res;
 }
 
-int
-rtmpdata(RTMP *r, ulong sid, u32int dt, int type, int fl, void *data, int sz)
+static int
+nalsz(u8int *p, int sz, int *csz)
 {
-	int res;
+	int n;
 
-	assert(type == Taudio || type == Tvideo);
+	for(n = 3; sz-n > 3; n++){
+		if(p[n] == 0 && p[n+1] == 0 && p[n+2] == 1){
+			*csz = 3;
+			return n;
+		}
+		if(p[n] == 0 && p[n+1] == 0 && p[n+2] == 0 && p[n+3] == 1){
+			*csz = 4;
+			return n;
+		}
+	}
 
-	qlock(r);
+	*csz = 0;
 
+	return sz;
+}
+
+static int
+mkps(RTMP *r, u8int *p)
+{
+	u8int *p₀;
+
+	p₀ = p;
+
+	*p++ = 1;   /* version */
+	*p++ = r->v.sps[1]; /* profile */
+	*p++ = r->v.sps[2]; /* compatibility */
+	*p++ = r->v.sps[3]; /* level */
+	*p++ = 0xfc | 3; /* reserved (6 bits), NULA length size - 1 (2 bits) */
+	*p++ = 0xe0 | 1; /* reserved (3 bits), num of SPS (5 bits) */
+	*p++ = r->v.spssz >> 8;
+	*p++ = r->v.spssz;
+	p = (u8int*)memmove(p, r->v.sps, r->v.spssz) + r->v.spssz;
+
+	*p++ = 1;
+	*p++ = r->v.ppssz >> 8;
+	*p++ = r->v.ppssz;
+	p = (u8int*)memmove(p, r->v.pps, r->v.ppssz) + r->v.ppssz;
+
+	return p - p₀;
+}
+
+static int
+data(RTMP *r, ulong sid, u32int dt, int type, int fl, void *data, int sz)
+{
 	bextend(&r->o, 64 + sz);
 
-	newmsg(r, type == Taudio ? Audio : Video, Type0, CSCtl);
+	newmsg(r, type == Taudio ? Audio : Video, Type0, CSData);
+	r->o.msg.ts = dt;
 	r->o.msg.sid = sid;
 
 	if(type == Taudio){
@@ -779,14 +844,67 @@
 	if(type == Tvideo){
 		putbyte(((fl & FlKey) ? 0x10 : 0x20) | VcodecH264);
 		putbyte((fl & FlHdr) ? 0 : 1);
-		puti24(dt);
+		puti24(0);
 		if((fl & FlHdr) == 0)
 			puti32(sz);
 	}
 	putdata(data, sz);
 
-	res = rtmpsend(r);
+	return rtmpsend(r);
+}
 
+static int
+h264data(RTMP *r, ulong sid, u32int dt, u8int *p, int sz)
+{
+	int csz, nsz, ntype;
+	u8int ps[32+256];
+
+	for(; sz > 0; p += nsz, sz -= nsz){
+		nsz = nalsz(p, sz, &csz);
+		p += csz;
+		sz -= csz;
+		nsz -= csz;
+
+		ntype = *p & 0x1f;
+		if(ntype == 7){
+			memmove(r->v.sps, p, nsz);
+			r->v.spssz = nsz;
+			continue;
+		}
+		if(ntype == 8){
+			memmove(r->v.pps, p, nsz);
+			r->v.ppssz = nsz;
+			continue;
+		}
+
+		if(r->v.spssz < 1 || r->v.ppssz < 1)
+			continue;
+
+		if(!r->v.hsent || ntype == 8){
+			r->v.hsent = 1;
+			if(data(r, sid, 0, Tvideo, FlKey|FlHdr, ps, mkps(r, ps)) < 0)
+				return -1;
+		}
+		if(ntype != 7 && ntype != 8){
+            if(data(r, sid, 100, Tvideo, ntype == 5 ? FlKey : 0, p-csz, nsz+csz) < 0)
+            	return -1;
+		}
+	}
+
+	return 0;
+}
+
+int
+rtmpdata(RTMP *r, ulong sid, u32int dt, int type, void *p, int sz)
+{
+	int res;
+
+	assert(type == Taudio || type == Tvideo);
+
+	qlock(r);
+	res = 0;
+	if(type == Tvideo)
+		res = h264data(r, sid, dt, p, sz);
 	qunlock(r);
 
 	return res;
@@ -817,6 +935,7 @@
 	putcommand("connect", connected);
 		putkvstr("app", r->app);
 		putkvstr("tcUrl", r->tcurl);
+		putkvstr("type", "nonprivate");
 		putkvbool("fpad", 0); /* no proxy */
 		putkvnum("audioCodecs", 0x4 | 0x400); /* mp3 + aac */
 		putkvnum("videoCodecs", 0x80); /* h.264 */
@@ -907,7 +1026,6 @@
 		*s = 0;
 		path = s+1;
 	}else{
-		*s = 0;
 		path = nil;
 	}
 
--- a/rtmp.h
+++ b/rtmp.h
@@ -20,11 +20,8 @@
 enum {
 	Taudio,
 	Tvideo,
-
-	FlKey = 1<<0,
-	FlHdr = 1<<1,
 };
-int rtmpdata(RTMP *r, ulong sid, u32int ts, int type, int fl, void *data, int sz);
+int rtmpdata(RTMP *r, ulong sid, u32int dt, int type, void *data, int sz);
 
 RTMP *rtmpdial(char *url);
 void rtmpclose(RTMP *r);