NiceLeeのBlog 用爱发电 bilibili~

Java 爬虫练习-bilibili视频下载 (六)

2019-03-02
nIceLee

阅读:


现在要做的就是增加对HTML5播放源的下载支持(๑•̀ㅂ•́)و✧

要做的事情

先上图,有个概念


使用HTML5播放的时候,浏览器不断的发了这么多请求(OPTIONS/GET + 视频源/音频源),请求的地址基本上是一样的,只是指向的文件的不同部分,这点从请求和回复的头部可以看出来。

再说说我的理解

  • 我先把HTML5的整个视频+音频的m4s文件下下来了,然后尝试了用播放器打开,结果失败。。。这对我造成了极大的困扰
  • 我先从一个旁观者的角度,思考这种视频流该怎样实现。
    • 首先,这个视频文件应该是可以分成多个片段的,并不一定要全部下载完才能播放,比如文件的600 - 700 字节可以播放2min30s - 2min31s的画面, 701-900 …
    • 其次,遵循这种格式的视频文件集合,应该可以支持多种清晰度的切换,比如720p的2min30s - 2min31s的画面对应600 - 700 字节,可能1080p的对应的是另一个文件的1600 - 2100等
    • 虽然不是很懂多媒体,但我想这里面有几个问题是显而易见的:
      • 一是即使同一清晰度,可能还有帧率啥的因素,导致一段时间(比如1s)所需消耗的文件大小可能也不同
      • 二是即使对应区间长度相同,选错对齐位置也播放不出来,还是以文件的600 - 700 字节播放2min30s - 2min31s的画面为例,[509 - 699]、[635 - 735]啥的肯定也播不出来
    • 如果换我来,我想把视频的分片计算的映射方法,还有其它一些必要信息写到文件头部,这个空间肯定占用不大,传输也花不了多长时间,嗯,耐撕的解决方案。
  • 经过观察,似乎整个播放流程就是这样实现的。下面截取了一部分配置,注意SegmentBase。每次播放源的前两个请求果然与这区间一致。
    {
      "duration": 83,
          "minBufferTime": 1.5,
          "video": [{
          ...
            
          "id": 80,
          "baseUrl": "http://upos-hz-mirrorkodou.acgvideo.com/upgcxcode/...",
          "backupUrl": null,
          "bandwidth": 2442010,
          "mimeType": "video/mp4",
          "codecs": "avc1.640028",
          "width": 1920,
          "height": 1080,
          "frameRate": "25",
          "sar": "1:1",
          "startWithSap": 1,
          "SegmentBase": {
              "Initialization": "0-975",
              "indexRange": "976-1211"
          },
          "codecid": 7
      }],
      "audio": [...]
    }
    

然后问题来了

我是谁?我在哪儿?我为什么要想这么多?_( ゚Д゚)ノ
我tm又不是要边下边播,为什么要在意这些。。。φ(≧ω≦*)♪
我把服务器那端的文件整个都挪过来了,怎么可能生不成想要的东西。。。Pia!(o ‵-′)ノ”(ノ﹏<。)
所以,直接ffmpeg处理一下试一试,现在我有一个音频,一个视频
FFmpeg将视频和音频合并

ffmpeg -i /path/to/input-no-audio.mp4 -i input.mp3 -c copy /path/to/output.mp4

搞掂

把HTML5的视频、音频源整个下下来, ffmpeg合并, 然后OK了。。。K了。。

实现

和flash源下载的不同的地方在于:

  • 获取下载地址的api参数
    //String url = "https://api.bilibili.com/x/player/playurl?fnval=2&fnver=0&player=1&otype=json&avid=%s&cid=%s&qn=%s";
    String url = "https://api.bilibili.com/x/player/playurl?fnval=16&fnver=0&type=&otype=json&avid=%s&cid=%s&qn=%s";
    url = String.format(url, avIdNum, cid, qn);
    
  • 下载的HTTP请求头部
    我这是完全按照浏览器的来,没有测试服务器有没有这方面的判断
    //FLV
    headerMap.put("X-Requested-With", "ShockwaveFlash/28.0.0.137");
    //M4S
    headerMap.remove("X-Requested-With");
    
  • 下载的文件 由一个.flv变为视频音频两个.m4s

  • 是否需要经过ffmpeg处理
    flash源下载可以直接食用,HTML5源需要经过处理
    public static String[] createConvertCmd(String videoName, String audioName, String dstName) {
      String cmd[] = {"ffmpeg",
              "-i", Global.savePath +videoName, 
              "-i", Global.savePath +audioName, 
              "-c", "copy", Global.savePath + dstName};
      return cmd;
    }
    

    源代码

  • 下载地址: https://github.com/nICEnnnnnnnLee/BilibiliDown/releases
  • GitHub: https://github.com/nICEnnnnnnnLee/BilibiliDown
  • Gitee码云: https://gitee.com/NiceLeee/BilibiliDown

内容
隐藏