ref: c296529d65687f301af4f36aa4db5a0978a6f059
parent: 0ec0e4fde657c442431548180c91018078c2bf3c
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Thu Jul 22 10:28:45 EDT 2021
more rtmp logic
--- a/amf.c
+++ b/amf.c
@@ -4,23 +4,46 @@
enum {
Anum,
+ Abool,
+ Astr,
+ Aobj,
Aarr = 8,
Aend,
Alstr = 12,
};
+#define atleast(x) do{ \
+ if(p == nil) \
+ return nil; \
+ if(e-p < x){ \
+ werrstr("buffer short"); \
+ return nil; \
+ } \
+}while(0)
+
u8int *
+amfbool(u8int *p, u8int *e, int v)
+{
+ atleast(2);
+ *p++ = Abool;
+ *p++ = !!v;
+ return p;
+}
+
+u8int *
+amfbyte(u8int *p, u8int *e, u8int byte)
+{
+ atleast(1);
+ *p++ = byte;
+ return p;
+}
+
+u8int *
amfi16(u8int *p, u8int *e, s16int i)
{
- if(p == nil)
- return nil;
- if(e-p < 2){
- werrstr("buffer short");
- return nil;
- }
+ atleast(2);
*p++ = i >> 8;
*p++ = i;
-
return p;
}
@@ -27,16 +50,10 @@
u8int *
amfi24(u8int *p, u8int *e, s32int i)
{
- if(p == nil)
- return nil;
- if(e-p < 3){
- werrstr("buffer short");
- return nil;
- }
+ atleast(3);
*p++ = i >> 16;
*p++ = i >> 8;
*p++ = i;
-
return p;
}
@@ -43,17 +60,11 @@
u8int *
amfi32(u8int *p, u8int *e, s32int i)
{
- if(p == nil)
- return nil;
- if(e-p < 4){
- werrstr("buffer short");
- return nil;
- }
+ atleast(4);
*p++ = i >> 24;
*p++ = i >> 16;
*p++ = i >> 8;
*p++ = i;
-
return p;
}
@@ -65,12 +76,8 @@
u64int u;
}x;
- if(p == nil)
- return nil;
- if(p+8 > e){
- werrstr("buffer short");
- return nil;
- }
+ atleast(1+8);
+ *p++ = Anum;
x.v = v;
*p++ = x.u >> 56;
*p++ = x.u >> 48;
@@ -85,59 +92,64 @@
}
u8int *
-amfkvnum(u8int *p, u8int *e, char *name, double v)
+amfstr(u8int *p, u8int *e, char *s)
{
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;
+ n = strlen(s);
+ atleast(1+4+n);
+ *p++ = Alstr;
- return amfnum(p, e, v);
+ return (u8int*)memmove(amfi32(p, e, n), s, n) + n;
}
u8int *
-amfstr(u8int *p, u8int *e, char *s)
+amfarr(u8int *p, u8int *e)
{
+ return amfbyte(p, e, Aarr);
+}
+
+u8int *
+amfobj(u8int *p, u8int *e)
+{
+ return amfbyte(p, e, Aobj);
+}
+
+u8int *
+amfend(u8int *p, u8int *e)
+{
+ return amfi24(p, e, Aend);
+}
+
+static u8int *
+amfkv(u8int *p, u8int *e, char *name)
+{
int n;
- if(p == nil)
- return nil;
- n = strlen(s);
- if(p+1+4+n > e){
+ if((n = strlen(name)) > 0xffff){
werrstr("string too long");
return nil;
}
- *p++ = Alstr;
+ atleast(2+n);
+ p = amfi16(p, e, n);
- return (u8int*)memmove(amfi32(p, e, n), s, n) + n;
+ return (u8int*)memmove(p, name, n) + n;
}
u8int *
-amfarr(u8int *p, u8int *e)
+amfkvbool(u8int *p, u8int *e, char *name, int v)
{
- if(p == nil)
- return nil;
- if(p == e){
- werrstr("buffer short");
- return nil;
- }
- *p++ = Aarr;
+ return amfbool(amfkv(p, e, name), e, v);
+}
- return p;
+u8int *
+amfkvnum(u8int *p, u8int *e, char *name, double v)
+{
+ return amfnum(amfkv(p, e, name), e, v);
}
u8int *
-amfend(u8int *p, u8int *e)
+amfkvstr(u8int *p, u8int *e, char *name, char *v)
{
- return amfi24(p, e, Aend);
+ return amfstr(amfkv(p, e, name), e, v);
}
--- a/amf.h
+++ b/amf.h
@@ -1,8 +1,13 @@
u8int *amfi16(u8int *p, u8int *e, s16int i);
+u8int *amfbool(u8int *p, u8int *e, int v);
+u8int *amfbyte(u8int *p, u8int *e, u8int byte);
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 *amfobj(u8int *p, u8int *e);
u8int *amfend(u8int *p, u8int *e);
+u8int *amfkvnum(u8int *p, u8int *e, char *name, double v);
+u8int *amfkvstr(u8int *p, u8int *e, char *name, char *v);
+u8int *amfkvbool(u8int *p, u8int *e, char *name, int v);
--- a/main.c
+++ b/main.c
@@ -18,8 +18,8 @@
void
threadmain(int argc, char **argv)
{
- Biobuf *a, *v, o;
u8int *b, *p, *e;
+ Biobuf *a, *v;
int bufsz;
u64int ns;
IVFrame f;
@@ -50,7 +50,7 @@
if(strcmp(ivf.type, "AVC1") != 0)
sysfatal("not H.264");
srand(time(nil));
- if((r = rtmpdial(argv[0])) < 0 || Binit(&o, fd, OWRITE) < 0)
+ if((r = rtmpdial(argv[0], ivf.w, ivf.h, a != nil)) == nil)
sysfatal("%r");
bufsz = 65536;
@@ -58,7 +58,7 @@
sysfatal("memory");
e = b + bufsz;
- if((p = flvscript(b, e, ivf.w, ivf.h, 0)) == nil || Bwrite(&o, b, p-b) < 0)
+ if((p = flvscript(b, e, ivf.w, ivf.h, a != nil)) == nil)
sysfatal("%r");
memset(&f, 0, sizeof(f));
@@ -77,9 +77,6 @@
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/rtmp.c
+++ b/rtmp.c
@@ -11,33 +11,76 @@
Port = 1935,
Sigsz = 1536,
+ Chunk = 128,
ChanCtl = 3,
- SzLarge,
- SzMedium,
- SzSmall,
- SzTiny,
+ SzTiny = 1,
+ SzSmall = 4,
+ SzMedium = 8,
+ SzLarge = 12,
PktInvoke = 20,
-};
-typedef struct RTMPacket RTMPacket;
-
-struct RTMP {
- int f;
- int invokes;
+ Biobufsz = 64*1024, /* FIXME don't know if it helps with anything */
+ Bufsz = 64*1024,
};
-struct RTMPacket {
+typedef struct Packet Packet;
+
+struct Packet {
+ int type;
+ int hsz;
int chan;
- int sztype;
- int pktype;
u32int ts;
u8int *data;
int sz;
};
+struct RTMP {
+ Biobufhdr;
+ char *app;
+ char *path;
+ char *tcurl;
+ Packet pk;
+ u8int *b, *p, *e;
+ int mode;
+ int bsz;
+ int invokes;
+ int i;
+ u8int biobuf[Biobufsz];
+};
+
+#define puti16(i) do{ r->p = amfi16(r->p, r->e, i); }while(0)
+#define puti24(i) do{ r->p = amfi24(r->p, r->e, i); }while(0)
+#define puti32(i) do{ r->p = amfi32(r->p, r->e, i); }while(0)
+#define putnum(v) do{ r->p = amfnum(r->p, r->e, v); }while(0)
+#define putstr(s) do{ r->p = amfstr(r->p, r->e, s); }while(0)
+#define putarr() do{ r->p = amfarr(r->p, r->e); }while(0)
+#define putobj() do{ r->p = amfobj(r->p, r->e); }while(0)
+#define putend() do{ r->p = amfend(r->p, r->e); }while(0)
+#define putkvnum(name, v) do{ r->p = amfkvnum(r->p, r->e, name, v); }while(0)
+#define putkvstr(name, s) do{ r->p = amfkvstr(r->p, r->e, name, s); }while(0)
+#define putkvbool(name, s) do{ r->p = amfkvbool(r->p, r->e, name, s); }while(0)
+
+static void
+newpacket(RTMP *r, int type, int hsz, int chan)
+{
+ memset(&r->pk, 0, sizeof(r->pk));
+
+ r->pk.type = type;
+ r->pk.hsz = hsz;
+ r->pk.chan = chan;
+ r->p = r->b + hsz;
+ r->b[0] = chan;
+
+ switch(hsz){
+ case SzTiny: r->b[0] |= 3<<6; break;
+ case SzSmall: r->b[0] |= 2<<6; break;
+ case SzMedium: r->b[0] |= 1<<6; break;
+ }
+}
+
static int
handshake(int f)
{
@@ -71,32 +114,67 @@
}
static int
-connect(int f, char *path)
+rtmpsend(RTMP *r)
{
- RTMPacket p;
+ u8int *h, *e;
+ int bodysz;
- memset(&p, 0, sizeof(p));
- p.chan = ChanCtl;
- p.sztype = SzLarge;
- p.pktype = PktInvoke;
+ bodysz = r->p - r->b - r->pk.hsz;
+ /* FIXME special case when bodysz is 0 */
+ h = r->b + 1;
+ e = h + r->pk.hsz;
+ if(r->pk.hsz >= SzSmall){
+ h = amfi24(h, e, 0); /* FIXME proper timestamps? */
+ if(r->pk.hsz >= SzMedium){
+ h = amfi24(h, e, bodysz);
+ h = amfbyte(h, e, r->pk.type);
+ if(r->pk.hsz >= SzLarge)
+ h = amfi32(h, e, 0); /* FIXME seems to be always 0? */
+ }
+ }
+ if(h == nil)
+ goto err;
+ memset(h, 0, e-h);
+ return 0;
+err:
+ werrstr("rtmpsend: %r");
return -1;
}
static int
-rtmpsend(RTMP *r, RTMPacket *p)
+connect(RTMP *r)
{
- return -1;
+ newpacket(r, PktInvoke, SzLarge, ChanCtl);
+ putstr("connect");
+ putnum(++r->invokes);
+ putobj();
+ putkvstr("app", r->app);
+ putkvstr("tcUrl", r->tcurl);
+ if(r->mode & OWRITE)
+ putkvstr("type", "nonprivate");
+ else{
+ putkvbool("fpad", 0);
+ putkvnum("capabilities", 15);
+ putkvnum("audioCodecs", 3191);
+ putkvnum("videoCodecs", 252);
+ putkvnum("videoFunction", 1);
+ }
+ putend();
+
+ return rtmpsend(r);
}
RTMP *
-rtmpdial(char *url)
+rtmpdial(char *url, int w, int h, int withaudio)
{
- char *s, *e, *path;
+ char *s, *e, *path, *app;
int f, port, ctl;
RTMP *r;
+ r = nil;
f = -1;
+ url = strdup(url); /* since we're changing it in-place */
if(memcmp(url, "rtmp://", 7) != 0){
werrstr("invalid url");
goto err;
@@ -115,6 +193,7 @@
}else{
path = e;
}
+ while(*(++path) == '/');
s = smprint("tcp!%.*s!%d", (int)(e-s), s, port);
f = dial(s, nil, nil, &ctl);
@@ -122,18 +201,57 @@
if(f < 0)
goto err;
- if(handshake(f) != 0 || connect(f, path) == 0)
+ app = path;
+ if((s = strchr(path, '/')) == nil){
+ werrstr("no path");
goto err;
+ }
+ if((e = strchr(s+1, '/')) != nil){
+ /* at this point it can be app instance if there is another slash following */
+ if((s = strchr(e+1, '/')) == nil){
+ /* no, just path leftovers */
+ s = e;
+ }
+ *s = 0;
+ path = s+1;
+ }else{
+ path = nil;
+ }
- if((r = calloc(1, sizeof(*r))) == nil)
+ if(handshake(f) != 0)
+ goto err;
+ if((r = calloc(1, sizeof(*r))) == nil || (r->b = malloc(Bufsz)) == nil)
sysfatal("memory");
- r->f = f;
+ if((r->app = strdup(app)) == nil || (path != nil && (r->path = strdup(path)) == nil))
+ sysfatal("memory");
+ r->tcurl = url;
+ r->bsz = Bufsz;
+ r->e = r->b + r->bsz;
+ Binits(r, f, OWRITE, r->biobuf, sizeof(r->biobuf));
+ r->i = f;
+ if(connect(r) != 0)
+ goto err;
return r;
err:
werrstr("rtmpdial: %r");
- if(f >= 0)
+ if(r != nil)
+ rtmpclose(r);
+ else if(f >= 0)
close(f);
+ free(url);
return nil;
+}
+
+void
+rtmpclose(RTMP *r)
+{
+ if(r == nil)
+ return;
+ free(r->path);
+ free(r->b);
+ close(r->i);
+ Bterm(r);
+ free(r);
}
--- a/rtmp.h
+++ b/rtmp.h
@@ -2,4 +2,5 @@
#pragma incomplete RTMP
-RTMP *rtmpdial(char *url);
+RTMP *rtmpdial(char *url, int w, int h, int withaudio);
+void rtmpclose(RTMP *r);