| @@ -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 |
| 411 | static void | 406 | static void |
| 412 | rtmpfree(RTMP *r) | 407 | rtmpfree(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 | |
| 806 | static int | 816 | static int |
| 807 | mkps(RTMP *r, u8int *p) | 817 | h264data(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 | | | |
| 831 | static int | | |
| 832 | data(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 | |
| 856 | static int | 880 | newmsg(r, Video, Type0, CSData); |
| 857 | h264data(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 | |
| 897 | int | 901 | int |
| @@ -974,7 +978,7 @@ msgfmt(Fmt *f) · 76 unmodified lines |
| 974 | RTMP * | 978 | RTMP * |
| 975 | rtmpdial(char *url) | 979 | rtmpdial(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); |