shithub: rtmp

Download patch

ref: 0ec0e4fde657c442431548180c91018078c2bf3c
parent: 3168be131e24f503aabda0f0bdf690136eaef03b
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Wed Jul 21 11:35:27 EDT 2021

split more

--- /dev/null
+++ b/amf.c
@@ -1,0 +1,143 @@
+#include <u.h>
+#include <libc.h>
+#include "amf.h"
+
+enum {
+	Anum,
+	Aarr = 8,
+	Aend,
+	Alstr = 12,
+};
+
+u8int *
+amfi16(u8int *p, u8int *e, s16int i)
+{
+	if(p == nil)
+		return nil;
+	if(e-p < 2){
+		werrstr("buffer short");
+		return nil;
+	}
+	*p++ = i >> 8;
+	*p++ = i;
+
+	return p;
+}
+
+u8int *
+amfi24(u8int *p, u8int *e, s32int i)
+{
+	if(p == nil)
+		return nil;
+	if(e-p < 3){
+		werrstr("buffer short");
+		return nil;
+	}
+	*p++ = i >> 16;
+	*p++ = i >> 8;
+	*p++ = i;
+
+	return p;
+}
+
+u8int *
+amfi32(u8int *p, u8int *e, s32int i)
+{
+	if(p == nil)
+		return nil;
+	if(e-p < 4){
+		werrstr("buffer short");
+		return nil;
+	}
+	*p++ = i >> 24;
+	*p++ = i >> 16;
+	*p++ = i >> 8;
+	*p++ = i;
+
+	return p;
+}
+
+u8int *
+amfnum(u8int *p, u8int *e, double v)
+{
+	union {
+		double v;
+		u64int u;
+	}x;
+
+	if(p == nil)
+		return nil;
+	if(p+8 > e){
+		werrstr("buffer short");
+		return nil;
+	}
+	x.v = v;
+	*p++ = x.u >> 56;
+	*p++ = x.u >> 48;
+	*p++ = x.u >> 40;
+	*p++ = x.u >> 32;
+	*p++ = x.u >> 24;
+	*p++ = x.u >> 16;
+	*p++ = x.u >> 8;
+	*p++ = x.u;
+
+	return p;
+}
+
+u8int *
+amfkvnum(u8int *p, u8int *e, char *name, double v)
+{
+	int n;
+
+	if(p == nil)
+		return nil;
+	if((n = strlen(name)) > 0xffff){
+		werrstr("string too long");
+		return nil;
+	}
+	if(p+2+n+8 > e){
+		werrstr("buffer short");
+		return nil;
+	}
+	p = amfi16(p, e, n);
+	p = (u8int*)memmove(p, name, n) + n;
+
+	return amfnum(p, e, v);
+}
+
+u8int *
+amfstr(u8int *p, u8int *e, char *s)
+{
+	int n;
+
+	if(p == nil)
+		return nil;
+	n = strlen(s);
+	if(p+1+4+n > e){
+		werrstr("string too long");
+		return nil;
+	}
+	*p++ = Alstr;
+
+	return (u8int*)memmove(amfi32(p, e, n), s, n) + n;
+}
+
+u8int *
+amfarr(u8int *p, u8int *e)
+{
+	if(p == nil)
+		return nil;
+	if(p == e){
+		werrstr("buffer short");
+		return nil;
+	}
+	*p++ = Aarr;
+
+	return p;
+}
+
+u8int *
+amfend(u8int *p, u8int *e)
+{
+	return amfi24(p, e, Aend);
+}
--- /dev/null
+++ b/amf.h
@@ -1,0 +1,8 @@
+u8int *amfi16(u8int *p, u8int *e, s16int i);
+u8int *amfi24(u8int *p, u8int *e, s32int i);
+u8int *amfi32(u8int *p, u8int *e, s32int i);
+u8int *amfnum(u8int *p, u8int *e, double v);
+u8int *amfkvnum(u8int *p, u8int *e, char *name, double v);
+u8int *amfstr(u8int *p, u8int *e, char *s);
+u8int *amfarr(u8int *p, u8int *e);
+u8int *amfend(u8int *p, u8int *e);
--- /dev/null
+++ b/flv.c
@@ -1,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include "amf.h"
+#include "flv.h"
+
+enum {
+	EncH264 = 7,
+	EncAAC = 10,
+};
+
+u8int *
+flvscript(u8int *p, u8int *e, int w, int h, int audio)
+{
+	u8int *psz, *d, *p0;
+	int stream;
+	u32int ts;
+
+	if(p+16 > e){
+		werrstr("buffer short");
+		return nil;
+	}
+
+	/* FIXME ever need to change these? */
+	stream = 0;
+	ts = 0;
+
+	p0 = p;
+	*p++ = Fvideo;
+	psz = p;
+	p = amfi24(p, e, 0); /* sz set later */
+	p = amfi24(p, e, ts);
+	*p++ = ts>>24;
+	p = amfi24(p, e, stream);
+
+	d = p;
+	p = amfstr(p, e, "onMetaData");
+	p = amfarr(p, e);
+	p = amfi32(p, e, audio ? 5 : 4);
+	p = amfkvnum(p, e, "duration", 0.0);
+	p = amfkvnum(p, e, "width", w);
+	p = amfkvnum(p, e, "height", h);
+	p = amfkvnum(p, e, "videocodecid", EncH264);
+	if(audio)
+		p = amfkvnum(p, e, "audiocodecid", EncAAC);
+	p = amfend(p, e);
+	amfi24(psz, e, p-d);
+
+	return amfi32(p, e, p-p0);
+}
+
+u8int *
+flvdata(u8int *p, u8int *e, u32int pts, u32int dts, void *data, int sz, int type, int fl)
+{
+	u8int *p0, *psz, *d;
+	int stream;
+
+	/* FIXME ever need to change these? */
+	stream = 0;
+
+	assert(type == Faudio || type == Fvideo);
+	p0 = p;
+	*p++ = type;
+	psz = p;
+	p = amfi24(p, e, 0); /* size to be set later */
+	p = amfi24(p, e, dts);
+	*p++ = dts >> 24;
+	p = amfi24(p, e, stream);
+
+	d = p;
+	if(type == Faudio){
+		*p++ = (EncAAC<<4) | 0x0f;
+		*p++ = (fl & FlHdr) ? 0 : 1;
+	}
+	if(type == Fvideo){
+		*p++ = ((fl & FlKey) ? 0x10 : 0x20) | EncH264;
+		*p++ = (fl & FlHdr) ? 0 : 1;
+		pts = ((fl & FlHdr) || pts < dts) ? 0 : (pts - dts);
+		p = amfi24(p, e, pts);
+		if((fl & FlHdr) == 0)
+			p = amfi32(p, e, sz);
+	}
+	p = (u8int*)memmove(p, data, sz) + sz;
+	amfi24(psz, e, p-d);
+
+	return amfi32(p, e, p-p0);
+}
--- /dev/null
+++ b/flv.h
@@ -1,0 +1,11 @@
+enum {
+	Faudio = 8,
+	Fvideo = 9,
+	Fscript = 18,
+
+	FlKey = 1<<0,
+	FlHdr = 1<<1,
+};
+
+u8int *flvscript(u8int *p, u8int *e, int w, int h, int audio);
+u8int *flvdata(u8int *p, u8int *e, u32int pts, u32int dts, void *data, int sz, int type, int fl);
--- /dev/null
+++ b/main.c
@@ -1,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include "ivf.h"
+#include "flv.h"
+#include "rtmp.h"
+
+int mainstacksize = 65536;
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-a AUDIO] -v VIDEO [URL]\n", argv0);
+	threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	Biobuf *a, *v, o;
+	u8int *b, *p, *e;
+	int bufsz;
+	u64int ns;
+	IVFrame f;
+	IVF ivf;
+	RTMP *r;
+
+	a = nil;
+	v = nil;
+	ARGBEGIN{
+	case 'a':
+		if((a = Bopen(EARGF(usage()), OREAD)) == nil)
+			sysfatal("%r");
+		break;
+	case 'v':
+		if((v = Bopen(EARGF(usage()), OREAD)) == nil)
+			sysfatal("%r");
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 1)
+		usage();
+	if(v == nil)
+		sysfatal("no video specified");
+	if(ivfopen(v, &ivf) != 0)
+		sysfatal("%r");
+	if(strcmp(ivf.type, "AVC1") != 0)
+		sysfatal("not H.264");
+	srand(time(nil));
+	if((r = rtmpdial(argv[0])) < 0 || Binit(&o, fd, OWRITE) < 0)
+		sysfatal("%r");
+
+	bufsz = 65536;
+	if((b = malloc(bufsz)) == nil)
+		sysfatal("memory");
+	e = b + bufsz;
+
+	if((p = flvscript(b, e, ivf.w, ivf.h, 0)) == nil || Bwrite(&o, b, p-b) < 0)
+		sysfatal("%r");
+
+	memset(&f, 0, sizeof(f));
+	for(;;){
+		if(ivfread(v, &f) != 0)
+			sysfatal("%r");
+		if(f.sz == 0)
+			break;
+		if(bufsz < f.sz+64){
+			free(b);
+			bufsz *= 2;
+			if((b = malloc(bufsz)) == nil)
+				sysfatal("memory");
+			e = b + bufsz;
+		}
+		ns = ivfns(&ivf, f.ts);
+		if((p = flvdata(b, e, ns, ns, f.buf, f.sz, Fvideo, FlHdr)) == nil)
+			sysfatal("video: flvdata: %r");
+		if(Bwrite(&o, b, p-b) < 0)
+			sysfatal("%r");
+		Bflush(&o);
+	}
+
+	threadexitsall(nil);
+}
--- a/mkfile
+++ b/mkfile
@@ -4,10 +4,16 @@
 TARG=rtmp
 
 HFILES=\
