nginx + nginx-rtmp-module + springboot 搭建直播流服务器实现推流、拉流实时直播功能

业务需求

最近公司在做养老相关的业务,现在需要子女从小程序端对家里的老人通过家庭终端交互屏进行实时看护。

解决方案

第三方的一些现成的服务:腾讯音视频通话、直播功能; 阿里的音视频通信;两者都挺好的,但是需要收费因此放弃决定自己搭建一套直播流服务;

阿里云盘地址:https://www.aliyundrive.com/s/Lym7dHiFkSG
github地址: https://github.com/Tom-shushu/work-study.git (vedio项目)

先看效果(自己服务器配置低有延迟、放到公司服务器上很流畅、清楚)

image

解决方案以及流程

发现有好多人对这个感兴趣,于是我就加了一个业务流程图,方便大家理解整个业务~
image
(可恶呀,画了好长时间的流程图不让我下载,哼~) ----> https://www.liuchengtu.com/

使用工具

Nginx、Nginx-Rtmp-Module

下载地址

Nginx:https://nginx.org/en/download.html
Nginx-Rtmp-Module:https://github.com/arut/nginx-rtmp-module

软件安装

1、下载Nginx
wget https://nginx.org/download/nginx-1.21.6.tar.gz
2、将压缩包移到需要的安装目录下
mv  nginx-1.21.6.tar.gz  /usr/local
3、下载Nginx-Rtmp-Module
git clone https://github.com/arut/nginx-rtmp-module.git
4、将文件移到需要安装目录下
mv nginx-rtmp-module  /usr/local
5、进入目录
cd /usr/local
6、解压Nginx压缩包
tar -zxvf nginx-1.21.6.tar.gz
7、进入Nginx目录
cd nginx-1.21.6
8、配置
./configure --prefix=/usr/local/nginx --add-module=../nginx-rtmp-module --with-http_ssl_module
9、安装
make && make install
10、配置 nginx.conf 文件(/usr/local/nginx/conf下)
#user  nobody;
# multiple workers works !
worker_processes  2;
 
#pid        logs/nginx.pid;
events {
    worker_connections  8192;
}
 
rtmp {
    server {
        listen 1935;
        chunk_size 4000;
        application live {
             live on;
 
             record all;
             record_path /tmp/av;
             record_max_size 1K;
 
             record_unique on;
             allow publish all;
             deny publish all;
 
             allow play all;
        }
    }
}
 
http {
    include       mime.types;
    default_type  application/octet-stream;
  
    sendfile        off;
 
    server_names_hash_bucket_size 128;
 
    client_body_timeout   10;
    client_header_timeout 10;
    keepalive_timeout     30;
    send_timeout          10;
    keepalive_requests    10;
    #gzip  on; 
    server {
        listen       8080;
        server_name  localhost;
 
 
        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }
        location /stat.xsl {
            root nginx-rtmp-module/;
        }
        location /control {
            rtmp_control all;
        }
 
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

11、启动Nginx

cd /usr/local/nginx/sbin
./nginx -t
./nginx -s reload

启动时可能会包错:nginx: [error] invalid PID number "" in "/usr/local/nginx/logs/nginx.pid"
解决:

/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

12、测试(注意打开服务器8080和1935端口安全组)
访问服务器外网 IP:8080
image

Java编码实现直播推流、拉流

1、新建SpringBoot项目 pom.xml 引入所需的jar包
        <!--直播相关依赖-->
        <!--javacv-->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.5.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.bytedeco.javacpp-presets/opencv-platform -->
        <!--opencv-->
        <dependency>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>opencv-platform</artifactId>
            <version>4.0.1-1.4.4</version>
        </dependency>
2、新建直播推流实现类
public class PushStream {

    /**
     * 直播推流实现
     */
    public void getRecordPush(String outputPath, int v_rs) throws Exception, org.bytedeco.javacv.FrameRecorder.Exception, InterruptedException {
            //创建采集器
            OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);  //本地摄像头默认为0
            //开启采集器
            try {
                grabber.start();
            } catch (Exception e) {
                try {
                    grabber.restart();  //一次重启尝试
                } catch (Exception e2) {
                    throw e;
                }
            }
            OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();  //转换器
            Frame grabframe = grabber.grab();  //获取一帧
            IplImage grabbedImage = null;
            if (grabframe != null) {
                grabbedImage = converter.convert(grabframe); //将这一帧转换为IplImage
            }
            //创建录制器
            FrameRecorder recorder;
            recorder = FrameRecorder.createDefault(outputPath, 1280, 720);   //输出路径,画面高,画面宽
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);  //设置编码格式
            recorder.setFormat("flv");
            recorder.setFrameRate(v_rs);
            recorder.setGopSize(v_rs);

            //开启录制器
            try {
                recorder.start();
            } catch (java.lang.Exception e) {
                try {
                    if (recorder != null) {  //尝试重启录制器
                        recorder.stop();
                        recorder.start();
                    }
                } catch (java.lang.Exception e1) {
                    e.printStackTrace();
                }
            }

            //直播效果展示窗口
            CanvasFrame frame = new CanvasFrame("主播-菜鸡-德华", CanvasFrame.getDefaultGamma() / grabber.getGamma());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setAlwaysOnTop(true);

            //推流
            while (frame.isVisible() && (grabframe = grabber.grab()) != null) {
                frame.showImage(grabframe);   //展示直播效果
                grabbedImage = converter.convert(grabframe);
                Frame rotatedFrame = converter.convert(grabbedImage);

                if (rotatedFrame != null) {
                    recorder.record(rotatedFrame);
                }

                Thread.sleep(50);  //50毫秒/帧
            }
        }
    }
3、新建直播推流启动类
/**
 * 直播推流--启动
 */
@SpringBootApplication
@Configuration
public class PushApplication {
    public static void main(String[] args) throws Exception {
        //设置rtmp服务器推流地址(写你自己服务器外网地址)
        String outputPath = "rtmp://xxx.xx.xxx.xx:1935/live/address";
        PushStream recordPush = new PushStream();
        recordPush.getRecordPush(outputPath, 25);
    }
}
4、新建直播拉流实现类
/**
 * 直播拉流实现
 */
public class PullStream {
    public void getPullStream(String inputPath) throws Exception, org.bytedeco.javacv.FrameRecorder.Exception {
        //创建+设置采集器
        FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(inputPath);
        grabber.setOption("rtsp_transport", "tcp");
        grabber.setImageWidth(960);
        grabber.setImageHeight(540);
        //开启采集器
        grabber.start();
        //直播播放窗口
        CanvasFrame canvasFrame = new CanvasFrame("德华正在喂饭。。。。。");
        canvasFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        canvasFrame.setAlwaysOnTop(true);
        OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
        //播流
        while (true){
            Frame frame = grabber.grabImage();  //拉流
            opencv_core.Mat mat = converter.convertToMat(frame);
            canvasFrame.showImage(frame);   //播放
        }
    }
}
5、新建直播拉流启动类(可以多建几个模拟多个客户端)
/**
 * 直播拉流--启动
 */
@SpringBootApplication
@Configuration
public class PullApplication1 {
    public static void main(String[] args) throws Exception {
        //rtmp服务器拉流地址(自己服务器外网地址)
        String inputPath = "rtmp://xxx.xx.xxx.xx/live/address";
        PullStream pullStream = new PullStream();
        pullStream.getPullStream(inputPath);
    }
}

6、测试
image

posted @ 2022-04-28 00:05  Tom-shushu  阅读(8598)  评论(14编辑  收藏  举报