Merge branch 'fanpeng' into 'develop'
Fanpeng See merge request StarlockTeam/wx-starlock!46
This commit is contained in:
commit
09ddaea597
2
App.vue
2
App.vue
@ -81,7 +81,7 @@
|
||||
listenerUI: {
|
||||
cameraRotation: 270,
|
||||
objectFit: 'fill',
|
||||
enableToggleCamera: false
|
||||
enableToggleCamera: true
|
||||
}
|
||||
})
|
||||
wmpfVoip.setVoipEndPagePath({
|
||||
|
||||
@ -104,7 +104,6 @@
|
||||
"modules" : {
|
||||
"Bluetooth" : {},
|
||||
"VideoPlayer" : {},
|
||||
"LivePusher" : {},
|
||||
"Camera" : {},
|
||||
"Record" : {}
|
||||
},
|
||||
|
||||
@ -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' })
|
||||
}
|
||||
|
||||
|
||||
@ -25,15 +25,16 @@
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<video
|
||||
:key="videoKey"
|
||||
autoplay
|
||||
id="playerRef"
|
||||
v-if="url"
|
||||
:muted="isMute"
|
||||
:src="urlPrefix"
|
||||
:advanced="advancedOptions"
|
||||
object-fit="cover"
|
||||
class="w-[100vw] h-[100vh]"
|
||||
:is-live="true"
|
||||
:play-strategy="2"
|
||||
:controls="false"
|
||||
:show-progress="false"
|
||||
:show-fullscreen-btn="false"
|
||||
@ -50,7 +51,7 @@
|
||||
></image>
|
||||
|
||||
<cover-view
|
||||
v-if="isApp || buttonInfo"
|
||||
v-if="(isApp || buttonInfo) && isVideoLoaded"
|
||||
:style="{
|
||||
top: isApp ? '60px' : buttonInfo.bottom + 15 + 'px'
|
||||
}"
|
||||
@ -188,13 +189,13 @@
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
import {
|
||||
startService,
|
||||
getLiveUrl,
|
||||
stopService,
|
||||
runSendService,
|
||||
stopSendService,
|
||||
dataSend
|
||||
startServiceFunction,
|
||||
stopServiceFunction,
|
||||
runSendServiceFunction,
|
||||
stopSendServiceFunction,
|
||||
dataSendFunction
|
||||
} from '@/uni_modules/xhj-tencent-xp2p'
|
||||
import { initAudio, onStartRecord, stopRecord, releaseRecord } from '@/uni_modules/xhj-record'
|
||||
// #endif
|
||||
|
||||
const $bluetooth = useBluetoothStore()
|
||||
@ -222,16 +223,65 @@
|
||||
const isVoice = ref(false)
|
||||
const isMute = ref(false)
|
||||
const isVideoLoaded = ref(false)
|
||||
const videoKey = ref(Date.now())
|
||||
const cleanupCalled = ref(false)
|
||||
|
||||
const advancedOptions = ref([
|
||||
{
|
||||
key: 'videotoolbox',
|
||||
value: 0,
|
||||
type: 'player'
|
||||
},
|
||||
{
|
||||
key: 'framedrop',
|
||||
value: 5,
|
||||
type: 'player'
|
||||
},
|
||||
{
|
||||
key: 'skip_loop_filter',
|
||||
value: 48,
|
||||
type: 'player'
|
||||
}
|
||||
])
|
||||
|
||||
const urlPrefix = computed(() => {
|
||||
return url.value + `ipc.flv?action=live&channel=0&quality=${range.value[index.value].value}`
|
||||
const data =
|
||||
url.value + `ipc.flv?action=live&channel=0&quality=${range.value[index.value].value}`
|
||||
return data
|
||||
})
|
||||
|
||||
const cleanupResources = () => {
|
||||
if (cleanupCalled.value) return
|
||||
cleanupCalled.value = true
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// 停止录音
|
||||
url.value = null
|
||||
isVideoLoaded.value = false
|
||||
if (isVoice.value) {
|
||||
stopRecord()
|
||||
}
|
||||
releaseRecord()
|
||||
if (deviceInfo.value) {
|
||||
stopSendServiceFunction(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`)
|
||||
stopServiceFunction(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`)
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
const page = getCurrentPages().pop()
|
||||
if (page) {
|
||||
const player = page.selectComponent('#playerRef')
|
||||
if (player && player.stopAll) {
|
||||
player.stopAll()
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// #ifdef APP-PLUS
|
||||
initAudio()
|
||||
isApp.value = true
|
||||
// 初始化录音管理器
|
||||
initRecorder()
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
@ -263,30 +313,32 @@
|
||||
// #ifdef APP-PLUS
|
||||
if (code === 0) {
|
||||
deviceInfo.value = data
|
||||
const result = await startService({
|
||||
const params = {
|
||||
appKey: 'aanuJXFtISXFYVVsd',
|
||||
appSecret: 'SsnOMHJUcazCvpULSVWY',
|
||||
productId: deviceInfo.value.productId,
|
||||
deviceName: deviceInfo.value.deviceName,
|
||||
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',
|
||||
true
|
||||
)
|
||||
handlePlaySuccess()
|
||||
} else {
|
||||
$basic.backAndToast(message)
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
|
||||
const result = await startServiceFunction(
|
||||
params.appKey,
|
||||
params.appSecret,
|
||||
params.productId,
|
||||
params.deviceName,
|
||||
params.xp2pInfo
|
||||
)
|
||||
console.log('初始化SDK结果:', result)
|
||||
if (result?.code === 0) {
|
||||
handlePlaySuccess()
|
||||
setTimeout(() => {
|
||||
url.value = result.data.url
|
||||
runSendServiceFunction(
|
||||
`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`,
|
||||
'channel=0',
|
||||
false
|
||||
)
|
||||
}, 0)
|
||||
} else {
|
||||
$basic.backAndToast(message)
|
||||
}
|
||||
@ -297,31 +349,19 @@
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
// #ifdef APP-PLUS
|
||||
// 停止录音
|
||||
if (isVoice.value && recorderManager.value) {
|
||||
stopRecording()
|
||||
}
|
||||
|
||||
stopSendService(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`)
|
||||
stopService({
|
||||
id: `${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`
|
||||
})
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
const page = getCurrentPages().pop()
|
||||
const player = page.selectComponent('#playerRef')
|
||||
if (player.stopAll) {
|
||||
player.stopAll()
|
||||
}
|
||||
// #endif
|
||||
cleanupResources()
|
||||
})
|
||||
|
||||
const showQualitySelector = () => {
|
||||
uni.showActionSheet({
|
||||
itemList: range.value.map(item => item.name),
|
||||
success: res => {
|
||||
index.value = res.tapIndex
|
||||
const value = res.tapIndex
|
||||
if (index.value === value) {
|
||||
return
|
||||
}
|
||||
index.value = value
|
||||
videoKey.value = Date.now()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -361,13 +401,7 @@
|
||||
}
|
||||
|
||||
const handleHangUp = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
const page = getCurrentPages().pop()
|
||||
const player = page.selectComponent('#playerRef')
|
||||
if (player.stopAll) {
|
||||
player.stopAll()
|
||||
}
|
||||
// #endif
|
||||
cleanupResources()
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
@ -480,104 +514,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(11111, 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 +536,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 => {
|
||||
dataSendFunction(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`, audioData)
|
||||
}
|
||||
|
||||
const handlePlaySuccess = () => {
|
||||
isVideoLoaded.value = true
|
||||
}
|
||||
|
||||
83
uni_modules/xhj-record/package.json
Normal file
83
uni_modules/xhj-record/package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.dcloud.nativeresouce">
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<application>
|
||||
<!--meta-data-->
|
||||
</application>
|
||||
</manifest>
|
||||
3
uni_modules/xhj-record/utssdk/app-android/config.json
Normal file
3
uni_modules/xhj-record/utssdk/app-android/config.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"minSdkVersion": "21"
|
||||
}
|
||||
209
uni_modules/xhj-record/utssdk/app-android/index.uts
Normal file
209
uni_modules/xhj-record/utssdk/app-android/index.uts
Normal file
@ -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<Result> {
|
||||
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<Result> {
|
||||
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<Result> {
|
||||
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<Result> {
|
||||
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<Result> {
|
||||
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
|
||||
8
uni_modules/xhj-record/utssdk/app-ios/Info.plist
Normal file
8
uni_modules/xhj-record/utssdk/app-ios/Info.plist
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>需要访问麦克风进行录音</string>
|
||||
</dict>
|
||||
</plist>
|
||||
3
uni_modules/xhj-record/utssdk/app-ios/config.json
Normal file
3
uni_modules/xhj-record/utssdk/app-ios/config.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"deploymentTarget": "12"
|
||||
}
|
||||
244
uni_modules/xhj-record/utssdk/app-ios/hybrid.swift
Normal file
244
uni_modules/xhj-record/utssdk/app-ios/hybrid.swift
Normal file
@ -0,0 +1,244 @@
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
@objc
|
||||
public class RecordPermission: NSObject {
|
||||
@objc
|
||||
public static func requestRecordPermission(_ completion: @escaping (Bool) -> Void) {
|
||||
AVAudioSession.sharedInstance().requestRecordPermission { granted in
|
||||
completion(granted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public class AudioRecorderManager: NSObject {
|
||||
private var audioEngine: AVAudioEngine?
|
||||
private var audioConverter: AVAudioConverter?
|
||||
private var aacBuffer: AVAudioCompressedBuffer?
|
||||
|
||||
private let lock = NSLock()
|
||||
private var _isRecording = false
|
||||
var isRecording: Bool {
|
||||
get {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return _isRecording
|
||||
}
|
||||
set {
|
||||
lock.lock()
|
||||
_isRecording = newValue
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public static let shared = AudioRecorderManager()
|
||||
|
||||
private override init() {}
|
||||
|
||||
@objc
|
||||
public func initAudio(_ completion: @escaping (Bool, String) -> Void) {
|
||||
completion(true, "Module initialized")
|
||||
}
|
||||
|
||||
@objc
|
||||
public func startRecord(_ completion: @escaping (Data?, Bool, String) -> Void) {
|
||||
if self.isRecording {
|
||||
completion(nil, false, "Recording is already in progress.")
|
||||
return
|
||||
}
|
||||
|
||||
let session = AVAudioSession.sharedInstance()
|
||||
do {
|
||||
try session.setCategory(.playAndRecord, mode: .default, options: .defaultToSpeaker)
|
||||
try session.setPreferredSampleRate(16000.0)
|
||||
try session.setPreferredInputNumberOfChannels(1)
|
||||
try session.setActive(true)
|
||||
} catch {
|
||||
completion(nil, false, "Failed to set up audio session: \(error.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
audioEngine = AVAudioEngine()
|
||||
guard let audioEngine = audioEngine else {
|
||||
completion(nil, false, "Failed to create audio engine")
|
||||
return
|
||||
}
|
||||
|
||||
let inputNode = audioEngine.inputNode
|
||||
let inputFormat = inputNode.outputFormat(forBus: 0)
|
||||
|
||||
var outputFormatDescription = AudioStreamBasicDescription(
|
||||
mSampleRate: 16000.0,
|
||||
mFormatID: kAudioFormatMPEG4AAC,
|
||||
mFormatFlags: 2,
|
||||
mBytesPerPacket: 0,
|
||||
mFramesPerPacket: 1024,
|
||||
mBytesPerFrame: 0,
|
||||
mChannelsPerFrame: 1,
|
||||
mBitsPerChannel: 0,
|
||||
mReserved: 0
|
||||
)
|
||||
|
||||
guard let outputFormat = AVAudioFormat(streamDescription: &outputFormatDescription) else {
|
||||
completion(nil, false, "Failed to create output audio format")
|
||||
return
|
||||
}
|
||||
|
||||
guard let converter = AVAudioConverter(from: inputFormat, to: outputFormat) else {
|
||||
completion(nil, false, "Failed to create audio converter")
|
||||
return
|
||||
}
|
||||
self.audioConverter = converter
|
||||
|
||||
self.aacBuffer = AVAudioCompressedBuffer(
|
||||
format: outputFormat,
|
||||
packetCapacity: 1,
|
||||
maximumPacketSize: converter.maximumOutputPacketSize
|
||||
)
|
||||
|
||||
inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) { [weak self] (pcmBuffer, when) in
|
||||
guard let self = self, self.isRecording else { return }
|
||||
self.convert(pcmBuffer: pcmBuffer, completion: completion)
|
||||
}
|
||||
|
||||
do {
|
||||
audioEngine.prepare()
|
||||
try audioEngine.start()
|
||||
self.isRecording = true
|
||||
completion(nil, true, "Recording started")
|
||||
} catch {
|
||||
self.isRecording = false
|
||||
completion(nil, false, "Failed to start audio engine: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
private func convert(pcmBuffer: AVAudioPCMBuffer, completion: @escaping (Data?, Bool, String) -> Void) {
|
||||
guard let converter = self.audioConverter, let outputBuffer = self.aacBuffer else { return }
|
||||
|
||||
outputBuffer.byteLength = 0
|
||||
outputBuffer.packetCount = 0
|
||||
|
||||
var error: NSError?
|
||||
var pcmBufferWasProvided = false
|
||||
let status = converter.convert(to: outputBuffer, error: &error) { _, outStatus in
|
||||
if pcmBufferWasProvided {
|
||||
outStatus.pointee = .noDataNow
|
||||
return nil
|
||||
}
|
||||
outStatus.pointee = .haveData
|
||||
pcmBufferWasProvided = true
|
||||
return pcmBuffer
|
||||
}
|
||||
|
||||
guard status != .error, error == nil else {
|
||||
print("AAC conversion error: \(error?.localizedDescription ?? "unknown")")
|
||||
return
|
||||
}
|
||||
|
||||
if outputBuffer.byteLength == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
let aacData = Data(bytes: outputBuffer.data, count: Int(outputBuffer.byteLength))
|
||||
|
||||
guard let adtsHeader = self.adtsHeader(for: aacData.count) else {
|
||||
print("Failed to create ADTS header")
|
||||
return
|
||||
}
|
||||
|
||||
var fullPacket = Data()
|
||||
fullPacket.append(Data(adtsHeader))
|
||||
fullPacket.append(aacData)
|
||||
|
||||
completion(fullPacket, true, "")
|
||||
}
|
||||
|
||||
private func adtsHeader(for aacFrameSize: Int) -> [UInt8]? {
|
||||
guard let outputFormat = self.audioConverter?.outputFormat else { return nil }
|
||||
|
||||
let adtsLength = aacFrameSize + 7
|
||||
let sampleRate = outputFormat.sampleRate
|
||||
let channels = outputFormat.channelCount
|
||||
|
||||
let sampleRateIndices: [Double: Int] = [
|
||||
96000: 0, 88200: 1, 64000: 2, 48000: 3, 44100: 4, 32000: 5,
|
||||
24000: 6, 22050: 7, 16000: 8, 12000: 9, 11025: 10, 8000: 11, 7350: 12
|
||||
]
|
||||
guard let freqIndex = sampleRateIndices[sampleRate] else {
|
||||
print("Unsupported sample rate for ADTS header: \(sampleRate)")
|
||||
return nil
|
||||
}
|
||||
|
||||
let profile = 2 // AAC-LC
|
||||
let channelCfg = channels
|
||||
|
||||
var adtsHeader = [UInt8](repeating: 0, count: 7)
|
||||
adtsHeader[0] = 0xFF
|
||||
adtsHeader[1] = 0xF9
|
||||
adtsHeader[2] = UInt8(((profile - 1) << 6) | (freqIndex << 2) | (Int(channelCfg) >> 2))
|
||||
adtsHeader[3] = UInt8((Int(channelCfg) & 3) << 6 | (adtsLength >> 11))
|
||||
adtsHeader[4] = UInt8((adtsLength & 0x7FF) >> 3)
|
||||
adtsHeader[5] = UInt8(((adtsLength & 7) << 5) | 0x1F)
|
||||
adtsHeader[6] = 0xFC
|
||||
|
||||
return adtsHeader
|
||||
}
|
||||
|
||||
@objc
|
||||
public func stopRecord(_ completion: @escaping (Bool, String, String) -> Void) {
|
||||
guard self.isRecording else {
|
||||
completion(false, "Recording is not in progress.", "")
|
||||
return
|
||||
}
|
||||
self.isRecording = false
|
||||
|
||||
audioEngine?.stop()
|
||||
audioEngine?.inputNode.removeTap(onBus: 0)
|
||||
audioEngine = nil
|
||||
audioConverter = nil
|
||||
aacBuffer = nil
|
||||
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
||||
} catch {
|
||||
print("Failed to deactivate audio session: \(error)")
|
||||
}
|
||||
|
||||
completion(true, "Recording stopped", "")
|
||||
}
|
||||
|
||||
@objc
|
||||
public func releaseRecord(_ completion: @escaping (Bool, String) -> Void) {
|
||||
if self.isRecording {
|
||||
self.isRecording = false
|
||||
audioEngine?.stop()
|
||||
audioEngine?.inputNode.removeTap(onBus: 0)
|
||||
}
|
||||
audioEngine = nil
|
||||
audioConverter = nil
|
||||
aacBuffer = nil
|
||||
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
||||
} catch {
|
||||
print("Failed to deactivate audio session on release: \(error)")
|
||||
}
|
||||
|
||||
completion(true, "Record released")
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public class UTSConversionHelper: NSObject {
|
||||
@objc
|
||||
public static func dataToNSArray(_ data: Data) -> NSArray {
|
||||
let byteArray = [UInt8](data)
|
||||
let nsArray = NSMutableArray()
|
||||
for byte in byteArray {
|
||||
nsArray.add(NSNumber(value: byte))
|
||||
}
|
||||
return nsArray
|
||||
}
|
||||
}
|
||||
85
uni_modules/xhj-record/utssdk/app-ios/index.uts
Normal file
85
uni_modules/xhj-record/utssdk/app-ios/index.uts
Normal file
@ -0,0 +1,85 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// @ts-ignore-start
|
||||
|
||||
import { Result } from '../interface.uts'
|
||||
|
||||
function dataToByteArray(data: Data): Array<UInt8> {
|
||||
const nsArray = UTSConversionHelper.dataToNSArray(data)
|
||||
const buffer: Array<UInt8> = []
|
||||
if (nsArray != null) {
|
||||
for (const item of nsArray) {
|
||||
const num = item as NSNumber
|
||||
buffer.push(num.uint8Value)
|
||||
}
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
export const requestPermission = async function (): Promise<Result> {
|
||||
return new Promise(resolve => {
|
||||
RecordPermission.requestRecordPermission((granted: boolean) => {
|
||||
if (granted) {
|
||||
resolve({ code: 0, data: {}, message: '成功' })
|
||||
} else {
|
||||
resolve({ code: -1, data: {}, message: '失败' })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const initAudio = async function (): Promise<Result> {
|
||||
return new Promise(resolve => {
|
||||
AudioRecorderManager.shared.initAudio((success: boolean, message: string) => {
|
||||
if (success) {
|
||||
resolve({ code: 0, data: {}, message: message })
|
||||
} else {
|
||||
resolve({ code: -1, data: {}, message: message })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function onStartRecord(callback: (data: Array<UInt8>) => void): Promise<Result> {
|
||||
return new Promise(resolve => {
|
||||
AudioRecorderManager.shared.startRecord(
|
||||
(data: Data | null, success: boolean, message: string) => {
|
||||
if (data != null) {
|
||||
callback(dataToByteArray(data!))
|
||||
} else if (success) {
|
||||
resolve({ code: 0, data: {}, message: message })
|
||||
} else {
|
||||
resolve({ code: -1, data: {}, message: message })
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const stopRecord = async function (): Promise<Result> {
|
||||
return new Promise(resolve => {
|
||||
AudioRecorderManager.shared.stopRecord(
|
||||
(success: boolean, message: string, filePath: string) => {
|
||||
if (success) {
|
||||
resolve({ code: 0, data: { filePath: filePath }, message: message })
|
||||
} else {
|
||||
resolve({ code: -1, data: {}, message: message })
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const releaseRecord = async function (): Promise<Result> {
|
||||
return new Promise(resolve => {
|
||||
AudioRecorderManager.shared.releaseRecord((success: boolean, message: string) => {
|
||||
if (success) {
|
||||
resolve({ code: 0, data: {}, message: message })
|
||||
} else {
|
||||
resolve({ code: -1, data: {}, message: message })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// @ts-ignore-end
|
||||
5
uni_modules/xhj-record/utssdk/interface.uts
Normal file
5
uni_modules/xhj-record/utssdk/interface.uts
Normal file
@ -0,0 +1,5 @@
|
||||
export type Result = {
|
||||
code: number
|
||||
data: object
|
||||
message: string
|
||||
}
|
||||
@ -1,49 +1,97 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// @ts-ignore-start
|
||||
|
||||
import { UTSAndroid } from 'io.dcloud.uts'
|
||||
import { XP2PAppConfig, XP2P } from 'com.tencent.xnet'
|
||||
import { Result, InitParams, IdParams } from '../interface.uts'
|
||||
import { XP2PAppConfig, XP2P, XP2PCallback } from 'com.tencent.xnet'
|
||||
import { Result } from '../interface.uts'
|
||||
|
||||
export const startService = async function (params: InitParams): Promise<Result> {
|
||||
try {
|
||||
const context = UTSAndroid.getAppContext()
|
||||
class XP2PCallbackImpl extends XP2PCallback {
|
||||
private eventNotifyCallback?: (id: string | null, msg: string | null, event: Int) => void
|
||||
|
||||
let xp2pConfig: XP2PAppConfig = new XP2PAppConfig()
|
||||
constructor(eventNotifyCallback?: (id: string | null, msg: string | null, event: Int) => void) {
|
||||
super()
|
||||
this.eventNotifyCallback = eventNotifyCallback
|
||||
}
|
||||
|
||||
xp2pConfig.appKey = params.appKey
|
||||
xp2pConfig.appSecret = params.appSecret
|
||||
override fail(msg: string | null, errorCode: Int): void {
|
||||
console.log('XP2P callback fail:', msg, errorCode)
|
||||
}
|
||||
|
||||
await XP2P.startService(
|
||||
context,
|
||||
params.productId,
|
||||
params.deviceName,
|
||||
params.xp2pInfo,
|
||||
xp2pConfig
|
||||
)
|
||||
override commandRequest(id: string | null, msg: string | null): void {
|
||||
console.log('XP2P callback commandRequest:', id, msg)
|
||||
}
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '成功'
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
message: error.toString()
|
||||
}
|
||||
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 ''
|
||||
}
|
||||
}
|
||||
|
||||
export const getLiveUrl = async function (params: IdParams): Promise<Result> {
|
||||
async function getLiveUrlAsync(
|
||||
id: string,
|
||||
resolve: (result: Result) => void,
|
||||
reject: (result: Result) => void
|
||||
): Promise<void> {
|
||||
try {
|
||||
const liveUrl = await XP2P.delegateHttpFlv(params.id)
|
||||
|
||||
return {
|
||||
const liveUrl = await XP2P.delegateHttpFlv(id)
|
||||
resolve({
|
||||
code: 0,
|
||||
data: {
|
||||
url: liveUrl
|
||||
},
|
||||
message: '成功'
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
reject({
|
||||
code: -1,
|
||||
data: {},
|
||||
message: error.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const startServiceFunction = async function (
|
||||
appKey: string,
|
||||
appSecret: string,
|
||||
productId: string,
|
||||
deviceName: string,
|
||||
xp2pInfo: string
|
||||
): Promise<Result> {
|
||||
try {
|
||||
const context = UTSAndroid.getAppContext()
|
||||
|
||||
let xp2pConfig: XP2PAppConfig = new XP2PAppConfig()
|
||||
|
||||
xp2pConfig.appKey = appKey
|
||||
xp2pConfig.appSecret = appSecret
|
||||
|
||||
await XP2P.startService(context, productId, deviceName, xp2pInfo, xp2pConfig)
|
||||
|
||||
return new Promise<Result>((resolve, reject) => {
|
||||
const callback: XP2PCallback = new XP2PCallbackImpl(
|
||||
(id: string | null, msg: string | null, event: Int): void => {
|
||||
if (event == 1004) {
|
||||
getLiveUrlAsync(id!, resolve, reject)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
XP2P.setCallback(callback)
|
||||
})
|
||||
} catch (error) {
|
||||
return {
|
||||
code: -1,
|
||||
@ -53,9 +101,9 @@ export const getLiveUrl = async function (params: IdParams): Promise<Result> {
|
||||
}
|
||||
}
|
||||
|
||||
export const stopService = async function (params: IdParams): Promise<Result> {
|
||||
export const stopServiceFunction = async function (id: string): Promise<Result> {
|
||||
try {
|
||||
await XP2P.stopService(params.id)
|
||||
await XP2P.stopService(id)
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
@ -70,21 +118,14 @@ export const stopService = async function (params: IdParams): Promise<Result> {
|
||||
}
|
||||
}
|
||||
|
||||
// export const setCallback = function (callback: XP2PCallback) {
|
||||
// try {
|
||||
// XP2P.setCallback(callback)
|
||||
// } catch (error) {
|
||||
// console.log(2, error)
|
||||
// }
|
||||
// }
|
||||
export const runSendService = async function (
|
||||
export const runSendServiceFunction = async function (
|
||||
id: string,
|
||||
cmd: string,
|
||||
crypto: boolean
|
||||
): Promise<Result> {
|
||||
try {
|
||||
await XP2P.runSendService(id, cmd, crypto)
|
||||
console.log('开始发送服务')
|
||||
const result = await XP2P.runSendService(id, cmd, crypto)
|
||||
console.log('开始发送服务', result)
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
@ -99,10 +140,10 @@ export const runSendService = async function (
|
||||
}
|
||||
}
|
||||
|
||||
export const stopSendService = async function (id: string): Promise<Result> {
|
||||
export const stopSendServiceFunction = async function (id: string): Promise<Result> {
|
||||
try {
|
||||
await XP2P.stopSendService(id, null)
|
||||
console.log('停止发送服务')
|
||||
const result = await XP2P.stopSendService(id, null)
|
||||
console.log('停止发送服务', result)
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
@ -117,33 +158,17 @@ export const stopSendService = async function (id: string): Promise<Result> {
|
||||
}
|
||||
}
|
||||
|
||||
export const dataSend = async function (id: string, data: Array<number>): Promise<Result> {
|
||||
export const dataSendFunction = async function (id: string, data: Array<number>): Promise<Result> {
|
||||
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<number>): Promis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore-end
|
||||
|
||||
13
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/Info.plist
Normal file
13
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/Info.plist
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsArbitraryLoadsInWebContent</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
333
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/Libs/TIoTLinkVideo/AppWrapper.h
Executable file
333
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/Libs/TIoTLinkVideo/AppWrapper.h
Executable file
@ -0,0 +1,333 @@
|
||||
#ifndef __APPWRAPPER_H_
|
||||
#define __APPWRAPPER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef WINDOWS
|
||||
#ifdef IPCLIBRARY_EXPORTS
|
||||
# define IPCLIBRARY_API __declspec(dllexport)
|
||||
#else
|
||||
# define IPCLIBRARY_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
# define IPCLIBRARY_API
|
||||
#endif
|
||||
|
||||
#define MAX_SIZE_OF_PARAMS 3000
|
||||
|
||||
static const char * VIDEOSDKVERSION = "2.4.x+git.2a469eda";
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XP2P_PROTOCOL_AUTO = 0, //auto模式,udp不通自动切换tcp
|
||||
XP2P_PROTOCOL_UDP = 1, //udp传输
|
||||
XP2P_PROTOCOL_TCP = 2, //tcp传输
|
||||
} XP2PProtocolType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XP2PTypeClose = 1000, //数据传输完成
|
||||
XP2PTypeLog = 1001, //日志输出
|
||||
XP2PTypeCmd = 1002, // command json
|
||||
XP2PTypeDisconnect = 1003, // p2p链路断开
|
||||
XP2PTypeDetectReady = 1004, // p2p链路初始化成功
|
||||
XP2PTypeDetectError = 1005, // p2p链路初始化失败
|
||||
XP2PTypeDeviceMsgArrived = 1006, // 设备端向App发消息
|
||||
XP2PTypeCmdNOReturn = 1007, // 设备未返回app自定义信令
|
||||
XP2PTypeStreamEnd = 1008, // 设备停止推流,或者由于达到设备最大连接数,拒绝推流
|
||||
XP2PTypeDownloadEnd = 1009, // 设备停止推流,下载结束
|
||||
XP2PTypeStreamRefush = 1010, // 设备拒绝推流,请求的devicename不一致
|
||||
XP2PTypeSaveFileOn = 8000, //获取保存音视频流开关状态
|
||||
XP2PTypeSaveFileUrl = 8001 //获取音视频流保存路径
|
||||
} XP2PType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XP2PVoiceServiceClose = 2000, //语音对讲服务关闭
|
||||
XP2PStreamServiceClose = 2001 //音视频流接收服务关闭
|
||||
} XP2PCloseSubType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XP2P_ERR_NONE = 0, //成功
|
||||
XP2P_ERR_INIT_PRM = -1000, //入参为空
|
||||
XP2P_ERR_GET_XP2PINFO = -1001, // SDK内部请求xp2p info失败
|
||||
XP2P_ERR_PROXY_INIT = -1002, //本地p2p代理初始化失败
|
||||
XP2P_ERR_UNINIT = -1003, //数据接收/发送服务未初始化
|
||||
XP2P_ERR_ENCRYPT = -1004, //数据加密失败
|
||||
XP2P_ERR_TIMEOUT = -1005, //请求超时
|
||||
XP2P_ERR_REQUEST_FAIL = -1006, //请求错误
|
||||
XP2P_ERR_VERSION = -1007, //设备版本过低,请升级设备固件
|
||||
XP2P_ERR_APPLICATION = -1008, // application初始化失败
|
||||
XP2P_ERR_REQUEST = -1009, // request初始化失败
|
||||
XP2P_ERR_DETECT_NOTREADY = -1010, // p2p探测未完成
|
||||
XP2P_ERR_P2P_ININED = -1011, //当前id对应的p2p已完成初始化
|
||||
XP2P_ERR_P2P_UNININ = -1012, //当前id对应的p2p未初始化
|
||||
XP2P_ERR_NEW_MEMERY = -1013, //内存申请失败
|
||||
XP2P_ERR_XP2PINFO_RULE = -1014, //获取到的xp2p info格式错误
|
||||
XP2P_ERR_XP2PINFO_DECRYPT = -1015, //获取到的xp2p info解码失败
|
||||
XP2P_ERR_PROXY_LISTEN = -1016, //本地代理监听端口失败
|
||||
XP2P_ERR_CLOUD_EMPTY = -1017, //云端返回空数据
|
||||
XP2P_ERR_JSON_PARSE = -1018, // json解析失败
|
||||
XP2P_ERR_SERVICE_NOTRUN = -1019, //当前id对应的服务(语音、直播等服务)没有在运行
|
||||
XP2P_ERR_CLIENT_NULL = -1020 //从map中取出的client为空
|
||||
} XP2PErrCode;
|
||||
|
||||
typedef struct data_report_t {
|
||||
unsigned char* report_buf;
|
||||
size_t report_size;
|
||||
uint64_t live_size;
|
||||
// uint64_t voice_size;
|
||||
const char *data_action;
|
||||
const char *status;
|
||||
const char *uniqueId;
|
||||
const char *appPeerName;
|
||||
const char *deviceP2PInfo;
|
||||
uint64_t appUpByte;
|
||||
uint64_t appDownByte;
|
||||
const char *appConnectIp;
|
||||
int errorcode;
|
||||
} data_report_t;
|
||||
|
||||
typedef struct app_config_t {
|
||||
const char *server;
|
||||
const char *ip;
|
||||
uint64_t port;
|
||||
XP2PProtocolType type;
|
||||
bool cross;
|
||||
} app_config_t;
|
||||
|
||||
typedef const char *(*msg_handle_t)(const char *id, XP2PType type, const char *msg);
|
||||
typedef void (*av_recv_handle_t)(const char *id, uint8_t *recv_buf, size_t recv_len);
|
||||
typedef char *(*device_data_recv_handle_t)(const char *id, uint8_t *recv_buf, size_t recv_len);
|
||||
typedef void (*av_log_handle_t)(const char *id, data_report_t data_buf);
|
||||
|
||||
/**
|
||||
* @brief 设置回调函数
|
||||
*
|
||||
* @param recv_handle: 音视频数据回调
|
||||
* @param msg_handle: 控制类消息回调
|
||||
* @param device_data_handle: 设备端向App发送消息的回调
|
||||
* @return 无返回值
|
||||
*/
|
||||
IPCLIBRARY_API void setUserCallbackToXp2p(av_recv_handle_t recv_handle, msg_handle_t msg_handle, device_data_recv_handle_t device_data_handle);
|
||||
|
||||
/**
|
||||
* @brief 发送信令消息给camera设备并等待回复,同步阻塞方式
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param command:
|
||||
* 可以为任意格式字符或二进制数据,长度由cmd_len提供,建议在16KB以内,否则会影响实时性
|
||||
* @param cmd_len: command长度
|
||||
* @param recv_buf:
|
||||
* 用于存放camera回复的数据,内存由接口内部申请外部释放,实际数据长度根据recv_len获取
|
||||
* @param recv_len: camera回复的数据长度
|
||||
* @param timeout_us: 命令超时时间,单位为微秒,值为0时采用默认超时(7500ms左右)
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int postCommandRequestSync(const char *id, const unsigned char *command, size_t cmd_len,
|
||||
unsigned char **recv_buf, size_t *recv_len, uint64_t timeout_us);
|
||||
|
||||
/**
|
||||
* @brief 发送信令消息给camera设备,camera回复的数据由注册的回调函数返回,异步非阻塞方式
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param command:
|
||||
* 可以为任意格式字符或二进制数据,长度由cmd_len提供,建议在16KB以内,否则会影响实时性
|
||||
* @param cmd_len: command长度
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int postCommandRequestWithAsync(const char *id, const unsigned char *command, size_t cmd_len);
|
||||
|
||||
/**
|
||||
* @brief 向camera设备请求媒体流,异步回调方式
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param params: 直播(`action=live`)或回放(`action=playback`)参数
|
||||
* @param crypto: 是否开启传输层加密,如果关闭(crypto=false),则建议用户在应用层加密,否则有安全风险
|
||||
* @return 请求句柄
|
||||
*/
|
||||
IPCLIBRARY_API void *startAvRecvService(const char *id, const char *params, bool crypto);
|
||||
|
||||
/**
|
||||
* @brief 关闭媒体流传输
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param req: 接口`startAvRecvService`的返回值,当前版本可传入null
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int stopAvRecvService(const char *id, void *req);
|
||||
|
||||
/**
|
||||
* @brief 初始化xp2p服务
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param product_id: 产品ID
|
||||
* @param device_name: 设备名称
|
||||
* @param sensor_timeout: 探测失败切换tcp的超时时间
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int startService(const char *id, const char *product_id, const char *device_name, app_config_t config);
|
||||
|
||||
/**
|
||||
* @brief 初始化xp2p服务
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param product_id: 产品ID
|
||||
* @param device_name: 设备名称
|
||||
* @param remote_host: 局域网设备ip
|
||||
* @param remote_port: 局域网设备端口
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int startLanService(const char *id, const char *product_id, const char *device_name,
|
||||
const char *remote_host, const char *remote_port);
|
||||
|
||||
/**
|
||||
* 延迟设置xp2pinfo,节省start时间
|
||||
*/
|
||||
IPCLIBRARY_API int setDeviceXp2pInfo(const char *id, const char *xp2p_info);
|
||||
/**
|
||||
* @brief 获取本地代理url
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @return 本地代理url
|
||||
*/
|
||||
IPCLIBRARY_API const char *delegateHttpFlv(const char *id);
|
||||
|
||||
/**
|
||||
* @brief 获取局域网url
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @return 局域网url
|
||||
*/
|
||||
IPCLIBRARY_API const char *getLanUrl(const char *id);
|
||||
|
||||
/**
|
||||
* @brief 获取局域网url
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @return 局域网本地代理端口号
|
||||
*/
|
||||
IPCLIBRARY_API int getLanProxyPort(const char *id);
|
||||
|
||||
/**
|
||||
* @brief 启动向camera设备发送语音或自定义数据服务,异步非阻塞方式
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param params: 请求参数采用 key1=value&key2=value2
|
||||
* 格式,key不允许以下划线_开头,且key和value中间不能包含&/+=特殊字符
|
||||
* @param crypto: 是否开启传输层加密,如果关闭(crypto=false),则建议用户在应用层加密,否则有安全风险
|
||||
* @return 请求句柄
|
||||
*/
|
||||
IPCLIBRARY_API void *runSendService(const char *id, const char *params, bool crypto);
|
||||
|
||||
/**
|
||||
* @brief 关闭语音发送传输
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param req: 接口`runSendService`的返回值,当前版本可传入null
|
||||
* @return 请求句柄
|
||||
*/
|
||||
IPCLIBRARY_API int stopSendService(const char *id, void *req);
|
||||
|
||||
/**
|
||||
* @brief 设置云api信息,若不用sdk获取xp2p信息,该接口可不用设置
|
||||
*
|
||||
* @param sec_id: 云API secrct_id
|
||||
* @param sec_key: 云API secrct_key
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int setQcloudApiCred(const char *sec_id, const char *sec_key);
|
||||
|
||||
/**
|
||||
* @brief 向camera设备发送语音或自定义数据
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param data: 要发送的数据内容
|
||||
* @param len: 要发送的数据长度
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int dataSend(const char *id, uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief 停止xp2p服务
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API void stopService(const char *id);
|
||||
|
||||
/**
|
||||
* @brief 日志开关
|
||||
*
|
||||
* @param console: 是否输出日志到控制台
|
||||
* @param file: 是否输出日志到文件
|
||||
*/
|
||||
IPCLIBRARY_API void setLogEnable(bool console, bool file);
|
||||
|
||||
/*================================废弃接口=======================================*/
|
||||
/**
|
||||
* @brief 发送信令消息给camera设备并等待回复,同步阻塞方式
|
||||
*
|
||||
* @param id: 目标camera在app端的唯一标识符
|
||||
* @param command: 字符格式命令
|
||||
* @param buf: 用于存放camera回复的数据,内存由接口内部申请外部释放,实际数据长度根据len获取
|
||||
* @param len: camera回复的数据长度
|
||||
* @param timeout_us: 命令超时时间,单位为微秒,值为0时采用默认超时(7500ms左右)
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int getCommandRequestWithSync(const char *id, const char *command, char **buf, size_t *len,
|
||||
uint64_t timeout_us);
|
||||
|
||||
/**
|
||||
* @brief 发送信令消息给camera设备,camera回复的数据由注册的回调函数返回,异步非阻塞方式
|
||||
*
|
||||
* @param id:目标camera在app端的唯一标识符
|
||||
* @param command:字符格式命令
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int getCommandRequestWithAsync(const char *id, const char *command);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取当前发送链路的连接模式:0 无效;62 直连;63 转发
|
||||
*
|
||||
* @param id:目标camera在app端的唯一标识符
|
||||
* @param command:字符格式命令
|
||||
* @return 0 为成功
|
||||
*/
|
||||
IPCLIBRARY_API int getStreamLinkMode(const char *id);
|
||||
|
||||
/**
|
||||
* @brief 调试接口,便于通过播放器请求httpflv时,保存拉取的原始流数据就行分析
|
||||
* @param id:目标camera在app端的唯一标识符
|
||||
*/
|
||||
IPCLIBRARY_API void startRecordPlayerStream(const char *id);
|
||||
|
||||
/**
|
||||
* @brief 获取发送对讲流缓存大小,可根据此接口判断 当发现p2p的水线超过一定值时,降低视频码率。送入体积较大的I帧、网络波动等都会影响水位值
|
||||
* @param id:目标camera在app端的唯一标识符
|
||||
*/
|
||||
IPCLIBRARY_API size_t getStreamBufSize(const char *id);
|
||||
|
||||
IPCLIBRARY_API void setStunServerToXp2p(const char *server, uint16_t port);
|
||||
|
||||
IPCLIBRARY_API void setContentDetail(const char *content, const char *detail, av_log_handle_t reportdata_handle);
|
||||
IPCLIBRARY_API const char* getUserID();
|
||||
|
||||
/**
|
||||
* @brief 是否走双中转开关
|
||||
*/
|
||||
IPCLIBRARY_API void setCrossStunTurn(bool enable);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/config.json
Normal file
4
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/config.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"deploymentTarget": "9",
|
||||
"frameworks": ["libc++.tbd", "libsqlite3.tbd", "libz.tbd"]
|
||||
}
|
||||
36
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/hybrid.swift
Normal file
36
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/hybrid.swift
Normal file
@ -0,0 +1,36 @@
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
public class P2PConversionHelper: NSObject {
|
||||
@objc
|
||||
public static func cStringToString(_ cString: UnsafePointer<CChar>?) -> String? {
|
||||
guard let pointer = cString else {
|
||||
return nil
|
||||
}
|
||||
return String(cString: pointer, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public class XP2PDataHelper: NSObject {
|
||||
@objc
|
||||
public static func createBytes(_ array: NSArray) -> UnsafeMutablePointer<UInt8>? {
|
||||
let count = array.count
|
||||
guard count > 0 else { return nil }
|
||||
|
||||
let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: count)
|
||||
for i in 0..<count {
|
||||
if let number = array[i] as? NSNumber {
|
||||
pointer[i] = number.uint8Value
|
||||
} else {
|
||||
pointer[i] = 0 // Default value
|
||||
}
|
||||
}
|
||||
return pointer
|
||||
}
|
||||
|
||||
@objc
|
||||
public static func deallocateBytes(_ pointer: UnsafeMutablePointer<UInt8>?) {
|
||||
pointer?.deallocate()
|
||||
}
|
||||
}
|
||||
200
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/index.uts
Normal file
200
uni_modules/xhj-tencent-xp2p/utssdk/app-ios/index.uts
Normal file
@ -0,0 +1,200 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// @ts-ignore-start
|
||||
|
||||
import { Result } from '../interface.uts'
|
||||
|
||||
const resolverMap = new Map<string, (value: Result) => void>()
|
||||
|
||||
function avRecvHandle(
|
||||
id: Optional<UnsafePointer<Int8>>,
|
||||
recv_buf: Optional<UnsafeMutablePointer<UInt8>>,
|
||||
recv_len: Int
|
||||
) {}
|
||||
|
||||
function msgHandle(
|
||||
id: Optional<UnsafePointer<Int8>>,
|
||||
type: XP2PType,
|
||||
msg: Optional<UnsafePointer<Int8>>
|
||||
): Optional<UnsafePointer<Int8>> {
|
||||
if (type.rawValue == 1004) {
|
||||
const idString = id != null ? P2PConversionHelper.cStringToString(id) : null
|
||||
if (idString != null) {
|
||||
const resolver = resolverMap.get(idString!)
|
||||
if (resolver != null) {
|
||||
const urlResult = delegateHttpFlv(idString!)
|
||||
if (urlResult != null) {
|
||||
const urlString = P2PConversionHelper.cStringToString(urlResult)
|
||||
if (urlString != null && urlString != '') {
|
||||
resolver!({
|
||||
code: 0,
|
||||
data: { url: urlString },
|
||||
message: '成功'
|
||||
})
|
||||
resolverMap.delete(idString!)
|
||||
return null
|
||||
}
|
||||
}
|
||||
resolver!({ code: -1, data: {}, message: '获取播放URL失败' })
|
||||
resolverMap.delete(idString!)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function deviceDataHandle(
|
||||
id: Optional<UnsafePointer<Int8>>,
|
||||
recv_buf: Optional<UnsafeMutablePointer<UInt8>>,
|
||||
recv_len: Int
|
||||
): Optional<UnsafeMutablePointer<Int8>> {
|
||||
// if (id != null) {
|
||||
// const idString = P2PConversionHelper.cStringToString(id)
|
||||
// // console.log(`deviceDataHandle: ${idString}, len: ${recv_len}`)
|
||||
// }
|
||||
return null
|
||||
}
|
||||
|
||||
export const startServiceFunction = async function (
|
||||
appKey: string,
|
||||
appSecret: string,
|
||||
productId: string,
|
||||
deviceName: string,
|
||||
xp2pInfo: string
|
||||
): Promise<Result> {
|
||||
const deviceId = `${productId}/${deviceName}`
|
||||
return new Promise<Result>((resolve, reject) => {
|
||||
resolverMap.set(deviceId, resolve)
|
||||
try {
|
||||
const config = app_config_t()
|
||||
config.server = UnsafePointer(strdup('')!)
|
||||
config.ip = UnsafePointer(strdup('')!)
|
||||
config.port = 20002
|
||||
config.type = XP2P_PROTOCOL_AUTO
|
||||
config.cross = true
|
||||
|
||||
// 设置回调
|
||||
setUserCallbackToXp2p(null, msgHandle, null)
|
||||
|
||||
const result = startService(deviceId, productId, deviceName, config)
|
||||
|
||||
if (NSNumber(0) === result) {
|
||||
const setP2PInfoResult = setDeviceXp2pInfo(deviceId, xp2pInfo)
|
||||
|
||||
if (NSNumber(0) === setP2PInfoResult) {
|
||||
setTimeout(() => {
|
||||
if (resolverMap.has(deviceId)) {
|
||||
resolve({ code: -1, data: {}, message: '获取播放URL超时' })
|
||||
resolverMap.delete(deviceId)
|
||||
}
|
||||
}, 20000)
|
||||
} else {
|
||||
resolve({ code: -1, data: {}, message: 'setDeviceXp2pInfo 调用失败' })
|
||||
resolverMap.delete(deviceId)
|
||||
}
|
||||
} else {
|
||||
resolve({ code: -1, data: {}, message: 'startService 调用失败' })
|
||||
resolverMap.delete(deviceId)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('startServiceFunction 报错', error)
|
||||
resolve({
|
||||
code: -1,
|
||||
data: {},
|
||||
message: error.toString()
|
||||
})
|
||||
resolverMap.delete(deviceId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const stopServiceFunction = async function (id: string): Promise<Result> {
|
||||
try {
|
||||
const result = await stopService(id)
|
||||
console.log('stopService', result)
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '成功'
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const runSendServiceFunction = async function (
|
||||
id: string,
|
||||
cmd: string,
|
||||
crypto: boolean
|
||||
): Promise<Result> {
|
||||
try {
|
||||
const result = runSendService(id, cmd, crypto)
|
||||
console.log('开始发送服务', result)
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '成功'
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const stopSendServiceFunction = async function (id: string): Promise<Result> {
|
||||
try {
|
||||
const result = stopSendService(id, null)
|
||||
console.log('停止发送服务', result)
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '成功'
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const dataSendFunction = async function (id: string, data: Array<number>): Promise<Result> {
|
||||
const buffer = XP2PDataHelper.createBytes(data as NSArray)
|
||||
|
||||
if (buffer == null) {
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
message: '创建数据缓冲区失败'
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = dataSend(id, buffer, data.length)
|
||||
// console.log('发送数据结果', result)
|
||||
|
||||
XP2PDataHelper.deallocateBytes(buffer)
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '成功'
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore-end
|
||||
@ -3,15 +3,3 @@ export type Result = {
|
||||
data: object
|
||||
message: string
|
||||
}
|
||||
|
||||
export type InitParams = {
|
||||
appKey: string
|
||||
appSecret: string
|
||||
productId: string
|
||||
deviceName: string
|
||||
xp2pInfo: string
|
||||
}
|
||||
|
||||
export type IdParams = {
|
||||
id: string
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user