From 6ce491445d1f7bb0484e1590cb6ac5d8306676c6 Mon Sep 17 00:00:00 2001 From: ydajiang Date: Fri, 29 Aug 2025 15:37:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8Dlivegbs=20stream/info?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.go | 153 +++++++++++++++++++++++++++++++++++++++++++++++ stream/config.go | 22 +++++++ 2 files changed, 175 insertions(+) diff --git a/api.go b/api.go index af6b3fd..673a977 100644 --- a/api.go +++ b/api.go @@ -8,6 +8,7 @@ import ( "github.com/gorilla/websocket" "github.com/lkmio/avformat/utils" "github.com/lkmio/lkm/flv" + "github.com/lkmio/lkm/gb28181" "github.com/lkmio/lkm/hls" "github.com/lkmio/lkm/log" "github.com/lkmio/lkm/rtc" @@ -132,6 +133,8 @@ func startApiServer(addr string) { runtime.GC() }) + apiServer.router.HandleFunc("/api/v1/stream/info", apiServer.OnStreamInfo) + apiServer.router.PathPrefix("/web/").Handler(http.StripPrefix("/web/", http.FileServer(http.Dir("./web")))) http.Handle("/", apiServer.router) @@ -497,3 +500,153 @@ func (api *ApiServer) OnSinkClose(v *IDS, w http.ResponseWriter, r *http.Request httpResponseOK(w, nil) } + +func (api *ApiServer) OnStreamInfo(w http.ResponseWriter, r *http.Request) { + id := r.URL.Query().Get("streamid") + source := stream.SourceManager.Find(id) + if source == nil || !source.IsCompleted() || source.IsClosed() { + return + } + + tracks := source.OriginTracks() + if len(tracks) < 1 { + return + } + + var deviceId string + var channelId string + split := strings.Split(id, "/") + if len(split) < 2 { + return + } + + deviceId = split[0] + channelId = split[1] + if len(split[1]) >= 20 { + channelId = split[1][:20] + } + + var transport string + if stream.SourceType28181 == source.GetType() { + if gb28181.SetupUDP != source.(gb28181.GBSource).SetupType() { + transport = "TCP" + } else { + transport = "UDP" + } + } + + var token string + cookie, err := r.Cookie("token") + if err == nil { + token = cookie.Value + } + + urls := stream.GetStreamPlayUrlsMap(id) + liveGBSUrls := make(map[string]string) + for streamName, url := range urls { + url += "?stream_token=" + token + + // 兼容livegbs前端播放webrtc + if streamName == "rtc" { + if strings.HasPrefix(url, "http") { + url = strings.Replace(url, "http", "webrtc", 1) + } else if strings.HasPrefix(url, "https") { + url = strings.Replace(url, "https", "webrtcs", 1) + } + + url += "&wf=livegbs" + } + + liveGBSUrls[streamName] = url + } + + statistics := source.GetBitrateStatistics() + response := struct { + AudioEnable bool `json:"AudioEnable"` + CDN string `json:"CDN"` + CascadeSize int `json:"CascadeSize"` + ChannelID string `json:"ChannelID"` + ChannelName string `json:"ChannelName"` + CloudRecord bool `json:"CloudRecord"` + DecodeSize int `json:"DecodeSize"` + DeviceID string `json:"DeviceID"` + Duration int `json:"Duration"` + FLV string `json:"FLV"` + HLS string `json:"HLS"` + InBitRate int `json:"InBitRate"` + InBytes int `json:"InBytes"` + NumOutputs int `json:"NumOutputs"` + Ondemand bool `json:"Ondemand"` + OutBytes int `json:"OutBytes"` + RTMP string `json:"RTMP"` + RTPCount int `json:"RTPCount"` + RTPLostCount int `json:"RTPLostCount"` + RTPLostRate int `json:"RTPLostRate"` + RTSP string `json:"RTSP"` + RecordStartAt string `json:"RecordStartAt"` + RelaySize int `json:"RelaySize"` + SMSID string `json:"SMSID"` + SnapURL string `json:"SnapURL"` + SourceAudioCodecName string `json:"SourceAudioCodecName"` + SourceAudioSampleRate int `json:"SourceAudioSampleRate"` + SourceVideoCodecName string `json:"SourceVideoCodecName"` + SourceVideoFrameRate int `json:"SourceVideoFrameRate"` + SourceVideoHeight int `json:"SourceVideoHeight"` + SourceVideoWidth int `json:"SourceVideoWidth"` + StartAt string `json:"StartAt"` + StreamID string `json:"StreamID"` + Transport string `json:"Transport"` + VideoFrameCount int `json:"VideoFrameCount"` + WEBRTC string `json:"WEBRTC"` + WS_FLV string `json:"WS_FLV"` + }{ + AudioEnable: true, + CDN: "", + CascadeSize: 0, + ChannelID: channelId, + ChannelName: "", + CloudRecord: false, + DecodeSize: 0, + DeviceID: deviceId, + Duration: int(time.Since(source.CreateTime()).Seconds()), + FLV: liveGBSUrls["flv"], + HLS: liveGBSUrls["hls"], + InBitRate: statistics.PreviousSecond() * 8 / 1024, + InBytes: int(statistics.Total()), + NumOutputs: 0, + Ondemand: true, + OutBytes: 0, + RTMP: liveGBSUrls["rtmp"], + RTPCount: 0, + RTPLostCount: 0, + RTPLostRate: 0, + RTSP: liveGBSUrls["rtsp"], + RecordStartAt: "", + RelaySize: 0, + SMSID: "", + SnapURL: "", + SourceVideoFrameRate: 0, + StartAt: source.CreateTime().Format("2006-01-02 15:04:05"), + StreamID: id, + Transport: transport, + VideoFrameCount: 0, + WEBRTC: liveGBSUrls["rtc"], + WS_FLV: liveGBSUrls["ws_flv"], + } + + for _, track := range tracks { + if utils.AVMediaTypeAudio == track.Stream.MediaType { + response.SourceAudioCodecName = track.Stream.CodecID.String() + response.SourceAudioSampleRate = track.Stream.AudioConfig.SampleRate + } else if utils.AVMediaTypeVideo == track.Stream.MediaType { + response.SourceVideoCodecName = track.Stream.CodecID.String() + // response.SourceVideoFrameRate + if track.Stream.CodecParameters != nil { + response.SourceVideoWidth = track.Stream.CodecParameters.Width() + response.SourceVideoHeight = track.Stream.CodecParameters.Height() + } + } + } + + httpResponseJson(w, &response) +} diff --git a/stream/config.go b/stream/config.go index 4b6abeb..698c835 100644 --- a/stream/config.go +++ b/stream/config.go @@ -220,6 +220,28 @@ func GetStreamPlayUrls(source string) []string { return urls } +func GetStreamPlayUrlsMap(source string) map[string]string { + urls := GetStreamPlayUrls(source) + playUrlMap := make(map[string]string) + for _, url := range urls { + + if strings.HasPrefix(url, "ws") { + playUrlMap["ws_flv"] = url + } else if strings.HasSuffix(url, ".flv") { + playUrlMap["flv"] = url + } else if strings.HasSuffix(url, ".m3u8") { + playUrlMap["hls"] = url + } else if strings.HasSuffix(url, ".rtc") { + playUrlMap["rtc"] = url + } else if strings.HasPrefix(url, "rtmp") { + playUrlMap["rtmp"] = url + } else if strings.HasPrefix(url, "rtsp") { + playUrlMap["rtsp"] = url + } + } + return playUrlMap +} + // DumpStream2File 保存推流到文件, 用4字节帧长分割 func DumpStream2File(sourceType SourceType, conn net.Conn, data []byte) { path := fmt.Sprintf("dump/%s-%s", sourceType.String(), conn.RemoteAddr().String())