video-dev / hls.js

HLS.js is a JavaScript library that plays HLS in browsers with support for MSE.
https://hlsjs.video-dev.org/demo
Other
14.68k stars 2.56k forks source link

Since 1.4.2, when playing FMP4 files, level switching occurred when the network condition was bad. but playback was not possible. #5756

Closed kakao-wise-kim closed 1 year ago

kakao-wise-kim commented 1 year ago

What version of Hls.js are you using?

1.4.2 to 1.4.10

What browser (including version) are you using?

chrome 116.0.5845.96

What OS (including version) are you using?

mac os 13.4.1

Test stream

If you need a link, please leave your email address.

Configuration

{
  "debug": true,
  "enableWorker": true,
  "lowLatencyMode": true,
  "backBufferLength": 90,
  "liveSyncDuration": 4
}

Additional player setup steps

I can see this in the hls.js demo, so there's nothing to set up.

Checklist

Steps to reproduce

I can reproduce the symptom when playing adaptive live videos with audio and video separated (m4s files) and multiple quality.

Step1 - Run the live video in a normal environment. Play for a sufficiently long time (enough time for a file of more than 9 segments to be downloaded and played). Step2 - After a period of time, set a random delay on network. I usually use the Network Conditions option in Chrome browser. Step3 - Limit the Network Condition until you encounter "Type: 'networkError', details: 'fragLoadError'" or "type: 'mediaError', details: 'bufferStalledError'" Buffer as an error. (Repeat the normal and limited settings, but don't do it too fast)

Expected behaviour

The video should switch to a lower quality and play.

What actually happened?

In this case, Video File tries to download a file in normal time, but Audio File tries to download a old time file. As a result, playback stops, and if you try to retry playback, it doesn't work. Therefore, the video buffer will continue to fill first. The Audio buffer is filled, but the time difference with the Video buffer is very large. Or, Audio buffer is not filled. Sometimes It try to download audio files from a long time ago. If the file doesn't exist due to a CDN TTL timeout, you'll get a 404.

image

Console output

If you give me your email address, I'll forward the log to a file.

Chrome media internals output

Timestamp   Property    Value
0:00:00 created "2023-08-22 00:17:15.559 UTC"
0:00:00 origin_url  "https://hlsjs.video-dev.org/"
0:00:00 kFrameUrl   "https://hlsjs.video-dev.org/demo/?src=https%3A%2F%2Fsbox-live-orisa-noacl.play.kakao.com%2Fsld3phtgn4ufqwrt002vtlsxm%2Fmain%2Fadaptive.m3u8&demoConfig=eyJlbmFibGVTdHJlYW1pbmciOnRydWUsImF1dG9SZWNvdmVyRXJyb3IiOnRydWUsInN0b3BPblN0YWxsIjpmYWxzZSwiZHVtcGZNUDQiOmZhbHNlLCJsZXZlbENhcHBpbmciOi0xLCJsaW1pdE1ldHJpY3MiOi0xfQ=="
0:00:00 kFrameTitle "hls.js demo"
0:00:00 url "blob:https://hlsjs.video-dev.org/81f016ae-dcef-459c-a318-9289092bdc8e"
0:00:00 kTextTracks [{"kind":"Metadata","label":"id3","language":""}]
0:00:00 info    "ChunkDemuxer"
0:00:00 kRendererName   "RendererImpl"
0:00:00 pipeline_state  "kStarting"
0:00:01 kVideoTracks    [{"alpha mode":"is_opaque","codec":"h264","coded size":"1280x720","color space":{"matrix":"BT709","primaries":"BT709","range":"LIMITED","transfer":"BT709"},"encryption scheme":"Unencrypted","has extra data":false,"hdr metadata":"unset","natural size":"1280x720","orientation":"0°","profile":"h264 high","visible rect":"0,0 1280x720"}]
0:00:01 kAudioTracks    [{"bytes per channel":4,"bytes per frame":8,"channel layout":"STEREO","channels":2,"codec":"aac","codec delay":0,"discard decoder delay":false,"encryption scheme":"Unencrypted","has extra data":false,"profile":"unknown","sample format":"Signed 32-bit","samples per second":44100,"seek preroll":"0us"}]
0:00:01 kIsAudioDecryptingDemuxerStream FALSE
0:00:01 kAudioDecoderName   "FFmpegAudioDecoder"
0:00:01 kIsPlatformAudioDecoder FALSE
0:00:01 info    "Selected FFmpegAudioDecoder for audio decoding, config: codec: aac, profile: unknown, bytes_per_channel: 4, channel_layout: STEREO, channels: 2, samples_per_second: 44100, sample_format: Signed 32-bit, bytes_per_frame: 8, seek_preroll: 0us, codec_delay: 0, has extra data: false, encryption scheme: Unencrypted, discard decoder delay: false, target_output_channel_layout: STEREO, target_output_sample_format: Unknown sample format, has aac extra data: true"
0:00:01 debug   "Video rendering in low delay mode."
0:00:01 info    "Cannot select DecryptingVideoDecoder for video decoding"
0:00:01 kIsVideoDecryptingDemuxerStream FALSE
0:00:01 kVideoDecoderName   "VDAVideoDecoder"
0:00:01 kIsPlatformVideoDecoder TRUE
0:00:01 info    "Selected VDAVideoDecoder for video decoding, config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [1280,720], visible rect: [0,0,1280,720], natural size: [1280,720], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}"
0:00:01 pipeline_state  "kPlaying"
0:00:01 seek_target 6.001399
0:00:01 pipeline_state  "kSeeking"
0:00:01 pipeline_state  "kPlaying"
0:00:01 dimensions  "1280x720"
0:00:01 kResolution "1280x720"
0:00:01 info    "Effective playback rate changed from 0 to 1"
0:00:01 event   "kPlay"
0:00:01 duration    12
0:00:01 pipeline_buffering_state    {"for_suspended_start":false,"state":"BUFFERING_HAVE_ENOUGH"}
0:00:03 info    "video decoder config changed midstream, new config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [1920,1080], visible rect: [0,0,1920,1080], natural size: [1920,1080], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}"
0:00:03 kIsVideoDecryptingDemuxerStream FALSE
0:00:03 kVideoDecoderName   "VDAVideoDecoder"
0:00:03 kIsPlatformVideoDecoder TRUE
0:00:03 info    "Selected VDAVideoDecoder for video decoding, config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [1920,1080], visible rect: [0,0,1920,1080], natural size: [1920,1080], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}"
0:00:03 dimensions  "1920x1080"
0:00:03 kResolution "1920x1080"
0:00:03 duration    14
0:00:05 duration    16
0:00:07 duration    18
0:00:09 duration    20
0:00:11 duration    22
0:00:13 duration    24
0:00:15 duration    26
0:00:17 duration    28
0:00:19 duration    30
0:00:21 duration    32
0:00:23 video_buffering_state   {"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:00:23 duration    34
0:00:25 info    "video decoder config changed midstream, new config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [1280,720], visible rect: [0,0,1280,720], natural size: [1280,720], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}"
0:00:25 kIsVideoDecryptingDemuxerStream FALSE
0:00:25 kVideoDecoderName   "VDAVideoDecoder"
0:00:25 kIsPlatformVideoDecoder TRUE
0:00:25 info    "Selected VDAVideoDecoder for video decoding, config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [1280,720], visible rect: [0,0,1280,720], natural size: [1280,720], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}"
0:00:25 dimensions  "1280x720"
0:00:25 kResolution "1280x720"
0:00:25 duration    36
0:00:26 pipeline_buffering_state    {"for_suspended_start":false,"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:00:27 duration    38
0:00:29 duration    40
0:00:30 pipeline_buffering_state    {"for_suspended_start":false,"state":"BUFFERING_HAVE_ENOUGH"}
0:00:31 video_buffering_state   {"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:00:31 duration    42
0:00:33 duration    44
0:00:34 pipeline_buffering_state    {"for_suspended_start":false,"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:00:35 seek_target 42.0101
0:00:35 pipeline_state  "kSeeking"
0:00:35 duration    46
0:00:35 audio_buffering_state   {"state":"BUFFERING_HAVE_NOTHING"}
0:00:37 duration    48
0:00:39 duration    50
0:00:41 pipeline_state  "kPlaying"
0:00:41 duration    52
0:00:41 pipeline_buffering_state    {"for_suspended_start":false,"state":"BUFFERING_HAVE_ENOUGH"}
0:00:43 duration    54
0:00:43 video_buffering_state   {"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:00:45 duration    56
0:00:46 pipeline_buffering_state    {"for_suspended_start":false,"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:00:47 seek_target 54.000399
0:00:47 pipeline_state  "kSeeking"
0:00:47 duration    58
0:00:47 audio_buffering_state   {"state":"BUFFERING_HAVE_NOTHING"}
0:00:49 duration    60
0:00:51 duration    62
0:00:52 pipeline_state  "kPlaying"
0:00:52 pipeline_buffering_state    {"for_suspended_start":false,"state":"BUFFERING_HAVE_ENOUGH"}
0:00:53 duration    64
0:00:54 video_buffering_state   {"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:00:55 duration    66
0:00:57 pipeline_buffering_state    {"for_suspended_start":false,"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:00:57 duration    68
0:00:59 seek_target 66.0013
0:00:59 pipeline_state  "kSeeking"
0:00:59 duration    70
0:00:59 audio_buffering_state   {"state":"BUFFERING_HAVE_NOTHING"}
0:01:01 duration    72
0:01:03 duration    74
0:01:05 pipeline_state  "kPlaying"
0:01:05 debug   "Audio buffer splice at PTS=7987680us. Trimmed tail of overlapped buffer (PTS=7967708us) by 3248us."
0:01:05 duration    76
0:01:05 pipeline_buffering_state    {"for_suspended_start":false,"state":"BUFFERING_HAVE_ENOUGH"}
0:01:07 info    "video decoder config changed midstream, new config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [428,240], visible rect: [0,0,428,240], natural size: [428,241], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}"
0:01:07 kIsVideoDecryptingDemuxerStream FALSE
0:01:07 kVideoDecoderName   "VDAVideoDecoder"
0:01:07 kIsPlatformVideoDecoder TRUE
0:01:07 info    "Selected VDAVideoDecoder for video decoding, config: codec: h264, profile: h264 high, level: not available, alpha_mode: is_opaque, coded size: [428,240], visible rect: [0,0,428,240], natural size: [428,241], has extra data: false, encryption scheme: Unencrypted, rotation: 0°, flipped: 0, color space: {primaries:BT709, transfer:BT709, matrix:BT709, range:LIMITED}"
0:01:07 pipeline_buffering_state    {"for_suspended_start":false,"reason":"DEMUXER_UNDERFLOW","state":"BUFFERING_HAVE_NOTHING"}
0:01:07 duration    77.983333
0:01:09 seek_target 74.984732
0:01:09 pipeline_state  "kSeeking"
0:01:09 duration    79.983333
0:01:09 video_buffering_state   {"state":"BUFFERING_HAVE_NOTHING"}
0:01:11 duration    81.983332
0:01:11 duration    81.983333
0:01:13 duration    83.983332
0:01:13 duration    83.983333
0:01:15 seek_target 80.985133
0:01:15 pipeline_state  "kPlaying"
0:01:15 pipeline_state  "kSeeking"
0:01:15 duration    85.983332
0:01:15 duration    85.983333
0:01:17 duration    87.983332
0:01:17 duration    87.983333
0:01:19 duration    89.983332
0:01:19 duration    89.983333
0:01:21 seek_target 86.984633
0:01:21 pipeline_state  "kPlaying"
0:01:21 pipeline_state  "kSeeking"
0:01:21 duration    91.983332
0:01:21 duration    91.983333
0:01:23 duration    93.983332
0:01:24 duration    94
0:01:25 duration    95.983332
0:01:25 duration    95.983333
0:01:27 seek_target 92.984332
0:01:27 pipeline_state  "kPlaying"
0:01:27 pipeline_state  "kSeeking"
0:01:27 duration    97.983332
0:01:27 duration    97.983333
0:01:29 duration    99.983333
0:01:31 duration    101.983332
0:01:31 duration    101.983333
0:01:33 seek_target 98.983633
0:01:33 pipeline_state  "kPlaying"
0:01:33 pipeline_state  "kSeeking"
0:01:33 duration    103.983332
0:01:34 duration    104
0:01:35 duration    106
0:01:37 duration    108
0:01:39 seek_target 105.0013
0:01:39 pipeline_state  "kPlaying"
0:01:39 pipeline_state  "kSeeking"
0:01:39 duration    110
0:01:41 duration    112
0:01:43 duration    114
0:01:45 seek_target 111.0005
0:01:45 pipeline_state  "kPlaying"
0:01:45 pipeline_state  "kSeeking"
0:01:45 duration    116
0:01:47 duration    117.983332
0:01:48 duration    118
0:01:49 duration    120
0:01:51 seek_target 117.0003
0:01:51 pipeline_state  "kPlaying"
0:01:51 pipeline_state  "kSeeking"
0:01:51 duration    122
0:01:53 duration    124
0:01:55 duration    126
0:01:57 seek_target 123.001399
0:01:57 pipeline_state  "kPlaying"
0:01:57 pipeline_state  "kSeeking"
0:01:57 duration    128
0:01:59 duration    130
0:02:01 duration    132
0:02:03 seek_target 129.012899
0:02:03 pipeline_state  "kPlaying"
0:02:03 pipeline_state  "kSeeking"
0:02:03 duration    134
0:02:05 duration    136
0:02:07 duration    138
0:02:09 seek_target 135.000399
0:02:09 pipeline_state  "kPlaying"
0:02:09 pipeline_state  "kSeeking"
0:02:09 duration    140
0:02:11 duration    142
0:02:13 duration    144
0:02:15 seek_target 141.0013
0:02:15 pipeline_state  "kPlaying"
0:02:15 pipeline_state  "kSeeking"
0:02:15 duration    146
0:02:17 duration    148
0:02:19 duration    150
0:02:21 seek_target 147.001399
0:02:21 pipeline_state  "kPlaying"
0:02:21 pipeline_state  "kSeeking"
0:02:21 duration    152
0:02:23 duration    154
0:02:25 duration    156
0:02:27 seek_target 153.001399
0:02:27 pipeline_state  "kPlaying"
0:02:27 pipeline_state  "kSeeking"
0:02:27 duration    158
0:02:29 duration    160
0:02:31 duration    162
0:02:33 seek_target 159.0003
0:02:33 pipeline_state  "kPlaying"
0:02:33 pipeline_state  "kSeeking"
0:02:33 duration    164
0:02:35 duration    166
0:02:37 duration    168
0:02:39 seek_target 165.0013
0:02:39 pipeline_state  "kPlaying"
0:02:39 pipeline_state  "kSeeking"
0:02:39 duration    170
0:02:41 duration    172
0:02:43 duration    174
0:02:45 seek_target 171.016399
0:02:45 pipeline_state  "kPlaying"
0:02:45 pipeline_state  "kSeeking"
0:02:45 duration    176
0:02:47 duration    178
0:02:49 duration    180
0:02:51 seek_target 177.001399
0:02:51 pipeline_state  "kPlaying"
0:02:51 pipeline_state  "kSeeking"
0:02:51 duration    182
0:02:53 duration    184
0:02:55 duration    186
0:02:57 seek_target 183.0043
0:02:57 pipeline_state  "kPlaying"
0:02:57 pipeline_state  "kSeeking"
0:02:57 duration    188
0:02:59 duration    190
0:03:01 duration    192
0:03:03 seek_target 189.0015
0:03:03 pipeline_state  "kPlaying"
0:03:03 pipeline_state  "kSeeking"
0:03:03 duration    194
0:03:05 duration    196
0:03:07 duration    198
0:03:09 seek_target 195.0013
0:03:09 pipeline_state  "kPlaying"
0:03:09 pipeline_state  "kSeeking"
0:03:09 duration    200
0:03:11 duration    202
0:03:13 duration    204
0:03:15 seek_target 201.000399
0:03:15 pipeline_state  "kPlaying"
0:03:15 pipeline_state  "kSeeking"
0:03:15 duration    206
0:03:17 duration    208
kakao-wise-kim commented 1 year ago

The most recent main branch does not reproduce this behavior. There are a few PRs related to the audio buffer that have been addressed, but it looks like these will be released to the new 1.5 version. Hopefully we'll see a release in the near future.

robwalch commented 1 year ago

Could this gave been fixed by #5742?

kakao-wise-kim commented 1 year ago

@robwalch

What version should I use to use the version with #5742 applied? This PR looks like it's supposed to apply to 1.5. Has it been applied to 1.4.11-Canary-9471 by any chance? I still have the same symptoms in 1.4.11-Canary.

The reason I ask is that I'm using hls.js from npm and combine it with external modules, so it's very easy to test im my test environments if you know the version.

robwalch commented 1 year ago

Hi @kakao-wise-kim,

Yes, dev and the latest canary has it:

https://0f22780e.hls-js-dev.pages.dev/demo/ (v1.4.11-0.canary.9471)

kakao-wise-kim commented 1 year ago

Hi @robwatch

This version also made a similar action. But It has not same symptoms with a version 1.4.2 to 1.4.10. This version has try to download normal time audio segments when a network throttling situation.

But, video segments download action was not normal. It was try to download video segments what a high level. And, It was canceled. and seeked playback timeline. And try downed level video segment downloading again. But It also maybe high level on a throttle network states. When this process is repeated, playback stops and only video segments are downloaded in the background.

After playback stops, playback will not start even if you click the Play button. It have to refresh to get back to normal.

image

robwalch commented 1 year ago

What kind of throttling are you performing exactly?

kakao-wise-kim commented 1 year ago

@robwalch Use the Network Condition tool that exists in chrome development tools. The live stream consists of 1 audio and 5 video levels. Most of the issues occur when throttling occurs sharply. For example, when I set the Network Condition from No Throttling to 750k.

kakao-wise-kim commented 1 year ago

@robwalch This afternoon I ran more tests and saw the same symptoms as before.

From my analysis, the symptoms are as follows

I increased the TTL of the CDN to avoid the 404, and here are the results of my tests.

The time of requested for audio download here varied from case to case: 10 seconds, 20 seconds, 30 seconds, over a minute, etc.

I have a question here.

robwalch commented 1 year ago

The most recent main branch does not reproduce this behavior. There are a few PRs related to the audio buffer that have been addressed, but it looks like these will be released to the new 1.5 version. Hopefully we'll see a release in the near future.

v1.5.0 will be in beta in the coming weeks, with a stable release some time in September.

Why does it only download the video segment first, after a certain amount of buffering, even though it is live?

Audio segment loading is handled by the audio-stream-controller doTickIdle() method. Audio segments should be loaded along side video segments (from media `currentTime), but they should not be loaded more than one segment or target duration past the end the video buffer.

Is it possible to set the number of video segments or time to download first? It only downloads video segments up to a minute or more. I'd like to limit the amount of time that only videos are downloaded when buffering etc. occurs, is there an option I can set?

HLS.js fills the main (video) forward buffer to at least maxBufferLength and idles only after maxMaxBufferLength or maxBufferSize has been reached (whichever comes first).

The audio buffer should follow the end of the video buffer +1 segment within the same discontinuity sequence.

Version 1.4.1 downloads audio and video together, are you thinking of changing this to be the same?

All versions should perform this way. It is still unclear why that is not the case with your live stream - but only for certain versions from your perspective. If this is fixed in dev, then the issue is resolved.

Looking at your screenshots, the issue is with your audio playlist not updating. If your audio playlist is behind, there is no way for the player to load segments it does not know about. This could be an issue with playlist requests timing out, bad target duration values, misaligned media timestamps, or something else - we can't tell without a sample.

I have tested 1.4.10 with live streams that I have access to and throttles down to 750kbps/100ms-rtt with the lowest level also being around 500-600kbps and I could not reproduce what you are describing. The player may have stalled for a couple of segments as the average causes level 1 to be selected (second lowest) before lowest, but audio and video were in lock step.

kakao-wise-kim commented 1 year ago

@robwalch

hello. I made a mistake when I explained about reproduction testing earlier. The audio is not 1 level. It is 3 levels. Video consists of 5 levels.

I found repeated one symptom here. is that if I use only one audio, I won't have the problem. However, if I configure multiple audio levels, the problem is reproduced when switching. This problem is also happening in the recently updated version 1.4.12.

This is our Master Playlist Sample.

#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a1",NAME="a0",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2",LANGUAGE="ko",URI="a0_a1/a1.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a2",NAME="a0",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2",LANGUAGE="ko",URI="a0_a2/a2.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a3",NAME="a0",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2",LANGUAGE="ko",URI="a0_a3/a3.m3u8"

#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=3192000,BANDWIDTH=6384000,CODECS="avc3.640020,mp4a.40.2",RESOLUTION=1280x720,FRAME-RATE=60.000,AUDIO="a1"
v0_v3/v3.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=8192000,BANDWIDTH=16384000,CODECS="avc3.64002a,mp4a.40.2",RESOLUTION=1920x1080,FRAME-RATE=60.000,AUDIO="a1"
v0_v1/v1.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1128000,BANDWIDTH=2256000,CODECS="avc3.64001f,mp4a.40.2",RESOLUTION=854x480,FRAME-RATE=30.000,AUDIO="a2"
v0_v5/v5.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=628000,BANDWIDTH=1256000,CODECS="avc3.64001e,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=30.000,AUDIO="a2"
v0_v7/v7.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=264000,BANDWIDTH=528000,CODECS="avc3.640015,mp4a.40.2",RESOLUTION=428x240,FRAME-RATE=30.000,AUDIO="a3"
v0_v8/v8.m3u8

If a Buffer Stall error occurs while audio level switching is occurring, the switched audio pts are set olded time the video pts. If the audio fragments continue to download and looks similar to the video pts, playback will start.

This symptom does not occur every time. Often they will switch normally. The symptom is mainly reproduced when a buffer stall occurs with level switching, as shown below.

image