SRS streaming media service (2) Use of SRS service Http_CallBack callback mechanism

SRS provides a series of http callbacks, which trigger the user-specified http request according to the different states of the client connecting to the server. The user-defined service accepts the information transmitted by the SRS service and performs a series of operations. For example, when the client connects to the server, it judges according to the callback data. Whether to allow clients to connect to the server.

The SRS service provides the following callbacks:

SRS service version: 4.0.166

on_connect: triggered when the client connects to the specified vhost and app

{
"action": "on_connect",
"client_id": 1985,
"ip": "192.168.1.10",
"vhost": "video.test.com", 
"app": "live",
"tcUrl": "rtmp://video.test.com/live?key=d2fa801d08e3f90ed1e1670e6e52651a",
"pageUrl": "http://www.test.com/live.html", 
"server_id": "vid-werty"
}

on_close: Triggered when the client closes the connection, or SRS actively closes the connection

{
"action": "on_close",
"client_id": 1985,
"ip": "192.168.1.10", 
"vhost": "video.test.com", 
"app": "live",
"send_bytes": 10240, 
"recv_bytes": 10240, 
"server_id": "vid-werty"
}

on_publish: Triggered when the client publishes the stream, such as pushing the stream to the server in flash/FMLE mode

{
"action": "on_publish",
"client_id": 1985,
"ip": "192.168.1.10", 
"vhost": "video.test.com", 
"app": "live",
"stream": "livestream", 
"param":"?token=xxx&salt=yyy", 
"server_id": "vid-werty"
}

on_unpublish: Triggered when the client stops publishing the stream

{
"action": "on_unpublish",
"client_id": 1985,
"ip": "192.168.1.10", 
"vhost": "video.test.com", 
"app": "live",
"stream": "livestream", 
"param":"?token=xxx&salt=yyy", 
"server_id": "vid-werty"
}

on_play: Triggered when the client starts playing the stream

{
"action": "on_play",
"client_id": 1985,
"ip": "192.168.1.10", 
"vhost": "video.test.com", 
"app": "live",
"stream": "livestream", 
"param":"?token=xxx&salt=yyy",
"pageUrl": "http://www.test.com/live.html", 
"server_id": "vid-werty"
}

on_stop: Triggered when the client stops playing. Remarks: Stopping playback may not close the connection and continue playback.

{
"action": "on_stop",
"client_id": 1985,
"ip": "192.168.1.10", 
"vhost": "video.test.com", 
"app": "live",
"stream": "livestream", 
"param":"?token=xxx&salt=yyy", 
"server_id": "vid-werty"
}

on_dvr: Triggered when DVR recording closes a flv file

{
"action": "on_dvr",
"client_id": 1985,
"ip": "192.168.1.10", 
"vhost": "video.test.com", 
"app": "live",
"stream": "livestream", 
"param":"?token=xxx&salt=yyy",
"cwd": "/usr/local/srs",
"file": "./objs/nginx/html/live/livestream.1420254068776.flv", 
"server_id": "vid-werty"
}

on_hls: Triggered when SRS gets HLS ts files

{
"action": "on_hls",
"client_id": 1985,
"ip": "192.168.1.10", 
"vhost": "video.test.com", 
"app": "live",
"stream": "livestream", 
"param":"?token=xxx&salt=yyy",
"duration": 9.36, // in seconds
"cwd": "/usr/local/srs",
"file": "./objs/nginx/html/live/livestream/2015-04-23/01/476584165.ts",
"url": "live/livestream/2015-04-23/01/476584165.ts",
"m3u8": "./objs/nginx/html/live/livestream/live.m3u8",
"m3u8_url": "live/livestream/live.m3u8",
"seq_no": 100, "server_id": "vid-werty"
}

on_hls_notify: Triggered when SRS obtains the HLS ts file, used to push the file to the CDN network, by obtaining the ts file from the CDN network. The request is a GET request, and the request address is in the format:

