SpringBoot+Nginx实现视频在线点播

本文介绍使用 SpringBoot+Nginx 实现视频在线点播,本人所发内容及涉及源码,均为亲手所撸,如总结内容有误,欢迎指出。

视频点播方案如下:

1.播放器通过 http协议从http服务器上下载视频文件进行播放

问题:必须等到视频下载完才可以播放,不支持快进到某个时间点进行播放

2.播放器通过rtmp协议连接媒体服务器以实时流方式播放视频

使用rtmp协议需要架设媒体服务器,造价高,对于直播多采用此方案。

3.播放器使用HLS协议连接http服务器(Nginx、Apache等)实现近实时流方式播放视频

HLS协议规定:基于Http协议,视频封装格式为ts,视频的编码格式为H264,音频编码格式为MP3、AAC或者AC-3

什么是HLS?

HLS的工作方式是:将视频拆分成若干ts格式的小文件,通过m3u8格式的索引文件对这些ts小文件建立索引。一般10秒一个ts文件,播放器连接m3u8文件播放,当快进时通过m3u8即可找到对应的索引文件,并去下载对应的ts文件,从而实现快进、快退以近实时的方式播放视频。

IOS、Android设备、及各大浏览器都支持HLS协议。

今天我们使用SpringBoot+Nginx实现一下视频在线点播。

1、ffmpeg的使用

FFmpeg被许多开源项目采用,QQ影音、暴风影音、VLC等。

下载:FFmpeg  https://www.ffmpeg.org/download.html#build-windows

将安装们目录配置于环境变量之中:

SpringBoot+Nginx实现视频在线点播

检测是否安装成功:

SpringBoot+Nginx实现视频在线点播

生成m3u8/ts文件

使用ffmpeg生成 m3u8的步骤如下:

第一步:先将avi视频转成mp4

ffmpeg.exe -i  lucene.avi -c:v libx264 -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 .\lucene.mp4

下面把各参数意思大概讲讲,大概了解意思即可,不再此展开流媒体专业知识的讲解。

-c:v 视频编码为x264,x264编码是H264的一种开源编码格式。

-s 设置分辨率

-pix_fmt yuv420p:设置像素采样方式,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,它的作用是根据采样方式来从码流中还原每个像素点的YUV(亮度信息与色彩信息)值。

-b 设置码率,-b:a和-b:v分别表示音频的码率和视频的码率,-b表示音频加视频的总码率。码率对一个视频质量有很大的作用。

-r:帧率,表示每秒更新图像画面的次数,通常大于24肉眼就没有连贯与停顿的感觉了。

第二步:将mp4生成m3u8

ffmpeg -i F:test\test.mp4 -c:v copy -c:a copy -f ssegment -segment_format mpegts -segment_list F:\m3u8\hls\test.m3u8 -segment_time 10 F:\m3u8\hls\test%05d.ts

-hls_time 设置每片的长度,单位为秒

-hls_list_size n: 保存的分片的数量,设置为0表示保存所有分片

-hls_segment_filename :段文件的名称,%05d表示5位数字

生成的效果是:将test.mp4视频文件每10秒生成一个ts文件,最后生成一个m3u8文件,m3u8文件是ts的索引文件。

使用VLC打开m3u8文件,测试播放效果,VLC 是一款自由、开源的跨平台多媒体播放器及框架,可播放大多数多媒体文件,以及 DVD、音频 CD、VCD 及各类流媒体协议。

m3u8转MP4文件

ffmpeg -i F:\m3u8\hls\1679771418348863490\1679771418348863490.m3u8 -c copy aaaa.mp4

2、Java使用ffmpeg生成m3u8

file:
  upload-path: "F:\\movie_online"
  ffmpeg-path: D:\\software\\ffmpeg\\ffmpeg-n5.1-latest-win64-lgpl-shared-5.1\\bin\\ffmpeg.exe
  target-path: "F:\\m3u8\\hls\\"

代码

FileHandleUtil.java


@Component
public class FileHandleUtil {

    @Value("${file.upload-path}")
    private String filePath;

    @Value("${file.target-path}")
    private String targetPath;

