更新记录

1.1.6(2026-07-29)

-优化Android的返回按钮 关闭相机事件

1.1.5(2026-07-29)

-优化界面 -增加手势缩放 -手电筒等等若干优化功能

1.1.4(2026-07-28)

-重新上传插件包

查看更多

平台兼容性

uni-app(4.18)

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

uni-app x(4.18)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

ly028-Camera UTS 三端实时帧相机

真实案例体验

门禁 / 考勤类真实项目落地(ly028-reg):人脸注册、人脸库管理、拍照识别、实时相机流识别

扫码下载真实案例
📱 下载真实案例 APK(ly028-reg)

真实案例截图 1 真实案例截图 2 真实案例截图 3 真实案例截图 4

实时相机流 OCR 识别(ly028-cameraOCR)-- 基于 ly028-Camera 帧捕获 + ly028-OCR 流式识别,实时文字识别。

扫码下载真实案例
📱 下载真实案例 APK(ly028-cameraOCR)

真实案例截图

实际接入项目:人脸识别考勤打卡(Camera + OffFace 组合方案) 扫码体验完整的人脸识别打卡 App:

插件 下载 插件市场
ly028-Camera(本插件) Camera 扫码下载
📱 下载 APK
https://ext.dcloud.net.cn/plugin?id=28471
ly028-OffFace(人脸检测识别) OffFace 扫码下载
📱 下载 APK
https://ext.dcloud.net.cn/plugin?id=28446

Android / iOS / HarmonyOS 原生相机 UTS 插件,实时多格式帧数据输出(NV12/NV21/YUV420P/BGR888/RGBA8888),全屏原生 UI 预览(Android/iOS),适用于人脸识别(seetaface6)、AI 视觉等场景。

特性

  • 📷 全屏原生相机 UI — Android/iOS 全屏原生相机 UI(鸿蒙需 uni-app x 配合 XComponent)
  • 🔄 实时多格式帧捕获 — 支持 NV12 / NV21 / YUV420P (I420) / BGR888 / RGBA8888 逐帧回调,无需前端转换即可直接喂给 AI 引擎(seetaface6/MediaPipe/OpenCV)
  • 📱 三端一致 API — Android / iOS / HarmonyOS API 完全相同,调用端无平台差异
  • 🎯 拍照 + 录像 — 支持拍照和视频录制
  • 👤 人脸框叠加 — 支持实时人脸检测框与标签文字叠加显示
  • 🔦 手电筒 — 原生 UI 手电筒按钮,点击点亮/关闭
  • 👆 点击对焦 — 触摸屏幕自动对焦到点击位置(原生手势,无需 API)
  • 双指变焦 — 双指捏合拉近拉远,显示变焦倍数(原生手势,无需 API)
  • 🔄 文字自适应 — 框标签文字自动沿长边旋转 + 面积法自动字号
  • 📱 沉浸式全屏兼容 — 适配挖孔屏/刘海屏系统栏,预览不变形
  • 🧩 UTS 插件 — uni-app 原生插件,即插即用

平台兼容

平台 最低版本 架构 预览 UI 无界面帧捕获
Android 7.0 (API 24) arm64-v8a, armeabi-v7a ✅ 全屏原生
iOS 12.0 arm64 ✅ 全屏原生
HarmonyOS (uni-app x) API 12 arm64 ✅ 需 xcomponentId
HarmonyOS (普通 uni-app) API 12 arm64 ❌ 见下文

鸿蒙(HarmonyOS)特别说明

架构限制

鸿蒙 ArkUI 与 iOS/Android 的 UI 架构有本质区别,这直接影响了相机预览的实现方式:

平台 创建全屏预览方式
iOS keyWindow.addSubview(cameraView) — UIKit 允许任意添加 UIView
Android decorView.addView(nativeView) — Android View 系统允许任意添加 View
鸿蒙 没有等价 API — ArkUI 组件树由框架统一管理,后台代码无法插入可见节点

iOS/Android 可以直接在插件后端创建全屏相机 View。鸿蒙则必须在页面组件树中有一个 <XComponent><NodeContainer> 作为相机预览的挂载点。

xcomponentId 参数(仅 uni-app x)

如果你使用 uni-app x,可以在 CameraOptions 中传入 XComponent 的 id:

import { openCamera } from '@/uni_modules/ly028-Camera'

openCamera(JSON.stringify({
  position: 'back',
  enableFrameCapture: true,
  xcomponentId: 'camera-preview',   // 页面中 <XComponent> 的 id
  format: 'rgba',
}), (res) => {
  if (JSON.parse(res).code === 0) {
    console.log('相机已启动(带预览)')
  }
})

xcomponentId 不为空时:

  1. 插件用该 id 创建 PreviewOutput 并加到相机 session
  2. XComponent 上会显示实时相机画面
  3. enableFrameCapture: true 同时获取帧数据

普通 uni-app(WebView)

普通 uni-app 的页面运行在 WebView 中,无法嵌入 ArkUI 原生 XComponent。因此在鸿蒙上:

  • 无相机预览画面
  • 但帧数据捕获正常工作 — 可通过 onFrame 获取 NV12/NV21/YUV420P/BGR888/RGBA 帧数据用于 AI 处理
  • API 调用方式与 Android/iOS 完全一致

三端 API 一致性

无论是否有预览,所有平台使用相同的 API:

import { openCamera, closeCamera, onFrame, onCameraClosed } from '@/uni_modules/ly028-Camera'

// 打开相机(三端一致)
openCamera(JSON.stringify({
  enableFrameCapture: true,
  format: 'nv12',
  maxFps: 15
}), () => {})

// 获取帧数据(三端一致)
onFrame((json) => {
  const frame = JSON.parse(json)
  console.log(frame.width, frame.height, frame.format)
})

| `closeCamera()` | 关闭相机释放资源 |
onCameraClosed(() => {
  console.log('相机已关闭')
})

// 关闭相机(三端一致)
| `closeCamera()` | 关闭相机释放资源 |

集成与调试

HBuilderX 自定义基座运行

  1. 插件市场购买插件后,在 manifest.json → App 原生插件配置中选择 ly028-Camera
  2. 选择需要使用的模块
  3. 自定义调试基座:菜单栏 → 运行 → 运行到手机或模拟器 → 制作自定义调试基座
  4. 制作完成后,选择自定义基座运行到真机即可调试所有功能

首次使用请确保已关联 uni-app 开发者证书,自定义基座有效期为 7 天,过期后需重新制作。

离线打包

插件为原生代码插件,离线打包需自行集成原生依赖。

快速开始

import { openCamera, closeCamera, onFrame, onCameraClosed } from '@/uni_modules/ly028-Camera'

// 1. 打开相机纯帧捕获模式(无 UI,直接回调帧数据)
openCamera(JSON.stringify({
  position: 'front',
  enableFrameCapture: true,
  format: 'rgba',              // 输出格式:nv12 / nv21 / yuv420p / bgr888 / rgba
  maxFps: 15,                  // 帧率上限
}), (res) => {
  if (JSON.parse(res).code === 0) {
    console.log('相机已启动')
  }
})

// 2. 监听帧数据
onFrame((json) => {
  const frame = JSON.parse(json)
  // frame: { data: base64, width, height, format, timestamp, rotation, position }
  console.log('帧:', frame.width, 'x', frame.height, 'format:', frame.format)

  // RGBA 帧可直接喂给 OffFace
  // import { detectFace } from '@/uni_modules/ly028-OffFace'
  // const buf = uni.base64ToArrayBuffer(frame.data)
  // const result = detectFace(buf, frame.width, frame.height, 4)
})

| `closeCamera()` | 关闭相机释放资源 |
onCameraClosed(() => {
  console.log('相机已关闭')
})

// 4. 关闭相机
| `closeCamera()` | 关闭相机释放资源 |

全量 API 参考

相机生命周期

API 说明
openCamera(options, callback) 打开相机预览
stopPreview() 停止预览(冻结画面,不释放相机资源)
closeCamera() 关闭相机释放资源

帧捕获

API 说明
onFrame(callback) 注册帧数据回调
offFrame() 移除帧数据回调
updateFaceRects(json) 更新人脸框(坐标/标签/颜色)
updateDebugInfo(json) 显示调试文字(text/color/x/y/fontSize)

帧捕获格式 — 通过 FrameCaptureOptionsformat 字段指定:

格式 说明 字节/像素 用途
'nv12' NV12 Y+交错UV 1.5 默认,iOS 原生输出
'nv21' NV21 Y+交错VU 1.5 Android 相机常见格式
'yuv420p' I420 平面 Y+U+V 1.5 标准 YUV 交换格式
'bgr888' BGR888 B,G,R 3 seetaface6 引擎内部格式
'rgba' RGBA8888 R,G,B,A 4 可直接喂给 OffFace detectFace(data, w, h, 4)

updateFaceRects — 人脸框 JSON schema

传入 JSON 数组字符串。坐标为帧空间(与 onFramewidth/height 一致),插件会缩放到预览层;前置摄像头会自动水平镜像 X。

字段 类型 必填 默认 说明
x / y number 框左上角(帧坐标)
width / height number 框宽高(帧坐标)
color string '#00FF00' 边框色,#RGB / #RRGGBB / #RRGGBBAA
label string '' 框上方文字
fontSize number 24 标签字号
fontColor string '#FFFFFF' 标签颜色
autoAngle boolean true true=竖长框文字自动旋转 90° 沿长边
autoFontSize boolean false true=字号按框面积 √(w×h)/5 自动计算,忽略 fontSize
angle number 0 文字旋转角度(autoAngle=false 时可手动指定)
import { updateFaceRects } from '@/uni_modules/ly028-Camera'

// 绘制人脸框
updateFaceRects(JSON.stringify([
  {
    x: 100, y: 200, width: 120, height: 120,
    color: '#00FF00', label: '人脸1',
    fontSize: 15, fontColor: '#FFFFFF'
  }
]))

// 清空所有框
updateFaceRects('[]')

updateDebugInfo — 调试文字 JSON schema

传入 JSON 对象字符串text 为空字符串时清屏;支持 \n 换行。

字段 类型 必填 默认 说明
text string 显示文字;'' 清屏
x / y number 10 预览层像素坐标
color string '#00FF00' 文字颜色
fontSize number 14 字号
import { updateDebugInfo } from '@/uni_modules/ly028-Camera'

updateDebugInfo(JSON.stringify({
  text: '640x480 120KB #42\nfmt: rgba',
  x: 16, y: 24, color: '#00FF00', fontSize: 14
}))

// 清屏
updateDebugInfo(JSON.stringify({ text: '' }))

拍照

API 说明
onPhoto(callback) 注册拍照结果回调
offPhoto() 移除拍照结果回调

签名:(json: string) => voidjson 为统一信封;data 是嵌套 JSON 字符串,需再 JSON.parse 一次。拍照按钮与相册选择都走同一回调。

import { onPhoto, offPhoto } from '@/uni_modules/ly028-Camera'

onPhoto((json) => {
  const r = JSON.parse(json)
  if (r.code === 0 && r.data) {
    const d = JSON.parse(r.data)
    // d: { filePath, width, height }
    console.log('照片:', d.filePath, d.width, 'x', d.height)
  }
})

// 页面卸载时移除
offPhoto()

成功载荷示例:

{
  "code": 0,
  "msg": "OK",
  "data": "{\"filePath\":\"/path/to/photo.jpg\",\"width\":1920,\"height\":1080}"
}

相册选择时 width / height 可能为 0(平台未解析图片尺寸)。

事件

API 说明
onError(callback) 错误回调 (code, subCode?, msg)
offError() 移除错误回调
onCameraClosed(callback) 相机已关闭回调
offCameraClosed() 移除关闭回调

onError — 三参签名

签名:(code: number, subCode: number | null, msg: string) => void。与其它 API 的 JSON 信封回调不同,错误事件直接传三个独立参数。

import { onError } from '@/uni_modules/ly028-Camera'

onError((code, subCode, msg) => {
  console.log('相机错误:', code, subCode, msg)
  // code 恒为 1;subCode 为 HTTP 风格细分码
})

常见 subCode

subCode 含义
400 参数错误 / 为空
404 资源未找到(如无可用相机)
422 操作不支持
500 内部错误
501 平台不支持
502 外部错误
503 服务不可用

onCameraClosed — 零参签名

| closeCamera() | 关闭相机释放资源 |

import { onCameraClosed, offCameraClosed } from '@/uni_modules/ly028-Camera'

onCameraClosed(() => {
  console.log('相机已关闭')
})

offCameraClosed()

权限