http://127.0.0.1:8085/api/v1/hls/[server_id]/[app]/[stream]/[ts_url][param];

[server_id]服务器ID替换
[app]应用名称替换
[stream]流名称替换
[ts_url]url替换
[param]参数替换
  • Event: When the event occurs, the HTTP address specified by the callback will be called.
  • HTTP address: Multiple addresses can be supported, separated by spaces, and SRS will call back these interfaces in turn.
  • Data: SRS sends data POST to the HTTP interface. The last callback on_hls_notify is a GET request, and the request parameters are spliced ​​in the URL.
  • Return value: SRS requires the HTTP server to return HTTP200 and the response content is an integer error code (0 means success), other error codes will disconnect the client.
  • The above callback transfer information is provided by the http_hooks callback request information in the official GitHub , which may be different from some of the information actually obtained. You can print and view it in the custom service interface, enable the callback and specify the request URL. Refer to SRS callback request settings

Callback request format, multiple interfaces are separated by spaces and end with a semicolon:

on_connect      http(https)://ip:port/api   http(https)://ip:port/api;

test demo

Server system: CentOS7.4

VM15Pro virtual machine network mode: bridge

Server IP address: 192.168.5.105

Server port and service:

 Local machine IP address: 192.168.5.104

1. Start the SRS service and enter the installation directory

1.1. View the configuration file and set the callback function

# main config for srs.
# @see full.conf for detail config.

listen              1935; //RTMP默认监听1935 TCP端口
max_connections     1000;  //最大连接数
#srs_log_tank        file;
#srs_log_file        ./objs/srs.log;
daemon              on;   //开启后台模式
http_api {
    enabled         on;   //支持外部程序管理SRS服务器,支持跨域请求,请求和响应数据只支持JSON
    listen          1985; //
}
http_server {
    enabled         on;   //开启SRS服务可视化界面查看系统信息和播放器推流器
    listen          8080;
    dir             ./objs/nginx/html;
}
rtc_server {
    enabled on;    //开启RTC服务器

    listen 8000;   //RTC默认监听8000端口为UDP端口

    candidate $CANDIDATE; //服务器提供服务的IP地址。开启RTC服务,这里一定不能错,云服务器这里可能读取不到可访问的外网IP或者错误IP地址,需要手动指定
}
vhost __defaultVhost__ {
    hls {
        enabled         on; 
    }
    http_remux {
        enabled     on;  //RTMP流转封装为http flv流分发
        mount       [vhost]/[app]/[stream].flv;
    }
    rtc {
        enabled     on;   

        #rtmp_to_rtc off; //RTMP推流转RTC拉流 

        #rtc_to_rtmp off;  //RTC推流转RTMP拉流
    }
    http_hooks {
        enabled          on;
        on_connect       https://192.168.5.102:400/connect;
        on_close         https://192.168.5.102:400/close;
        on_publish       https://192.168.5.102:400/publish;
        on_unpublish     https://192.168.5.102:400/unpublish;
        on_play          https://192.168.5.102:400/play;
        on_stop          https://192.168.5.102:400/stop;
    }
}

SRS4.0.166 cannot set rtmp_to_rtc, otherwise the startup service will report an illegal error, but you can push RTMP and pull RTC. There is no error reporting when setting this item in subsequent versions, and the SRS4.0.X version is currently a development version

1.2. Start the service and specify the srs.conf configuration to start

cd  /usr/local/srs/SRS4.0/trunk   //进入安装目录

./objs/srs -c conf/srs.conf    //启动服务指定配置文件为srs.conf

./etc/init.d/srs status   //查看运行状态
./objs/srs -v   //查看安装版本号

 2. Create a springboot project and write an interface

configuration class

package com.example.live.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


