test dvrip
This commit is contained in:
		
							
								
								
									
										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 | ||||
| } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Andrey Semochkin
					Andrey Semochkin