|
效果预览
视频画面
网络请求
代码实现
ZLMRTCClient.js
当前使用的版本:
1.0.1 Mon Mar 27 2023 19:11:59 GMT+0800
首先需要修改 ZLMRTCClient.js 的代码,解决由于网络导致播放失败时无法触发 WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED 事件的问题。
修改前:
修改后:
修改内容:
- // 添加 catch()
- axios({
- }).then(() => {
- }).catch(() => {
- // 网络异常时触发事件
- this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, null);
- });
复制代码 video-preview.js
- // 2024-05-30
- // 初始版本
- /**
- * @typedef CacheItem
- * @property {HTMLElement | null} element
- * @property {ZLMPlayer | null} player
- * @property {number} usedAt
- */
- /** @typedef {InstanceType<typeof ZLMRTCClient.Endpoint>} ZLMPlayer */
- /** 画布渲染间隔 */
- const INTERVAL_RENDER = 100;
- /** 画布分辨率更新间隔 */
- const INTERVAL_RESIZE = 1000;
- /** 检测画布是否在页面上间隔 */
- const INTERVAL_WATCH_CANVAS = 1000;
- /** 检测视频是否存在调用间隔 */
- const INTERVAL_WATCH_VIDEO = 20000;
- /** 模块名称 */
- const PREFIX = '[video-preview]';
- /** 重新播放间隔 */
- const RESTART_TIMEOUT = 2000;
- /** ZLM 客户端 */
- const ZLMRTCClient = window.ZLMRTCClient;
- /**
- * @desc 缓存信息列表
- * @type {Record<string, CacheItem | null>}
- */
- export const cacheList = {};
- /**
- * @description 初始化播放器
- * @param {string} url 视频流地址
- */
- function initPlayer(url = '') {
- try {
- if (!url) {
- throw new Error('缺少 url 参数');
- }
- /** 是否主动停止播放 */
- let isStoped = false;
- /**
- * @description 初始化 & 更新数据
- * @param {CacheItem} cache
- */
- let fnInit = (cache) => {
- let element = document.createElement('video');
- // 开启自动播放
- // 注:不能用 `setAttribute`,否则没效果
- element.autoplay = true;
- element.controls = false;
- element.muted = true;
- // 添加到页面,否则无法播放
- element.setAttribute('style', 'position: fixed; top: 0; left: 0; width: 0; height: 0');
- document.body.appendChild(element);
- let player = new ZLMRTCClient.Endpoint({
- // video 标签
- element: element,
- // 是否打印日志
- debug: false,
- // 流地址
- zlmsdpUrl: url,
- // 功能开关
- audioEnable: false,
- simulcast: false,
- useCamera: false,
- videoEnable: true,
- // 仅查看,不推流
- recvOnly: true,
- // 推流分辨率
- resolution: { w: 1280, h: 720 },
- // 文本收发
- // https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/send
- usedatachannel: false,
- });
- // // 监听事件:ICE 协商出错
- // player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, function () {
- // console.error(PREFIX, 'ICE 协商出错')
- // });
- // 监听事件:获取到了远端流,可以播放
- player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (event) {
- console.log(PREFIX, '播放成功', event.streams);
- });
- // 监听事件:offer anwser 交换失败
- player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, function (event) {
- console.error(PREFIX, 'offer anwser 交换失败', event);
- // 当前没有主动停止
- if (!isStoped) {
- // 停止播放
- stopPlayer(player, element);
- // 重新播放
- setTimeout(() => {
- fnInit(cache);
- }, RESTART_TIMEOUT);
- }
- });
- // 监听事件:RTC 状态变化
- player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, function (state) {
- console.log(PREFIX, 'RTC 状态变化', state);
- // 状态为已断开
- if (state === 'disconnected' && !isStoped) {
- // 停止播放
- stopPlayer(player, element);
- // 重新播放
- setTimeout(() => {
- fnInit(cache);
- }, RESTART_TIMEOUT);
- }
- });
- cache.element = element;
- cache.player = player;
- cache.usedAt = Date.now();
- };
- let cacheItem = cacheList[url];
- if (cacheItem) {
- return cacheItem;
- } else {
- cacheItem = {};
- }
- console.log(PREFIX, '初始化', cacheItem);
- // 初始化
- fnInit(cacheItem);
- // 添加缓存信息
- cacheList[url] = cacheItem;
- // 监听调用情况
- let watchTimer = setInterval(() => {
- let currTime = Date.now();
- let lastTime = cacheItem.usedAt;
- // 一段时间内没有被调用,停止播放
- if (currTime - lastTime > INTERVAL_WATCH_VIDEO) {
- console.debug(PREFIX, '视频没有被调用,停止播放', { url });
- isStoped = true;
- stopPlayer(cacheItem.player, cacheItem.element);
- cacheList[url] = null;
- clearInterval(watchTimer);
- }
- }, INTERVAL_WATCH_VIDEO);
- return cacheItem;
- } catch (error) {
- console.error(PREFIX, '初始化播放器失败:');
- console.error(error);
- return null;
- }
- }
- /**
- * @description 停止播放
- * @param {ZLMPlayer} player
- * @param {HTMLVideoElement} element
- */
- function stopPlayer(player, element) {
- try {
- if (player) {
- console.debug(PREFIX, 'stopPlayer - 停止播放');
- player.close();
- }
- if (element instanceof HTMLVideoElement) {
- console.debug(PREFIX, 'stopPlayer - 移除元素');
- element.remove();
- }
- return true;
- } catch (error) {
- console.error(PREFIX, '停止播放失败:');
- console.error(error);
- return false;
- }
- }
- /**
- * @description 获取视频画面 canvas
- * @param {string} url
- */
- export function getVideoCanvas(url = '') {
- try {
- if (!url) {
- throw new Error('缺少 url 参数');
- }
- let cacheItem = initPlayer(url);
- let canvas = document.createElement('canvas');
- let ctx = canvas.getContext('2d');
- // 背景填充
- canvas.style.backgroundPosition = 'center center';
- canvas.style.backgroundSize = '100% 100%';
- /** 更新画布分辨率 */
- let fnResize = () => {
- let parent = canvas.parentElement;
- let rect = parent ? parent.getBoundingClientRect() : null;
- if (rect) {
- let cWidth = Math.round(canvas.width);
- let cHeight = Math.round(canvas.height);
- let rWidth = Math.round(rect.width);
- let rHeight = Math.round(rect.height);
- if (cWidth !== rWidth || cHeight !== rHeight) {
- // 更新画布分辨率前将画面设置为背景,防止闪烁
- canvas.style.backgroundImage = `url(${canvas.toDataURL('image/png')})`;
- // 更新画布分辨率(将会自动清空画布内容)
- canvas.width = rWidth;
- canvas.height = rHeight;
- }
- }
- };
- if (!cacheItem) {
- throw new Error('获取缓存数据失败');
- }
- // 渲染画面
- let renderTimer = setInterval(() => {
- // 注:
- // 每次渲染都重新获取,防止重连后获取不到新创建的 video 元素
- let video = cacheItem.element;
- let cWidth = canvas.width;
- let cHeight = canvas.height;
- if (document.contains(video)) {
- ctx.drawImage(video, 0, 0, cWidth, cHeight);
- }
- canvas.style.backgroundImage = '';
- cacheItem.usedAt = Date.now();
- }, INTERVAL_RENDER);
- // 更新分辨率
- let resizeTimer = setInterval(fnResize, INTERVAL_RESIZE);
- // 监听元素
- let watchTimer = setInterval(() => {
- if (!document.contains(canvas)) {
- console.debug(PREFIX, '画布已被移除,停止渲染画面', { url });
- clearInterval(renderTimer);
- clearInterval(resizeTimer);
- clearInterval(watchTimer);
- }
- }, INTERVAL_WATCH_CANVAS);
- // 初始化分辨率
- setTimeout(fnResize, 0);
- return canvas;
- } catch (error) {
- console.error(PREFIX, '获取 canvas 失败:');
- console.error(error);
- return null;
- }
- }
复制代码 使用时只需要调用 getVideoCanvas() 获取 canvas,然后插入到 DOM 即可,画布会自适应父元素宽高。
来源:https://www.cnblogs.com/frost-zx/p/-/zlm-rtc-client-multi-video-pull-once
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|