美文网首页
flutter 加密的HLS m3u8 下载到本地播放

flutter 加密的HLS m3u8 下载到本地播放

作者: azhansy | 来源:发表于2022-07-28 16:44 被阅读0次
        //1、设置好已下载的m3u8文件路径    
        final String fold = (await FileUtils.getPackageDirectory(subDir: 'video')).path;
        final String host = url.substring(0, url.lastIndexOf('/'));
        final String m3u8FileName = url.substring(url.lastIndexOf('/'));
        final String m3u8Path = '$fold/$m3u8FileName';
        final String m3u8Name = m3u8FileName.split('.').first;
        //2、获取key的下载地址
        final String key = '$fold/../../video_enc.key';
    

    3、下载好m3u8Path文件、key文件后,
    4、解析m3u8文件
    playList = await HlsPlaylistParser.create().parse(Uri.parse(url), await File(m3u8Path).readAsLines());
    5、下载每个ts视频片段
    6、使用FFmpegKit 合并ts视频片段
    关键核心代码

    // #EXTM3U
    // #EXT-X-VERSION:3
    // #EXT-X-TARGETDURATION:11
    // #EXT-X-MEDIA-SEQUENCE:0
    // #EXT-X-PLAYLIST-TYPE:VOD
    // #EXT-X-KEY:METHOD=AES-128,URI="../../video_enc.key",IV=0xxxxxxxxxxx
    // #EXTINF:10.560000,
    // xxx_0000.ts
    // #EXTINF:10.720000,
    // xxx_0001.ts
    // #EXTINF:6.760000,
    // xxx_0002.ts
    // #EXT-X-ENDLIST
    
    /**
     * https://www.jianshu.com/p/b3a99aa7b154
     */
    class DownloadUtil {
      DownloadUtil._();
    
      static late final DownloadUtil instance = DownloadUtil._();
      List<String> downLoadUrl = [];
      Map<String, String> downLoadUrlMap = {};
    
      Future<void> downloadM3u8(String url) async {
        debugPrint('DownloadUtil, _url=$url');
        // if (downLoadUrl.contains(url)) {
        //   debugPrint('DownloadUtil, 已进入下载队列 _url=$url, path=${downLoadUrlMap[url]}');
        //   return;
        // }
        final String fold = (await FileUtils.getPackageDirectory(subDir: 'video')).path;
        downLoadUrl.add(url);
        final String host = url.substring(0, url.lastIndexOf('/'));
        final String m3u8FileName = url.substring(url.lastIndexOf('/'));
        final String m3u8Path = '$fold/$m3u8FileName';
        final String m3u8Name = m3u8FileName.split('.').first;
        final String key = '$fold/../../video_enc.key';
    
        final Response response = await HttpManager().downloadFile(url, m3u8Path);
        final Response responseKey = await HttpManager().downloadFile('http://xxxx.com/video_enc.key', key);
        if (response.statusCode != 200) {
          debugPrint('DownloadUtil, 下载 m3u8 失败 _url=$url');
          downLoadUrl.remove(url);
          return;
        }
        if (responseKey.statusCode != 200) {
          debugPrint('DownloadUtil, 下载 key 失败 _url=$url');
          downLoadUrl.remove(url);
          return;
        }
        debugPrint('DownloadUtil, 下载m3u8成功 m3u8Path=$m3u8Path,key=$key');
    
        Uri playlistUri;
        List<String> lines;
        HlsPlaylist? playList;
        try {
          playList = await HlsPlaylistParser.create().parse(Uri.parse(url), await File(m3u8Path).readAsLines());
        } on ParserException catch (e) {
          print(e);
        }
    
        if (playList is HlsMasterPlaylist) {
          // master m3u8 file
        } else if (playList is HlsMediaPlaylist) {
          final key = playList.segments.map((it) => it.fullSegmentEncryptionKeyUri);
    
          debugPrint('DownloadUtil, 下载  key=$key');
    
          // media m3u8 file
          final mediaPlaylistUrls = playList.segments.map((it) => it.url);
          for (var value in mediaPlaylistUrls) {
            String tsUrl = '$host/$value';
            File file = File('$fold/$value');
            if (!file.existsSync()) {
              file.create();
            }
            debugPrint('DownloadUtil, 下载tsUrl tsUrl=$tsUrl, file.path=${file.path}');
            final Response response = await HttpManager().downloadFile(tsUrl,
                file.path /*, onReceiveProgress: (int count, int total){
              debugPrint('DownloadUtil, onReceiveProgress tsUrl=$tsUrl, count=$count,total=$total,process=${count/total}');
            }*/
                );
            // if(response.statusCode != 200){
            //   return;
            // }
            debugPrint('DownloadUtil, 下载tsUrl tsUrl=$tsUrl, file.path=${file.path}, 完成=========');
          }
          downLoadUrlMap[url] = m3u8Path;
          debugPrint('DownloadUtil, 全部ts视频下载完成===');
          // String cmd = '-i \"/storage/emulated/0/Android/data/com.azhansy.video/files/video/hevc.mp4\" -vcodec h264 \"/storage/emulated/0/Android/data/com.azhansy.video/files/video/hevc_h264.mp4\"';
          // String cmd = '-i \"/storage/emulated/0/Android/data/com.azhansy.video/files/video/hhh.mp4\" \"/storage/emulated/0/Android/data/com.azhansy.video/files/video/hhh111i.mp4\"';
          //-protocol_whitelist "file,http,crypto,tcp"
          '下载完成'.toast();
          String cmd =
              '-allowed_extensions ALL -i $m3u8Path  \"/storage/emulated/0/Android/data/com.azhansy.video/files/video/$m3u8Name.mp4\"';
    
          ///合并ts为mp4
          FFmpegKit.executeAsync(cmd, (FFmpegSession session) {
            cLogInfo('DownloadUtil, FFmpegKit session completeCallback cmd=${session.getCommand()}');
          }, (Log log) {
            debugPrint('DownloadUtil, FFmpegKit log===${log.getMessage()}');
          }, (Statistics statistics) {
            debugPrint('DownloadUtil, FFmpegKit statistics===${statistics.getTime()}');
          });
        }
      }
    }
    
    class DialogHeading extends StatelessWidget {
      const DialogHeading({Key? key, required this.text}) : super(key: key);
    
      final String text;
    
      @override
      Widget build(BuildContext context) => Padding(
            child: Text(
              text,
              style: Theme.of(context).textTheme.headline6,
            ),
            padding: EdgeInsets.only(top: 24, bottom: 8),
          );
    }
    
    

    用到的插件:

    https://pub.dev/packages/flutter_hls_parser

    flutter_hls_parser: ^2.0.0
    ffmpeg_kit_flutter: ^4.5.1

    相关文章

      网友评论

          本文标题:flutter 加密的HLS m3u8 下载到本地播放

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