更新记录

1.0.1(2025-12-09)

  • 新增iOS端

1.0.0(2025-12-03)

  • 新版发布

平台兼容性

uni-app(4.71)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× × × 6.0 ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × - × ×

uni-app x(4.71)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × 6.0 × ×

海康威视综合安防平台视频播放插件

基于海康威视软件平台 SDK 封装的 UTS 组件插件,支持 uni-app / uni-app x 项目,提供实时预览、录像回放、语音对讲、抓图、本地录像、电子放大、鱼眼矫正、云台控制等能力。

平台 Android SDK iOS SDK 支持框架
Android / iOS v2.4.5 v2.4.0 uni-app(nvue)、uni-app x(uvue)

SDK 详细说明请参考 海康官方开发指南


特别提醒

  • 购买前请先试用,并充分自测确认满足需求后再购买。虚拟物品一经购买,无法退款。
  • 如有使用疑问或发现 bug,可进交流群联系作者。
  • 请在合法范围内使用本插件;若用于非法开发,作者概不负责。
  • 请结合本文档与示例 Demo 集成使用。
  • Android 端请将 manifest.jsonminSdkVersion 设为 23 或以上。
  • 示例项目:uni-app 示例见插件主页;uni-app x 示例见 uni-app 示例项目 /static/HKPlayDemo.zip

插件功能

  • 实时预览
  • 网络录像回放
  • 语音对讲
  • 播放抓图
  • 本地录像
  • 电子放大
  • 鱼眼矫正
  • 云台控制(待测)

插件接口文档

兼容模式不支持 nvue 的同步返回值,因此 uni-app 与 uni-app x 项目的集成方式、回调机制、部分接口参数均不相同。请根据项目类型查阅对应章节:

项目类型 组件标签 页面类型 文档章节
uni-app <yt-hikvideo> nvue uni-app 项目集成 · 完整示例
uni-app x <ytx-hikvideo> uvue uni-app x 项目集成 · 完整示例

核心差异一览:

对比项 uni-app uni-app x
组件标签 yt-hikvideo ytx-hikvideo
页面 仅 nvue uvue
有返回值接口 通过 @actionCallback 异步返回 方法同步返回
抓图参数 screenshootThumbnail(fileName) screenshootThumbnail({ fileName })
类型声明 无需 import 可 import YtxHikvideoElement 强类型调用

uni-app 项目集成

适用于 uni-app 的 nvue 项目。UI 类 UTS 组件只能在 nvue 中使用,无需 import,直接使用标签即可。

集成步骤

  1. DCloud 插件市场 购买并绑定项目。
  2. manifest.jsonApp 原生插件配置 中勾选本插件。
  3. nvue 页面中使用组件:
<yt-hikvideo
  ref="hikVideo"
  @playStatusCallback="onPlayStatus"
  @voiceTalkCallback="onVoiceTalk"
  @actionCallback="onAction"
></yt-hikvideo>
  1. 打包自定义基座云打包后,在真机调试。

回调事件

playStatusCallback — 播放状态

字段 类型 说明
status Number 0 播放成功 / 1 播放失败 / 2 取流异常 / 3 播放结束
errorCode String 错误码,仅 status 为 1 或 2 时有值
  • Android: 参数在事件对象根级 → e.statuse.errorCode
  • iOS: 参数在 e.detail 中 → e.detail.statuse.detail.errorCode

voiceTalkCallback — 语音对讲状态

字段 类型 说明
status Number 0 开启成功 / 1 开启失败 / 2 取流异常
errorCode String 错误码,仅 status 为 1 或 2 时有值
  • Android: 根级字段
  • iOS: e.detail

actionCallback — 操作结果

抓图、录像、查询时间、电子放大、鱼眼、云台等需要返回值的接口,均通过此事件异步返回(nvue 兼容模式不支持同步返回值)。

Android 回调结构:

{
  functionName: 'getOSDTime',  // 调用的方法名
  errorCode: '0',              // '0' 成功,'-1' 失败,其他为 SDK 错误码
  time: '1234567890'           // 部分接口附加字段(如 time、fullPath、frameRate)
}

iOS 回调结构:

{
  detail: {
    functionName: 'getOSDTime',
    errorCode: '0',
    time: '1234567890'
  }
}

监听示例:

functionName 与触发接口一一对应,完整清单如下:

functionName 附加字段 说明
screenshootThumbnail fullPath 抓图路径,仅 errorCode 为 '0' 时有值
getOSDTime time 当前回放时间戳(毫秒)
startRecord fullPath 录像文件路径
startRecordAndConvert fullPath 转码录像文件路径
stopRecord 停止录像
getTotalTime time 文件总时长(秒)
getPlayedTime time 已播放时长(秒)
setCurrentFrame 设置播放进度
openDigitalZoom 开启电子放大
closeDigitalZoom 关闭电子放大
setFishEyeEnable 开关鱼眼矫正
setFishEyeMode 设置鱼眼矫正方式
handleFishEyeCorrect 处理鱼眼矫正参数
getFrameRate frameRate 码流帧率
setExpectedFrameRate 设置预期帧率
setDecodeThreadNum 设置解码线程数
ptzControl 云台控制
ptzSelZoom 云台框选放大
ptzPreset 云台预置点
ptzCruise 云台巡航

errorCode 为 '0' 表示成功,'-1' 表示失败,其他值为 SDK 错误码。Android 回调参数在根级,iOS 在 e.detail 中。

actionCallback(e) {
  const data = e.detail || e; // 兼容 Android / iOS
  const { functionName, errorCode } = data;
  const success = `${errorCode}` === '0';

  switch (functionName) {
    case 'screenshootThumbnail':
      if (success) console.log('抓图路径:', data.fullPath);
      break;
    case 'getOSDTime':
      if (success) console.log('回放时间戳(ms):', data.time);
      break;
    case 'startRecord':
    case 'startRecordAndConvert':
      if (success) console.log('录像路径:', data.fullPath);
      break;
    case 'stopRecord':
      console.log(success ? '录像已停止' : '停止录像失败:' + errorCode);
      break;
    case 'getTotalTime':
    case 'getPlayedTime':
      if (success) console.log('时长(秒):', data.time);
      break;
    case 'setCurrentFrame':
      console.log(success ? '进度设置成功' : '进度设置失败:' + errorCode);
      break;
    case 'openDigitalZoom':
    case 'closeDigitalZoom':
    case 'setFishEyeEnable':
    case 'setFishEyeMode':
    case 'handleFishEyeCorrect':
    case 'setExpectedFrameRate':
    case 'setDecodeThreadNum':
    case 'ptzControl':
    case 'ptzSelZoom':
    case 'ptzPreset':
    case 'ptzCruise':
      console.log(`${functionName} ${success ? '成功' : '失败:' + errorCode}`);
      break;
    case 'getFrameRate':
      if (success) console.log('码流帧率:', data.frameRate);
      break;
    default:
      console.log(`未知回调:${functionName}`, data);
  }
}

使用示例

实时预览

this.$refs.hikVideo.setPlayConfig({
  hardDecode: true,
  privateData: true,
  timeout: 20,
  secretKey: '',
  bufferLength: 5242880,
  qualityType: 0,
  fetchStreamType: 0
});

// realPlayUrl 为预览短链接,需通过 openApi 获取
this.$refs.hikVideo.setPlayParam(realPlayUrl, headers);
this.$refs.hikVideo.startPlay();

网络录像回放

this.$refs.hikVideo.setPlayConfig({ hardDecode: true, privateData: true });

let headers = {
  startTime: '2021-07-29T00:00:00.000+08:00',
  endTime: '2021-07-29T23:59:59.000+08:00'
};
this.$refs.hikVideo.setPlayParam(playbackUrl, headers);
this.$refs.hikVideo.startPlay();

抓图(异步回调)

this.$refs.hikVideo.screenshootThumbnail('snapshot.jpg');
// 结果在 onAction 中接收,functionName 为 'screenshootThumbnail'

查询回放时间(异步回调)

this.$refs.hikVideo.getOSDTime();
// 结果在 onAction 中接收,functionName 为 'getOSDTime'

完整示例代码(uni-app / nvue)

以下为 uni-app 项目完整 Demo 页面,覆盖插件全部接口。页面需保存为 nvue 文件(如 pages/hikvideo/demo.nvue)。