API 说明
checkCameraPermission() 检查相机权限 → Boolean

CameraOptions

字段 类型 默认 说明
position 'back' \| 'front' 'back' 相机位置
resolution {width, height} 自动 预览分辨率
fps number 30 预览帧率
focusMode 'auto' \| 'continuous' \| 'tap' \| 'locked' 'continuous' 对焦模式
exposureMode 'auto' \| 'continuous' \| 'locked' 'auto' 曝光模式
flashMode 'off' \| 'on' \| 'auto' \| 'torch' 'off' 闪光模式
whiteBalance 'auto' \| 'sunlight' \| 'cloudy' \| 'fluorescent' \| 'incandescent' 'auto' 白平衡
resizeMode 'cover' \| 'contain' \| 'fill' 'cover' 预览缩放
enableAudio boolean true 录像是否录音
enableFrameCapture boolean false 启用帧捕获模式
xcomponentId string '' 鸿蒙 uni-app x 专用:XComponent 的 id,不为空时显示预览画面
format string 'nv12' 帧输出格式
maxFps number 15 帧捕获最大帧率
debug boolean false 调试日志开关

返回值格式

所有 API 返回统一 JSON 信封:

// 成功
{"code": 0, "msg": "OK"}

// 带数据(拍照:data 为嵌套 JSON 字符串,含 filePath / width / height)
{"code": 0, "data": "{\\"filePath\\":\\"/path/to/photo.jpg\\",\\"width\\":1920,\\"height\\":1080}"}

// 失败
{"code": 1, "subCode": 500, "msg": "错误描述"}

帧数据结构

{
  "data": "<base64 编码帧数据>",
  "width": 640,
  "height": 480,
  "format": "rgba",
  "timestamp": 1712345678000,
  "rotation": 90,
  "position": "front"
}
  • 格式data 为 Base64 编码字符串,需前端解码为 ArrayBuffer 使用
  • 数据类型:根据 format 不同,每像素字节数不同(nv12/nv21/yuv420p=1.5B,bgr888=3B,rgba=4B)

帧数据传输:Base64 方案说明

帧数据采用 Base64 编码字符串 传输,而非 ArrayBufferbyte[]

选择 Base64 的原因:兼容 uni-app 和 uni-app x,三端类型统一。

方案 uni-app JS uni-app x UTS Android iOS HarmonyOS
ArrayBuffer ✅ 支持 ❌ uni-app x 不支持
Uint8Array ❌ 类型不一致
String (Base64) base64ToArrayBuffer base64ToArrayBuffer Base64.decode Data(base64Encoded:) ✅ 内置 Base64
byte[] (原生) ❌ 无法直传 ✅ 但跨平台不一致

结论: Base64 是 Android / iOS / HarmonyOS 三端和 uni-app / uni-app x 两种模式下唯一都能直接处理的帧数据格式。前端或后端业务层按需解码即可。

注意事项

  1. 帧数据较大(1080p 约 3MB/帧),建议按需开启 frameCallback
  2. 相机权限需在 manifest.json 中配置 CAMERARECORD_AUDIO 权限
  3. 前后切换会短暂黑屏,属正常现象
  4. 不支持模拟器,请在真机上调试

隐私声明

  • 本插件不采集任何用户数据
  • 所有相机操作在设备端本地完成
  • 不上传任何图像或视频数据

体验

实际接入项目:人脸识别考勤打卡(Camera + OffFace 组合)

以下 APK 为 Camera + OffFace 人脸识别的完整集成示例,扫码可下载 Android 体验包。

插件 下载 插件市场
ly028-Camera(本插件) Camera 扫码下载
📱 下载 APK
https://ext.dcloud.net.cn/plugin?id=28471
ly028-OffFace(人脸检测) OffFace 扫码下载
📱 下载 APK
https://ext.dcloud.net.cn/plugin?id=28446

Camera 提供实时帧数据,OffFace 进行人脸检测/识别,两者组合即可实现完整的端上人脸识别方案。

许可

  • 插件市场购买后获得使用授权
  • 禁止反编译、二次分发

隐私、权限声明

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

Android: CAMERA, RECORD_AUDIO; iOS: NSCameraUsageDescription; Harmony: ohos.permission.CAMERA

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

No data collected. All camera operations are local.

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

none