test dvrip
This commit is contained in:
parent
5adbbcc01f
commit
5b25bda1a0
502
format/dvrip/client.go
Normal file
502
format/dvrip/client.go
Normal file
@ -0,0 +1,502 @@
|
||||
package dvrip
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/codec"
|
||||
"github.com/deepch/vdk/codec/h264parser"
|
||||
)
|
||||
|
||||
const (
|
||||
SignalStreamStop = iota
|
||||
SignalCodecUpdate
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
conn net.Conn
|
||||
login string
|
||||
password string
|
||||
host string
|
||||
stream string
|
||||
sequenceNumber int32
|
||||
session int32
|
||||
aliveInterval time.Duration
|
||||
CodecData []av.CodecData
|
||||
OutgoingPacketQueue chan *av.Packet
|
||||
Signals chan int
|
||||
options ClientOptions
|
||||
sps []byte
|
||||
pps []byte
|
||||
}
|
||||
|
||||
type ClientOptions struct {
|
||||
Debug bool
|
||||
URL string
|
||||
DialTimeout time.Duration
|
||||
ReadWriteTimeout time.Duration
|
||||
DisableAudio bool
|
||||
}
|
||||
|
||||
//Dial func
|
||||
func Dial(options ClientOptions) (*Client, error) {
|
||||
client := &Client{
|
||||
Signals: make(chan int, 100),
|
||||
OutgoingPacketQueue: make(chan *av.Packet, 3000),
|
||||
options: options,
|
||||
}
|
||||
err := client.parseURL(html.UnescapeString(client.options.URL))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.conn, err = net.DialTimeout("tcp", client.host, time.Second*2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = client.conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = client.Login()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = client.SetTime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go client.Monitor()
|
||||
return client, nil
|
||||
}
|
||||
|
||||
//Close func
|
||||
func (client *Client) Close() error {
|
||||
err := client.conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
//SetKeepAlive func
|
||||
func (client *Client) SetKeepAlive() error {
|
||||
body, err := json.Marshal(map[string]string{
|
||||
"Name": "KeepAlive",
|
||||
"SessionID": fmt.Sprintf("0x%08X", client.session),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = client.send(codeKeepAlive, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Monitor func
|
||||
func (client *Client) Monitor() {
|
||||
defer func() {
|
||||
client.Signals <- SignalStreamStop
|
||||
}()
|
||||
_, _, err := client.Command(codeOPMonitor, map[string]interface{}{
|
||||
"Action": "Claim",
|
||||
"Parameter": map[string]interface{}{
|
||||
"Channel": 0,
|
||||
"CombinMode": "NONE",
|
||||
"StreamType": client.stream,
|
||||
"TransMode": "TCP",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
payload, err := json.Marshal(map[string]interface{}{
|
||||
"Name": "OPMonitor",
|
||||
"SessionID": fmt.Sprintf("0x%08X", client.session),
|
||||
"OPMonitor": map[string]interface{}{
|
||||
"Action": "Start",
|
||||
"Parameter": map[string]interface{}{
|
||||
"Channel": 0,
|
||||
"CombinMode": "NONE",
|
||||
"StreamType": client.stream,
|
||||
"TransMode": "TCP",
|
||||
},
|
||||
},
|
||||
})
|
||||
err = client.send(1410, payload)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var length uint32 = 0
|
||||
var dataType uint32
|
||||
timer := time.Now()
|
||||
var fps int
|
||||
for {
|
||||
if time.Now().Sub(timer).Milliseconds() > client.aliveInterval.Milliseconds() {
|
||||
err = client.SetKeepAlive()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
timer = time.Now()
|
||||
}
|
||||
_, body, err := client.recv(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := bytes.NewReader(body)
|
||||
err = binary.Read(buf, binary.BigEndian, &dataType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch dataType {
|
||||
case 0x1FC, 0x1FE:
|
||||
frame := struct {
|
||||
Media byte
|
||||
FPS byte
|
||||
Width byte
|
||||
Height byte
|
||||
DateTime uint32
|
||||
Length uint32
|
||||
}{}
|
||||
err = binary.Read(buf, binary.LittleEndian, &frame)
|
||||
fps = int(frame.FPS)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var packet bytes.Buffer
|
||||
if frame.Length > uint32(buf.Len()) {
|
||||
need := frame.Length - uint32(buf.Len())
|
||||
_, err = buf.WriteTo(&packet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err := client.recvSize(&packet, need)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, err = buf.WriteTo(&packet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if parseMediaType(dataType, frame.Media) == av.H264.String() {
|
||||
packets, _ := h264parser.SplitNALUs(packet.Bytes())
|
||||
for _, i2 := range packets {
|
||||
naluType := i2[0] & 0x1f
|
||||
switch {
|
||||
case naluType >= 1 && naluType <= 5:
|
||||
client.OutgoingPacketQueue <- &av.Packet{Duration: time.Duration(1000/fps) * time.Millisecond, Idx: 0, IsKeyFrame: naluType == 5, Data: append(binSize(len(i2)), i2...)}
|
||||
case naluType == 7:
|
||||
client.CodecUpdateSPS(i2)
|
||||
case naluType == 8:
|
||||
client.CodecUpdatePPS(i2)
|
||||
}
|
||||
}
|
||||
}
|
||||
case 0x1FD:
|
||||
err = binary.Read(buf, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var packet bytes.Buffer
|
||||
if length > uint32(buf.Len()) {
|
||||
need := length - uint32(buf.Len())
|
||||
_, err = buf.WriteTo(&packet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err := client.recvSize(&packet, need)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, err = buf.WriteTo(&packet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
packets, _ := h264parser.SplitNALUs(packet.Bytes())
|
||||
for _, i2 := range packets {
|
||||
naluType := i2[0] & 0x1f
|
||||
switch {
|
||||
case naluType >= 1 && naluType <= 5:
|
||||
if fps != 0 {
|
||||
client.OutgoingPacketQueue <- &av.Packet{Duration: time.Duration(1000/fps) * time.Millisecond, Idx: 0, IsKeyFrame: naluType == 5, Data: append(binSize(len(i2)), i2...)}
|
||||
}
|
||||
case naluType == 7:
|
||||
client.CodecUpdateSPS(i2)
|
||||
case naluType == 8:
|
||||
client.CodecUpdatePPS(i2)
|
||||
}
|
||||
}
|
||||
case 0x1FA, 0x1F9:
|
||||
if client.options.DisableAudio {
|
||||
continue
|
||||
}
|
||||
frame := struct {
|
||||
Media byte
|
||||
SampleRate byte
|
||||
Length uint16
|
||||
}{}
|
||||
err = binary.Read(buf, binary.LittleEndian, &frame)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var packet bytes.Buffer
|
||||
if uint32(frame.Length) > uint32(buf.Len()) {
|
||||
need := uint32(frame.Length) - uint32(buf.Len())
|
||||
_, err = buf.WriteTo(&packet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err := client.recvSize(&packet, need)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, err = buf.WriteTo(&packet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if parseMediaType(dataType, frame.Media) == av.PCM_ALAW.String() {
|
||||
if client.CodecData != nil {
|
||||
if len(client.CodecData) == 1 {
|
||||
client.CodecUpdatePCMAlaw()
|
||||
}
|
||||
client.OutgoingPacketQueue <- &av.Packet{Duration: time.Duration(8000/packet.Len()) * time.Millisecond, Idx: 1, Data: packet.Bytes()}
|
||||
}
|
||||
}
|
||||
case 0xFFD8FFE0:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (client *Client) SetTime() error {
|
||||
_, _, err := client.Command(codeOPTimeSetting, time.Now().Format("2006-01-02 15:04:05"))
|
||||
return err
|
||||
}
|
||||
func (client *Client) Login() error {
|
||||
body, err := json.Marshal(map[string]string{
|
||||
"EncryptType": "MD5",
|
||||
"LoginType": "DVRIP-WEB",
|
||||
"PassWord": sofiaHash(client.password),
|
||||
"UserName": client.login,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = client.send(codeLogin, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, resp, err := client.recv(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := LoginResp{}
|
||||
err = json.Unmarshal(resp, &res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (statusCode(res.Ret) != statusOK) && (statusCode(res.Ret) != statusUpgradeSuccessful) {
|
||||
return fmt.Errorf("unexpected status code: %v - %v", res.Ret, statusCodes[statusCode(res.Ret)])
|
||||
}
|
||||
client.aliveInterval = time.Duration(res.AliveInterval) * time.Second
|
||||
session, err := strconv.ParseUint(res.SessionID, 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.session = int32(session)
|
||||
return nil
|
||||
}
|
||||
|
||||
//Command func
|
||||
func (client *Client) Command(command requestCode, data interface{}) (*Payload, []byte, error) {
|
||||
params, err := json.Marshal(map[string]interface{}{
|
||||
"Name": requestCodes[command],
|
||||
"SessionID": fmt.Sprintf("0x%08X", client.session),
|
||||
requestCodes[command]: data,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = client.send(command, params)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
resp, body, err := client.recv(true)
|
||||
return resp, body, err
|
||||
}
|
||||
|
||||
//send func
|
||||
func (client *Client) send(msgID requestCode, data []byte) error {
|
||||
var buf bytes.Buffer
|
||||
if err := binary.Write(&buf, binary.LittleEndian, Payload{
|
||||
Head: 255,
|
||||
Version: 0,
|
||||
Session: client.session,
|
||||
SequenceNumber: client.sequenceNumber,
|
||||
MsgID: int16(msgID),
|
||||
BodyLength: int32(len(data)) + 2,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
err := client.conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(&buf, binary.LittleEndian, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(&buf, binary.LittleEndian, magicEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = client.conn.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.sequenceNumber++
|
||||
return nil
|
||||
}
|
||||
|
||||
//recvSize func
|
||||
func (client *Client) recvSize(buffer *bytes.Buffer, size uint32) ([]byte, error) {
|
||||
all := uint32(0)
|
||||
for {
|
||||
_, body, err := client.recv(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all += uint32(len(body))
|
||||
buffer.Write(body)
|
||||
if all == size {
|
||||
break
|
||||
} else if all > size {
|
||||
return nil, fmt.Errorf("invalid read size")
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
//recv func
|
||||
func (client *Client) recv(text bool) (*Payload, []byte, error) {
|
||||
var p Payload
|
||||
var b = make([]byte, 20)
|
||||
err := client.conn.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
_, err = client.conn.Read(b)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
client.sequenceNumber += 1
|
||||
if p.BodyLength <= 0 || p.BodyLength >= 100000 {
|
||||
return nil, nil, fmt.Errorf("invalid bodylength: %v", p.BodyLength)
|
||||
}
|
||||
body := make([]byte, p.BodyLength)
|
||||
err = binary.Read(client.conn, binary.LittleEndian, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if text && len(body) > 2 && bytes.Compare(body[len(body)-2:], []byte{10, 0}) == 0 {
|
||||
body = body[:len(body)-2]
|
||||
}
|
||||
return &p, body, nil
|
||||
}
|
||||
|
||||
//parseURL func
|
||||
func (client *Client) parseURL(rawURL string) error {
|
||||
l, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
username := l.User.Username()
|
||||
password, _ := l.User.Password()
|
||||
l.User = nil
|
||||
if l.Port() == "" {
|
||||
l.Host = fmt.Sprintf("%s:%s", l.Host, "34567")
|
||||
}
|
||||
if username == "" {
|
||||
username = "admin"
|
||||
}
|
||||
if password == "" {
|
||||
password = "admin"
|
||||
}
|
||||
client.login = username
|
||||
client.password = password
|
||||
client.host = l.Host
|
||||
client.stream = strings.Trim(l.EscapedPath(), "/")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *Client) CodecUpdateSPS(val []byte) {
|
||||
if bytes.Compare(val, client.sps) == 0 {
|
||||
return
|
||||
}
|
||||
client.sps = val
|
||||
if len(client.pps) == 0 {
|
||||
return
|
||||
}
|
||||
codecData, err := h264parser.NewCodecDataFromSPSAndPPS(val, client.pps)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(client.CodecData) > 0 {
|
||||
for i, i2 := range client.CodecData {
|
||||
if i2.Type().IsVideo() {
|
||||
client.CodecData[i] = codecData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.CodecData = append(client.CodecData, codecData)
|
||||
}
|
||||
client.Signals <- SignalCodecUpdate
|
||||
}
|
||||
|
||||
func (client *Client) CodecUpdatePPS(val []byte) {
|
||||
if bytes.Compare(val, client.pps) == 0 {
|
||||
return
|
||||
}
|
||||
client.pps = val
|
||||
if len(client.sps) == 0 {
|
||||
return
|
||||
}
|
||||
codecData, err := h264parser.NewCodecDataFromSPSAndPPS(client.sps, val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(client.CodecData) > 0 {
|
||||
for i, i2 := range client.CodecData {
|
||||
if i2.Type().IsVideo() {
|
||||
client.CodecData[i] = codecData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.CodecData = append(client.CodecData, codecData)
|
||||
}
|
||||
client.Signals <- SignalCodecUpdate
|
||||
}
|
||||
|
||||
func (client *Client) CodecUpdatePCMAlaw() {
|
||||
CodecData := codec.NewPCMAlawCodecData()
|
||||
client.CodecData = append(client.CodecData, CodecData)
|
||||
client.Signals <- SignalCodecUpdate
|
||||
}
|
185
format/dvrip/struct.go
Normal file
185
format/dvrip/struct.go
Normal file
@ -0,0 +1,185 @@
|
||||
package dvrip
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
)
|
||||
|
||||
var magicEnd = [2]byte{0x0A, 0x00}
|
||||
|
||||
const alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
type Payload struct {
|
||||
Head byte
|
||||
Version byte
|
||||
_ byte
|
||||
_ byte
|
||||
Session int32
|
||||
SequenceNumber int32
|
||||
_ byte
|
||||
_ byte
|
||||
MsgID int16
|
||||
BodyLength int32
|
||||
}
|
||||
type LoginResp struct {
|
||||
AliveInterval int `json:"AliveInterval"`
|
||||
ChannelNum int `json:"ChannelNum"`
|
||||
DeviceType string `json:"DeviceType "`
|
||||
ExtraChannel int `json:"ExtraChannel"`
|
||||
Ret int `json:"Ret"`
|
||||
SessionID string `json:"SessionID"`
|
||||
}
|
||||
|
||||
type requestCode uint16
|
||||
|
||||
const (
|
||||
codeLogin requestCode = 1000
|
||||
codeKeepAlive requestCode = 1006
|
||||
codeSystemInfo requestCode = 1020
|
||||
codeNetWorkNetCommon requestCode = 1042
|
||||
codeGeneral requestCode = 1042
|
||||
codeChannelTitle requestCode = 1046
|
||||
codeSystemFunction requestCode = 1360
|
||||
codeEncodeCapability requestCode = 1360
|
||||
codeOPPTZControl requestCode = 1400
|
||||
codeOPMonitor requestCode = 1413
|
||||
codeOPTalk requestCode = 1434
|
||||
codeOPTimeSetting requestCode = 1450
|
||||
codeOPMachine requestCode = 1450
|
||||
codeOPTimeQuery requestCode = 1452
|
||||
codeAuthorityList requestCode = 1470
|
||||
codeUsers requestCode = 1472
|
||||
codeGroups requestCode = 1474
|
||||
codeAddGroup requestCode = 1476
|
||||
codeModifyGroup requestCode = 1478
|
||||
codeDelGroup requestCode = 1480
|
||||
codeAddUser requestCode = 1482
|
||||
codeModifyUser requestCode = 1484
|
||||
codeDelUser requestCode = 1486
|
||||
codeModifyPassword requestCode = 1488
|
||||
codeAlarmSet requestCode = 1500
|
||||
codeOPNetAlarm requestCode = 1506
|
||||
codeAlarmInfo requestCode = 1504
|
||||
codeOPSendFile requestCode = 1522
|
||||
codeOPSystemUpgrade requestCode = 1525
|
||||
codeOPNetKeyboard requestCode = 1550
|
||||
codeOPSNAP requestCode = 1560
|
||||
codeOPMailTest requestCode = 1636
|
||||
)
|
||||
|
||||
type statusCode int
|
||||
|
||||
const (
|
||||
statusOK statusCode = 100
|
||||
statusUnknownError statusCode = 101
|
||||
statusUnsupportedVersion statusCode = 102
|
||||
statusRequestNotPermitted statusCode = 103
|
||||
statusUserAlreadyLoggedIn statusCode = 104
|
||||
statusUserIsNotLoggedIn statusCode = 105
|
||||
statusUsernameOrPasswordIsIncorrect statusCode = 106
|
||||
statusUserDoesNotHaveNecessaryPermissions statusCode = 107
|
||||
statusPasswordIsIncorrect statusCode = 203
|
||||
statusStartOfUpgrade statusCode = 511
|
||||
statusUpgradeWasNotStarted statusCode = 512
|
||||
statusUpgradeDataErrors statusCode = 513
|
||||
statusUpgradeError statusCode = 514
|
||||
statusUpgradeSuccessful statusCode = 515
|
||||
)
|
||||
|
||||
var statusCodes = map[statusCode]string{
|
||||
statusOK: "OK",
|
||||
statusUnknownError: "Unknown error",
|
||||
statusUnsupportedVersion: "Unsupported version",
|
||||
statusRequestNotPermitted: "Request not permitted",
|
||||
statusUserAlreadyLoggedIn: "User already logged in",
|
||||
statusUserIsNotLoggedIn: "User is not logged in",
|
||||
statusUsernameOrPasswordIsIncorrect: "Username or password is incorrect",
|
||||
statusUserDoesNotHaveNecessaryPermissions: "User does not have necessary permissions",
|
||||
statusPasswordIsIncorrect: "Password is incorrect",
|
||||
statusStartOfUpgrade: "Start of upgrade",
|
||||
statusUpgradeWasNotStarted: "Upgrade was not started",
|
||||
statusUpgradeDataErrors: "Upgrade data errors",
|
||||
statusUpgradeError: "Upgrade error",
|
||||
statusUpgradeSuccessful: "Upgrade successful",
|
||||
}
|
||||
|
||||
var requestCodes = map[requestCode]string{
|
||||
codeOPMonitor: "OPMonitor",
|
||||
codeOPTimeSetting: "OPTimeSetting",
|
||||
}
|
||||
|
||||
type MetaInfo struct {
|
||||
Width int
|
||||
Height int
|
||||
Datetime time.Time
|
||||
FPS int
|
||||
Frame string
|
||||
Type string
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
Data []byte
|
||||
Meta MetaInfo
|
||||
}
|
||||
|
||||
//sofiaHash func
|
||||
func sofiaHash(password string) string {
|
||||
digest := md5.Sum([]byte(password))
|
||||
hash := make([]byte, 0, 8)
|
||||
for i := 1; i < len(digest); i += 2 {
|
||||
sum := int(digest[i-1]) + int(digest[i])
|
||||
hash = append(hash, alnum[sum%len(alnum)])
|
||||
}
|
||||
return string(hash)
|
||||
}
|
||||
|
||||
//parseMediaType func
|
||||
func parseMediaType(dataType uint32, mediaCode byte) string {
|
||||
switch dataType {
|
||||
case 0x1FC, 0x1FD:
|
||||
switch mediaCode {
|
||||
case 1:
|
||||
return "MPEG4"
|
||||
case 2:
|
||||
return "H264"
|
||||
case 3:
|
||||
return "H265"
|
||||
}
|
||||
case 0x1F9:
|
||||
if mediaCode == 1 || mediaCode == 6 {
|
||||
return "info"
|
||||
}
|
||||
case 0x1FA:
|
||||
if mediaCode == 0xE {
|
||||
return "PCM_ALAW"
|
||||
}
|
||||
case 0x1FE:
|
||||
if mediaCode == 0 {
|
||||
return "JPEG"
|
||||
}
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
return "unexpected"
|
||||
}
|
||||
|
||||
//parseDatetime func
|
||||
func parseDatetime(value uint32) time.Time {
|
||||
second := int(value & 0x3F)
|
||||
minute := int((value & 0xFC0) >> 6)
|
||||
hour := int((value & 0x1F000) >> 12)
|
||||
day := int((value & 0x3E0000) >> 17)
|
||||
month := int((value & 0x3C00000) >> 22)
|
||||
year := int(((value & 0xFC000000) >> 26) + 2000)
|
||||
|
||||
return time.Date(year, time.Month(month), day, hour, minute, second, 0, time.UTC)
|
||||
}
|
||||
|
||||
//binSize func
|
||||
func binSize(val int) []byte {
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, uint32(val))
|
||||
return buf
|
||||
}
|
@ -3820,3 +3820,103 @@ func (self *TrackFragDecodeTime) Unmarshal(b []byte, offset int) (n int, err err
|
||||
func (self TrackFragDecodeTime) Children() (r []Atom) {
|
||||
return
|
||||
}
|
||||
|
||||
const FTYP = Tag(0x66747970)
|
||||
|
||||
type FileType struct {
|
||||
MajorBrand uint32
|
||||
MinorVersion uint32
|
||||
CompatibleBrands []uint32
|
||||
AtomPos
|
||||
}
|
||||
|
||||
func (t FileType) Tag() Tag {
|
||||
return FTYP
|
||||
}
|
||||
|
||||
func (f FileType) Marshal(b []byte) (n int) {
|
||||
l := 16 + 4*len(f.CompatibleBrands)
|
||||
pio.PutU32BE(b, uint32(l))
|
||||
pio.PutU32BE(b[4:], uint32(FTYP))
|
||||
pio.PutU32BE(b[8:], f.MajorBrand)
|
||||
pio.PutU32BE(b[12:], f.MinorVersion)
|
||||
for i, v := range f.CompatibleBrands {
|
||||
pio.PutU32BE(b[16+4*i:], v)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (f FileType) Len() int {
|
||||
return 16 + 4*len(f.CompatibleBrands)
|
||||
}
|
||||
|
||||
func (f *FileType) Unmarshal(b []byte, offset int) (n int, err error) {
|
||||
f.AtomPos.setPos(offset, len(b))
|
||||
n = 8
|
||||
if len(b) < n+8 {
|
||||
return 0, parseErr("MajorBrand", offset+n, nil)
|
||||
}
|
||||
f.MajorBrand = pio.U32BE(b[n:])
|
||||
n += 4
|
||||
f.MinorVersion = pio.U32BE(b[n:])
|
||||
n += 4
|
||||
for n < len(b)-3 {
|
||||
f.CompatibleBrands = append(f.CompatibleBrands, pio.U32BE(b[n:]))
|
||||
n += 4
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f FileType) Children() []Atom {
|
||||
return nil
|
||||
}
|
||||
|
||||
const STYP = Tag(0x73747970)
|
||||
|
||||
type SegmentType struct {
|
||||
MajorBrand uint32
|
||||
MinorVersion uint32
|
||||
CompatibleBrands []uint32
|
||||
AtomPos
|
||||
}
|
||||
|
||||
func (t SegmentType) Tag() Tag {
|
||||
return STYP
|
||||
}
|
||||
|
||||
func (f SegmentType) Marshal(b []byte) (n int) {
|
||||
l := 16 + 4*len(f.CompatibleBrands)
|
||||
pio.PutU32BE(b, uint32(l))
|
||||
pio.PutU32BE(b[4:], uint32(STYP))
|
||||
pio.PutU32BE(b[8:], f.MajorBrand)
|
||||
pio.PutU32BE(b[12:], f.MinorVersion)
|
||||
for i, v := range f.CompatibleBrands {
|
||||
pio.PutU32BE(b[16+4*i:], v)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (f SegmentType) Len() int {
|
||||
return 16 + 4*len(f.CompatibleBrands)
|
||||
}
|
||||
|
||||
func (f *SegmentType) Unmarshal(b []byte, offset int) (n int, err error) {
|
||||
f.AtomPos.setPos(offset, len(b))
|
||||
n = 8
|
||||
if len(b) < n+8 {
|
||||
return 0, parseErr("MajorBrand", offset+n, nil)
|
||||
}
|
||||
f.MajorBrand = pio.U32BE(b[n:])
|
||||
n += 4
|
||||
f.MinorVersion = pio.U32BE(b[n:])
|
||||
n += 4
|
||||
for n < len(b)-3 {
|
||||
f.CompatibleBrands = append(f.CompatibleBrands, pio.U32BE(b[n:]))
|
||||
n += 4
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f SegmentType) Children() []Atom {
|
||||
return nil
|
||||
}
|
||||
|
@ -3820,3 +3820,103 @@ func (self *TrackFragDecodeTime) Unmarshal(b []byte, offset int) (n int, err err
|
||||
func (self TrackFragDecodeTime) Children() (r []Atom) {
|
||||
return
|
||||
}
|
||||
|
||||
const FTYP = Tag(0x66747970)
|
||||
|
||||
type FileType struct {
|
||||
MajorBrand uint32
|
||||
MinorVersion uint32
|
||||
CompatibleBrands []uint32
|
||||
AtomPos
|
||||
}
|
||||
|
||||
func (t FileType) Tag() Tag {
|
||||
return FTYP
|
||||
}
|
||||
|
||||
func (f FileType) Marshal(b []byte) (n int) {
|
||||
l := 16 + 4*len(f.CompatibleBrands)
|
||||
pio.PutU32BE(b, uint32(l))
|
||||
pio.PutU32BE(b[4:], uint32(FTYP))
|
||||
pio.PutU32BE(b[8:], f.MajorBrand)
|
||||
pio.PutU32BE(b[12:], f.MinorVersion)
|
||||
for i, v := range f.CompatibleBrands {
|
||||
pio.PutU32BE(b[16+4*i:], v)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (f FileType) Len() int {
|
||||
return 16 + 4*len(f.CompatibleBrands)
|
||||
}
|
||||
|
||||
func (f *FileType) Unmarshal(b []byte, offset int) (n int, err error) {
|
||||
f.AtomPos.setPos(offset, len(b))
|
||||
n = 8
|
||||
if len(b) < n+8 {
|
||||
return 0, parseErr("MajorBrand", offset+n, nil)
|
||||
}
|
||||
f.MajorBrand = pio.U32BE(b[n:])
|
||||
n += 4
|
||||
f.MinorVersion = pio.U32BE(b[n:])
|
||||
n += 4
|
||||
for n < len(b)-3 {
|
||||
f.CompatibleBrands = append(f.CompatibleBrands, pio.U32BE(b[n:]))
|
||||
n += 4
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f FileType) Children() []Atom {
|
||||
return nil
|
||||
}
|
||||
|
||||
const STYP = Tag(0x73747970)
|
||||
|
||||
type SegmentType struct {
|
||||
MajorBrand uint32
|
||||
MinorVersion uint32
|
||||
CompatibleBrands []uint32
|
||||
AtomPos
|
||||
}
|
||||
|
||||
func (t SegmentType) Tag() Tag {
|
||||
return STYP
|
||||
}
|
||||
|
||||
func (f SegmentType) Marshal(b []byte) (n int) {
|
||||
l := 16 + 4*len(f.CompatibleBrands)
|
||||
pio.PutU32BE(b, uint32(l))
|
||||
pio.PutU32BE(b[4:], uint32(STYP))
|
||||
pio.PutU32BE(b[8:], f.MajorBrand)
|
||||
pio.PutU32BE(b[12:], f.MinorVersion)
|
||||
for i, v := range f.CompatibleBrands {
|
||||
pio.PutU32BE(b[16+4*i:], v)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (f SegmentType) Len() int {
|
||||
return 16 + 4*len(f.CompatibleBrands)
|
||||
}
|
||||
|
||||
func (f *SegmentType) Unmarshal(b []byte, offset int) (n int, err error) {
|
||||
f.AtomPos.setPos(offset, len(b))
|
||||
n = 8
|
||||
if len(b) < n+8 {
|
||||
return 0, parseErr("MajorBrand", offset+n, nil)
|
||||
}
|
||||
f.MajorBrand = pio.U32BE(b[n:])
|
||||
n += 4
|
||||
f.MinorVersion = pio.U32BE(b[n:])
|
||||
n += 4
|
||||
for n < len(b)-3 {
|
||||
f.CompatibleBrands = append(f.CompatibleBrands, pio.U32BE(b[n:]))
|
||||
n += 4
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f SegmentType) Children() []Atom {
|
||||
return nil
|
||||
}
|
||||
|
@ -871,6 +871,8 @@ func (client *RTSPClient) Println(v ...interface{}) {
|
||||
log.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
//binSize
|
||||
func binSize(val int) []byte {
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, uint32(val))
|
||||
|
@ -2,9 +2,10 @@ package tsio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/deepch/vdk/utils/bits/pio"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/deepch/vdk/utils/bits/pio"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -513,6 +514,7 @@ func NewTSWriter(pid uint16) *TSWriter {
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
//TSHeader func
|
||||
type TSHeader struct {
|
||||
PID uint
|
||||
@ -524,6 +526,7 @@ type TSHeader struct {
|
||||
RandomAccessIndicator bool
|
||||
HeaderLength uint
|
||||
}
|
||||
|
||||
//WriteTSHeader func
|
||||
func WriteTSHeader(w io.Writer, element TSHeader, dataLength int) (written int, err error) {
|
||||
var flags, extFlags uint
|
||||
@ -614,15 +617,6 @@ func (self *TSWriter) WritePackets(w io.Writer, datav [][]byte, pcr time.Duratio
|
||||
self.tshdr[3] = byte(self.ContinuityCounter)&0xf | 0x30
|
||||
self.tshdr[5] = 0 // flags
|
||||
hdrlen := 6
|
||||
//pid := uint16((self.tshdr[1]&0x1f))<<8 | uint16(self.tshdr[2])
|
||||
//if pid != 256 {
|
||||
//self.tshdr[3] = 0x01
|
||||
|
||||
//self.tshdr[3] = 0x47
|
||||
//self.tshdr[4]
|
||||
// log.Println(self.tshdr[:5])
|
||||
// log.Println("pid", pid,self.tshdr[3] )
|
||||
//}
|
||||
self.ContinuityCounter++
|
||||
|
||||
if writepos == 0 {
|
||||
@ -716,9 +710,10 @@ func WriteUInt64(w io.Writer, val uint64, n int) (err error) {
|
||||
func WriteUInt(w io.Writer, val uint, n int) (err error) {
|
||||
return WriteUInt64(w, uint64(val), n)
|
||||
}
|
||||
|
||||
//PCRToUInt func
|
||||
func PCRToUInt(pcr uint64) uint64 {
|
||||
base := pcr / 300
|
||||
ext := pcr % 300
|
||||
return base<<15 | 0x3f<<9 | ext
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user