注意:

  • onLoad 中需先调用 initHikSDK() 初始化 SDK
  • 预览/回放/对讲 URL 需替换为通过海康 openApi 获取的实际地址
  • 回调中 iOS 参数在 e.detail;Android 的 actionCallback 参数在根级,使用时可用 const result = e.detail || e
<template>
    <view class="content">
        <view class="video_bg" style="width: 750rpx;height: 500rpx;background-color: black;">
            <yt-hikvideo ref="hikVideo" style="width: 750rpx;height: 450rpx;" @actionCallback="actionCallback"
                @playStatusCallback="playStatusCallback" @voiceTalkCallback="voiceTalkCallback"></yt-hikvideo>
        </view>
        <!-- <image :src="imageScr" style="width: 750rpx;height: 300rpx;background-color: aquamarine;" mode="aspectFit">
        </image> -->
        <view class="row_view">
            <view class="btn1" @click="setPlayConfig"><text>播放配置</text></view>
            <view class="btn1" @click="setPlayParam"><text>设置播放参数</text></view>
            <view class="btn1" @click="startPlay"><text>开始(预览/回放)</text></view>
        </view>

        <view class="row_view">
            <view class="btn1" @click="stopPlay"><text>停止预览/回放</text></view>
            <view class="btn1" @click="pauseStream"><text>暂停取流(预览/回放)</text></view>
            <view class="btn1" @click="resumeStream"><text>恢复取流(预览/回放)</text></view>
        </view>
        <view class="row_view">
            <view class="btn1" @click="changeStream"><text>切换码流</text></view>
            <view class="btn1" @click="setPlayParam2"><text>设置回放播放参数</text></view>
            <view class="btn1" @click="screenshootThumbnail"><text>预览/回放抓图</text></view>
        </view>
        <view class="row_view">
            <view class="btn1" @click="seekPlayback"><text>按时间回放定位</text></view>
            <view class="btn1" @click="pause"><text>暂停回放</text></view>
            <view class="btn1" @click="resume"><text>恢复回放</text></view>
        </view>
        <view class="row_view">
            <view class="btn1" @click="setPlaybackSpeed(-2)"><text>回放倍数-2</text></view>
            <view class="btn1" @click="setPlaybackSpeed(-1)"><text>回放倍数-1</text></view>
            <view class="btn1" @click="setPlaybackSpeed(1)"><text>回放倍数1</text></view>
            <view class="btn1" @click="setPlaybackSpeed(2)"><text>回放倍数2</text></view>
        </view>

        <view class="row_view">
            <view class="btn1" @click="startRecord"><text>开始录像</text></view>
            <view class="btn1" @click="startRecordAndConvert"><text>开始转码录像</text></view>
            <view class="btn1" @click="stopRecord"><text>停止录像</text></view>
            <view class="btn1" @click="playFile"><text>播放录像</text></view>
        </view>
        <view class="row_view">
            <view class="btn1" @click="getTotalTime"><text>获取录像总时长</text></view>
            <view class="btn1" @click="getPlayedTime"><text>获取已播放时长</text></view>
            <view class="btn1" @click="setCurrentFrame"><text>设置播放进度</text></view>

        </view>
        <view class="row_view">
            <view class="btn1" @click="setVoiceDataSource"><text>设置语⾳对讲参数</text></view>
            <view class="btn1" @click="startVoiceTalk"><text>开启对讲</text></view>
            <view class="btn1" @click="stopVoiceTalk"><text>关闭对讲</text></view>

        </view>
        <view class="row_view">
            <view class="btn1" @click="openDigitalZoom"><text>开启电子放大</text></view>
            <view class="btn1" @click="closeDigitalZoom"><text>关闭电子放大</text></view>
            <view class="btn1" @click="setFishEyeEnable"><text>开/关鱼眼</text></view>

        </view>
        <view class="row_view">
            <view class="btn1" @click="setFishEyeMode"><text>⻥眼矫正⽅式</text></view>
            <view class="btn1" @click="handleFishEyeCorrect"><text>处理⻥眼矫正参数</text></view>
            <view class="btn1" @click="getFrameRate"><text>获取帧率</text></view>

        </view>
        <view class="row_view">
            <view class="btn1" @click="setExpectedFrameRate"><text> 设置预期帧率</text></view>
            <!-- <view class="btn1" @click="getOSDTime"><text> getOSDTime</text></view> -->
            <view class="btn1" @click="requestRecordAudioPermission"><text>申请麦克风权限</text></view>
        </view>
    </view>
</template>

