ref: c9a45262d2655e843fcc0ad7382f08efcc523926
dir: /main.c/
#include <u.h> #include <libc.h> #include <thread.h> #include <bio.h> #include "adts.h" #include "ivf.h" #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 Conn *cs; static int ncs; static uvlong ns₀, vms; static vlong aoff; static uvlong ns2ms(uvlong z, uvlong ns) { if(z != 0 && z != Zns₀) ns = z - ns₀ + ns; return ns / 1000000ULL; } static void audio(void *aux) { ADTSFrame af; Biobuf *a; u64int ms; Conn *c; int i; a = aux; memset(&af, 0, sizeof(af)); af.ns₀ = Zns₀; for(;;){ if(adtsread(a, &af) != 0) sysfatal("%r"); 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){ fprint(2, "%s: %r\n", c->url); goto out; } } /* protect against overruns */ if(vms+200 < ms) sleep(100); } /* FIXME properly close RTMP connection */ out: threadexitsall(nil); } static void usage(void) { fprint(2, "usage: %s [-A AUDIOOFF] [-a AUDIO] URL [URL...]\n", argv0); threadexitsall("usage"); } void threadmain(int argc, char **argv) { Biobuf *a, v; IVFrame vf; u64int ms; IVF ivf; Conn *c; int i; a = nil; ARGBEGIN{ case 'd': debug++; break; case 'a': if((a = Bopen(EARGF(usage()), OREAD)) == nil) sysfatal("%r"); break; case 'A': aoff = atoll(EARGF(usage())); break; default: usage(); }ARGEND 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"); 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((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) proccreate(audio, a, mainstacksize); memset(&vf, 0, sizeof(vf)); for(;;){ if(ivfread(&ivf, &vf) != 0) sysfatal("%r"); if(vf.sz == 0) break; ms = ns2ms(ivf.ns₀, vf.ns); 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); }