History

fix video stream packing and url parsing (can stream to Twitch directly now)

Sigrid Solveig Haflínudóttir committed 5 years ago

Commit 94d64d61 parent 09b812b4 files · +132 −143Browse files

Unified · Split

4 files
  1. amf0.c +3 −0
  2. ivf.c +4 −9
  3. main.c +9 −19
  4. rtmp.c +116 −115
amf0.c+3−0
@@ -243,10 +243,13 @@ a₀free(A₀ *a) · 242 unmodified lines
243 free(a->obj.k[i]);243 free(a->obj.k[i]);
244 a₀free(a->obj.v[i]);244 a₀free(a->obj.v[i]);
245 }245 }
246 free(a->obj.k);
247 free(a->obj.v);
246 break;248 break;
247 case Tarr:249 case Tarr:
248 for(i = 0; i < a->arr.n; i++)250 for(i = 0; i < a->arr.n; i++)
249 a₀free(a->arr.v[i]);251 a₀free(a->arr.v[i]);
252 free(a->arr.v);
250 case Tnull:253 case Tnull:
251 case Tnum:254 case Tnum:
252 case Tbool:255 case Tbool:
ivf.c+4−9
@@ -2,6 +2,7 @@ · 1 unmodified line
2#include <libc.h>2#include <libc.h>
3#include <bio.h>3#include <bio.h>
4#include "ivf.h"4#include "ivf.h"
5#include "util.h"
5 6
6static int7static int
7Bu16le(Biobuf *b, u16int *o)8Bu16le(Biobuf *b, u16int *o)
@@ -84,7 +85,6 @@ err: · 76 unmodified lines
84int85int
85ivfread(Biobuf *v, IVFrame *f)86ivfread(Biobuf *v, IVFrame *f)
86{87{
87 u8int *buf;
88 u64int ts;88 u64int ts;
89 u32int sz;89 u32int sz;
90 int n;90 int n;
@@ -94,19 +94,14 @@ ivfread(Biobuf *v, IVFrame *f) · 3 unmodified lines
94 f->sz = 0;94 f->sz = 0;
95 return 0;95 return 0;
96 }96 }
97 buf = f->buf;
98 if(sz > f->bufsz){97 if(sz > f->bufsz){
99 if((buf = realloc(f->buf, sz)) == nil){98 f->bufsz = sz*2;
100 werrstr("frame is too big: %d bytes", sz);99 f->buf = erealloc(f->buf, sz*2);
101 goto err;
102 }
103 f->buf = buf;
104 }100 }
105 if((n = Bread(v, buf, sz)) != sz){101 if((n = Bread(v, f->buf, sz)) != sz){
106 werrstr("short read (%d < %d)", n, sz);102 werrstr("short read (%d < %d)", n, sz);
107 goto err;103 goto err;
108 }104 }
109 f->buf = buf;
110 f->sz = sz;105 f->sz = sz;
111 f->ts = ts;106 f->ts = ts;
112 107
main.c+9−19
@@ -19,8 +19,9 @@ usage(void) · 18 unmodified lines
19void19void
20threadmain(int argc, char **argv)20threadmain(int argc, char **argv)
21{21{
22 u64int ns, ons;
23 Biobuf *a, *v;22 Biobuf *a, *v;
23 u64int ns;
24 ulong sid;
24 IVFrame f;25 IVFrame f;
25 IVF ivf;26 IVF ivf;
26 RTMP *r;27 RTMP *r;
@@ -51,37 +52,26 @@ threadmain(int argc, char **argv) · 24 unmodified lines
51 sysfatal("%r");52 sysfatal("%r");
52 if(strcmp(ivf.type, "AVC1") != 0)53 if(strcmp(ivf.type, "AVC1") != 0)
53 sysfatal("not H.264");54 sysfatal("not H.264");
55
54 srand(time(nil));56 srand(time(nil));
55 if((r = rtmpdial(argv[0])) == nil)57 if((r = rtmpdial(argv[0])) == nil)
56 sysfatal("%r");58 sysfatal("%r");
57 ulong sid;
58 fprint(2, "asking for a stream\n");
59 59
60 if(rtmpstream(r, &sid) == 0){60 if(rtmpstream(r, &sid) != 0 ||
61 fprint(2, "stream: %lud\n", sid);61 rtmppublish(r, sid, PubLive, nil) != 0 ||
62 if(rtmppublish(r, sid, PubLive, "???") == 0){62 rtmpmeta(r, sid, VcodecH264, ivf.w, ivf.h, -1) != 0){
63 fprint(2, "stream published\n");63 sysfatal("%r");
64 if(rtmpmeta(r, sid, VcodecH264, 1920, 1088, -1) == 0)
65 fprint(2, "metadata sent\n");
66 else
67 fprint(2, "metadata failed: %r\n");
68 }else
69 fprint(2, "stream publish failed: %r\n");
70 }else{
71 fprint(2, "stream failed\n");
72 }64 }
73 65
74 ons = 0;
75 memset(&f, 0, sizeof(f));66 memset(&f, 0, sizeof(f));
76 for(;;){67 for(;;){
77 if(ivfread(v, &f) != 0)68 if(ivfread(v, &f) != 0)
78 sysfatal("%r");69 sysfatal("%r");
79 if(f.sz == 0)70 if(f.sz == 0)
80 break;71 break;
81 ns = ivfns(&ivf, f.ts);72 ns = ivfns(&ivf, f.ts)/1000000ULL;
82 if(rtmpdata(r, sid, (ns - ons) / 1000000ULL, Tvideo, f.buf, f.sz) != 0)73 if(rtmpdata(r, sid, ns, Tvideo, f.buf, f.sz) != 0)
83 sysfatal("video: flvdata: %r");74 sysfatal("video: flvdata: %r");
84 ons = ns;
85 }75 }
86 76
87 threadexitsall(nil);77 threadexitsall(nil);
rtmp.c+116−115
@@ -108,9 +108,10 @@ struct RTMP { · 107 unmodified lines
108 Buffer i;108 Buffer i;
109 Buffer o;109 Buffer o;
110 Channel *c;110 Channel *c;
111 char *app;
112 char *path;
113 char *tcurl;111 char *tcurl;
112 char *app;
113 char *inst;
114 char *path; /* FIXME no idea what this is for */
114 int chunkin;115 int chunkin;
115 int chunkout;116 int chunkout;
116 int mode;117 int mode;
@@ -122,13 +123,7 @@ struct RTMP { · 5 unmodified lines
122 int tid;123 int tid;
123 Command *w;124 Command *w;
124 }cmds;125 }cmds;
125 struct {126 int sps;
126 int hsent;
127 int spssz;
128 int ppssz;
129 u8int sps[128];
130 u8int pps[128];
131 }v;
132 u8int biobuf[Biobufsz];127 u8int biobuf[Biobufsz];
133};128};
134 129
@@ -238,7 +233,7 @@ bextend(Buffer *b, int bsz) · 103 unmodified lines
238 if(b->bsz >= bsz)233 if(b->bsz >= bsz)
239 return;234 return;
240 ob = b->b;235 ob = b->b;
241 b->b = erealloc(b->b, bsz*2);236 b->b = erealloc(ob, bsz*2);
242 if(ob != nil)237 if(ob != nil)
243 b->p = b->b + (intptr)(ob - b->p);238 b->p = b->b + (intptr)(ob - b->p);
244 b->bsz = bsz*2;239 b->bsz = bsz*2;
@@ -411,11 +406,12 @@ err: · 166 unmodified lines
411static void406static void
412rtmpfree(RTMP *r)407rtmpfree(RTMP *r)
413{408{
414 free(r->app);
415 free(r->i.b);409 free(r->i.b);
416 free(r->o.b);410 free(r->o.b);
417 free(r->path);
418 free(r->tcurl);411 free(r->tcurl);
412 free(r->app);
413 free(r->inst);
414 free(r->path);
419 if(r->c != nil){415 if(r->c != nil){
420 sendp(r->c, "done");416 sendp(r->c, "done");
421 chanfree(r->c);417 chanfree(r->c);
@@ -569,7 +565,7 @@ loop(void *aux) · 147 unmodified lines
569 }565 }
570 break;566 break;
571 567
572 case WindowAckSize:568 case WindowAckSize: /* FIXME send acks too */
573 if(a₀i32get(s, e, &r->winacksz) == nil)569 if(a₀i32get(s, e, &r->winacksz) == nil)
574 goto err;570 goto err;
575 if(debug)571 if(debug)
@@ -715,8 +711,10 @@ rtmppublish(RTMP *r, ulong sid, int type, char *name) · 139 unmodified lines
715 werrstr("invalid publish type %d", type);711 werrstr("invalid publish type %d", type);
716 return -1;712 return -1;
717 }713 }
718 if(name == nil)714 if(name == nil && (name = r->inst) == nil){
719 name = "";715 werrstr("no name to publish to");
716 return -1;
717 }
720 718
721 c = chancreate(sizeof(char*), 0);719 c = chancreate(sizeof(char*), 0);
722 720
@@ -763,8 +761,9 @@ rtmpmeta(RTMP *r, ulong sid, int vcodec, int w, int h, int acodec) · 40 unmodified lines
763 761
764 putstr("onMetaData");762 putstr("onMetaData");
765 putarr();763 putarr();
766 puti32(1 + (vcodec < 0 ? 0 : 3) + (acodec < 0 ? 0 : 1));764 puti32(2 + (vcodec < 0 ? 0 : 3) + (acodec < 0 ? 0 : 1));
767 putkvnum("duration", 0.0);765 putkvnum("duration", 0);
766 putkvnum("filesize", 0);
768 if(vcodec >= 0){767 if(vcodec >= 0){
769 putkvnum("videocodecid", vcodec);768 putkvnum("videocodecid", vcodec);
770 putkvnum("width", w);769 putkvnum("width", w);
@@ -787,111 +786,116 @@ nalsz(u8int *p, int sz, int *csz) · 16 unmodified lines
787{786{
788 int n;787 int n;
789 788
790 for(n = 3; sz-n > 3; n++){789 *csz = 0;
791 if(p[n] == 0 && p[n+1] == 0 && p[n+2] == 1){790 for(n = 0; n < sz-3;){
792 *csz = 3;791 if(p[n] == 0 && p[n+1] == 0){
793 return n;792 if(p[n+2] == 1){
794 }793 *csz += 3;
795 if(p[n] == 0 && p[n+1] == 0 && p[n+2] == 0 && p[n+3] == 1){794 n += 3;
796 *csz = 4;795 }else if(p[n+2] == 0 && p[n+3] == 1){
797 return n;796 *csz += 4;
798 }797 n += 4;
798 }else
799 break;
800 }else
801 break;
799 }802 }
800 803
801 *csz = 0;804 for(; n < sz-3; n++){
805 if(p[n] == 0 && p[n+1] == 0){
806 if(p[n+2] == 1)
807 return n;
808 else if(p[n+2] == 0 && p[n+3] == 1)
809 return n;
810 }
811 }
802 812
803 return sz;813 return sz;
804}814}
805 815
806static int816static int
807mkps(RTMP *r, u8int *p)817h264data(RTMP *r, ulong sid, u32int dt, u8int *p, int sz)
808{818{
809 u8int *p₀;819 u8int *p₀, sps[128], pps[128], ps[16+sizeof(sps)+sizeof(pps)];
820 int sz₀, csz, nsz, ntype, spssz, ppssz, key, total;
810 821
822 sz₀ = sz;
811 p₀ = p;823 p₀ = p;
824 spssz = 0;
825 ppssz = 0;
826 key = 0;
827 for(total = 0; sz > 0; total += 4+nsz, p += nsz, sz -= nsz){
828 nsz = nalsz(p, sz, &csz);
829 p += csz;
830 sz -= csz;
831 nsz -= csz;
812 832
813 *p++ = 1; /* version */833 ntype = *p & 0x1f;
814 *p++ = r->v.sps[1]; /* profile */834 if(ntype == 7){
815 *p++ = r->v.sps[2]; /* compatibility */835 memmove(sps, p, nsz);
816 *p++ = r->v.sps[3]; /* level */836 spssz = nsz;
817 *p++ = 0xfc | 3; /* reserved (6 bits), NULA length size - 1 (2 bits) */837 }
818 *p++ = 0xe0 | 1; /* reserved (3 bits), num of SPS (5 bits) */838 if(ntype == 8){
819 *p++ = r->v.spssz >> 8;839 memmove(pps, p, nsz);
820 *p++ = r->v.spssz;840 ppssz = nsz;
821 p = (u8int*)memmove(p, r->v.sps, r->v.spssz) + r->v.spssz;841 }
822 842 if(ntype == 5)
823 *p++ = 1;843 key = 1;
824 *p++ = r->v.ppssz >> 8;844 }
825 *p++ = r->v.ppssz;
826 p = (u8int*)memmove(p, r->v.pps, r->v.ppssz) + r->v.ppssz;
827
828 return p - p₀;
829}
830
831static int
832data(RTMP *r, ulong sid, u32int dt, int type, int fl, void *data, int sz)
833{
834 bextend(&r->o, 64 + sz);
835 845
836 newmsg(r, type == Taudio ? Audio : Video, Type0, CSData);846 if(spssz > 0 && ppssz > 0 && !r->sps){
837 r->o.msg.ts = dt;847 newmsg(r, Video, Type0, CSData);
838 r->o.msg.sid = sid;848 r->o.msg.ts = dt;
849 r->o.msg.sid = sid;
839 850
840 if(type == Taudio){851 putbyte(0x10 | VcodecH264);
841 putbyte(AcodecAAC<<4 | 0x0f);852 putbyte(0);
842 putbyte((fl & FlHdr) ? 0 : 1);
843 }
844 if(type == Tvideo){
845 putbyte(((fl & FlKey) ? 0x10 : 0x20) | VcodecH264);
846 putbyte((fl & FlHdr) ? 0 : 1);
847 puti24(0);853 puti24(0);
848 if((fl & FlHdr) == 0)854
849 puti32(sz);855 p = ps;
856 *p++ = 1; /* version */
857 *p++ = sps[1]; /* profile */
858 *p++ = sps[2]; /* compatibility */
859 *p++ = sps[3]; /* level */
860 *p++ = 0xfc | 3; /* reserved (6 bits), NULA length size - 1 (2 bits) */
861 *p++ = 0xe0 | 1; /* reserved (3 bits), num of SPS (5 bits) */
862 *p++ = spssz >> 8;
863 *p++ = spssz;
864 p = (u8int*)memmove(p, sps, spssz) + spssz;
865 *p++ = 1;
866 *p++ = ppssz >> 8;
867 *p++ = ppssz;
868 p = (u8int*)memmove(p, pps, ppssz) + ppssz;
869
870 putdata(ps, p-ps);
871
872 if(rtmpsend(r) < 0)
873 return -1;
874
875 r->sps = 1;
850 }876 }
851 putdata(data, sz);
852 877
853 return rtmpsend(r);878 bextend(&r->o, 64+total);
854}
855 879
856static int880 newmsg(r, Video, Type0, CSData);
857h264data(RTMP *r, ulong sid, u32int dt, u8int *p, int sz)881 r->o.msg.ts = dt;
858{882 r->o.msg.sid = sid;
859 int csz, nsz, ntype;
860 u8int ps[32+256];
861 883
862 for(; sz > 0; p += nsz, sz -= nsz){884 putbyte((key ? 0x10 : 0x20) | VcodecH264);
885 putbyte(1);
886 puti24(0);
887
888 for(p = p₀, sz = sz₀; sz > 0; p += nsz, sz -= nsz){
863 nsz = nalsz(p, sz, &csz);889 nsz = nalsz(p, sz, &csz);
864 p += csz;890 p += csz;
865 sz -= csz;891 sz -= csz;
866 nsz -= csz;892 nsz -= csz;
867 893
868 ntype = *p & 0x1f;894 puti32(nsz);
869 if(ntype == 7){895 putdata(p, nsz);
870 memmove(r->v.sps, p, nsz);
871 r->v.spssz = nsz;
872 continue;
873 }
874 if(ntype == 8){
875 memmove(r->v.pps, p, nsz);
876 r->v.ppssz = nsz;
877 continue;
878 }
879
880 if(r->v.spssz < 1 || r->v.ppssz < 1)
881 continue;
882
883 if(!r->v.hsent || ntype == 8){
884 r->v.hsent = 1;
885 if(data(r, sid, 0, Tvideo, FlKey|FlHdr, ps, mkps(r, ps)) < 0)
886 return -1;
887 }
888 if(ntype != 7 && ntype != 8){
889 if(data(r, sid, 100, Tvideo, ntype == 5 ? FlKey : 0, p-csz, nsz+csz) < 0)
890 return -1;
891 }
892 }896 }
893 897
894 return 0;898 return rtmpsend(r);
895}899}
896 900
897int901int
@@ -974,7 +978,7 @@ msgfmt(Fmt *f) · 76 unmodified lines
974RTMP *978RTMP *
975rtmpdial(char *url)979rtmpdial(char *url)
976{980{
977 char *s, *e, *path, *app;981 char *s, *e, *p, *app, *inst, *path;
978 int f, port, ctl;982 int f, port, ctl;
979 RTMP *r;983 RTMP *r;
980 984
@@ -997,14 +1001,14 @@ rtmpdial(char *url) · 16 unmodified lines
997 }1001 }
998 port = 1935;1002 port = 1935;
999 if(*e == ':'){1003 if(*e == ':'){
1000 if((port = strtol(e+1, &path, 10)) < 1 || path == e+1 || *path != '/'){1004 if((port = strtol(e+1, &p, 10)) < 1 || p == e+1 || *p != '/'){
1001 werrstr("invalid port");1005 werrstr("invalid port");
1002 goto err;1006 goto err;
1003 }1007 }
1004 }else{1008 }else{
1005 path = e;1009 p = e;
1006 }1010 }
1007 while(*(++path) == '/');1011 while(*(++p) == '/');
1008 1012
1009 s = smprint("tcp!%.*s!%d", (int)(e-s), s, port);1013 s = smprint("tcp!%.*s!%d", (int)(e-s), s, port);
1010 f = dial(s, nil, nil, &ctl);1014 f = dial(s, nil, nil, &ctl);
@@ -1012,21 +1016,17 @@ rtmpdial(char *url) · 1 unmodified line
1012 if(f < 0)1016 if(f < 0)
1013 goto err;1017 goto err;
1014 1018
1015 app = path;1019 /* rtmp://host:port/app[/inst[/path]] */
1016 if((s = strchr(path, '/')) == nil){1020 app = p;
1017 werrstr("no path");1021 inst = nil;
1018 goto err;1022 path = nil;
1019 }1023 if((s = strchr(p, '/')) != nil){ /* app instance */
1020 if((e = strchr(s+1, '/')) != nil){
1021 /* at this point it can be app instance if there is another slash following */
1022 if((s = strchr(e+1, '/')) == nil){
1023 /* no, just path leftovers */
1024 s = e;
1025 }
1026 *s = 0;1024 *s = 0;
1027 path = s+1;1025 inst = s+1;
1028 }else{1026 if((s = strchr(s+1, '/')) != nil){ /* path */
1029 path = nil;1027 *s = 0;
1028 path = s+1;
1029 }
1030 }1030 }
1031 1031
1032 if(handshake(f) != 0)1032 if(handshake(f) != 0)
@@ -1040,6 +1040,7 @@ rtmpdial(char *url) · 7 unmodified lines
1040 url = nil;1040 url = nil;
1041 r->c = chancreate(sizeof(void*), 0);1041 r->c = chancreate(sizeof(void*), 0);
1042 r->app = estrdup(app);1042 r->app = estrdup(app);
1043 r->inst = inst == nil ? nil : estrdup(inst);
1043 r->path = path == nil ? nil : estrdup(path);1044 r->path = path == nil ? nil : estrdup(path);
1044 bextend(&r->i, Bufsz);1045 bextend(&r->i, Bufsz);
1045 bextend(&r->o, Bufsz);1046 bextend(&r->o, Bufsz);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t