更新记录

1.0.0(2022-01-19)

首次更新


平台兼容性

Android Android CPU类型 iOS
适用版本区间:4.4 - 14.0 armeabi-v7a:支持,arm64-v8a:支持,x86:未测试 适用版本区间:11 - 17

原生插件通用使用流程:

  1. 购买插件,选择该插件绑定的项目。
  2. 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
  3. 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
  4. 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
  5. 开发完毕后正式云打包

付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios

注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择


KJ-HatomPlayer

海康iSecure Center实时视频预览、录像回放、倍数回放、抓图、声音、录像、语言对讲、鱼眼(ios、android)

海康V1版本地址

海康(HIKVISION)实时视频预览、录像回放、语音对讲:https://ext.dcloud.net.cn/plugin?id=2403

引入插件

<KJ-HatomPlayer ref='KJHatomPlayer' style="width:750rpx;height:200px" @onPlayerStatus="onPlayerStatus"
            @onTalkStatus="onTalkStatus"></KJ-HatomPlayer>

基本使用步骤

1.如何获取海康视频流地址?
答:后台开发人员对接海康提供的接口,

获取流地址,参考海康的开放平台:https://open.hikvision.com/docs/docId?productId=5c67f1e2f05948198c909700&curNodeId=979ab5f343114ad6a96f2d46d8cc26c9

相关代码

<template>
    <div>
        <KJ-HatomPlayer ref='KJHatomPlayer' style="width:750rpx;height:200px" @onPlayerStatus="onPlayerStatus"
            @onTalkStatus="onTalkStatus"></KJ-HatomPlayer>
        <view class="btns">
            <button type="primary" @click="init">初始化</button>
            <button type="primary" @click="start">开始播放</button>
            <button type="primary" @click="stop">停止播放</button>
            <button type="primary" @click="changeStream">切换码流</button>

            <button type="primary" @click="startVoiceTalk">开启对讲</button>
            <button type="primary" @click="stopVoiceTalk">停止对讲</button>

            <button type="primary" @click="startPlayback">开启录像回放</button>
            <button type="primary" @click="seekPlayback">按绝对时间回放定位</button>
            <button type="primary" @click="pause">暂停回放</button>
            <button type="primary" @click="resume">恢复回放</button>
            <button type="primary" @click="getPlaybackSpeed">获取回放倍速值</button>
            <button type="primary" @click="setPlaybackSpeed">设置回放倍速</button>

            <button type="primary" @click="enableAudio">开启声音</button>
            <button type="primary" @click="enableAudio_close">关闭声音</button>

            <button type="primary" @click="getOSDTime">获取视频中屏幕显示时间</button>
            <button type="primary" @click="screenshoot">抓图</button>
            <button type="primary" @click="getTotalTraffic">获取消耗的流量</button>

            <button type="primary" @click="startRecord">开始录像</button>
            <button type="primary" @click="stopRecord">结束录像</button>

            <button type="primary" @click="setFishEyeEnable">开启鱼眼</button>
            <button type="primary" @click="setFishEyeEnable_close">关闭鱼眼</button>
            <button type="primary" @click="setFishEyeMode">设置鱼眼模式</button>
        </view>

        <image :src="imageSrc" mode="aspectFill"> </image>
    </div>
</template>

