New issue
Advanced search Search tips

Issue 900426 link

Starred by 2 users

Issue metadata

Status: Assigned
Owner:
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: ----
Pri: 3
Type: Bug



Sign in to add a comment

CHUNK_DEMUXER_ERROR_APPEND_FAILED when playing HLS video with low MSE buffer limit

Reported by ch...@amazon.com, Oct 31

Issue description

Chrome Version       : Unsure if it affects other versions but I tested on 69 and 70
URLs (if applicable) :
  * https://beaufortfrancois.github.io/sandbox/media/source-buffer-limit.html
  * https://video-dev.github.io/hls.js/stable/demo/
  * Longer than 5 minute videos on: https://www.cnbc.com/video/
    * https://www.cnbc.com/video/2018/07/02/dell-ceo-michael-dell-on-the-decision-to-go-public.html
  * Suspecting other HLS stream
 
Other browsers tested: 
  Add OK or FAIL, along with the version, after other browsers where you
have tested this issue:
     Safari: Didn't Test
    Firefox: OK
       Edge: Didn't Test
 
Device requirements (Only one condition needs to be met):
  * Here I believe the crucial thing is to trigger a mse-video-buffer-size-limit-mb to anything less than ~66
  * --enable-low-end-device-mode=1 or < 512MB memory
  * Any device with less than 512MB memory.
  * Android Go devices (assuming they are 512mb
  * Setting --mse-video-buffer-size-limit-mb=30
 
What steps will reproduce the problem?
(1) Meet one of the Device requirements above
(2) Go to one of the video sources
(3) Play the video
 
What is the expected result?
Video playback is fine with no interruptions.
 
What happens instead?
 
A couple of things can happen but I believe the root cause should all seem from a buffer error:
  * After playing video for a while, the video player will restart and start from the beginning.
  * Video player crashes with CHUNK_DEMUXER_ERROR_APPEND_FAILED
  * InvalidStateError: Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null.
video.error.message = CHUNK_DEMUXER_ERROR_APPEND_FAILED: Failed to prepare video sample for decode
 
In summary, the video might not start playing or it may start playing but the player can restart after a minute or so of video playback. I have tested the bug on Android, Linux, MacOS and it is all reproducible as long as somehow, I trigger the a reduced MSE video buffer limit less than around 66mb.
 
 
Please provide any additional information below. Attach a screenshot if
possible.
This issue isn't occurring on Firefox when I manually set the MSE Buffer limit to 30 through about:config -> media.mediasource.eviction_threshold.video;15728640. Looking at the difference between the logs being outputted Chromium tries to append smaller segment of data (which isn't incorrect) while Firefox doesn't try at all.
 
I started putting some print statement in ConvertAVCToAnnexBInPlaceForLengthSize4 https://cs.chromium.org/chromium/src/media/formats/mp4/avc.cc. to see exactly what is happened when it is trying to read the nal_length for each buffer segments. I did my testing on MacOS Chrome with https://beaufortfrancois.github.io/sandbox/media/source-buffer-limit.html. Initially, the full sized buffers are appending correctly. Once it gets to trying smaller a buffer size, this is where the error starts to occur. As expected, it should throw QuotaExceededError when it cannot fit the buffer. When the buffer does fit in the remaining defined size, Chromium tries to fit it. This is fine for the first time it tries to accomplish this, and all of the nal_length is read correctly. The last nal_length it tries to read, it returns a kNeedMoreData because it realizes the buf size and sample size of the segment is smaller (https://cs.chromium.org/chromium/src/media/formats/mp4/mp4_stream_parser.cc?l=780). Typically, when appending the fullsize segments, they do end up returning kNeedMoreData but usually end up returning OnEndOfMediaSegment (https://cs.chromium.org/chromium/src/media/filters/source_buffer_state.cc?l=881&gsn=OnEndOfMediaSegment). Now, when trying to read a smaller buffer for a second time, the point where buf is pointing at is the previous smaller segment. In other words, I believe buf is pointed at an incorrect location. I believe once the second smaller segment come it, it is being treated as a continuation of kNeedsMoreData (https://cs.chromium.org/chromium/src/media/base/decryptor.h?type=cs&q=kNeedMoreData&g=0&l=34). I feel like I haven't dived deep into the code base enough for sure to say this is the root cause, but this is a behavior that I noticed when putting log statements throughout [mp4_stream_parser.cc, source_buffer_state.cc, avc.cc]
 
chrome://media-internals for
  * MacOS Chrome
  * 69.0.3497.100 (Official Build) (64-bit)
  * ./Google Chrome --enable-logging --v=5 --mse-video-buffer-size-limit-mb=30 --flag-switches-begin --enable-hardware-overlays=single-fullscreen,single-on-top,underlay --flag-switches-end
 
