web和uniapp接入腾讯云直播

发布于:2025-06-19 ⋅ 阅读:(18) ⋅ 点赞:(0)

        准备工作:首选需要一个通过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
     */
    @Override
    public 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>
       <div
      id="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 = true
        createMessage.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" playsinline
                     webkit-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 为播放器容器 ID
  livePlayer.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端:

        ​​​​​​​


网站公告

今日签到

点亮在社区的每一天
去签到