萤石云播放器React Native端的使用

520 阅读1分钟

需求分析

最近有个做app播放器,实现视频实时监控语回放的功能需求。

背景:APP是使用React Native开发的,使用萤石云监控设备。 基于我们没有暂时没有原生开发人员支持,采用技术栈为React Native嵌套Webview。 我们目前是敏捷迭代,需要两天开发好功能。

使用技术

在网上找了一个框架,是基于萤石云视频的二次封装: EZUIKit-JavaScript-npm

由于技术为Webview嵌套h5,直接用最基础的html模版生成h5就好,

使用了下图的两个模版 mobileLive 与 mobileRect

主要核心代码在ezuikits.js,如果要修改部分逻辑,可直接修改这个js

image.png

代码

webview嵌套逻辑,动态渲染不同模版:

import { live, replay } from "./templates";
<WebView
    style={{
        width: "100%",
        height: "100%",
        backgroundColor: "#fff"
    }}
    ref={webviewRef}
    originWhitelist={["*"]}
    useWebKit // ios使用最新webkit内核渲染
    allowUniversalAccessFromFileURLs
    geolocationEnabled
    mixedContentMode={"always"}
    scrollEnabled={false}
    nestedScrollEnabled
    javaScriptEnabled
    startInLoadingState={false}
    onMessage={handleMessage}
    hideKeyboardAccessoryView={false}
    renderError={() => {
        return <Text>加载失败</Text>;
    }}
  
    source={{
        baseUrl: "",
        html:
            type === "live"  
                ? live({ url, accessToken, newAlarmText, deviceName })
                : replay({ url, accessToken, isAlarm, deviceName })
    }}
/>

模版Html:

编写样式:要设置body的padding跟margin,不然会有样式问题;

初始化播放器:new EZUIKit.EZUIKitPlayer()

react native回调事件:window.ReactNativeWebView.postMessage()

// 直播
const live = ({ url, accessToken, newAlarmText, deviceName }: templateOption): string => {
    return `
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>Document</title>
    <script src="./ezuikit.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
            background: #F5F5F5;
        }
        .device-name {
            font-size: 16px;
            font-weight: bold;
            color: #333333;
            background-color: #fff;
            padding: 16px;
            position: relative;
            top: -3px;
            z-index: -1;
        }
        .header-back {
            line-height: 36px;
            color: #FFFFFF;
            position: absolute;
            top: 36px;
            left: 0;
            right: 0;
            background: linear-gradient(180deg, rgba(3,3,3,0.5) 0%, rgba(0,0,0,0) 100%);
            z-index: 1;
            display: none;
        }
        .back-container {
            display: inline-block;
            padding: 0 24px 0 16px;
            line-height: 36px;
            color: #FFFFFF;
        }
        .mobile-ez-ptz-container, .live-ptz-title {
            display: none !important;
        }
        .video-containerdraw-window {
            border: 0 !important;
        }
    </style>
    </head>

    <body>
    <div class="live">
        <div class="header-back" id="back-container">
            <div class="back-container" onclick="handleBackClick()">
                <svg viewBox="64 64 896 896" focusable="false" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></svg>
            </div>
        </div>
        <div id="video-container" style="width: 100%"></div>
        <div class="device-name" id="deviceName">摄像头</div>
        <div class="ysy-list-content"
            <div class="list-item" onclick="ysyToReplay()">
                <div class="left">
                    <div class="content"> 
                        <div>回看</div>
                        <div class="desc">查看历史监测记录</div>
                    </div>
                </div>
                <img class="icon-arrow" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARxJREFUWEft1SFuwzAUxvHvSQMFzXVCiqKiqXS5weDAYEHBWOHAYA9QsgOMhYz5KaE7wngCk5fJLGCR7fhZIQlNpP/PtvxCWPmhlfvYAF470DTNoe/7S9d1ZVEUneaxOQE2PgzDF4A9gO+2bR81EU4AM18BnCerVkU4ATZsjHknotcUCC9ASoQ3IBUiCGARzHwD8Kx1HMGAqqoesiy7A3jSQAQDbFQTsQigiVgM0EJEATQQ0YCZ6/kjIkWe57+u/0Y04J8p6R23uChAbDwKoBFfDNCKLwJoxoMB2vEgQIq4NyBV3AtgjPkgopfJQAm659GDiJlP4zh+EtEOgGrcawfsR3VdH0XkTURKn/HqWvX0fdQkDAnNfbsBVt+BP7ve4iEfMHDpAAAAAElFTkSuQmCC" alt="icon-arrow">
            </div>
        </div>
    </div>
    <script>
        window.onload = (event) => {
            const videoWidth = document.body.clientWidth;
            const videoHeight = videoWidth * 0.6;
            var playr;
            playr = new EZUIKit.EZUIKitPlayer({
                id: 'video-container', // 视频容器ID
                accessToken: '${accessToken}',
                url: '${url}',
                template: 'mobileLive',
                width: videoWidth,
                height: videoHeight,
            });
    
            window.ysyToReplay = () => {
                window.ReactNativeWebView.postMessage("replay");
            }

            window.handleBackClick = () => {
                window.ReactNativeWebView.postMessage("back"); 
            }

            function getStyle(obj,attr){
                return obj.currentStyle?obj.currentStyle[attr]:getComputedStyle(obj)[attr];
            }

            let backShowId = null;
            setTimeout(() => {
                // 控制返回按钮显隐
                document.getElementById("video-container").addEventListener("touchstart", function() {
                    const backContainer = document.getElementById("back-container");
                    
                    if (getStyle(backContainer, "display") === "none") {
                        backContainer.style.display = "block";
                        if (backShowId) {
                            clearTimeout(backShowId);
                        }
    
                        backShowId = setTimeout(() => {
                            backContainer.style.display = "none";
                        }, 8000)
                    }
                });       
            }, 1000);
        };
    </script>
    </body>

    </html>
    `;
};