1
This commit is contained in:
parent
7d822955d4
commit
601bfb1e16
@ -550,7 +550,10 @@
|
||||
`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`,
|
||||
audioData
|
||||
)
|
||||
console.log(`数据传输结果:${result.data.result}`, audioData)
|
||||
console.log(
|
||||
`数据传输结果:${result?.data?.result ? result?.data?.result : result?.data?.dynamicJSONFields?.result}`,
|
||||
audioData
|
||||
)
|
||||
}
|
||||
|
||||
const handlePlaySuccess = () => {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
{
|
||||
"minSdkVersion": "21"
|
||||
}
|
||||
"minSdkVersion": "21",
|
||||
"dependencies": ["com.tencent.iot.thirdparty.android:media-server:1.0.7"]
|
||||
}
|
||||
|
||||
@ -5,14 +5,57 @@ import 'android.media.AudioRecord'
|
||||
import 'android.media.MediaRecorder'
|
||||
import 'android.media.AudioFormat'
|
||||
import 'android.media.MediaSyncEvent'
|
||||
import 'android.media.MediaCodec'
|
||||
import 'android.media.MediaCodecInfo'
|
||||
import 'android.media.MediaFormat'
|
||||
import 'java.lang.Thread'
|
||||
import { UTSAndroid } from 'io.dcloud.uts'
|
||||
import { Result } from '../interface.uts'
|
||||
// @ts-ignore-end
|
||||
import { FLVPacker, FLVListener } from 'com.tencent.iot.thirdparty.flv'
|
||||
|
||||
let recorder: AudioRecord | null = null
|
||||
let recorderState: boolean = false
|
||||
let aacEncoder: MediaCodec | null = null
|
||||
let flvPacker: FLVPacker | null = null
|
||||
let recordThread: Thread | null = null
|
||||
let isRecording: boolean = false
|
||||
let bufferSizeInBytes: Int = 0
|
||||
|
||||
class MyFLVListener extends FLVListener {
|
||||
private callback: (data: Array<number>) => void
|
||||
|
||||
constructor(callback: (data: Array<number>) => void) {
|
||||
super()
|
||||
this.callback = callback
|
||||
}
|
||||
|
||||
override onFLV(data: ByteArray) {
|
||||
if (isRecording) {
|
||||
const numberArray: Array<number> = []
|
||||
for (let i: Int = 0; i < data.size; i++) {
|
||||
numberArray.push(data[i].toInt())
|
||||
}
|
||||
UTSAndroid.getUniActivity()?.runOnUiThread(() => {
|
||||
this.callback(numberArray)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addADTStoPacket(packet: ByteArray, packetLen: Int) {
|
||||
const profile = 2 // AAC LC
|
||||
const freqIdx = 8 // 16000Hz
|
||||
const chanCfg = 1 // MONO
|
||||
|
||||
packet[0] = (0xff).toByte()
|
||||
packet[1] = (0xf9).toByte()
|
||||
packet[2] = (((profile - 1) << 6) | (freqIdx << 2) | (chanCfg >> 2)).toByte()
|
||||
packet[3] = (((chanCfg & 3) << 6) | (packetLen >> 11)).toByte()
|
||||
packet[4] = ((packetLen & 0x7ff) >> 3).toByte()
|
||||
packet[5] = (((packetLen & 7) << 5) | 0x1f).toByte()
|
||||
packet[6] = (0xfc).toByte()
|
||||
}
|
||||
|
||||
export const requestPermission = async function (): Promise<Result> {
|
||||
return new Promise(resolve => {
|
||||
try {
|
||||
@ -57,7 +100,7 @@ export const requestPermission = async function (): Promise<Result> {
|
||||
export const initAudio = async function (): Promise<Result> {
|
||||
try {
|
||||
const audioSource = MediaRecorder.AudioSource.MIC
|
||||
const sampleRateInHz = 44100
|
||||
const sampleRateInHz = 16000
|
||||
const channelConfig = AudioFormat.CHANNEL_IN_MONO
|
||||
const audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
||||
|
||||
@ -67,8 +110,6 @@ export const initAudio = async function (): Promise<Result> {
|
||||
audioFormat
|
||||
)
|
||||
|
||||
console.log('bufferSizeInBytes', bufferSizeInBytes)
|
||||
|
||||
recorder = new AudioRecord(
|
||||
audioSource,
|
||||
sampleRateInHz.toInt(),
|
||||
@ -79,7 +120,6 @@ export const initAudio = async function (): Promise<Result> {
|
||||
|
||||
const currentRecorder = recorder
|
||||
if (currentRecorder !== null) {
|
||||
console.log('初始化录音结果:', currentRecorder.getState())
|
||||
if (currentRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
|
||||
return {
|
||||
code: 0,
|
||||
@ -94,7 +134,6 @@ export const initAudio = async function (): Promise<Result> {
|
||||
message: '初始化录音失败'
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('初始化录音失败', error)
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
@ -103,45 +142,115 @@ export const initAudio = async function (): Promise<Result> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function onStartRecord(callback: (data: Array) => void): Promise<Result> {
|
||||
export async function onStartRecord(callback: (data: Array<number>) => 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
|
||||
}
|
||||
await stopRecord()
|
||||
|
||||
const currentRecorder = recorder
|
||||
if (currentRecorder !== null) {
|
||||
currentRecorder.startRecording()
|
||||
console.log('开始录音')
|
||||
recorderState = true
|
||||
|
||||
callbackFunction()
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '成功'
|
||||
}
|
||||
if (currentRecorder == null) {
|
||||
return { code: -1, data: {}, message: '录音尚未初始化' }
|
||||
}
|
||||
|
||||
const listener = new MyFLVListener(callback)
|
||||
flvPacker = new FLVPacker(listener, true, false)
|
||||
|
||||
const sampleRateInHz = 16000
|
||||
const channelCount = 1
|
||||
const bitRate = 96000
|
||||
aacEncoder = MediaCodec.createEncoderByType('audio/mp4a-latm')
|
||||
const mediaFormat = MediaFormat.createAudioFormat(
|
||||
'audio/mp4a-latm',
|
||||
sampleRateInHz.toInt(),
|
||||
channelCount.toInt()
|
||||
)
|
||||
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate.toInt())
|
||||
mediaFormat.setInteger(
|
||||
MediaFormat.KEY_AAC_PROFILE,
|
||||
MediaCodecInfo.CodecProfileLevel.AACObjectLC
|
||||
)
|
||||
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1024 * 1024)
|
||||
aacEncoder!!.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
|
||||
|
||||
recordThread = new Thread(() => {
|
||||
try {
|
||||
if (aacEncoder == null || currentRecorder == null || flvPacker == null) {
|
||||
return
|
||||
}
|
||||
|
||||
currentRecorder.startRecording()
|
||||
aacEncoder!!.start()
|
||||
isRecording = true
|
||||
|
||||
const audioInfo = new MediaCodec.BufferInfo()
|
||||
while (isRecording) {
|
||||
// Feed encoder
|
||||
const inputBufferId = aacEncoder!!.dequeueInputBuffer(10000)
|
||||
if (inputBufferId >= 0) {
|
||||
const inputBuffer = aacEncoder!!.getInputBuffer(inputBufferId)!!
|
||||
const readSize = currentRecorder.read(inputBuffer, bufferSizeInBytes)
|
||||
if (readSize > 0 && isRecording) {
|
||||
aacEncoder!!.queueInputBuffer(
|
||||
inputBufferId,
|
||||
0,
|
||||
readSize,
|
||||
(Date.now() * 1000).toLong(),
|
||||
0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Drain encoder
|
||||
var outputBufferId = aacEncoder!!.dequeueOutputBuffer(audioInfo, 10000)
|
||||
while (outputBufferId >= 0 && isRecording) {
|
||||
const outputBuffer = aacEncoder!!.getOutputBuffer(outputBufferId)!!
|
||||
if (audioInfo.size > 0 && flvPacker != null) {
|
||||
const outDataSize = audioInfo.size
|
||||
const packetLen = outDataSize + 7
|
||||
const aacPacket = ByteArray(packetLen)
|
||||
|
||||
addADTStoPacket(aacPacket, packetLen)
|
||||
|
||||
outputBuffer.position(audioInfo.offset)
|
||||
outputBuffer.limit(audioInfo.offset + outDataSize)
|
||||
outputBuffer.get(aacPacket, 7, outDataSize)
|
||||
outputBuffer.position(audioInfo.offset)
|
||||
|
||||
if (flvPacker != null && isRecording) {
|
||||
flvPacker!!.encodeFlv(aacPacket, FLVPacker.TYPE_AUDIO, Date.now().toLong())
|
||||
}
|
||||
}
|
||||
aacEncoder!!.releaseOutputBuffer(outputBufferId, false)
|
||||
outputBufferId = aacEncoder!!.dequeueOutputBuffer(audioInfo, 0)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Log error
|
||||
} finally {
|
||||
try {
|
||||
currentRecorder?.stop()
|
||||
} catch (e) {}
|
||||
try {
|
||||
aacEncoder?.stop()
|
||||
aacEncoder?.release()
|
||||
} catch (e) {}
|
||||
try {
|
||||
flvPacker?.release()
|
||||
} catch (e) {}
|
||||
|
||||
aacEncoder = null
|
||||
flvPacker = null
|
||||
isRecording = false
|
||||
}
|
||||
})
|
||||
|
||||
recordThread!!.start()
|
||||
|
||||
return {
|
||||
code: -1,
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '开始录音失败'
|
||||
message: '成功'
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('开始录音失败', error)
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
@ -152,11 +261,18 @@ export async function onStartRecord(callback: (data: Array) => void): Promise<Re
|
||||
|
||||
export const stopRecord = async function (): Promise<Result> {
|
||||
try {
|
||||
const currentRecorder = recorder
|
||||
if (currentRecorder !== null) {
|
||||
currentRecorder.stop()
|
||||
recorderState = false
|
||||
console.log('停止录音')
|
||||
if (isRecording) {
|
||||
isRecording = false
|
||||
|
||||
if (recordThread != null) {
|
||||
try {
|
||||
recordThread!!.join(1000)
|
||||
} catch (e) {
|
||||
console.log('recordThread.join error: ' + e.toString())
|
||||
}
|
||||
recordThread = null
|
||||
}
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
@ -164,13 +280,13 @@ export const stopRecord = async function (): Promise<Result> {
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: -1,
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '停止录音失败'
|
||||
message: '录音未开始'
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('停止录音失败', error)
|
||||
recorderState = false
|
||||
isRecording = false
|
||||
recordThread = null
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
@ -181,23 +297,24 @@ export const stopRecord = async function (): Promise<Result> {
|
||||
|
||||
export const releaseRecord = async function (): Promise<Result> {
|
||||
try {
|
||||
await stopRecord()
|
||||
|
||||
const currentRecorder = recorder
|
||||
if (currentRecorder !== null) {
|
||||
currentRecorder.release()
|
||||
console.log('释放录音')
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '成功'
|
||||
}
|
||||
try {
|
||||
if (currentRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
|
||||
currentRecorder.release()
|
||||
}
|
||||
} catch (e) {}
|
||||
recorder = null
|
||||
}
|
||||
|
||||
return {
|
||||
code: -1,
|
||||
code: 0,
|
||||
data: {},
|
||||
message: '释放录音失败'
|
||||
message: '成功'
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('释放录音失败', error)
|
||||
return {
|
||||
code: -1,
|
||||
data: {},
|
||||
|
||||
@ -15,28 +15,28 @@ class XP2PCallbackImpl extends XP2PCallback {
|
||||
}
|
||||
|
||||
override fail(msg: string | null, errorCode: Int): void {
|
||||
console.log('XP2P callback fail:', msg, errorCode)
|
||||
// console.log('XP2P callback fail:', msg, errorCode)
|
||||
}
|
||||
|
||||
override commandRequest(id: string | null, msg: string | null): void {
|
||||
console.log('XP2P callback commandRequest:', id, msg)
|
||||
// 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)
|
||||
// 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)
|
||||
// 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)
|
||||
// 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)
|
||||
// console.log('XP2P callback onDeviceMsgArrived:', id, len)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
@ -103,7 +103,8 @@ export const startServiceFunction = async function (
|
||||
|
||||
export const stopServiceFunction = async function (id: string): Promise<Result> {
|
||||
try {
|
||||
await XP2P.stopService(id)
|
||||
const result = await XP2P.stopService(id)
|
||||
console.log('停止服务', result)
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
@ -125,7 +126,7 @@ export const runSendServiceFunction = async function (
|
||||
): Promise<Result> {
|
||||
try {
|
||||
const result = await XP2P.runSendService(id, cmd, crypto)
|
||||
console.log('开始发送服务', result)
|
||||
console.log('发送服务', result)
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
@ -168,11 +169,11 @@ export const dataSendFunction = async function (id: string, data: Array<number>)
|
||||
|
||||
const result = await XP2P.dataSend(id, byteArray, data.length.toInt())
|
||||
|
||||
console.log('发送数据结果', result, byteArray)
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
data: {},
|
||||
data: {
|
||||
result: result
|
||||
},
|
||||
message: '成功'
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user