{
  "19:4": {
    "id": "19:4",
    "properties": {
      "render_id": 19,
      "player_id": 4,
      "event": "WEBMEDIAPLAYER_DESTROYED"
    },
    "allEvents": [
      {
        "time": 0,
        "key": "event",
        "value": "WEBMEDIAPLAYER_DESTROYED"
      }
    ],
    "lastRendered": 0,
    "firstTimestamp_": 215891337.647,
    "destructed": true
  },
  "19:6": {
    "id": "19:6",
    "properties": {
      "render_id": 19,
      "player_id": 6,
      "origin_url": "https://beaufortfrancois.github.io/",
      "frame_url": "https://beaufortfrancois.github.io/sandbox/media/source-buffer-limit.html",
      "frame_title": "",
      "url": "blob:https://beaufortfrancois.github.io/0e19afca-da94-4452-b298-c49b8003ac35",
      "info": "Selected GpuVideoDecoder for video decoding, config: codec: h264 format: 1 profile: h264 main coded size: [1280,720] visible rect: [0,0,1280,720] natural size: [1280,720] has extra data? false encryption scheme: Unencrypted rotation: 0°",
      "pipeline_state": "kStopped",
      "found_video_stream": true,
      "video_codec_name": "h264",
      "video_dds": false,
      "video_decoder": "GpuVideoDecoder",
      "is_platform_video_decoder": true,
      "video_buffering_state": "BUFFERING_HAVE_ENOUGH",
      "height": 720,
      "width": 1280,
      "for_suspended_start": false,
      "pipeline_buffering_state": "BUFFERING_HAVE_ENOUGH",
      "error": "Append: stream parsing failed. Data size=46848 append_window_start=0 append_window_end=inf",
      "pipeline_error": "CHUNK_DEMUXER_ERROR_APPEND_FAILED",
      "duration": 372.625
    },
    "allEvents": [
      {
        "time": 0,
        "key": "origin_url",
        "value": "https://beaufortfrancois.github.io/"
      },
      {
        "time": 0.008000016212463379,
        "key": "frame_url",
        "value": "https://beaufortfrancois.github.io/sandbox/media/source-buffer-limit.html"
      },
      {
        "time": 0.011000007390975952,
        "key": "frame_title",
        "value": ""
      },
      {
        "time": 0.09300002455711365,
        "key": "url",
        "value": "blob:https://beaufortfrancois.github.io/0e19afca-da94-4452-b298-c49b8003ac35"
      },
      {
        "time": 0.13199999928474426,
        "key": "info",
        "value": "ChunkDemuxer: buffering by DTS"
      },
      {
        "time": 0.18800002336502075,
        "key": "pipeline_state",
        "value": "kStarting"
      },
      {
        "time": 19.548000007867813,
        "key": "found_video_stream",
        "value": true
      },
      {
        "time": 19.55300000309944,
        "key": "video_codec_name",
        "value": "h264"
      },
      {
        "time": 19.570000022649765,
        "key": "info",
        "value": "Custom video per-track SourceBuffer size limit=31457280"
      },
      {
        "time": 47.09000000357628,
        "key": "video_dds",
        "value": false
      },
      {
        "time": 47.09499999880791,
        "key": "video_decoder",
        "value": "GpuVideoDecoder"
      },
      {
        "time": 47.09700000286102,
        "key": "is_platform_video_decoder",
        "value": true
      },
      {
        "time": 47.11300000548363,
        "key": "info",
        "value": "Selected GpuVideoDecoder for video decoding, config: codec: h264 format: 1 profile: h264 main coded size: [1280,720] visible rect: [0,0,1280,720] natural size: [1280,720] has extra data? false encryption scheme: Unencrypted rotation: 0°"
      },
      {
        "time": 47.131999999284744,
        "key": "pipeline_state",
        "value": "kPlaying"
      },
      {
        "time": 108.85500001907349,
        "key": "video_buffering_state",
        "value": "BUFFERING_HAVE_ENOUGH"
      },
      {
        "time": 108.88899999856949,
        "key": "height",
        "value": 720
      },
      {
        "time": 108.88899999856949,
        "key": "width",
        "value": 1280
      },
      {
        "time": 109.79800000786781,
        "key": "for_suspended_start",
        "value": false
      },
      {
        "time": 109.79800000786781,
        "key": "pipeline_buffering_state",
        "value": "BUFFERING_HAVE_ENOUGH"
      },
      {
        "time": 234.55000001192093,
        "key": "error",
        "value": "Failed to prepare video sample for decode"
      },
      {
        "time": 234.6220000088215,
        "key": "error",
        "value": "Append: stream parsing failed. Data size=46848 append_window_start=0 append_window_end=inf"
      },
      {
        "time": 235.7490000128746,
        "key": "pipeline_error",
        "value": "CHUNK_DEMUXER_ERROR_APPEND_FAILED"
      },
      {
        "time": 235.86500000953674,
        "key": "pipeline_state",
        "value": "kStopping"
      },
      {
        "time": 235.95800000429153,
        "key": "pipeline_state",
        "value": "kStopped"
      },
      {
        "time": 19.628000020980835,
        "key": "duration",
        "value": 372.625
      }
    ],
    "lastRendered": 0,
    "firstTimestamp_": 215891378.649
  }
}
 
What's the size of bytes between keyframes? E.g., what's your gop size?
Cc: xhw...@chromium.org sande...@chromium.org
Components: -Internals>Media Internals>Media>Source
Owner: wolenetz@chromium.org
Status: Assigned (was: Unconfirmed)
OIC, this isn't a quota error, but a parse failure because stream parser isn't working as expected. +experts on these pieces.
Labels: Needs-Feedback
Examining your demo link (https://beaufortfrancois.github.io/sandbox/media/source-buffer-limit.html), it's clear that once a QuotaExceededError results and appendSomeData is called with a percent < 100, then only a subset of the mp4 bytes are attempted to be appended. *If* such an append succeeds, then the demo tries to again, appendSomeData with that smaller percent value. However, each time it appends, it starts with bytes from the beginning of the demo mp4 stream, rather than continuing the previously partially appended demo mp4 stream. This leads to a malformed sequence of bytes appended.

I suggest that when you handle an updateend in appendSomeData() for a percent < 100, that you call sourceBuffer.abort() to reset the parser. Or I suggest that you modify the demo to append more bytes from where it left off at the previous partial append.


The list of URLs in the OP is more than just your demo. I'll continue investigation using those (since the failure mode of the first URL is expected since the app isn't appending a well-formed sequence of bytes in all cases.

Meanwhile, please verify my suggestions fix the problem you were encountering with the first URL in OP.
For the issue with the first URL in OP, I've filed https://github.com/beaufortfrancois/sandbox/issues/20

Sign in to add a comment