diff --git a/pages/p2p/p2pPlayer.vue b/pages/p2p/p2pPlayer.vue index 790d932..004d9bd 100644 --- a/pages/p2p/p2pPlayer.vue +++ b/pages/p2p/p2pPlayer.vue @@ -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 = () => { diff --git a/uni_modules/xhj-record/utssdk/app-android/config.json b/uni_modules/xhj-record/utssdk/app-android/config.json index bf95925..3658343 100644 --- a/uni_modules/xhj-record/utssdk/app-android/config.json +++ b/uni_modules/xhj-record/utssdk/app-android/config.json @@ -1,3 +1,4 @@ { - "minSdkVersion": "21" -} \ No newline at end of file + "minSdkVersion": "21", + "dependencies": ["com.tencent.iot.thirdparty.android:media-server:1.0.7"] +} diff --git a/uni_modules/xhj-record/utssdk/app-android/index.uts b/uni_modules/xhj-record/utssdk/app-android/index.uts index 6735ea4..c6deb72 100644 --- a/uni_modules/xhj-record/utssdk/app-android/index.uts +++ b/uni_modules/xhj-record/utssdk/app-android/index.uts @@ -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) => void + + constructor(callback: (data: Array) => void) { + super() + this.callback = callback + } + + override onFLV(data: ByteArray) { + if (isRecording) { + const numberArray: Array = [] + 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 { return new Promise(resolve => { try { @@ -57,7 +100,7 @@ export const requestPermission = async function (): Promise { export const initAudio = async function (): Promise { 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 { audioFormat ) - console.log('bufferSizeInBytes', bufferSizeInBytes) - recorder = new AudioRecord( audioSource, sampleRateInHz.toInt(), @@ -79,7 +120,6 @@ export const initAudio = async function (): Promise { 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 { message: '初始化录音失败' } } catch (error) { - console.log('初始化录音失败', error) return { code: -1, data: {}, @@ -103,45 +142,115 @@ export const initAudio = async function (): Promise { } } -export async function onStartRecord(callback: (data: Array) => void): Promise { +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 - } + 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 { 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 { } } 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 { export const releaseRecord = async function (): Promise { 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: {}, 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 e4c4919..165632b 100644 --- a/uni_modules/xhj-tencent-xp2p/utssdk/app-android/index.uts +++ b/uni_modules/xhj-tencent-xp2p/utssdk/app-android/index.uts @@ -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 { 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 { 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) const result = await XP2P.dataSend(id, byteArray, data.length.toInt()) - console.log('发送数据结果', result, byteArray) - return { code: 0, - data: {}, + data: { + result: result + }, message: '成功' } } catch (error) {