ipc_media.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. package main
  2. import (
  3. "errors"
  4. "io/ioutil"
  5. "net/http"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "github.com/go-resty/resty/v2"
  11. "lc/common/mqtt"
  12. "lc/common/onvif/profiles/media"
  13. "lc/common/protocol"
  14. "lc/common/util"
  15. )
  16. var (
  17. AppLive = "live"
  18. UploadSnapshot = "/camera/v1/snapshot"
  19. UploadFlv = "/camera/v1/flv"
  20. UploadPresetSnapshot = "/camera/v1/preset/picture"
  21. )
  22. func (o *LcDevice) GetProfiles() error {
  23. XAddr, ok := o.endpoints["media"]
  24. if !ok {
  25. return errors.New("不支持media")
  26. }
  27. m := media.NewMedia(o.client, XAddr)
  28. reply, err := m.GetProfiles(&media.GetProfiles{})
  29. if err != nil {
  30. return err
  31. }
  32. for _, v := range reply.Profiles {
  33. vt := VideoToken{
  34. Token: string(v.Token),
  35. Name: string(v.Name),
  36. VideoEncoding: string(v.VideoEncoderConfiguration.Encoding),
  37. VideoResolution: Resolution{
  38. Width: v.VideoEncoderConfiguration.Resolution.Width,
  39. Height: v.VideoEncoderConfiguration.Resolution.Height,
  40. },
  41. VideoFrameRateLimit: v.VideoEncoderConfiguration.RateControl.FrameRateLimit,
  42. VideoBitrateLimit: v.VideoEncoderConfiguration.RateControl.BitrateLimit,
  43. }
  44. o.tokens[vt.Token] = &vt
  45. }
  46. return nil
  47. }
  48. func (o *LcDevice) GetAllStreamUri() error {
  49. XAddr, ok := o.endpoints["media"]
  50. if !ok {
  51. return errors.New("不支持media")
  52. }
  53. m := media.NewMedia(o.client, XAddr)
  54. for k, v := range o.tokens {
  55. for retry := 3; retry > 0; retry-- {
  56. reply, err := m.GetStreamUri(&media.GetStreamUri{ProfileToken: media.ReferenceToken(k)})
  57. if err != nil {
  58. time.Sleep(3 * time.Second)
  59. } else {
  60. v.VideoRtspUrl = strings.ReplaceAll(string(reply.MediaUri.Uri), "rtsp://", "rtsp://"+o.onvifDev.User+":"+o.onvifDev.Password+"@")
  61. break
  62. }
  63. }
  64. }
  65. return nil
  66. }
  67. func (o *LcDevice) GetAllSnapshotUri() error {
  68. XAddr, ok := o.endpoints["media"]
  69. if !ok {
  70. return errors.New("不支持media")
  71. }
  72. m := media.NewMedia(o.client, XAddr)
  73. for k, v := range o.tokens {
  74. for retry := 3; retry > 0; retry-- {
  75. reply, err := m.GetSnapshotUri(&media.GetSnapshotUri{ProfileToken: media.ReferenceToken(k)})
  76. if err != nil {
  77. //其他错误停3秒重试
  78. time.Sleep(5 * time.Second)
  79. } else {
  80. v.SnapshotUrl = string(reply.MediaUri.Uri)
  81. break
  82. }
  83. }
  84. }
  85. return nil
  86. }
  87. // SnapshotFromRtsp 通过RTSP截取一张图片
  88. func (o *LcDevice) SnapshotFromRtsp(token string) (string, error) {
  89. RtspUrl := o.GetRtspUrl(token)
  90. if RtspUrl == "" {
  91. return "", errors.New("找不到RtspUrl")
  92. }
  93. wd, _ := os.Getwd()
  94. proc := &os.ProcAttr{
  95. Dir: wd,
  96. Env: os.Environ(),
  97. Files: []*os.File{
  98. os.Stdin,
  99. os.Stdout,
  100. os.Stderr,
  101. },
  102. }
  103. fileName := util.GetPath(4) + o.onvifDev.Code + "_" + strconv.FormatInt(util.MlNow().Unix(), 10) + ".jpg"
  104. args := []string{onvifDevConfig.Ffmpeg, "-i", RtspUrl, "-t", "0.001", "-y", "-f", "mjpeg", "-r", "1", fileName}
  105. process, err := os.StartProcess(onvifDevConfig.Ffmpeg, args, proc)
  106. if err != nil {
  107. return "", err
  108. } else {
  109. go func() {
  110. time.Sleep(6 * time.Second)
  111. if process != nil {
  112. process.Signal(os.Kill)
  113. }
  114. }()
  115. _, err = process.Wait()
  116. process = nil
  117. }
  118. //判断是否已经抓图成功
  119. if FileExist(fileName) {
  120. return fileName, nil
  121. }
  122. err = errors.New("snapshotFromRtsp:抓图失败")
  123. return "", err
  124. }
  125. func (o *LcDevice) GetRtspUrl(token string) string {
  126. rtspUrl := ""
  127. if v, ok := o.tokens[token]; !ok {
  128. max := int32(0)
  129. for _, v := range o.tokens { //查找分辨率最高的SnapshotUrl
  130. r := v.VideoResolution.Height * v.VideoResolution.Width
  131. if r > max {
  132. max = r
  133. rtspUrl = v.VideoRtspUrl
  134. }
  135. }
  136. } else {
  137. rtspUrl = v.VideoRtspUrl
  138. }
  139. return rtspUrl
  140. }
  141. func (o *LcDevice) GetSnapshotUrl(token string) string {
  142. snapshotUrl := ""
  143. max := int32(0)
  144. if v, ok := o.tokens[token]; !ok {
  145. for _, v := range o.tokens { //查找分辨率最高的SnapshotUrl
  146. r := v.VideoResolution.Height * v.VideoResolution.Width
  147. if r > max {
  148. max = r
  149. snapshotUrl = v.SnapshotUrl
  150. }
  151. }
  152. } else {
  153. snapshotUrl = v.SnapshotUrl
  154. }
  155. return snapshotUrl
  156. }
  157. func (o *LcDevice) GetProfileToken(token string) string {
  158. max := int32(0)
  159. if _, ok := o.tokens[token]; !ok {
  160. for _, v := range o.tokens { //查找分辨率最高的SnapshotUrl
  161. r := v.VideoResolution.Height * v.VideoResolution.Width
  162. if r > max {
  163. max = r
  164. token = v.Token
  165. }
  166. }
  167. }
  168. return token
  169. }
  170. func (o *LcDevice) Snapshot(token string) {
  171. snapshotUrl := o.GetSnapshotUrl(token)
  172. if snapshotUrl == "" {
  173. return
  174. }
  175. var fileName string
  176. client := resty.New()
  177. client.SetTimeout(6 * time.Second)
  178. client.SetBasicAuth(o.onvifDev.User, o.onvifDev.Password)
  179. resp, err := client.R().Get(snapshotUrl)
  180. if err != nil {
  181. fileName, err = o.SnapshotFromRtsp(token)
  182. } else {
  183. if resp.StatusCode() != http.StatusOK {
  184. fileName, err = o.SnapshotFromRtsp(token)
  185. } else if len(resp.Body()) == 0 {
  186. fileName, err = o.SnapshotFromRtsp(token)
  187. } else {
  188. fileName = util.GetPath(4) + o.onvifDev.Code + "_" + strconv.FormatInt(util.MlNow().Unix(), 10) + ".jpg"
  189. if err = ioutil.WriteFile(fileName, resp.Body(), os.ModePerm); err != nil {
  190. return
  191. }
  192. }
  193. }
  194. if fileName != "" && err == nil {
  195. client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadSnapshot)
  196. os.Remove(fileName)
  197. }
  198. }
  199. func (o *LcDevice) Snapshot2(file string) error {
  200. snapshotUrl := o.GetSnapshotUrl("")
  201. if snapshotUrl == "" {
  202. return errors.New("找不到链接,截图错误")
  203. }
  204. var fileName string
  205. client := resty.New()
  206. client.SetTimeout(6 * time.Second)
  207. client.SetBasicAuth(o.onvifDev.User, o.onvifDev.Password)
  208. resp, err := client.R().Get(snapshotUrl)
  209. if err != nil {
  210. return err
  211. }
  212. if resp.StatusCode() != http.StatusOK {
  213. return errors.New("调用截图接口发生错误")
  214. }
  215. fileName = util.GetPath(4) + file
  216. err = ioutil.WriteFile(fileName, resp.Body(), os.ModePerm)
  217. if err != nil {
  218. return err
  219. }
  220. resp, err = client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadPresetSnapshot)
  221. os.Remove(fileName)
  222. if err != nil {
  223. return err
  224. }
  225. if resp.StatusCode() != http.StatusOK {
  226. return errors.New("截图上传发生错误")
  227. }
  228. return nil
  229. }
  230. func (o *LcDevice) CreateProcess(rtspurl string) {
  231. dir, _ := os.Getwd()
  232. proc := &os.ProcAttr{
  233. Dir: dir,
  234. Env: os.Environ(),
  235. Files: []*os.File{
  236. os.Stdin,
  237. os.Stdout,
  238. os.Stderr,
  239. },
  240. }
  241. args := []string{onvifDevConfig.Ffmpeg, "-fflags", "nobuffer", "-i", rtspurl, "-c", "copy", "-f", "flv", o.onvifDev.RtmpServer + "/" + AppLive + "/" + o.onvifDev.Code}
  242. process, err := os.StartProcess(onvifDevConfig.Ffmpeg, args, proc)
  243. if err != nil {
  244. return
  245. }
  246. o.ffmpeg = process
  247. go o.WatchFfmpeg()
  248. }
  249. func (o *LcDevice) CleanupProcess() {
  250. if o.ffmpeg != nil {
  251. o.ffmpeg.Kill()
  252. }
  253. }
  254. func (o *LcDevice) WatchFfmpeg() {
  255. status := make(chan *os.ProcessState)
  256. died := make(chan error)
  257. go func() {
  258. state, err := o.ffmpeg.Wait()
  259. if err != nil {
  260. died <- err
  261. return
  262. }
  263. status <- state
  264. }()
  265. select {
  266. case _ = <-status:
  267. case _ = <-died:
  268. }
  269. o.ffmpeg = nil
  270. }
  271. func (o *LcDevice) RecordToFLV(token string, second int) {
  272. //最长录制60秒视频
  273. if second > 60 {
  274. second = 60
  275. }
  276. RtspUrl := o.GetRtspUrl(token)
  277. if RtspUrl == "" {
  278. return
  279. }
  280. dir, _ := os.Getwd()
  281. proc := &os.ProcAttr{
  282. Dir: dir,
  283. Env: os.Environ(),
  284. Files: []*os.File{
  285. os.Stdin,
  286. os.Stdout,
  287. os.Stderr,
  288. },
  289. }
  290. fileName := util.GetPath(4) + o.onvifDev.Code + "_" + strconv.FormatInt(util.MlNow().Unix(), 10) + ".flv"
  291. args := []string{onvifDevConfig.Ffmpeg, "-i", RtspUrl, "-c", "copy", "-t", strconv.Itoa(second), fileName}
  292. process, err := os.StartProcess(onvifDevConfig.Ffmpeg, args, proc)
  293. if err != nil {
  294. return
  295. } else if process != nil {
  296. go func() {
  297. time.Sleep(time.Duration(second+5) * time.Second)
  298. if process != nil {
  299. process.Signal(os.Kill)
  300. }
  301. }()
  302. _, err = process.Wait()
  303. //判断是否已经抓图成功
  304. if fileName != "" && err == nil {
  305. if FileExist(fileName) {
  306. client := resty.New()
  307. client.SetTimeout(6 * time.Second)
  308. client.R().SetFile("file", fileName).Post(o.onvifDev.WebServer + UploadFlv)
  309. os.Remove(fileName)
  310. }
  311. }
  312. }
  313. }
  314. // HandleTpOnvifSnapshot 截图
  315. func (o *LcDevice) HandleTpOnvifSnapshot(m mqtt.Message) {
  316. var obj protocol.Pack_MediaCommonInfo
  317. if err := obj.DeCode(m.PayloadString()); err != nil {
  318. return
  319. }
  320. if o.onvifDev.Code != obj.Id {
  321. return
  322. }
  323. var ret protocol.Pack_IPCCommonACK
  324. if strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, nil); err == nil {
  325. GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_SNAPSHOT_ACK), strRet, mqtt.AtMostOnce, ToAll)
  326. }
  327. go o.Snapshot(obj.Data.ProfileToken)
  328. }
  329. // HandleTpOnvifVideo 拉流推流,停止拉流推流
  330. func (o *LcDevice) HandleTpOnvifVideo(m mqtt.Message) {
  331. var obj protocol.Pack_MediaCommonInfo
  332. if err := obj.DeCode(m.PayloadString()); err != nil {
  333. return
  334. }
  335. if o.onvifDev.Code != obj.Id {
  336. return
  337. }
  338. var ret protocol.Pack_IPCCommonACK
  339. if strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, nil); err == nil {
  340. GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_VIDEO_ACK), strRet, mqtt.AtMostOnce, ToAll)
  341. }
  342. //如果已开启GB28181,则不用启动ffmpeg推流
  343. if o.onvifDev.Gb28181 {
  344. return
  345. }
  346. if obj.Data.Flag == 1 {
  347. o.observer++
  348. if o.ffmpeg != nil {
  349. return
  350. }
  351. if rtspUrl := o.GetRtspUrl(obj.Data.ProfileToken); rtspUrl == "" {
  352. return
  353. } else {
  354. o.CreateProcess(rtspUrl)
  355. }
  356. } else if obj.Data.Flag == 2 { //停止推流
  357. if o.observer > 0 {
  358. o.observer--
  359. if o.observer == 0 {
  360. o.CleanupProcess()
  361. }
  362. }
  363. }
  364. }
  365. func (o *LcDevice) HandleTpOnvifRecord(m mqtt.Message) {
  366. var obj protocol.Pack_MediaCommonInfo
  367. if err := obj.DeCode(m.PayloadString()); err != nil {
  368. return
  369. }
  370. if o.onvifDev.Code != obj.Id {
  371. return
  372. }
  373. var ret protocol.Pack_IPCCommonACK
  374. if strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, nil); err == nil {
  375. GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_RECORD_ACK), strRet, mqtt.AtMostOnce, ToAll)
  376. }
  377. go o.RecordToFLV("", 30)
  378. }