如何解决在 ExoPlayer2
我有一个 AES 128 位加密的 m3u8 播放列表。我试图在,
- 通过 Cloudflare 实现的 Google Cloud Storage Bucket
- 本地 Xampp 服务器
播放列表适用于 HTML5 网络播放器。然后我尝试在 Android 应用程序中播放 m3u8 文件。我尝试过,一个 Flutter 应用、一个 React 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,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响应头,
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 年 4 月 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 文档说:
密钥文件被读取为二进制格式的 16 个八位字节的单个打包数组
您的密钥文件有 32 个字节。如果您获取当前密钥文件的前 16 个字节并以二进制形式输出它们,它将正确解密。示例:
xxd -p -l 16 video.key | xxd -r -p - video_bin.key
改用播放列表中的 video_bin.key
。当然最好先生成一个有效的密钥。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。