mirror of https://bitbucket.org/ausocean/av.git
rtmp: more simplification of url parsing
This commit is contained in:
parent
9212bed0ed
commit
dde20e1a7b
|
@ -32,6 +32,7 @@ LICENSE
|
||||||
package rtmp
|
package rtmp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,6 +69,8 @@ type C_AVal struct {
|
||||||
av_len int32
|
av_len int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *C_AVal) String() string { return CAV(s) }
|
||||||
|
|
||||||
// C_AVal is in amf.h
|
// C_AVal is in amf.h
|
||||||
// amf.h +62
|
// amf.h +62
|
||||||
func AVC(str string) C_AVal {
|
func AVC(str string) C_AVal {
|
||||||
|
@ -82,6 +85,12 @@ func AVC(str string) C_AVal {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CAV(av *C_AVal) string {
|
func CAV(av *C_AVal) string {
|
||||||
|
if av.av_len <= 0 || av.av_val == nil {
|
||||||
|
if av.av_len < 0 {
|
||||||
|
panic(fmt.Sprintf("invalid length: %d", av.av_len))
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return string((*[_Gi]byte)(unsafe.Pointer(av.av_val))[:av.av_len])
|
return string((*[_Gi]byte)(unsafe.Pointer(av.av_val))[:av.av_len])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
328
rtmp/parseurl.go
328
rtmp/parseurl.go
|
@ -6,6 +6,7 @@ DESCRIPTION
|
||||||
See Readme.md
|
See Readme.md
|
||||||
|
|
||||||
AUTHOR
|
AUTHOR
|
||||||
|
Dan Kortschak <dan@ausocean.org>
|
||||||
Saxon Nelson-Milton <saxon@ausocean.org>
|
Saxon Nelson-Milton <saxon@ausocean.org>
|
||||||
|
|
||||||
LICENSE
|
LICENSE
|
||||||
|
@ -32,303 +33,72 @@ LICENSE
|
||||||
package rtmp
|
package rtmp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, AVal *playpath, AVal *app);
|
// int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, AVal *playpath, AVal *app);
|
||||||
// parseurl.c +33
|
// parseurl.c +33
|
||||||
func C_RTMP_ParseURL(url *byte, host *C_AVal, port *uint16, playpath, app *C_AVal) (protocol int32, ok bool) {
|
func C_RTMP_ParseURL(addr string) (protocol int32, host C_AVal, port uint16, playpath, app C_AVal, ok bool) {
|
||||||
var p, end, col, ques, slash *byte
|
u, err := url.Parse(addr)
|
||||||
// TODO: use our logger here
|
if err != nil {
|
||||||
// RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
|
log.Printf("failed to parse addr: %v", err)
|
||||||
|
return protocol, host, port, app, playpath, false
|
||||||
protocol = RTMP_PROTOCOL_RTMP
|
|
||||||
*port = 0
|
|
||||||
playpath.av_len = 0
|
|
||||||
playpath.av_val = nil
|
|
||||||
app.av_len = 0
|
|
||||||
app.av_val = nil
|
|
||||||
|
|
||||||
p = strstr(url, goStrToCStr("://"))
|
|
||||||
|
|
||||||
if p == nil {
|
|
||||||
// TODO: use our logger here
|
|
||||||
log.Println("RTMP URL: No :// in url!")
|
|
||||||
return protocol, false
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
NOTE: the following code nees to be ported if we're using anything other than
|
|
||||||
rtmp!
|
|
||||||
{
|
|
||||||
int len = (int)(p-url);
|
|
||||||
|
|
||||||
if(len == 4 && strncasecmp(url, "rtmp", 4)==0)
|
|
||||||
*protocol = RTMP_PROTOCOL_RTMP;
|
|
||||||
else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0)
|
|
||||||
*protocol = RTMP_PROTOCOL_RTMPT;
|
|
||||||
else if(len == 5 && strncasecmp(url, "rtmps", 5)==0)
|
|
||||||
*protocol = RTMP_PROTOCOL_RTMPS;
|
|
||||||
else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0)
|
|
||||||
*protocol = RTMP_PROTOCOL_RTMPE;
|
|
||||||
else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0)
|
|
||||||
*protocol = RTMP_PROTOCOL_RTMFP;
|
|
||||||
else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0)
|
|
||||||
*protocol = RTMP_PROTOCOL_RTMPTE;
|
|
||||||
else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0)
|
|
||||||
*protocol = RTMP_PROTOCOL_RTMPTS;
|
|
||||||
else {
|
|
||||||
RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n");
|
|
||||||
goto parsehost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// TODO: implement new logger here
|
|
||||||
// RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
|
|
||||||
|
|
||||||
// Get the hostname
|
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(p), 3))
|
|
||||||
|
|
||||||
// check for sudden death
|
|
||||||
if *p == 0 {
|
|
||||||
// TODO: use new logger here
|
|
||||||
// RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
|
|
||||||
return protocol, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(strlen(p))))
|
switch u.Scheme {
|
||||||
col = strchr(p, ':')
|
case "rtmp":
|
||||||
ques = strchr(p, '?')
|
protocol = RTMP_PROTOCOL_RTMP
|
||||||
slash = strchr(p, '/')
|
case "rtmpt":
|
||||||
|
protocol = RTMP_PROTOCOL_RTMPT
|
||||||
{
|
case "rtmps":
|
||||||
var hostlen int32
|
protocol = RTMP_PROTOCOL_RTMPS
|
||||||
if slash != nil {
|
case "rtmpe":
|
||||||
hostlen = int32(uintptr(unsafe.Pointer(slash)) - uintptr(unsafe.Pointer(p)))
|
protocol = RTMP_PROTOCOL_RTMPE
|
||||||
} else {
|
case "rtmfp":
|
||||||
hostlen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p)))
|
protocol = RTMP_PROTOCOL_RTMFP
|
||||||
}
|
case "rtmpte":
|
||||||
if col != nil && int32(uintptr(unsafe.Pointer(col))-uintptr(unsafe.Pointer(p))) < hostlen {
|
protocol = RTMP_PROTOCOL_RTMPTE
|
||||||
hostlen = int32(uintptr(unsafe.Pointer(col)) - uintptr(unsafe.Pointer(p)))
|
case "rtmpts":
|
||||||
}
|
protocol = RTMP_PROTOCOL_RTMPTS
|
||||||
|
default:
|
||||||
if hostlen < 256 {
|
log.Printf("unknown scheme: %q", u.Scheme)
|
||||||
host.av_val = (*byte)(unsafe.Pointer(p))
|
return protocol, host, port, app, playpath, false
|
||||||
host.av_len = hostlen
|
|
||||||
// TODO: use new logger with this
|
|
||||||
//RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host.av_val);
|
|
||||||
} else {
|
|
||||||
// TODO: use new logger with this
|
|
||||||
// RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
|
|
||||||
}
|
|
||||||
|
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(p), int(hostlen)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get port number if available
|
host = AVC(u.Host)
|
||||||
if *p == ':' {
|
if p := u.Port(); p != "" {
|
||||||
var p2 uint32
|
pi, err := strconv.Atoi(p)
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(p), 1))
|
|
||||||
tmp, err := strconv.Atoi(cStrToGoStr(p))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return protocol, false
|
return protocol, host, port, app, playpath, false
|
||||||
}
|
|
||||||
p2 = uint32(tmp)
|
|
||||||
if p2 > 65535 {
|
|
||||||
// TODO: use new logger with this
|
|
||||||
// RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
|
|
||||||
} else {
|
|
||||||
*port = uint16(p2)
|
|
||||||
}
|
}
|
||||||
|
port = uint16(pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
if slash == nil {
|
if !path.IsAbs(u.Path) {
|
||||||
// TODO: use new logger
|
return protocol, host, port, playpath, app, true
|
||||||
// RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
|
|
||||||
return protocol, true
|
|
||||||
}
|
}
|
||||||
|
elems := strings.SplitN(u.Path[1:], "/", 3)
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(slash), 1))
|
app = AVC(elems[0])
|
||||||
|
playpath = AVC(elems[1])
|
||||||
{
|
if len(elems) == 3 && len(elems[2]) != 0 {
|
||||||
/* parse application
|
inst := path.Join(elems[1:]...)
|
||||||
*
|
switch ext := path.Ext(inst); ext {
|
||||||
* rtmp://host[:port]/app[/appinstance][/...]
|
case ".f4v", ".mp4":
|
||||||
* application = app[/appinstance]
|
inst = "mp4:" + inst[:len(inst)-len(ext)]
|
||||||
*/
|
case ".mp3":
|
||||||
|
inst = "mp3:" + inst[:len(inst)-len(ext)]
|
||||||
var slash2 *byte
|
case ".flv":
|
||||||
var slash3 *byte = nil
|
inst = inst[:len(inst)-len(ext)]
|
||||||
var slash4 *byte = nil
|
|
||||||
var applen, appnamelen int32
|
|
||||||
|
|
||||||
slash2 = strchr(p, '/')
|
|
||||||
|
|
||||||
if slash2 != nil {
|
|
||||||
slash3 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash2))+
|
|
||||||
uintptr(1))), '/')
|
|
||||||
}
|
}
|
||||||
if slash3 != nil {
|
if u.RawQuery != "" {
|
||||||
slash4 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash3))+
|
inst += "?" + u.RawQuery
|
||||||
uintptr(1))), '/')
|
|
||||||
}
|
}
|
||||||
|
playpath = AVC(inst)
|
||||||
// ondemand, pass all parameters as app
|
|
||||||
applen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p)))
|
|
||||||
appnamelen = applen
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case ques != nil && strstr(p, goStrToCStr("slist=")) != nil:
|
|
||||||
appnamelen = int32(uintptr(unsafe.Pointer(ques)) - uintptr(unsafe.Pointer(p)))
|
|
||||||
case strings.Compare(cStrToGoStr(p)[:9], "ondemand/") == 0:
|
|
||||||
/* app = ondemand/foobar, only pass app=ondemand */
|
|
||||||
applen = 8
|
|
||||||
appnamelen = 8
|
|
||||||
default:
|
|
||||||
switch {
|
|
||||||
case slash4 != nil:
|
|
||||||
appnamelen = int32(uintptr(unsafe.Pointer(slash4)) - uintptr(
|
|
||||||
unsafe.Pointer(p)))
|
|
||||||
case slash3 != nil:
|
|
||||||
appnamelen = int32(uintptr(unsafe.Pointer(slash3)) - uintptr(
|
|
||||||
unsafe.Pointer(p)))
|
|
||||||
case slash2 != nil:
|
|
||||||
appnamelen = int32(uintptr(unsafe.Pointer(slash2)) - uintptr(
|
|
||||||
unsafe.Pointer(p)))
|
|
||||||
}
|
|
||||||
|
|
||||||
applen = appnamelen
|
|
||||||
}
|
|
||||||
|
|
||||||
app.av_val = (*byte)(unsafe.Pointer(p))
|
|
||||||
app.av_len = applen
|
|
||||||
// TODO: use new logging here
|
|
||||||
// RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
|
|
||||||
|
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(p), int(appnamelen)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *p == '/' {
|
return protocol, host, port, app, playpath, true
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(p), 1))
|
|
||||||
}
|
|
||||||
// NOTE: don't think we currently need this section - see 787 for this func
|
|
||||||
|
|
||||||
if int(uintptr(unsafe.Pointer(end))-uintptr(unsafe.Pointer(p))) != 0 {
|
|
||||||
var av C_AVal
|
|
||||||
av.av_val = (*byte)(unsafe.Pointer(p))
|
|
||||||
av.av_len = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p)))
|
|
||||||
// TODO: port THis
|
|
||||||
//C.RTMP_ParsePlaypath(&av, playpath)
|
|
||||||
C_RTMP_ParsePlaypath(&av, playpath)
|
|
||||||
}
|
|
||||||
|
|
||||||
return protocol, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// void RTMP_ParsePlaypath(AVal *in, AVal *out);
|
|
||||||
// parseurl.c +201
|
|
||||||
func C_RTMP_ParsePlaypath(in, out *C_AVal) {
|
|
||||||
var addMP4 int32 = 0
|
|
||||||
var addMP3 int32 = 0
|
|
||||||
var subExt int32 = 0
|
|
||||||
playpath := in.av_val
|
|
||||||
var temp, q *byte
|
|
||||||
var ext *byte = nil
|
|
||||||
ppstart := (*byte)(unsafe.Pointer(playpath))
|
|
||||||
var streamname, destptr, p *byte
|
|
||||||
|
|
||||||
pplen := in.av_len
|
|
||||||
|
|
||||||
out.av_val = nil
|
|
||||||
out.av_len = 0
|
|
||||||
|
|
||||||
temp = strstr((*byte)(unsafe.Pointer(ppstart)), goStrToCStr("slist="))
|
|
||||||
if *ppstart == '?' && temp != nil {
|
|
||||||
ppstart = (*byte)(incBytePtr(unsafe.Pointer(temp), 6))
|
|
||||||
pplen = strlen(ppstart)
|
|
||||||
|
|
||||||
temp = strchr(ppstart, '&')
|
|
||||||
|
|
||||||
if temp != nil {
|
|
||||||
pplen = int32(uintptr(unsafe.Pointer(temp)) - uintptr(unsafe.Pointer(ppstart)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
q = strchr(ppstart, '?')
|
|
||||||
|
|
||||||
if pplen >= 4 {
|
|
||||||
if q != nil {
|
|
||||||
ext = (*byte)(decBytePtr(unsafe.Pointer(q), 4))
|
|
||||||
} else {
|
|
||||||
ext = (*byte)(sliceToPtr((*[_Gi]byte)(unsafe.Pointer(ppstart))[uintptr(pplen)-4:]))
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case strings.Compare(cStrToGoStr(ext)[:4], ".f4v") == 0 ||
|
|
||||||
strings.Compare(cStrToGoStr(ext)[:4], ".mp4") == 0:
|
|
||||||
addMP4 = 1
|
|
||||||
subExt = 1
|
|
||||||
case ppstart == (*byte)(unsafe.Pointer(playpath)) && strings.Compare(
|
|
||||||
cStrToGoStr(ext)[:4], ".flv") == 0:
|
|
||||||
subExt = 1
|
|
||||||
case strings.Compare(cStrToGoStr(ext)[:4], ".mp3") == 0:
|
|
||||||
addMP3 = 1
|
|
||||||
subExt = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
streamname = (*byte)(malloc(uintptr(pplen + 4 + 1)))
|
|
||||||
|
|
||||||
if streamname == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
destptr = streamname
|
|
||||||
switch {
|
|
||||||
case addMP4 != 0:
|
|
||||||
if strings.Compare(cStrToGoStr(ppstart)[:4], "mp4") != 0 {
|
|
||||||
memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp4:")),
|
|
||||||
uintptr(len("mp4:")))
|
|
||||||
destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4))
|
|
||||||
} else {
|
|
||||||
subExt = 0
|
|
||||||
}
|
|
||||||
case addMP3 != 0:
|
|
||||||
if strings.Compare(cStrToGoStr(ppstart)[:4], "mp3") != 0 {
|
|
||||||
memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp3:")),
|
|
||||||
uintptr(len("mp4:")))
|
|
||||||
destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4))
|
|
||||||
} else {
|
|
||||||
subExt = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p = ppstart
|
|
||||||
for pplen > 0 {
|
|
||||||
if subExt != 0 && p == ext {
|
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(p), 4))
|
|
||||||
pplen -= 4
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if *p == '%' {
|
|
||||||
var c uint32
|
|
||||||
fmt.Sscanf(cStrToGoStr((*byte)(incBytePtr(unsafe.Pointer(p), 1))), "%02x", &c)
|
|
||||||
(*[_Gi]byte)(unsafe.Pointer(destptr))[0] = byte(c)
|
|
||||||
destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1))
|
|
||||||
pplen -= 3
|
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(p), 3))
|
|
||||||
} else {
|
|
||||||
(*[_Gi]byte)(unsafe.Pointer(destptr))[0] = *p
|
|
||||||
destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1))
|
|
||||||
p = (*byte)(incBytePtr(unsafe.Pointer(p), 1))
|
|
||||||
pplen--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*destptr = '\x00'
|
|
||||||
|
|
||||||
out.av_val = (*byte)(unsafe.Pointer(streamname))
|
|
||||||
out.av_len = int32(uintptr(unsafe.Pointer(destptr)) - uintptr(unsafe.Pointer(
|
|
||||||
streamname)))
|
|
||||||
}
|
}
|
||||||
|
|
36
rtmp/rtmp.go
36
rtmp/rtmp.go
|
@ -296,45 +296,19 @@ func C_SocksSetup(r *C_RTMP, sockshost *C_AVal) {
|
||||||
// rtmp.c +757
|
// rtmp.c +757
|
||||||
// NOTE: code dealing with rtmp over http has been disregarded
|
// NOTE: code dealing with rtmp over http has been disregarded
|
||||||
func C_RTMP_SetupURL(r *C_RTMP, addr string) (ok bool) {
|
func C_RTMP_SetupURL(r *C_RTMP, addr string) (ok bool) {
|
||||||
u := goStrToCStr(addr)
|
r.Link.protocol, r.Link.hostname, r.Link.port, r.Link.app, r.Link.playpath0, ok = C_RTMP_ParseURL(addr)
|
||||||
length := strlen(u)
|
|
||||||
|
|
||||||
r.Link.protocol, ok = C_RTMP_ParseURL(u, &r.Link.hostname, &r.Link.port, &r.Link.playpath0, &r.Link.app)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
r.Link.playpath = r.Link.playpath0
|
r.Link.playpath = r.Link.playpath0
|
||||||
|
|
||||||
if r.Link.tcUrl.av_len == 0 {
|
if r.Link.tcUrl.av_len == 0 {
|
||||||
r.Link.tcUrl.av_val = (*byte)(unsafe.Pointer(u))
|
|
||||||
if r.Link.app.av_len != 0 {
|
if r.Link.app.av_len != 0 {
|
||||||
if int(uintptr(unsafe.Pointer(r.Link.app.av_val))) <
|
r.Link.tcUrl = AVC(fmt.Sprintf("%v://%v:%v/%v",
|
||||||
int(uintptr(incBytePtr(unsafe.Pointer(u), int(length)))) {
|
RTMPProtocolStringsLower[r.Link.protocol], &r.Link.hostname, r.Link.port, &r.Link.app))
|
||||||
|
r.Link.lFlags |= RTMP_LF_FTCU
|
||||||
r.Link.tcUrl.av_len = int32(int(r.Link.app.av_len) +
|
|
||||||
int(uintptr(decBytePtr(unsafe.Pointer(r.Link.app.av_val),
|
|
||||||
int(uintptr(unsafe.Pointer(u)))))))
|
|
||||||
} else {
|
|
||||||
length = int32(r.Link.hostname.av_len) + int32(r.Link.app.av_len) +
|
|
||||||
int32(len("rtmpte://:65535/\x00"))
|
|
||||||
|
|
||||||
r.Link.tcUrl.av_val = (*byte)(malloc(uintptr(uintptr(length))))
|
|
||||||
hostname := string((*[_Gi]byte)(unsafe.Pointer(r.Link.hostname.av_val))[:r.Link.hostname.av_len])
|
|
||||||
|
|
||||||
app := string((*[_Gi]byte)(unsafe.Pointer(r.Link.app.av_val))[:r.Link.app.av_len])
|
|
||||||
|
|
||||||
fString := fmt.Sprintf("%v://%v:%v/%v",
|
|
||||||
RTMPProtocolStringsLower[r.Link.protocol], hostname, r.Link.port, app)
|
|
||||||
|
|
||||||
r.Link.tcUrl.av_val = (*byte)(bToUP(goStrToCStr(fString)))
|
|
||||||
r.Link.tcUrl.av_len = int32(strLen(RTMPProtocolStringsLower[r.Link.protocol]) +
|
|
||||||
strLen(string("://")) + strLen(hostname) + strLen(string(":")) +
|
|
||||||
strLen(strconv.Itoa(int(r.Link.port))) + strLen(string("/")) + strLen(app))
|
|
||||||
|
|
||||||
r.Link.lFlags |= RTMP_LF_FTCU
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
r.Link.tcUrl.av_len = int32(strlen(u))
|
r.Link.tcUrl = AVC(addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue