feat: 收口视频访问与摄像头密钥输出
This commit is contained in:
@@ -86,7 +86,11 @@ func listCameras(db *gorm.DB, media *service.MediaService) gin.HandlerFunc {
|
||||
slog.Warn("同步 WVP 设备信息失败,保留 DB 状态", "error", err)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, cameras)
|
||||
publicCameras := make([]CameraPublic, 0, len(cameras))
|
||||
for i := range cameras {
|
||||
publicCameras = append(publicCameras, toCameraPublic(cameras[i]))
|
||||
}
|
||||
c.JSON(http.StatusOK, publicCameras)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,18 +103,19 @@ func getCamera(db *gorm.DB) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "camera not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, camera)
|
||||
c.JSON(http.StatusOK, toCameraPublic(camera))
|
||||
}
|
||||
}
|
||||
|
||||
// createCamera 新建摄像头
|
||||
func createCamera(db *gorm.DB, media *service.MediaService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var camera model.Camera
|
||||
if err := c.ShouldBindJSON(&camera); err != nil {
|
||||
var input CameraInput
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
camera := input.toModel()
|
||||
camera.ID = 0 // 让数据库自动生成
|
||||
if camera.RoomID == nil {
|
||||
defaultRoom := "1"
|
||||
@@ -137,7 +142,7 @@ func createCamera(db *gorm.DB, media *service.MediaService) gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusCreated, camera)
|
||||
c.JSON(http.StatusCreated, toCameraPublic(camera))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +190,7 @@ func updateCamera(db *gorm.DB, media *service.MediaService) gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, camera)
|
||||
c.JSON(http.StatusOK, toCameraPublic(camera))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +217,12 @@ func deleteCamera(db *gorm.DB, media *service.MediaService) gin.HandlerFunc {
|
||||
// playCamera 播放摄像头实时流(body: {format},调用 media.StartPlay)
|
||||
func playCamera(db *gorm.DB, media *service.MediaService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userID := currentUserID(c)
|
||||
if userID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未提供用户信息"})
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Param("id")
|
||||
var camera model.Camera
|
||||
if db.Where("id = ?", id).First(&camera).Error != nil {
|
||||
@@ -223,71 +234,45 @@ func playCamera(db *gorm.DB, media *service.MediaService) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
var body struct {
|
||||
Format string `json:"format"`
|
||||
token, err := IssueVideoToken(*userID, "camera", strconv.FormatUint(uint64(camera.ID), 10), videoTokenTTL)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "生成播放令牌失败"})
|
||||
return
|
||||
}
|
||||
c.ShouldBindJSON(&body)
|
||||
format := body.Format
|
||||
if format == "" {
|
||||
format = "hls"
|
||||
}
|
||||
|
||||
expiresAt := time.Now().Add(30 * time.Minute).UTC().Format(time.RFC3339)
|
||||
expiresAt := time.Now().Add(videoTokenTTL).UTC().Format(time.RFC3339)
|
||||
streamURL := fmt.Sprintf("/api/v1/video/cameras/%d/live/proxy?videoToken=%s", camera.ID, token)
|
||||
|
||||
// GB28181 摄像头:通过 WVP 媒体服务器播放
|
||||
if camera.GbDeviceID != nil && camera.GbChannelID != nil &&
|
||||
*camera.GbDeviceID != "" && *camera.GbChannelID != "" {
|
||||
result, err := media.StartPlay(*camera.GbDeviceID, *camera.GbChannelID)
|
||||
if err != nil {
|
||||
_, playErr := media.StartPlay(*camera.GbDeviceID, *camera.GbChannelID)
|
||||
if playErr != nil {
|
||||
// StartPlay 失败不等于摄像头离线,可能是 WVP/ZLM 瞬时问题,不修改 is_online
|
||||
slog.Warn("StartPlay 失败", "deviceId", *camera.GbDeviceID, "channelId", *camera.GbChannelID, "error", err)
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "播放失败: " + err.Error()})
|
||||
slog.Warn("StartPlay 失败", "deviceId", *camera.GbDeviceID, "channelId", *camera.GbChannelID, "error", playErr)
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "播放失败: " + playErr.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
url := result.HLS
|
||||
if format == "flv" && result.FLV != "" {
|
||||
url = result.FLV
|
||||
} else if format == "webrtc" && result.WebRtc != "" {
|
||||
url = result.WebRtc
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"cameraId": camera.ID,
|
||||
"gbDeviceId": camera.GbDeviceID,
|
||||
"gbChannelId": camera.GbChannelID,
|
||||
"format": format,
|
||||
"url": url,
|
||||
"format": "flv",
|
||||
"url": streamURL,
|
||||
"expiresAt": expiresAt,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Fallback:使用摄像头自身的流地址
|
||||
var url string
|
||||
switch format {
|
||||
case "flv":
|
||||
if camera.FlvURL != nil {
|
||||
url = *camera.FlvURL
|
||||
}
|
||||
case "webrtc":
|
||||
if camera.WebrtcURL != nil {
|
||||
url = *camera.WebrtcURL
|
||||
}
|
||||
default:
|
||||
if camera.HlsURL != nil {
|
||||
url = *camera.HlsURL
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback 不再返回直连流地址,避免绕过令牌代理。
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"cameraId": camera.ID,
|
||||
"gbDeviceId": nil,
|
||||
"gbChannelId": nil,
|
||||
"format": format,
|
||||
"url": url,
|
||||
"format": "flv",
|
||||
"url": streamURL,
|
||||
"expiresAt": expiresAt,
|
||||
"mock": url == "",
|
||||
"mock": true,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -295,6 +280,12 @@ func playCamera(db *gorm.DB, media *service.MediaService) gin.HandlerFunc {
|
||||
// playbackCamera 查询摄像头的历史录像片段(query: from/to/limit)
|
||||
func playbackCamera(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userID := currentUserID(c)
|
||||
if userID == nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未提供用户信息"})
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Param("id")
|
||||
|
||||
limit := 50
|
||||
@@ -318,7 +309,11 @@ func playbackCamera(db *gorm.DB) gin.HandlerFunc {
|
||||
|
||||
// 为每个片段设置 playbackUrl
|
||||
for i := range clips {
|
||||
url := fmt.Sprintf("/api/v1/video/clips/%d/stream", clips[i].ID)
|
||||
token, err := IssueVideoToken(*userID, "clip", strconv.FormatUint(uint64(clips[i].ID), 10), videoTokenTTL)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
url := fmt.Sprintf("/api/v1/video/clips/%d/stream?videoToken=%s", clips[i].ID, token)
|
||||
clips[i].PlaybackURL = &url
|
||||
}
|
||||
|
||||
@@ -342,7 +337,6 @@ func getWvpConfig(media *service.MediaService) gin.HandlerFunc {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"sipId": sip["id"],
|
||||
"sipDomain": sip["domain"],
|
||||
"sipPassword": sip["password"],
|
||||
"sipPort": sip["port"],
|
||||
"sipShowIp": sip["showIp"],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user