ref: 0ec0e4fde657c442431548180c91018078c2bf3c
parent: 3168be131e24f503aabda0f0bdf690136eaef03b
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Wed Jul 21 11:35:27 EDT 2021
split more
--- /dev/null
+++ b/amf.c
@@ -1,0 +1,143 @@
+#include <u.h>
+#include <libc.h>
+#include "amf.h"
+
+enum {
+ Anum,
+ Aarr = 8,
+ Aend,
+ Alstr = 12,
+};
+
+u8int *
+amfi16(u8int *p, u8int *e, s16int i)
+{
+ if(p == nil)
+ return nil;
+ if(e-p < 2){
+ werrstr("buffer short");
+ return nil;
+ }
+ *p++ = i >> 8;
+ *p++ = i;
+
+ return p;
+}
+
+u8int *
+amfi24(u8int *p, u8int *e, s32int i)
+{
+ if(p == nil)
+ return nil;
+ if(e-p < 3){
+ werrstr("buffer short");
+ return nil;
+ }
+ *p++ = i >> 16;
+ *p++ = i >> 8;
+ *p++ = i;
+
+ return p;
+}
+
+u8int *
+amfi32(u8int *p, u8int *e, s32int i)
+{
+ if(p == nil)
+ return nil;
+ if(e-p < 4){
+ werrstr("buffer short");
+ return nil;
+ }
+ *p++ = i >> 24;
+ *p++ = i >> 16;
+ *p++ = i >> 8;
+ *p++ = i;
+
+ return p;
+}
+
+u8int *
+amfnum(u8int *p, u8int *e, double v)
+{
+ union {
+ double v;
+ u64int u;
+ }x;
+
+ if(p == nil)
+ return nil;
+ if(p+8 > e){
+ werrstr("buffer short");
+ return nil;
+ }
+ x.v = v;
+ *p++ = x.u >> 56;
+ *p++ = x.u >> 48;
+ *p++ = x.u >> 40;
+ *p++ = x.u >> 32;
+ *p++ = x.u >> 24;
+ *p++ = x.u >> 16;
+ *p++ = x.u >> 8;
+ *p++ = x.u;
+
+ return p;
+}
+
+u8int *
+amfkvnum(u8int *p, u8int *e, char *name, double v)
+{
+ int n;
+
+ if(p == nil)
+ return nil;
+ if((n = strlen(name)) > 0xffff){
+ werrstr("string too long");
+ return nil;
+ }
+ if(p+2+n+8 > e){
+ werrstr("buffer short");
+ return nil;
+ }
+ p = amfi16(p, e, n);
+ p = (u8int*)memmove(p, name, n) + n;
+
+ return amfnum(p, e, v);
+}
+
+u8int *
+amfstr(u8int *p, u8int *e, char *s)
+{
+ int n;
+
+ if(p == nil)
+ return nil;
+ n = strlen(s);
+ if(p+1+4+n > e){
+ werrstr("string too long");
+ return nil;
+ }
+ *p++ = Alstr;
+
+ return (u8int*)memmove(amfi32(p, e, n), s, n) + n;
+}
+
+u8int *
+amfarr(u8int *p, u8int *e)
+{
+ if(p == nil)
+ return nil;
+ if(p == e){
+ werrstr("buffer short");
+ return nil;
+ }
+ *p++ = Aarr;
+
+ return p;
+}
+
+u8int *
+amfend(u8int *p, u8int *e)
+{
+ return amfi24(p, e, Aend);
+}
--- /dev/null
+++ b/amf.h
@@ -1,0 +1,8 @@
+u8int *amfi16(u8int *p, u8int *e, s16int i);
+u8int *amfi24(u8int *p, u8int *e, s32int i);
+u8int *amfi32(u8int *p, u8int *e, s32int i);
+u8int *amfnum(u8int *p, u8int *e, double v);
+u8int *amfkvnum(u8int *p, u8int *e, char *name, double v);
+u8int *amfstr(u8int *p, u8int *e, char *s);
+u8int *amfarr(u8int *p, u8int *e);
+u8int *amfend(u8int *p, u8int *e);
--- /dev/null
+++ b/flv.c
@@ -1,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include "amf.h"
+#include "flv.h"
+
+enum {
+ EncH264 = 7,
+ EncAAC = 10,
+};
+
+u8int *
+flvscript(u8int *p, u8int *e, int w, int h, int audio)
+{
+ u8int *psz, *d, *p0;
+ int stream;
+ u32int ts;
+
+ if(p+16 > e){
+ werrstr("buffer short");
+ return nil;
+ }
+
+ /* FIXME ever need to change these? */
+ stream = 0;
+ ts = 0;
+
+ p0 = p;
+ *p++ = Fvideo;
+ psz = p;
+ p = amfi24(p, e, 0); /* sz set later */
+ p = amfi24(p, e, ts);
+ *p++ = ts>>24;
+ p = amfi24(p, e, stream);
+
+ d = p;
+ p = amfstr(p, e, "onMetaData");
+ p = amfarr(p, e);
+ p = amfi32(p, e, audio ? 5 : 4);
+ p = amfkvnum(p, e, "duration", 0.0);
+ p = amfkvnum(p, e, "width", w);
+ p = amfkvnum(p, e, "height", h);
+ p = amfkvnum(p, e, "videocodecid", EncH264);
+ if(audio)
+ p = amfkvnum(p, e, "audiocodecid", EncAAC);
+ p = amfend(p, e);
+ amfi24(psz, e, p-d);
+
+ return amfi32(p, e, p-p0);
+}
+
+u8int *
+flvdata(u8int *p, u8int *e, u32int pts, u32int dts, void *data, int sz, int type, int fl)
+{
+ u8int *p0, *psz, *d;
+ int stream;
+
+ /* FIXME ever need to change these? */
+ stream = 0;
+
+ assert(type == Faudio || type == Fvideo);
+ p0 = p;
+ *p++ = type;
+ psz = p;
+ p = amfi24(p, e, 0); /* size to be set later */
+ p = amfi24(p, e, dts);
+ *p++ = dts >> 24;
+ p = amfi24(p, e, stream);
+
+ d = p;
+ if(type == Faudio){
+ *p++ = (EncAAC<<4) | 0x0f;
+ *p++ = (fl & FlHdr) ? 0 : 1;
+ }
+ if(type == Fvideo){
+ *p++ = ((fl & FlKey) ? 0x10 : 0x20) | EncH264;
+ *p++ = (fl & FlHdr) ? 0 : 1;
+ pts = ((fl & FlHdr) || pts < dts) ? 0 : (pts - dts);
+ p = amfi24(p, e, pts);
+ if((fl & FlHdr) == 0)
+ p = amfi32(p, e, sz);
+ }
+ p = (u8int*)memmove(p, data, sz) + sz;
+ amfi24(psz, e, p-d);
+
+ return amfi32(p, e, p-p0);
+}
--- /dev/null
+++ b/flv.h
@@ -1,0 +1,11 @@
+enum {
+ Faudio = 8,
+ Fvideo = 9,
+ Fscript = 18,
+
+ FlKey = 1<<0,
+ FlHdr = 1<<1,
+};
+
+u8int *flvscript(u8int *p, u8int *e, int w, int h, int audio);
+u8int *flvdata(u8int *p, u8int *e, u32int pts, u32int dts, void *data, int sz, int type, int fl);
--- /dev/null
+++ b/main.c
@@ -1,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include "ivf.h"
+#include "flv.h"
+#include "rtmp.h"
+
+int mainstacksize = 65536;
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [-a AUDIO] -v VIDEO [URL]\n", argv0);
+ threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char **argv)
+{
+ Biobuf *a, *v, o;
+ u8int *b, *p, *e;
+ int bufsz;
+ u64int ns;
+ IVFrame f;
+ IVF ivf;
+ RTMP *r;
+
+ a = nil;
+ v = nil;
+ ARGBEGIN{
+ case 'a':
+ if((a = Bopen(EARGF(usage()), OREAD)) == nil)
+ sysfatal("%r");
+ break;
+ case 'v':
+ if((v = Bopen(EARGF(usage()), OREAD)) == nil)
+ sysfatal("%r");
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc != 1)
+ usage();
+ if(v == nil)
+ sysfatal("no video specified");
+ if(ivfopen(v, &ivf) != 0)
+ sysfatal("%r");
+ if(strcmp(ivf.type, "AVC1") != 0)
+ sysfatal("not H.264");
+ srand(time(nil));
+ if((r = rtmpdial(argv[0])) < 0 || Binit(&o, fd, OWRITE) < 0)
+ sysfatal("%r");
+
+ bufsz = 65536;
+ if((b = malloc(bufsz)) == nil)
+ sysfatal("memory");
+ e = b + bufsz;
+
+ if((p = flvscript(b, e, ivf.w, ivf.h, 0)) == nil || Bwrite(&o, b, p-b) < 0)
+ sysfatal("%r");
+
+ memset(&f, 0, sizeof(f));
+ for(;;){
+ if(ivfread(v, &f) != 0)
+ sysfatal("%r");
+ if(f.sz == 0)
+ break;
+ if(bufsz < f.sz+64){
+ free(b);
+ bufsz *= 2;
+ if((b = malloc(bufsz)) == nil)
+ sysfatal("memory");
+ e = b + bufsz;
+ }
+ ns = ivfns(&ivf, f.ts);
+ if((p = flvdata(b, e, ns, ns, f.buf, f.sz, Fvideo, FlHdr)) == nil)
+ sysfatal("video: flvdata: %r");
+ if(Bwrite(&o, b, p-b) < 0)
+ sysfatal("%r");
+ Bflush(&o);
+ }
+
+ threadexitsall(nil);
+}
--- a/mkfile
+++ b/mkfile
@@ -4,10 +4,16 @@
TARG=rtmp
HFILES=\
+ amf.h\
+ flv.h\
ivf.h\
+ rtmp.h\
OFILES=\
+ amf.$O\
+ flv.$O\
ivf.$O\
+ main.$O\
rtmp.$O\
default:V: all
--- a/rtmp.c
+++ b/rtmp.c
@@ -3,245 +3,43 @@
#include <thread.h>
#include <bio.h>
#include <libsec.h>
+#include "amf.h"
#include "ivf.h"
+#include "rtmp.h"
enum {
Port = 1935,
- Anum,
- Aarr = 8,
- Aend,
- Alstr = 12,
-
- Faudio = 8,
- Fvideo = 9,
- Fscript = 18,
-
- EncH264 = 7,
- EncAAC = 10,
-
- FlKey = 1<<0,
- FlHdr = 1<<1,
-
Sigsz = 1536,
ChanCtl = 3,
-};
-int mainstacksize = 65536;
+ SzLarge,
+ SzMedium,
+ SzSmall,
+ SzTiny,
-static u8int *
-amfi16(u8int *p, u8int *e, s16int i)
-{
- if(p == nil)
- return nil;
- if(e-p < 2){
- werrstr("buffer short");
- return nil;
- }
- *p++ = i >> 8;
- *p++ = i;
+ PktInvoke = 20,
+};
- return p;
-}
+typedef struct RTMPacket RTMPacket;
-static u8int *
-amfi24(u8int *p, u8int *e, s32int i)
-{
- if(p == nil)
- return nil;
- if(e-p < 3){
- werrstr("buffer short");
- return nil;
- }
- *p++ = i >> 16;
- *p++ = i >> 8;
- *p++ = i;
+struct RTMP {
+ int f;
+ int invokes;
+};
- return p;
-}
-
-static u8int *
-amfi32(u8int *p, u8int *e, s32int i)
-{
- if(p == nil)
- return nil;
- if(e-p < 4){
- werrstr("buffer short");
- return nil;
- }
- *p++ = i >> 24;
- *p++ = i >> 16;
- *p++ = i >> 8;
- *p++ = i;
-
- return p;
-}
-
-static u8int *
-amfnum(u8int *p, u8int *e, double v)
-{
- union {
- double v;
- u64int u;
- }x;
-
- if(p == nil)
- return nil;
- if(p+8 > e){
- werrstr("buffer short");
- return nil;
- }
- x.v = v;
- *p++ = x.u >> 56;
- *p++ = x.u >> 48;
- *p++ = x.u >> 40;
- *p++ = x.u >> 32;
- *p++ = x.u >> 24;
- *p++ = x.u >> 16;
- *p++ = x.u >> 8;
- *p++ = x.u;
-
- return p;
-}
-
-static u8int *
-amfkvnum(u8int *p, u8int *e, char *name, double v)
-{
- int n;
-
- if(p == nil)
- return nil;
- if((n = strlen(name)) > 0xffff){
- werrstr("string too long");
- return nil;
- }
- if(p+2+n+8 > e){
- werrstr("buffer short");
- return nil;
- }
- p = amfi16(p, e, n);
- p = (u8int*)memmove(p, name, n) + n;
-
- return amfnum(p, e, v);
-}
-
-static u8int *
-amfstr(u8int *p, u8int *e, char *s)
-{
- int n;
-
- if(p == nil)
- return nil;
- n = strlen(s);
- if(p+1+4+n > e){
- werrstr("string too long");
- return nil;
- }
- *p++ = Alstr;
-
- return (u8int*)memmove(amfi32(p, e, n), s, n) + n;
-}
-
-static u8int *
-amfarr(u8int *p, u8int *e)
-{
- if(p == nil)
- return nil;
- if(p == e){
- werrstr("buffer short");
- return nil;
- }
- *p++ = Aarr;
-
- return p;
-}
-
-static u8int *
-amfend(u8int *p, u8int *e)
-{
- return amfi24(p, e, Aend);
-}
-
-static u8int *
-flvscript(u8int *p, u8int *e, int w, int h, int audio)
-{
- u8int *psz, *d, *p0;
- int stream;
+struct RTMPacket {
+ int chan;
+ int sztype;
+ int pktype;
u32int ts;
+ u8int *data;
+ int sz;
+};
- if(p+16 > e){
- werrstr("buffer short");
- return nil;
- }
-
- /* FIXME ever need to change these? */
- stream = 0;
- ts = 0;
-
- p0 = p;
- *p++ = Fvideo;
- psz = p;
- p = amfi24(p, e, 0); /* sz set later */
- p = amfi24(p, e, ts);
- *p++ = ts>>24;
- p = amfi24(p, e, stream);
-
- d = p;
- p = amfstr(p, e, "onMetaData");
- p = amfarr(p, e);
- p = amfi32(p, e, audio ? 5 : 4);
- p = amfkvnum(p, e, "duration", 0.0);
- p = amfkvnum(p, e, "width", w);
- p = amfkvnum(p, e, "height", h);
- p = amfkvnum(p, e, "videocodecid", EncH264);
- if(audio)
- p = amfkvnum(p, e, "audiocodecid", EncAAC);
- p = amfend(p, e);
- amfi24(psz, e, p-d);
-
- return amfi32(p, e, p-p0);
-}
-
-static u8int *
-flvdata(u8int *p, u8int *e, u32int pts, u32int dts, void *data, int sz, int type, int fl)
-{
- u8int *p0, *psz, *d;
- int stream;
-
- /* FIXME ever need to change these? */
- stream = 0;
-
- assert(type == Faudio || type == Fvideo);
- p0 = p;
- *p++ = type;
- psz = p;
- p = amfi24(p, e, 0); /* size to be set later */
- p = amfi24(p, e, dts);
- *p++ = dts >> 24;
- p = amfi24(p, e, stream);
-
- d = p;
- if(type == Faudio){
- *p++ = (EncAAC<<4) | 0x0f;
- *p++ = (fl & FlHdr) ? 0 : 1;
- }
- if(type == Fvideo){
- *p++ = ((fl & FlKey) ? 0x10 : 0x20) | EncH264;
- *p++ = (fl & FlHdr) ? 0 : 1;
- pts = ((fl & FlHdr) || pts < dts) ? 0 : (pts - dts);
- p = amfi24(p, e, pts);
- if((fl & FlHdr) == 0)
- p = amfi32(p, e, sz);
- }
- p = (u8int*)memmove(p, data, sz) + sz;
- amfi24(psz, e, p-d);
-
- return amfi32(p, e, p-p0);
-}
-
static int
-handshake(int f, char *path)
+handshake(int f)
{
u8int cl[1+Sigsz], sv[1+Sigsz];
@@ -265,20 +63,40 @@
goto err;
}
- return f;
+ return 0;
err:
werrstr("handshake: %r");
- close(f);
return -1;
}
static int
+connect(int f, char *path)
+{
+ RTMPacket p;
+
+ memset(&p, 0, sizeof(p));
+ p.chan = ChanCtl;
+ p.sztype = SzLarge;
+ p.pktype = PktInvoke;
+
+ return -1;
+}
+
+static int
+rtmpsend(RTMP *r, RTMPacket *p)
+{
+ return -1;
+}
+
+RTMP *
rtmpdial(char *url)
{
char *s, *e, *path;
int f, port, ctl;
+ RTMP *r;
+ f = -1;
if(memcmp(url, "rtmp://", 7) != 0){
werrstr("invalid url");
goto err;
@@ -304,84 +122,18 @@
if(f < 0)
goto err;
- return handshake(f, path);
-err:
- werrstr("rtmpdial: %r");
- return -1;
-}
+ if(handshake(f) != 0 || connect(f, path) == 0)
+ goto err;
-static void
-usage(void)
-{
- fprint(2, "usage: %s [-a AUDIO] -v VIDEO [URL]\n", argv0);
- threadexitsall("usage");
-}
-
-void
-threadmain(int argc, char **argv)
-{
- Biobuf *a, *v, o;
- u8int *b, *p, *e;
- int bufsz, fd;
- u64int ns;
- IVFrame f;
- IVF ivf;
-
- a = nil;
- v = nil;
- ARGBEGIN{
- case 'a':
- if((a = Bopen(EARGF(usage()), OREAD)) == nil)
- sysfatal("%r");
- break;
- case 'v':
- if((v = Bopen(EARGF(usage()), OREAD)) == nil)
- sysfatal("%r");
- break;
- default:
- usage();
- }ARGEND
-
- if(argc != 1)
- usage();
- if(v == nil)
- sysfatal("no video specified");
- if(ivfopen(v, &ivf) != 0)
- sysfatal("%r");
- if(strcmp(ivf.type, "AVC1") != 0)
- sysfatal("not H.264");
- srand(time(nil));
- if((fd = rtmpdial(argv[0])) < 0 || Binit(&o, fd, OWRITE) < 0)
- sysfatal("%r");
-
- bufsz = 65536;
- if((b = malloc(bufsz)) == nil)
+ if((r = calloc(1, sizeof(*r))) == nil)
sysfatal("memory");
- e = b + bufsz;
+ r->f = f;
- if((p = flvscript(b, e, ivf.w, ivf.h, 0)) == nil || Bwrite(&o, b, p-b) < 0)
- sysfatal("%r");
+ return r;
- memset(&f, 0, sizeof(f));
- for(;;){
- if(ivfread(v, &f) != 0)
- sysfatal("%r");
- if(f.sz == 0)
- break;
- if(bufsz < f.sz+64){
- free(b);
- bufsz *= 2;
- if((b = malloc(bufsz)) == nil)
- sysfatal("memory");
- e = b + bufsz;
- }
- ns = ivfns(&ivf, f.ts);
- if((p = flvdata(b, e, ns, ns, f.buf, f.sz, Fvideo, FlHdr)) == nil)
- sysfatal("video: flvdata: %r");
- if(Bwrite(&o, b, p-b) < 0)
- sysfatal("%r");
- Bflush(&o);
- }
-
- threadexitsall(nil);
+err:
+ werrstr("rtmpdial: %r");
+ if(f >= 0)
+ close(f);
+ return nil;
}
--- /dev/null
+++ b/rtmp.h
@@ -1,0 +1,5 @@
+typedef struct RTMP RTMP;
+
+#pragma incomplete RTMP
+
+RTMP *rtmpdial(char *url);