1

I am writing a spring boot app which has REST apis (using spring mvc framework) that stream audio/video to HTML5 player on the browser. These apis support range requests for the content.

I have run into an issue where the HTML5 video player complains with error ERR_CONTENT_LENGTH_MISMATCH periodically during streaming.

It seems that bytes received from server do not match bytes advertised by server in Content-Length header.

Please advise what could be the root cause of this.

Things that I have researched so far that could potentially solve the issue but haven't in my case:

No buffering in response. No apache in front of tomcat.

Here is my code:

@Api("Player API")

@RestController public class PlayerController {

@Autowired
FetchAssetService fetchAssetService;

@ApiOperation("Get video")
@RequestMapping(value = "player/video/{packageId}/{username}", method = RequestMethod.GET)
public ResponseEntity<StreamingResponseBody> getProxy(@RequestHeader(value="Range", required=false) String range, @PathVariable Long packageId, @PathVariable String username) throws Exception {
    Optional<Stream> videoAssetMetaData = fetchAssetService.fetchVideoAssetMetaData(packageId);
    if (!videoAssetMetaData.isPresent()) {
        throw new AssetNotFoundException("Video asset not found in MPL for package: "+packageId);
    }
    HttpHeaders httpHeaders = new HttpHeaders();
    HttpStatus status = HttpStatus.OK;
    Optional<AssetRange> optionalAssetRange = AssetRange.create(range,videoAssetMetaData.get().getLength());
    if (optionalAssetRange.isPresent()) {
        if (optionalAssetRange.get().isSatisfiable()) {
            setSuccessRangeHeaders(httpHeaders,optionalAssetRange.get());
            status = HttpStatus.PARTIAL_CONTENT;
        } else {
            setErrorRangeHeaders(httpHeaders,optionalAssetRange.get());
            status = HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE;
            return new ResponseEntity(null,httpHeaders,status);
        }
    }
    setContentHeaders(httpHeaders, “video.mp4");
    try {
        return new ResponseEntity(fetchAssetService.getStreamingResponseBody(packageId,videoAssetMetaData.get(),optionalAssetRange,username),
                httpHeaders,
                status);
    } catch (Exception ex) {
        log.error("Exception while video streaming: package={}, user={}, range={}",packageId,username,range,ex);
        throw ex;
    }
}

private void setContentHeaders(HttpHeaders httpHeaders, String fileName) {
    httpHeaders.add(HttpHeaders.ACCEPT_RANGES,"bytes");
    httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
    httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename="+ fileName);
}

private void setSuccessRangeHeaders(HttpHeaders httpHeaders, AssetRange range) {
    httpHeaders.add(HttpHeaders.CONTENT_LENGTH, Long.toString(range.getRangeLength()));
    httpHeaders.add(HttpHeaders.CONTENT_RANGE, String.format("bytes %d-%d/%d", range.getStart(), range.getEnd(), range.getTotalLength()));
}

private void setErrorRangeHeaders(HttpHeaders httpHeaders, AssetRange range) {
    httpHeaders.add(HttpHeaders.CONTENT_RANGE, String.format("bytes */%d", range.getTotalLength()));
}

@ExceptionHandler(AssetNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handleAppException(AssetNotFoundException ex) {
    return ex.getMessage();
}

@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleAppException(Exception ex) {
    return ex.getMessage();
}

}

1 Answers1

0

Best guess, in setSuccessRangeHeaders, you are setting the content length to a range value rather than the actual content length of your response.

try not setting content_length at all or try setting it more accurately.

this might help: How to set content length as long value in http header in java?

Community
  • 1
  • 1
obi1
  • 314
  • 2
  • 6