ref: 139fd1d0439e7fc8e8672a371e4c53f7d609e431
parent: edfe7779751000c6f9f2f6a805668309f33ef314
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
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);
}