    @Value("${file.ffmpeg-path}")
    private String ffmpegPath;
    // originFile  文件全路径   fileName   文件名
    public void mp4TranM3u8Ts(String originFile, String fileName){
        File file = new File(originFile);
        if (!file.exists()) {
            throw new RuntimeException("源文件不存在");
        }
        File targetFile = new File(targetPath + fileName);
        if (!targetFile.exists()) {
            targetFile.mkdir();
        }
        String output = targetPath + fileName + File.separator + fileName + ".m3u8";
        List<String> command = new ArrayList<>();
        command.add(ffmpegPath);
        command.add("-i");
        command.add(originFile);
        command.add("-c:v");
        command.add("copy");
        command.add("-c:a");
        command.add("copy");
        command.add("-f");
        command.add("ssegment");
        command.add("-segment_format");
        command.add("mpegts");
        command.add("-segment_list");
        command.add(output);
        command.add("-segment_time");
        command.add("10");
        String ts = targetPath + fileName + File.separator + fileName + "%05d.ts";
        command.add(ts);
        ProcessUtil.execute(command);
    }
}

ProcessUtil.java


public class ProcessUtil {
    public static String execute(List<String> command) {
        StringBuffer inputStringBuffer = new StringBuffer();
        StringBuffer errorStringBuffer = new StringBuffer();
        try {
            ProcessBuilder builder = new ProcessBuilder(command);
            Process process = builder.start();
            System.out.println("============inputStream============");
            // 处理InputStream
            Thread t1 = new Thread(() -> {
                InputStream input = null;
                InputStreamReader reader = null;
                BufferedReader buffer = null;

                try {
                    input = process.getInputStream();
                    reader = new InputStreamReader(input);
                    buffer = new BufferedReader(reader);
                    String inputLine = "";
                    while ((inputLine = buffer.readLine()) != null) {
                        System.out.println(inputLine);
                        inputStringBuffer.append(inputLine);
                    }
                    //退出循环后表示结束流
                    System.out.println("===》》退出循环后表示结束流");
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (buffer != null) {
                            buffer.close();
                        }
                        if (reader != null) {
                            reader.close();
                        }
                        if (input != null) {
                            input.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            t1.setName("deviceName");
            t1.start();

            System.out.println("============errorStream============");
            // 处理ErrorStream
            new Thread(() -> {
                InputStream input = null;
                InputStreamReader reader = null;
                BufferedReader buffer = null;
                try {
                    input = process.getErrorStream();
                    reader = new InputStreamReader(input);
                    buffer = new BufferedReader(reader);
                    String errorLine = "";
                    while ((errorLine = buffer.readLine()) != null) {
                        System.out.println(errorLine);
                        errorStringBuffer.append(errorLine);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (buffer != null) {
                            buffer.close();
                        }
                        if (reader != null) {
                            reader.close();
                        }
                        if (input != null) {
                            input.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            /**
             * 只会存在一个输入流返回
             */
            if (inputStringBuffer != null) {
                return inputStringBuffer.toString();
            }
            if (errorStringBuffer != null) {
                return errorStringBuffer.toString();
            }

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

文件上传部分省略了,在文件上传后,使用上述工具类将视频转为m3u8/ts文件,保存在配置好的F:\m3u8\hls\目录即可

3、Nginx文件服务器配置

server {
        listen       80;
        server_name  localhost;
	    charset utf-8;

        location ^~ /hls/ {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Headers X-Requested-With;
            add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
            
            types {  
                application/vnd.apple.mpegurl m3u8;  
                video/mp2t ts;  
            }
            alias  F:/m3u8/hls/;
            expires -1;
            add_header Cache-Control no-cache;  
        }
    }

文件夹目录

SpringBoot+Nginx实现视频在线点播

4、H5代码

需要使用两个库,可在cdn下载,相关资料也会上传群文件


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试视频播放</title>
    <script type="text/javascript" src="video-6.7.3.js"></script>
    <script type="text/javascript" src="videojs-contrib-hls-5.14.1.min.js"></script>
    <link href="video.js_6.7.3.min.css" rel="stylesheet" type="text/css"/>
</head>
<body>
    <video id="example-video" width="800" height="600" controls poster="http://127.0.0.1:8888/hls/add.png" class="video-js vjs-default-skin vjs-big-play-centered">
        <source src="http://127.0.0.1/hls/1679771414267805698/1679771414267805698.m3u8" type="application/x-mpegURL">
    </video>
    <input type="button" onclick="switchvideo()" value="switch"/>
<script type="text/javascript">
    var player = videojs('example-video');

    function switchvideo() {
        player.src({
            src: 'http://127.0.0.1/hls/1679769948731760641/1679769948731760641.m3u8',
            type: 'application/x-mpegURL',
            withCredentials: true
        })
        player.play();
    }
</script>
</body>
</html>

5、测试

在nginx安装目录下启动nginx,打开页面:

SpringBoot+Nginx实现视频在线点播

作者:全栈小白
原文:https://mp.weixin.qq.com/s/3g_b-1-7D37g1Vyk22shng

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论