av/protocol/rtmp/parseurl.go

126 lines
3.2 KiB
Go
Raw Permalink Normal View History

2022-05-31 08:17:06 +03:00
/*
NAME
parseurl.go
DESCRIPTION
See Readme.md
AUTHOR
Dan Kortschak <dan@ausocean.org>
Saxon Nelson-Milton <saxon@ausocean.org>
Alan Noble <alan@ausocean.org>
LICENSE
parseurl.go is Copyright (C) 2017-2019 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
along with revid in gpl.txt. If not, see http://www.gnu.org/licenses.
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
*/
package rtmp
import (
"errors"
"fmt"
"net/url"
"path"
"strconv"
"strings"
)
// Errors.
var (
errInvalidPath = errors.New("invalid url path")
errInvalidElements = errors.New("invalid url elements")
)
// parseURL parses an RTMP URL (ok, technically it is lexing).
func parseURL(addr string) (protocol int32, host string, port uint16, app, playpath string, err error) {
u, err := url.Parse(addr)
if err != nil {
return protocol, host, port, app, playpath, fmt.Errorf("could not parse to url value: %w", err)
}
switch u.Scheme {
case "rtmp":
protocol = protoRTMP
case "rtmpt":
protocol = protoRTMPT
case "rtmps":
protocol = protoRTMPS
case "rtmpe":
protocol = protoRTMPE
case "rtmfp":
protocol = protoRTMFP
case "rtmpte":
protocol = protoRTMPTE
case "rtmpts":
protocol = protoRTMPTS
default:
return protocol, host, port, app, playpath, fmt.Errorf("unknown scheme: %s", u.Scheme)
}
host = u.Host
if p := u.Port(); p != "" {
pi, err := strconv.Atoi(p)
if err != nil {
return protocol, host, port, app, playpath, fmt.Errorf("could convert port to integer: %w", err)
}
port = uint16(pi)
}
if len(u.Path) < 1 || !path.IsAbs(u.Path) {
return protocol, host, port, app, playpath, errInvalidPath
}
elems := strings.SplitN(u.Path[1:], "/", 3)
if len(elems) < 2 || elems[0] == "" || elems[1] == "" {
return protocol, host, port, app, playpath, errInvalidElements
}
app = elems[0]
playpath = path.Join(elems[1:]...)
switch ext := path.Ext(playpath); ext {
case ".f4v", ".mp4":
playpath = playpath[:len(playpath)-len(ext)]
if !strings.HasPrefix(playpath, "mp4:") {
playpath = "mp4:" + playpath
}
case ".mp3":
playpath = playpath[:len(playpath)-len(ext)]
if !strings.HasPrefix(playpath, "mp3:") {
playpath = "mp3:" + playpath
}
case ".flv":
playpath = playpath[:len(playpath)-len(ext)]
}
if u.RawQuery != "" {
playpath += "?" + u.RawQuery
}
switch {
case port != 0:
case (protocol & featureSSL) != 0:
return protocol, host, port, app, playpath, errors.New("ssl not implemented")
case (protocol & featureHTTP) != 0:
port = 80
default:
port = 1935
}
return protocol, host, port, app, playpath, nil
}