1,安装参考播客:https://blog.csdn.net/u012946310/article/details/81535751
2,安装完成后下载 nginx-rtmp-module 模块(我这里的目录是在/usr/local/nginx-1.15.0 下面)
cd /usr/local/nginx-1.15.0/
nginx-rtmp-module 的官方 github 下载地址:https://github.com/arut/nginx-rtmp-module
这里使用git进行下载:
git clone https://github.com/arut/nginx-rtmp-module.git
( 如果没有安装git使用yun进行安装,yum install git)
3,重新编译 nginx,添加 nginx-rtmp-module 模块
./configure --prefix=/usr/local/nginx --add-module=nginx-rtmp-module --with-http_ssl_module
make
make install
4,nginx 与 nginx-rtmp-module 模块安装完成,修改 nginx.conf 配置文件
#在最外层增加如下配置
rtmp {
server {
listen 1935; #监听的端口
chunk_size 4000;
#rtmp配置
application live{ #rtmp推流请求路径 (切记路径错了会推不上流)
live on; #开启实时
hls on; #开启hls
hls_path /www/rtmp; #rtmp推流请求路径,文件存放路径
hls_fragment 5s; #每个TS文件包含5秒的视频内容
#开始推流回调,url地址为java接口
on_publish http://192.168.2.101:6540/rtmp/uploadRtmp;
#推流停止回调,url地址为java接口
on_done http://192.168.2.101:6540/rtmp/uploadRtmp;
}
}
}
至此,nginx 及 nginx-rtmp-module 部分配置完成 。
1,百度OBS下载及安装
2,添加来源,选择媒体源,选择本地的播放文件
3,添加来源后点击控件设置,配置OBS推流地址:
rtmp://192.168.2.122:1935/live // nginx的ip和application地址也就是推流地址
rtmp?token=123456 // 流名称,可以作为会话名称并且可以传递参数验证权限
单击开始推流,验证,查看服务器目录已经存在有流文件**(上面nginx配置的 hls_path 地址)**,并且在开始推流之前回调了所配置的java接口代码
package cx.manager.client.demo.controller;
import cx.manager.common.vo.ResultPageVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
@Controller
@RequestMapping("rtmp")
@Slf4j
public class RtmpController {
@RequestMapping("/uploadRtmp")
@ResponseBody
public ResultPageVo uploadRemp(String token, HttpServletRequest request, HttpServletResponse response) {
Map parameterMap = request.getParameterMap();
System.out.println("参数数量:" + parameterMap.size());
/**这三个参数是比较有用的参数
tcurl:rtmp地址
name:"rtmp://ngnix服务器地址/live/id"地址中去除推流地址的最后一个参数,可传入直播用户ID
type:推流类型
*/
System.out.println("tcurl:" + request.getParameter("tcurl"));
System.out.println("name:" + request.getParameter("name"));
System.out.println("type:" + request.getParameter("type"));
//打印所有回调传过来的参数
for (Map.Entry entry : parameterMap.entrySet()) {
System.out.println(entry.getKey() + "====================");
for (String str : entry.getValue()) {
System.out.println("\t" + str);
}
}
return ResultPageVo.success();
// try {
// if (token.equals("123456")) {
// return ResultPageVo.success();
// } else {
// response.setStatus(500);
// return ResultPageVo.failure("token无效");
// }
// } catch (Exception e) {
// response.setStatus(500);
// return ResultPageVo.failure("token无效");
// }
}
@GetMapping("/video")
public String video(ModelMap modelMap) {
modelMap.put("videoUrl", "rtmp://rtmp.test.orancrab.com:1935/live/rtmp");
return "video";
}
}
@ApiModel(value = "ResultPageVo", description = "页面通用响应VO")
public class ResultPageVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "响应码 1:成功 0:失败,其他根据实际情况自定义在ResultCode中,同时每个接口的特殊返回码需要注释。", dataType = "int", required = true)
private Integer status;
@ApiModelProperty(value = "消息提示", dataType = "string")
private String message = "";
@ApiModelProperty(value = "响应数据", dataType = "object")
private T data;
@ApiModelProperty(value = "响应数据", dataType = "object")
private Object spareData;
private ResultPageVo(Integer status) {
this.status = status;
}
private ResultPageVo(Integer status, String message) {
this.status = status;
this.message = message;
}
private ResultPageVo(Integer status, T data) {
this.status = status;
this.data = data;
}
private ResultPageVo(Integer status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
public static ResultPageVo success() {
return new ResultPageVo(ResultCode.SUCCESS.status(), ResultCode.SUCCESS.message());
}
public static ResultPageVo success(T data) {
return new ResultPageVo(ResultCode.SUCCESS.status(), ResultCode.SUCCESS.message(), data);
}
public static ResultPageVo success(String message, T data) {
return new ResultPageVo(ResultCode.SUCCESS.status(), message, data);
}
public static ResultPageVo failure() {
return new ResultPageVo(ResultCode.FAILURE.status(), ResultCode.FAILURE.message());
}
public static ResultPageVo failure(String message) {
return new ResultPageVo(ResultCode.FAILURE.status(), message);
}
public static ResultPageVo failure(T data) {
return new ResultPageVo(ResultCode.FAILURE.status(), ResultCode.FAILURE.message(), data);
}
public static ResultPageVo failure(String message, T data) {
return new ResultPageVo(ResultCode.FAILURE.status(), message, data);
}
public static ResultPageVo create(ResultCode resultCode) {
return new ResultPageVo(resultCode.status(), resultCode.message());
}
public static ResultPageVo create(ResultCode resultCode, T data) {
return new ResultPageVo(resultCode.status(), resultCode.message(), data);
}
public static ResultPageVo create(Integer status) {
return new ResultPageVo(status);
}
public static ResultPageVo create(Integer status, String message) {
return new ResultPageVo(status, message);
}
public static ResultPageVo create(Integer status, T data) {
return new ResultPageVo(status, data);
}
public static ResultPageVo create(Integer status, String message, T data) {
return new ResultPageVo(status, message, data);
}
public Integer getStatus() {
return status;
}
public String getMessage() {
return message;
}
public T getData() {
return data;
}
public Object getSpareData() {
return spareData;
}
public void setSpareData(Object spareData) {
this.spareData = spareData;
}
@Override
public String toString() {
return "ResultPageVo{" +
"status=" + status +
", message='" + message + '\'' +
", data=" + data +
", spareData=" + spareData +
'}';
}
}
public enum ResultCode {
//带消息的响应码
SUCCESS(1, "成功"),
FAILURE(0, "失败"),//其他失败,全部走这里 请注意
TOKEN_ERROR(10, "token验证失败"),
PERMISSION_ERROR(11, "权限不足");
private Integer status;
private String message;
ResultCode(Integer status) {
this.status = status;
}
ResultCode(Integer status, String message) {
this.status = status;
this.message = message;
}
public Integer status() {
return status;
}
public String message() {
return message;
}
}
只做了个简单测试,可根据这些参数完善后台,实现用户推流开始把用户ID和推流地址存入redis或其他高性能存储,前台展示当前直播的用户,点击用户返回推流地址,实现直播,用spring boot写的
拉流地址:rtmp://192.168.2.122:1935/live/rtmp
1,百度VLC下载及安装
2,安装好之后选择媒体 -> 流,选择网络,输入流地址后开始拉流
输入后点击串流直接下一步到完成就好了
最后看到已经拉取到流了
3,使用网络播放器进行验证
(网络拉流播放器地址:http://www.cutv.com/demo/live_test.swf)
RTMP拉流地址:rtmp://192.168.2.122:1935/live/rtmp(和VLC拉流地址一样)
验证通过,已经可以拉到流了。。。。。。。
4,使用移动端app设置推流与h5拉流测试
安卓推流App下载地址(小米9可用,其他机型没试,IOS自己在网上找):https://download.csdn.net/download/u012946310/11170486
下载好后打开APP点击左上角 -> Profile 里面编辑推流地址和其他参数,推流地址就是之前的推流地址
4-1,h5播放页面代码(页面需要放在服务容器里面运行,如nginx、tomcat等,电脑需要安装flash,手机端不支持),拉流地址改成自己的流地址就可以了
视频直播
直播间
本地测试
推流命令:/usr/local/ffmpeg/bin/ffmpeg -re -i rtmp.mp4 -f flv rtmp://192.168.2.122:1935/live/rtmp?token=123456
拉流地址:rtmp://192.168.2.122:1935/live/rtmp?token=123456
远程测试
推流地址:
播放地址:http://cx.manager.test.orancrab.com/client_demo/rtmp/video
到此一个简单的 nginx + rtmp 搭建流媒体服务器,完成!虽然不是一个完整的例子,但是核心就是以上这些,大家可以在次基础上扩展,有什么不妥的地方希望大家指正,共同学习