zoukankan      html  css  js  c++  java
  • rtsp流转为fmp4并由WebSocket网关转发,及对应js播放器

    web端是无法直接播放rtsp流的,目前常用的解决方案是如jsmpeg、flv.js等。这些方案都是要推送流到服务端,之后才能在web上播放视频,相对比较麻烦。我采用websocket结合mse的方式,实现了一个websocket网关,及其对应的js播放器,在这里做下说明,具体代码参考github上我的源码。

    这套方案的原理是,ws网关在拉到rtsp流后,取得mime,将其发送给web端,然后将rtsp流转为fmp4格式,以二进制数据格式发给web端;web端用其初始化mse,然后将websocket收到的二进制数据扔给mse,实现视频的播放。

    ws网关有两个关键的问题需要解决,一是封装成fmp4后,输出要到内存而不是文件,二是要能取得mime。如果以网上以回调函数作为ffmpeg输出的例子来写,会发现创建失败。mime对应的是编码类型,需要解析流才能得到,具体怎么解决这两个问题,看看下面的说明。

    创建输出的AVFormatContext的代码:

    1.  
      if (avformat_alloc_output_context2(&Out_FormatContext, NULL, "mp4", NULL) < 0)
    2.  
      return false;
    3.  
      pb_Buf = (uint8_t*)av_malloc(sizeof(uint8_t)*(D_PB_BUF_SIZE));
    4.  
      Out_FormatContext->pb = avio_alloc_context(pb_Buf, D_PB_BUF_SIZE,1,(void*)this,NULL,write_buffer,NULL);
    5.  
      if (Out_FormatContext->pb == NULL)
    6.  
      {
    7.  
      avformat_free_context(Out_FormatContext);
    8.  
      Out_FormatContext = NULL;
    9.  
      sendWSString("fail");
    10.  
      return false;
    11.  
      }
    12.  
      Out_FormatContext->pb->write_flag = 1;
    13.  
      Out_FormatContext->pb->seekable = 1;
    14.  
      Out_FormatContext->flags=AVFMT_FLAG_CUSTOM_IO;
    15.  
      Out_FormatContext->flags |= AVFMT_FLAG_FLUSH_PACKETS;
    16.  
      Out_FormatContext->flags |= AVFMT_NOFILE;
    17.  
      Out_FormatContext->flags |= AVFMT_FLAG_AUTO_BSF;
    18.  
      Out_FormatContext->flags |= AVFMT_FLAG_NOBUFFER;

    这里需要注意的是pb不仅write_flag要设置成1,seekable也要设置成1,seekable这个很容易就忽略了,然而这个如果不是1,那么创建会失败。ffmpeg写数据输出到内存部分,参考avio_alloc_context的回调函数用法。

    获取mime的方法:

    1.  
      static std::string GetMIME(uint8_t* data, int len)
    2.  
      {
    3.  
      int n = 0;
    4.  
      if (data[0] == 0)
    5.  
      {
    6.  
      while (n + 3 < len)
    7.  
      {
    8.  
      if ((data[n] == 0) & (data[n + 1] == 0) & (data[n + 2] == 1))
    9.  
      {
    10.  
      n += 3;
    11.  
      break;
    12.  
      }
    13.  
      else
    14.  
      n++;
    15.  
      }
    16.  
      }
    17.  
      n += 1;
    18.  
      if (n + 3 > len)
    19.  
      return "";
    20.  
      char mime[10] = {0};
    21.  
      sprintf(mime,"%.2x%.2x%.2x",data[n], data[n + 1], data[n + 2]);
    22.  
      return std::string(mime);
    23.  
      }

    mime可以通过spspps取得,ffmpeg在创建AVStream后,264的spspps可以从codecpar->extradata取得,在extradata中跳过264的分隔符后,接下来的第2、3、4个字节就可以拼出264的mime。

    mime的音频部分可以参考https://wiki.multimedia.cx/index.php?title=MPEG-4_Audio,以"mp4a.40.2"举例,mp4a.40表示音频解码器为aac,.2表示AAC LC,对比ffmpeg的定义可以发现,这个值就是codecpar的profile加1.

    另外说明一下movflags,fmp4需要设置成frag_keyframe+empty_moov,这样就是fmp4,我设置的值是frag_keyframe+empty_moov+omit_tfhd_offset+faststart+separate_moof+disable_chpl+default_base_moof+dash,其中omit_tfhd_offset这个设置是针对chrome浏览器的,如果不设置的该项,chrome上是会播放失败的,faststart是为了将moov移动到mdat前面,separate_moof如果不加上,chrome处理音频时会有问题,尚未找出是视频源问题还是共性问题。

    js播放器这边很简单,需要说明一下的是收到ws数据后的处理

    1.  
      if(typeof(evt.data)=="string") //服务器传过来的可能是字符串,判断是不是{
    2.  
      var str = evt.data;
    3.  
      console.log(str);
    4.  
      var strs = new Array(); //定义一数组
    5.  
      strs = str.split(":"); //字符分割
    6.  
      if (strs[0] == "open")
    7.  
      {
    8.  
      var mimestr = strs[1];
    9.  
      this.playurl(mimestr);
    10.  
      }
    11.  
      }
    12.  
      else
    13.  
      {
    14.  
      var result = new Uint8Array(evt.data);
    15.  
      this.queue.push(result);
    16.  
      if (this.needsend == true)
    17.  
      {
    18.  
      this.loadvideo();
    19.  
      }
    20.  
      }

    字符串数据这里只用了很简单的定义,如果ws网关打开rtsp失败,那么返回的是“fail”,如果返回成功,则是“open:mime”,通过分隔符将mime取出,就可以初始化mime了;如果是二进制数据,则直接放到队列中。js代码不熟,有需要的各位自己按需求优化。

    这套代码对rtsp源有格式要求,必须是h264+aac或纯h264的rtsp数据,因为mse对能播放的fmp4有要求,代码中并未对音视频进行重编码。另外代码中是使用rtp over tcp来传输的,使用udp模式请修改代码。最后,本方案达到的延时极低,但在chrome和firefox上对比,firefox的延时略大一些,估计各个浏览器的缓存策略造成了差异。

    from:https://blog.csdn.net/garefield/article/details/105510485?utm_medium=distribute.pc_relevant_download.none-task-blog-BlogCommendFromBaidu-1.nonecase&dist_request_id=6315c8a9-2f1c-4009-97b9-b40f51fcb585&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-BlogCommendFromBaidu-1.nonecas

  • 相关阅读:
    Use Prerender to improve AngularJS SEO
    Prerender.io
    Prerender Application Level Middleware
    Prerender Application Level Middleware
    正则获取html标签字符串中图片地址
    xml转json
    videojs实现双击视频全屏播放、播放器全屏时视频未全屏
    自己编写jquery插件
    点击回退时需要点击2次才可返回js
    if中有逗号的写法
  • 原文地址:https://www.cnblogs.com/lidabo/p/14434511.html
Copyright © 2011-2022 走看看