<script>
    import * as HikVideoManger from '@/uni_modules/yt-hikvideo'
    export default {
        data() {
            return {
                title: 'Hello',
                type: 1,
                imageScr: "/var/mobile/Containers/Data/Application/7DCD19F6-66D0-408A-AECF-5494E6DFC16B/Documents/Pandora/apps/__UNI__84FA73D/data1765264672353.jpg",
                path: '/var/mobile/Containers/Data/Application/7DCD19F6-66D0-408A-AECF-5494E6DFC16B/Documents/Pandora/apps/__UNI__84FA73D/data1765264769501.mp4',
                fishEyeEnable: false //是否开启鱼眼
            }
        },
        onLoad() {
            HikVideoManger.initHikSDK();
            //实时预览 | 网络视频回放 | 本地录像播放前必须先调用播放配置
            setTimeout(() => {
                this.setPlayConfig();
            }, 1000)
        },
        methods: {

            //实时预览|回放|本地录像播放回调
            playStatusCallback(e) {
                let result = e.detail || e;
                let status = result.status;
                let errorCode = result.errorCode;
                console.log(`播放状态:${status}  错误码:${errorCode}`);
            },
            //对讲状态回调
            voiceTalkCallback(e) {
                let result = e.detail || e;
                let status = result.status;
                let errorCode = result.errorCode;
                console.log(`对讲状态:${status}  错误码:${errorCode}`);
            },
            //操作回调
            actionCallback(e) {
                const result = e.detail || e; // 兼容 Android / iOS
                console.log(result);
                let functionName = result.functionName;
                let errorCode = result.errorCode;
                if (`${errorCode}` == "0") { //成功,其他是失败

                }
                console.log(`函数名称:${functionName}`);
                if (functionName == 'screenshootThumbnail') { //抓图回调
                    let fullPath = result.fullPath; //抓拍图片路径--仅errorCode == 0有该值
                    console.log(`这是抓图回调路径:${fullPath}`);
                    this.imageScr = `file://${fullPath}`;
                } else if (functionName ==
                    'getOSDTime') { //查询当前播放时间戳接⼝ 返回值errorCode:-1表⽰失败,errorCode==0 成功,time表⽰当前回放的时间戳,单位:毫秒。
                    console.log('查询当前播放时间戳回调,time=' + result.time);
                } else if (functionName == 'startRecord') { //开始录像回调 errorCode==0  成功,否则errorCode是错误码
                    let fullPath = result.fullPath; //录像视频路径--仅errorCode == 0有该值
                    this.path = fullPath;
                    console.log(`开始录像回调视频地址:${fullPath}`);
                } else if (functionName == 'startRecordAndConvert') { //开始转码录像 errorCode==0  成功,否则errorCode是错误码
                    let fullPath = result.fullPath; //录像视频路径--仅errorCode == 0有该值
                    this.path = fullPath;
                    console.log(`开始转码录像回调视频地址:${fullPath}`);
                } else if (functionName == 'stopRecord') { //停止录像 errorCode==0  成功,否则errorCode是错误码
                    console.log('停止录像回调');
                } else if (functionName == 'getTotalTime') { //获取播放文件总长-播放总时⻓,单位为:秒 . 播放成功后调⽤,否则errorCode返回-1;
                    console.log('获取播放文件总长回调,time=' + result.time);
                } else if (functionName == 'getPlayedTime') { //获取播放⽂件的已播放时⻓,单位为:秒 . 播放成功后调⽤,否则errorCode返回-1;
                    console.log('获取已播放时长回调,time=' + result.time);
                } else if (functionName == 'setCurrentFrame') { //设置播放进度 errorCode==0  成功,否则errorCode是错误码
                    console.log('设置播放进度回调');
                } else if (functionName == 'openDigitalZoom') { //设置电子放大回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('设置电子放大回调');
                } else if (functionName == 'closeDigitalZoom') { //关闭电子放大回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('关闭电子放大回调');
                } else if (functionName == 'setFishEyeEnable') { //开关鱼眼矫正回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('开关鱼眼矫正回调 ');
                } else if (functionName ==
                    'setFishEyeMode') { //设置鱼眼矫正方式-打开⻥眼功能后,设置⻥眼矫正⽅式回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('设置鱼眼矫正方式-打开⻥眼功能后,设置⻥眼矫正⽅式回调 ');
                } else if (functionName == 'handleFishEyeCorrect') { //处理⻥眼矫正参数回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('处理⻥眼矫正参数回调 ');
                } else if (functionName == 'getFrameRate') { //获取成功返回码流帧率回调,获取失败返回errorCode == -1。frameRate若码流中的帧率⼩于1,返回0。
                    console.log('获取码流帧率回调,frameRate=' + result.frameRate);
                } else if (functionName == 'setExpectedFrameRate') { //设置预期帧率回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('设置预期帧率回调 ');
                } else if (functionName ==
                    'setDecodeThreadNum') { //设置多线程解码线程数-设置多线程解码线程数回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('设置多线程解码线程数-设置多线程解码线程数回调');
                } else if (functionName == 'ptzControl') { //云台控制操作(需先启动预览)回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('云台控制操作(需先启动预览)回调');
                } else if (functionName == 'ptzSelZoom') { //云台框选放大回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('云台框选放大回调');
                } else if (functionName == 'ptzPreset') { //云台预置点操作(需先启动预览)回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('云台预置点操作(需先启动预览)');
                } else if (functionName == 'ptzCruise') { //云台巡航操作,需先启动预览回调 errorCode==0  成功,否则errorCode是错误码
                    console.log('云台巡航操作,需先启动预览');
                } else {
                    console.log('未处理的回调:', functionName, result);
                }
            },

            setPlayConfig() {
                /*
                let playConfig = {
                    hardDecode: true,//使用硬解码  Android, iOS
                    privateData: true,//开启智能信息  Android, iOS
                    timeout : 30,//取流超时时间 默认20s  Android, iOS
                    secretKey : ""// 码流解密key  Android, iOS 没有不传
                    bufferLength: 5*1024*1024// 流缓冲区大小,默认为5M   格式:5*1024*1024 Android, iOS
                    ip : "",//设备ip Android, iOS
                    port : 8080,//端口号 Android, iOS
                    username : "",//设备用户名Android, iOS   没有不传
                    password : "",//设备密码 Android, iOS 没有不传
                    channelNum: 1,//播放通道号 Android, iOS 可选参数默认不传
                    qualityType : 0,//清晰度 0-主码流 1-子码流 Android, iOS  可选参数
                    deviceSerial : ""//萤石设备序列号 Android, iOS 可选参数
                    fetchStreamType : 0///// 取流方式 0-hpsclient 1-设备直连 2-npclient, 默认hpsclient取流  可选参数
                    waterConfigs : ["11111111",'22222222222'],// 水印信息  iOS
                    //   waterConfig: {// 水印信息 Android
                    //     isWaterTrans:5,
                    //     waterFontWidth:40,
                    //     waterFontHeight:15,
                    //     userWaterContent: ['1111', '2222', '3333'],
                    //     waterOffset: [{
                    //       OffsetX: 11,
                    //       OffsetY: 11
                    //     },
                    //     {
                    //       OffsetX: 22,
                    //       OffsetY: 22
                    //     },
                    //     {
                    //       OffsetX: 44,
                    //       OffsetY: 44
                    //     }]
                    //   }
                };
                */
                let playConfig = {
                    hardDecode: true,
                    privateData: true,
                    timeout: 30
                };
                this.$refs["hikVideo"].setPlayConfig(playConfig)
            },
            setPlayParam() {
                let param = {
                    token: ''
                };
                this.$refs["hikVideo"].setPlayParam("rtsp://122.225.221.210:554/openUrl/vsignWFm9qM67810acc192b4bf2b4b8d",
                    param);
            },
            setPlayParam2() {
                let endTime = this.getFormattedTimeWithOffset()
                let startTime = this.getBeforeTime(endTime, 1)
                startTime = "2025-11-25T17:04:00.195-08:00"
                endTime = "2025-11-26T17:04:30.195-08:00"
                console.log(`开始:${startTime}----结束:${endTime}`)
                let param = {
                    token: "",
                    startTime: startTime,
                    endTime: endTime
                };
                this.$refs["hikVideo"].setPlayParam("rtsp://122.225.221.210:554/openUrl/vsigzIaIMPm7c69790c15be4dfbb22f6",
                    param);
            },
            //回放设置时间-按绝对时间回放定位
            //1. 使⽤按绝对时间回放定位接⼝之前,必须先调⽤开始回放接⼝;
            //2. 定位播放时,时间格式为'yyyy-MM-dd'T'HH:mm:ss.SSSZ'。
            seekPlayback() {
                let startTime = "2025-11-25T17:09:00.195-08:00";
                this.$refs["hikVideo"].seekPlayback(startTime)
            },
            //暂停回放-暂停录像回放取流及解码播放。
            pause() {
                this.$refs["hikVideo"].pause()
            },
            //恢复回放-恢复录像回放取流及解码播放。
            resume() {
                this.$refs["hikVideo"].resume()
            },
            //设置回放倍数
            setPlaybackSpeed(e) {
                this.$refs["hikVideo"].setPlaybackSpeed(e)
            },
            startPlay() {
                this.$refs["hikVideo"].startPlay()
            },
            stopPlay() {
                this.$refs["hikVideo"].stopPlay()
            },
            pauseStream() {
                this.$refs["hikVideo"].pauseStream()
            },
            resumeStream() {
                this.$refs["hikVideo"].resumeStream()
            },
            changeStream() {
                this.type = this.type == 0 ? 1 : 0;
                this.$refs["hikVideo"].changeStream(this.type)
            },
            screenshootThumbnail() {
                let time = new Date().getTime();
                var fileName = time + ".jpg"
                this.$refs["hikVideo"].screenshootThumbnail(fileName);

            },
            //开始录像
            startRecord() {
                let time = new Date().getTime();
                var fileName = time + ".mp4"
                let map = {
                    fileName: fileName
                };
                this.$refs["hikVideo"].startRecord(map)
            },
            //开始转码录像
            startRecordAndConvert() {
                let time = new Date().getTime();
                var fileName = time + ".mp4"
                let map = {
                    fileName: fileName
                };
                this.$refs["hikVideo"].startRecordAndConvert(map)
            },
            //停止录像
            stopRecord() {
                this.$refs["hikVideo"].stopRecord()
            },
            //播放录像
            playFile() {
                this.$refs["hikVideo"].playFile(this.path)
            },
            //获取播放录像的时长-- 播放成功后调⽤,否则返回-1;
            getTotalTime() {
                this.$refs["hikVideo"].getTotalTime();
            },
            //获取播放⽂件的已播放时⻓-播放成功后调⽤,否则返回-1;
            getPlayedTime() {
                this.$refs["hikVideo"].getPlayedTime();
            },
            //设置当前⽂件的播放进度百分⽐-播放百分⽐,取值范围 0-1.0
            setCurrentFrame() {
                this.$refs["hikVideo"].setCurrentFrame(0.5);
            },
            //========================语音对讲==========================
            setVoiceDataSource() {
                let map = {
                    token: ''
                };
                this.$refs["hikVideo"].setVoiceDataSource(
                    "rtsp://122.225.221.210:554/openUrl/vsigE7OZkMUee5f2f552f6b47068247f", map)
            },
            //开始对讲
            startVoiceTalk() {
                this.$refs["hikVideo"].startVoiceTalk();
            },
            //结束
            stopVoiceTalk() {
                this.$refs["hikVideo"].stopVoiceTalk();
            },
            //======================电子放大============================
            openDigitalZoom() {
                let currentA = {
                    left: 3,
                    top: 4,
                    right: 3,
                    bottom: 4
                };
                let originalA = {
                    left: 1,
                    top: 2,
                    right: 1,
                    bottom: 2
                };

                let param = {
                    originalA: originalA, //android
                    currentA: currentA, //android
                    original: "{{0, 0}, {300, 300}}", // iOS
                    current: "{{40, 40}, {300, 300}}", // iOS
                };
                this.$refs["hikVideo"].openDigitalZoom(param);
            },
            closeDigitalZoom() {
                this.$refs["hikVideo"].closeDigitalZoom();
            },
            //=========================鱼眼=========================
            setFishEyeEnable() { //开启预览后,设置是否开启⻥眼矫正效果;
                this.$refs["hikVideo"].setFishEyeEnable(!this.fishEyeEnable);
            },
            //设置鱼眼矫正方式
            setFishEyeMode() {
                this.$refs["hikVideo"].setFishEyeMode(3, 0);
            },
            //
            setOriginalFECParam() {

            },
            //处理⻥眼矫正参数
            /// @param isZoom 是否缩放
            /// @param zoom 缩放系数
            /// @param zoom3D 3D缩放比例 android独有
            /// @param curX 当前X坐标值  android独有
            /// @param curY 当前Y坐标值  android独有
            /// @param originalPoint ios独有
            /// @param currentPoint ios独有
            /// @param translationPoint ios独有
            ///1. ⻥眼最多⽀持4路;
            //2. 电⼦放⼤和⻥眼不能同时开启。
            handleFishEyeCorrect() {
                let param = {
                    isZoom: false, // Android, iOS
                    zoom: 0.3, // Android, iOS
                    originalPoint: "{0, 1}", // iOS
                    currentPoint: "{4, 2}", // iOS
                    translationPoint: "{3.5, 22.2}", // iOS
                    zoom3D: 3.5, // Android
                    curX: 10.5, // Android
                    curY: 20.5 // Android
                }
                this.$refs["hikVideo"].handleFishEyeCorrect(param);
            },
            //获取帧率-获取成功返回码流帧率,获取失败返回-1。若码流中的帧率⼩于1,返回0。
            getFrameRate() {
                this.$refs["hikVideo"].getFrameRate();
            },
            // 设置预期帧率
            // / 设置期望帧率(硬解不支持)
            // / @param frameRate 帧率范围(1~码流最大帧率),播放成功后可以通过getFrameRate获取当前码流最大帧率
            // 1. 此接⼝需要在播放成功后调⽤。
            // 2. 设置的预期帧率⼤于实际码流帧率的时候⽆效。
            // 3. 设置预期帧率⼩于1时⽆效。
            // 4. 开启硬解时⽆效。
            // 5. 此⽅法从V2.1.0版本开始⽀持。
            setExpectedFrameRate() {
                this.$refs["hikVideo"].setExpectedFrameRate(25);
            },
            getOSDTime() {
                this.$refs["hikVideo"].getOSDTime();
            },
            //申请麦克风权限
            requestRecordAudioPermission() {
                HikVideoManger.requestRecordAudioPermission();
            },
            getFormattedTimeWithOffset() {
                const date = new Date();
                const timezoneOffset = -date.getTimezoneOffset();
                const offsetHours = Math.floor(timezoneOffset / 60);
                const offsetMinutes = timezoneOffset % 60;

                // 克隆日期对象并添加时区偏移
                const adjustedDate = new Date(date.getTime());
                adjustedDate.setMinutes(date.getMinutes() + date.getTimezoneOffset());

                // 获取ISO字符串并替换时区部分
                const isoString = adjustedDate.toISOString();
                return isoString.slice(0, -1) +
                    (offsetHours >= 0 ? '+' : '-') +
                    Math.abs(offsetHours).toString().padStart(2, '0') + ':' +
                    Math.abs(offsetMinutes).toString().padStart(2, '0');
            },
            getBeforeTime(isoString, hour) {
                // 解析输入的时间字符串
                const date = new Date(isoString);
                // 验证输入是否有效
                if (isNaN(date.getTime())) {
                    throw new Error('Invalid ISO 8601 format provided');
                }
                const targetHoursEarlier = new Date(date.getTime() - (hour * 60 * 60 * 1000));
                // 提取时间组件
                const year = targetHoursEarlier.getFullYear();
                const month = `${targetHoursEarlier.getMonth() + 1}`.padStart(2, '0');
                const day = `${targetHoursEarlier.getDate()}`.padStart(2, '0');
                const hours = `${targetHoursEarlier.getHours()}`.padStart(2, '0');
                const minutes = `${targetHoursEarlier.getMinutes()}`.padStart(2, '0');
                const seconds = `${targetHoursEarlier.getSeconds()}`.padStart(2, '0');
                const milliseconds = `${targetHoursEarlier.getMilliseconds()}`.padStart(3, '0');
                // 获取时区偏移
                const offset = targetHoursEarlier.getTimezoneOffset();
                const offsetHours = Math.floor(Math.abs(offset) / 60).toString().padStart(2, '0');
                const offsetMinutes = (Math.abs(offset) % 60).toString().padStart(2, '0');
                const offsetSign = offset >= 0 ? '+' : '-';

                // 组合结果字符串
                return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${offsetSign}${offsetHours}:${offsetMinutes}`;
            }
        }
    }
</script>

<style>
    .content {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        width: 750rpx;
    }

    .btn {
        display: flex;
        width: 600rpx;
        height: 60rpx;
        margin-top: 15rpx;
        background-color: antiquewhite;
        border-radius: 8rpx;
        align-items: center;
        justify-content: center;
    }

    .btn1 {
        display: flex;
        padding: 0 10rpx;
        height: 60rpx;
        margin-top: 15rpx;
        background-color: antiquewhite;
        border-radius: 8rpx;
        align-items: center;
        justify-content: center;
        margin-right: 10rpx;
    }

    .row_view {
        margin-top: 10rpx;
        display: flex;
        width: 750rpx;
        flex-direction: row;
        padding: 0 16rpx;
    }
</style>

uni-app 接口说明

以下接口中,标注 异步 的需监听 @actionCallback;未标注的为 void 方法,无返回值。

初始化

setPlayConfig(playConfig)

参数 类型 必填 说明 平台
hardDecode Boolean 硬解码,默认 true Android、iOS
privateData Boolean 智能信息,默认 true Android、iOS
timeout Number 取流超时(秒),默认 20 Android、iOS
secretKey String 码流解密密钥;萤石设备为验证码 Android、iOS
bufferLength Number 流缓冲区,默认 5M Android、iOS
ip / port / username / password 设备直连参数 Android、iOS
channelNum Number 播放通道号 Android、iOS
qualityType Number 0 主码流 / 1 子码流 Android、iOS
deviceSerial String 萤石设备序列号 Android、iOS
fetchStreamType Number 0 hpsclient / 1 设备直连 / 2 npclient Android、iOS
waterConfigs Array 水印信息 iOS
waterConfig Map 水印信息(含 isWaterTrans、userWaterContent、waterOffset 等) Android

视频播放

方法 说明
setPlayParam(url, headers) 设置取流 URL 及请求头
startPlay() 开始预览或回放
stopPlay() 停止播放
pauseStream() 暂停取流
resumeStream() 恢复取流
changeStream(qualityType) 平滑切换码流,0 主码流 / 1 标清 / 2 低清

网络录像回放

方法 说明 返回
seekPlayback(seekTime) 绝对时间定位,需先 startPlay
getOSDTime() 当前回放时间戳(毫秒) 异步
pause() / resume() 暂停 / 恢复回放
setPlaybackSpeed(speed) 倍速:-2(0.25x) / -1(0.5x) / 1 / 2 / 4 / 8 / 16

语音对讲

方法 说明
setVoiceDataSource(url, headers) 设置对讲 URL
startVoiceTalk() 开启对讲 → voiceTalkCallback
stopVoiceTalk() 停止对讲

抓图 / 录像

方法 说明 返回
screenshootThumbnail(fileName) 抓图保存至本地 异步,返回 fullPath
startRecord({ fileName }) 开始录像 异步
startRecordAndConvert({ fileName }) 开始录像并转 MP4 异步
stopRecord() 停止录像 异步

声音 / 电子放大 / 鱼眼 / 本地播放 / 帧率 / 云台

方法 说明 返回
enableAudio(enable) 开关声音
openDigitalZoom(map) Android 用 originalA/currentA;iOS 用 original/current 字符串 异步
closeDigitalZoom() 关闭电子放大 异步
setFishEyeEnable(enable) 开关鱼眼矫正 异步
setFishEyeMode(correctType, placeType) 设置矫正方式 异步
setOriginalFECParam(x, y, w, h) 初始 PTZ(Android)
handleFishEyeCorrect(map) 鱼眼手势,Android/iOS 参数不同 异步
playFile(path) 播放本地文件
getTotalTime() / getPlayedTime() 总时长 / 已播时长(秒) 异步
setCurrentFrame(scale) 进度 0~1.0 异步
getFrameRate() 码流帧率 异步
setExpectedFrameRate(rate) 预期帧率(硬解不支持) 异步
setDecodeThreadNum(num) 解码线程 1~8(硬解不支持) 异步
ptzControl(action, command, speed) 云台控制,需先预览 异步
ptzSelZoom(v1, v2, v3, v4) 框选放大 异步
ptzPreset(cmd, index) 预置点 异步
ptzCruise(cmd, index) 巡航 异步

uni-app x 项目集成

适用于 uni-app x 的 uvue 项目。组件标签为 ytx-hikvideo(注意不是 yt-hikvideo),接口同步返回结果, @actionCallback 事件。

集成步骤

  1. DCloud 插件市场 购买并绑定项目。
  2. manifest.jsonApp 原生插件配置 中勾选本插件。
  3. uvue 页面中使用组件:
<ytx-hikvideo
  ref="hikVideo"
  @playStatusCallback="playStatusCallback"
  @voiceTalkCallback="voiceTalkCallback"
></ytx-hikvideo>
  1. 页面 onLoad 中初始化 SDK:
import * as HikVideoManger from '@/uni_modules/yt-hikvideo'

onLoad() {
  HikVideoManger.initHikSDK()
}
  1. 通过 (this.$refs["hikVideo"] as YtxHikvideoElement) 调用组件方法。
  2. 打包自定义基座云打包后,在真机调试。

与 uni-app 的核心差异

对比项 uni-app x uni-app
组件标签 ytx-hikvideo yt-hikvideo
返回值 方法同步返回 @actionCallback 异步返回
抓图 screenshootThumbnail({ fileName }) screenshootThumbnail(fileName)
Map 参数 Android 用 Map,iOS 用普通对象 均用普通对象
回调 playStatusCallbackvoiceTalkCallback 另有 actionCallback

回调事件

uni-app x 不含 actionCallback,播放状态与对讲状态通过以下两个事件回调:

playStatusCallback — 播放状态

status 说明
0 播放成功
1 播放失败
2 取流异常
3 播放结束(录像回放完成)
  • iOS: 参数在 res.detail
  • Android: 参数为 Map<string, any>,通过 e.get("status") 读取

voiceTalkCallback — 语音对讲状态

status 说明
0 开启对讲成功
1 开启对讲失败
2 取流异常
  • iOS: 参数在 res.detail
  • Android: 参数为 Map<string, any>

错误码 errorCode 仅在 status 为 1 或 2 时有值,具体含义请参考海康 SDK 文档。

完整示例代码(uni-app x / uvue)

以下为 uni-app x 项目完整 Demo 页面,覆盖插件全部接口。页面需保存为 uvue 文件(如 pages/hikvideo/demo.uvue)。

注意:

  • onLoad 中需调用 HikVideoManger.initHikSDK() 初始化 SDK
  • 组件标签为 ytx-hikvideo,方法通过 YtxHikvideoElement 强类型调用
  • @actionCallback,抓图/录像等结果直接从方法返回值获取
  • Android 与 iOS 部分参数类型不同,示例中使用 #ifdef APP-ANDROID / #ifdef APP-IOS 区分
<template>
    <view class="content">
        <view class="video_bg" style="width: 750rpx;height: 500rpx;background-color: black;">

            <ytx-hikvideo ref="hikVideo" style="width: 750rpx;height: 500rpx;" @playStatusCallback="playStatusCallback"
                @voiceTalkCallback="voiceTalkCallback"></ytx-hikvideo>
        </view>
        <!-- <image :src="path" style="width: 750rpx;height: 300rpx;background-color: aquamarine;" mode="aspectFit"></image> -->
        <view class="row_view">
            <view class="btn1" @click="setPlayConfig"><text>播放设置配置</text></view>
            <view class="btn1" @click="setPlayParam"><text>设置播放参数</text></view>
            <view class="btn1" @click="startPlay"><text>开始(预览/回放)</text></view>
        </view>

        <view class="row_view">
            <view class="btn1" @click="stopPlay"><text>停止预览/回放</text></view>
            <view class="btn1" @click="pauseStream"><text>暂停取流(预览/回放)</text></view>
            <view class="btn1" @click="resumeStream"><text>恢复取流(预览/回放)</text></view>
        </view>
        <view class="row_view">
            <view class="btn1" @click="changeStream"><text>切换码流</text></view>
            <view class="btn1" @click="setPlayParam2"><text>设置回放播放参数</text></view>
            <view class="btn1" @click="screenshootThumbnail"><text>预览/回放抓图</text></view>
        </view>
        <view class="row_view">
            <view class="btn1" @click="seekPlayback"><text>按时间回放定位</text></view>
            <view class="btn1" @click="pause"><text>暂停回放</text></view>
            <view class="btn1" @click="resume"><text>恢复回放</text></view>
        </view>
        <view class="row_view">
            <view class="btn1" @click="setPlaybackSpeed(-2)"><text>回放倍数-2</text></view>
            <view class="btn1" @click="setPlaybackSpeed(-1)"><text>回放倍数-1</text></view>
            <view class="btn1" @click="setPlaybackSpeed(1)"><text>回放倍数1</text></view>
            <view class="btn1" @click="setPlaybackSpeed(2)"><text>回放倍数2</text></view>
        </view>

        <view class="row_view">
            <view class="btn1" @click="startRecord"><text>开始录像</text></view>
            <view class="btn1" @click="startRecordAndConvert"><text>开始转码录像</text></view>
            <view class="btn1" @click="stopRecord"><text>停止录像</text></view>
            <view class="btn1" @click="playFile"><text>播放录像</text></view>
        </view>
        <view class="row_view">
            <view class="btn1" @click="getTotalTime"><text>获取录像总时长</text></view>
            <view class="btn1" @click="getPlayedTime"><text>获取已播放时长</text></view>
            <view class="btn1" @click="setCurrentFrame"><text>设置播放进度</text></view>

        </view>
        <view class="row_view">
            <view class="btn1" @click="setVoiceDataSource"><text>设置语⾳对讲参数</text></view>
            <view class="btn1" @click="startVoiceTalk"><text>开启对讲</text></view>
            <view class="btn1" @click="stopVoiceTalk"><text>关闭对讲</text></view>

        </view>
        <view class="row_view">
            <view class="btn1" @click="openDigitalZoom"><text>开启电子放大</text></view>
            <view class="btn1" @click="closeDigitalZoom"><text>关闭电子放大</text></view>
            <view class="btn1" @click="setFishEyeEnable"><text>开/关鱼眼</text></view>

        </view>
        <view class="row_view">
            <view class="btn1" @click="setFishEyeMode"><text>⻥眼矫正⽅式</text></view>
            <view class="btn1" @click="handleFishEyeCorrect"><text>处理⻥眼矫正参数</text></view>
            <view class="btn1" @click="getFrameRate"><text>获取帧率</text></view>

        </view>
        <view class="row_view">
            <view class="btn1" @click="setExpectedFrameRate"><text> 设置预期帧率</text></view>
        </view>

    </view>
</template>

<script>
    import *as HikVideoManger from '@/uni_modules/yt-hikvideo'
    export default {
        data() {
            return {
                title: 'Hello',
                type: 1,
                path: '/var/mobile/Containers/Data/Application/0349719B-D339-4C8F-97D7-B8C1F1A1E8B9/Documents/uni-app-x/apps/__UNI__798EC0C/1765190848716.mp4',
                fishEyeEnable: false//是否开启鱼眼
            }
        },
        onLoad() {
            HikVideoManger.initHikSDK()
        },
        methods: {
            //实时预览|回放|本地录播播放回调
            // #ifdef APP-IOS
            playStatusCallback(res) {
                console.log(res.detail);
                let status = res.detail.status ?? 1;
                ////错误码,只有 FAILED 和 EXCEPTION 才有值  返回的错误码可自行去海康咨询
                let errorCode = res.detail.errorCode ?? "";
                if (status == 0) {//播放成功

                } else if (status == 1) {//播放失败

                } else if (status == 2) {//取流异常

                } else if (status == 3) {//FINISH(录像回放成功)

                }
                console.log(`状态码:${status} 错误码:${errorCode}`);
            },
            // #endif
            // #ifdef APP-ANDROID
            playStatusCallback(e : Map<string, any>) {
                console.log(e);
                let status = e.get("status") ?? 1
                let errorCode = e.get("errorCode") ?? ""////错误码,只有 FAILED 和 EXCEPTION 才有值  返回的错误码可自行去海康咨询
                if (status == 0) {//播放成功

                } else if (status == 1) {//播放失败

                } else if (status == 2) {//取流异常

                } else if (status == 3) {//FINISH(录像回放成功)

                }
            },
            // #endif
            // #ifdef APP-IOS
            voiceTalkCallback(res) {
                console.log(e);
                let status = res.detail.status ?? 1;
                ////错误码,只有 FAILED 和 EXCEPTION 才有值  返回的错误码可自行去海康咨询
                let errorCode = res.detail.errorCode ?? "";
                if (status == 0) {//开启对讲成功

                } else if (status == 1) {//(开启对讲失败

                } else if (status == 2) {//取流异常

                }
                console.log(`状态码:${status} 错误码:${errorCode}`);
            },
            // #endif
            // #ifdef APP-ANDROID
            //对讲回调
            voiceTalkCallback(e : Map<string, any>) {
                console.log(e);
                let status = e.get("status") ?? 1
                let errorCode = e.get("errorCode") ?? ""//错误码,只有 FAILED 和 EXCEPTION 才有值  返回的错误码可自行去海康咨询
                if (status == 0) {//开启对讲成功

                } else if (status == 1) {//(开启对讲失败

                } else if (status == 2) {//取流异常

                }
            },
            // #endif
            //预览先调用
            setPlayConfig() {
                /*
                let playConfig = {
                    hardDecode: true,//使用硬解码  Android, iOS
                    privateData: true,//开启智能信息  Android, iOS
                    timeout : 30,//取流超时时间 默认20s  Android, iOS
                    secretKey : ""// 码流解密key  Android, iOS 没有不传
                    bufferLength: 5*1024*1024// 流缓冲区大小,默认为5M   格式:5*1024*1024 Android, iOS
                    ip : "",//设备ip Android, iOS
                    port : 8080,//端口号 Android, iOS
                    username : "",//设备用户名Android, iOS   没有不传
                    password : "",//设备密码 Android, iOS 没有不传
                    channelNum: 1,//播放通道号 Android, iOS 可选参数默认不传
                    qualityType : 0,//清晰度 0-主码流 1-子码流 Android, iOS  可选参数
                    deviceSerial : ""//萤石设备序列号 Android, iOS 可选参数
                    fetchStreamType : 0///// 取流方式 0-hpsclient 1-设备直连 2-npclient, 默认hpsclient取流  可选参数
                    waterConfigs : ["11111111",'22222222222'],// 水印信息  iOS
                    //   waterConfig: {// 水印信息 Android
                    //     isWaterTrans:5,
                    //     waterFontWidth:40,
                    //     waterFontHeight:15,
                    //     userWaterContent: ['1111', '2222', '3333'],
                    //     waterOffset: [{
                    //       OffsetX: 11,
                    //       OffsetY: 11
                    //     },
                    //     {
                    //       OffsetX: 22,
                    //       OffsetY: 22
                    //     },
                    //     {
                    //       OffsetX: 44,
                    //       OffsetY: 44
                    //     }]
                    //   }
                };
                */
                // #ifdef APP-ANDROID
                let playConfig = new Map<string, any>()
                playConfig.set("hardDecode", true)//使用硬解码  Android, iOS
                playConfig.set("privateData", true);//开启智能信息  Android, iOS
                (this.$refs["hikVideo"] as YtxHikvideoElement).setPlayConfig(playConfig)
                // #endif
                // #ifdef APP-IOS
                let playConfig = {
                    hardDecode: true,
                    privateData: true
                };
                (this.$refs["hikVideo"] as YtxHikvideoElement).setPlayConfig(playConfig)
                // #endif
            },
            setPlayParam() {
                // #ifdef APP-ANDROID
                let map : Map<string, string> = new Map();
                map.set("token", "");
                (this.$refs["hikVideo"] as YtxHikvideoElement).setPlayParam("rtsp://122.225.221.210:554/openUrl/vsigAV2dJjq786a1522aca44b5a9e9f8", map)
                // #endif
                // #ifdef APP-IOS
                let map = {
                    token: ""
                };
                (this.$refs["hikVideo"] as YtxHikvideoElement).setPlayParam("rtsp://122.225.221.210:554/openUrl/vsigAU1cMr6cf31ee51f96e484fa62b9", map)
                // #endif

            },
            setPlayParam2() {
                let endTime = this.getFormattedTimeWithOffset()
                let startTime = this.getBeforeTime(endTime, 1);
                startTime = "2025-11-25T17:04:00.195-08:00"
                endTime = "2025-11-26T17:04:30.195-08:00";
                console.log(`开始:${startTime}----结束:${endTime}`)
                // #ifdef APP-ANDROID
                let map : Map<string, string> = new Map();
                map.set("token", "");
                map.set("startTime", startTime);
                map.set("endTime", endTime);
                (this.$refs["hikVideo"] as YtxHikvideoElement).setPlayParam("rtsp://122.225.221.210:554/openUrl/vsiglDNtc1aed4e885a8e704b70be75e", map)
                // #endif
                // #ifdef APP-IOS
                let map = {
                    token: "",
                    startTime: startTime,
                    endTime: endTime
                };
                (this.$refs["hikVideo"] as YtxHikvideoElement).setPlayParam("rtsp://122.225.221.210:554/openUrl/vsiglDNtc1aed4e885a8e704b70be75e", map)
                // #endif

            },
            //回放设置时间-按绝对时间回放定位
            //1. 使⽤按绝对时间回放定位接⼝之前,必须先调⽤开始回放接⼝;
            //2. 定位播放时,时间格式为'yyyy-MM-dd'T'HH:mm:ss.SSSZ'。
            seekPlayback() {
                let startTime : string = "2025-11-25T17:09:00.195-08:00";
                (this.$refs["hikVideo"] as YtxHikvideoElement).seekPlayback(startTime)
            },
            //暂停回放-暂停录像回放取流及解码播放。
            pause() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).pause()
            },
            //恢复回放-恢复录像回放取流及解码播放。
            resume() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).resume()
            },
            //设置回放倍数
            setPlaybackSpeed(e : number) {
                let flag = (this.$refs["hikVideo"] as YtxHikvideoElement).setPlaybackSpeed(e)
                console.log(`setPlaybackSpeed:${flag}`);
            },
            //开始实时预览 || 网络录像回放 
            startPlay() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).startPlay()
            },
            //停止实时预览 || 网络录像回放 
            stopPlay() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).stopPlay()
            },
            //暂停取流
            pauseStream() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).pauseStream()
            },
            //恢复取流
            resumeStream() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).resumeStream()
            },
            //平滑切换码流
            changeStream() {
                this.type = this.type == 0 ? 1 : 0;
                (this.$refs["hikVideo"] as YtxHikvideoElement).changeStream(this.type)
            },
            //抓图
            screenshootThumbnail() {
                let time = new Date().getTime();
                var fileName = time + ".jpg"
                let flag : Map<string, any>;
                // #ifdef APP-ANDROID
                let map = new Map<string, string>()
                map.set("fileName", fileName)
                flag = (this.$refs["hikVideo"] as YtxHikvideoElement).screenshootThumbnail(map)
                // #endif
                // #ifdef APP-IOS
                let map = {
                    fileName: fileName
                };
                flag = (this.$refs["hikVideo"] as YtxHikvideoElement).screenshootThumbnail(map)
                // #endif
                console.log(flag);
                let errorCode : number = flag["errorCode"] as number;
                if (errorCode == 0) {
                    let fullPath : string = flag["fullPath"] as string;
                    this.path = fullPath;

                } else {
                    uni.showToast({
                        title: '抓图失败',
                        icon: 'none'
                    })
                }

            },
            //开始录像
            startRecord() {
                let time = new Date().getTime();
                var fileName = time + ".mp4"
                // #ifdef APP-ANDROID
                let map : Map<string, string> = new Map();
                map.set("fileName", fileName);
                let flag : Map<string, any> = (this.$refs["hikVideo"] as YtxHikvideoElement).startRecord(map)
                console.log(flag);
                let errorCode : number = flag["errorCode"] as number;
                if (errorCode == 0) {
                    let fullPath : string = flag["fullPath"] as string;
                    this.path = fullPath;
                    console.log(`录像地址:${fullPath}`)
                } else {
                    uni.showToast({
                        title: '录制失败',
                        icon: 'none'
                    })
                }
                // #endif
                // #ifdef APP-IOS
                let map = {
                    fileName: fileName
                };
                let flag : Map<string, any> = (this.$refs["hikVideo"] as YtxHikvideoElement).startRecord(map)
                console.log(flag);
                let errorCode : number = flag["errorCode"] as number;
                if (errorCode == 0) {
                    let fullPath : string = flag["fullPath"] as string;
                    this.path = fullPath;
                    console.log(`录像地址:${fullPath}`)
                } else {
                    uni.showToast({
                        title: '录制失败',
                        icon: 'none'
                    })
                }
                // #endif
            },
            //开始转码录像
            startRecordAndConvert() {
                let time = new Date().getTime();
                var fileName = time + ".mp4"
                // #ifdef APP-ANDROID
                let map : Map<string, string> = new Map();
                map.set("fileName", fileName);
                let flag : Map<string, any> = (this.$refs["hikVideo"] as YtxHikvideoElement).startRecordAndConvert(map)
                console.log(flag);
                let errorCode : number = flag["errorCode"] as number;
                if (errorCode == 0) {
                    let fullPath : string = flag["fullPath"] as string;
                    this.path = fullPath;
                    console.log(`录像地址:${fullPath}`)
                } else {
                    uni.showToast({
                        title: '录制失败',
                        icon: 'none'
                    })
                }
                // #endif
                // #ifdef APP-IOS
                let map = {
                    fileName: fileName
                };
                let flag : Map<string, any> = (this.$refs["hikVideo"] as YtxHikvideoElement).startRecordAndConvert(map)
                console.log(flag);
                let errorCode : number = flag["errorCode"] as number;
                if (errorCode == 0) {
                    let fullPath : string = flag["fullPath"] as string;
                    this.path = fullPath;
                    console.log(`录像地址:${fullPath}`)
                } else {
                    uni.showToast({
                        title: '录制失败',
                        icon: 'none'
                    })
                }
                // #endif

            },
            //停止录像
            stopRecord() {
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).stopRecord()
                let v = "停止录制失败";
                if (flag == 0) {
                    v = "停止录制成功"
                }
                uni.showToast({
                    title: v,
                    icon: 'none'
                })
            },
            //播放录像
            playFile() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).playFile(this.path)
            },
            //获取播放录像的时长-- 播放成功后调⽤,否则返回-1;
            getTotalTime() {
                let time : number = (this.$refs["hikVideo"] as YtxHikvideoElement).getTotalTime();
                console.log(`播放视频总时长:${time}`)
            },
            //获取播放⽂件的已播放时⻓-播放成功后调⽤,否则返回-1;
            getPlayedTime() {
                let time : number = (this.$refs["hikVideo"] as YtxHikvideoElement).getPlayedTime();
                console.log(`已播放时⻓:${time}`)
            },
            //设置当前⽂件的播放进度百分⽐-播放百分⽐,取值范围 0-1.0
            setCurrentFrame() {
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).setCurrentFrame(0.5);
                console.log(flag == 0 ? '设置成功' : '设置失败')
            },
            //========================语音对讲==========================
            setVoiceDataSource() {

                // #ifdef APP-ANDROID
                let map : Map<string, string> = new Map();
                map.set("token", "");
                (this.$refs["hikVideo"] as YtxHikvideoElement).setVoiceDataSource("rtsp://122.225.221.210:554/openUrl/vsigE7OZkMUee5f2f552f6b47068247f", map)
                // #endif
                // #ifdef APP-IOS
                let map = {
                    token: ""
                };
                (this.$refs["hikVideo"] as YtxHikvideoElement).setVoiceDataSource("rtsp://122.225.221.210:554/openUrl/vsigE7OZkMUee5f2f552f6b47068247f", map)
                // #endif

            },
            //开始对讲
            startVoiceTalk() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).startVoiceTalk();
            },
            //结束
            stopVoiceTalk() {
                (this.$refs["hikVideo"] as YtxHikvideoElement).stopVoiceTalk();
            },
            //======================电子放大============================
            openDigitalZoom() {

                // #ifdef APP-ANDROID
                let param : Map<string, any> = new Map();
                let originalADict : Map<string, any> = new Map();
                originalADict.set("left", 1);
                originalADict.set("top", 2);
                originalADict.set("right", 1);
                originalADict.set("bottom", 2);
                let currentADict : Map<string, any> = new Map();
                currentADict.set("left", 3);
                currentADict.set("top", 4);
                currentADict.set("right", 3);
                currentADict.set("bottom", 4);
                param.set("originalA", originalADict);
                param.set("currentA", currentADict);
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).openDigitalZoom(param);
                console.log(flag)
                // #endif
                // #ifdef APP-IOS
                let param = {
                    original: "{{0, 0}, {300, 300}}",// iOS
                    current: "{{40, 40}, {300, 300}}",// iOS
                };
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).openDigitalZoom(param);
                console.log(flag)
                // #endif

            },
            closeDigitalZoom() {
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).closeDigitalZoom();
                console.log(flag == 0 ? '关闭成功' : '关闭失败')
            },
            //=========================鱼眼=========================
            setFishEyeEnable() {//开启预览后,设置是否开启⻥眼矫正效果;
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).setFishEyeEnable(!this.fishEyeEnable);
                console.log(`开关鱼眼:${flag}`)
            },
            //设置鱼眼矫正方式
            setFishEyeMode() {
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).setFishEyeMode(3, 0);
                console.log(`设置鱼眼矫正方式:${flag}`)
            },
            //
            setOriginalFECParam() {

            },
            //处理⻥眼矫正参数
            /// @param isZoom 是否缩放
            /// @param zoom 缩放系数
            /// @param zoom3D 3D缩放比例
            /// @param curX 当前X坐标值
            /// @param curY 当前Y坐标值
            ///1. ⻥眼最多⽀持4路;
            //2. 电⼦放⼤和⻥眼不能同时开启。
            handleFishEyeCorrect() {

                // #ifdef APP-ANDROID
                let param : Map<string, any> = new Map();
                param.set("isZoom", false);// Android, iOS
                param.set("zoom", 0.3);// Android, iOS
                param.set("zoom3D", 3.5);// Android
                param.set("curX", 10.5);// Android
                param.set("curY", 20.5);// Android
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).handleFishEyeCorrect(param);
                console.log(`处理⻥眼矫正参数:${flag}`);
                // #endif
                // #ifdef APP-IOS
                let param = {
                    isZoom: false,// Android, iOS
                    zoom: 0.3,// Android, iOS
                    originalPoint: "{0, 1}",// iOS
                    currentPoint: "{4, 2}",// iOS
                    translationPoint: "{3.5, 22.2}"// iOS
                }
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).handleFishEyeCorrect(param);
                console.log(`处理⻥眼矫正参数:${flag}`);
                // #endif

            },
            //获取帧率-获取成功返回码流帧率,获取失败返回-1。若码流中的帧率⼩于1,返回0。
            getFrameRate() {
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).getFrameRate();
                console.log(`获取帧率:${flag}`);
            },
            // 设置预期帧率
            // / 设置期望帧率(硬解不支持)
            // / @param frameRate 帧率范围(1~码流最大帧率),播放成功后可以通过getFrameRate获取当前码流最大帧率
            // 1. 此接⼝需要在播放成功后调⽤。
            // 2. 设置的预期帧率⼤于实际码流帧率的时候⽆效。
            // 3. 设置预期帧率⼩于1时⽆效。
            // 4. 开启硬解时⽆效。
            // 5. 此⽅法从V2.1.0版本开始⽀持。
            setExpectedFrameRate() {
                let flag : number = (this.$refs["hikVideo"] as YtxHikvideoElement).setExpectedFrameRate(60);
                console.log(`设置预期帧率:${flag}`);
            },
            getFormattedTimeWithOffset() {
                const date = new Date();
                const timezoneOffset = -date.getTimezoneOffset();
                const offsetHours = Math.floor(timezoneOffset / 60);
                const offsetMinutes = timezoneOffset % 60;

                // 克隆日期对象并添加时区偏移
                const adjustedDate = new Date(date.getTime());
                adjustedDate.setMinutes(date.getMinutes() + date.getTimezoneOffset());

                // 获取ISO字符串并替换时区部分
                const isoString = adjustedDate.toISOString();
                return isoString.slice(0, -1) +
                    (offsetHours >= 0 ? '+' : '-') +
                    Math.abs(offsetHours).toString().padStart(2, '0') + ':' +
                    Math.abs(offsetMinutes).toString().padStart(2, '0');
            },
            getBeforeTime(isoString : string, hour : number) {
                // 解析输入的时间字符串
                const date = new Date(isoString);
                // 验证输入是否有效
                if (isNaN(date.getTime())) {
                    throw new Error('Invalid ISO 8601 format provided');
                }
                const targetHoursEarlier = new Date(date.getTime() - (hour * 60 * 60 * 1000));
                // 提取时间组件
                const year = targetHoursEarlier.getFullYear();
                const month = `${targetHoursEarlier.getMonth() + 1}`.padStart(2, '0');
                const day = `${targetHoursEarlier.getDate()}`.padStart(2, '0');
                const hours = `${targetHoursEarlier.getHours()}`.padStart(2, '0');
                const minutes = `${targetHoursEarlier.getMinutes()}`.padStart(2, '0');
                const seconds = `${targetHoursEarlier.getSeconds()}`.padStart(2, '0');
                const milliseconds = `${targetHoursEarlier.getMilliseconds()}`.padStart(3, '0');
                // 获取时区偏移
                const offset = targetHoursEarlier.getTimezoneOffset();
                const offsetHours = Math.floor(Math.abs(offset) / 60).toString().padStart(2, '0');
                const offsetMinutes = (Math.abs(offset) % 60).toString().padStart(2, '0');
                const offsetSign = offset >= 0 ? '+' : '-';

                // 组合结果字符串
                return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${offsetSign}${offsetHours}:${offsetMinutes}`;
            }

        }
    }