/*
 * 配置类
 * @author jiang
 * @date 2021.10.17
 * */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    //解决  No mapping for GET  访问静态资源
    public void addResourceHandlers(ResourceHandlerRegistry registry){
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/");

    }
    //http(https)://ip:port/访问本地址
    public void addViewControllers(ViewControllerRegistry registry){
        registry.addViewController("/").setViewName("client");
    }
}

token generation and verification class

package com.example.live.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.live.constant.Constant;
import java.io.UnsupportedEncodingException;
import java.util.Date;

/*
 * token生成、验证工具类
 * @author jiang
 * @date 2021.10.17
 * */
public class TokenUtils {
    //有效期  1000为1秒
    private static final long EXPIRE_TIME = 60*60*1000;
    //密钥
    private static final String SECRET = Constant.SECRET;

    /*
     * 校验token是否正确
     */
    public static boolean verify(String token, String username) {
        try {
            //根据密码生成JWT效验器
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
            //效验token
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    /*
     * 获得token中的用户名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /*
     * 生成签名
     */
    public static String sign(String username) throws UnsupportedEncodingException {
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        Date date1 = new Date();
        return JWT.create()
                .withIssuer("auth0")//签发人
                .withClaim("username", username)//载体数据
                .withIssuedAt(date1)//签发时间
                .withExpiresAt(date)//过期时间
                .sign(algorithm);//生成新的JWT
    }
}

constant class

package com.example.live.constant;


/*
 * 常量累
 * @author jiang
 * @date 2021.10.17
 * */
public class Constant {
    //token加密密钥
    public static final String SECRET="live";
}

SRS callback data encapsulation class

package com.example.live.callbackbean;

/*
 * SRS回调数据
 * @author jiang
 * @date 2021.10.17
 * */
public class CallBackDataOnConnect {
    String action;
    String client_id;
    String ip;
    String vhost;
    String app;
    String stream;
    String tcUrl;
    String pageUrl;
    String param;

    public String getStream() {
        return stream;
    }

    public void setStream(String stream) {
        this.stream = stream;
    }

    public String getParam() {
        return param;
    }

    public void setParam(String param) {
        this.param = param;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public void setClient_id(String client_id) {
        this.client_id = client_id;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public void setVhost(String vhost) {
        this.vhost = vhost;
    }

    public void setApp(String app) {
        this.app = app;
    }

    public void setTcUrl(String tcUrl) {
        this.tcUrl = tcUrl;
    }

    public void setPageUrl(String pageUrl) {
        this.pageUrl = pageUrl;
    }

    public String getAction() {
        return action;
    }

    public String getClient_id() {
        return client_id;
    }

    public String getIp() {
        return ip;
    }

    public String getVhost() {
        return vhost;
    }

    public String getApp() {
        return app;
    }

    public String getTcUrl() {
        return tcUrl;
    }

    public String getPageUrl() {
        return pageUrl;
    }
}
package com.example.live.callbackbean;

/*
 * SRS回调数据
 * @author jiang
 * @date 2021.10.17
 * */
public class CallBackDataOnPublish {
            String server_id;
            String action ;
            String client_id ;
            String ip ;
            String vhost ;
            String app ;
            String tcUrl ;
            String stream ;
            String param ;

    public String getServer_id() {
        return server_id;
    }

    public void setServer_id(String server_id) {
        this.server_id = server_id;
    }

    public String getTcUrl() {
        return tcUrl;
    }

    public void setTcUrl(String tcUrl) {
        this.tcUrl = tcUrl;
    }

    public String getParam() {
        return param;
    }

    public void setParam(String param) {
        this.param = param;
    }

    public String getAction() {
        return action;
    }

    public String getClient_id() {
        return client_id;
    }

    public String getIp() {
        return ip;
    }

    public String getVhost() {
        return vhost;
    }

    public String getApp() {
        return app;
    }

    public String getStream() {
        return stream;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public void setClient_id(String client_id) {
        this.client_id = client_id;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public void setVhost(String vhost) {
        this.vhost = vhost;
    }

    public void setApp(String app) {
        this.app = app;
    }

    public void setStream(String stream) {
        this.stream = stream;
    }
}
package com.example.live.callbackbean;

/**
 * SRS回调数据
 * @author jiang
 * @date 2021/10/21 21:26
 */
public class CallBackDataOnPlay {
    String server_id;
    String action ;
    String client_id ;
    String ip ;
    String vhost ;
    String app ;
    String pageUrl ;
    String stream ;
    String param ;

    public String getServer_id() {
        return server_id;
    }

    public void setServer_id(String server_id) {
        this.server_id = server_id;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getClient_id() {
        return client_id;
    }

    public void setClient_id(String client_id) {
        this.client_id = client_id;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getVhost() {
        return vhost;
    }

    public void setVhost(String vhost) {
        this.vhost = vhost;
    }

    public String getApp() {
        return app;
    }

    public void setApp(String app) {
        this.app = app;
    }

    public String getPageUrl() {
        return pageUrl;
    }

    public void setPageUrl(String pageUrl) {
        this.pageUrl = pageUrl;
    }

    public String getStream() {
        return stream;
    }

    public void setStream(String stream) {
        this.stream = stream;
    }

    public String getParam() {
        return param;
    }

    public void setParam(String param) {
        this.param = param;
    }
}

Service class interface and implementation

package com.example.live.service;

public interface StreamService {
    public String generateURL();
}
package com.example.live.service.serviceImpl;

import com.example.live.service.StreamService;
import com.example.live.utils.TokenUtils;
import org.springframework.stereotype.Service;
import java.util.UUID;

@Service
public class StreamServiceImpl implements StreamService {
    @Override
    public String generateURL() {
        String token = null;
        String stream = null;
        try{
            token = TokenUtils.sign("jx");
            stream= UUID.randomUUID().toString();
        }catch (Exception e){
            e.getStackTrace();
        }
        String url="rtmp://192.168.5.105/live/"+stream+"?token="+token;
        return url;
    }
}

Process the front-end page to obtain the push address request interface

package com.example.live.controllers;

import com.example.live.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/*
 * 
 * @author jiang
 * @date 2021.10.17
 * */
@Controller()
public class StreamController {
    @Autowired
    private StreamService streamService;
    @ResponseBody
    @RequestMapping("/getUrl")
    public String getUrl(){
        return streamService.generateURL();
    }

}

SRS callback interface

package com.example.live.controllers;

import com.example.live.callbackbean.CallBackDataOnConnect;
import com.example.live.callbackbean.CallBackDataOnPlay;
import com.example.live.callbackbean.CallBackDataOnPublish;
import com.example.live.utils.TokenUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/*
 * 操作SRS流媒体服务回调函数
 * @author jiang
 * @date 2021.10.17
 *
 * 该类所有方法返回值均为int类型,根据SRS回调返回值要求
 * 含义:0成功,非0失败
 * */

@Controller
public class SRSCallBackController {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    @ResponseBody
    @RequestMapping("/connect")
    /*
    * 客户端连接SRS服务器时回调函数请求此接口
    *返回0表示校验成功,非0为失败
    * */
    public int on_connect(@RequestBody CallBackDataOnConnect data){
        int code = 1;
            if(data.getApp()!=""&&data.getApp()!=null&&!data.getApp().isEmpty()&&"live".equals(data.getApp())){
                code = 0;
                log.info("推流应用名称正确...客户端连接成功...");
                return  code;
            }else {
                log.info("推流应用名称错误...客户端连接失败...");
                return code;
            }

    }
    @ResponseBody
    @RequestMapping("/close")
    /*
     * 客户端断开连接时触发
     * 客户端断开包括客户端主动断开和客户端被动断开(服务器主动停止客户端推流断开)
     * */
    public int on_close(@RequestBody String data){
        log.info("客户端断开连接"+data);
        return 0;
    }
    @ResponseBody
    @RequestMapping("/publish")
    /*
    * 用户推流(即直播)时触发
    * 返回0允许推流,返回非0拒绝推流
    * */
    public int on_publish(@RequestBody CallBackDataOnPublish data){
        int code = 1;
        if(data.getApp()!=""&&data.getApp()!=null&&!data.getApp().isEmpty()&&"live".equals(data.getApp())){
            //携带url验证格式为?token=....携带参数应为大于7个字符且问号在第一位
            if (data.getParam().length()>7&&data.getParam().indexOf("?")==0){
                Boolean flag = false;
                //截取?后面5个字符判断是否为token
                String str = data.getParam().substring(data.getParam().indexOf("?")+1,6);
                if("token".equals(str)){
                    //截取=后面字符串校验token是否有效
                    String token = data.getParam().substring(data.getParam().indexOf("=")+1,data.getParam().length());
                    flag = TokenUtils.verify(token,"jx");
                    if(flag){
                        code = 0;
                        String username = TokenUtils.getUsername(token);
                        log.info("token验证成功...客户端开始推流...,用户名为:"+username);
                        return  code;
                    }else {
                        log.info("token验证失败...禁止客户端推流...");
                        return code;
                    }
                }else {
                    log.info("请求未携带token...禁止客户端推流");
                    return code;
                }
            }else {
                log.info("请求未携带token...禁止客户端推流");
                return code;
            }
        }else {
            log.info("推流应用名称错误...禁止客户端推流");
            return code;
        }

    }
    @ResponseBody
    @RequestMapping("/unpublish")
    /*
    * 客户端停止推流时触发
    * 包括客户端主动停止推流和客户端被动停止推流(服务器主动停止客户端推流)
    * */
    public int on_unpublish(@RequestBody String data){
        log.info("客户端停止推流"+data);
        return 0;
    }

    @ResponseBody
    @RequestMapping("/play")
    /*
    * 客户端拉流(播放)时触发
    * 0表示允许拉流,非0表示拒绝拉流
    *
    * */
    public int on_play(@RequestBody CallBackDataOnPlay data) {
        int code = 1;

        if (data.getApp() != "" && data.getApp() != null && !data.getApp().isEmpty() && "live".equals(data.getApp())) {
            //携带url验证格式为?token=....携带参数应为大于7个字符且问号在第一位
            if (data.getParam().length()>7&&data.getParam().indexOf("?")==0){
                //截取?后面5个字符判断是否为token
                String str = data.getParam().substring(data.getParam().indexOf("?") + 1, 6);
                if ("token".equals(str)) {
                    Boolean flag = false;
                    //截取=后面字符串校验token是否有效
                    String token = data.getParam().substring(data.getParam().indexOf("=") + 1, data.getParam().length());
                    flag = TokenUtils.verify(token, "jx");
                    if (flag) {
                        code = 0;
                        String username = TokenUtils.getUsername(token);
                        log.info("token验证成功...客户端开始播放...,用户名为:" + username);
                        return code;
                    } else {
                        log.info("token验证失败...禁止客户端播放...");
                        return code;
                    }
                } else {
                    log.info("请求未携带token...禁止客户端播放");
                    return code;
                }
            }else {
                log.info("请求未携带token...禁止客户端播放");
                return code;
            }

        } else {
            log.info("推流应用名称错误...禁止客户端播放");
            return code;
        }
    }
    @ResponseBody
    @RequestMapping("/stop")
    /*
    * 客户端停止播放时触发
    * 官方介绍客户端停止播放时触发,可再次播放,实际通过PC端和移动端谷歌/火狐浏览器(webRTC协议拉流仅支持这俩浏览器)测试貌似仅服务器主动踢流时会触发此回调,
    * 客户端端主动暂停后可再播放,不会触发此回调,客户端关闭窗口会触发此回调
    * */
    public int on_stop(@RequestBody String data){
        log.info("客户端停止播放"+data);
        return 0;
    }
}

startup class

package com.example.live;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LiveApplication {

    public static void main(String[] args) {
        SpringApplication.run(LiveApplication.class, args);
    }

}

front page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主播端</title>

    <style>

        .stream{
            width: 100%;
            height: 50px;
            line-height: 50px;
            text-align: center;
            background-color: #0e90d2;
        }
        .div_btn{
            width: 100%;
            height: 30px;
            margin-top: 30px;
        }
        .btn{
            background-color: #2f96b4;
            width: 20%;
            height: 100%;
            text-align: center;
            margin-left: 40%;;
        }
        .push_url{
            width: 100%;
            height: 30px;
            margin-top: 50px;

        }
        span{
            display: inline-block;
            width: 20%;
            height: 30px;
            line-height: 30px;
            font-size: 18px;
            text-align: right;
            margin-left: 15%;
            margin-right: 0;
        }
        .input{

            display: inline-block;
            width: 50%;
            height: 29px;
            line-height: 29px;
            padding: 0;
            margin:0;
            border-width: 1px;
        }
        .tips{
            width: 100%;
            height: 50px;
        }
        .tips>div{
            width: 80%;
            height: 100%;
            margin: 0 10%;
        }
        .tips>div>span{
            width: 100%;
            height: 100%;
            font-size: 18px;
            line-height: 50px;
            text-align: center;
            margin-left: 0;
            color: red;
        }
    </style>
</head>
<body>
    <div class="stream">主播推流端</div>
    <div class="div_btn">
        <button class="btn" onclick="getURL()">点击获取推流地址</button>
    </div>
    <div class="push_url">
        <span>推流地址:</span><input class="input" value=""/>
    </div>
    <div class="tips">
        <div>
            <span class="tips_span"></span>
        </div>
    </div>

</body>
<script type="text/javascript" src="/static/js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" >
    $(function(){
        $("input").val("");
        var str = null;

    })
    /*
    * 请求获取推流地址
    * */
    function getURL() {
        var url="https://localhost:400/getUrl";
        $.get(
            url,
            (data)=>{
                $("input").val(data);
                str = data;
                var str1 = str.toString().substring(0,25);
                var str2 = str.toString().substring(26,str.toString().length);
                if(str2.toString().length>10){
                    var text = "温馨提示:PC端请使用OBS软件推流,其中推流地址为:"+str1+",密钥为:"
                                +str2.toString().substring(0,10)+"...";
                }else {
                    var text = "温馨提示:PC端请使用OBS软件推流,其中推流地址为:"+str1+",密钥为:"+str2;
                }
                $(".tips_span").hide();
                $(".tips_span").text(text);
                $(".tips_span").fadeIn(2000);
            }
        )
    }

</script>
</html>

application.properties configuration

# 应用名称
spring.application.name=live
# 应用服务 WEB 访问端口
#server.port=8080
#端口号HTTPS默认端口号为443,本机443端口被占用
server.port=400
#你生成的证书名字
server.ssl.key-store=tomcat.keystore
#密钥库密码
server.ssl.key-store-password=123456
server.ssl.keyStoreType=JKS
#别名
server.ssl.keyAlias=tomcat

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>live</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>live</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
       <!-- <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>-->

        <!--jwt依赖-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.example.live.LiveApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

3. Start springboot

Visit https://192.168.5.104:400 to get live streaming

 OBS tool streaming

 The callback function takes effect

 

pull flow 

Close the player, OBS stops streaming

 Modify the wrong token, OBS streaming attempt authentication 

 RTMP push stream HTTP_FLV pull stream, the delay is scary

 RTMP push stream RTC pull stream is much faster. . . Um. . It is related to the server environment. . .

Guess you like

Origin blog.csdn.net/weixin_44341110/article/details/120829847