+	amf.h\
+	flv.h\
 	ivf.h\
+	rtmp.h\
 
 OFILES=\
+	amf.$O\
+	flv.$O\
 	ivf.$O\
+	main.$O\
 	rtmp.$O\
 
 default:V: all
--- a/rtmp.c
+++ b/rtmp.c
@@ -3,245 +3,43 @@
 #include <thread.h>
 #include <bio.h>
 #include <libsec.h>
+#include "amf.h"
 #include "ivf.h"
+#include "rtmp.h"
 
 enum {
 	Port = 1935,
 
-	Anum,
-	Aarr = 8,
-	Aend,
-	Alstr = 12,
-
-	Faudio = 8,
-	Fvideo = 9,
-	Fscript = 18,
-
-	EncH264 = 7,
-	EncAAC = 10,
-
-	FlKey = 1<<0,
-	FlHdr = 1<<1,
-
 	Sigsz = 1536,
 
 	ChanCtl = 3,
-};
 
-int mainstacksize = 65536;
+	SzLarge,
+	SzMedium,
+	SzSmall,
+	SzTiny,
 
-static u8int *
-amfi16(u8int *p, u8int *e, s16int i)
-{
-	if(p == nil)
-		return nil;
-	if(e-p < 2){
-		werrstr("buffer short");
-		return nil;
-	}
-	*p++ = i >> 8;
-	*p++ = i;
+	PktInvoke = 20,
+};
 
