• 如何在RTSP协议网页无插件直播流媒体视频平台EasyNVR演示系统内做到自定义断流?


    我们在对EasyDSS做功能测试的时候,讲过在EasyDSS演示平台上,为了节省资源占用设置的自动停播问题。基于EasyDSS的成功经验,我们在EasyNVR的官网也做了同样一套机制。

    分析问题

    在EasyNVR的演示平台内设置自动断流机制,限制几分钟后流自动断开,这样客户在浏览的时候就算看了忘了关,系统也会在几分钟就自动断开,耗费流量就会少很多。

    解决问题

    在获取通过直播链接的时候,在直播链接后面添加一个校验的流的字符串。

    func wrapURLWithLiveToken(rawURL string, c *gin.Context) (wrapURL string) {
       wrapURL = rawURL
       demo := utils.Conf().Section("base_config").Key("demo").MustBool(false)
       if !demo {
          return
       }
       if rawURL == "" {
          return
       }
       _url, err := url.Parse(rawURL)
       if err != nil {
          return
       }
       q := _url.Query()
       //token := utils.MD5(sessions.Default(c).ID() + rawURL)
       token := createRandomString(8)
       q.Set("token", token)
       _url.RawQuery = q.Encode()
       wrapURL = _url.String()
       liveTokenCache.SetDefault(token, wrapURL)
       return
    }
     
    func createRandomString(len int) string {
       var container string
       var str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
       b := bytes.NewBufferString(str)
       length := b.Len()
       bigInt := big.NewInt(int64(length))
       for i := 0; i < len; i++ {
          randomInt, _ := rand.Int(rand.Reader, bigInt)
          container += string(str[randomInt.Int64()])
       }
       return container
    }
    

      

    这样,直播链接就会有一个校验参数token。

    针对不同流的特点来进行不同的限制:

    1、ws-flv流

    func WSFlvHandler() gin.HandlerFunc {
       return func(c *gin.Context) {
          demo := utils.Conf().Section("base_config").Key("demo").MustBool(false)
          demoDuration := utils.Conf().Section("base_config").Key("demo_duration").MustInt(180)
          flag := false
          path := c.Param("path")
          if strings.HasSuffix(path, ".flv") {
             target := fmt.Sprintf("127.0.0.1:%v", dss.GetHTTPPort())
             //获取nginx里面的真实流地址
             flvUrl := "http://" + target + path
             upGrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool {
                return true
             }}
             //websocket长连接
             ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
             if err != nil {
                return
             }
             defer func() {
                //fmt.Println("关闭ws-flv长连接")
                ws.Close()
             }()
             if demo {
                go func() {
                   time.Sleep(time.Duration(demoDuration) * time.Second)
                   flag = true
                }()
             }
             //发送http请求,将视频流数据循环写入到websocket
             req, _ := http.NewRequest("GET", flvUrl, nil)
             res, _ := http.DefaultClient.Do(req)
             reader := bufio.NewReader(res.Body)
             defer res.Body.Close()
             //循环遍历
             for {
                line, err := reader.ReadBytes(' ')
                if err != nil {
                   return
                }
                ws.WriteMessage(websocket.BinaryMessage, line)
                if flag {
                   return
                }
             }
          }
          c.Next()
       }
    }
    

      

    至此,ws-flv流就限制成功了。

    2、http-flv流

    //可关闭 Transport
    type ShutDownTransport struct {
       Trans    *http.Transport
       response *http.Response
    }
     
    //覆盖上层Transport
    func (t *ShutDownTransport) RoundTrip(req *http.Request) (*http.Response, error) {
       res, err := t.Trans.RoundTrip(req)
       t.response = res
       return res, err
    }
     
    //实现关闭方法
    func (t *ShutDownTransport) ShutDown(d time.Duration) {
       time.AfterFunc(d, func() {
          res := t.response
          if res != nil {
             if res.Body != nil {
                res.Body.Close()
             }
          }
       })
    }
     
    // FlvHandler flv request handler
    func FlvHandler() gin.HandlerFunc {
       return func(c *gin.Context) {
          demo := utils.Conf().Section("base_config").Key("demo").MustBool(false)
          demoDuration := utils.Conf().Section("base_config").Key("demo_duration").MustInt(180)
          path := c.Param("path")
          if strings.HasSuffix(path, ".flv") {
             target := fmt.Sprintf("127.0.0.1:%v", dss.GetHTTPPort())
             director := func(req *http.Request) {
                req.URL.Scheme = "http"
                req.URL.Host = target
                req.URL.Path = path
             }
             modifyRes := func(res *http.Response) (err error) {
                res.Header.Del("Access-Control-Allow-Credentials")
                res.Header.Del("Access-Control-Allow-Headers")
                res.Header.Del("Access-Control-Allow-Methods")
                res.Header.Del("Access-Control-Allow-Origin")
                res.Header.Del("Vary")
                res.Header.Del("Server")
                return
             }
             transport := &ShutDownTransport{
                Trans: &http.Transport{
                   Proxy: http.ProxyFromEnvironment,
                   DialContext: (&net.Dialer{
                      Timeout:   30 * time.Second,
                      KeepAlive: 30 * time.Second,
                   }).DialContext,
                   ForceAttemptHTTP2:     true,
                   MaxIdleConns:          100,
                   IdleConnTimeout:       90 * time.Second,
                   TLSHandshakeTimeout:   10 * time.Second,
                   ExpectContinueTimeout: 1 * time.Second,
                   ResponseHeaderTimeout: 10 * time.Second,
                },
             }
             if demo {
                transport.ShutDown(time.Duration(demoDuration) * time.Second)
             }
             proxy := &httputil.ReverseProxy{
                Director:       director,
                Transport:      transport,
                ModifyResponse: modifyRes,
             }
             defer func() {
                if p := recover(); p != nil {
                   log.Println(p)
                }
             }()
             proxy.ServeHTTP(c.Writer, c.Request)
             return
          }
          c.Next()
       }
    }
    

      

    至此,http-flv限流就完成了。

    3、hls流

    因为hls流和ws-flv、http-flv流不同,前端会一直请求这个链接,所以就不用向上面一样限流。

    // 检查断流
    func checkTime() gin.HandlerFunc {
      return func(c *gin.Context) {
         path := c.Param("path")
         demo := utils.Conf().Section("base_config").Key("demo").MustBool(false)
         isTS := strings.HasSuffix(path, ".ts")
         if demo && !isTS {
            token := c.Query("token")
            if token == "" {
               c.IndentedJSON(401, "Token Not Found")
               return
            }
            if _, ok := liveTokenCache.Get(token); !ok {
               c.IndentedJSON(401, "Invalid Token")
               return
            }
         }
         c.Next()
      }
    }
    

      

    因为播放器又断流自动重连机制,所以在请求的流链接时先要判断一下,请求的这个流地址是不是系统缓存中,不在系统缓存中就不让播放流了。

    只要有演示平台需求的项目都可以通过该调用方法节省公网的浏览和带宽,大家可以参考《EasyGBS平台如何开启“演示”模式》一文了解一下演示平台的机制和作用。

     
  • 相关阅读:
    sqlhelper中事务的简单用法(初学者)
    sqlhelper中事务的简单用法(初学者)
    sqlhelper中事务的简单用法(初学者)
    【】SQlServer数据库生成简单的说明文档小工具(附源码)
    【】SQlServer数据库生成简单的说明文档小工具(附源码)
    一种M2M业务的架构及实现M2M业务的方法
    【】SQlServer数据库生成简单的说明文档小工具(附源码)
    程序员搜索技巧
    通用的Sql存储过程
    python安装过程的一些问题解决方案
  • 原文地址:https://www.cnblogs.com/EasyNVR/p/13739475.html
Copyright © 2020-2023  润新知