目录
一、阿里云视频直播
1、什么是视频直播
视频直播服务(ApsaraVideo Live)是基于领先的内容接入、分发网络和大规模分布式实时转码技术打造的音视频直播平台,提供便捷接入、高清流畅、低延迟、高并发的音视频直播服务。
2、视频直播架构
-
主播通过采集设备采集直播内容后,通过推流SDK推送直播流,视频直播服务通过边缘推流的方式将直播流推送至阿里云直播中心,推送的视频流通过CDN边缘节点进行加速保证上行传输的稳定性。
-
视频流推送至阿里云直播中心后,可按需对视频流进行转码、时移、录制、截图等处理。
-
处理好的视频流通过CDN内容分发网络,下发至观众的设备中进行播放。移动端的播放设备可以集成阿里云提供的播放器SDK进行开发。
-
直播视频除了可以进行转码截图等操作外,还可以进行直播转点播的操作,将录制下来的视频转至点播系统中再进行点播播放和短视频云剪辑。方便直播与短视频内容生产和传播的联动。
3、官方文档链接
二、使用流程
1、使用条件
- 已有阿里云账号
- 账号已经实名认证
- 准备好2个已完成备案的域名
- 开通视频直播服务
2、添加推流域名和播流域名
进入直播控制台,点击域名管理,添加域名
- 首先添加推流域名,业务类型选择推流域名
加速域名填子域名,就是在域名前加上一个随机前缀play,如 play.xxx.com,域名解析地址为xxx.com
- 这里进行DNS解析验证,去解析域名,添加记录
- 选择对应的域名,点击解析域名
- 点击添加记录
- 将需要验证的信息填到这里
记录类型:TXT
主机记录:verification
记录值:verify_xxxxxxxxxxxxxxxxxxxxxx
- 添加添加记录后,这里点击验证
- 如图所示对应的加速域名添加成功
- 然后添加播流域名,业务类型选择播流域名,其他同上
3、配置CNAME
-
这里将记录值复制下来
-
在域名解析这里添加记录,记录类型选择CNAME,记录值为刚刚加速域名添加的随机前缀
我这里播流域名为 play.xxx.com,即为play
推流域名为 push.xxx.com,即为push,
然后将对应的记录值粘贴过来即可
- 刚开始会显示配置中,如图所示配置成功
4、关联推流域名和播流域名
- 选择一个播流域名,单击域名配置
- 选择推流信息一栏,点击添加推流信息
- 选择推流域名,点击确定保存
5、生成推流地址和播放地址
- 选择工具箱,点击地址生成器
- 选择播流域名和推流域名
- AppName:随便填
- StringName:随便填(如0000)
- 生成地址示例如下:
6、开始测试推流和播流
- 快速入门链接.
- OBS推流工具下载链接. (window 64版)
- VLC media player播流工具下载链接.
- 打开OBS,选择文件,打开设置
- 服务:自定义
- 推流地址例子:rtmp://push.aliyunlive.com/app/stream?auth_key=1543302081-0-0-9c6e7c8190c10bdfb3c0xxxxxx
- 服务器:rtmp://push.aliyunlive.com/app/
- 串流密钥:stream?auth_key=1543302081-0-0-9c6e7c8190c10bdfb3c0xxxxxx(以AppName为分割)
- 点击+号,选择视频采集设备
若想传本地视频,可以选择媒体源,打开选择一个mp4格式的视频文件即可
- 创建好后,确定保存
- 右下方控件,点击开始推流
- 打开 VLC,选择媒体,单击打开网络串流
- 此处以FLV播流地址为例
- 将播流地址粘贴到网络 URL处
- 效果如下
右边OBS通过推流,将摄像头的画面推送出去,左侧VLC通过播流地址播放实时播放画面
三、Java实现推流和播流地址生成
1、添加依赖
<!-- Hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.0.12</version>
</dependency>
<!-- aliyun 核心sdk-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-vod</artifactId>
<version>2.15.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20170516</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
2、配置application.properties
#阿里云直播配置
#推流域名
live.push.domain=xxx
#推流鉴权url key
live.push.ident.key=xxx
#拉流域名
live.pull.domain=xxx
#拉流鉴权url key
live.pull.ident.key=xxx
#直播测试appName
live.appName=xxx
#直播测试streamName{直播类型}{类型id}
live.streamName=xxx
#鉴权url的有效时间(秒),默认30分钟,1800秒
live.ident.url.validTime=1800
推流域名:前面的生成的push.xxx.com
播流(拉流)域名:前面的生成的pull.xxx.com
推流鉴权url key:点击域名管理,域名配置,访问控制,url鉴权查看key
播流鉴权url key:同上,选择播流域名查看
appName:自定义
streamName:自定义(前端传参就是sourceId,就是根据这streamName(sourceId)生成不同的直播房间,推流地址)
3、创建LIveConfig
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Component
public class LiveConfig implements Serializable {
/**
* 推流域名
*/
@Value("${live.push.domain}")
private String livePushDomain;
/**
* 拉流域名
*/
@Value("${live.pull.domain}")
private String livePullDomain;
/**
* 直播测试appName
*/
@Value("${live.appName}")
private String liveAppName;
/**
* 直播测试streamName{直播类型}_{类型id}
*/
// @Value("${live.streamName}")
// private String liveStreamName;
/**
* 推流鉴权url key
*/
@Value("${live.push.ident.key}")
private String livePushIdentKey;
/**
* 拉流鉴权url key
*/
@Value("${live.pull.ident.key}")
private String livePullIdentKey;
/**
* 鉴权url的有效时间(秒),默认30分钟,1800秒 key
*/
@Value("${live.ident.url.validTime}")
private Integer liveIdentUrlValidTime;
public String getLivePushDomain() {
return livePushDomain;
}
public void setLivePushDomain(String livePushDomain) {
this.livePushDomain = livePushDomain;
}
public String getLivePullDomain() {
return livePullDomain;
}
public void setLivePullDomain(String livePullDomain) {
this.livePullDomain = livePullDomain;
}
public String getLiveAppName() {
return liveAppName;
}
public void setLiveAppName(String liveAppName) {
this.liveAppName = liveAppName;
}
public String getLivePushIdentKey() {
return livePushIdentKey;
}
public void setLivePushIdentKey(String livePushIdentKey) {
this.livePushIdentKey = livePushIdentKey;
}
public String getLivePullIdentKey() {
return livePullIdentKey;
}
public void setLivePullIdentKey(String livePullIdentKey) {
this.livePullIdentKey = livePullIdentKey;
}
public Integer getLiveIdentUrlValidTime() {
return liveIdentUrlValidTime;
}
public void setLiveIdentUrlValidTime(Integer liveIdentUrlValidTime) {
this.liveIdentUrlValidTime = liveIdentUrlValidTime;
}
}
4、创建LiveUtil
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LiveUtil {
private static final Logger log = LoggerFactory.getLogger(LiveUtil.class);
/**
* 推拉流地址示例:
* rtmp://www.ttest.ygdjonline.com/a/a?auth_key=1558065152-0-0-c3cb54d946c0590ca9aeee63573201ee
* 播流地址
* 原画
* rtmp://www.btest.ygdjonline.com/a/a?auth_key=1558065152-0-0-fc711455c0815aeb581385f33451d5b4
* http://www.btest.ygdjonline.com/a/a.flv?auth_key=1558065152-0-0-221abff1da1ee32151e365cf0dd42a53
* http://www.btest.ygdjonline.com/a/a.m3u8?auth_key=1558065152-0-0-72124fcc3aee3404b0d65dcc114e207f
*/
/**
* 根据源id创建该id的推流url
*
* @param sourceId
* @param liveConfig
* @return
*/
public static String createPushUrl(String sourceId, LiveConfig liveConfig) {
// 推流域名
String pushDomain = liveConfig.getLivePushDomain();
// 应用名称
String appName = liveConfig.getLiveAppName();
// 流名称
String streamName = StrUtil.format(sourceId);
// 推流签名key
String pushIdentKey = liveConfig.getLivePushIdentKey();
// 签名url有效时间
Integer identUrlValidTime = liveConfig.getLiveIdentUrlValidTime();
//log.info("签名url有效时间" + identUrlValidTime);
// 计算过期时间
String timestamp = String.valueOf((System.currentTimeMillis() / 1000) + identUrlValidTime);
// log.info("计算过期时间" + timestamp);
// 组合推流域名前缀
//rtmp://{pushDomain}/{appName}/{streamName}
String rtmpUrl = StrUtil.format("rtmp://{}/{}/{}", pushDomain, appName, streamName);
// 组合md5加密串
///{appName}/{streamName}-{timestamp}-0-0-{pushIdentKey}
String md5Url = StrUtil.format("/{}/{}-{}-0-0-{}", appName, streamName, timestamp, pushIdentKey);
log.info("组合md5加密串"+md5Url);
// md5加密
String md5Str = DigestUtil.md5Hex(md5Url);
// log.info("md5加密串,md5Url=" + md5Url + "------md5加密结果,md5Str=" + md5Str);
// 组合最终鉴权过的推流域名
// {rtmpUrl}?auth_key={timestamp}-0-0-{md5Str}
String finallyPushUrl = StrUtil.format("{}?auth_key={}-0-0-{}", rtmpUrl, timestamp, md5Str);
log.info("最终鉴权过的推流域名=" + finallyPushUrl);
return finallyPushUrl;
}
/**
* 创建拉流域名,key=rtmpUrl、flvUrl、m3u8Url,代表三种拉流类型域名
*
* @param sourceId
* @param liveConfig
* @return
*/
public static String createPullUrl(String sourceId, LiveConfig liveConfig) {
// 拉流域名
String pullDomain = liveConfig.getLivePullDomain();
// 应用名称
String appName = liveConfig.getLiveAppName();
// 流名称
String streamName = StrUtil.format(sourceId);
// 拉流签名key
String pullIdentKey = liveConfig.getLivePullIdentKey();
// 签名url有效时间
Integer identUrlValidTime = liveConfig.getLiveIdentUrlValidTime();
// 计算过期时间
String timestamp = String.valueOf((System.currentTimeMillis() / 1000) + identUrlValidTime);
// 组合通用域名
// {pullDomain}/{appName}/{streamName}
String pullUrl = StrUtil.format("{}/{}/{}", pullDomain, appName, streamName);
//log.info("组合通用域名,pullUrl=" + pullUrl);
// 组合md5加密串
// /{appName}/{streamName}-{timestamp}-0-0-{pullIdentKey}
String md5Url = StrUtil.format("/{}/{}-{}-0-0-{}", appName, streamName, timestamp, pullIdentKey);
String md5FlvUrl = StrUtil.format("/{}/{}.flv-{}-0-0-{}", appName, streamName, timestamp, pullIdentKey);
String md5M3u8Url = StrUtil.format("/{}/{}.m3u8-{}-0-0-{}", appName, streamName, timestamp, pullIdentKey);
// md5加密
String md5Str = DigestUtil.md5Hex(md5Url);
String md5FlvStr = DigestUtil.md5Hex(md5FlvUrl);
String md5M3u8Str = DigestUtil.md5Hex(md5M3u8Url);
//log.info("md5加密串,md5Url =" + md5Url + " ------ md5加密结果,md5Str=" + md5Str);
//log.info("md5加密串,md5FlvUrl =" + md5FlvUrl + " ------ md5加密结果,md5FlvStr=" + md5FlvStr);
//log.info("md5加密串,md5M3u8Url=" + md5M3u8Url + " ------ md5加密结果,md5M3u8Str=" + md5M3u8Str);
// 组合三种拉流域名前缀
// rtmp://{pullUrl}?auth_key={timestamp}-0-0-{md5Str}
String rtmpUrl = StrUtil.format("rtmp://{}?auth_key={}-0-0-{}", pullUrl, timestamp, md5Str);
// http://{pullUrl}.flv?auth_key={timestamp}-0-0-{md5FlvStr}
String flvUrl = StrUtil.format("http://{}.flv?auth_key={}-0-0-{}", pullUrl, timestamp, md5FlvStr);
// http://{pullUrl}.m3u8?auth_key={timestamp}-0-0-{md5M3u8Str}
String m3u8Url = StrUtil.format("http://{}.m3u8?auth_key={}-0-0-{}", pullUrl, timestamp, md5M3u8Str);
log.info("最终鉴权过的拉流rtmp域名=" + rtmpUrl);
log.info("最终鉴权过的拉流flv域名 =" + flvUrl);
log.info("最终鉴权过的拉流m3u8域名=" + m3u8Url);
return flvUrl;
}
}
5、创建LiveAddress
import java.io.Serializable;
public class LiveAddress implements Serializable {
private String pushUrl;
private String pullUrl;
public String getPushUrl() {
return pushUrl;
}
public void setPushUrl(String pushUrl) {
this.pushUrl = pushUrl;
}
public String getPullUrl() {
return pullUrl;
}
public void setPullUrl(String pullUrl) {
this.pullUrl = pullUrl;
}
}
6、创建LiveController
import com.dimpt.base.util.LiveConfig;
import com.dimpt.base.util.LiveUtil;
import com.dimpt.rest.model.LiveAddress;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/live")
public class LiveController {
@Autowired
private LiveConfig liveConfig;
/**
* 生成推流播流地址
* sourceId 在这里我将它设置为房间号
*/
@GetMapping("/save_Live")
public LiveAddress save_Live(@RequestParam("sourceId") String sourceId){
//生成推流地址
String pushUrl = LiveUtil.createPushUrl(sourceId, liveConfig);
//生成播流地址
String pullUrl = LiveUtil.createPullUrl(sourceId, liveConfig);
LiveAddress liveAddress = new LiveAddress();
liveAddress.setPushUrl(pushUrl);
liveAddress.setPullUrl(pullUrl);
return liveAddress;
}
}
7、测试运行
sourceId=任意值,推流播流地址就生成了