Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: Measured peak bitrate compared to master playlist declared value exceeds error tolerance #39

Open
samueleastdev opened this issue Jun 3, 2020 · 4 comments

Comments

@samueleastdev
Copy link

Hi @bentasker

Not sure if this is an issue because I get the same error if I test with various streams but curious on why you think this may be occurring.

If you download the hls validation tools from here https://developer.apple.com/download/more/?=http%20live%20streaming%20tools

And then run the mediastreamvalidator tool on a created playlist you should see errors similar to this.

--------------------------------------------------------------------------------
MUST fix issues
--------------------------------------------------------------------------------

Error: Measured peak bitrate compared to master playlist declared value exceeds error tolerance
--> Detail:  Measured: 590.99 kb/s, Master playlist: 256.00 kb/s, Error: 130.86%
--> Source:  mermaid.mov_master.m3u8
--> Compare: mermaid.mov_256.m3u8

--> Detail:  Measured: 395.00 kb/s, Master playlist: 128.00 kb/s, Error: 208.59%
--> Source:  mermaid.mov_master.m3u8
--> Compare: mermaid.mov_128.m3u8

--> Detail:  Measured: 260.27 kb/s, Master playlist: 28.00 kb/s, Error: 829.52%
--> Source:  mermaid.mov_master.m3u8
--> Compare: mermaid.mov_28.m3u8

--> Detail:  Measured: 282.60 kb/s, Master playlist: 64.00 kb/s, Error: 341.55%
--> Source:  mermaid.mov_master.m3u8
--> Compare: mermaid.mov_64.m3u8

Is this something to be concerned about or is it just a guide to follow.

Thanks

@bentasker
Copy link
Owner

Hi,

It seems you've been quite unlucky :)

First, to explain what it's warning about.

In your encode, you've specified 4 bitrates - -b 28,64,128,256. These (obviously) get passed through to ffmpeg and also written into the master playlist.

But, by default ffmpeg will use a Variable Bit-Rate. In effect, it tries to keep the average bitrate to the one you've specified, so there may be spikes above it, and dips below it.

The apple tool checks the maximum bitrate observed within the stream and compares it to that declared in the manifest. As you can see, sometimes it's spiked much higher than that declared.

A bit of variance isn't something to be too concerned about, but significant variance could cause playback issues. Adaptive players will try to calculate the bandwidth available to them and playback the most appropriate stream (actually, most take resolution into consideration too, but lets ignore that).

One problem comes when the player decides there's (say) 80Kbps bandwidth, and selects your 64Kbps representation. Because there are peaks up to 260Kbps (i.e. 3.25x the available bandwidth) playback may stall. Whether it actually does depends on a variety of things (like what the bitrates leading up to that spike were - if you've a long succession of lows followed by a spike, the player may have had time to fetch it ahead of time).

When encoding video, though, most will go with VBR because it traditionally gives higher quality files (being able to switch the bitrate down when "safe" allows for higher spikes later etc etc)

If you run HLS-Stream-Creator with -C it will tell ffmpeg to use CBR instead, which should please the tool.

I've not personally had any delivery issues from using VBR, but as described above it is a technical possibility

@samueleastdev
Copy link
Author

Thanks, @bentasker really appreciate the detailed answer.

I have been testing a few different FFmpeg commands mainly this one.

https://docs.peer5.com/guides/production-ready-hls-vod/

I don't think I was really understanding the relationship between the bitrate in the master manifest and -b:v option in the FFmpeg command.

In the option at the link above the master, manifest bandwidths are.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480
480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
1080p.m3u8

Obviously I understand now that the BANDWIDTH value is set by the -b:v option

ffmpeg -hide_banner -y -i beach.mkv \
  -vf scale=w=640:h=360:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod  -b:v 800k -maxrate 856k -bufsize 1200k -b:a 96k -hls_segment_filename beach/360p_%03d.ts beach/360p.m3u8 \
  -vf scale=w=842:h=480:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 1400k -maxrate 1498k -bufsize 2100k -b:a 128k -hls_segment_filename beach/480p_%03d.ts beach/480p.m3u8 \
  -vf scale=w=1280:h=720:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 2800k -maxrate 2996k -bufsize 4200k -b:a 128k -hls_segment_filename beach/720p_%03d.ts beach/720p.m3u8 \
  -vf scale=w=1920:h=1080:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 5000k -maxrate 5350k -bufsize 7500k -b:a 192k -hls_segment_filename beach/1080p_%03d.ts beach/1080p.m3u8

And your generated manifest is the same.

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=28000
mertime.mp4_28.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=64000
mertime.mp4_64.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=128000
mertime.mp4_128.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=256000
mertime.mp4_256.m3u8

Relating to -b 28,64,128,256

