2018-08-24 03:55:36 +03:00
|
|
|
/*
|
|
|
|
NAME
|
|
|
|
parseurl.go
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
See Readme.md
|
|
|
|
|
|
|
|
AUTHOR
|
|
|
|
Saxon Nelson-Milton <saxon@ausocean.org>
|
|
|
|
|
|
|
|
LICENSE
|
|
|
|
parseurl.go is Copyright (C) 2017-2018 the Australian Ocean Lab (AusOcean)
|
|
|
|
|
|
|
|
It is free software: you can redistribute it and/or modify them
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
option) any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2018-08-30 14:01:19 +03:00
|
|
|
along with revid in gpl.txt. If not, see http://www.gnu.org/licenses.
|
2018-08-24 03:55:36 +03:00
|
|
|
|
2018-08-30 14:01:19 +03:00
|
|
|
Derived from librtmp under the GNU Lesser General Public License 2.1
|
|
|
|
Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org
|
|
|
|
Copyright (C) 2008-2009 Andrej Stepanchuk
|
|
|
|
Copyright (C) 2009-2010 Howard Chu
|
2018-08-24 03:55:36 +03:00
|
|
|
*/
|
2018-08-24 00:34:01 +03:00
|
|
|
package rtmp
|
|
|
|
|
2018-08-24 03:03:05 +03:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
2018-08-24 00:34:01 +03:00
|
|
|
// int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
|
|
|
|
// AVal *playpath, AVal *app);
|
|
|
|
// parseurl.c +33
|
|
|
|
func C_RTMP_ParseURL(url *byte, protocol *int32, host *C_AVal, port *uint32,
|
|
|
|
playpath *C_AVal, app *C_AVal) int {
|
|
|
|
|
|
|
|
var p, end, col, ques, slash *byte
|
|
|
|
// TODO: use our logger here
|
|
|
|
// RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
|
|
|
|
|
|
|
|
*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 0
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
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 0
|
|
|
|
}
|
|
|
|
|
|
|
|
end = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(strlen(p))))
|
|
|
|
col = strchr(p, ':')
|
|
|
|
ques = strchr(p, '?')
|
|
|
|
slash = strchr(p, '/')
|
|
|
|
|
|
|
|
{
|
|
|
|
var hostlen int32
|
|
|
|
if slash != nil {
|
|
|
|
hostlen = int32(uintptr(unsafe.Pointer(slash)) - uintptr(unsafe.Pointer(p)))
|
|
|
|
} else {
|
|
|
|
hostlen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p)))
|
|
|
|
}
|
|
|
|
if col != nil && int32(uintptr(unsafe.Pointer(col))-uintptr(unsafe.Pointer(p))) < hostlen {
|
|
|
|
hostlen = int32(uintptr(unsafe.Pointer(col)) - uintptr(unsafe.Pointer(p)))
|
|
|
|
}
|
|
|
|
|
|
|
|
if hostlen < 256 {
|
|
|
|
host.av_val = (*byte)(unsafe.Pointer(p))
|
2018-09-01 16:52:25 +03:00
|
|
|
host.av_len = hostlen
|
2018-08-24 00:34:01 +03:00
|
|
|
// 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
|
|
|
|
if *p == ':' {
|
|
|
|
var p2 uint32
|
|
|
|
p = (*byte)(incBytePtr(unsafe.Pointer(p), 1))
|
|
|
|
tmp, _ := strconv.Atoi(cStrToGoStr(p))
|
|
|
|
p2 = uint32(tmp)
|
|
|
|
if p2 > 65535 {
|
|
|
|
// TODO: use new logger with this
|
|
|
|
// RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
|
|
|
|
} else {
|
|
|
|
*port = p2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if slash == nil {
|
|
|
|
// TODO: use new logger
|
|
|
|
// RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
p = (*byte)(incBytePtr(unsafe.Pointer(slash), 1))
|
|
|
|
|
|
|
|
{
|
|
|
|
/* parse application
|
|
|
|
*
|
|
|
|
* rtmp://host[:port]/app[/appinstance][/...]
|
|
|
|
* application = app[/appinstance]
|
|
|
|
*/
|
|
|
|
|
|
|
|
var slash2 *byte
|
|
|
|
var slash3 *byte = nil
|
|
|
|
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 {
|
|
|
|
slash4 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash3))+
|
|
|
|
uintptr(1))), '/')
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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))
|
2018-09-01 16:52:25 +03:00
|
|
|
app.av_len = applen
|
2018-08-24 00:34:01 +03:00
|
|
|
// TODO: use new logging here
|
|
|
|
// RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
|
|
|
|
|
|
|
|
p = (*byte)(incBytePtr(unsafe.Pointer(p), int(appnamelen)))
|
|
|
|
}
|
|
|
|
|
|
|
|
if *p == '/' {
|
|
|
|
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 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2018-09-01 16:52:25 +03:00
|
|
|
pplen := in.av_len
|
2018-08-24 00:34:01 +03:00
|
|
|
|
|
|
|
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))
|
2018-09-01 16:52:25 +03:00
|
|
|
pplen = strlen(ppstart)
|
2018-08-24 00:34:01 +03:00
|
|
|
|
|
|
|
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 {
|
2018-08-31 05:31:34 +03:00
|
|
|
ext = (*byte)(sliceToPtr((*[_Gi]byte)(unsafe.Pointer(ppstart))[uintptr(pplen)-4:]))
|
2018-08-24 00:34:01 +03:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-01 16:52:25 +03:00
|
|
|
p = ppstart
|
2018-08-24 00:34:01 +03:00
|
|
|
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)
|
2018-08-31 04:54:42 +03:00
|
|
|
(*[_Gi]byte)(unsafe.Pointer(destptr))[0] = byte(c)
|
2018-08-24 00:34:01 +03:00
|
|
|
destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1))
|
|
|
|
pplen -= 3
|
|
|
|
p = (*byte)(incBytePtr(unsafe.Pointer(p), 3))
|
|
|
|
} else {
|
2018-08-31 04:54:42 +03:00
|
|
|
(*[_Gi]byte)(unsafe.Pointer(destptr))[0] = *p
|
2018-08-24 00:34:01 +03:00
|
|
|
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)))
|
|
|
|
}
|