</script>

<style>
    .content {
        width: 750rpx;
        display: flex;
        flex-direction: column;
        align-items: center;
    }

    .btn {
        display: flex;
        width: 600rpx;
        height: 60rpx;
        margin-top: 15rpx;
        background-color: antiquewhite;
        border-radius: 8rpx;
        align-items: center;
        justify-content: center;
    }

    .btn1 {
        display: flex;
        padding: 0 10rpx;
        height: 60rpx;
        margin-top: 15rpx;
        background-color: antiquewhite;
        border-radius: 8rpx;
        align-items: center;
        justify-content: center;
        margin-right: 10rpx;
    }

    .row_view {
        margin-top: 10rpx;
        display: flex;
        width: 750rpx;
        flex-direction: row;
        padding: 0 16rpx;
    }
</style>

同步返回值说明

以下接口调用后直接返回结果(0'0' 表示成功,-1 表示失败):

方法 返回类型 说明
screenshootThumbnail(map) Map<string, any> 含 errorCode、fullPath
startRecord(map) Map<string, any> 含 errorCode、fullPath
startRecordAndConvert(map) Map<string, any> 含 errorCode、fullPath
stopRecord() number 0 成功
getOSDTime() number 回放时间戳(毫秒),-1 失败
getTotalTime() number 总时长(秒)
getPlayedTime() number 已播时长(秒)
setCurrentFrame(scale) number 0 成功
openDigitalZoom(map) number 0 成功
closeDigitalZoom() number 0 成功
setFishEyeEnable(enable) number 0 成功
setFishEyeMode(a, b) number 0 成功
handleFishEyeCorrect(map) number 0 成功
getFrameRate() number 帧率,-1 失败
setExpectedFrameRate(rate) number 0 成功
setDecodeThreadNum(num) number 0 成功
ptzControl(...) 等云台接口 number 0 成功

