美文网首页Android音视频系列
M3U8-TS文件合并为MP4文件

M3U8-TS文件合并为MP4文件

作者: 码上就说 | 来源:发表于2020-09-30 23:59 被阅读0次

    M3U8文件是一个索引文件,里面包好N个TS的分片文件,组成一个视频文件。目前在直播和点播中应用非常广泛。我们下载一个M3U8视频文件,就是下载了N个TS分片文件,导致我们手机相册中多了很多碎片的小视频文件。如果是羞羞的视频,更加不好意思了。删除都要删除半天,更不用说想把M3U8文件拷贝出来,放到电脑上观看欣赏。
    例如给一个M3U8例子:https://tv2.youkutv.cc/2020/04/14/MbqulRmS8sjQGJG9/playlist.m3u8,解析出来的索引文件如下:

    #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-ALLOW-CACHE:YES
    #EXT-X-TARGETDURATION:19
    #EXTINF:13.960000,
    out000.ts
    #EXTINF:6.320000,
    out001.ts
    #EXTINF:10.280000,
    out002.ts
    #EXTINF:10.320000,
    out003.ts
    #EXTINF:9.960000,
    out004.ts
    #EXTINF:12.240000,
    out005.ts
    #EXTINF:8.680000,
    out006.ts
    #EXTINF:8.280000,
    out007.ts
    #EXTINF:10.240000,
    out008.ts
    #EXTINF:15.000000,
    out009.ts
    #EXTINF:5.000000,
    out010.ts
    #EXTINF:14.200000,
    out011.ts
    #EXTINF:7.920000,
    out012.ts
    #EXTINF:8.920000,
    out013.ts
    #EXTINF:10.960000,
    out014.ts
    #EXTINF:7.920000,
    out015.ts
    #EXTINF:10.000000,
    out016.ts
    #EXTINF:12.040000,
    out017.ts
    #EXTINF:9.280000,
    out018.ts
    #EXTINF:17.160000,
    out019.ts
    #EXTINF:8.080000,
    out020.ts
    #EXTINF:6.840000,
    out021.ts
    #EXTINF:8.040000,
    out022.ts
    #EXTINF:11.640000,
    out023.ts
    #EXTINF:10.000000,
    out024.ts
    #EXTINF:7.760000,
    out025.ts
    #EXTINF:9.040000,
    out026.ts
    #EXTINF:13.760000,
    out027.ts
    #EXTINF:7.120000,
    out028.ts
    #EXTINF:10.560000,
    out029.ts
    #EXTINF:11.640000,
    out030.ts
    #EXTINF:7.880000,
    out031.ts
    #EXTINF:11.880000,
    out032.ts
    #EXTINF:12.640000,
    out033.ts
    #EXTINF:6.920000,
    out034.ts
    #EXTINF:8.320000,
    out035.ts
    #EXTINF:10.720000,
    out036.ts
    #EXTINF:8.840000,
    out037.ts
    #EXTINF:10.320000,
    out038.ts
    #EXTINF:9.760000,
    out039.ts
    #EXTINF:10.320000,
    out040.ts
    #EXTINF:10.800000,
    out041.ts
    #EXTINF:13.200000,
    out042.ts
    #EXTINF:7.480000,
    out043.ts
    #EXTINF:8.560000,
    out044.ts
    #EXTINF:10.160000,
    out045.ts
    #EXTINF:10.160000,
    out046.ts
    #EXTINF:9.360000,
    out047.ts
    #EXTINF:11.960000,
    out048.ts
    #EXTINF:10.640000,
    out049.ts
    #EXTINF:11.360000,
    out050.ts
    #EXTINF:8.040000,
    out051.ts
    #EXTINF:7.640000,
    out052.ts
    #EXTINF:10.480000,
    out053.ts
    #EXTINF:10.400000,
    out054.ts
    #EXTINF:9.360000,
    out055.ts
    #EXTINF:15.720000,
    out056.ts
    #EXTINF:4.600000,
    out057.ts
    #EXTINF:9.640000,
    out058.ts
    #EXTINF:17.800000,
    out059.ts
    #EXTINF:6.040000,
    out060.ts
    #EXTINF:9.400000,
    out061.ts
    #EXTINF:10.000000,
    out062.ts
    #EXTINF:10.000000,
    out063.ts
    #EXTINF:10.000000,
    out064.ts
    #EXTINF:10.000000,
    out065.ts
    #EXTINF:9.000000,
    out066.ts
    #EXTINF:10.000000,
    out067.ts
    #EXTINF:10.000000,
    out068.ts
    #EXTINF:8.920000,
    out069.ts
    #EXTINF:15.920000,
    out070.ts
    #EXTINF:5.200000,
    out071.ts
    #EXTINF:8.240000,
    out072.ts
    #EXTINF:13.200000,
    out073.ts
    #EXTINF:14.400000,
    out074.ts
    #EXTINF:8.960000,
    out075.ts
    #EXTINF:5.880000,
    out076.ts
    #EXTINF:10.000000,
    out077.ts
    #EXTINF:10.000000,
    out078.ts
    #EXTINF:6.720000,
    out079.ts
    #EXTINF:16.720000,
    out080.ts
    #EXTINF:10.000000,
    out081.ts
    #EXTINF:10.000000,
    out082.ts
    #EXTINF:10.000000,
    out083.ts
    #EXTINF:9.640000,
    out084.ts
    #EXTINF:9.080000,
    out085.ts
    #EXTINF:7.000000,
    out086.ts
    #EXTINF:10.000000,
    out087.ts
    #EXTINF:17.320000,
    out088.ts
    #EXTINF:9.920000,
    out089.ts
    #EXTINF:8.000000,
    out090.ts
    #EXTINF:6.040000,
    out091.ts
    #EXTINF:7.560000,
    out092.ts
    #EXTINF:10.000000,
    out093.ts
    #EXTINF:8.800000,
    out094.ts
    #EXTINF:10.000000,
    out095.ts
    #EXTINF:10.000000,
    out096.ts
    #EXTINF:12.200000,
    out097.ts
    #EXTINF:10.000000,
    out098.ts
    #EXTINF:17.240000,
    out099.ts
    #EXTINF:10.000000,
    out100.ts
    #EXTINF:9.680000,
    out101.ts
    #EXTINF:10.000000,
    out102.ts
    #EXTINF:10.000000,
    out103.ts
    #EXTINF:10.000000,
    out104.ts
    #EXTINF:10.000000,
    out105.ts
    #EXTINF:10.000000,
    out106.ts
    #EXTINF:10.000000,
    out107.ts
    #EXTINF:10.000000,
    out108.ts
    #EXTINF:9.080000,
    out109.ts
    #EXTINF:10.000000,
    out110.ts
    #EXTINF:10.000000,
    out111.ts
    #EXTINF:10.000000,
    out112.ts
    #EXTINF:10.000000,
    out113.ts
    #EXTINF:10.000000,
    out114.ts
    #EXTINF:10.000000,
    out115.ts
    #EXTINF:10.000000,
    out116.ts
    #EXTINF:6.040000,
    out117.ts
    #EXTINF:9.000000,
    out118.ts
    #EXTINF:10.000000,
    out119.ts
    #EXTINF:10.000000,
    out120.ts
    #EXTINF:10.000000,
    out121.ts
    #EXTINF:10.000000,
    out122.ts
    #EXTINF:10.000000,
    out123.ts
    #EXTINF:10.000000,
    out124.ts
    #EXTINF:10.000000,
    out125.ts
    #EXTINF:10.000000,
    out126.ts
    #EXTINF:10.000000,
    out127.ts
    #EXTINF:10.000000,
    out128.ts
    #EXTINF:10.000000,
    out129.ts
    #EXTINF:10.000000,
    out130.ts
    #EXTINF:10.000000,
    out131.ts
    #EXTINF:9.760000,
    out132.ts
    #EXTINF:10.000000,
    out133.ts
    #EXTINF:10.000000,
    out134.ts
    #EXTINF:10.000000,
    out135.ts
    #EXTINF:10.000000,
    out136.ts
    #EXTINF:10.000000,
    out137.ts
    #EXTINF:8.880000,
    out138.ts
    #EXTINF:9.240000,
    out139.ts
    #EXTINF:11.840000,
    out140.ts
    #EXTINF:10.000000,
    out141.ts
    #EXTINF:10.000000,
    out142.ts
    #EXTINF:8.280000,
    out143.ts
    #EXTINF:10.000000,
    out144.ts
    #EXTINF:16.400000,
    out145.ts
    #EXTINF:3.960000,
    out146.ts
    #EXTINF:10.160000,
    out147.ts
    #EXTINF:8.360000,
    out148.ts
    #EXTINF:11.160000,
    out149.ts
    #EXTINF:12.440000,
    out150.ts
    #EXTINF:7.520000,
    out151.ts
    #EXTINF:12.600000,
    out152.ts
    #EXTINF:14.400000,
    out153.ts
    #EXTINF:4.800000,
    out154.ts
    #EXTINF:13.280000,
    out155.ts
    #EXTINF:10.000000,
    out156.ts
    #EXTINF:6.520000,
    out157.ts
    #EXTINF:9.160000,
    out158.ts
    #EXTINF:10.000000,
    out159.ts
    #EXTINF:7.920000,
    out160.ts
    #EXTINF:17.960000,
    out161.ts
    #EXTINF:8.000000,
    out162.ts
    #EXTINF:10.000000,
    out163.ts
    #EXTINF:10.000000,
    out164.ts
    #EXTINF:8.680000,
    out165.ts
    #EXTINF:8.920000,
    out166.ts
    #EXTINF:15.880000,
    out167.ts
    #EXTINF:3.360000,
    out168.ts
    #EXTINF:11.200000,
    out169.ts
    #EXTINF:8.160000,
    out170.ts
    #EXTINF:10.920000,
    out171.ts
    #EXTINF:8.280000,
    out172.ts
    #EXTINF:8.640000,
    out173.ts
    #EXTINF:14.400000,
    out174.ts
    #EXTINF:10.000000,
    out175.ts
    #EXTINF:8.960000,
    out176.ts
    #EXTINF:10.000000,
    out177.ts
    #EXTINF:13.560000,
    out178.ts
    #EXTINF:10.000000,
    out179.ts
    #EXTINF:10.000000,
    out180.ts
    #EXTINF:10.000000,
    out181.ts
    #EXTINF:10.000000,
    out182.ts
    #EXTINF:10.000000,
    out183.ts
    #EXTINF:10.000000,
    out184.ts
    #EXTINF:10.000000,
    out185.ts
    #EXTINF:10.000000,
    out186.ts
    #EXTINF:10.000000,
    out187.ts
    #EXTINF:10.000000,
    out188.ts
    #EXTINF:10.000000,
    out189.ts
    #EXTINF:10.000000,
    out190.ts
    #EXTINF:10.000000,
    out191.ts
    #EXTINF:10.000000,
    out192.ts
    #EXTINF:10.000000,
    out193.ts
    #EXTINF:10.000000,
    out194.ts
    #EXTINF:9.200000,
    out195.ts
    #EXTINF:10.000000,
    out196.ts
    #EXTINF:10.000000,
    out197.ts
    #EXTINF:10.000000,
    out198.ts
    #EXTINF:8.720000,
    out199.ts
    #EXTINF:10.000000,
    out200.ts
    #EXTINF:13.040000,
    out201.ts
    #EXTINF:6.360000,
    out202.ts
    #EXTINF:10.000000,
    out203.ts
    #EXTINF:15.200000,
    out204.ts
    #EXTINF:7.960000,
    out205.ts
    #EXTINF:8.120000,
    out206.ts
    #EXTINF:10.000000,
    out207.ts
    #EXTINF:9.600000,
    out208.ts
    #EXTINF:9.720000,
    out209.ts
    #EXTINF:10.000000,
    out210.ts
    #EXTINF:10.000000,
    out211.ts
    #EXTINF:10.000000,
    out212.ts
    #EXTINF:9.160000,
    out213.ts
    #EXTINF:10.000000,
    out214.ts
    #EXTINF:10.000000,
    out215.ts
    #EXTINF:8.040000,
    out216.ts
    #EXTINF:10.000000,
    out217.ts
    #EXTINF:10.000000,
    out218.ts
    #EXTINF:10.000000,
    out219.ts
    #EXTINF:10.000000,
    out220.ts
    #EXTINF:10.000000,
    out221.ts
    #EXTINF:10.000000,
    out222.ts
    #EXTINF:10.000000,
    out223.ts
    #EXTINF:10.000000,
    out224.ts
    #EXTINF:8.240000,
    out225.ts
    #EXTINF:15.200000,
    out226.ts
    #EXTINF:6.200000,
    out227.ts
    #EXTINF:9.560000,
    out228.ts
    #EXTINF:10.000000,
    out229.ts
    #EXTINF:10.000000,
    out230.ts
    #EXTINF:10.000000,
    out231.ts
    #EXTINF:9.560000,
    out232.ts
    #EXTINF:9.640000,
    out233.ts
    #EXTINF:17.880000,
    out234.ts
    #EXTINF:8.600000,
    out235.ts
    #EXTINF:9.560000,
    out236.ts
    #EXTINF:10.000000,
    out237.ts
    #EXTINF:10.000000,
    out238.ts
    #EXTINF:8.000000,
    out239.ts
    #EXTINF:10.000000,
    out240.ts
    #EXTINF:10.000000,
    out241.ts
    #EXTINF:10.000000,
    out242.ts
    #EXTINF:10.000000,
    out243.ts
    #EXTINF:10.000000,
    out244.ts
    #EXTINF:10.000000,
    out245.ts
    #EXTINF:9.440000,
    out246.ts
    #EXTINF:14.240000,
    out247.ts
    #EXTINF:5.840000,
    out248.ts
    #EXTINF:6.600000,
    out249.ts
    #EXTINF:9.920000,
    out250.ts
    #EXTINF:11.080000,
    out251.ts
    #EXTINF:14.600000,
    out252.ts
    #EXTINF:7.160000,
    out253.ts
    #EXTINF:8.840000,
    out254.ts
    #EXTINF:11.320000,
    out255.ts
    #EXTINF:8.720000,
    out256.ts
    #EXTINF:8.240000,
    out257.ts
    #EXTINF:9.880000,
    out258.ts
    #EXTINF:18.480000,
    out259.ts
    #EXTINF:10.000000,
    out260.ts
    #EXTINF:8.760000,
    out261.ts
    #EXTINF:9.240000,
    out262.ts
    #EXTINF:6.120000,
    out263.ts
    #EXTINF:13.480000,
    out264.ts
    #EXTINF:10.000000,
    out265.ts
    #EXTINF:8.360000,
    out266.ts
    #EXTINF:6.080000,
    out267.ts
    #EXTINF:14.080000,
    out268.ts
    #EXTINF:7.520000,
    out269.ts
    #EXTINF:9.240000,
    out270.ts
    #EXTINF:10.000000,
    out271.ts
    #EXTINF:6.960000,
    out272.ts
    #EXT-X-ENDLIST
    

    里面有273个分片文件,可以使用:https://github.com/JeffMony/M3U8ParserTools/blob/master/m3u8_function/parse_m3u8_info.py脚本获取当前的M3U8索引文件。
    下载好视频文件如下:

    PD1824:/sdcard/Android/data/com.jeffmony.videodemo/files/Video/Download/a03663b3bd0a2fe6fcb8bb36b657cf80 $ ls
    local.m3u8   video_124.ts video_152.ts video_180.ts video_208.ts video_236.ts video_264.ts video_47.ts video_75.ts 
    remote.m3u8  video_125.ts video_153.ts video_181.ts video_209.ts video_237.ts video_265.ts video_48.ts video_76.ts 
    video_0.ts   video_126.ts video_154.ts video_182.ts video_21.ts  video_238.ts video_266.ts video_49.ts video_77.ts 
    video_1.ts   video_127.ts video_155.ts video_183.ts video_210.ts video_239.ts video_267.ts video_5.ts  video_78.ts 
    video_10.ts  video_128.ts video_156.ts video_184.ts video_211.ts video_24.ts  video_268.ts video_50.ts video_79.ts 
    video_100.ts video_129.ts video_157.ts video_185.ts video_212.ts video_240.ts video_269.ts video_51.ts video_8.ts  
    video_101.ts video_13.ts  video_158.ts video_186.ts video_213.ts video_241.ts video_27.ts  video_52.ts video_80.ts 
    video_102.ts video_130.ts video_159.ts video_187.ts video_214.ts video_242.ts video_270.ts video_53.ts video_81.ts 
    video_103.ts video_131.ts video_16.ts  video_188.ts video_215.ts video_243.ts video_271.ts video_54.ts video_82.ts 
    video_104.ts video_132.ts video_160.ts video_189.ts video_216.ts video_244.ts video_272.ts video_55.ts video_83.ts 
    video_105.ts video_133.ts video_161.ts video_19.ts  video_217.ts video_245.ts video_28.ts  video_56.ts video_84.ts 
    video_106.ts video_134.ts video_162.ts video_190.ts video_218.ts video_246.ts video_29.ts  video_57.ts video_85.ts 
    video_107.ts video_135.ts video_163.ts video_191.ts video_219.ts video_247.ts video_3.ts   video_58.ts video_86.ts 
    video_108.ts video_136.ts video_164.ts video_192.ts video_22.ts  video_248.ts video_30.ts  video_59.ts video_87.ts 
    video_109.ts video_137.ts video_165.ts video_193.ts video_220.ts video_249.ts video_31.ts  video_6.ts  video_88.ts 
    video_11.ts  video_138.ts video_166.ts video_194.ts video_221.ts video_25.ts  video_32.ts  video_60.ts video_89.ts 
    video_110.ts video_139.ts video_167.ts video_195.ts video_222.ts video_250.ts video_33.ts  video_61.ts video_9.ts  
    video_111.ts video_14.ts  video_168.ts video_196.ts video_223.ts video_251.ts video_34.ts  video_62.ts video_90.ts 
    video_112.ts video_140.ts video_169.ts video_197.ts video_224.ts video_252.ts video_35.ts  video_63.ts video_91.ts 
    video_113.ts video_141.ts video_17.ts  video_198.ts video_225.ts video_253.ts video_36.ts  video_64.ts video_92.ts 
    video_114.ts video_142.ts video_170.ts video_199.ts video_226.ts video_254.ts video_37.ts  video_65.ts video_93.ts 
    video_115.ts video_143.ts video_171.ts video_2.ts   video_227.ts video_255.ts video_38.ts  video_66.ts video_94.ts 
    video_116.ts video_144.ts video_172.ts video_20.ts  video_228.ts video_256.ts video_39.ts  video_67.ts video_95.ts 
    video_117.ts video_145.ts video_173.ts video_200.ts video_229.ts video_257.ts video_4.ts   video_68.ts video_96.ts 
    video_118.ts video_146.ts video_174.ts video_201.ts video_23.ts  video_258.ts video_40.ts  video_69.ts video_97.ts 
    video_119.ts video_147.ts video_175.ts video_202.ts video_230.ts video_259.ts video_41.ts  video_7.ts  video_98.ts 
    video_12.ts  video_148.ts video_176.ts video_203.ts video_231.ts video_26.ts  video_42.ts  video_70.ts video_99.ts 
    video_120.ts video_149.ts video_177.ts video_204.ts video_232.ts video_260.ts video_43.ts  video_71.ts 
    video_121.ts video_15.ts  video_178.ts video_205.ts video_233.ts video_261.ts video_44.ts  video_72.ts 
    video_122.ts video_150.ts video_179.ts video_206.ts video_234.ts video_262.ts video_45.ts  video_73.ts 
    video_123.ts video_151.ts video_18.ts  video_207.ts video_235.ts video_263.ts video_46.ts  video_74.ts
    

    如果能将这些TS文件合成一个视频文件就好了。

    TS文件合成一个MP4视频,需要的注意点有:

    • 有些M3U8视频是加密的,TS源文件需要解密才能播放
    • TS文件一个个拼接的方式最后得到的还是一个TS视频,只不过比较大一点,并不是后缀名改成.mp4就是MP4视频了。

    加密的视频

    M3U8中的EXT-X-KEY中就包含M3U8的加密方式以及密钥。
    例如http://video.yjf138.com:8091/20180812/6yl0Q2YZ/index.m3u8中就有
    #EXT-X-KEY:METHOD=AES-128,URI="key.key"
    可以看出加密方式是AES-128对称加密,密钥是key.key
    转化为链接就是:https://video.yjf138.com:8091/20180812/6yl0Q2YZ/1500kb/hls/key.key
    那我们就可以使用AES-128解密了,通常的做法是:

    /**
     * 解密ts
     *
     * @param sSrc ts文件字节数组
     * @param sKey 密钥
     * @return 解密后的字节数组
     */
     private static byte[] decrypt(byte[] sSrc, String sKey, String method) {
       try {
         if (StringUtils.isNotEmpty(method) && !method.contains("AES")) {
           throw new M3u8Exception("未知的算法!");
         }
         // 判断Key是否正确
         if (StringUtils.isEmpty(sKey)) {
           return sSrc;
         }
         // 判断Key是否为16位
         if (sKey.length() != 16) {
           System.out.print("Key长度不是16位");
           return null;
         }
         Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
         SecretKeySpec keySpec = new SecretKeySpec(sKey.getBytes("utf-8"), "AES");
         // 如果m3u8有IV标签,那么IvParameterSpec构造函数就把IV标签后的内容转成字节数组传进去
         AlgorithmParameterSpec paramSpec = new IvParameterSpec(new byte[16]);
         cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);
         return cipher.doFinal(sSrc);
       } catch (Exception ex) {
         ex.printStackTrace();
         return null;
       }
     }
    

    但是在Android平台上,这样的写法并不能兼容所有的场景,因为不同Android level上面的crypto写法不同,调用系统的api得到的结果也会不一样,所以最好将解密场景放在native层,使用openssl库来帮我们实现解密。

    TS转化为MP4

    之前说过,TS合并通常的做法使用InputStream读取一个一个的TS分片,然后利用OutputStream写入本地的MP4文件中,这样看上去好像是生成了一个新的MP4文件,但是实际上这个新的视频是真正的MP4格式吗?

    显然不是,因为MP4的封装格式和TS是完全不一样的。
    最好的做法就是将最终生成的文件按照MP4的封装规则重写一遍,这样最终生成的文件肯定是MP4的文件。

    我们不用对照MP4的位flag来一个个生成,只要借助ffmpeg来帮我们实现这个转化就可以了。

    • 对源文件进行解封装处理,取出源文件的音频流和视频流
    • 创建目标文件的封装格式头信息。
    • 读取源文件音频流和视频流中的包数据、帧数据,然后按照规则封装到目标文件格式中。

    最终具体的代码见项目:
    https://github.com/JeffMony/VideoDownloader

    相关文章

      网友评论

        本文标题:M3U8-TS文件合并为MP4文件

        本文链接:https://www.haomeiwen.com/subject/rznyuktx.html