| @@ -108,9 +108,10 @@ struct RTMP { · 107 unmodified lines |
| 108 | 108 | Buffer i; |
| 109 | 109 | Buffer o; |
| 110 | 110 | Channel *c; |
| 111 | | − char *app; |
| 112 | | − char *path; |
| 113 | 111 | char *tcurl; |
| 112 | + char *app; |
| 113 | + char *inst; |
| 114 | + char *path; /* FIXME no idea what this is for */ |
| 114 | 115 | int chunkin; |
| 115 | 116 | int chunkout; |
| 116 | 117 | int mode; |
| @@ -122,13 +123,7 @@ struct RTMP { · 5 unmodified lines |
| 122 | 123 | int tid; |
| 123 | 124 | Command *w; |
| 124 | 125 | }cmds; |
| 125 | | − struct { |
| 126 | | − int hsent; |
| 127 | | − int spssz; |
| 128 | | − int ppssz; |
| 129 | | − u8int sps[128]; |
| 130 | | − u8int pps[128]; |
| 131 | | − }v; |
| 126 | + int sps; |
| 132 | 127 | u8int biobuf[Biobufsz]; |
| 133 | 128 | }; |
| 134 | 129 | |
| @@ -238,7 +233,7 @@ bextend(Buffer *b, int bsz) · 103 unmodified lines |
| 238 | 233 | if(b->bsz >= bsz) |
| 239 | 234 | return; |
| 240 | 235 | ob = b->b; |
| 241 | | − b->b = erealloc(b->b, bsz*2); |
| 236 | + b->b = erealloc(ob, bsz*2); |
| 242 | 237 | if(ob != nil) |
| 243 | 238 | b->p = b->b + (intptr)(ob - b->p); |
| 244 | 239 | b->bsz = bsz*2; |
| @@ -411,11 +406,12 @@ err: · 166 unmodified lines |
| 411 | 406 | static void |
| 412 | 407 | rtmpfree(RTMP *r) |
| 413 | 408 | { |
| 414 | | − free(r->app); |
| 415 | 409 | free(r->i.b); |
| 416 | 410 | free(r->o.b); |
| 417 | | − free(r->path); |
| 418 | 411 | free(r->tcurl); |
| 412 | + free(r->app); |
| 413 | + free(r->inst); |
| 414 | + free(r->path); |
| 419 | 415 | if(r->c != nil){ |
| 420 | 416 | sendp(r->c, "done"); |
| 421 | 417 | chanfree(r->c); |
| @@ -569,7 +565,7 @@ loop(void *aux) · 147 unmodified lines |
| 569 | 565 | } |
| 570 | 566 | break; |
| 571 | 567 | |
| 572 | | − case WindowAckSize: |
| 568 | + case WindowAckSize: /* FIXME send acks too */ |
| 573 | 569 | if(a₀i32get(s, e, &r->winacksz) == nil) |
| 574 | 570 | goto err; |
| 575 | 571 | if(debug) |
| @@ -715,8 +711,10 @@ rtmppublish(RTMP *r, ulong sid, int type, char *name) · 139 unmodified lines |
| 715 | 711 | werrstr("invalid publish type %d", type); |
| 716 | 712 | return -1; |
| 717 | 713 | } |
| 718 | | − if(name == nil) |
| 719 | | − name = ""; |
| 714 | + if(name == nil && (name = r->inst) == nil){ |
| 715 | + werrstr("no name to publish to"); |
| 716 | + return -1; |
| 717 | + } |
| 720 | 718 | |
| 721 | 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 | 762 | putstr("onMetaData"); |
| 765 | 763 | putarr(); |
| 766 | | − puti32(1 + (vcodec < 0 ? 0 : 3) + (acodec < 0 ? 0 : 1)); |
| 767 | | − putkvnum("duration", 0.0); |
| 764 | + puti32(2 + (vcodec < 0 ? 0 : 3) + (acodec < 0 ? 0 : 1)); |
| 765 | + putkvnum("duration", 0); |
| 766 | + putkvnum("filesize", 0); |
| 768 | 767 | if(vcodec >= 0){ |
| 769 | 768 | putkvnum("videocodecid", vcodec); |
| 770 | 769 | putkvnum("width", w); |
| @@ -787,111 +786,116 @@ nalsz(u8int *p, int sz, int *csz) · 16 unmodified lines |
| 787 | 786 | { |
| 788 | 787 | int n; |
| 789 | 788 | |
| 790 | | − for(n = 3; sz-n > 3; n++){ |
| 791 | | − if(p[n] == 0 && p[n+1] == 0 && p[n+2] == 1){ |
| 792 | | − *csz = 3; |
| 793 | | − return n; |
| 794 | | − } |
| 795 | | − if(p[n] == 0 && p[n+1] == 0 && p[n+2] == 0 && p[n+3] == 1){ |
| 796 | | − *csz = 4; |
| 797 | | − return n; |
| 798 | | − } |
| 789 | + *csz = 0; |
| 790 | + for(n = 0; n < sz-3;){ |
| 791 | + if(p[n] == 0 && p[n+1] == 0){ |
| 792 | + if(p[n+2] == 1){ |
| 793 | + *csz += 3; |
| 794 | + n += 3; |
| 795 | + }else if(p[n+2] == 0 && p[n+3] == 1){ |
| 796 | + *csz += 4; |
| 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 | 813 | return sz; |
| 804 | 814 | } |
| 805 | 815 | |
| 806 | 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 | 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 */ |
| 814 | | − *p++ = r->v.sps[1]; /* profile */ |
| 815 | | − *p++ = r->v.sps[2]; /* compatibility */ |
| 816 | | − *p++ = r->v.sps[3]; /* level */ |
| 817 | | − *p++ = 0xfc | 3; /* reserved (6 bits), NULA length size - 1 (2 bits) */ |
| 818 | | − *p++ = 0xe0 | 1; /* reserved (3 bits), num of SPS (5 bits) */ |
| 819 | | − *p++ = r->v.spssz >> 8; |
| 820 | | − *p++ = r->v.spssz; |
| 821 | | − p = (u8int*)memmove(p, r->v.sps, r->v.spssz) + r->v.spssz; |
| 822 | | − |
| 823 | | − *p++ = 1; |
| 824 | | − *p++ = r->v.ppssz >> 8; |
| 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); |
| 833 | + ntype = *p & 0x1f; |
| 834 | + if(ntype == 7){ |
| 835 | + memmove(sps, p, nsz); |
| 836 | + spssz = nsz; |
| 837 | + } |
| 838 | + if(ntype == 8){ |
| 839 | + memmove(pps, p, nsz); |
| 840 | + ppssz = nsz; |
| 841 | + } |
| 842 | + if(ntype == 5) |
| 843 | + key = 1; |
| 844 | + } |
| 835 | 845 | |
| 836 | | − newmsg(r, type == Taudio ? Audio : Video, Type0, CSData); |
| 837 | | − r->o.msg.ts = dt; |
| 838 | | − r->o.msg.sid = sid; |
| 846 | + if(spssz > 0 && ppssz > 0 && !r->sps){ |
| 847 | + newmsg(r, Video, Type0, CSData); |
| 848 | + r->o.msg.ts = dt; |
| 849 | + r->o.msg.sid = sid; |
| 839 | 850 | |
| 840 | | − if(type == Taudio){ |
| 841 | | − putbyte(AcodecAAC<<4 | 0x0f); |
| 842 | | − putbyte((fl & FlHdr) ? 0 : 1); |
| 843 | | − } |
| 844 | | − if(type == Tvideo){ |
| 845 | | − putbyte(((fl & FlKey) ? 0x10 : 0x20) | VcodecH264); |
| 846 | | − putbyte((fl & FlHdr) ? 0 : 1); |
| 851 | + putbyte(0x10 | VcodecH264); |
| 852 | + putbyte(0); |
| 847 | 853 | puti24(0); |
| 848 | | − if((fl & FlHdr) == 0) |
| 849 | | − puti32(sz); |
| 854 | + |
| 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); |
| 854 | | −} |
| 878 | + bextend(&r->o, 64+total); |
| 855 | 879 | |
| 856 | | −static int |
| 857 | | −h264data(RTMP *r, ulong sid, u32int dt, u8int *p, int sz) |
| 858 | | −{ |
| 859 | | − int csz, nsz, ntype; |
| 860 | | − u8int ps[32+256]; |
| 880 | + newmsg(r, Video, Type0, CSData); |
| 881 | + r->o.msg.ts = dt; |
| 882 | + r->o.msg.sid = sid; |
| 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 | 889 | nsz = nalsz(p, sz, &csz); |
| 864 | 890 | p += csz; |
| 865 | 891 | sz -= csz; |
| 866 | 892 | nsz -= csz; |
| 867 | 893 | |
| 868 | | − ntype = *p & 0x1f; |
| 869 | | − if(ntype == 7){ |
| 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 | | − } |
| 894 | + puti32(nsz); |
| 895 | + putdata(p, nsz); |
| 892 | 896 | } |
| 893 | 897 | |
| 894 | | − return 0; |
| 898 | + return rtmpsend(r); |
| 895 | 899 | } |
| 896 | 900 | |
| 897 | 901 | int |
| @@ -974,7 +978,7 @@ msgfmt(Fmt *f) · 76 unmodified lines |
| 974 | 978 | RTMP * |
| 975 | 979 | rtmpdial(char *url) |
| 976 | 980 | { |
| 977 | | − char *s, *e, *path, *app; |
| 981 | + char *s, *e, *p, *app, *inst, *path; |
| 978 | 982 | int f, port, ctl; |
| 979 | 983 | RTMP *r; |
| 980 | 984 | |
| @@ -997,14 +1001,14 @@ rtmpdial(char *url) · 16 unmodified lines |
| 997 | 1001 | } |
| 998 | 1002 | port = 1935; |
| 999 | 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 | 1005 | werrstr("invalid port"); |
| 1002 | 1006 | goto err; |
| 1003 | 1007 | } |
| 1004 | 1008 | }else{ |
| 1005 | | − path = e; |
| 1009 | + p = e; |
| 1006 | 1010 | } |
| 1007 | | − while(*(++path) == '/'); |
| 1011 | + while(*(++p) == '/'); |
| 1008 | 1012 | |
| 1009 | 1013 | s = smprint("tcp!%.*s!%d", (int)(e-s), s, port); |
| 1010 | 1014 | f = dial(s, nil, nil, &ctl); |
| @@ -1012,21 +1016,17 @@ rtmpdial(char *url) · 1 unmodified line |
| 1012 | 1016 | if(f < 0) |
| 1013 | 1017 | goto err; |
| 1014 | 1018 | |
| 1015 | | − app = path; |
| 1016 | | − if((s = strchr(path, '/')) == nil){ |
| 1017 | | − werrstr("no path"); |
| 1018 | | − goto err; |
| 1019 | | − } |
| 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 | | − } |
| 1019 | + /* rtmp://host:port/app[/inst[/path]] */ |
| 1020 | + app = p; |
| 1021 | + inst = nil; |
| 1022 | + path = nil; |
| 1023 | + if((s = strchr(p, '/')) != nil){ /* app instance */ |
| 1026 | 1024 | *s = 0; |
| 1027 | | − path = s+1; |
| 1028 | | − }else{ |
| 1029 | | − path = nil; |
| 1025 | + inst = s+1; |
| 1026 | + if((s = strchr(s+1, '/')) != nil){ /* path */ |
| 1027 | + *s = 0; |
| 1028 | + path = s+1; |
| 1029 | + } |
| 1030 | 1030 | } |
| 1031 | 1031 | |
| 1032 | 1032 | if(handshake(f) != 0) |
| @@ -1040,6 +1040,7 @@ rtmpdial(char *url) · 7 unmodified lines |
| 1040 | 1040 | url = nil; |
| 1041 | 1041 | r->c = chancreate(sizeof(void*), 0); |
| 1042 | 1042 | r->app = estrdup(app); |
| 1043 | + r->inst = inst == nil ? nil : estrdup(inst); |
| 1043 | 1044 | r->path = path == nil ? nil : estrdup(path); |
| 1044 | 1045 | bextend(&r->i, Bufsz); |
| 1045 | 1046 | bextend(&r->o, Bufsz); |