Map 返回值读取示例(Android / iOS 通用):

let flag = (this.$refs["hikVideo"] as YtxHikvideoElement).screenshootThumbnail(map)
let errorCode = flag["errorCode"] as number
if (errorCode == 0) {
  let fullPath = flag["fullPath"] as string
}

平台参数差异

部分接口在 Android 与 iOS 上参数类型不同,需使用条件编译:

接口 Android iOS
setPlayConfig Map<string, any> 普通对象 { hardDecode, privateData, ... }
setPlayParam headers Map<string, string> 普通对象 { token, startTime, endTime }
screenshootThumbnail Map 含 fileName 对象 { fileName }
startRecord 等录像 Map 含 fileName 对象 { fileName }
openDigitalZoom originalA / currentA(Rect Map) original / current 字符串
handleFishEyeCorrect isZoom, zoom, zoom3D, curX, curY isZoom, zoom, originalPoint, currentPoint, translationPoint

uni-app x 接口说明

以下接口均为同步调用,标注返回类型的方法直接接收返回值。参数说明与 uni-app 相同,见 公共参数说明

视频播放

方法 返回 说明
setPlayConfig(playConfig) void Android 传 Map,iOS 传对象
setPlayParam(url, headers) void Android headers 为 Map,iOS 为对象
startPlay() void 开始预览或回放
stopPlay() void 停止播放
pauseStream() void 暂停取流
resumeStream() void 恢复取流
changeStream(qualityType) void 平滑切换码流