-	return p;
-}
+typedef struct RTMPacket RTMPacket;
 
-static u8int *
-amfi24(u8int *p, u8int *e, s32int i)
-{
-	if(p == nil)
-		return nil;
-	if(e-p < 3){
-		werrstr("buffer short");
-		return nil;
-	}
-	*p++ = i >> 16;
-	*p++ = i >> 8;
-	*p++ = i;
+struct RTMP {
+	int f;
+	int invokes;
+};
 
-	return p;
-}
-
-static u8int *
-amfi32(u8int *p, u8int *e, s32int i)
-{
-	if(p == nil)
-		return nil;
-	if(e-p < 4){
-		werrstr("buffer short");
-		return nil;
-	}
-	*p++ = i >> 24;
-	*p++ = i >> 16;
-	*p++ = i >> 8;
-	*p++ = i;
-
-	return p;
-}
-
-static u8int *
-amfnum(u8int *p, u8int *e, double v)
-{
-	union {
-		double v;
-		u64int u;
-	}x;
-
-	if(p == nil)
-		return nil;
-	if(p+8 > e){
-		werrstr("buffer short");
-		return nil;
-	}
-	x.v = v;
-	*p++ = x.u >> 56;
-	*p++ = x.u >> 48;
-	*p++ = x.u >> 40;
-	*p++ = x.u >> 32;
-	*p++ = x.u >> 24;
-	*p++ = x.u >> 16;
-	*p++ = x.u >> 8;
-	*p++ = x.u;
-
-	return p;
-}
-
-static u8int *
-amfkvnum(u8int *p, u8int *e, char *name, double v)
-{
-	int n;
-
-	if(p == nil)
-		return nil;
-	if((n = strlen(name)) > 0xffff){
-		werrstr("string too long");
-		return nil;
-	}
-	if(p+2+n+8 > e){
-		werrstr("buffer short");
-		return nil;
-	}
-	p = amfi16(p, e, n);
-	p = (u8int*)memmove(p, name, n) + n;
-
-	return amfnum(p, e, v);
-}
-
-static u8int *
-amfstr(u8int *p, u8int *e, char *s)
-{
-	int n;
-
-	if(p == nil)
-		return nil;
-	n = strlen(s);
-	if(p+1+4+n > e){
-		werrstr("string too long");
-		return nil;
-	}
-	*p++ = Alstr;
-
-	return (u8int*)memmove(amfi32(p, e, n), s, n) + n;
-}
-
-static u8int *
-amfarr(u8int *p, u8int *e)
-{
-	if(p == nil)
-		return nil;
-	if(p == e){
-		werrstr("buffer short");
-		return nil;
-	}
-	*p++ = Aarr;
-
-	return p;
-}
-
-static u8int *
-amfend(u8int *p, u8int *e)
-{
-	return amfi24(p, e, Aend);
-}
-
-static u8int *
-flvscript(u8int *p, u8int *e, int w, int h, int audio)
-{
-	u8int *psz, *d, *p0;
-	int stream;
+struct RTMPacket {
+	int chan;
+	int sztype;
+	int pktype;
 	u32int ts;
+	u8int *data;
+	int sz;
+};
 
-	if(p+16 > e){
-		werrstr("buffer short");
-		return nil;
-	}
-
-	/* FIXME ever need to change these? */
-	stream = 0;
-	ts = 0;
-
-	p0 = p;
-	*p++ = Fvideo;
-	psz = p;
-	p = amfi24(p, e, 0); /* sz set later */
-	p = amfi24(p, e, ts);
-	*p++ = ts>>24;
-	p = amfi24(p, e, stream);
-
-	d = p;
-	p = amfstr(p, e, "onMetaData");
-	p = amfarr(p, e);
-	p = amfi32(p, e, audio ? 5 : 4);
-	p = amfkvnum(p, e, "duration", 0.0);
-	p = amfkvnum(p, e, "width", w);
-	p = amfkvnum(p, e, "height", h);
-	p = amfkvnum(p, e, "videocodecid", EncH264);
-	if(audio)
-		p = amfkvnum(p, e, "audiocodecid", EncAAC);
-	p = amfend(p, e);
-	amfi24(psz, e, p-d);
-
-	return amfi32(p, e, p-p0);
-}
-
-static u8int *
-flvdata(u8int *p, u8int *e, u32int pts, u32int dts, void *data, int sz, int type, int fl)
-{
-	u8int *p0, *psz, *d;
-	int stream;
-
-	/* FIXME ever need to change these? */
-	stream = 0;
-
-	assert(type == Faudio || type == Fvideo);
-	p0 = p;
-	*p++ = type;
-	psz = p;
-	p = amfi24(p, e, 0); /* size to be set later */
-	p = amfi24(p, e, dts);
-	*p++ = dts >> 24;
-	p = amfi24(p, e, stream);
-
-	d = p;
-	if(type == Faudio){
-		*p++ = (EncAAC<<4) | 0x0f;
-		*p++ = (fl & FlHdr) ? 0 : 1;
-	}
-	if(type == Fvideo){
-		*p++ = ((fl & FlKey) ? 0x10 : 0x20) | EncH264;
-		*p++ = (fl & FlHdr) ? 0 : 1;
-		pts = ((fl & FlHdr) || pts < dts) ? 0 : (pts - dts);
-		p = amfi24(p, e, pts);
-		if((fl & FlHdr) == 0)
-			p = amfi32(p, e, sz);
-	}
-	p = (u8int*)memmove(p, data, sz) + sz;
-	amfi24(psz, e, p-d);
-
-	return amfi32(p, e, p-p0);
-}
-
 static int
