client.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package isapi
  2. import (
  3. "errors"
  4. "io"
  5. "net"
  6. "net/http"
  7. "net/url"
  8. "github.com/AlexxIT/go2rtc/pkg/core"
  9. "github.com/AlexxIT/go2rtc/pkg/tcp"
  10. )
  11. // Deprecated: should be rewritten to core.Connection
  12. type Client struct {
  13. core.Listener
  14. url string
  15. channel string
  16. conn net.Conn
  17. medias []*core.Media
  18. sender *core.Sender
  19. send int
  20. }
  21. func Dial(rawURL string) (*Client, error) {
  22. // check if url is valid url
  23. u, err := url.Parse(rawURL)
  24. if err != nil {
  25. return nil, err
  26. }
  27. u.Scheme = "http"
  28. u.Path = ""
  29. client := &Client{url: u.String()}
  30. if err = client.Dial(); err != nil {
  31. return nil, err
  32. }
  33. return client, err
  34. }
  35. func (c *Client) Dial() (err error) {
  36. link := c.url + "/ISAPI/System/TwoWayAudio/channels"
  37. req, err := http.NewRequest("GET", link, nil)
  38. if err != nil {
  39. return err
  40. }
  41. res, err := tcp.Do(req)
  42. if err != nil {
  43. return
  44. }
  45. if res.StatusCode != http.StatusOK {
  46. tcp.Close(res)
  47. return errors.New(res.Status)
  48. }
  49. b, err := io.ReadAll(res.Body)
  50. if err != nil {
  51. return err
  52. }
  53. xml := string(b)
  54. codec := core.Between(xml, `<audioCompressionType>`, `<`)
  55. switch codec {
  56. case "G.711ulaw":
  57. codec = core.CodecPCMU
  58. case "G.711alaw":
  59. codec = core.CodecPCMA
  60. default:
  61. return nil
  62. }
  63. c.channel = core.Between(xml, `<id>`, `<`)
  64. media := &core.Media{
  65. Kind: core.KindAudio,
  66. Direction: core.DirectionSendonly,
  67. Codecs: []*core.Codec{
  68. {Name: codec, ClockRate: 8000},
  69. },
  70. }
  71. c.medias = append(c.medias, media)
  72. return nil
  73. }
  74. func (c *Client) Open() (err error) {
  75. // Hikvision ISAPI may not accept a new open request if the previous one was not closed (e.g.
  76. // using the test button on-camera or via curl command) but a close request can be sent even if
  77. // the audio is already closed. So, we send a close request first and then open it again. Seems
  78. // janky but it works.
  79. if err = c.Close(); err != nil {
  80. return err
  81. }
  82. link := c.url + "/ISAPI/System/TwoWayAudio/channels/" + c.channel
  83. req, err := http.NewRequest("PUT", link+"/open", nil)
  84. if err != nil {
  85. return err
  86. }
  87. res, err := tcp.Do(req)
  88. if err != nil {
  89. return
  90. }
  91. tcp.Close(res)
  92. ctx, pconn := tcp.WithConn()
  93. req, err = http.NewRequestWithContext(ctx, "PUT", link+"/audioData", nil)
  94. if err != nil {
  95. return err
  96. }
  97. req.Header.Set("Content-Type", "application/octet-stream")
  98. req.Header.Set("Content-Length", "0")
  99. res, err = tcp.Do(req)
  100. if err != nil {
  101. return err
  102. }
  103. c.conn = *pconn
  104. // just block until c.conn closed
  105. b := make([]byte, 1)
  106. _, _ = c.conn.Read(b)
  107. tcp.Close(res)
  108. return nil
  109. }
  110. func (c *Client) Close() (err error) {
  111. link := c.url + "/ISAPI/System/TwoWayAudio/channels/" + c.channel
  112. req, err := http.NewRequest("PUT", link+"/close", nil)
  113. if err != nil {
  114. return err
  115. }
  116. res, err := tcp.Do(req)
  117. if err != nil {
  118. return err
  119. }
  120. tcp.Close(res)
  121. return nil
  122. }
  123. //type XMLChannels struct {
  124. // Channels []Channel `xml:"TwoWayAudioChannel"`
  125. //}
  126. //type Channel struct {
  127. // ID string `xml:"id"`
  128. // Enabled string `xml:"enabled"`
  129. // Codec string `xml:"audioCompressionType"`
  130. //}