更新记录
1.0.7(2023-08-03)
取消预览成功提示
1.0.6(2022-06-30)
修复Demo中的Bug
1.0.2(2022-05-18)
发布新版本
查看更多平台兼容性
Android | Android CPU类型 | iOS |
---|---|---|
适用版本区间:5.0 - 11.0 | armeabi-v7a:支持,arm64-v8a:未测试,x86:未测试 | × |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
- 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
- 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
- 开发完毕后正式云打包
付费原生插件目前不支持离线打包。
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原生插件配置”->”云端插件“列表中删除该插件重新选择
注意:Android打包需设置:minSdkVersion>=21、targetSdkVersion>=26;文件必须写为".nvue"。
写在前面
因为有不少网友反馈没法使用,因此建议先测试你所使用的的摄像头是否是UVC摄像头。测试方法可以下载下面的这个APK安装包,安装到手机上,插入摄像头之后启动APP,看能否正常使用,能正常使用就说明是UVC摄像头,如果不能使用的话也没必要试用该插件了,以免给你造成困扰。
测试APK下载链接: https://pan.baidu.com/s/18sH3svBykgVEz_uJJSNsfg 提取码: f524
插件添加
0、点击右侧“试用”,选择你对应的工程,将插件添加到项目
1、添加插件之后,在HBuildX中编辑项目的manifest.json文件,打开云端插件,如下图:
2、在“App常用其他设置”指定sdk版本,并勾选CPU类型
3、切换到“源码视图”,在distribute->android->permissions节点下增加以下权限:
"<uses-permission android:name=\"android.hardware.usb.host\"/>",
"<uses-permission android:name=\"android.hardware.usb.accessory\"/>",
4、制作自定义基座,可以看DCloud自定义基座详细
5、选择自定义基座运行,在真机上进行测试。
插件说明
添加插件之后可以只用使用sintrb-uvcviewer
UVC显示组件,只能在.nvue中用,该组件有以下方法:
let iv = this.$refs.iv; // 先获取组件
iv.test(callback); // 测试用,无意义
iv.start(callback); // 开始
iv.stop(callback); // 停止
iv.restart(callback); // 重启摄像头
iv.snap(options, callback); // 截图,options无意义,可传{}
iv.getSupportedPreviewSizes(callback); // 获取当前摄像头支持的预览尺寸
iv.setPreviewSize(options, callback); // 设置当前摄像头的预览尺寸,options参数类似{index:0},其中index参数为getSupportedPreviewSizes方法回调得到的尺寸列表下标序号
该组件有以下属性:
- showControlBar: bool 是否显示控制功能
- rotation: float 旋转角度
- showFps: bool 是否显示FPS
- previewSizeIndex: int 预览尺寸的索引,默认为0(一般就是摄像头支持的最高分辨率)
- deviceId: int UVC设备ID,即手机分配给USB设备的ID。
该组件有以下事件@onStatusChange,当状态发送变化是通过该事件进行通知,可通过事件对象event.detail.status得到当前状态,状态值定义如下
private final int STATUS_NONE = 0; // 初始状态
private final int STATUS_INITING = 1000; // 正在初始化中
private final int STATUS_RETRY = 1100; // 正在重试中
private final int STATUS_CAM_GETTING = 2000; // 正在获取摄像头
private final int STATUS_CAM_OPENING = 2100; // 正在打开摄像头
private final int STATUS_CAM_INITING = 2200; // 摄像头初始化中
private final int STATUS_CAM_DETACHED = 2300; // 摄像头已移除
private final int STATUS_WAIT_PLAY = 5000; // 摄像头打开成,等待预览
private final int STATUS_PLAYING = 5100; // 正在预览
private final int STATUS_STOPED = 6000; // 预览已停止
private final int STATUS_ERROR = -1; // 操作出错
辅助模块sintrb-uvcmodule
可用于获取当前设备所连接的USB设备(可以在.js、.vue、.nvue中用),用法如下:
const iuvc = uni.requireNativePlugin("sintrb-uvcmodule");
// 获取UVC设备列表(仅bDeviceClass为239、bDeviceSubclass为2的设备)
iuvc.getUvcDevices({}, res => {
console.log(JSON.stringify(res))
});
// 获取所有USB设备列表()
iuvc.getUsbDevices({}, res => {
console.log(JSON.stringify(res))
});
反复打开关闭摄像头,有时候会出现状态显示为成功,但实际看不到图像,这种情况下可以先把组件卸载了重新再加载试试(用一个变量结合v-if控制试试)。有时候摄像头正常但一直显示打开失败(类似错误码-99),可以试试重启程序和重新拔插一下摄像头。
更多信息可以看例子里面的代码,文件必须写为.nvue。
<template>
<view style="display: flex;flex-direction: column; font-size: 12px;">
<view class="previews" v-if="show">
<view class="preview-wrap">
<sintrb-uvcviewer ref="iv" class="preview" :rotation="rotation" @onStatusChange="onStatusChange">
</sintrb-uvcviewer>
</view>
</view>
<view v-if="previewSizeList.length" style="display: flex; flex-direction: row; flex-wrap: wrap;">
<view :class="{selected:previewSizeI === i}" @tap="previewSizeI = i"
style="border: 2rpx solid #eee; padding: 5rpx;" v-for="s,i in previewSizeList">{{s.width}}x{{s.height}}
</view>
</view>
<view class="flex btns">
<button class="grow1 button" size="mini" type="default" @tap="show = !show">{{show?"关闭":"显示"}}</button>
<button class="grow1 button" size="mini" type="default" @tap="doIVAction('test')">测试</button>
<button class="grow1 button" size="mini" type="default" @tap="doIVAction('start')">开始</button>
<button class="grow1 button" size="mini" type="default" @tap="doIVAction('stop')">停止</button>
<button class="grow1 button" size="mini" type="default" @tap="getSnap()">截图</button>
<button class="grow1 button" size="mini" type="default" @tap="rotation = (rotation + 90) % 360">旋转</button>
<button class="grow1 button" size="mini" type="default" @tap="getSupportedPreviewSizes()">获取支持的尺寸</button>
<button class="grow1 button" size="mini" type="default" @tap="getUvcDevices()">获取USB设备列表</button>
</view>
<scroll-view v-if="images.length" scroll-x="true" style="flex-direction: row;margin-top: 5px;">
<view style="display: flex;flex-direction: row;">
<image v-for="it,ix in images" @tap="viewImg(it,ix)" :key="it.key" :src="it.src" mode="heightFix"
style="max-width: 60px; height: 60px;border: 1px solid red; margin-right: 1px;"></image>
</view>
</scroll-view>
<scroll-view class="logs" scroll-y="true" style="flex-direction: column;margin-top: 5px;">
<view style="display: flex;flex-direction: column;">
<view v-for="l in logs"
style="margin-top: 1rpx; font-size: 8px; width: auto; border: 1rpx solid #EEEEEE; padding: 10rpx;">
<text>{{l}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
const iuvc = uni.requireNativePlugin("sintrb-uvcmodule")
export default {
data() {
return {
show: true,
previewSizeI: -1,
previewSizeList: [],
rotation: 0,
logs: [],
images: [],
}
},
computed: {
},
watch: {
previewSizeI() {
// console.log("previewSizeI", this.previewSizeI);
this.addLog("previewSizeI " + this.previewSizeI);
// let size = this.previewSizeList[this.previewSizeI];
this.doIVAction("setPreviewSize", {
index: this.previewSizeI
});
}
},
methods: {
getUvcDevices() {
iuvc.getUvcDevices({}, res => {
res.data.devices.map(dev => {
let ndev = JSON.parse(JSON.stringify(dev));
dev.showJson = false;
dev.showPreview = false;
return dev;
})
console.log(JSON.stringify(res))
this.devices = res.data.devices
this.addLog(res);
});
},
getSupportedPreviewSizes() {
this.doIVAction("getSupportedPreviewSizes", null, res => {
if (res && res.data) {
this.previewSizeList = res.data.items;
}
})
},
getSnap() {
this.doIVAction("snap", {}, res => {
this.addLog(res);
if (res && res.data) {
this.addImg(res.data.path);
}
})
},
async doIVAction(action, options, cbk) {
let iv = this.$refs.iv;
if (!iv) {
this.res = "没有iv " + Object.keys(this.$refs).join(",")
return;
}
let func = iv[action];
if (!func) {
this.addLog("没有iv." + action + " " + Object.keys(iv).join(","));
return;
}
// this.res = 'R ' + action + ' : ' + func;
let args = [];
if (options) {
args.push(options);
}
args.push(res => {
this.addLog(res);
if (cbk) {
cbk(res);
}
})
this.res = args;
try {
func.apply(iv, args);
} catch (e) {
this.addLog("ERR " + e);
}
},
onStatusChange(e) {
this.addLog(e.detail);
if (e.detail.status === 5100 && !this.previewSizeList.length) {
// 预览成功,获取分辨率
this.getSupportedPreviewSizes()
}
},
addLog(l) {
if (typeof(l) !== "string") {
l = JSON.stringify(l);
}
this.logs.unshift(l);
},
viewImg(it, ix) {
uni.previewImage({
urls: this.images.map(r => r.src),
index: ix,
})
},
addImg(img) {
this.images.splice(0, 0, {
src: img,
key: Date.now(),
})
this.addLog(img);
},
}
}
</script>
<style lang="scss">
.mini-btn {
padding: 5rpx;
}
.btns {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
}
.button {
// width: 100rpx;
padding: 3px 5px;
}
.previews {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
margin-bottom: 10px;
}
.preview-wrap {
background: black;
margin: 2px;
min-width: 320px;
min-height: 240px;
}
.preview {
width: 400px;
height: 300px;
}
.selected {
background: red;
}
.logs {
// border: 1rpx solid #eee;
// padding: 5px;
// margin: 5px;
}
</style>