Skip to content

Commit

Permalink
messages:
Browse files Browse the repository at this point in the history
- [feat] httpflv, httpts, hls支持跨域请求播放
- [feat] 新增package alpha/stun,学习stun协议
  • Loading branch information
q191201771 committed Sep 5, 2020
1 parent 395b434 commit 5f4ec66
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 81 deletions.
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,6 @@ lalserver详细配置见: [《配置注释文档》](https://github.com/q19120

目前唯一的第三方依赖(我自己写的Go基础库): [github.com/q191201771/naza](https://github.com/q191201771/naza)

### 文档

* [流媒体音视频相关的点我](https://pengrl.com/categories/%E6%B5%81%E5%AA%92%E4%BD%93%E9%9F%B3%E8%A7%86%E9%A2%91/)
* [Go语言相关的点我](https://pengrl.com/categories/Go/)
* [我写的其他文章](https://pengrl.com/all/)

### 联系我

扫码加我微信(微信号: q191201771),进行技术交流或扯淡。微信群已开放,加我好友后可拉进群。
Expand Down
2 changes: 1 addition & 1 deletion app/demo/analyseflv/analyseflv.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import (
var (
timestampCheckFlag = true
printStatFlag = true
printEveryTagFlag = false
printEveryTagFlag = true
printMetaData = true
analysisVideoTagFlag = true
)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ module github.com/q191201771/lal

go 1.12

require github.com/q191201771/naza v0.13.4
require github.com/q191201771/naza v0.14.0
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
github.com/q191201771/naza v0.13.4 h1:ogdobhcSMGqxKUH3tAbiQvF3gHXqMTt7AUdewiqgrWs=
github.com/q191201771/naza v0.13.4/go.mod h1:SE14GBGO9mAn6JZl3NlfWGtNOT7xQjxOG7f3YOdBThM=
github.com/q191201771/naza v0.14.0 h1:bgjiNRmaSxoYwuyjNvCOtvJgwFYmGlBj6tQpaaK2zhE=
github.com/q191201771/naza v0.14.0/go.mod h1:SE14GBGO9mAn6JZl3NlfWGtNOT7xQjxOG7f3YOdBThM=
51 changes: 51 additions & 0 deletions pkg/alpha/stun/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)

package stun

import (
"fmt"
"strings"

"github.com/q191201771/naza/pkg/nazanet"
)

// TODO chef:
// - 设置超时
// - 重试

type Client struct {
}

// @param addr 填入server地址,如果不包含端口,则使用默认端口3478
func (c *Client) Query(addr string, timeoutMS int) (ip string, port int, err error) {
if !strings.Contains(addr, ":") {
addr = fmt.Sprintf("%s:%d", addr, DefaultPort)
}

uc, err := nazanet.NewUDPConnection("", addr)
if err != nil {
return "", 0, err
}
req, err := PackBindingRequest()
if err != nil {
return "", 0, err
}
if err := uc.Write(req); err != nil {
return "", 0, err
}

b, _, err := uc.ReadWithTimeout(timeoutMS)
if err != nil {
return "", 0, err
}

_ = uc.Dispose()

return ParseMessage(b)
}
152 changes: 152 additions & 0 deletions pkg/alpha/stun/pack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)

package stun

import (
"bytes"
"crypto/rand"
"fmt"

"github.com/q191201771/naza/pkg/bele"
)

// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0 0| STUN Message Type | Message Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Magic Cookie |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// | Transaction ID (96 bits) |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Figure 2: Format of STUN Message Header

const minStunMessageSize = 20

var (
magicCookie = []byte{0x21, 0x12, 0xa4, 0x42}
magicCookieBE = 0x2112a442

//typeBindSuccessResponse = []byte{0x1, 0x1}
typeBindingRequest = []byte{0x0, 0x1}
typeBindSuccessResponseBE = 0x0101

//attrTypeXORMappedAddress = []byte{0x0, 0x20}
//attrTypeXORMappedAddress2 = []byte{0x80, 0x20}
//attrTypeMappedAddress = []byte{0x0, 0x1}
attrTypeXORMappedAddressBE = 0x0020
attrTypeXORMappedAddress2BE = 0x8020
attrTypeMappedAddressBE = 0x0001

protocolFamilyIPv4 = []byte{0x0, 0x1}
)

func PackBindingRequest() ([]byte, error) {
b := make([]byte, minStunMessageSize)
copy(b, typeBindingRequest)
// b[2:4] message length 0
copy(b[4:], magicCookie)
// transaction id
if _, err := rand.Reader.Read(b[8:]); err != nil {
return nil, err
}
return b, nil
}

func ParseMessage(b []byte) (ip string, port int, err error) {
if len(b) < minStunMessageSize {
return "", 0, ErrStun
}
// TODO chef: only impled bind success response
if int(bele.BEUint16(b[:2])) != typeBindSuccessResponseBE {
return "", 0, ErrStun
}

messageLength := bele.BEUint16(b[2:])

if bytes.Compare(b[4:8], magicCookie) != 0 {
return "", 0, ErrStun
}

// transaction id

if len(b) < minStunMessageSize+int(messageLength) {
return "", 0, ErrStun
}

// attr list
pos := minStunMessageSize
for {
if len(b[pos:]) < 4 {
return "", 0, ErrStun
}
at := int(bele.BEUint16(b[pos : pos+2]))
al := int(bele.BEUint16(b[pos+2 : pos+4]))
pos += 4
if len(b[pos:]) < al {
return "", 0, ErrStun
}

if at == attrTypeXORMappedAddressBE || at == attrTypeXORMappedAddress2BE {
ip, port, err = parseAttrXORMappedAddress(b[pos:])
if err != nil {
return "", 0, err
}
}
if at == attrTypeMappedAddressBE {
ip, port, err = parseAttrMappedAddress(b[pos:])
if err != nil {
return "", 0, err
}
}

pos += al
if pos == minStunMessageSize+int(messageLength) {
break
}
}

return ip, port, nil
}

func parseAttrXORMappedAddress(b []byte) (ip string, port int, err error) {
if bytes.Compare(b[:2], protocolFamilyIPv4) != 0 {
return "", 0, ErrStun
}

port = int(bele.BEUint16(b[2:])) ^ (magicCookieBE >> 16)

ipb := make([]byte, 4)
xor(b[4:], magicCookie, ipb)
ip = fmt.Sprintf("%d.%d.%d.%d", ipb[0], ipb[1], ipb[2], ipb[3])
return
}

func parseAttrMappedAddress(b []byte) (ip string, port int, err error) {
if bytes.Compare(b[:2], protocolFamilyIPv4) != 0 {
return "", 0, ErrStun
}

port = int(bele.BEUint16(b[2:]))
ip = fmt.Sprintf("%d.%d.%d.%d", b[4], b[5], b[6], b[7])
return
}

func xor(a, b, res []byte) {
n := len(a)
if n > len(b) {
n = len(b)
}
for i := 0; i < n; i++ {
res[i] = a[i] ^ b[i]
}
}
23 changes: 23 additions & 0 deletions pkg/alpha/stun/stun.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)

package stun

import "errors"

// TODO chef:
// - attr soft

// Session Traversal Utilities for NAT
//
// rfc 5389
//

var ErrStun = errors.New("lal.stun: fxxk")

var DefaultPort = 3478
72 changes: 72 additions & 0 deletions pkg/alpha/stun/stun_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)

package stun

import (
"sync"
"testing"

"github.com/q191201771/naza/pkg/nazalog"
)

var serverAddrList = []string{
// dial udp: lookup stun01.sipphone.com: no such host
// ----------
//"stun01.sipphone.com",

// XOR-MAPPED-ADDRESS
// ----------
"stun.l.google.com:19302",
"stun4.l.google.com:19302",

// XOR-MAPPED-ADDRESS
// MAPPED-ADDRESS
// RESPONSE-ORIGIN
// OTHER-ADDRESS
// SOFTWARE
// FINGERPRINT
// ----------
"stun.freeswitch.org:3478",

// MAPPED-ADDRESS
// SOURCE_ADDRESS
// CHANGED_ADDRESS
// XOR-MAPPED-ADDRESS
// SOFTWARE
// ----------
"stun.xten.com",
"stun.ekiga.net",
"stun.schlund.de",

// MAPPED-ADDRESS
// SOURCE_ADDRESS
// CHANGED_ADDRESS
// ----------
"stun.ideasip.com",
"stun.voiparound.com",
"stun.voipbuster.com",
"stun.voipstunt.com",
}

func TestClient(t *testing.T) {
var wg sync.WaitGroup
for _, s := range serverAddrList {
wg.Add(1)
go func(ss string) {
var c Client
ip, port, err := c.Query(ss, 200)
nazalog.Debugf("server=%s, addr=%s:%d, err=%+v", ss, ip, port, err)
wg.Done()
}(s)
}
wg.Wait()
}

func TestServer(t *testing.T) {
}
62 changes: 62 additions & 0 deletions pkg/hls/m3u8.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)

package hls

import (
"bytes"
"fmt"
"os"
"strconv"
)

// @param content 需写入文件的内容
// @param filename m3u8文件名
// @param filenameBak m3u8临时文件名
//
func writeM3U8File(content []byte, filename string, filenameBak string) error {
var fp *os.File
var err error
if fp, err = os.Create(filenameBak); err != nil {
return err
}
if _, err = fp.Write(content); err != nil {
return err
}
if err = fp.Close(); err != nil {
return err
}
if err = os.Rename(filenameBak, filename); err != nil {
return err
}
return nil
}

func updateTargetDurationInM3U8(content []byte, currDuration int) ([]byte, error) {
l := bytes.Index(content, []byte("#EXT-X-TARGETDURATION:"))
if l == -1 {
return content, ErrHLS
}
r := bytes.Index(content[l:], []byte{'\n'})
if r == -1 {
return content, ErrHLS
}
oldDurationStr := bytes.TrimPrefix(content[l:l+r], []byte("#EXT-X-TARGETDURATION:"))
oldDuration, err := strconv.Atoi(string(oldDurationStr))
if err != nil {
return content, err
}
if currDuration > oldDuration {
tmpContent := make([]byte, l)
copy(tmpContent, content[:l])
tmpContent = append(tmpContent, []byte(fmt.Sprintf("#EXT-X-TARGETDURATION:%d", currDuration))...)
tmpContent = append(tmpContent, content[l+r:]...)
content = tmpContent
}
return content, nil
}
Loading

0 comments on commit 5f4ec66

Please sign in to comment.