mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-26 20:31:11 +08:00
Add support eseecloud source #1690
This commit is contained in:
10
internal/eseecloud/eseecloud.go
Normal file
10
internal/eseecloud/eseecloud.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package eseecloud
|
||||
|
||||
import (
|
||||
"github.com/AlexxIT/go2rtc/internal/streams"
|
||||
"github.com/AlexxIT/go2rtc/pkg/eseecloud"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
streams.HandleFunc("eseecloud", eseecloud.Dial)
|
||||
}
|
2
main.go
2
main.go
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/AlexxIT/go2rtc/internal/doorbird"
|
||||
"github.com/AlexxIT/go2rtc/internal/dvrip"
|
||||
"github.com/AlexxIT/go2rtc/internal/echo"
|
||||
"github.com/AlexxIT/go2rtc/internal/eseecloud"
|
||||
"github.com/AlexxIT/go2rtc/internal/exec"
|
||||
"github.com/AlexxIT/go2rtc/internal/expr"
|
||||
"github.com/AlexxIT/go2rtc/internal/ffmpeg"
|
||||
@@ -90,6 +91,7 @@ func main() {
|
||||
doorbird.Init() // doorbird source
|
||||
v4l2.Init() // v4l2 source
|
||||
flussonic.Init()
|
||||
eseecloud.Init()
|
||||
|
||||
// 6. Helper modules
|
||||
|
||||
|
180
pkg/eseecloud/eseecloud.go
Normal file
180
pkg/eseecloud/eseecloud.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package eseecloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/h264/annexb"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type Producer struct {
|
||||
core.Connection
|
||||
rd *core.ReadBuffer
|
||||
|
||||
videoPT, audioPT uint8
|
||||
}
|
||||
|
||||
func Dial(rawURL string) (core.Producer, error) {
|
||||
rawURL, _ = strings.CutPrefix(rawURL, "eseecloud")
|
||||
res, err := http.Get("http" + rawURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prod, err := Open(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if info, ok := prod.(core.Info); ok {
|
||||
info.SetProtocol("http")
|
||||
info.SetURL(rawURL)
|
||||
}
|
||||
|
||||
return prod, nil
|
||||
}
|
||||
|
||||
func Open(r io.Reader) (core.Producer, error) {
|
||||
prod := &Producer{
|
||||
Connection: core.Connection{
|
||||
ID: core.NewID(),
|
||||
FormatName: "eseecloud",
|
||||
Transport: r,
|
||||
},
|
||||
rd: core.NewReadBuffer(r),
|
||||
}
|
||||
|
||||
if err := prod.probe(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return prod, nil
|
||||
}
|
||||
|
||||
func (p *Producer) probe() error {
|
||||
b, err := p.rd.Peek(1024)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i := bytes.Index(b, []byte("\r\n\r\n"))
|
||||
if i == -1 {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
b = make([]byte, i+4)
|
||||
_, _ = p.rd.Read(b)
|
||||
|
||||
re := regexp.MustCompile(`m=(video|audio) (\d+) (\w+)/(\d+)\S*`)
|
||||
for _, item := range re.FindAllStringSubmatch(string(b), 2) {
|
||||
p.SDP += item[0] + "\n"
|
||||
|
||||
switch item[3] {
|
||||
case "H264", "H265":
|
||||
p.Medias = append(p.Medias, &core.Media{
|
||||
Kind: core.KindVideo,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{
|
||||
{
|
||||
Name: item[3],
|
||||
ClockRate: 90000,
|
||||
PayloadType: core.PayloadTypeRAW,
|
||||
},
|
||||
},
|
||||
})
|
||||
p.videoPT = byte(core.Atoi(item[2]))
|
||||
|
||||
case "G711":
|
||||
p.Medias = append(p.Medias, &core.Media{
|
||||
Kind: core.KindAudio,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{
|
||||
{
|
||||
Name: core.CodecPCMA,
|
||||
ClockRate: 8000,
|
||||
},
|
||||
},
|
||||
})
|
||||
p.audioPT = byte(core.Atoi(item[2]))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Producer) Start() error {
|
||||
receivers := make(map[uint8]*core.Receiver)
|
||||
|
||||
for _, receiver := range p.Receivers {
|
||||
switch receiver.Codec.Kind() {
|
||||
case core.KindVideo:
|
||||
receivers[p.videoPT] = receiver
|
||||
case core.KindAudio:
|
||||
receivers[p.audioPT] = receiver
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
pkt, err := p.readPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recv := receivers[pkt.PayloadType]; recv != nil {
|
||||
switch recv.Codec.Name {
|
||||
case core.CodecH264, core.CodecH265:
|
||||
// timestamp = seconds x 1000000
|
||||
pkt = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Timestamp: uint32(uint64(pkt.Timestamp) * 90000 / 1000000),
|
||||
},
|
||||
Payload: annexb.EncodeToAVCC(pkt.Payload),
|
||||
}
|
||||
case core.CodecPCMA:
|
||||
pkt = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
SequenceNumber: pkt.SequenceNumber,
|
||||
Timestamp: uint32(uint64(pkt.Timestamp) * 8000 / 1000000),
|
||||
},
|
||||
Payload: pkt.Payload,
|
||||
}
|
||||
}
|
||||
recv.WriteRTP(pkt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Producer) readPacket() (*core.Packet, error) {
|
||||
b := make([]byte, 8)
|
||||
|
||||
if _, err := io.ReadFull(p.rd, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b[0] != '$' {
|
||||
return nil, errors.New("eseecloud: wrong start byte")
|
||||
}
|
||||
|
||||
size := binary.BigEndian.Uint32(b[4:])
|
||||
b = make([]byte, size)
|
||||
if _, err := io.ReadFull(p.rd, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkt := &core.Packet{}
|
||||
if err := pkt.Unmarshal(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.Recv += int(size)
|
||||
|
||||
return pkt, nil
|
||||
}
|
Reference in New Issue
Block a user