网络录像回放

方法 返回 说明
seekPlayback(seekTime) void 绝对时间定位
getOSDTime() number 回放时间戳(毫秒),-1 失败
pause() / resume() void 暂停 / 恢复
setPlaybackSpeed(speed) void 倍速:-2 / -1 / 1 / 2 / 4 / 8 / 16

语音对讲

方法 返回 说明
setVoiceDataSource(url, headers) void 设置对讲 URL
startVoiceTalk() void 开启对讲 → voiceTalkCallback
stopVoiceTalk() void 停止对讲

抓图 / 录像

方法 返回 说明
screenshootThumbnail(map) Map { fileName },返回 errorCode、fullPath
startRecord(map) Map 开始录像
startRecordAndConvert(map) Map 录像并转 MP4
stopRecord() number 0 成功

声音 / 电子放大 / 鱼眼 / 本地播放 / 帧率 / 云台

方法 返回 说明
enableAudio(enable) void 开关声音
playFile(path) void 播放本地文件
getTotalTime() number 总时长(秒)
getPlayedTime() number 已播时长(秒)
setCurrentFrame(scale) number 进度 0~1.0
openDigitalZoom(map) number Android/iOS 参数不同
closeDigitalZoom() number 关闭电子放大
setFishEyeEnable(enable) number 开关鱼眼
setFishEyeMode(correctType, placeType) number 矫正方式
setOriginalFECParam(x, y, w, h) void 初始 PTZ(Android)
handleFishEyeCorrect(map) number 鱼眼手势
getFrameRate() number 码流帧率
setExpectedFrameRate(rate) number 预期帧率(硬解不支持)
setDecodeThreadNum(num) number 解码线程 1~8
ptzControl(action, command, speed) number 云台控制
ptzSelZoom(v1, v2, v3, v4) number 框选放大
ptzPreset(cmd, index) number 预置点
ptzCruise(cmd, index) number 巡航


