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


    业务需求

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

    解决方案

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

    代码码云地址: https://gitee.com/Tom-shushu/vedio.git

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

    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

  • 相关阅读:
    Confluence OGNL注入漏洞复现(CVE-2022-26134)
    基于遗传算法的微电网调度(风、光、蓄电池、微型燃气轮机)(Matlab代码实现)
    Redis学习笔记13:基于spring data redis及lua脚本list列表实现环形结构案例
    Text-to-SQL小白入门(五)开源最强代码大模型Code Llama
    求职面试、笔试 经验与准备流程
    计算机网络:以太网与IEEE 802.3
    【软考 系统架构设计师】案例分析⑧ 数据库索引与视图
    使用Python进行Base64编码和解码
    简单的卷积神经网络编程,卷积神经网络算法代码
    工控机通过485modbus转profinet网关与温度智能表通讯案例
  • 原文地址:https://www.cnblogs.com/Tom-shushu/p/16201229.html