如何使用HTML5和Rest Service流式传输视频

时间:2018-07-08 08:30:41

标签: angular spring-boot

我正在开发一个允许用户上传和观看视频的应用程序。 但是我在流式传输视频时遇到了问题。我有两个申请 Angular4中的UI前端,后端中的Spring-boot Rest服务。 我尝试使用HTML 5 Video元素,因为我不希望任何第三方API支持播放视频。  我做了很多关于如何在网站上播放视频的研究,由于很多不同的建议,我每次都感到困惑。我希望有一种正确的方法来在前端流式传输视频。 这是我用来流式传输视频的休息服务的代码。

@Override
public ResponseEntity<InputStreamResource> retrieveResource(String fileId, String range) throws ServiceException {

    VideoDetails dto = searchDao.getVideo(fileId);
    File file = new File(dto.getFilePath());

    long rangeStart = Long.parseLong(range.replace("bytes=", "").split("-")[0]);
    long rangeEnd = Long.parseLong(range.replace("bytes=", "").split("-")[0]);
    long contentLength = file.length();

    return getStream(dto.getFilePath(), rangeStart, rangeEnd, contentLength);
}

private ResponseEntity<InputStreamResource> getStream(String filePath, long rangeStart, long rangeEnd,
        long contentLength) throws ServiceException{

    try {

        InputStream inputStream = FileUtils.openInputStream(new File(filePath));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("video/mp4"));
        headers.set("Accept-Ranges", "bytes");
        headers.set("Expires", "0");
        headers.set("Cache-Control", "no-cache, no-store");
        headers.set("Connection", "keep-alive");
        headers.set("Content-Transfer-Encoding", "binary");
        headers.set("Content-Length", String.valueOf(contentLength - rangeEnd)); //I do not know what to put here


        if (rangeStart == 0) {
            return new ResponseEntity<>(new InputStreamResource(inputStream), headers, HttpStatus.OK);
        } else {
            headers.set("Content-Range", String.format("bytes %s-%s/%s", rangeStart, rangeEnd, contentLength));
            return new ResponseEntity<>(new InputStreamResource(inputStream), headers, HttpStatus.PARTIAL_CONTENT);
        }
    } catch (Exception ex) {

        throw new ServiceException(
                "Unable to find the video resource for the give fileId: [FilePath = " + filePath + "]");

    }

}

我在技术博客之一上获得了上述Java代码,而在UI端,我则获得了以下代码,

<div id="playerDiv" *ngIf="content">
<video id="my-video" controls preload="auto" width="640" height="400" controlsList="nodownload" src="http://localhost:8080/searchvideo/watch?fileId=AzxeyYab1">
</video>

</div>

我能够看到视频中流的第一个块,但是video元素不发送获取其余流块的请求。据我所知,视频元素不需要任何额外的代码来进行流传输。请纠正我,如果我错了

任何人都可以在这里帮助我。我想在这里提及一件事。实际上,我对流媒体相关的东西了解甚少。我什至不知道哪个是最好的API,在谷歌搜索后,我对此主题感到非常困惑,以至于我在想我的最后选择。

1 个答案:

答案 0 :(得分:0)

好的,最后我得到了一些正确的答案。我将其发布为答案,但我不能接受,因为它仍然存在一些问题,我希望有人在这里为我提供帮助。请查看更改后的代码。

public void retrieveResource(String fileId, String range,HttpServletRequest request, HttpServletResponse response) throws ServiceException {


    VideoDetails dto = searchDao.getVideo(fileId);
    File file = new File(dto.getFilePath());
    long contentLength = file.length();
    int chunkSize = 10240 * 10240; //10MB at once
    byte[] buffer  = new byte[chunkSize];

    response.setContentType("video/"+getFileExtension(file));
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("Expires", "0");
    response.setHeader("Cache-Control", "no-cache, no-store");
    response.setHeader("Connection", "keep-alive");
    response.setHeader("Content-Transfer-Encoding", "binary");
    RandomAccessFile input = null;
    OutputStream os = null;

    if(range == null) {
    //TO DO
    }else {

        try {
            os = response.getOutputStream();
            input = new RandomAccessFile(file, "r");

        } catch (Exception e) {
            e.printStackTrace();
        };


    long rangeStart = Long.parseLong(range.replace("bytes=", "").split("-")[0]);


      long rangeEnd = chunkSize + rangeStart;

      if (rangeEnd >= contentLength) {
            rangeEnd =  contentLength - 1;
        }
        if (range.length() == 2) {
            rangeEnd = Long.parseLong(range.replace("bytes=", "").split("-")[1]);
        }

        try {
            input.seek(rangeStart);
        } catch (IOException e) {
            e.printStackTrace();
        }
                try {

                    response.setHeader("Content-Range", String.format("bytes %d-%d/%d", rangeStart, rangeEnd, contentLength));
                    //response.setHeader("Content-Length",String.valueOf(rangeEnd-rangeStart+1) );
                    response.setStatus(206);

                    if(rangeEnd >= contentLength-1) {
                        input.readFully(buffer, 0, (int)(rangeEnd - rangeStart + 1));
                    }else {
                        input.readFully(buffer, 0, chunkSize);  
                    }

                    os.write(buffer);
            //      os.flush();
                //  os.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
    }
}

上面的代码工作正常,我也可以在视频中来回导航,但不知何故,我在导航中遇到以下错误。

org.apache.catalina.connector.ClientAbortException: java.io.IOException: Connection reset by peer
at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:393)
at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:344)
at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:418)
at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:406)
at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:97)
at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:90)
at com.critic.video.service.search.SearchServiceImpl.retrieveResource(SearchServiceImpl.java:174)
at com.critic.video.controller.search.SearchVideoController.retrieveResource(SearchVideoController.java:76)
at com.critic.video.controller.search.SearchVideoController$$FastClassBySpringCGLIB$$58bf0cbb.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
at com.critic.video.controller.search.SearchVideoController$$EnhancerBySpringCGLIB$$8506950.retrieveResource(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)

但是我的网页仍然可以正常工作。视频仍然继续。任何人都可以在这里帮助我,以摆脱该错误该怎么办。非常感谢您的帮助。