公共参数说明

以下参数在两个项目中通用,供查阅。

setPlayConfig 完整参数

参数 类型 必填 说明 平台
hardDecode Boolean 硬解码,默认 true Android、iOS
privateData Boolean 智能信息,默认 true Android、iOS
timeout Number 取流超时(秒),默认 20 Android、iOS
secretKey String 码流解密密钥 Android、iOS
bufferLength Number 流缓冲区,默认 5M Android、iOS
ip String 设备 IP Android、iOS
port Number 设备端口 Android、iOS
username String 设备用户名 Android、iOS
password String 设备密码 Android、iOS
channelNum Number 播放通道号 Android、iOS
qualityType Number 0 主码流 / 1 子码流 Android、iOS
deviceSerial String 萤石设备序列号 Android、iOS
fetchStreamType Number 0 hpsclient / 1 设备直连 / 2 npclient Android、iOS
waterConfigs Array 水印信息 iOS
waterConfig Map 水印信息 Android

电子放大参数

Android openDigitalZoom:

{
  originalA: { left: 0, top: 0, right: 100, bottom: 100 },
  currentA: { left: 25, top: 25, right: 75, bottom: 75 }
}

iOS openDigitalZoom:

{
  original: '{{0, 0}, {100, 100}}',
  current: '{{25, 25}, {50, 50}}'
}

鱼眼参数

correctType: 0 PTZ / 1 180° / 2 360° / 3 LAT / 4 SEM / 5 CYC / 6 PLA / 7 CYC_SPL

placeType: 1 壁装 / 2 地装 / 3 顶装

Android handleFishEyeCorrect: { isZoom, zoom, zoom3D, curX, curY }

iOS handleFishEyeCorrect: { isZoom, zoom, originalPoint, currentPoint, translationPoint }

鱼眼最多支持 4 路;电子放大与鱼眼不能同时开启。


更多插件推荐

隐私、权限声明

1. 本插件需要申请的系统权限列表:

<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: