November 24, 2020

Apple Low Latency HLS setup in Nimble Streamer

Apple has introduced Low Latency HLS spec back in 2019 and has officially released the final version of the spec in the fall of 2020, along with making the playback available on iOS and Mac.

Nimble Streamer now has full support for Apple LL HLS in addition to "legacy" RFC8216 HLS. You can read about major advantages on LL HLS digest page. This article describes the setup process of Nimble Streamer.

We assume you already have Nimble Streamer installed on one of your servers,or upgraded to the latest version and you have full access there.


You watch a short version of this article in our video tutorial below.

However we highly recommend reading this article to use this feature efficiently.


LL HLS uses HTTP/2 via SSL as a transport protocol. So you need to enable it before performing any further setup.


1. HTTP/2 setup

1.1 First, install the latest Nimble Streamer or upgrade your existing instance. 

1.2 Now set up the SSL certificate for Nimble Streamer. This is an important steps so please make sure SSL is working before moving forward.

1.3 Add ssl_http2_enabled=true parameter into nimble.conf and restart Nimble Streamer. Read parameters reference to find more about config location and restart.
LiteSpeed HPACK library: Nimble Streamer uses LS-HPACK library for encoding and decoding HTTP headers using HPACK compression mechanism. Softvelum team would like to say thank you to LiteSpeed team, we appreciate their effort and technical expertise.
1.4 After that you'll be able to use HTTP/2 to reach live streams with HLS and MPEG-DASH protocols enabled. You can check if Nimble Streamer delivers HTTP/2 by checking access log. Read this article to get familiar with logging. E.g. you can enable logs by using log_access=file parameter. Once you add it and re-start Nimble, you'll be able to view the log. In Ubuntu it's located in /var/log/nimble/access.log by default.
Now when you try to get your regular HLS live stream via https:// via curl or HTTP/2-capable player, you'll get this kind of record in access log:
Dec 24 17:43:09 nimble[5500]: [nimble-access.log] 192.18.1.2 - - "GET /livell/stream/playlist.m3u8 HTTP/2" 200 84 1114 372 "-" "AppleCoreMedia/1.0.0.17C54 (iPhone; U; CPU OS 13_3 like Mac OS X; en_us)"
You can see HTTP/2 there which means it's working. In other cases it will have HTTP/1.1 and this will mean you need to check what's wrong.

If a client tries to access LL-HLS stream via HTTP/1.1, or if HTTP/2 is not properly set up, then the player will fall back to legacy HLS and will not use any advantages of LL-HLS.

2. Live streaming setup


Now you need to set up transmuxing settings via WMSPanel web service. If you are not familiar with live streaming setup of Nimble Streamer, please refer to live streaming digest page, or respective input protocol pages, such as RTMP streaming.

Please make sure you have a correctly set up live stream (like regular-latency HLS, RTMP or SLDP) before trying to use LL HLS.

Once you have a live stream set up in WMSPanel, go to Nimble Streamer top menu and select Live streams settings. You will see Global setting tab for selected server. You may create application-specific setting as well if it's needed for your streaming scenario.


Currently, Nimble Streamer supports all 3 container types available for HLS, you can see their respective check boxes on the screenshot above. Those types have the following meaning for Low Latency HLS case:
  • HLS - this type is recommended for audio-only LL streams. It's optimized for audio delivery and it has a reduced chunk size. This allows significantly saving bandwidth for audio streams. The ID3 tags are also inserted in each audio part.
  • fMP4 (CMAF) - fragmented MP4 for video+audio, audio-only and video-only modes. We highly recommend using this type of container for video+audio and video-only cases as it allows utilizing all advantages of LL HLS.
  • HLS (MPEGTS) - this type supports both video+audio, audio-only and video-only modes, however we do not recommend it for LL HLS. It brings a lot overhead which diminishes all advantages and increases the latency. Also it does not support HEVC unlike fMP4. We've added this type support as a fix for a bug of iOS 12 which implemented previous LL HLS spec. So even though you can use it with LL, Apple does not recommend using it and Nimble Streamer team does not as well.
