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);
}