2

I'm creating a local video library but I can't workout how I can improve the performance of video seeking/scrubbing. When I view a video file which is stored locally it takes 10-15 seconds before it starts playing, and jumping around with the seek bar can take up to 45 seconds.

Opening the video with VLC and this is all instant. Is there just a straight up limitation using the HTML5 player, or is my backend code where the issue is?

To serve the video I'm using Springboot with the following method in the controller

@GetMapping(value = "/videos/p/{id}", produces = "video/mp4")
public FileSystemResource videoSource(@PathVariable String id) {
    Optional<Video> optionalVideo = videoRepository.findById(id);

    return optionalVideo
            .map(video -> new FileSystemResource(new File(video.getAbsolutePath())))
            .orElse(null);
}

I'm calling this in the frontend with a simple

<video width="100%" controls>
  <source src="http://localhost:8080/videos/p/{{video.id}}" type="video/mp4">
</video>

Is there a obvious way to increase the performance here?

Chris
  • 3,437
  • 6
  • 40
  • 73

1 Answers1

2

I've found some interesting answers on SO. But I've also worked out how this can be done easily with Spring WebFlux.

This post outlines the awesome MultipartFileSender from Davin Kevin which worked but was still a little slow. Changing the InputStream to a RandomAccessFile as suggested as another answer further down, made scrubbing almost instant.

Knowing now more about what I was trying to Google I found the Jira ticket to add http byte-range support. Which ultimately lead to my solution.

@GetMapping(value = "/videos/p/{id}")
public ResponseEntity<UrlResource> getFullVideo(@PathVariable String id) throws MalformedURLException {
    Optional<Video> optionalVideo = videoRepository.findById(id);

    if (optionalVideo.isPresent()) {
        final String videoPath = optionalVideo.get().getAbsolutePath();
        final UrlResource video = new UrlResource(String.format("file:%s", videoPath));

        return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
                .contentType(MediaTypeFactory.getMediaType(video)
                        .orElse(MediaType.APPLICATION_OCTET_STREAM))
                .body(video);
    }

    return ResponseEntity.noContent().build();
}
Chris
  • 3,437
  • 6
  • 40
  • 73