Compare commits

..

8 Commits

Author SHA1 Message Date
dexter
2fac74846a Merge pull request #19 from dwdcth/patch-1
Update message.go
2021-01-24 12:27:43 +08:00
dwdcth
fed0b22513 Update message.go 2021-01-24 12:20:52 +08:00
dwdcth
2326500086 Update message.go
宇视平台设备 xml body 换行修复
2021-01-24 11:10:01 +08:00
langhuihui
c31d10c349 Merge branch 'master' of https://github.com/Monibuca/plugin-gb28181 2021-01-18 23:11:55 +08:00
langhuihui
0d1a15f511 设置AutoUnPublish 2021-01-18 23:11:47 +08:00
dexter
0f0b36dc3d 多路播放invite机制调整 2021-01-18 22:33:14 +08:00
langhuihui
f224a96033 多画面采用低画质流 2021-01-17 21:30:25 +08:00
langhuihui
3b70a3ee69 修复超时判断逻辑 2021-01-13 21:20:34 +08:00
12 changed files with 251 additions and 97 deletions

100
device.go
View File

@@ -1,7 +1,6 @@
package gb28181
import (
"encoding/json"
"fmt"
"strings"
"time"
@@ -39,20 +38,20 @@ type Channel struct {
ChannelEx //自定义属性
}
func (c *Channel) MarshalJSON() ([]byte, error) {
var data = map[string]interface{}{
"DeviceID": c.DeviceID,
"Name": c.Name,
"Manufacturer": c.Manufacturer,
"Address": c.Address,
"Status": c.Status,
"RecordSP": c.RecordSP,
"LiveSP": c.LiveSP,
"Records": c.Records,
"Connected": c.Connected,
}
return json.Marshal(data)
}
// func (c *Channel) MarshalJSON() ([]byte, error) {
// var data = map[string]interface{}{
// "DeviceID": c.DeviceID,
// "Name": c.Name,
// "Manufacturer": c.Manufacturer,
// "Address": c.Address,
// "Status": c.Status,
// "RecordSP": c.RecordSP,
// "LiveSP": c.LiveSP,
// "Records": c.Records,
// "Connected": c.Connected,
// }
// return json.Marshal(data)
// }
// Record 录像
type Record struct {
@@ -205,7 +204,52 @@ func (d *Device) Control(channelIndex int, PTZCmd string) int {
requestMsg.ContentLength = len(requestMsg.Body)
return d.SendMessage(requestMsg).Code
}
func (d *Device) Invite(channelIndex int, start, end string) int {
/*
f字段 f = v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
各项具体含义:
v后续参数为视频的参数各参数间以 “/”分割;
编码格式:十进制整数字符串表示
1 MPEG-4 2 H.264 3 SVAC 4 3GP
分辨率:十进制整数字符串表示
1 QCIF 2 CIF 3 4CIF 4 D1 5 720P 6 1080P/I
帧率:十进制整数字符串表示 099
码率类型:十进制整数字符串表示
1 固定码率CBR 2 可变码率VBR
码率大小:十进制整数字符串表示 0100000如 1表示1kbps
a后续参数为音频的参数各参数间以 “/”分割;
编码格式:十进制整数字符串表示
1 G.711 2 G.723.1 3 G.729 4 G.722.1
码率大小:十进制整数字符串
音频编码码率: 1 — 5.3 kbps G.723.1中使用)
2 — 6.3 kbps G.723.1中使用)
3 — 8 kbps G.729中使用)
4 — 16 kbps G.722.1中使用)
5 — 24 kbps G.722.1中使用)
6 — 32 kbps G.722.1中使用)
7 — 48 kbps G.722.1中使用)
8 — 64 kbpsG.711中使用)
采样率:十进制整数字符串表示
1 — 8 kHzG.711/ G.723.1/ G.729中使用)
2—14 kHzG.722.1中使用)
3—16 kHzG.722.1中使用)
4—32 kHzG.722.1中使用)
注1字符串说明
本节中使用的“十进制整数字符串”的含义为“0”“4294967296” 之间的十进制数字字符串。
注2参数分割标识
各参数间以“/”分割,参数间的分割符“/”不能省略;
若两个分割符 “/”间的某参数为空时(即两个分割符 “/”直接将相连时)表示无该参数值;
注3f字段说明
使用f字段时应保证视频和音频参数的结构完整性即在任何时候f字段的结构都应是完整的结构
f = v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
若只有视频时,音频中的各参数项可以不填写,但应保持 “a///”的结构:
f = v/编码格式/分辨率/帧率/码率类型/码率大小a///
若只有音频时也类似处理,视频中的各参数项可以不填写,但应保持 “v/”的结构:
f = v/a/编码格式/码率大小/采样率
f字段中视、音频参数段之间不需空格分割。
可使用f字段中的分辨率参数标识同一设备不同分辨率的码流。
*/
func (d *Device) Invite(channelIndex int, start, end string, f string) int {
channel := d.Channels[channelIndex]
port, publisher := d.publish(channel.GetPublishStreamPath(start))
if port == 0 {
@@ -213,14 +257,34 @@ func (d *Device) Invite(channelIndex int, start, end string) int {
return 304
}
ssrc := "0200000001"
sdpInfo := []string{"v=0", fmt.Sprintf("o=%s 0 0 IN IP4 %s", d.Serial, d.SipIP), "s=Play", "u=" + channel.DeviceID + ":0", "c=IN IP4 " + d.SipIP, fmt.Sprintf("t=%s %s", start, end), fmt.Sprintf("m=video %d RTP/AVP 96 97 98", port), "a=recvonly", "a=rtpmap:96 PS/90000", "a=rtpmap:97 MPEG4/90000", "a=rtpmap:98 H264/90000", "y=" + ssrc}
// size := 1
// fps := 15
// bitrate := 200
// fmt.Sprintf("f=v/2/%d/%d/1/%da///", size, fps, bitrate)
s := "Play"
if start != "0" {
sdpInfo[2] = "s=Playback"
s = "Playback"
publisher.AutoUnPublish = true
channel.RecordSP = publisher.StreamPath
} else {
channel.LiveSP = publisher.StreamPath
}
sdpInfo := []string{
"v=0",
fmt.Sprintf("o=%s 0 0 IN IP4 %s", d.Serial, d.SipIP),
"s=" + s,
"u=" + channel.DeviceID + ":0",
"c=IN IP4 " + d.SipIP,
fmt.Sprintf("t=%s %s", start, end),
fmt.Sprintf("m=video %d RTP/AVP 96 97 98", port),
"a=recvonly",
"a=rtpmap:96 PS/90000",
"a=rtpmap:97 MPEG4/90000",
"a=rtpmap:98 H264/90000",
"y=" + ssrc,
"f=" + f,
}
invite := channel.CreateMessage(sip.INVITE)
invite.ContentType = "application/sdp"
invite.Contact = &sip.Contact{

3
go.sum
View File

@@ -33,6 +33,7 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrU
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0=
@@ -125,6 +126,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
@@ -174,4 +176,5 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

15
main.go
View File

@@ -90,7 +90,7 @@ func run() {
var list []*Device
Devices.Range(func(key, value interface{}) bool {
device := value.(*Device)
if device.UpdateTime.Sub(device.RegisterTime) > time.Duration(config.RegisterValidity)*time.Second {
if time.Since(device.UpdateTime) > time.Duration(config.RegisterValidity)*time.Second {
Devices.Delete(key)
} else {
list = append(list, device)
@@ -121,10 +121,12 @@ func run() {
})
http.HandleFunc("/gb28181/invite", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
id := r.URL.Query().Get("id")
channel, err := strconv.Atoi(r.URL.Query().Get("channel"))
startTime := r.URL.Query().Get("startTime")
endTime := r.URL.Query().Get("endTime")
query := r.URL.Query()
id := query.Get("id")
channel, err := strconv.Atoi(query.Get("channel"))
startTime := query.Get("startTime")
endTime := query.Get("endTime")
f := query.Get("f")
if startTime == "" {
startTime = "0"
}
@@ -135,7 +137,7 @@ func run() {
w.WriteHeader(404)
}
if v, ok := Devices.Load(id); ok {
w.WriteHeader(v.(*Device).Invite(channel, startTime, endTime))
w.WriteHeader(v.(*Device).Invite(channel, startTime, endTime, f))
} else {
w.WriteHeader(404)
}
@@ -223,6 +225,7 @@ func (d *Device) publish(name string) (port int, publisher *rtp.RTP_PS) {
}
}()
publisher.Type = "GB28181"
publisher.AutoUnPublish = true
var conn *net.UDPConn
var err error
rang := int(config.MediaPortMax - config.MediaPortMin)

View File

@@ -219,8 +219,10 @@ func Decode(data []byte) (msg *Message, err error) {
}
headStr := strings.TrimSpace(msgArr[0])
if len(msgArr) > 1 {
msg.Body = strings.TrimSpace(msgArr[1])
if msgArrLen := len(msgArr); msgArrLen > 1 {
for i := 1; i < msgArrLen; i++ {
msg.Body += strings.TrimSpace(msgArr[i])
}
}
headStr = strings.Trim(headStr, CRLF)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
.arrow1[data-v-43b2c727]{grid-column:2;grid-row:1}.arrow2[data-v-43b2c727]{transform:rotate(90deg);grid-column:3;grid-row:2}.arrow3[data-v-43b2c727]{transform:rotate(180deg);grid-column:2;grid-row:3}.arrow4[data-v-43b2c727]{transform:rotate(270deg);grid-column:1;grid-row:2}.arrow5[data-v-43b2c727]{transform:rotate(-45deg);grid-column:1;grid-row:1}.arrow6[data-v-43b2c727]{transform:rotate(45deg);grid-column:3;grid-row:1}.arrow7[data-v-43b2c727]{transform:rotate(-135deg);grid-column:1;grid-row:3}.arrow8[data-v-43b2c727]{transform:rotate(135deg);grid-column:3;grid-row:3}.arrow9[data-v-43b2c727]{grid-column:2;grid-row:2}.container[data-v-43b2c727]{position:relative;height:350px}.control[data-v-43b2c727]{position:absolute;top:20px;right:0;display:grid;grid-template-columns:repeat(3,33.33%);grid-template-rows:repeat(3,33.33%);width:192px;height:192px}.control2[data-v-43b2c727]{top:210px}.control3[data-v-43b2c727]{top:260px}.control4[data-v-43b2c727]{top:310px}.control5[data-v-43b2c727]{top:360px}.control>[data-v-43b2c727]{cursor:pointer;fill:grey;width:50px;height:50px}.control5>[data-v-43b2c727]{margin-right:10px}.control2>[data-v-43b2c727],.control3>[data-v-43b2c727],.control4>[data-v-43b2c727]{width:40px;height:40px}.control>[data-v-43b2c727]:hover,.cycling[data-v-43b2c727]{fill:#0ff}.player-wrap[data-v-3d23233a]{width:100%;height:100%;border-radius:4px;box-shadow:0 0 5px #40d3fc,inset 0 0 5px #40d3fc,0 0 0 1px #40d3fc}.player-wrap video[data-v-3d23233a]{width:100%;height:100%}.container[data-v-1496ea1d]{position:relative;height:500px;background-image:radial-gradient(rgba(197,45,208,.48),rgba(74,23,152,.48),rgba(3,0,19,.48));color:#fff;background-color:#000;overflow:auto}.search[data-v-1496ea1d]{padding:10px 0}.flex-box[data-v-f9042a1a]{display:flex;flex-flow:row wrap;align-content:flex-start}.flex-item[data-v-f9042a1a]{flex:0 0 33.3333%;height:275px;box-sizing:border-box;padding:10px}
.arrow1[data-v-43b2c727]{grid-column:2;grid-row:1}.arrow2[data-v-43b2c727]{transform:rotate(90deg);grid-column:3;grid-row:2}.arrow3[data-v-43b2c727]{transform:rotate(180deg);grid-column:2;grid-row:3}.arrow4[data-v-43b2c727]{transform:rotate(270deg);grid-column:1;grid-row:2}.arrow5[data-v-43b2c727]{transform:rotate(-45deg);grid-column:1;grid-row:1}.arrow6[data-v-43b2c727]{transform:rotate(45deg);grid-column:3;grid-row:1}.arrow7[data-v-43b2c727]{transform:rotate(-135deg);grid-column:1;grid-row:3}.arrow8[data-v-43b2c727]{transform:rotate(135deg);grid-column:3;grid-row:3}.arrow9[data-v-43b2c727]{grid-column:2;grid-row:2}.container[data-v-43b2c727]{position:relative;height:350px}.control[data-v-43b2c727]{position:absolute;top:20px;right:0;display:grid;grid-template-columns:repeat(3,33.33%);grid-template-rows:repeat(3,33.33%);width:192px;height:192px}.control2[data-v-43b2c727]{top:210px}.control3[data-v-43b2c727]{top:260px}.control4[data-v-43b2c727]{top:310px}.control5[data-v-43b2c727]{top:360px}.control>[data-v-43b2c727]{cursor:pointer;fill:grey;width:50px;height:50px}.control5>[data-v-43b2c727]{margin-right:10px}.control2>[data-v-43b2c727],.control3>[data-v-43b2c727],.control4>[data-v-43b2c727]{width:40px;height:40px}.control>[data-v-43b2c727]:hover,.cycling[data-v-43b2c727]{fill:#0ff}.player-wrap[data-v-3d23233a]{width:100%;height:100%;border-radius:4px;box-shadow:0 0 5px #40d3fc,inset 0 0 5px #40d3fc,0 0 0 1px #40d3fc}.player-wrap video[data-v-3d23233a]{width:100%;height:100%}.container[data-v-1496ea1d]{position:relative;height:500px;background-image:radial-gradient(rgba(197,45,208,.48),rgba(74,23,152,.48),rgba(3,0,19,.48));color:#fff;background-color:#000;overflow:auto}.search[data-v-1496ea1d]{padding:10px 0}.flex-box[data-v-6660cc55]{display:flex;flex-flow:row wrap;align-content:flex-start}.flex-item[data-v-6660cc55]{flex:0 0 33.3333%;height:275px;box-sizing:border-box;padding:10px}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -16,11 +16,10 @@
<td>
<mu-button
flat
v-if="item.Connected"
@click="ptz(prop.row.ID, $index, item)"
>云台
</mu-button>
<mu-button
<!-- <mu-button
flat
v-if="item.Connected"
@click="bye(prop.row.ID, $index, item)"
@@ -31,7 +30,7 @@
flat
@click="invite(prop.row.ID, $index, item)"
>连接
</mu-button>
</mu-button> -->
<mu-button
flat
@click="
@@ -60,9 +59,19 @@
<div class="tabpanel" v-if="$parent.titleTabActive === 1">
<div class="flex-box">
<template v-for="(channel, index) in channelShowList">
<div class="flex-item" :key="index" v-if="channel.DeviceID">
<div class="flex-item" :key="index">
<webrtc-player2
:stream-path="'gb28181/' + channel.DeviceID"
@hook:mounted="
invite(
channel.device.ID,
channel.device.Channels.indexOf(channel),
channel,
'v/2/1/15/1/200a///'
)
"
:stream-path="
channel.device.ID + '/' + channel.DeviceID
"
></webrtc-player2>
</div>
</template>
@@ -172,7 +181,9 @@ export default {
this.Devices.forEach((device) => {
const channels = device.Channels || [];
if (channels.length > 0) {
channelList = channelList.concat(channels);
channelList = channelList.concat(
channels.map((x) => ((x.device = device), x))
);
}
if (this.recordSearch.id && this.recordSearch.deviceId) {
const channel = channels.find((i) => {
@@ -191,14 +202,24 @@ export default {
};
this.$once("hook:destroyed", () => listES.close());
},
ptz(id, channel, item) {
async ptz(id, channel, item) {
await this.invite(id, channel, item);
this.context = {
id,
channel,
item,
};
this.previewStreamPath = true;
this.$nextTick(() => this.$refs.player.play(item.LiveSP));
const unwatch = this.$watch(
"previewStreamPath",
(newValue, oldValue) => {
this.bye(id, channel, item);
unwatch();
}
);
this.$nextTick(() =>
this.$refs.player.play(id + "/" + item.DeviceID)
);
},
sendPtz(options) {
const ptzCmd = getPTZCmd(options);
@@ -238,6 +259,7 @@ export default {
});
this.channelShowList = showList;
if (showList.length > 0) {
this.pageInfo.currentPage = page;
}
@@ -254,13 +276,15 @@ export default {
this.handlePageChange(1);
}
},
invite(id, channel, item) {
this.ajax.get("/gb28181/invite", { id, channel }).then((x) => {
item.Connected = true;
});
invite(id, channel, item, f = "") {
return this.ajax
.get("/gb28181/invite", { id, channel, f })
.then((x) => {
item.Connected = true;
});
},
bye(id, channel, item) {
this.ajax.get("/gb28181/bye", { id, channel }).then((x) => {
return this.ajax.get("/gb28181/bye", { id, channel }).then((x) => {
item.Connected = false;
});
},