ref: 09b812b49d237cfdcd53273b33dea9ccc355fe9a
parent: 36986cc948a8f422bed5631bf3582ce5657bcde3
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Tue Aug 3 08:26:34 EDT 2021
send video data
--- a/amf0.c
+++ b/amf0.c
@@ -77,6 +77,17 @@
}
u8int *
+a₀i32le(u8int *p, u8int *e, s32int i)
+{
+ atleast("i32le", 4);
+ *p++ = i;
+ *p++ = i >> 8;
+ *p++ = i >> 16;
+ *p++ = i >> 24;
+ return p;
+}
+
+u8int *
a₀num(u8int *p, u8int *e, double v)
{
union {
--- a/amf0.h
+++ b/amf0.h
@@ -33,6 +33,7 @@
u8int *a₀byte(u8int *p, u8int *e, u8int byte);
u8int *a₀i24(u8int *p, u8int *e, s32int i);
u8int *a₀i32(u8int *p, u8int *e, s32int i);
+u8int *a₀i32le(u8int *p, u8int *e, s32int i);
u8int *a₀num(u8int *p, u8int *e, double v);
u8int *a₀str(u8int *p, u8int *e, char *s);
u8int *a₀arr(u8int *p, u8int *e);
--- a/main.c
+++ b/main.c
@@ -59,9 +59,9 @@
if(rtmpstream(r, &sid) == 0){
fprint(2, "stream: %lud\n", sid);
- if(rtmppublish(r, sid, PubLive, "live") == 0){
+ if(rtmppublish(r, sid, PubLive, "???") == 0){
fprint(2, "stream published\n");
- if(rtmpmeta(r, sid, VcodecH264, 1920, 1080, -1) == 0)
+ if(rtmpmeta(r, sid, VcodecH264, 1920, 1088, -1) == 0)
fprint(2, "metadata sent\n");
else
fprint(2, "metadata failed: %r\n");
@@ -79,7 +79,7 @@
if(f.sz == 0)
break;
ns = ivfns(&ivf, f.ts);
- if(rtmpdata(r, sid, (ns - ons) / 1000000ULL, Tvideo, FlHdr, f.buf, f.sz) != 0)
+ if(rtmpdata(r, sid, (ns - ons) / 1000000ULL, Tvideo, f.buf, f.sz) != 0)
sysfatal("video: flvdata: %r");
ons = ns;
}
--- a/rtmp.c
+++ b/rtmp.c
@@ -14,8 +14,11 @@
Port = 1935,
CSsz = 1536,
ChunkDefault = 128,
- ChunkDesired = 4096,
+ ChunkDesired = 65536,
+ FlKey = 1<<0,
+ FlHdr = 1<<1,
+
Type0 = 0,
Type1,
Type2,
@@ -23,6 +26,7 @@
CSUserCtl = 2,
CSCtl = 3,
+ CSData = 4,
CbCommand = 0,
CbTransID,
@@ -76,6 +80,7 @@
void (*cb)(RTMP *r, int ok, A₀ *a[NumCb], void *aux);
void *aux;
int tid;
+ int cs;
Command *prev, *next;
};
@@ -117,6 +122,13 @@
int tid;
Command *w;
}cmds;
+ struct {
+ int hsent;
+ int spssz;
+ int ppssz;
+ u8int sps[128];
+ u8int pps[128];
+ }v;
u8int biobuf[Biobufsz];
};
@@ -142,6 +154,11 @@
r->o.msg.cmd.cb = cb_; \
}while(0)
+#define putcall(name) do { \
+ putstr(name); \
+ putnum(r->o.msg.cmd.tid); \
+}while(0)
+
static int szs[] = {
[Type3] = 0,
[Type2] = 3,
@@ -204,6 +221,7 @@
r->o.msg.cmd.tid = ++r->cmds.tid;
else
r->o.msg.cmd.tid = 0;
+ r->o.msg.cmd.cs = cs;
}
static void
@@ -328,12 +346,12 @@
hsz = szs[m->fmt];
e = h + hsz;
if(hsz >= szs[Type2]){
- h = a₀i24(h, e, 0); /* FIXME put actual timestamps? */
+ h = a₀i24(h, e, m->ts);
if(hsz >= szs[Type1]){
h = a₀i24(h, e, m->sz);
h = a₀byte(h, e, m->type);
if(hsz >= szs[Type0])
- h = a₀i32(h, e, m->sid);
+ h = a₀i32le(h, e, m->sid);
}
}
assert(h != nil);
@@ -487,7 +505,14 @@
werrstr("transaction ID is not a number");
goto err;
}
- for(c = r->cmds.w; c != nil && c->tid != a[n]->num; c = c->next);
+ for(c = r->cmds.w; c != nil; c = c->next){
+ /* transaction id match */
+ if(c->tid == a[n]->num)
+ break;
+ /* no transaction id, but the chunk stream matches */
+ if(a[n]->num == 0 && c->cs == m->cs)
+ break;
+ }
if(c == nil)
fprint(2, "response to non-existent transaction %d", (int)a[n]->num);
break;
@@ -648,8 +673,7 @@
qlock(r);
newmsg(r, AMF0Command, Type0, CSCtl);
- putstr("createStream");
- putnum(r->o.msg.cmd.tid);
+ putcall("createStream");
putnull();
r->o.msg.cmd.cb = streamcreated;
@@ -698,10 +722,8 @@
qlock(r);
- newmsg(r, AMF0Command, Type0, CSCtl);
- notransaction(r);
- putstr("publish");
- putnum(0);
+ newmsg(r, AMF0Command, Type0, CSData);
+ putcall("publish");
putnull();
putstr(name);
putstr(pubtype2s[type]);
@@ -735,12 +757,13 @@
qlock(r);
- newmsg(r, AMF0Metadata, Type0, CSCtl);
- r->o.msg.sid = sid;
+ newmsg(r, AMF0Metadata, Type0, CSData);
+ putstr("@setDataFrame");
+
putstr("onMetaData");
- putnum(0);
- putobj();
+ putarr();
+ puti32(1 + (vcodec < 0 ? 0 : 3) + (acodec < 0 ? 0 : 1));
putkvnum("duration", 0.0);
if(vcodec >= 0){
putkvnum("videocodecid", vcodec);
@@ -750,6 +773,7 @@
if(acodec >= 0)
putkvnum("audiocodecid", acodec);
putend();
+ r->o.msg.sid = sid;
res = rtmpsend(r);
@@ -758,18 +782,59 @@
return res;
}
-int
-rtmpdata(RTMP *r, ulong sid, u32int dt, int type, int fl, void *data, int sz)
+static int
+nalsz(u8int *p, int sz, int *csz)
{
- int res;
+ int n;
- assert(type == Taudio || type == Tvideo);
+ for(n = 3; sz-n > 3; n++){
+ if(p[n] == 0 && p[n+1] == 0 && p[n+2] == 1){
+ *csz = 3;
+ return n;
+ }
+ if(p[n] == 0 && p[n+1] == 0 && p[n+2] == 0 && p[n+3] == 1){
+ *csz = 4;
+ return n;
+ }
+ }
- qlock(r);
+ *csz = 0;
+ return sz;
+}
+
+static int
+mkps(RTMP *r, u8int *p)
+{
+ u8int *p₀;
+
+ p₀ = p;
+
+ *p++ = 1; /* version */
+ *p++ = r->v.sps[1]; /* profile */
+ *p++ = r->v.sps[2]; /* compatibility */
+ *p++ = r->v.sps[3]; /* level */
+ *p++ = 0xfc | 3; /* reserved (6 bits), NULA length size - 1 (2 bits) */
+ *p++ = 0xe0 | 1; /* reserved (3 bits), num of SPS (5 bits) */
+ *p++ = r->v.spssz >> 8;
+ *p++ = r->v.spssz;
+ p = (u8int*)memmove(p, r->v.sps, r->v.spssz) + r->v.spssz;
+
+ *p++ = 1;
+ *p++ = r->v.ppssz >> 8;
+ *p++ = r->v.ppssz;
+ p = (u8int*)memmove(p, r->v.pps, r->v.ppssz) + r->v.ppssz;
+
+ return p - p₀;
+}
+
+static int
+data(RTMP *r, ulong sid, u32int dt, int type, int fl, void *data, int sz)
+{
bextend(&r->o, 64 + sz);
- newmsg(r, type == Taudio ? Audio : Video, Type0, CSCtl);
+ newmsg(r, type == Taudio ? Audio : Video, Type0, CSData);
+ r->o.msg.ts = dt;
r->o.msg.sid = sid;
if(type == Taudio){
@@ -779,14 +844,67 @@
if(type == Tvideo){
putbyte(((fl & FlKey) ? 0x10 : 0x20) | VcodecH264);
putbyte((fl & FlHdr) ? 0 : 1);
- puti24(dt);
+ puti24(0);
if((fl & FlHdr) == 0)
puti32(sz);
}
putdata(data, sz);
- res = rtmpsend(r);
+ return rtmpsend(r);
+}
+static int
+h264data(RTMP *r, ulong sid, u32int dt, u8int *p, int sz)
+{
+ int csz, nsz, ntype;
+ u8int ps[32+256];
+
+ for(; sz > 0; p += nsz, sz -= nsz){
+ nsz = nalsz(p, sz, &csz);
+ p += csz;
+ sz -= csz;
+ nsz -= csz;
+
+ ntype = *p & 0x1f;
+ if(ntype == 7){
+ memmove(r->v.sps, p, nsz);
+ r->v.spssz = nsz;
+ continue;
+ }
+ if(ntype == 8){
+ memmove(r->v.pps, p, nsz);
+ r->v.ppssz = nsz;
+ continue;
+ }
+
+ if(r->v.spssz < 1 || r->v.ppssz < 1)
+ continue;
+
+ if(!r->v.hsent || ntype == 8){
+ r->v.hsent = 1;
+ if(data(r, sid, 0, Tvideo, FlKey|FlHdr, ps, mkps(r, ps)) < 0)
+ return -1;
+ }
+ if(ntype != 7 && ntype != 8){
+ if(data(r, sid, 100, Tvideo, ntype == 5 ? FlKey : 0, p-csz, nsz+csz) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+rtmpdata(RTMP *r, ulong sid, u32int dt, int type, void *p, int sz)
+{
+ int res;
+
+ assert(type == Taudio || type == Tvideo);
+
+ qlock(r);
+ res = 0;
+ if(type == Tvideo)
+ res = h264data(r, sid, dt, p, sz);
qunlock(r);
return res;
@@ -817,6 +935,7 @@
putcommand("connect", connected);
putkvstr("app", r->app);
putkvstr("tcUrl", r->tcurl);
+ putkvstr("type", "nonprivate");
putkvbool("fpad", 0); /* no proxy */
putkvnum("audioCodecs", 0x4 | 0x400); /* mp3 + aac */
putkvnum("videoCodecs", 0x80); /* h.264 */
@@ -907,7 +1026,6 @@
*s = 0;
path = s+1;
}else{
- *s = 0;
path = nil;
}
--- a/rtmp.h
+++ b/rtmp.h
@@ -20,11 +20,8 @@
enum {
Taudio,
Tvideo,
-
- FlKey = 1<<0,
- FlHdr = 1<<1,
};
-int rtmpdata(RTMP *r, ulong sid, u32int ts, int type, int fl, void *data, int sz);
+int rtmpdata(RTMP *r, ulong sid, u32int dt, int type, void *data, int sz);
RTMP *rtmpdial(char *url);
void rtmpclose(RTMP *r);