VideoStream/handler/tool.go

336 lines
9.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"VideoStream/proto"
"VideoStream/service"
"VideoStream/worker"
"fmt"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/gorilla/websocket"
"io"
"log"
"net/http"
"runtime"
"strconv"
"time"
)
var (
upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
// 允许所有来源的连接
return true
},
}
)
func SetUpToolGroup(router *gin.Engine) {
toolGroup := router.Group("/tool")
toolGroup.GET("/video_stream", GetVideoStream)
toolGroup.GET("/video_real_time", GetRealTimeImage)
}
// 跨域访问cross origin resource share
func CorsHandler() gin.HandlerFunc {
return func(context *gin.Context) {
//method := context.Request.Method
context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
context.Header("Access-Control-Allow-Origin", "*") // 设置允许访问所有域
context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
context.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma,token,openid,opentoken")
context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar")
context.Header("Access-Control-Max-Age", "172800")
context.Header("Access-Control-Allow-Credentials", "false")
context.Set("content-type", "application/json") //设置返回格式是json
// if method == "OPTIONS" {
// context.JSON(http.StatusOK, gin.H{
// "code":1,
// "message":"error",
// "data":"request error",
// })
// }
//处理请求
context.Next()
}
}
type videoStreamReq struct {
ID int `json:"id" form:"id"`
Key string `json:"key" form:"key"`
}
func GetVideoStream(c *gin.Context) {
id, _ := c.Get("id")
id1 := id.(int)
deviceIDSTR := c.Query("id")
deviceID, err := strconv.Atoi(deviceIDSTR)
if err != nil {
c.JSON(400, gin.H{"error": "device_id error"})
return
}
key := c.Query("key")
//校验权限
device := service.GetDevice(deviceID, id1)
if device.ID == 0 {
c.JSON(400, gin.H{"error": "device not exist"})
return
}
rKey := worker.GetRedis("video_stream_get_stream_key")
if rKey == "" {
rKey = "123456"
}
//查看id是否存在
index := -1
for _, device_ := range proto.Config.DeviceInfo {
if device_.ID == deviceID {
index = deviceID
break
}
}
if index == -1 {
c.JSON(400, gin.H{"error": "id config not exist"})
return
}
//查看key是否正确
if key != rKey {
c.JSON(400, gin.H{"error": "key error"})
return
}
//查看设备是否在获取
isGetting := worker.GetRedis(fmt.Sprintf("device_%d_is_getting", deviceID))
if isGetting != "true" {
c.JSON(400, gin.H{"error": "device is not getting or not exist"})
log.Printf("stream device_id:%d is not getting or not exist", deviceID)
return
}
//设备流
c.Stream(func(w io.Writer) bool {
var count int
errCount := 0
for {
if errCount > 10 {
log.Printf("stream device:%d errCount > 10", deviceID)
return false
}
buf, cnt := service.GetDeviceCurrentFrameV4(deviceID, count)
if cnt == count || cnt == -1 {
time.Sleep(50 * time.Millisecond)
log.Printf("stream device:%d,cnt =%d,count=%d,errCount=%d", deviceID, cnt, count, errCount)
errCount++
continue
}
count = cnt
_, err = w.Write([]byte("--frame\r\nContent-Type: image/jpeg\r\n\r\n"))
if err != nil {
fmt.Printf("写入头部信息错误: %v\n", err)
buf = nil
return false
}
_, err = w.Write(buf)
if err != nil {
fmt.Printf("写入帧数据错误: %v\n", err)
buf = nil
return false
}
_, err = w.Write([]byte("\r\n"))
if err != nil {
fmt.Printf("写入帧结束标记错误: %v\n", err)
buf = nil
return false
}
time.Sleep(50 * time.Millisecond) // 控制帧率模拟每秒约20帧可按实际调整
buf = nil
}
})
}
// 发送实时视频流
func GetRealTimeImage(c *gin.Context) {
id, _ := c.Get("id")
id1 := id.(int)
deviceId := c.Query("device_id")
deviceIdInt, _ := strconv.Atoi(deviceId)
device := service.GetDevice(deviceIdInt, id1)
if device.ID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 4, "message": "device not found"})
return
}
//查看设备是否在获取
isGetting := worker.GetRedis(fmt.Sprintf("device_%d_is_getting", device.ID))
if isGetting != "true" {
c.JSON(http.StatusOK, gin.H{"code": 4, "message": "device is not getting or not exist"})
log.Printf("device_id:%d is not getting or not exist", deviceIdInt)
return
}
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
log.Printf("connect wss err:%v", err)
return
}
worker.SetRedisWithExpire(strconv.Itoa(int(device.ID))+"_is_play", "1", time.Minute*5)
isGetting = worker.GetRedis(fmt.Sprintf("device_%d_is_getting", device.ID))
if isGetting != "true" {
c.JSON(http.StatusOK, gin.H{"code": 5, "message": "device is not getting or not exist"})
log.Printf("device_id:%d is not getting or not exist", deviceIdInt)
return
}
subscribeAndHandleMessagesV6(ws, deviceIdInt)
}
func subscribeAndHandleMessagesV6(ws *websocket.Conn, deviceId int) {
// 生成唯一连接 uuid
con_id := uuid.New().String()
online_conn_key := "device_" + strconv.Itoa(deviceId) + "_online_conn_ids"
// 加入设备在线连接集合
worker.SetRedisSetAddWithExpire(online_conn_key, con_id, time.Minute*5)
//图片计数器
count := 0
//定时器,发送计数器
tCount := 0
//计算帧率
for {
//从service获取当前帧
c := service.DeviceFrameCount[deviceId]
if c != count {
count = c
buf := service.Device1CurrentFrame.ToBytes()
err2 := ws.WriteMessage(websocket.BinaryMessage, buf)
if err2 != nil {
log.Printf("send message to client err:%v", err2)
worker.SetRedisSetRemove(online_conn_key, con_id)
break
}
buf = nil
c = count
} else {
//每秒发送一次心跳检测
if tCount%10 == 0 {
err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second))
if err != nil {
log.Printf("Connection check failed:%v", err)
worker.SetRedisSetRemove(online_conn_key, con_id)
break
} else {
log.Printf("Connection check success")
}
}
}
time.Sleep(100 * time.Millisecond)
tCount++
}
// 查看是否还有其他连接,没有则设置 is_play 为 0
if worker.IsContainKey(online_conn_key) == false {
worker.SetRedisWithExpire(strconv.Itoa(deviceId)+"_is_play", "1", time.Minute*5)
log.Printf("device_id: %d has set is_play to 0", deviceId)
}
}
func subscribeAndHandleMessagesV5(ws *websocket.Conn, deviceId int) {
// 生成唯一连接 uuid
con_id := uuid.New().String()
online_conn_key := "device_" + strconv.Itoa(deviceId) + "_online_conn_ids"
// 加入设备在线连接集合
worker.SetRedisSetAddWithExpire(online_conn_key, con_id, time.Minute*5)
//图片计数器
count := 0
//定时器,发送计数器
tCount := 0
buf := make([]byte, 1024)
//计算帧率
for {
//从service获取当前帧
c := service.GetDeviceCurrentFrameV5(deviceId, count, buf)
if c != count {
count = c
err2 := ws.WriteMessage(websocket.BinaryMessage, buf)
if err2 != nil {
log.Printf("send message to client err:%v", err2)
worker.SetRedisSetRemove(online_conn_key, con_id)
break
}
c = count
} else {
//每秒发送一次心跳检测
if tCount%10 == 0 {
err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second))
if err != nil {
log.Printf("Connection check failed:%v", err)
worker.SetRedisSetRemove(online_conn_key, con_id)
break
} else {
log.Printf("Connection check success")
}
}
}
time.Sleep(100 * time.Millisecond)
tCount++
}
// 查看是否还有其他连接,没有则设置 is_play 为 0
if worker.IsContainKey(online_conn_key) == false {
worker.SetRedisWithExpire(strconv.Itoa(deviceId)+"_is_play", "1", time.Minute*5)
log.Printf("device_id: %d has set is_play to 0", deviceId)
}
}
func subscribeAndHandleMessagesV3(ws *websocket.Conn, deviceId int) {
// 生成唯一连接 uuid
con_id := uuid.New().String()
online_conn_key := "device_" + strconv.Itoa(deviceId) + "_online_conn_ids"
// 加入设备在线连接集合
worker.SetRedisSetAddWithExpire(online_conn_key, con_id, time.Minute*5)
//图片计数器
count := 0
//定时器,发送计数器
tCount := 0
var buf []byte
//计算帧率
for {
//从service获取当前帧
newBuf, c := service.GetDeviceCurrentFrameV4(deviceId, count)
if c != count {
buf = newBuf
count = c
err2 := ws.WriteMessage(websocket.BinaryMessage, buf)
if err2 != nil {
log.Printf("send message to client err:%v", err2)
worker.SetRedisSetRemove(online_conn_key, con_id)
break
}
c = count
} else {
//每秒发送一次心跳检测
if tCount%10 == 0 {
err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second))
if err != nil {
log.Printf("Connection check failed:%v", err)
worker.SetRedisSetRemove(online_conn_key, con_id)
break
} else {
log.Printf("Connection check success")
}
}
}
time.Sleep(100 * time.Millisecond)
tCount++
}
// 查看是否还有其他连接,没有则设置 is_play 为 0
if worker.IsContainKey(online_conn_key) == false {
worker.SetRedisWithExpire(strconv.Itoa(deviceId)+"_is_play", "1", time.Minute*5)
log.Printf("device_id: %d has set is_play to 0", deviceId)
}
//垃圾回收
runtime.GC()
}