<script>
    var RealPlayUrl = "rtsp://xxx.xxx.xxx.xxx:xxx/openUrl/4YdN7xu" //预览取流URL
    var PlaybackUrl = "rtsp://xxx.xxx.xxx.xxx:xxx/openUrl/3ToKcqk" //回放取流URL
    var voiceDataSource = "rtsp://xxx.xxx.xxx.xxx:xxx/openUrl/xxx" //语言对讲取流URL

    export default {
        data() {
            return {
                imageSrc: '../../static/logo.png',
                recordFilePath: null
            }
        },
        onLoad() {

        },
        onReady() {
            //this.startPlayback();
        },
        methods: {
            /**
             * 播放状态回调
             * */
            onPlayerStatus(e) {
                //{"playStatus":"SUCCESS","errorCode":"HVPErrorCodeSuccess"}
                //错误码参考插件市场文档
                console.log("onPlayerStatus: " + JSON.stringify(e.detail))
            },
            /**
             * 语音对讲状态回调
             * */
            onTalkStatus(e) {
                console.log("onTalkStatus: " + JSON.stringify(e.detail))
            },

            init() {
                this.$refs.KJHatomPlayer.initSDK(""); //萤石appkey,如果是海康isc平台,传空字符
            },

            /**
             * 开启预览
             * */
            start() {
                console.log(this.$refs.KJHatomPlayer)
                var dic = {
                    "playConfig": {
                        hardDecode: true, //是否开启硬解码 true-开启 false-关闭
                        privateData: true, //是否显示智能信息 true-显示 false-不显示
                        timeout: 20, //取流超时时间,默认20s
                        secretKey: "", //码流解密key
                        bufferLength: 5 * 1024 * 1024, //流缓冲区大小,默认为5M
                    },
                    "dataSource": RealPlayUrl, //播放url
                    "headers": {
                        "token": ""
                    } //请求参数 默认要传token,值可以为空字符串
                }
                this.$refs.KJHatomPlayer.initPlayConfig(dic);
                this.$refs.KJHatomPlayer.start();
            },
            stop() {
                this.$refs.KJHatomPlayer.stop();
            },
            /**
             * 切换码流
             * */
            changeStream() {
                var dic = {
                    "qualityType": 2, //0-高清 1-标清 2-流畅
                }
                this.$refs.KJHatomPlayer.changeStream(dic, (res) => {
                    console.log("changeStream: " + JSON.stringify(res))
                });
            },
            /**
             * 开启对讲
             * 需要麦克风权限,android需要动态获取
             * */
            startVoiceTalk() {
                var dic = {
                    "voiceDataSource": voiceDataSource, //对讲url
                    "headers": {
                        "token": ""
                    } //请求参数 默认要传token,值可以为空字符串
                }
                this.$refs.KJHatomPlayer.startVoiceTalk(dic, (res) => {
                    console.log("startVoiceTalk: " + JSON.stringify(res))
                });
            },
            /**
             * 停止对讲
             * */
            stopVoiceTalk() {
                this.$refs.KJHatomPlayer.stopVoiceTalk();
            },

            /**
             * 开启录像回放
             * */
            startPlayback() {
                var dic = {
                    "playConfig": {
                        hardDecode: true, //是否开启硬解码 true-开启 false-关闭
                        privateData: true, //是否显示智能信息 true-显示 false-不显示
                        timeout: 20, //取流超时时间,默认20s
                        secretKey: "", //码流解密key
                        bufferLength: 5 * 1024 * 1024, //流缓冲区大小,默认为5M
                    },
                    "dataSource": PlaybackUrl, //回放url
                    "headers": {
                        "token": "", //请求参数 默认要传token,值可以为空字符串
                        "startTime": "2021-07-29T00:00:00.000+08:00", //开始时间,必传,yyyy-MM-dd'T'HH:mm:ss.SSS+08:00时间格式
                        "endTime": "2021-07-29T23:59:59.000+08:00", // 结束时间,必传,yyyy-MM-dd'T'HH:mm:ss.SSS+08:00时间格式
                    }
                }
                this.$refs.KJHatomPlayer.initPlayConfig(dic);
                this.$refs.KJHatomPlayer.start();
            },
            /**
             * 按绝对时间回放定位
             * 使用按绝对时间回放定位接口之前,必须先调用开始回放接口
             * */
            seekPlayback() {
                var dic = {
                    "seekTime": "", //定位时间,格式为 yyyy-MM-dd'T'HH:mm:ss.SSS
                }
                this.$refs.KJHatomPlayer.seekPlayback(dic);
            },

            /**
             * 暂停回放
             * */
            pause() {
                this.$refs.KJHatomPlayer.pause();
            },
            /**
             * 恢复回放
             * */
            resume() {
                this.$refs.KJHatomPlayer.resume();
            },
            getPlaybackSpeed() {
                this.$refs.KJHatomPlayer.getPlaybackSpeed((res) => {
                    console.log("getPlaybackSpeed: " + JSON.stringify(res))
                });
            },
            setPlaybackSpeed() {
                var dic = {
                    "speed": 2, //倍速值-8/-4/-2/1/2/4/8,负数为慢放,正数为快放
                }
                this.$refs.KJHatomPlayer.setPlaybackSpeed(dic, (res) => {
                    console.log("setPlaybackSpeed: " + JSON.stringify(res))
                });
            },
            /**
             * 开启声音
             * */
            enableAudio() {
                var dic = {
                    "enable": true, //true-开启声音 false-关闭声音
                }
                this.$refs.KJHatomPlayer.enableAudio(dic, (res) => {
                    console.log("openSound: " + JSON.stringify(res))
                });
            },
            /**
             * 开启声音
             * */
            enableAudio_close() {
                var dic = {
                    "enable": false, //true-开启声音 false-关闭声音
                }
                this.$refs.KJHatomPlayer.enableAudio(dic, (res) => {
                    console.log("openSound: " + JSON.stringify(res))
                });
            },
            /**
             *获取视频中屏幕显示时间
             * */
            getOSDTime() {
                var _this = this;
                var interval = setInterval(function() {
                    _this.$refs.KJHatomPlayer.getOSDTime((res) => {
                        console.log("getOSDTime: " + JSON.stringify(res))
                    });
                }, 1000);
            },
            getTotalTraffic() {
                var _this = this;
                var interval = setInterval(function() {
                    _this.$refs.KJHatomPlayer.getTotalTraffic((res) => {
                        console.log("getTotalTraffic: " + JSON.stringify(res))
                    });
                }, 1000);
            },
            /**
             *抓图
             *注意:filePath 一定要是_doc绝对目录
             * */
            screenshoot() {
                var filePath = plus.io.convertLocalFileSystemURL("_doc/KJHatomPlayer");
                var fileName = new Date().getTime() + ".png"
                var dic = {
                    "filePath": filePath,
                    "fileName": fileName,
                }
                console.log("dic: " + JSON.stringify(dic))
                if (plus.os.name == 'Android') {
                    this.$refs.KJHatomPlayer.screenshoot(dic, (res) => {
                        console.log("screenshoot: " + JSON.stringify(res))
                        this.imageSrc = plus.io.convertAbsoluteFileSystem(filePath + "/" +
                            fileName);
                        console.log(this.imageSrc);
                        uni.saveImageToPhotosAlbum({
                            filePath: this.imageSrc,
                            fail(res) {
                                console.log(JSON.stringify(res))
                            }
                        })
                    });
                } else {
                    this.$refs.KJHatomPlayer.screenshoot2(dic, (res) => {
                        console.log("screenshoot: " + JSON.stringify(res))
                        this.imageSrc = plus.io.convertAbsoluteFileSystem(filePath + "/" +
                            fileName);
                        console.log(this.imageSrc);
                        uni.saveImageToPhotosAlbum({
                            filePath: this.imageSrc,
                            fail(res) {
                                console.log(JSON.stringify(res))
                            }
                        })
                    });
                }
            },
            /**
             *开始录像
             *注意:filePath 一定要是_doc绝对目录
             * */
            startRecord() {
                var filePath = plus.io.convertLocalFileSystemURL("_doc/KJHatomPlayer");
                var fileName = new Date().getTime() + ".png"
                var dic = {
                    "filePath": filePath,
                    "fileName": fileName
                }
                this.recordFilePath = plus.io.convertAbsoluteFileSystem(filePath + "/" +
                    fileName)
                this.$refs.KJHatomPlayer.startRecord(dic, (res) => {
                    console.log("startRecord: " + JSON.stringify(res))

                });
            },
            /**
             *结束录像
             * */
            stopRecord() {
                this.$refs.KJHatomPlayer.stopRecord((res) => {
                    console.log("stopRecord: " + JSON.stringify(res))
                    uni.saveVideoToPhotosAlbum({
                        filePath: this.recordFilePath,
                        fail(res) {
                            console.log(JSON.stringify(res))
                        }
                    })
                });
            },
            /**
             *开启鱼眼
             * */
            setFishEyeEnable() {
                var dic = {
                    "enable": true, //是否打开
                    "isAddGr": true, //是否添加手势
                }
                this.$refs.KJHatomPlayer.setFishEyeEnable(dic, (res) => {
                    console.log("setFishEyeEnable: " + JSON.stringify(res))
                });
            },
            /**
             *关闭鱼眼
             * */
            setFishEyeEnable_close() {
                var dic = {
                    "enable": false, //是否打开
                    "isAddGr": false, //是否添加手势
                }
                this.$refs.KJHatomPlayer.setFishEyeEnable(dic, (res) => {
                    console.log("setFishEyeEnable: " + JSON.stringify(res))
                });
            },
            /**
             *设置鱼眼模式
             * 安装方式与矫正方式关系表 √-支持 x-不支持
             * 矫正方式    | Wall | Floor | Ceiling
             * Ptz        | √    | √     | √ 
             * 180        | x    | √     | √ 
             * 360        | √    | √     | √ 
             * Lat        | √    | x     | x 
             * Hemisphere | x    | √     | √ 
             * Cylinder   | x    | √     | √ 
             * Planet     | x    | √     | √ 
             * CylinderCut| x    | √     | √ 
             * */
            setFishEyeMode() {
                var dic = {
                    "correctType": "Hemisphere", //Ptz-PTZ模式 180-180度矫正(对于2P)360-360度矫正(对应1P)Lat-维度拉伸 Hemisphere-半球模式 Cylinder-圆柱模式 Planet-小行星模式 CylinderCut-圆柱断开
                    "placeType": "Ceiling", //None-无安装 Wall-壁装(法线水平)Floor-地面安装(法线向上)Ceiling-顶装方式(法线向下)
                }
                this.$refs.KJHatomPlayer.setFishEyeMode(dic, (res) => {
                    console.log("setFishEyeMode: " + JSON.stringify(res))
                });
            }
        }
    }
