shithub: rtmp

Download patch

ref: 5745445bdf695dc69d3c15189e083432f2c1f42c
parent: a73d1bc55bf8d60f5cdd23c0f99080024a4c5f72
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sat Jul 15 22:53:36 EDT 2023

get rid of crap; run audio/aacenc ourselves with a small raw pcm buffer

--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 
 ## Usage (until I write a manpage)
 
-	video/hj264 -f 25 /dev/screen | video/rtmp -a ... rtmp://.... rtmp://...
+	video/hj264 -f 25 /dev/screen | video/rtmp -a /dev/audio 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
@@ -17,13 +17,8 @@
 [audio/aacenc](https://git.sr.ht/~ft/aacenc) is installed.  Then run
 this (change according to your preferences):
 
-	video/hj264 -f 25 /dev/screen | video/rtmp -a <{audio/aacenc -b </dev/audio} rtmp://....
+	video/hj264 -f 25 /dev/screen | video/rtmp -a /dev/audio rtmp://....
 
-Twitch does not require an audio stream to be present.  PeerTube seems
-to require it, so in case you don't want to stream audio to PeerTube,
-it's possible to use silence by passing `/dev/zero` instead of
-`/dev/audio`.
-
 For audio loopback you can do the following (make sure you have latest 9front):
 
 	audio/mixfs -s mixfs -m /n/mixfs
@@ -35,7 +30,7 @@
 
 	# streaming, another window
 	mount /srv/mixfs /n/mixfs
-	video/hj264 -f 30 /dev/screen | video/rtmp -a <{audio/aacenc -b < /n/mixfs/audio} $url
+	video/hj264 -f 30 /dev/screen | video/rtmp -a /n/mixfs/audio $url
 
 You can also mix in audio from the microphone by mounting mixfs and
 writing PCM data from your phone to `/n/mixfs/audio`.
--- a/main.c
+++ b/main.c
@@ -15,6 +15,10 @@
 	ulong sid;
 };
 
+enum {
+	Abufsz = 441*2*2, /* 1/100s */
+};
+
 int mainstacksize = 65536;
 int debug = 0;
 
@@ -21,7 +25,8 @@
 static Conn *cs;
 static int ncs;
 static uvlong ns₀, vms;
-static vlong aoff;
+static int afd;
+static Channel *ans₀;
 
 static uvlong
 ns2ms(uvlong z, uvlong ns)
@@ -33,8 +38,37 @@
 }
 
 static void
-audio(void *aux)
+audioenc(void *aux)
 {
+	int nssent, fd, n;
+	u8int *buf;
+	uvlong ns;
+
+	buf = emalloc(Abufsz);
+	fd = *(int*)aux;
+	nssent = 0;
+	for(;;){
+		if((n = readn(afd, buf, Abufsz)) < 1)
+			break;
+		if(nssent == 0){
+			ns = nsec() - 10000000ULL;
+			if(send(ans₀, &ns) != 1)
+				break;
+			nssent = 1;
+		}
+		if(write(fd, buf, n) != n)
+			break;
+	}
+	chanclose(ans₀);
+	close(afd);
+	close(fd);
+	
+	threadexits(nil);
+}
+
+static void
+audiosend(void *aux)
+{
 	ADTSFrame af;
 	Biobuf *a;
 	u64int ms;
@@ -41,9 +75,11 @@
 	Conn *c;
 	int i;
 
-	a = aux;
+	if((a = Bfdopen(*(int*)aux, OREAD)) == nil)
+		sysfatal("%r");
 	memset(&af, 0, sizeof(af));
-	af.ns₀ = Zns₀;
+	if(recv(ans₀, &af.ns₀) != 1)
+		sysfatal("no audio timestamp");
 	for(;;){
 		if(adtsread(a, &af) != 0)
 			sysfatal("%r");
@@ -50,8 +86,6 @@
 		if(af.sz == 0) /* eof */
 			break;
 		ms = ns2ms(af.ns₀, af.ns);
-		if((vlong)ms+aoff >= 0)
-			ms += aoff;
 
 		for(c = cs, i = 0; i < ncs; i++, c++){
 			if(rtmpdata(c->r, c->sid, ms, Taudio, af.buf, af.sz) != 0){
@@ -71,9 +105,36 @@
 }
 
 static void
+audio(void)
+{
+	static int p[4], pid;
+	Dir d;
+
+	nulldir(&d);
+	d.length = Abufsz;
+	pipe(p);
+	pipe(p+2);
+	dirfwstat(p[0], &d);
+	if((pid = fork()) < 0)
+		sysfatal("%r");
+	if(pid == 0){
+		close(afd);
+		dup(p[0], 0); close(p[0]);
+		dup(p[2], 1); close(p[2]);
+		execl("/bin/audio/aacenc", "audio/aacenc", nil);
+		sysfatal("aacenc: %r");
+	}
+	close(p[0]);
+	close(p[2]);
+	ans₀ = chancreate(sizeof(uvlong), 1);
+	proccreate(audioenc, &p[1], mainstacksize);
+	proccreate(audiosend, &p[3], mainstacksize);
+}
+
+static void
 usage(void)
 {
-	fprint(2, "usage: %s [-A AUDIOOFF] [-a AUDIO] URL [URL...]\n", argv0);
+	fprint(2, "usage: %s [-a AUDIO] URL [URL...]\n", argv0);
 	threadexitsall("usage");
 }
 
@@ -80,25 +141,22 @@
 void
 threadmain(int argc, char **argv)
 {
-	Biobuf *a, v;
 	IVFrame vf;
 	u64int ms;
+	Biobuf v;
 	IVF ivf;
 	Conn *c;
 	int i;
 
-	a = nil;
+	afd = open("/dev/zero", OREAD|OCEXEC);
 	ARGBEGIN{
 	case 'd':
 		debug++;
 		break;
 	case 'a':
-		if((a = Bopen(EARGF(usage()), OREAD)) == nil)
+		if((afd = open(EARGF(usage()), OREAD|OCEXEC)) < 0)
 			sysfatal("%r");
 		break;
-	case 'A':
-		aoff = atoll(EARGF(usage()));
-		break;
 	default:
 		usage();
 	}ARGEND
@@ -123,13 +181,13 @@
 
 		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){
+		   rtmpmeta(c->r, c->sid, VcodecH264, ivf.w, ivf.h, afd >= 0 ? AcodecAAC : -1) != 0){
 			sysfatal("%r");
 		}
 	}
 
-	if(a != nil)
-		proccreate(audio, a, mainstacksize);
+	if(afd >= 0)
+		audio();
 
 	memset(&vf, 0, sizeof(vf));
 	for(;;){
@@ -149,5 +207,7 @@
 
 	/* FIXME properly close RTMP connection */
 out:
+	close(afd);
+
 	threadexitsall(nil);
 }