From 3a85d42c022a472185aaa23171f8526c35a87e7d Mon Sep 17 00:00:00 2001
From: fanpeng <438123081@qq.com>
Date: Tue, 24 Jun 2025 18:15:27 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=AE=89=E5=8D=93?=
=?UTF-8?q?=E7=AB=AF=E7=9B=91=E6=8E=A7=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pages/main/lockDetail.vue | 17 +-
pages/p2p/p2pPlayer.vue | 141 ++----------
uni_modules/xhj-record/package.json | 83 +++++++
.../utssdk/app-android/AndroidManifest.xml | 8 +
.../xhj-record/utssdk/app-android/config.json | 3 +
.../xhj-record/utssdk/app-android/index.uts | 209 ++++++++++++++++++
uni_modules/xhj-record/utssdk/interface.uts | 5 +
.../utssdk/app-android/index.uts | 131 ++++++-----
8 files changed, 419 insertions(+), 178 deletions(-)
create mode 100644 uni_modules/xhj-record/package.json
create mode 100644 uni_modules/xhj-record/utssdk/app-android/AndroidManifest.xml
create mode 100644 uni_modules/xhj-record/utssdk/app-android/config.json
create mode 100644 uni_modules/xhj-record/utssdk/app-android/index.uts
create mode 100644 uni_modules/xhj-record/utssdk/interface.uts
diff --git a/pages/main/lockDetail.vue b/pages/main/lockDetail.vue
index bd4ee91..3cc70b9 100644
--- a/pages/main/lockDetail.vue
+++ b/pages/main/lockDetail.vue
@@ -288,6 +288,10 @@
import { deleteKeyRequest } from '@/api/key'
import { transportType } from '@/constant/transportType'
+ // #ifdef APP-PLUS
+ import { requestPermission } from '@/uni_modules/xhj-record'
+ // #endif
+
const $bluetooth = useBluetoothStore()
const $basic = useBasicStore()
const $lock = useLockStore()
@@ -309,7 +313,18 @@
await getServeTime()
})
- const jumpToPlayer = () => {
+ const jumpToPlayer = async () => {
+ // #ifdef APP-PLUS
+ // 检查权限
+ const result = await requestPermission()
+ if (result.code !== 0) {
+ uni.showToast({
+ title: '请在设置中打开应用的麦克风权限',
+ icon: 'none'
+ })
+ return
+ }
+ // #endif
$basic.routeJump({ name: 'p2pPlayer' })
}
diff --git a/pages/p2p/p2pPlayer.vue b/pages/p2p/p2pPlayer.vue
index b38b8f2..12c34a6 100644
--- a/pages/p2p/p2pPlayer.vue
+++ b/pages/p2p/p2pPlayer.vue
@@ -189,12 +189,12 @@
// #ifdef APP-PLUS
import {
startService,
- getLiveUrl,
stopService,
runSendService,
stopSendService,
dataSend
} from '@/uni_modules/xhj-tencent-xp2p'
+ import { initAudio, onStartRecord, stopRecord, releaseRecord } from '@/uni_modules/xhj-record'
// #endif
const $bluetooth = useBluetoothStore()
@@ -229,9 +229,8 @@
onMounted(async () => {
// #ifdef APP-PLUS
+ initAudio()
isApp.value = true
- // 初始化录音管理器
- initRecorder()
// #endif
// #ifdef MP-WEIXIN
@@ -271,22 +270,13 @@
xp2pInfo: deviceInfo.value.xp2pInfo
})
if (result.code === 0) {
- setTimeout(async () => {
- const urlResult = await getLiveUrl({
- id: `${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`
- })
- if (urlResult.code === 0) {
- url.value = urlResult.data.url
- runSendService(
- `${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`,
- 'channel=0',
- false
- )
- handlePlaySuccess()
- } else {
- $basic.backAndToast(message)
- }
- }, 200)
+ url.value = result.data.url
+ runSendService(
+ `${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`,
+ 'channel=0',
+ false
+ )
+ handlePlaySuccess()
} else {
$basic.backAndToast(message)
}
@@ -299,10 +289,7 @@
onUnload(() => {
// #ifdef APP-PLUS
// 停止录音
- if (isVoice.value && recorderManager.value) {
- stopRecording()
- }
-
+ releaseRecord()
stopSendService(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`)
stopService({
id: `${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`
@@ -480,104 +467,6 @@
return false
}
- // #ifdef APP-PLUS
- const recorderManager = ref(null)
- const recordingFilePath = ref('')
- // #endif
-
- const convertAudioFileToUint8Array = filePath => {
- // #ifdef APP-PLUS
- try {
- plus.io.resolveLocalFileSystemURL(filePath, entry => {
- entry.file(
- file => {
- console.log('文件内容', file)
- if (file.size === 0) return
-
- const fileReader = new plus.io.FileReader()
-
- fileReader.onloadend = async evt => {
- try {
- console.log(evt)
- const base64 = evt.target.result.split(',')[1]
-
- const binaryString = atob(base64)
- const byteArray = []
-
- for (let i = 0; i < binaryString.length; i++) {
- byteArray[i] = binaryString.charCodeAt(i)
- }
-
- const id = `${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`
-
- await dataSend(id, byteArray)
- } catch (error) {
- console.error('音频数据转换失败:', error)
- }
- }
-
- fileReader.onerror = () => {
- console.error('音频文件读取失败')
- }
-
- fileReader.readAsDataURL(file)
- },
- error => console.error('获取文件对象失败:', error)
- )
- })
- } catch (error) {
- console.error('音频文件处理异常:', error)
- }
- // #endif
- }
-
- const initRecorder = () => {
- // #ifdef APP-PLUS
- recorderManager.value = uni.getRecorderManager()
-
- recorderManager.value.onStart(() => {
- console.log('录音开始')
- })
-
- recorderManager.value.onError(error => {
- console.error('录音出错:', error)
- })
-
- recorderManager.value.onStop(res => {
- console.log('录音结束:', res)
- recordingFilePath.value = res.tempFilePath
-
- convertAudioFileToUint8Array(res.tempFilePath)
- })
- // #endif
- }
-
- // 开始录音
- const startRecording = () => {
- // #ifdef APP-PLUS
- if (!recorderManager.value) {
- initRecorder()
- }
-
- recorderManager.value.start({
- duration: 60000,
- sampleRate: 16000,
- format: 'aac'
- })
- console.log('开始录音')
- // #endif
- }
-
- // 停止录音
- const stopRecording = () => {
- // #ifdef APP-PLUS
- if (recorderManager.value) {
- recorderManager.value.stop()
- console.log('停止录音')
- }
- // #endif
- }
-
const toggleVoice = () => {
// #ifdef MP-WEIXIN
if (isVoice.value) {
@@ -600,17 +489,19 @@
// #ifdef APP-PLUS
if (isVoice.value) {
isVoice.value = false
-
- stopRecording()
+ stopRecord()
} else {
uni.vibrateLong()
isVoice.value = true
- // 初始化并开始录音
- startRecording()
+ onStartRecord(callback)
}
// #endif
}
+ const callback = audioData => {
+ dataSend(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`, audioData)
+ }
+
const handlePlaySuccess = () => {
isVideoLoaded.value = true
}
diff --git a/uni_modules/xhj-record/package.json b/uni_modules/xhj-record/package.json
new file mode 100644
index 0000000..4b6c479
--- /dev/null
+++ b/uni_modules/xhj-record/package.json
@@ -0,0 +1,83 @@
+{
+ "id": "xhj-record",
+ "displayName": "xhj-record",
+ "version": "1.0.0",
+ "description": "xhj-record",
+ "keywords": [
+ "xhj-record"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.6.8"
+ },
+ "dcloudext": {
+ "type": "uts",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "",
+ "data": "",
+ "permissions": ""
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "u",
+ "aliyun": "u",
+ "alipay": "u"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "u",
+ "vue3": "u"
+ },
+ "App": {
+ "app-android": "u",
+ "app-ios": "u",
+ "app-harmony": "u"
+ },
+ "H5-mobile": {
+ "Safari": "u",
+ "Android Browser": "u",
+ "微信浏览器(Android)": "u",
+ "QQ浏览器(Android)": "u"
+ },
+ "H5-pc": {
+ "Chrome": "u",
+ "IE": "u",
+ "Edge": "u",
+ "Firefox": "u",
+ "Safari": "u"
+ },
+ "小程序": {
+ "微信": "u",
+ "阿里": "u",
+ "百度": "u",
+ "字节跳动": "u",
+ "QQ": "u",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/xhj-record/utssdk/app-android/AndroidManifest.xml b/uni_modules/xhj-record/utssdk/app-android/AndroidManifest.xml
new file mode 100644
index 0000000..211df66
--- /dev/null
+++ b/uni_modules/xhj-record/utssdk/app-android/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/uni_modules/xhj-record/utssdk/app-android/config.json b/uni_modules/xhj-record/utssdk/app-android/config.json
new file mode 100644
index 0000000..bf95925
--- /dev/null
+++ b/uni_modules/xhj-record/utssdk/app-android/config.json
@@ -0,0 +1,3 @@
+{
+ "minSdkVersion": "21"
+}
\ No newline at end of file
diff --git a/uni_modules/xhj-record/utssdk/app-android/index.uts b/uni_modules/xhj-record/utssdk/app-android/index.uts
new file mode 100644
index 0000000..6735ea4
--- /dev/null
+++ b/uni_modules/xhj-record/utssdk/app-android/index.uts
@@ -0,0 +1,209 @@
+/* eslint-disable */
+// @ts-nocheck
+// @ts-ignore-start
+import 'android.media.AudioRecord'
+import 'android.media.MediaRecorder'
+import 'android.media.AudioFormat'
+import 'android.media.MediaSyncEvent'
+import 'java.lang.Thread'
+import { UTSAndroid } from 'io.dcloud.uts'
+import { Result } from '../interface.uts'
+
+let recorder: AudioRecord | null = null
+let recorderState: boolean = false
+let bufferSizeInBytes: Int = 0
+
+export const requestPermission = async function (): Promise {
+ return new Promise(resolve => {
+ try {
+ let permissionNeed = ['android.permission.RECORD_AUDIO']
+
+ UTSAndroid.requestSystemPermission(
+ UTSAndroid.getUniActivity()!,
+ permissionNeed,
+ function (allRight: boolean, _: string[]) {
+ if (allRight) {
+ resolve({
+ code: 0,
+ data: {},
+ message: '成功'
+ })
+ } else {
+ resolve({
+ code: -1,
+ data: {},
+ message: '失败'
+ })
+ }
+ },
+ function (_: boolean, _: string[]) {
+ resolve({
+ code: -1,
+ data: {},
+ message: '失败'
+ })
+ }
+ )
+ } catch (error) {
+ resolve({
+ code: -1,
+ data: {},
+ message: error.toString()
+ })
+ }
+ })
+}
+
+export const initAudio = async function (): Promise {
+ try {
+ const audioSource = MediaRecorder.AudioSource.MIC
+ const sampleRateInHz = 44100
+ const channelConfig = AudioFormat.CHANNEL_IN_MONO
+ const audioFormat = AudioFormat.ENCODING_PCM_16BIT
+
+ bufferSizeInBytes = AudioRecord.getMinBufferSize(
+ sampleRateInHz.toInt(),
+ channelConfig,
+ audioFormat
+ )
+
+ console.log('bufferSizeInBytes', bufferSizeInBytes)
+
+ recorder = new AudioRecord(
+ audioSource,
+ sampleRateInHz.toInt(),
+ channelConfig,
+ audioFormat,
+ bufferSizeInBytes
+ )
+
+ const currentRecorder = recorder
+ if (currentRecorder !== null) {
+ console.log('初始化录音结果:', currentRecorder.getState())
+ if (currentRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
+ return {
+ code: 0,
+ data: {},
+ message: '成功'
+ }
+ }
+ }
+ return {
+ code: -1,
+ data: {},
+ message: '初始化录音失败'
+ }
+ } catch (error) {
+ console.log('初始化录音失败', error)
+ return {
+ code: -1,
+ data: {},
+ message: error.toString()
+ }
+ }
+}
+
+export async function onStartRecord(callback: (data: Array) => void): Promise {
+ try {
+ const callbackFunction = () => {
+ const currentRecorder = recorder
+ if (currentRecorder !== null) {
+ while (recorderState) {
+ let audioData = new ByteArray(bufferSizeInBytes)
+ const result: Int = currentRecorder.read(audioData, 0, bufferSizeInBytes)
+ if (result > 0) {
+ callback(Array.fromNative(audioData))
+ console.log('录音按帧返回数据', Array.fromNative(audioData))
+ }
+ Thread.sleep(10)
+ }
+ }
+ return
+ }
+
+ const currentRecorder = recorder
+ if (currentRecorder !== null) {
+ currentRecorder.startRecording()
+ console.log('开始录音')
+ recorderState = true
+
+ callbackFunction()
+
+ return {
+ code: 0,
+ data: {},
+ message: '成功'
+ }
+ }
+ return {
+ code: -1,
+ data: {},
+ message: '开始录音失败'
+ }
+ } catch (error) {
+ console.log('开始录音失败', error)
+ return {
+ code: -1,
+ data: {},
+ message: error.toString()
+ }
+ }
+}
+
+export const stopRecord = async function (): Promise {
+ try {
+ const currentRecorder = recorder
+ if (currentRecorder !== null) {
+ currentRecorder.stop()
+ recorderState = false
+ console.log('停止录音')
+ return {
+ code: 0,
+ data: {},
+ message: '成功'
+ }
+ }
+ return {
+ code: -1,
+ data: {},
+ message: '停止录音失败'
+ }
+ } catch (error) {
+ console.log('停止录音失败', error)
+ recorderState = false
+ return {
+ code: -1,
+ data: {},
+ message: error.toString()
+ }
+ }
+}
+
+export const releaseRecord = async function (): Promise {
+ try {
+ const currentRecorder = recorder
+ if (currentRecorder !== null) {
+ currentRecorder.release()
+ console.log('释放录音')
+ return {
+ code: 0,
+ data: {},
+ message: '成功'
+ }
+ }
+ return {
+ code: -1,
+ data: {},
+ message: '释放录音失败'
+ }
+ } catch (error) {
+ console.log('释放录音失败', error)
+ return {
+ code: -1,
+ data: {},
+ message: error.toString()
+ }
+ }
+}
+
+// @ts-ignore-end
diff --git a/uni_modules/xhj-record/utssdk/interface.uts b/uni_modules/xhj-record/utssdk/interface.uts
new file mode 100644
index 0000000..0dab6cd
--- /dev/null
+++ b/uni_modules/xhj-record/utssdk/interface.uts
@@ -0,0 +1,5 @@
+export type Result = {
+ code: number
+ data: object
+ message: string
+}
diff --git a/uni_modules/xhj-tencent-xp2p/utssdk/app-android/index.uts b/uni_modules/xhj-tencent-xp2p/utssdk/app-android/index.uts
index f37c1e1..60e6bc9 100644
--- a/uni_modules/xhj-tencent-xp2p/utssdk/app-android/index.uts
+++ b/uni_modules/xhj-tencent-xp2p/utssdk/app-android/index.uts
@@ -1,7 +1,69 @@
+/* eslint-disable */
+// @ts-nocheck
+// @ts-ignore-start
+
import { UTSAndroid } from 'io.dcloud.uts'
-import { XP2PAppConfig, XP2P } from 'com.tencent.xnet'
+import { XP2PAppConfig, XP2P, XP2PCallback } from 'com.tencent.xnet'
import { Result, InitParams, IdParams } from '../interface.uts'
+class XP2PCallbackImpl extends XP2PCallback {
+ private eventNotifyCallback?: (id: string | null, msg: string | null, event: Int) => void
+
+ constructor(eventNotifyCallback?: (id: string | null, msg: string | null, event: Int) => void) {
+ super()
+ this.eventNotifyCallback = eventNotifyCallback
+ }
+
+ override fail(msg: string | null, errorCode: Int): void {
+ console.log('XP2P callback fail:', msg, errorCode)
+ }
+
+ override commandRequest(id: string | null, msg: string | null): void {
+ console.log('XP2P callback commandRequest:', id, msg)
+ }
+
+ override xp2pEventNotify(id: string | null, msg: string | null, event: Int): void {
+ console.log('XP2P callback xp2pEventNotify:', id, msg, event)
+ this.eventNotifyCallback?.invoke(id, msg, event)
+ }
+
+ override avDataRecvHandle(id: string | null, data: ByteArray | null, len: Int): void {
+ console.log('XP2P callback avDataRecvHandle:', id, len)
+ }
+
+ override avDataCloseHandle(id: string | null, msg: string | null, errorCode: Int): void {
+ console.log('XP2P callback avDataCloseHandle:', id, msg, errorCode)
+ }
+
+ override onDeviceMsgArrived(id: string | null, data: ByteArray | null, len: Int): string {
+ console.log('XP2P callback onDeviceMsgArrived:', id, len)
+ return ''
+ }
+}
+
+async function getLiveUrlAsync(
+ id: string,
+ resolve: (result: Result) => void,
+ reject: (result: Result) => void
+): Promise {
+ try {
+ const liveUrl = await XP2P.delegateHttpFlv(id)
+ resolve({
+ code: 0,
+ data: {
+ url: liveUrl
+ },
+ message: '成功'
+ })
+ } catch (error) {
+ reject({
+ code: -1,
+ data: {},
+ message: error.toString()
+ })
+ }
+}
+
export const startService = async function (params: InitParams): Promise {
try {
const context = UTSAndroid.getAppContext()
@@ -19,31 +81,17 @@ export const startService = async function (params: InitParams): Promise
xp2pConfig
)
- return {
- code: 0,
- data: {},
- message: '成功'
- }
- } catch (error) {
- return {
- code: -1,
- data: {},
- message: error.toString()
- }
- }
-}
+ return new Promise((resolve, reject) => {
+ const callback: XP2PCallback = new XP2PCallbackImpl(
+ (id: string | null, msg: string | null, event: Int): void => {
+ if (event == 1004) {
+ getLiveUrlAsync(id!, resolve, reject)
+ }
+ }
+ )
-export const getLiveUrl = async function (params: IdParams): Promise {
- try {
- const liveUrl = await XP2P.delegateHttpFlv(params.id)
-
- return {
- code: 0,
- data: {
- url: liveUrl
- },
- message: '成功'
- }
+ XP2P.setCallback(callback)
+ })
} catch (error) {
return {
code: -1,
@@ -70,13 +118,6 @@ export const stopService = async function (params: IdParams): Promise {
}
}
-// export const setCallback = function (callback: XP2PCallback) {
-// try {
-// XP2P.setCallback(callback)
-// } catch (error) {
-// console.log(2, error)
-// }
-// }
export const runSendService = async function (
id: string,
cmd: string,
@@ -119,31 +160,15 @@ export const stopSendService = async function (id: string): Promise {
export const dataSend = async function (id: string, data: Array): Promise {
try {
- let byteTest = new ByteArray(data.length.toInt())
- // byteTest.set(0, (0xff).toByte())
- // byteTest.set(1, (0xf9).toByte())
-
- // let profile = 2
- // let freqIdx = 8
- // let chanCfg = 1
- // let packetLen = data.length + 7
-
- // byteTest.set(2, (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2)).toByte())
- // byteTest.set(3, (((chanCfg & 3) << 6) + (packetLen >> 11)).toByte())
- // byteTest.set(4, ((packetLen & 0x7ff) >> 3).toByte())
- // byteTest.set(5, (((packetLen & 7) << 5) + 0x1f).toByte())
- // byteTest.set(6, (0xfc).toByte())
+ let byteArray = new ByteArray(data.length.toInt())
for (let i = 0; i < data.length; i++) {
- byteTest.set(i.toInt(), data[i].toByte())
+ byteArray.set(i.toInt(), data[i].toByte())
}
- console.log(byteTest)
- console.log(1, id, byteTest[0], byteTest[1], data.length.toInt())
+ const result = await XP2P.dataSend(id, byteArray, data.length.toInt())
- const result = await XP2P.dataSend(id, byteTest, data.length.toInt())
-
- console.log('发送数据', result)
+ console.log('发送数据结果', result, byteArray)
return {
code: 0,
@@ -158,3 +183,5 @@ export const dataSend = async function (id: string, data: Array): Promis
}
}
}
+
+// @ts-ignore-end