Stream video using Spring Boot chunk by chunk over Http.

You are looking for stream video over Http and hard to find how to stream over Http using java. This is pretty much playing with the header and all about status code. Here @RequestHeader is responsible for playing video over Http.

 @RequestHeader(value = "Range", required = false) String httpRangeList

Range

The range header is requested by the client that the range of response data requested by the client. The client requests up to a certain range starting with 0. For example: Range 0 – 400000.

Lets start using spring boot

Create a simple spring boot application using start.spring.io or you can simply create a maven project.

Please add the following dependancy

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-lang3</artifactId>
</dependency>

Now create a simple controller class and add the following

package com.stream.controller;

import java.io.File;
import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.core.io.support.ResourceRegion;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import com.stream.service.VideoStreamingService;

@RestController
public class VideoStreamController {

	@Autowired
	VideoStreamingService service;
	public static final String VideoUploadingDir = System.getProperty("user.dir") + "/Uploads/Posts/Videos";

	@GetMapping(value = "/video", produces = "application/octet-stream")
	public ResponseEntity<ResourceRegion> getVideo(@RequestHeader(value = "Range", required = false) String rangeHeader)
			throws IOException {

		if (!new File(VideoUploadingDir).exists()) {
			new File(VideoUploadingDir).mkdirs();
		}
		return service.getVideoRegion(rangeHeader, VideoUploadingDir);

	}

}

Accept-Range

Accept-Range header defines the type of data and range going to received by the client. Ex. Accept-Range = byte.

Content Range

Content-Range state the client that the range of byte that the server serve to the client.

Content-Type

This is the most important header that defines the type of the response.For the particular example the content-type is video/mp4

Lets create a service class for reading the file as byte

package com.stream.service;

import static java.lang.Math.min;

import java.io.IOException;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.ResourceRegion;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

@Service
public class VideoStreamingService {
	private static final long CHUNK_SIZE = 1000000L;
	public ResponseEntity<ResourceRegion> getVideoRegion(String rangeHeader ,String directory) throws IOException {
		FileUrlResource videoResource = new FileUrlResource(directory + "/video.mp4");
		ResourceRegion resourceRegion = getResourceRegion(videoResource, rangeHeader);

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

	private ResourceRegion getResourceRegion(UrlResource video, String httpHeaders) throws IOException {
		ResourceRegion resourceRegion = null;

		long contentLength = video.contentLength();
		int fromRange = 0;
		int toRange = 0;
		if (StringUtils.isNotBlank(httpHeaders)) {
			String[] ranges = httpHeaders.substring("bytes=".length()).split("-");
			fromRange = Integer.valueOf(ranges[0]);
			if (ranges.length > 1) {
				toRange = Integer.valueOf(ranges[1]);
			} else {
				toRange = (int) (contentLength - 1);
			}
		}

		if (fromRange > 0) {
			long rangeLength = min(CHUNK_SIZE, toRange - fromRange + 1);
			resourceRegion = new ResourceRegion(video, fromRange, rangeLength);
		} else {
			long rangeLength = min(CHUNK_SIZE, contentLength);
			resourceRegion = new ResourceRegion(video, 0, rangeLength);
		}

		return resourceRegion;
	}
}

Content Length

It specify that the total amount of byte should transfer by the server to the client.

Status Code

Here in this example, the most important work is done by the status code. As we all know about the 200 status code i.e OK but here we use status code 206 which is called PARTIAL CONTENT. 206 status code widely used when we are requesting for header range on the server.

You can simply run the project and type http://localhost:8080/video on browser.

You can find the details of the code on git here

Thanks for visiting us.

Leave a Reply

Your email address will not be published.

Back To Top