</script>
<style>
    .btns {
        display: flex;
        flex-wrap: wrap;
        flex-direction: row;
        align-items: center;
        justify-content: flex-start;
    }
</style>

错误码及说明

playStatus

错误码 说明
SUCCESS 播放成功(包括开启预览、开启回放、回放seek操作、开启 对讲)
FAILED 播放失败(包括开启预览、开启回放、回放seek操作、开启 对讲)
EXCEPTION 播放中异常(包括预览、回放、对讲中)
FINISH 播放结束

errorCode

错误码 说明
errorCode 错误码,只有在 playStatus 状态为: FAILED 、 EXCEPTION 才有值 ,其他 status 值为 -1。

取流库错误码 errorCode

错误代码数值 说明
0x017开头 媒体⽹网关或媒体取流SDK的错误
0x018开头 视频点播服务的错误码
0x019开头 设备接⼊入框架的错误码
0x022开头 视频联⽹网⽹网关的错误码

常⻅见问题排查

1.SDK 初始化的 appKey 从哪获取?

目前版本的 SDK 不校验 appKey,可以是任意字符,也可以为 nil。

2.开启语音对讲失败?

开放平台 OpenAPI 目前仅支持海康、国标、Ehome 协议接入的监控设备语音对讲。

3.为什么 SDK 不支持云台操作?

