| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- package main
- import (
- "errors"
- "io/ioutil"
- "net/http"
- "os"
- "strconv"
- "strings"
- "time"
- "github.com/go-resty/resty/v2"
- "lc/common/mqtt"
- "lc/common/onvif/profiles/media"
- "lc/common/protocol"
- "lc/common/util"
- )
- var (
- AppLive = "live"
- UploadSnapshot = "/camera/v1/snapshot"
- UploadFlv = "/camera/v1/flv"
- UploadPresetSnapshot = "/camera/v1/preset/picture"
- )
- func (o *LcDevice) GetProfiles() error {
- XAddr, ok := o.endpoints["media"]
- if !ok {
- return errors.New("不支持media")
- }
- m := media.NewMedia(o.client, XAddr)
- reply, err := m.GetProfiles(&media.GetProfiles{})
- if err != nil {
- return err
- }
- for _, v := range reply.Profiles {
- vt := VideoToken{
- Token: string(v.Token),
- Name: string(v.Name),
- VideoEncoding: string(v.VideoEncoderConfiguration.Encoding),
- VideoResolution: Resolution{
- Width: v.VideoEncoderConfiguration.Resolution.Width,
- Height: v.VideoEncoderConfiguration.Resolution.Height,
- },
- VideoFrameRateLimit: v.VideoEncoderConfiguration.RateControl.FrameRateLimit,
- VideoBitrateLimit: v.VideoEncoderConfiguration.RateControl.BitrateLimit,
- }
- o.tokens[vt.Token] = &vt
- }
- return nil
- }
- func (o *LcDevice) GetAllStreamUri() error {
- XAddr, ok := o.endpoints["media"]
- if !ok {
- return errors.New("不支持media")
- }
- m := media.NewMedia(o.client, XAddr)
- for k, v := range o.tokens {
- for retry := 3; retry > 0; retry-- {
- reply, err := m.GetStreamUri(&media.GetStreamUri{ProfileToken: media.ReferenceToken(k)})
- if err != nil {
- time.Sleep(3 * time.Second)
- } else {
- v.VideoRtspUrl = strings.ReplaceAll(string(reply.MediaUri.Uri), "rtsp://", "rtsp://"+o.onvifDev.User+":"+o.onvifDev.Password+"@")
- break
- }
- }
- }
- return nil
- }
- func (o *LcDevice) GetAllSnapshotUri() error {
- XAddr, ok := o.endpoints["media"]
- if !ok {
- return errors.New("不支持media")
- }
- m := media.NewMedia(o.client, XAddr)
- for k, v := range o.tokens {
- for retry := 3; retry > 0; retry-- {
- reply, err := m.GetSnapshotUri(&media.GetSnapshotUri{ProfileToken: media.ReferenceToken(k)})
- if err != nil {
- //其他错误停3秒重试
- time.Sleep(5 * time.Second)
- } else {
- v.SnapshotUrl = string(reply.MediaUri.Uri)
- break
- }
- }
- }
- return nil
- }
- // SnapshotFromRtsp 通过RTSP截取一张图片
- func (o *LcDevice) SnapshotFromRtsp(token string) (string, error) {
- RtspUrl := o.GetRtspUrl(token)
- if RtspUrl == "" {
- return "", errors.New("找不到RtspUrl")
- }
- wd, _ := os.Getwd()
- proc := &os.ProcAttr{
- Dir: wd,
- Env: os.Environ(),
- Files: []*os.File{
- os.Stdin,
- os.Stdout,
- os.Stderr,
- },
- }
- fileName := util.GetPath(4) + o.onvifDev.Code + "_" + strconv.FormatInt(util.MlNow().Unix(), 10) + ".jpg"
- args := []string{onvifDevConfig.Ffmpeg, "-i", RtspUrl, "-t", "0.001", "-y", "-f", "mjpeg", "-r", "1", fileName}
- process, err := os.StartProcess(onvifDevConfig.Ffmpeg, args, proc)
- if err != nil {
- return "", err
- } else {
- go func() {
- time.Sleep(6 * time.Second)
- if process != nil {
- process.Signal(os.Kill)
- }
- }()
- _, err = process.Wait()
- process = nil
- }
- //判断是否已经抓图成功
- if FileExist(fileName) {
- return fileName, nil
- }
- err = errors.New("snapshotFromRtsp:抓图失败")
- return "", err
- }
- func (o *LcDevice) GetRtspUrl(token string) string {
- rtspUrl := ""
- if v, ok := o.tokens[token]; !ok {
- max := int32(0)
- for _, v := range o.tokens { //查找分辨率最高的SnapshotUrl
- r := v.VideoResolution.Height * v.VideoResolution.Width
- if r > max {
- max = r
- rtspUrl = v.VideoRtspUrl
- }
- }
- } else {
- rtspUrl = v.VideoRtspUrl
- }
- return rtspUrl
- }
- func (o *LcDevice) GetSnapshotUrl(token string) string {
- snapshotUrl := ""
- max := int32(0)
- if v, ok := o.tokens[token]; !ok {
- for _, v := range o.tokens { //查找分辨率最高的SnapshotUrl
- r := v.VideoResolution.Height * v.VideoResolution.Width
- if r > max {
- max = r
- snapshotUrl = v.SnapshotUrl
- }
- }
- } else {
- snapshotUrl = v.SnapshotUrl
- }
- return snapshotUrl
- }
- func (o *LcDevice) GetProfileToken(token string) string {
- max := int32(0)
- if _, ok := o.tokens[token]; !ok {
- for _, v := range o.tokens { //查找分辨率最高的SnapshotUrl
- r := v.VideoResolution.Height * v.VideoResolution.Width
- if r > max {
- max = r
- token = v.Token
- }
- }
- }
- return token
- }
- func (o *LcDevice) Snapshot(token string) {
- snapshotUrl := o.GetSnapshotUrl(token)
- if snapshotUrl == "" {
- return
- }
- var fileName string
- client := resty.New()
- client.SetTimeout(6 * time.Second)
- client.SetBasicAuth(o.onvifDev.User, o.onvifDev.Password)
- resp, err := client.R().Get(snapshotUrl)
- if err != nil {
- fileName, err = o.SnapshotFromRtsp(token)
- } else {
- if resp.StatusCode() != http.StatusOK {
- fileName, err = o.SnapshotFromRtsp(token)
- } else if len(resp.Body()) == 0 {
- fileName, err = o.SnapshotFromRtsp(token)
- } else {
- fileName = util.GetPath(4) + o.onvifDev.Code + "_" + strconv.FormatInt(util.MlNow().Unix(), 10) + ".jpg"
- if err = ioutil.WriteFile(fileName, resp.Body(), os.ModePerm); err != nil {
- return
- }
- }
- }
- if fileName != "" && err == nil {
- client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadSnapshot)
- os.Remove(fileName)
- }
- }
- func (o *LcDevice) Snapshot2(file string) error {
- snapshotUrl := o.GetSnapshotUrl("")
- if snapshotUrl == "" {
- return errors.New("找不到链接,截图错误")
- }
- var fileName string
- client := resty.New()
- client.SetTimeout(6 * time.Second)
- client.SetBasicAuth(o.onvifDev.User, o.onvifDev.Password)
- resp, err := client.R().Get(snapshotUrl)
- if err != nil {
- return err
- }
- if resp.StatusCode() != http.StatusOK {
- return errors.New("调用截图接口发生错误")
- }
- fileName = util.GetPath(4) + file
- err = ioutil.WriteFile(fileName, resp.Body(), os.ModePerm)
- if err != nil {
- return err
- }
- resp, err = client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadPresetSnapshot)
- os.Remove(fileName)
- if err != nil {
- return err
- }
- if resp.StatusCode() != http.StatusOK {
- return errors.New("截图上传发生错误")
- }
- return nil
- }
- func (o *LcDevice) CreateProcess(rtspurl string) {
- dir, _ := os.Getwd()
- proc := &os.ProcAttr{
- Dir: dir,
- Env: os.Environ(),
- Files: []*os.File{
- os.Stdin,
- os.Stdout,
- os.Stderr,
- },
- }
- args := []string{onvifDevConfig.Ffmpeg, "-fflags", "nobuffer", "-i", rtspurl, "-c", "copy", "-f", "flv", o.onvifDev.RtmpServer + "/" + AppLive + "/" + o.onvifDev.Code}
- process, err := os.StartProcess(onvifDevConfig.Ffmpeg, args, proc)
- if err != nil {
- return
- }
- o.ffmpeg = process
- go o.WatchFfmpeg()
- }
- func (o *LcDevice) CleanupProcess() {
- if o.ffmpeg != nil {
- o.ffmpeg.Kill()
- }
- }
- func (o *LcDevice) WatchFfmpeg() {
- status := make(chan *os.ProcessState)
- died := make(chan error)
- go func() {
- state, err := o.ffmpeg.Wait()
- if err != nil {
- died <- err
- return
- }
- status <- state
- }()
- select {
- case _ = <-status:
- case _ = <-died:
- }
- o.ffmpeg = nil
- }
- func (o *LcDevice) RecordToFLV(token string, second int) {
- //最长录制60秒视频
- if second > 60 {
- second = 60
- }
- RtspUrl := o.GetRtspUrl(token)
- if RtspUrl == "" {
- return
- }
- dir, _ := os.Getwd()
- proc := &os.ProcAttr{
- Dir: dir,
- Env: os.Environ(),
- Files: []*os.File{
- os.Stdin,
- os.Stdout,
- os.Stderr,
- },
- }
- fileName := util.GetPath(4) + o.onvifDev.Code + "_" + strconv.FormatInt(util.MlNow().Unix(), 10) + ".flv"
- args := []string{onvifDevConfig.Ffmpeg, "-i", RtspUrl, "-c", "copy", "-t", strconv.Itoa(second), fileName}
- process, err := os.StartProcess(onvifDevConfig.Ffmpeg, args, proc)
- if err != nil {
- return
- } else if process != nil {
- go func() {
- time.Sleep(time.Duration(second+5) * time.Second)
- if process != nil {
- process.Signal(os.Kill)
- }
- }()
- _, err = process.Wait()
- //判断是否已经抓图成功
- if fileName != "" && err == nil {
- if FileExist(fileName) {
- client := resty.New()
- client.SetTimeout(6 * time.Second)
- client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadFlv)
- os.Remove(fileName)
- }
- }
- }
- }
- // HandleTpOnvifSnapshot 截图
- func (o *LcDevice) HandleTpOnvifSnapshot(m mqtt.Message) {
- var obj protocol.Pack_MediaCommonInfo
- if err := obj.DeCode(m.PayloadString()); err != nil {
- return
- }
- if o.onvifDev.Code != obj.Id {
- return
- }
- var ret protocol.Pack_IPCCommonACK
- if strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, nil); err == nil {
- GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_SNAPSHOT_ACK), strRet, mqtt.AtMostOnce, ToAll)
- }
- go o.Snapshot(obj.Data.ProfileToken)
- }
- // HandleTpOnvifVideo 拉流推流,停止拉流推流
- func (o *LcDevice) HandleTpOnvifVideo(m mqtt.Message) {
- var obj protocol.Pack_MediaCommonInfo
- if err := obj.DeCode(m.PayloadString()); err != nil {
- return
- }
- if o.onvifDev.Code != obj.Id {
- return
- }
- var ret protocol.Pack_IPCCommonACK
- if strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, nil); err == nil {
- GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_VIDEO_ACK), strRet, mqtt.AtMostOnce, ToAll)
- }
- //如果已开启GB28181,则不用启动ffmpeg推流
- if o.onvifDev.Gb28181 {
- return
- }
- if obj.Data.Flag == 1 {
- o.observer++
- if o.ffmpeg != nil {
- return
- }
- if rtspUrl := o.GetRtspUrl(obj.Data.ProfileToken); rtspUrl == "" {
- return
- } else {
- o.CreateProcess(rtspUrl)
- }
- } else if obj.Data.Flag == 2 { //停止推流
- if o.observer > 0 {
- o.observer--
- if o.observer == 0 {
- o.CleanupProcess()
- }
- }
- }
- }
- func (o *LcDevice) HandleTpOnvifRecord(m mqtt.Message) {
- var obj protocol.Pack_MediaCommonInfo
- if err := obj.DeCode(m.PayloadString()); err != nil {
- return
- }
- if o.onvifDev.Code != obj.Id {
- return
- }
- var ret protocol.Pack_IPCCommonACK
- if strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, nil); err == nil {
- GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_RECORD_ACK), strRet, mqtt.AtMostOnce, ToAll)
- }
- go o.RecordToFLV("", 30)
- }
|