shithub: rtmp

Download patch

ref: 139fd1d0439e7fc8e8672a371e4c53f7d609e431
parent: edfe7779751000c6f9f2f6a805668309f33ef314
author: Sigrid Solveig Haflínudóttir <[email protected]>
date: Fri Aug 13 09:23:09 EDT 2021

allow streaming to several URLs at the same time

--- a/README.md
+++ b/README.md
@@ -1,10 +1,11 @@
 # rtmp
 
-RTMP streaming for Plan 9. Tested with Twitch and PeerTube.
+RTMP streaming for Plan 9.  Tested with Twitch and PeerTube.  Can
+stream to several RTMP urls at the same time.
 
 ## Usage (until I write a manpage)
 
-	video/hj264 -f 25 /dev/screen | video/rtmp -a ... rtmp://....
+	video/hj264 -f 25 /dev/screen | video/rtmp -a ... rtmp://.... rtmp://...
 
 For how to use `rtmp` with Twitch, refer to the documentation they
 provide.  All you need is the correct RTMP URL.  Preferably of a
--- a/main.c
+++ b/main.c
@@ -7,21 +7,21 @@
 #include "rtmp.h"
 #include "util.h"
 
+typedef struct Conn Conn;
+
+struct Conn {
+	char *url;
+	RTMP *r;
+	ulong sid;
+};
+
 int mainstacksize = 65536;
 int debug = 0;
 
-static RTMP *r;
-static ulong sid;
-static uvlong ns₀;
-static uvlong vms;
+static Conn *cs;
+static int ncs;
+static uvlong ns₀, vms;
 
-static void
-usage(void)
-{
-	fprint(2, "usage: %s [-a AUDIO] URL\n", argv0);
-	threadexitsall("usage");
-}
-
 static uvlong
 ns2ms(uvlong z, uvlong ns)
 {
@@ -37,6 +37,8 @@
 	ADTSFrame af;
 	Biobuf *a;
 	u64int ms;
+	Conn *c;
+	int i;
 
 	a = aux;
 	memset(&af, 0, sizeof(af));
@@ -43,15 +45,16 @@
 	af.ns₀ = Zns₀;
 	for(;;){
 		if(adtsread(a, &af) != 0)
-			goto err;
+			sysfatal("%r");
 		if(af.sz == 0) /* eof */
 			break;
 		ms = ns2ms(af.ns₀, af.ns);
 
-		if(rtmpdata(r, sid, ms, Taudio, af.buf, af.sz) != 0){
-err:
-			fprint(2, "%r\n");
-			break;
+		for(c = cs, i = 0; i < ncs; i++, c++){
+			if(rtmpdata(c->r, c->sid, ms, Taudio, af.buf, af.sz) != 0){
+				fprint(2, "%s: %r\n", c->url);
+				goto out;
+			}
 		}
 
 		/* protect against overruns */
@@ -60,10 +63,17 @@
 	}
 
 	/* FIXME properly close RTMP connection */
-
+out:
 	threadexitsall(nil);
 }
 
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-a AUDIO] URL [URL...]\n", argv0);
+	threadexitsall("usage");
+}
+
 void
 threadmain(int argc, char **argv)
 {
@@ -71,6 +81,8 @@
 	IVFrame vf;
 	u64int ms;
 	IVF ivf;
+	Conn *c;
+	int i;
 
 	a = nil;
 	ARGBEGIN{
@@ -85,22 +97,29 @@
 		usage();
 	}ARGEND
 
-	if(argc != 1)
+	ncs = argc;
+	if(ncs < 1)
 		usage();
+	ns₀ = nsec() - 10ULL*1000000000ULL; /* base, -10s */
+	srand(time(nil));
+
 	if(Binit(&v, 0, OREAD) != 0 || ivfopen(&v, &ivf) != 0)
 		sysfatal("%r");
 	if(strcmp(ivf.type, "AVC1") != 0)
 		sysfatal("not H.264");
 
-	ns₀ = nsec() - 10ULL*1000000000ULL; /* base, -10s */
-	srand(time(nil));
-	if((r = rtmpdial(argv[0])) == nil)
-		sysfatal("%r");
+	cs = ecalloc(argc, sizeof(*cs));
+	for(c = cs, i = 0; i < ncs; i++, c++){
+		c->url = "rtmp://REDACTED";//FIXME the key has to be redacted argv[i];
 
-	if(rtmpstream(r, &sid) != 0 ||
-	   rtmppublish(r, sid, PubLive, nil) != 0 ||
-	   rtmpmeta(r, sid, VcodecH264, ivf.w, ivf.h, a != nil ? AcodecAAC : -1) != 0){
-		sysfatal("%r");
+		if((c->r = rtmpdial(argv[i])) == nil)
+			sysfatal("%r");
+
+		if(rtmpstream(c->r, &c->sid) != 0 ||
+		   rtmppublish(c->r, c->sid, PubLive, nil) != 0 ||
+		   rtmpmeta(c->r, c->sid, VcodecH264, ivf.w, ivf.h, a != nil ? AcodecAAC : -1) != 0){
+			sysfatal("%r");
+		}
 	}
 
 	if(a != nil)
@@ -113,14 +132,16 @@
 		if(vf.sz == 0)
 			break;
 		ms = ns2ms(ivf.ns₀, vf.ns);
-		if(rtmpdata(r, sid, ms, Tvideo, vf.buf, vf.sz) != 0){
-			fprint(2, "%r\n");
-			break;
-		}
 		vms = ms;
+		for(c = cs, i = 0; i < ncs; i++, c++){
+			if(rtmpdata(c->r, c->sid, ms, Tvideo, vf.buf, vf.sz) != 0){
+				fprint(2, "%s: %r\n", c->url);
+				goto out;
+			}
+		}
 	}
 
 	/* FIXME properly close RTMP connection */
-
+out:
 	threadexitsall(nil);
 }