• 如何在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平台如何开启“演示”模式》一文了解一下演示平台的机制和作用。

     
  • 相关阅读:
    Java基础之IO框架
    Java基础之RTTI 运行时类型识别
    Java进阶之多线程
    Java进阶之内存管理与垃圾回收
    Java进阶之网络编程
    springcloud的Hystrix turbine断路器聚合监控实现(基于springboot2.02版本)
    springmvc,controller层在接收浏览器url传来的参数带中文乱码问题。
    Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:mav问题
    springboot2.x自定义拦截把static静态文件给拦截的坑
    Eclipse启动发生的错误:An internal error occurred during: "Initializing Java Tooling".
  • 原文地址:https://www.cnblogs.com/EasyNVR/p/13739475.html
Copyright © 2020-2023  润新知