SDK 只负责取流及解码展示。而云台操作需要通过开放平台 OpenAPI 接口调用设备能力实现。

4.iOS11 以下 iPhone 转 MP4 格式后本地录像文件无法播放?

登录摄像头的页面(浏览器中输入摄像头的 ip 地址,输入摄像头用户名和密码),检查摄像头的视频格 式是否为 h265,iOS11 以下不支持 h265 解码,请升级 iPhone 手机的系统为 iOS11 及以上,或者设置 摄像头的视频格式为 h264(不推荐)。

5.开启实时预览或录像回放后,始终不显示画面?

首先请确认要查看的监控设备是否是大华设备,获取监控点取流 URL 的请求体"expand"字段中指定扩展 字段为: "expand": " transcode=1&videotype=h264" 。否则 SDK 无法解码大华协议的码流,导致画面无 法显示。

6.鹰眼全景相机设备无法预览

鹰眼全景相机设备不支持子码流,只支持主码流。将码流切换成主码流即可预览。

7.开启语音对讲失败?

请确认进行对讲的设备类型,目前仅支持海康、国标、Ehome协议接入的监控设备语音对讲。 如果出现错误码:0x03d1000b 则此设备的音频解码类型是Android手机不支持的类型。 解码类型只支持:G722、G711_U、G711_A三种,需要把设备的解码类型改成支持的解码类型

8.进行本地录像后发现没有录像文件生成?

请检测App是否有写入权限,如没有,则无法保存录像文件。 请确认是否使用转码库将录像文件进行转码,如已使用,请确认是否正常完成转码。

隐私、权限声明

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

麦克风权限:1.ios:NSMicrophoneUsageDescription;2.android:<uses-permission android:name="android.permission.RECORD_AUDIO"/> 动态申请麦克风权限

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

插件使用的 海康 SDK 会采集数据,详情可参考:https://open.hikvision.com/

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

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问