Once you select either of those containers, WMSPanel will show Enable Apple's Low Latency HLS checkbox and you need to select it. You will then see HLS part duration edit box to define parts' duration in milliseconds.
Please consider the following when choosing parts duration:
  • We recommend using part duration of 2000ms with chunk duration of 6 seconds. The expected latency will be about 6 seconds in this case. This duration provides optimal bandwidth and latency as well as good playback buffer.
  • The minimum recommended part duration of 1000ms. The expected latency will be about 4-5 seconds in this case. This duration gives better latency but less playback buffer and more bandwidth consumption as well as server resources' usage.
  • The smallest duration value allowed by the web UI is 500 ms because it's the minimum that makes sense in terms of low latency delivery. The latency will be around 2 seconds in this case. This duration gives very low playback buffer, severe bandwidth consumption and server resources' usage. Use it only if you have a managed network and latency is crucial for your case.
  • Part size smaller than 500ms does not make any practical sense because of the overhead in delivery and processing chain. Even though Nimble is able to produce smaller parts, we just don't want our customers to deal with potential bottlenecks in other areas.
  • You may align key frame interval with chunk size. E.g. for 6 seconds chunk and 1000ms part duration the valid key frame intervals would be 1, 2, 3 seconds. For same chunk size and part duration of 2000, the valid key frame intervals will be 1, 2, 3, 6 seconds.
  • Key frame interval can be set on your encoder side. Nimble Live Transcoder allows setting it as well.

Once LL HLS is enabled, you need to re-start the input stream so Nimble Streamer could start producing LL HLS output stream.

3. Workflow and playlists


Now as the set up has been made, you can use the player to consume the stream using the usual playback URL. The main playlist will have proper chunklists which will have a content according to LL-HLS spec. Here is an example of such chunk list (obtained via video.m3u8).
   #EXTM3U
    #EXT-X-VERSION:7
    #EXT-X-INDEPENDENT-SEGMENTS
    #EXT-X-MAP:URI="audio.fmp4?nimblesessionid=1"
    #EXT-X-TARGETDURATION:7
    #EXT-X-MEDIA-SEQUENCE:1
    #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=1.536
    #EXT-X-PART-INF:PART-TARGET=0.512
    #EXT-X-PROGRAM-DATE-TIME:2019-12-23T02:29:02.609Z
    #EXTINF:5.995,
    a_6_6016_1.fmp4?nimblesessionid=1
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_0.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_1.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_2.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_3.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_4.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_5.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_6.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_7.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_8.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_9.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_12011_2_10.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.363,URI="a_6_12011_2_11.fmp4?nimblesessionid=1"
    #EXT-X-PROGRAM-DATE-TIME:2019-12-23T02:29:08.604Z
    #EXTINF:5.995,
    a_6_12011_2.fmp4?nimblesessionid=1
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_0.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_1.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_2.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_3.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_4.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_5.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_6.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_7.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_8.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_9.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_18006_3_10.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.362,URI="a_6_18006_3_11.fmp4?nimblesessionid=1"
    #EXT-X-PROGRAM-DATE-TIME:2019-12-23T02:29:14.599Z
    #EXTINF:5.994,
    a_6_18006_3.fmp4?nimblesessionid=1
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_0.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_1.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_2.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_3.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_4.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_5.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_6.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_7.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_8.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_9.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_10.fmp4?nimblesessionid=1"
    #EXT-X-PART:DURATION=0.384,URI="a_6_24000_4_11.fmp4?nimblesessionid=1"
    #EXT-X-PROGRAM-DATE-TIME:2019-12-23T02:29:20.593Z
    #EXTINF:6.016,
    a_6_24000_4.fmp4?nimblesessionid=1 
    #EXT-X-PRELOAD-HINT:TYPE=PART,URI="a_6_30000_5_0.fmp4?nimblesessionid=1"

      

Parts in chunklist. Comparing to regular HLS, you see a lot of lines representing parts like this:
#EXT-X-PART:DURATION=0.512,URI="a_6_24000_4_0.fmp4?nimblesessionid=1"
The full chunk which contain these parts will be described after all parts' lines:
a_6_24000_4.fmp4?nimblesessionid=1
All parts within chunks are numerated starting from zero. So "a_6_18006_3_0.fmp4" mean its the first part of chunk number 3.

Part length. This attribute declares a designated size of upcoming parts:
#EXT-X-PART-INF:PART-TARGET=0.512
In this example it's 512 milliseconds.

Can block reload
. Check this line:
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=1.536
The "CAN-BLOCK-RELOAD" declares that media server allows holding playlist request.

Hold playlist request. LL-HLS allows requesting the server to hold sending out the playlist until a specific chunk and/or part is available for the stream.
So a player may request some part which is going to be available within a few seconds from now, then Nimble Streamer will check if that part is available. Once the requested part is ready, Nimble will return a playlist.

Check this request example:
curl -k "https://localhost:8443/livell/stream/chunks.m3u8?nimblesessionid=1&_HLS_msn=59&_HLS_part=5"
The highlighted _HLS_msn=59 and _HLS_part=5 parameters indicate that the server must hold the request until Nimble Streamer has part number 5 of chunk number 59 or later and then it could return a playlist. You can use only _HLS_msn=59 parameter, in this case the playlist will be sent out only once full chunk is available.

The resulting chunklist will look like this:
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MAP:URI="audio.fmp4?nimblesessionid=1"
#EXT-X-TARGETDURATION:7
#EXT-X-MEDIA-SEQUENCE:55
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=1.536
#EXT-X-PART-INF:PART-TARGET=0.512
#EXT-X-PROGRAM-DATE-TIME:2019-12-23T02:34:26.599Z
#EXTINF:5.994,
a_6_330006_55.fmp4?nimblesessionid=1
#EXT-X-PROGRAM-DATE-TIME:2019-12-23T02:34:32.593Z
#EXTINF:6.016,
a_6_336000_56.fmp4?nimblesessionid=1
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_0.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_1.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_2.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_3.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_4.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_5.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_6.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_7.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_8.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_9.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_342016_57_10.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.363,URI="a_6_342016_57_11.fmp4?nimblesessionid=1"
#EXT-X-PROGRAM-DATE-TIME:2019-12-23T02:34:38.609Z
#EXTINF:5.995,
a_6_342016_57.fmp4?nimblesessionid=1
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_0.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_1.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_2.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_3.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_4.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_5.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_6.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_7.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_8.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_9.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_348011_58_10.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.363,URI="a_6_348011_58_11.fmp4?nimblesessionid=1"
#EXT-X-PROGRAM-DATE-TIME:2019-12-23T02:34:44.604Z
#EXTINF:5.995,
a_6_348011_58.fmp4?nimblesessionid=1

#EXT-X-PART:DURATION=0.512,URI="a_6_354006_59_0.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_354006_59_1.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_354006_59_2.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_354006_59_3.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_354006_59_4.fmp4?nimblesessionid=1"
#EXT-X-PART:DURATION=0.512,URI="a_6_354006_59_5.fmp4?nimblesessionid=1" 
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="
a_6_354006_59_6.fmp4?nimblesessionid=1"

 

You can see it ends with part a_6_354006_59_5.fmp4 - it's part number 5 of the upcoming chunk 59. That chunk will be available only a few seconds later, but the player can already perform the playback, this helps a lot with reducing the latency.

Pre-load hint. Check this line:
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="a_6_354006_59_6.fmp4?nimblesessionid=1"
Here Nimble declares that part 6 of chunk 59 is being created right at this moment. The player may request this part and it will get it as soon as that part is available.

4. Playback


Low Latency HLS is now supported in all Apple devices that are capable of HLS playback. So you can use an iPhone with iOS 14 to play this HLS even in your browser. We also have support for LL HLS in Larix Player for iOS which uses platform playback component.

Also, THEOPlayer has a test page with their implementation of LLHLS which is working in various other browsers on other platforms. You can read configuration guide to see how you can adapt THEO Player to your use case.

Other platforms do not have LL HLS support yet. Once we know of that kind of solutions, we'll mention them here.

5. Known issues and troubleshooting


5.1. Chunk duration and key frame alignment
If the playback has issues, you need to make sure that chunk duration is equal to the duration that you specified for your stream settings.

Let's check a couple of examples.
In this example we set up chunk of 6 seconds with part duration of 1000ms. So you see 6 parts for each chunk, and every chunk has equal duration:
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MAP:URI="video.fmp4?nimblesessionid=249"
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:1296
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3
#EXT-X-PART-INF:PART-TARGET=1
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:39:14.406Z
#EXTINF:6,
v_80_7777992_1296.fmp4?nimblesessionid=249
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:39:20.406Z
#EXTINF:6,
v_80_7783992_1297.fmp4?nimblesessionid=249
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:39:26.406Z
#EXTINF:6,
v_80_7789992_1298.fmp4?nimblesessionid=249
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:39:32.406Z
#EXTINF:6,
v_80_7795992_1299.fmp4?nimblesessionid=249
#EXT-X-PART:DURATION=1,URI="v_80_7801992_1300_0.fmp4?nimblesessionid=249",INDEPENDENT=YES
#EXT-X-PART:DURATION=1,URI="v_80_7801992_1300_1.fmp4?nimblesessionid=249"
#EXT-X-PART:DURATION=1,URI="v_80_7801992_1300_2.fmp4?nimblesessionid=249"
#EXT-X-PART:DURATION=1,URI="v_80_7801992_1300_3.fmp4?nimblesessionid=249",INDEPENDENT=YES
#EXT-X-PART:DURATION=1,URI="v_80_7801992_1300_4.fmp4?nimblesessionid=249"
#EXT-X-PART:DURATION=1,URI="v_80_7801992_1300_5.fmp4?nimblesessionid=249"
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:39:38.406Z
#EXTINF:6,
v_80_7801992_1300.fmp4?nimblesessionid=249
#EXT-X-PART:DURATION=1,URI="v_80_7807992_1301_0.fmp4?nimblesessionid=249",INDEPENDENT=YES
#EXT-X-PART:DURATION=1,URI="v_80_7807992_1301_1.fmp4?nimblesessionid=249"
#EXT-X-PART:DURATION=1,URI="v_80_7807992_1301_2.fmp4?nimblesessionid=249"
#EXT-X-PART:DURATION=1,URI="v_80_7807992_1301_3.fmp4?nimblesessionid=249",INDEPENDENT=YES
#EXT-X-PART:DURATION=1,URI="v_80_7807992_1301_4.fmp4?nimblesessionid=249"
#EXT-X-PART:DURATION=1,URI="v_80_7807992_1301_5.fmp4?nimblesessionid=249"
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:39:44.406Z
#EXTINF:6,
v_80_7807992_1301.fmp4?nimblesessionid=249
#EXT-X-PART:DURATION=1,URI="v_80_7813992_1302_0.fmp4?nimblesessionid=249",INDEPENDENT=YES
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="v_80_7813992_1302_1.fmp4?nimblesessionid=249"

Now check your chunk list. Get your playlist via URL like https://yourdomain.com/test/stream/playlist.m3u8 and then find the chunklist that corresponds to the video, like https://yourdomain.com/test/stream/video.m3u8. You can do that via various tools like "curl".

Check the following real-life example of LLHLS stream.
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MAP:URI="video.fmp4?nimblesessionid=57"
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:9873
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.003
#EXT-X-PART-INF:PART-TARGET=1.001
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:28:36.050Z
#EXTINF:5.338,
v_26_60085735_9873.fmp4?nimblesessionid=57
#EXT-X-PART:DURATION=1.001,URI="v_26_60091073_9874_0.fmp4?nimblesessionid=57",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.001,URI="v_26_60091073_9874_1.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60091073_9874_2.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60091073_9874_3.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=0.333,URI="v_26_60091073_9874_4.fmp4?nimblesessionid=57"
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:28:41.223Z
#EXTINF:4.338,
v_26_60091073_9874.fmp4?nimblesessionid=57
#EXT-X-PART:DURATION=1.001,URI="v_26_60095411_9875_0.fmp4?nimblesessionid=57",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.001,URI="v_26_60095411_9875_1.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60095411_9875_2.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60095411_9875_3.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60095411_9875_4.fmp4?nimblesessionid=57"
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:28:45.740Z
#EXTINF:5.005,
v_26_60095411_9875.fmp4?nimblesessionid=57
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_0.fmp4?nimblesessionid=57",INDEPENDENT=YES
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_1.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_2.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_3.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_4.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_5.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_6.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_7.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_8.fmp4?nimblesessionid=57"
#EXT-X-PART:DURATION=1.001,URI="v_26_60100416_9876_9.fmp4?nimblesessionid=57"
#EXT-X-PROGRAM-DATE-TIME:2020-12-14T01:28:50.745Z
#EXTINF:10.01,
v_26_60100416_9876.fmp4?nimblesessionid=57
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="v_26_60110426_9877_0.fmp4?nimblesessionid=57"
It has chunks of various durations which differ from the duration requested during the setup (you can see 4.338, 5.005 and even 10.01). This may cause some players to work incorrectly.
So your original stream source need to have proper key frames setup in order to produce playlists for smooth playback.

As you can see, LLHLS can be capricious when it comes to key frame alignment. So you have to make sure that your initial source stream is properly encoded and delivered prior to re-packaging into LLHLS. Reliable delivery protocols like SRT are excellent for the vast majority of streaming scenarios but this case may require additional efforts prior to transmuxing.
If you want to produce reliable output for last-mile delivery regardless of your source, you need to consider SLDP low latency protocol. We designed and implemented it having various real-life cases in mind and whatever you bring into Nimble Streamer, it will be properly played in SLDP-powered players.

5.2. Interleaving compensation
If you have video+audio stream you may have issues with playback due to interleaving as described in this article. This kind of issues becomes even more severe in case of low latency playback. In order to fix this you can try enabling interleaving compensation with Min. delay set to zero, see the image below.



Feel free to try Nimble Streamer with Low Latency HLS and let us know if you have any questions.