web和uniapp接入腾讯云直播
准备工作:首选需要一个通过ICP备案的域名,如minglina.com。开通腾讯云云直播服务。
一、添加推流域名、播放域名
使用云直播服务,至少需要2个域名,一个作为推流域名,一个作为播放域名,且推流和播放不能使用相同的域名。但可以通过二级域名来进行区分,不局限于是否两个子域名,例如可以使用 push.minglina.com
作为推流域名,将 play.minglina.com
作为播放域名。
参考域名管理。
二、生成推流、播放地址
1.生成推流地址
云直播控制台提供地址生成器功能,支持通过填写地址拼接信息,辅助用户快速生成推流/播放地址。其中直播地址主要由域名(domain)、应用名称(AppName)、流名称(StreamName)以及鉴权 Key 组成。
域名管理,选择推流域名->推流配置,获取推流的鉴权key。
参考直播推流 。
2.生成播放地址
域名管理,选择播放域名->访问控制,获取播放的鉴权key。
参考 推流url 。
3.代码生成推流、播放地址:
import cn.hutool.core.date.DateUtil;
import com.ynfy.buss.live.entity.vo.LiveVO;
import com.ynfy.buss.live.service.ILiveService;
import com.ynfy.common.utils.LiveUtil;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;/*** @Description: 直播服务* @Author: jeecg-boot* @Date: 2025-06-11* @Version: V1.0*/
@Slf4j
@Service
public class LiveServiceImpl implements ILiveService {/*** 是否开启直播功能*/@Value("${tencent.cloud.live.enable}")public Boolean liveEnable;/*** 推流域名*/@Value("${tencent.cloud.live.pushDomain}")public String pushDomain;/*** 播放域名*/@Value("${tencent.cloud.live.playDomain}")public String playDomain;/*** app名称*/@Value("${tencent.cloud.live.appName}")public String appName;/*** 推流鉴权key*/@Value("${tencent.cloud.live.pushKey}")public String pushKey;/*** 播放鉴权key*/@Value("${tencent.cloud.live.playKey}")public String playKey;/*** 获取推流、播放 URL** @param streamName*/@Overridepublic LiveVO getPushPlayUrl(String streamName, String endTime) {if (!liveEnable) {throw new JeecgBootException("直播功能暂未开放");}try {//获取推流鉴权信息String pushAuthInfo = LiveUtil.getSafeUrl(pushKey, streamName, DateUtil.parseDateTime(endTime).getTime() / 1000);//获取播放鉴权信息String playAuthInfo = LiveUtil.getSafeUrl(playKey, streamName, DateUtil.parseDateTime(endTime).getTime() / 1000);//推流地址String rtmpPushUrl = new StringBuilder().append("rtmp://").append(pushDomain).append("/").append(appName).append("/").append(streamName).append("?").append(pushAuthInfo).toString();String webRTCPushUrl = new StringBuilder().append("webrtc://").append(pushDomain).append("/").append(appName).append("/").append(streamName).append("?").append(pushAuthInfo).toString();//播放地址String rtmpPlayUrl = new StringBuilder().append("rtmp://").append(playDomain).append("/").append(appName).append("/").append(streamName).append("?").append(playAuthInfo).toString();String webRTCPlayUrl = new StringBuilder().append("webrtc://").append(playDomain).append("/").append(appName).append("/").append(streamName).append("?").append(playAuthInfo).toString();String hlsPlayUrl = new StringBuilder().append("http://").append(playDomain).append("/").append(appName).append("/").append(streamName).append(".m3u8?").append(playAuthInfo).toString();LiveVO live = new LiveVO();live.setRtmpPushUrl(rtmpPushUrl);live.setWebrtcPushUrl(webRTCPushUrl);live.setRtmpPlayUrl(rtmpPlayUrl);live.setWebrtcPlayUrl(webRTCPlayUrl);live.setHlsPlayUrl(hlsPlayUrl);return live;} catch (Exception e) {e.printStackTrace();log.error("获取推流 URL出错:{}", e.getMessage());throw new JeecgBootException("获取推流、播放 URL出错");}}
}
生成权限信息工具类:
import java.security.MessageDigest;/*** 腾讯云直播工具类*/
public class LiveUtil {public static void main(String[] args) {System.out.println(getSafeUrl("txrtmp", "11212122", 1469762325L));}private static final char[] DIGITS_LOWER ={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};/** KEY+ streamName + txTime*/public static String getSafeUrl(String key, String streamName, long txTime) {String input = new StringBuilder().append(key).append(streamName).append(Long.toHexString(txTime).toUpperCase()).toString();String txSecret = null;try {MessageDigest messageDigest = MessageDigest.getInstance("MD5");txSecret = byteArrayToHexString(messageDigest.digest(input.getBytes("UTF-8")));} catch (Exception e) {e.printStackTrace();}return txSecret == null ? "" :new StringBuilder().append("txSecret=").append(txSecret).append("&").append("txTime=").append(Long.toHexString(txTime).toUpperCase()).toString();}private static String byteArrayToHexString(byte[] data) {char[] out = new char[data.length << 1];for (int i = 0, j = 0; i < data.length; i++) {out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];out[j++] = DIGITS_LOWER[0x0F & data[i]];}return new String(out);}
}
三、前端页面推流
1.首选需调用上述接口生成推流和播放地址。
2.推流
web推流
云直播提供了推流 SDK TXLivePusher
用于 Web 推流。 index.html页面的body 部分引入TXLivePusher js脚本。
需要获取摄像头使用权限。推流页面代码:
<template><divid="local_video"ref="local_video"style="width: 100%; height: 500px; display: flex; align-items: center; justify-content: center;background-color: #bababa"></div><div style="display: flex;justify-content: center"><a-space style="margin-top: 30px;padding: 0 20px" size="large"><a-button type="dashed" block preIcon="ant-design:video-camera-outlined" @click="startLive"v-if="liveIng == false">开始直播</a-button><a-button type="primary" danger preIcon="ant-design:minus-circle-filled" @click="stopLive"v-else>停止直播</a-button></a-space></div></template><script lang="ts" setup>
import {ref} from "vue";
import {useMessage} from "@/hooks/web/useMessage";const record = ref<any>(); //传入你的数据
const local_video = ref();
const livePusher = ref<any>();
const {createMessage} = useMessage();
const liveIng = ref<boolean>(false);onMounted(() => {//初始化播放器initLivePusher()});//初始化播放器
function initLivePusher() {livePusher.value = new TXLivePusher();livePusher.value.setRenderView('local_video');livePusher.value.videoView.muted = true;// // 设置视频质量livePusher.value.setVideoQuality('720p');// // 设置音频质量livePusher.value.setAudioQuality('standard');// // 自定义设置帧率livePusher.value.setProperty('setVideoFPS', 25);
}//开始直播
function startLive() {// 采集完摄像头和麦克风之后自动推流Promise.all([livePusher.value.startCamera(), livePusher.value.startMicrophone()]).then(function () {livePusher.value.startPush(record.value.webrtcPushUrl);});observerLive()
}//停止直播
function stopLive() {livePusher.value.stopPush();// 关闭摄像头livePusher.value.stopCamera();// 关闭麦克风livePusher.value.stopMicrophone();liveIng.value = false
}function observerLive() {livePusher.value.setObserver({onError: function (status, message) {createMessage.error(message)stopLive()},onPushStatusUpdate: function (status, message) {if (status == 2) {liveIng.value = truecreateMessage.success("与服务器连接成功,直播推流开始啦!")}if (status == 0) {createMessage.warn("与服务器连接断开,已关闭直播推流!")}},});
}
</script><style lang="less" scoped>
</style>
参考 TXLivePusher 。
另一种是OBS工具推流。下载后添加源。
设置直播推流地址和推流码。
设置好后点击开始直播。
四、播放
1.web播放,使用腾讯云视立方SDK播放。
进入视立方控制台申请web端License:
安装 tcplayer 的 npm 包:
npm install tcplayer.js
licenseUrl为上述环节申请的。tcplayer播放器拉流WebRTC地址播放。
<template><div class="course-banner-inner"><section class="section-study"><div class="wrapper-player"><div class="player-container"><div class="loki-player-wrapper" id="loki-player-wrapper"><div class="tc_player" id="course-playback-player"style="transform: translate(0px, 0px);"><video id="live-player" width="1200" height="618" preload="auto" playsinlinewebkit-playsinline></video></div></div></div></div></section></div>
</template><script lang="ts" setup>
import TCPlayer from "tcplayer.js";
import "tcplayer.js/dist/tcplayer.min.css";
import { onBeforeUnmount, ref, watchEffect } from "vue";import { useMessage } from "@/hooks/web/useMessage";const { createMessage, } = useMessage();
const livePlayer = ref<any>();const props = defineProps({form: {type: Object,default: {}},
});watchEffect(() => {if (props.form?.isLive) { //直播课程创建直播播放器createLivePlayer(props.form.webrtcPlayUrl);}
});//创建直播播放器
function createLivePlayer(playUrl) {// live-player 为播放器容器 IDlivePlayer.value = TCPlayer("live-player", {sources: [{src: playUrl // 播放地址}],licenseUrl: "https://license.vod2.myqcloud.com/license/v4/13013228971_1/v_cube.license" // license 地址,必传});livePlayer.value.on("error", function(e) {createMessage.error("直播出错啦:" + (e.data.source.message ?? "") + "(代码:" + e.data.code + ")");});livePlayer.value.on("play", function() {createTimer();});
}//页面销毁前解除监听
onBeforeUnmount(async () => {//销毁播放器if (livePlayer.value) {livePlayer.value.dispose();}
});
</script><style scoped></style>
参考 Tcplayer。
2.uniapp播放。
一种方式是<live-player> 标签。<live-player> 是小程序内部用于支持音视频下行(播放)能力的功能标签。开通该标签需要满足规定的类目,详见live-player。
另一种是小程序端和app端都使用video标签播放。在前面获取到的播放地址中已经生成了HLS播放地址。
<template><view class="player-content"><video id="live-player" ref="livePlayer" :src="playUrl" autoplay controls class="live-player" @error="handleError"></video></view>
</template><script>export default {props: ['form'],data() {return {playUrl: ""};},watch: {form: {handler(val, oldVal) {if (val?.isLive) { //直播课程创建直播播放器if (val?.hlsPlayUrl) {//替换直播播放地址,从服务器代理获取推流this.playUrl = val.hlsPlayUrl.replace("http://play.minglin.com","https://exam.minglin.com/hls-player");}}},immediate: true},},methods: {handleError() {uni.showToast({icon: "none",title: "获取直播推流失败"})},}}
</script><style lang="scss" scoped>.player-content {position: relative;width: 750rpx;height: 450rpx;display: flex;background-size: 100% 100%;.live-player {width: 100%;height: 100%;position: relative;}}
</style>
需要注意的是生成的HLS播放地址是http的,小程序支持https。所以需要转换下,然后通过nginx代理播放直播流。
五、效果
1.web端:
2.uniapp端: