|
|
@@ -10,17 +10,15 @@ import (
|
|
|
"time"
|
|
|
|
|
|
"github.com/go-resty/resty/v2"
|
|
|
- "github.com/sirupsen/logrus"
|
|
|
|
|
|
"lc/common/mqtt"
|
|
|
"lc/common/onvif/profiles/media"
|
|
|
- "lc/common/onvif/soap"
|
|
|
"lc/common/protocol"
|
|
|
"lc/common/util"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
- APPLIVE = "live"
|
|
|
+ AppLive = "live"
|
|
|
UploadSnapshot = "/camera/v1/snapshot"
|
|
|
UploadFlv = "/camera/v1/flv"
|
|
|
UploadPresetSnapshot = "/camera/v1/preset/picture"
|
|
|
@@ -34,9 +32,6 @@ func (o *LcDevice) GetProfiles() error {
|
|
|
m := media.NewMedia(o.client, XAddr)
|
|
|
reply, err := m.GetProfiles(&media.GetProfiles{})
|
|
|
if err != nil {
|
|
|
- if serr, ok := err.(*soap.SOAPFault); ok {
|
|
|
- logrus.Error(serr)
|
|
|
- }
|
|
|
return err
|
|
|
}
|
|
|
for _, v := range reply.Profiles {
|
|
|
@@ -66,11 +61,6 @@ func (o *LcDevice) GetAllStreamUri() error {
|
|
|
for retry := 3; retry > 0; retry-- {
|
|
|
reply, err := m.GetStreamUri(&media.GetStreamUri{ProfileToken: media.ReferenceToken(k)})
|
|
|
if err != nil {
|
|
|
- //soap错误,则退出
|
|
|
- if serr, ok := err.(*soap.SOAPFault); ok {
|
|
|
- logrus.Error(serr)
|
|
|
- break
|
|
|
- }
|
|
|
time.Sleep(3 * time.Second)
|
|
|
} else {
|
|
|
v.VideoRtspUrl = strings.ReplaceAll(string(reply.MediaUri.Uri), "rtsp://", "rtsp://"+o.onvifDev.User+":"+o.onvifDev.Password+"@")
|
|
|
@@ -92,11 +82,6 @@ func (o *LcDevice) GetAllSnapshotUri() error {
|
|
|
for retry := 3; retry > 0; retry-- {
|
|
|
reply, err := m.GetSnapshotUri(&media.GetSnapshotUri{ProfileToken: media.ReferenceToken(k)})
|
|
|
if err != nil {
|
|
|
- //soap错误,则退出
|
|
|
- if serr, ok := err.(*soap.SOAPFault); ok {
|
|
|
- logrus.Error(serr)
|
|
|
- break
|
|
|
- }
|
|
|
//其他错误停3秒重试
|
|
|
time.Sleep(5 * time.Second)
|
|
|
} else {
|
|
|
@@ -112,12 +97,11 @@ func (o *LcDevice) GetAllSnapshotUri() error {
|
|
|
func (o *LcDevice) SnapshotFromRtsp(token string) (string, error) {
|
|
|
RtspUrl := o.GetRtspUrl(token)
|
|
|
if RtspUrl == "" {
|
|
|
- logrus.Errorf("未获取到设备[%s]的RtspUrl,无法截图.", o.onvifDev.Code)
|
|
|
return "", errors.New("找不到RtspUrl")
|
|
|
}
|
|
|
- wdir, _ := os.Getwd()
|
|
|
+ wd, _ := os.Getwd()
|
|
|
proc := &os.ProcAttr{
|
|
|
- Dir: wdir,
|
|
|
+ Dir: wd,
|
|
|
Env: os.Environ(),
|
|
|
Files: []*os.File{
|
|
|
os.Stdin,
|
|
|
@@ -129,7 +113,6 @@ func (o *LcDevice) SnapshotFromRtsp(token string) (string, error) {
|
|
|
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 {
|
|
|
- logrus.Errorf("启动ffmpeg失败:%s", err.Error())
|
|
|
return "", err
|
|
|
} else {
|
|
|
go func() {
|
|
|
@@ -150,37 +133,37 @@ func (o *LcDevice) SnapshotFromRtsp(token string) (string, error) {
|
|
|
}
|
|
|
|
|
|
func (o *LcDevice) GetRtspUrl(token string) string {
|
|
|
- rtspurl := ""
|
|
|
+ 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
|
|
|
+ rtspUrl = v.VideoRtspUrl
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- rtspurl = v.VideoRtspUrl
|
|
|
+ rtspUrl = v.VideoRtspUrl
|
|
|
}
|
|
|
- return rtspurl
|
|
|
+ return rtspUrl
|
|
|
}
|
|
|
|
|
|
func (o *LcDevice) GetSnapshotUrl(token string) string {
|
|
|
- snapshoturl := ""
|
|
|
+ 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
|
|
|
+ snapshotUrl = v.SnapshotUrl
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- snapshoturl = v.SnapshotUrl
|
|
|
+ snapshotUrl = v.SnapshotUrl
|
|
|
}
|
|
|
- return snapshoturl
|
|
|
+ return snapshotUrl
|
|
|
}
|
|
|
|
|
|
func (o *LcDevice) GetProfileToken(token string) string {
|
|
|
@@ -198,58 +181,46 @@ func (o *LcDevice) GetProfileToken(token string) string {
|
|
|
}
|
|
|
|
|
|
func (o *LcDevice) Snapshot(token string) {
|
|
|
- snapshoturl := o.GetSnapshotUrl(token)
|
|
|
- if snapshoturl == "" {
|
|
|
- logrus.Debugf("未获取到设备[%s]的SnapshotUrl,无法截图.", o.onvifDev.Code)
|
|
|
+ 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)
|
|
|
+ resp, err := client.R().Get(snapshotUrl)
|
|
|
if err != nil {
|
|
|
- logrus.Errorf("抓图失败:%s", err.Error())
|
|
|
fileName, err = o.SnapshotFromRtsp(token)
|
|
|
} else {
|
|
|
if resp.StatusCode() != http.StatusOK {
|
|
|
- logrus.Errorf("抓图失败,http返回错误:%s", resp.Status())
|
|
|
fileName, err = o.SnapshotFromRtsp(token)
|
|
|
} else if len(resp.Body()) == 0 {
|
|
|
- logrus.Error("抓图失败,内容为空")
|
|
|
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 {
|
|
|
- logrus.Errorf("存储图片失败:%s", err.Error())
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if fileName != "" && err == nil {
|
|
|
- resp, err := client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadSnapshot)
|
|
|
- if err != nil {
|
|
|
- logrus.Errorf("上传文件失败:%s", err.Error())
|
|
|
- } else {
|
|
|
- if resp.StatusCode() != http.StatusOK {
|
|
|
- logrus.Errorf("上传文件失败,http返回错误:%s", resp.Status())
|
|
|
- }
|
|
|
- }
|
|
|
+ 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 == "" {
|
|
|
+ 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)
|
|
|
+ resp, err := client.R().Get(snapshotUrl)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -273,9 +244,9 @@ func (o *LcDevice) Snapshot2(file string) error {
|
|
|
}
|
|
|
|
|
|
func (o *LcDevice) CreateProcess(rtspurl string) {
|
|
|
- wdir, _ := os.Getwd()
|
|
|
+ dir, _ := os.Getwd()
|
|
|
proc := &os.ProcAttr{
|
|
|
- Dir: wdir,
|
|
|
+ Dir: dir,
|
|
|
Env: os.Environ(),
|
|
|
Files: []*os.File{
|
|
|
os.Stdin,
|
|
|
@@ -283,23 +254,18 @@ func (o *LcDevice) CreateProcess(rtspurl string) {
|
|
|
os.Stderr,
|
|
|
},
|
|
|
}
|
|
|
- args := []string{onvifDevConfig.Ffmpeg, "-fflags", "nobuffer", "-i", rtspurl, "-c", "copy", "-f", "flv", o.onvifDev.RtmpServer + "/" + APPLIVE + "/" + o.onvifDev.Code}
|
|
|
+ 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 {
|
|
|
- logrus.Errorf("启动ffmpeg失败:%s", err.Error())
|
|
|
return
|
|
|
}
|
|
|
o.ffmpeg = process
|
|
|
go o.WatchFfmpeg()
|
|
|
- logrus.Infof("启动ffmpeg成功:pid=%d", o.ffmpeg.Pid)
|
|
|
}
|
|
|
|
|
|
func (o *LcDevice) CleanupProcess() {
|
|
|
if o.ffmpeg != nil {
|
|
|
- logrus.Infof("ffmpeg进程销毁:pid=%d", o.ffmpeg.Pid)
|
|
|
- if err := o.ffmpeg.Kill(); err != nil {
|
|
|
- logrus.Infof("ffmpeg进程%dKill失败:%s", o.ffmpeg.Pid, err.Error())
|
|
|
- }
|
|
|
+ o.ffmpeg.Kill()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -315,10 +281,8 @@ func (o *LcDevice) WatchFfmpeg() {
|
|
|
status <- state
|
|
|
}()
|
|
|
select {
|
|
|
- case s := <-status:
|
|
|
- logrus.Infof("ffmpeg已退出:%s", s.String())
|
|
|
- case err := <-died:
|
|
|
- logrus.Infof("ffmpeg已退出:%s", err.Error())
|
|
|
+ case _ = <-status:
|
|
|
+ case _ = <-died:
|
|
|
}
|
|
|
o.ffmpeg = nil
|
|
|
}
|
|
|
@@ -330,12 +294,11 @@ func (o *LcDevice) RecordToFLV(token string, second int) {
|
|
|
}
|
|
|
RtspUrl := o.GetRtspUrl(token)
|
|
|
if RtspUrl == "" {
|
|
|
- logrus.Debugf("未获取到设备[%s]的RtspUrl,无法录制视频.", o.onvifDev.Code)
|
|
|
return
|
|
|
}
|
|
|
- wdir, _ := os.Getwd()
|
|
|
+ dir, _ := os.Getwd()
|
|
|
proc := &os.ProcAttr{
|
|
|
- Dir: wdir,
|
|
|
+ Dir: dir,
|
|
|
Env: os.Environ(),
|
|
|
Files: []*os.File{
|
|
|
os.Stdin,
|
|
|
@@ -347,7 +310,6 @@ func (o *LcDevice) RecordToFLV(token string, second int) {
|
|
|
args := []string{onvifDevConfig.Ffmpeg, "-i", RtspUrl, "-c", "copy", "-t", strconv.Itoa(second), fileName}
|
|
|
process, err := os.StartProcess(onvifDevConfig.Ffmpeg, args, proc)
|
|
|
if err != nil {
|
|
|
- logrus.Errorf("启动ffmpeg失败:%s", err.Error())
|
|
|
return
|
|
|
} else if process != nil {
|
|
|
go func() {
|
|
|
@@ -362,14 +324,7 @@ func (o *LcDevice) RecordToFLV(token string, second int) {
|
|
|
if FileExist(fileName) {
|
|
|
client := resty.New()
|
|
|
client.SetTimeout(6 * time.Second)
|
|
|
- resp, err := client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadFlv)
|
|
|
- if err != nil {
|
|
|
- logrus.Errorf("上传文件失败:%s", err.Error())
|
|
|
- } else {
|
|
|
- if resp.StatusCode() != http.StatusOK {
|
|
|
- logrus.Errorf("上传文件失败,http返回错误:%s", resp.Status())
|
|
|
- }
|
|
|
- }
|
|
|
+ client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadFlv)
|
|
|
os.Remove(fileName)
|
|
|
}
|
|
|
}
|
|
|
@@ -411,26 +366,22 @@ func (o *LcDevice) HandleTpOnvifVideo(m mqtt.Message) {
|
|
|
|
|
|
//如果已开启GB28181,则不用启动ffmpeg推流
|
|
|
if o.onvifDev.Gb28181 {
|
|
|
- logrus.Debugf("设备[%s]已开启GB28181视频服务,无需ffmpeg推流", o.onvifDev.Code)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if obj.Data.Flag == 1 {
|
|
|
o.observer++
|
|
|
- logrus.Debugf("执行推流一次,当前有%d次引用", o.observer)
|
|
|
if o.ffmpeg != nil {
|
|
|
return
|
|
|
}
|
|
|
- if rtspurl := o.GetRtspUrl(obj.Data.ProfileToken); rtspurl == "" {
|
|
|
- logrus.Errorf("请求设备[%s]的视频流错误,找不到拉流地址.", o.onvifDev.Code)
|
|
|
+ if rtspUrl := o.GetRtspUrl(obj.Data.ProfileToken); rtspUrl == "" {
|
|
|
return
|
|
|
} else {
|
|
|
- o.CreateProcess(rtspurl)
|
|
|
+ o.CreateProcess(rtspUrl)
|
|
|
}
|
|
|
} else if obj.Data.Flag == 2 { //停止推流
|
|
|
if o.observer > 0 {
|
|
|
o.observer--
|
|
|
- logrus.Debugf("停止推流一次,余下%d次引用", o.observer)
|
|
|
if o.observer == 0 {
|
|
|
o.CleanupProcess()
|
|
|
}
|