Java 在ExoPlayer2上找不到同步字节m3u8

Java 在ExoPlayer2上找不到同步字节m3u8,java,android,flutter,http-live-streaming,m3u8,Java,Android,Flutter,Http Live Streaming,M3u8,我有一个AES 128位加密的m3u8播放列表。我试着在电视上主持这个节目 通过Cloudflare的Google云存储桶 本地Xampp服务器 播放列表在HTML5网络播放器上工作。然后我尝试在Android应用程序中播放m3u8文件。我试过了,一个flatter应用程序,一个React Native应用程序和一个Native Java应用程序 我已经尝试了几乎所有适用于Flutter和React Native的HLS库。但在最后,每个玩家都显示了关于谷歌ExoPlayer的相同错误。我正在努

我有一个AES 128位加密的m3u8播放列表。我试着在电视上主持这个节目

  • 通过Cloudflare的Google云存储桶
  • 本地Xampp服务器
  • 播放列表在HTML5网络播放器上工作。然后我尝试在Android应用程序中播放m3u8文件。我试过了,一个flatter应用程序,一个React Native应用程序和一个Native Java应用程序

    我已经尝试了几乎所有适用于Flutter和React Native的HLS库。但在最后,每个玩家都显示了关于谷歌ExoPlayer的相同错误。我正在努力解决这个问题,已经快一个月了。我已经检查了Github的大部分问题,但运气不好

    这就是我看到的错误(从Flatter终端复制,但RN和本机java应用程序也显示相同的错误)

    我还将添加m3u8文件的内容

    #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-TARGETDURATION:12
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-KEY:METHOD=AES-128,URI="http://192.168.1.2/key/video.key",IV=0x00000000000000000000000000000000
    #EXTINF:10.666667,
    playlist0.ts
    #EXTINF:11.666667,
    playlist1.ts
    #EXT-X-ENDLIST
    
    我发现问题出在
    TS
    文件上,因为错误消息在
    TsExtractor.java
    文件中显示了错误。在这里,我还尝试使用curl查看其中一个TS文件的HTTP响应头

    C:\Users\mdils>curl -D - http://localhost/key/playlist0.ts
    HTTP/1.1 200 OK
    Date: Fri, 23 Apr 2021 16:01:24 GMT
    Server: Apache/2.4.46 (Win64) OpenSSL/1.1.1g PHP/7.4.11
    Last-Modified: Sun, 28 Feb 2021 15:29:09 GMT
    ETag: "239f80-5bc6729a3ba75"
    Accept-Ranges: bytes
    Content-Length: 2334592
    Access-Control-Allow-Origin: *
    
    任何关于这方面的帮助都非常感谢

    更新

    播放列表示例-

    用于编码的FFMPEG命令

    ffmpeg -i input.mp4 -c copy -bsf:v h264_mp4toannexb -hls_list_size 0 -hls_time 10 -hls_key_info_file key_info.txt playback.m3u8
    

    更新-2021-04-25

    这是一个工作的m3u8文件(从ExoPlayer演示应用程序复制)

    将此m3u8文件与上述文件进行比较,唯一的区别是出现错误的文件具有aes-128位加密播放列表

    然后,当我检查源代码时,我在ExoPlayer源代码上找到了这个方法

      /**
       * Returns the position of the end of the first TS packet (exclusive) in the packet buffer.
       *
       * <p>This may be a position beyond the buffer limit if the packet has not been read fully into
       * the buffer, or if no packet could be found within the buffer.
       */
      private int findEndOfFirstTsPacketInBuffer() throws ParserException {
        int searchStart = tsPacketBuffer.getPosition();
        int limit = tsPacketBuffer.limit();
        int syncBytePosition =
            TsUtil.findSyncBytePosition(tsPacketBuffer.getData(), searchStart, limit);
        // Discard all bytes before the sync byte.
        // If sync byte is not found, this means discard the whole buffer.
        tsPacketBuffer.setPosition(syncBytePosition);
        int endOfPacket = syncBytePosition + TS_PACKET_SIZE;
        if (endOfPacket > limit) {
          bytesSinceLastSync += syncBytePosition - searchStart;
          if (mode == MODE_HLS && bytesSinceLastSync > TS_PACKET_SIZE * 2) {
            throw new ParserException("Cannot find sync byte. Most likely not a Transport Stream.");
          }
        } else {
          // We have found a packet within the buffer.
          bytesSinceLastSync = 0;
        }
        return endOfPacket;
      } 
    
    /**
    *返回第一个TS数据包(独占)在数据包缓冲区中的结束位置。
    *
    *如果数据包未完全读入,则该位置可能超出缓冲区限制
    *缓冲区,或者如果在缓冲区内找不到数据包。
    */
    private int findEndOfFirstTsPacketInBuffer()引发ParserException{
    int searchStart=tsPacketBuffer.getPosition();
    int limit=tsPacketBuffer.limit();
    int syncBytePosition=
    TsUtil.findSyncBytePosition(tsPacketBuffer.getData(),searchStart,limit);
    //放弃同步字节之前的所有字节。
    //如果找不到同步字节,这意味着丢弃整个缓冲区。
    tsPacketBuffer.setPosition(syncBytePosition);
    int endOfPacket=syncBytePosition+TS\u数据包大小;
    如果(endOfPacket>limit){
    ByTessinclastSync+=syncBytePosition-searchStart;
    if(mode==mode_HLS&&bytessinclastsync>TS_数据包大小*2){
    抛出新的ParserException(“找不到同步字节。很可能不是传输流”);
    }
    }否则{
    //我们在缓冲区内发现了一个数据包。
    ByTessinclastSync=0;
    }
    返回endOfPacket;
    } 
    

    根据上述函数的注释,当无法找到同步字节时,会抛出上述错误消息。因此,我唯一可以假设的是,可能播放器未能用提供的密钥解密第一个TS文件?(密钥是正确的,因为它在HLS web播放器上工作)

    您的密钥文件无效,在解密TS段时会收到垃圾。
    hls\u key\u info\u文件的FFmpeg文档说明:

    密钥文件以二进制格式读取为16个八位字节的单个压缩数组

    您的密钥文件有32个字节。如果您获取当前密钥文件的前16个字节并以二进制形式输出,它将正确解密。例如:

    xxd-p-l 16 video.key | xxd-r-p-video\u bin.key


    在播放列表中使用
    video\u bin.key
    。当然,最好首先生成一个有效的密钥。

    听起来段(
    .ts
    文件)的内容有问题。你能在stackoverflow上分享吗?@jmsn谢谢你的评论。我已经更新了问题,非常感谢你的回答。我不明白为什么它与hls.js一起工作。因为我没有找到解决方案,所以我添加了一个带有hls.js的WebView作为临时解决方案。使用32字节长的密钥(不是问题中的同一个,而是一个类似的密钥),它可以完美地工作。@Dilshan它可能会像我一样截断密钥。如果玩家验证了钥匙,你就有麻烦了。例如,您的播放列表在使用SafariOh的macOS上无法正常工作。再次感谢你。您保存了整个项目:)我刚刚用16字节的键进行了测试。它工作得很好。
    #EXTM3U
    #EXT-X-TARGETDURATION:10
    #EXT-X-VERSION:3
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-PLAYLIST-TYPE:VOD
    #EXTINF:9.97667,    
    fileSequence0.ts
    #EXTINF:9.97667,    
    fileSequence1.ts
    #EXTINF:9.97667,
    ... 
    
      /**
       * Returns the position of the end of the first TS packet (exclusive) in the packet buffer.
       *
       * <p>This may be a position beyond the buffer limit if the packet has not been read fully into
       * the buffer, or if no packet could be found within the buffer.
       */
      private int findEndOfFirstTsPacketInBuffer() throws ParserException {
        int searchStart = tsPacketBuffer.getPosition();
        int limit = tsPacketBuffer.limit();
        int syncBytePosition =
            TsUtil.findSyncBytePosition(tsPacketBuffer.getData(), searchStart, limit);
        // Discard all bytes before the sync byte.
        // If sync byte is not found, this means discard the whole buffer.
        tsPacketBuffer.setPosition(syncBytePosition);
        int endOfPacket = syncBytePosition + TS_PACKET_SIZE;
        if (endOfPacket > limit) {
          bytesSinceLastSync += syncBytePosition - searchStart;
          if (mode == MODE_HLS && bytesSinceLastSync > TS_PACKET_SIZE * 2) {
            throw new ParserException("Cannot find sync byte. Most likely not a Transport Stream.");
          }
        } else {
          // We have found a packet within the buffer.
          bytesSinceLastSync = 0;
        }
        return endOfPacket;
      }