-handshake(int f, char *path)
+handshake(int f)
 {
 	u8int cl[1+Sigsz], sv[1+Sigsz];
 
@@ -265,20 +63,40 @@
 		goto err;
 	}
 
-	return f;
+	return 0;
 
 err:
 	werrstr("handshake: %r");
-	close(f);
 	return -1;
 }
 
 static int
+connect(int f, char *path)
+{
+	RTMPacket p;
+
+	memset(&p, 0, sizeof(p));
+	p.chan = ChanCtl;
+	p.sztype = SzLarge;
+	p.pktype = PktInvoke;
+
+	return -1;
+}
+
+static int
+rtmpsend(RTMP *r, RTMPacket *p)
+{
+	return -1;
+}
+
+RTMP *
 rtmpdial(char *url)
 {
 	char *s, *e, *path;
 	int f, port, ctl;
+	RTMP *r;
 
+	f = -1;
 	if(memcmp(url, "rtmp://", 7) != 0){
 		werrstr("invalid url");
 		goto err;
@@ -304,84 +122,18 @@
 	if(f < 0)
 		goto err;
 
-	return handshake(f, path);
-err:
-	werrstr("rtmpdial: %r");
-	return -1;
-}
+	if(handshake(f) != 0 || connect(f, path) == 0)
+		goto err;
 
-static void
-usage(void)
-{
-	fprint(2, "usage: %s [-a AUDIO] -v VIDEO [URL]\n", argv0);
-	threadexitsall("usage");
-}
-
-void
-threadmain(int argc, char **argv)
-{
-	Biobuf *a, *v, o;
-	u8int *b, *p, *e;
-	int bufsz, fd;
-	u64int ns;
-	IVFrame f;
-	IVF ivf;
-
-	a = nil;
-	v = nil;
-	ARGBEGIN{
-	case 'a':
-		if((a = Bopen(EARGF(usage()), OREAD)) == nil)
-			sysfatal("%r");
-		break;
-	case 'v':
-		if((v = Bopen(EARGF(usage()), OREAD)) == nil)
-			sysfatal("%r");
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	if(argc != 1)
-		usage();
-	if(v == nil)
-		sysfatal("no video specified");
-	if(ivfopen(v, &ivf) != 0)
-		sysfatal("%r");
-	if(strcmp(ivf.type, "AVC1") != 0)
-		sysfatal("not H.264");
-	srand(time(nil));
-	if((fd = rtmpdial(argv[0])) < 0 || Binit(&o, fd, OWRITE) < 0)
-		sysfatal("%r");
-
-	bufsz = 65536;
-	if((b = malloc(bufsz)) == nil)
+	if((r = calloc(1, sizeof(*r))) == nil)
 		sysfatal("memory");
-	e = b + bufsz;
+	r->f = f;
 
-	if((p = flvscript(b, e, ivf.w, ivf.h, 0)) == nil || Bwrite(&o, b, p-b) < 0)
-		sysfatal("%r");
+	return r;
 
-	memset(&f, 0, sizeof(f));
-	for(;;){
-		if(ivfread(v, &f) != 0)
-			sysfatal("%r");
-		if(f.sz == 0)
-			break;
-		if(bufsz < f.sz+64){
-			free(b);
-			bufsz *= 2;
-			if((b = malloc(bufsz)) == nil)
-				sysfatal("memory");
-			e = b + bufsz;
-		}
-		ns = ivfns(&ivf, f.ts);
-		if((p = flvdata(b, e, ns, ns, f.buf, f.sz, Fvideo, FlHdr)) == nil)
-			sysfatal("video: flvdata: %r");
-		if(Bwrite(&o, b, p-b) < 0)
-			sysfatal("%r");
-		Bflush(&o);
-	}
-
-	threadexitsall(nil);
+err:
+	werrstr("rtmpdial: %r");
+	if(f >= 0)
+		close(f);
+	return nil;
 }
--- /dev/null
+++ b/rtmp.h
@@ -1,0 +1,5 @@
+typedef struct RTMP RTMP;
+
+#pragma incomplete RTMP
+
+RTMP *rtmpdial(char *url);