Reputation: 3001
I have a AES 128bit encrypted m3u8 playlist. I tried to host this on,
The playlist works on HTML5 web player. Then I tried to play the m3u8 file in a Android app. I tried, A Flutter app, A React Native app and a native Java app
I have tried almost all the HLS libraries available for Flutter and React Native. But at the end every players shows the same error Regarding Google ExoPlayer. I'm trying to fix this for almost a month now. I have checked most of the Github issues but no luck.
This is the error I see ( Copied from the Flutter terminal but same error shows for RN and native java app, too)
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):
I'll also add the content of the m3u8 file,
#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
I see that the issue is with TS
files since the error message show the error in TsExtractor.java
file. Here I also tried to see the HTTP response headers of one of the TS file with curl,
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: *
Any help regarding this really appreciate.
UPDATE
Sample playlist - https://drive.google.com/drive/folders/1Q6MJNy5HT-wlMqAUmpFBvmHaCumW73Xz?usp=sharing
The FFMPEG command used for encode
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
UPDATE - 2021-04-25
Here is a working m3u8 file, ( Copied from ExoPlayer demo app )
#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,
...
Comparing this m3u8 file with the above one, the only difference is the file with the error has a aes-128 bit encrypted playlist.
Then, when I check the source code, I found this method on ExoPlayer source code,
/**
* 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;
}
According to the comments of the above function, the above error message is thrown when it can't find the sync byte. So the only thing I can assume is maybe the player failed to decrypted the first TS file with the provided key ? ( The key is correct as this works on HLS web players )
Upvotes: 1
Views: 1463
Reputation: 31209
Your key file is invalid and you get garbage when decrypting the TS segments. The FFmpeg documentation for hls_key_info_file
says:
The key file is read as a single packed array of 16 octets in binary format
Your key file has 32 bytes. If you take the first 16 bytes of your current key file and output them in binary it will decrypt correctly. Example:
xxd -p -l 16 video.key | xxd -r -p - video_bin.key
Use video_bin.key
in your playlist instead. Of course it would be better to generate a valid key in the first place.
Upvotes: 2