在 ExoPlayer2 上找不到同步字节 m3u8
Cannot find sync byte m3u8 on ExoPlayer2
我有一个 AES 128 位加密的 m3u8 播放列表。我试着在
上主持这个
- Google 通过 Cloudflare 的云存储桶
- 本地Xampp服务器
播放列表适用于 HTML5 网络播放器。然后我尝试在 Android 应用程序中播放 m3u8 文件。我试过了,一个 Flutter 应用程序,一个 React Native 应用程序和一个 native Java 应用程序
我已经尝试了几乎所有适用于 Flutter 和 React Native 的 HLS 库。但最后每个玩家都显示关于 Google ExoPlayer 的相同错误。我试图解决这个问题已经将近一个月了。我已经检查了大多数 Github 问题,但没有运气。
这是我看到的错误(从 Flutter 终端复制,但 RN 和本机 java 应用程序也显示相同的错误)
Restarted application in 4,802ms.
I/ExoPlayerImpl(25099): Release 2354449 [ExoPlayerLib/2.13.1] [m21, SM-M215F, samsung, 30] [goog.exo.core, goog.exo.hls]
I/ExoPlayerImpl(25099): Init 91918d7 [ExoPlayerLib/2.13.1] [m21, SM-M215F, samsung, 30]
6
I/System.out(25099): (HTTPLog)-Static: isSBSettingEnabled false
E/ExoPlayerImplInternal(25099): Playback error
E/ExoPlayerImplInternal(25099): com.google.android.exoplayer2.ExoPlaybackException: Source error
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:579)
E/ExoPlayerImplInternal(25099): at android.os.Handler.dispatchMessage(Handler.java:102)
E/ExoPlayerImplInternal(25099): at android.os.Looper.loop(Looper.java:246)
E/ExoPlayerImplInternal(25099): at android.os.HandlerThread.run(HandlerThread.java:67)
E/ExoPlayerImplInternal(25099): Caused by: com.google.android.exoplayer2.ParserException: Cannot find sync byte. Most likely not a Transport Stream.
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.extractor.ts.TsExtractor.findEndOfFirstTsPacketInBuffer(TsExtractor.java:453)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.extractor.ts.TsExtractor.read(TsExtractor.java:320)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.source.hls.BundledHlsMediaChunkExtractor.read(BundledHlsMediaChunkExtractor.java:67)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.source.hls.HlsMediaChunk.feedDataToExtractor(HlsMediaChunk.java:434)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.source.hls.HlsMediaChunk.loadMedia(HlsMediaChunk.java:404)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.source.hls.HlsMediaChunk.load(HlsMediaChunk.java:355)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:415)
E/ExoPlayerImplInternal(25099): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/ExoPlayerImplInternal(25099): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/ExoPlayerImplInternal(25099): at java.lang.Thread.run(Thread.java:923)
E/flutter (25099): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: PlatformException(VideoError, Video player had error com.google.android.exoplayer2.ExoPlaybackException: Source error, null, null)
E/flutter (25099):
我把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 响应 headers,
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: *
非常感谢有关此的任何帮助。
更新
示例播放列表 - https://drive.google.com/drive/folders/1Q6MJNy5HT-wlMqAUmpFBvmHaCumW73Xz?usp=sharing
用于编码的 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 演示应用程序复制)
#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,
...
这个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 文件? (密钥是正确的,因为这适用于 HLS 网络播放器)
您的密钥文件无效,您在解密 TS 段时得到垃圾。 hls_key_info_file
的 FFmpeg 文档说:
The key file is read as a single packed array of 16 octets in binary format
您的密钥文件有 32 个字节。如果您获取当前密钥文件的前 16 个字节并以二进制形式输出它们,它将正确解密。示例:
xxd -p -l 16 video.key | xxd -r -p - video_bin.key
改为在您的播放列表中使用 video_bin.key
。当然最好先生成一个有效的密钥。
我有一个 AES 128 位加密的 m3u8 播放列表。我试着在
上主持这个- Google 通过 Cloudflare 的云存储桶
- 本地Xampp服务器
播放列表适用于 HTML5 网络播放器。然后我尝试在 Android 应用程序中播放 m3u8 文件。我试过了,一个 Flutter 应用程序,一个 React Native 应用程序和一个 native Java 应用程序
我已经尝试了几乎所有适用于 Flutter 和 React Native 的 HLS 库。但最后每个玩家都显示关于 Google ExoPlayer 的相同错误。我试图解决这个问题已经将近一个月了。我已经检查了大多数 Github 问题,但没有运气。
这是我看到的错误(从 Flutter 终端复制,但 RN 和本机 java 应用程序也显示相同的错误)
Restarted application in 4,802ms.
I/ExoPlayerImpl(25099): Release 2354449 [ExoPlayerLib/2.13.1] [m21, SM-M215F, samsung, 30] [goog.exo.core, goog.exo.hls]
I/ExoPlayerImpl(25099): Init 91918d7 [ExoPlayerLib/2.13.1] [m21, SM-M215F, samsung, 30]
6
I/System.out(25099): (HTTPLog)-Static: isSBSettingEnabled false
E/ExoPlayerImplInternal(25099): Playback error
E/ExoPlayerImplInternal(25099): com.google.android.exoplayer2.ExoPlaybackException: Source error
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:579)
E/ExoPlayerImplInternal(25099): at android.os.Handler.dispatchMessage(Handler.java:102)
E/ExoPlayerImplInternal(25099): at android.os.Looper.loop(Looper.java:246)
E/ExoPlayerImplInternal(25099): at android.os.HandlerThread.run(HandlerThread.java:67)
E/ExoPlayerImplInternal(25099): Caused by: com.google.android.exoplayer2.ParserException: Cannot find sync byte. Most likely not a Transport Stream.
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.extractor.ts.TsExtractor.findEndOfFirstTsPacketInBuffer(TsExtractor.java:453)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.extractor.ts.TsExtractor.read(TsExtractor.java:320)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.source.hls.BundledHlsMediaChunkExtractor.read(BundledHlsMediaChunkExtractor.java:67)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.source.hls.HlsMediaChunk.feedDataToExtractor(HlsMediaChunk.java:434)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.source.hls.HlsMediaChunk.loadMedia(HlsMediaChunk.java:404)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.source.hls.HlsMediaChunk.load(HlsMediaChunk.java:355)
E/ExoPlayerImplInternal(25099): at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:415)
E/ExoPlayerImplInternal(25099): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/ExoPlayerImplInternal(25099): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/ExoPlayerImplInternal(25099): at java.lang.Thread.run(Thread.java:923)
E/flutter (25099): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: PlatformException(VideoError, Video player had error com.google.android.exoplayer2.ExoPlaybackException: Source error, null, null)
E/flutter (25099):
我把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 响应 headers,
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: *
非常感谢有关此的任何帮助。
更新
示例播放列表 - https://drive.google.com/drive/folders/1Q6MJNy5HT-wlMqAUmpFBvmHaCumW73Xz?usp=sharing
用于编码的 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 演示应用程序复制)
#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,
...
这个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 文件? (密钥是正确的,因为这适用于 HLS 网络播放器)
您的密钥文件无效,您在解密 TS 段时得到垃圾。 hls_key_info_file
的 FFmpeg 文档说:
The key file is read as a single packed array of 16 octets in binary format
您的密钥文件有 32 个字节。如果您获取当前密钥文件的前 16 个字节并以二进制形式输出它们,它将正确解密。示例:
xxd -p -l 16 video.key | xxd -r -p - video_bin.key
改为在您的播放列表中使用 video_bin.key
。当然最好先生成一个有效的密钥。