I also use the AWS Media Convert encoder which outputs this in the manifest.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:BANDWIDTH=513609,AVERAGE-BANDWIDTH=498366,CODECS="avc1.4d401f,mp4a.40.5",RESOLUTION=480x270,FRAME-RATE=14.985
master_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_15Hz_400Kbps.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=745121,AVERAGE-BANDWIDTH=718453,CODECS="avc1.4d401f,mp4a.40.5",RESOLUTION=640x360,FRAME-RATE=29.970
master_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_30Hz_600Kbps.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3912327,AVERAGE-BANDWIDTH=3738736,CODECS="avc1.640028,mp4a.40.5",RESOLUTION=960x540,FRAME-RATE=29.970
master_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_30Hz_3500Kbps.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5413455,AVERAGE-BANDWIDTH=5269104,CODECS="avc1.640028,mp4a.40.5",RESOLUTION=1280x720,FRAME-RATE=29.970
master_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_30Hz_5000Kbps.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=9498225,AVERAGE-BANDWIDTH=8904479,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1920x1080,FRAME-RATE=29.970
master_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_30Hz_8500Kbps.m3u8

Not sure how they are grabbing the bandwidth, whether it is done via a two pass as they don't seem to be set values?

@bentasker
Copy link
Owner

Hi,

After generation, they probably check the stream with the equivalent of a ffprobe -i [stream] to extract those details.

If you're so minded, you can get ffprobe to dump details out in JSON to make extracting things a little easier.

I do something similar (though it's on the input rather than the output) here - https://snippets.bentasker.co.uk/page-1804131128-Automating-HLS-Stream-Creator-Python.html

I think, if you wanted to declare AVERAGE-BANDWIDTH and FRAME-RATE in your manifest, you could probably write a little bit of code to iterate over entries in the manifest and probe them

This

for manifest in $MASTER
do

ffprobe -show_streams -print_format json -loglevel quiet "$manifest"
done

Will dump out info, but you're probably best doing the probe (and manifest rewrite) in Python. One thing to watch for is the difference in fields (both names and presence) depending on the codec.

@samueleastdev
Copy link
Author

samueleastdev commented Jun 9, 2020

Hi @bentasker

Yes, that's exactly what I was initially trying to do with ffprobe.

I would get the following output when running on the input file.

{
    "streams": [
        {
            "index": 0,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "High",
            "codec_type": "video",
            "codec_time_base": "1001/48000",
            "codec_tag_string": "avc1",
            "codec_tag": "0x31637661",
            "width": 1920,
            "height": 816,
            "coded_width": 1920,
            "coded_height": 816,
            "has_b_frames": 2,
            "pix_fmt": "yuv420p",
            "level": 40,
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "true",
            "nal_length_size": "4",
            "r_frame_rate": "24000/1001",
            "avg_frame_rate": "24000/1001",
            "time_base": "1/24000",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 4060056,
            "duration": "169.169000",
            "bit_rate": "2317499",
            "bits_per_raw_sample": "8",
            "nb_frames": "4056",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "language": "eng",
                "handler_name": "Apple Video Media Handler"
            }
        },
        {
            "index": 1,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_time_base": "1/44100",
            "codec_tag_string": "mp4a",
            "codec_tag": "0x6134706d",
            "sample_fmt": "fltp",
            "sample_rate": "44100",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/44100",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 7460838,
            "duration": "169.180000",
            "bit_rate": "127635",
            "max_bit_rate": "128000",
            "nb_frames": "7287",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "language": "eng",
                "handler_name": "Apple Sound Media Handler"
            }
        }
    ],
    "format": {
        "filename": "mertime.mp4",
        "nb_streams": 2,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "169.204000",
        "size": "51835599",
        "bit_rate": "2450797",
        "probe_score": 100,
        "tags": {
            "major_brand": "isom",
            "minor_version": "512",
            "compatible_brands": "isomiso2avc1mp41",
            "encoder": "Lavf58.34.101"
        }
    }
}

This outputs everything needed but the bit_rate is (2317499) obviously a single value for the input video I wasn't sure how to work out the BANDWIDTH master.m3u8 manifest values for the different resolutions 360p, 480p, 720p, 1080p.

Also if I run ffprobe in the master.m3u8 file after encoding to hls I get this.

{
    "streams": [{
            "index": 0,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "Main",
            "codec_type": "video",
            "codec_time_base": "1001/48000",
            "codec_tag_string": "[27][0][0][0]",
            "codec_tag": "0x001b",
            "width": 640,
            "height": 272,
            "coded_width": 640,
            "coded_height": 272,
            "has_b_frames": 2,
            "pix_fmt": "yuv420p",
            "level": 21,
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "false",
            "nal_length_size": "0",
            "r_frame_rate": "24000/1001",
            "avg_frame_rate": "24000/1001",
            "time_base": "1/90000",
            "start_pts": 7508,
            "start_time": "0.083422",
            "bits_per_raw_sample": "8",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "variant_bitrate": "800000"
            }
        },
        {
            "index": 1,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_time_base": "1/48000",
            "codec_tag_string": "[15][0][0][0]",
            "codec_tag": "0x000f",
            "sample_fmt": "fltp",
            "sample_rate": "48000",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/90000",
            "start_pts": 5588,
            "start_time": "0.062089",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "variant_bitrate": "800000"
            }
        },
        {
            "index": 2,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "Main",
            "codec_type": "video",
            "codec_time_base": "1001/48000",
            "codec_tag_string": "[27][0][0][0]",
            "codec_tag": "0x001b",
            "width": 842,
            "height": 358,
            "coded_width": 848,
            "coded_height": 368,
            "has_b_frames": 2,
            "pix_fmt": "yuv420p",
            "level": 30,
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "false",
            "nal_length_size": "0",
            "r_frame_rate": "24000/1001",
            "avg_frame_rate": "24000/1001",
            "time_base": "1/90000",
            "start_pts": 7508,
            "start_time": "0.083422",
            "bits_per_raw_sample": "8",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "variant_bitrate": "1400000"
            }
        },
        {
            "index": 3,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_time_base": "1/48000",
            "codec_tag_string": "[15][0][0][0]",
            "codec_tag": "0x000f",
            "sample_fmt": "fltp",
            "sample_rate": "48000",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/90000",
            "start_pts": 5588,
            "start_time": "0.062089",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "variant_bitrate": "1400000"
            }
        },
        {
            "index": 4,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "Main",
            "codec_type": "video",
            "codec_time_base": "1001/48000",
            "codec_tag_string": "[27][0][0][0]",
            "codec_tag": "0x001b",
            "width": 1280,
            "height": 544,
            "coded_width": 1280,
            "coded_height": 544,
            "has_b_frames": 2,
            "pix_fmt": "yuv420p",
            "level": 31,
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "false",
            "nal_length_size": "0",
            "r_frame_rate": "24000/1001",
            "avg_frame_rate": "24000/1001",
            "time_base": "1/90000",
            "start_pts": 7508,
            "start_time": "0.083422",
            "bits_per_raw_sample": "8",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "variant_bitrate": "2800000"
            }
        },
        {
            "index": 5,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_time_base": "1/48000",
            "codec_tag_string": "[15][0][0][0]",
            "codec_tag": "0x000f",
            "sample_fmt": "fltp",
            "sample_rate": "48000",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/90000",
            "start_pts": 5588,
            "start_time": "0.062089",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "variant_bitrate": "2800000"
            }
        },
        {
            "index": 6,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "Main",
            "codec_type": "video",
            "codec_time_base": "1001/48000",
            "codec_tag_string": "[27][0][0][0]",
            "codec_tag": "0x001b",
            "width": 1920,
            "height": 816,
            "coded_width": 1920,
            "coded_height": 816,
            "has_b_frames": 2,
            "pix_fmt": "yuv420p",
            "level": 40,
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "false",
            "nal_length_size": "0",
            "r_frame_rate": "24000/1001",
            "avg_frame_rate": "24000/1001",
            "time_base": "1/90000",
            "start_pts": 7508,
            "start_time": "0.083422",
            "bits_per_raw_sample": "8",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "variant_bitrate": "5000000"
            }
        },
        {
            "index": 7,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_time_base": "1/48000",
            "codec_tag_string": "[15][0][0][0]",
            "codec_tag": "0x000f",
            "sample_fmt": "fltp",
            "sample_rate": "48000",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/90000",
            "start_pts": 5588,
            "start_time": "0.062089",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "variant_bitrate": "5000000"
            }
        }
    ],
    "format": {
        "filename": "master.m3u8",
        "nb_streams": 8,
        "nb_programs": 4,
        "format_name": "hls",
        "format_long_name": "Apple HTTP Live Streaming",
        "start_time": "0.062089",
        "duration": "169.180238",
        "size": "575",
        "bit_rate": "27",
        "probe_score": 100
    }
}

And the validator output is.

Error: Measured peak bitrate compared to master playlist declared value exceeds error tolerance
--> Detail:  Measured: 5939.35 kb/s, Master playlist: 5000.00 kb/s, Error: 18.79%
--> Source:  https://d25hd5yfabpc2n.cloudfront.net/s3test1/master.m3u8
--> Compare: 1080/1080p.m3u8

--> Detail:  Measured: 1694.70 kb/s, Master playlist: 1400.00 kb/s, Error: 21.05%
--> Source:  https://d25hd5yfabpc2n.cloudfront.net/s3test1/master.m3u8
--> Compare: 480/480p.m3u8

--> Detail:  Measured: 3192.97 kb/s, Master playlist: 2800.00 kb/s, Error: 14.03%
--> Source:  https://d25hd5yfabpc2n.cloudfront.net/s3test1/master.m3u8
--> Compare: 720/720p.m3u8

--> Detail:  Measured: 1066.33 kb/s, Master playlist: 800.00 kb/s, Error: 33.29%
--> Source:  https://d25hd5yfabpc2n.cloudfront.net/s3test1/master.m3u8
--> Compare: 360/360p.m3u8

I can't figure out where the validator is getting its bitrate values from.

--> Detail:  Measured: 5939.35 kb/s, Master playlist: 5000.00 kb/s, Error: 18.79%

But maybe this isn't something to worry about.

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants