263 lines
7.8 KiB
Plaintext
263 lines
7.8 KiB
Plaintext
/* eslint-disable */
|
|
// @ts-nocheck
|
|
// @ts-ignore-start
|
|
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'
|
|
import { FLVPacker, FLVListener } from 'com.tencent.iot.thirdparty.flv'
|
|
|
|
let recorder: AudioRecord | null = null
|
|
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
|
|
const freqIdx = 8
|
|
const chanCfg = 1
|
|
|
|
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 {
|
|
const permissionNeed = ['android.permission.RECORD_AUDIO']
|
|
UTSAndroid.requestSystemPermission(
|
|
UTSAndroid.getUniActivity()!,
|
|
permissionNeed,
|
|
(allRight: boolean, _: string[]) => {
|
|
if (allRight) {
|
|
resolve({ code: 0, data: {}, message: '成功' })
|
|
} else {
|
|
resolve({ code: -1, data: {}, message: '失败' })
|
|
}
|
|
},
|
|
(_: 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 = 16000
|
|
const channelConfig = AudioFormat.CHANNEL_IN_MONO
|
|
const audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
|
|
|
bufferSizeInBytes = AudioRecord.getMinBufferSize(
|
|
sampleRateInHz.toInt(),
|
|
channelConfig,
|
|
audioFormat
|
|
)
|
|
|
|
recorder = new AudioRecord(
|
|
audioSource,
|
|
sampleRateInHz.toInt(),
|
|
channelConfig,
|
|
audioFormat,
|
|
bufferSizeInBytes
|
|
)
|
|
|
|
if (recorder?.getState() == AudioRecord.STATE_INITIALIZED) {
|
|
return { code: 0, data: {}, message: '成功' }
|
|
} else {
|
|
return { code: -1, data: {}, message: '初始化录音失败' }
|
|
}
|
|
} catch (error) {
|
|
return { code: -1, data: {}, message: error.toString() }
|
|
}
|
|
}
|
|
|
|
export function onStartRecord(callback: (data: Array<number>) => void) {
|
|
stopRecord()
|
|
.then(() => {
|
|
const currentRecorder = recorder
|
|
if (currentRecorder == null) {
|
|
return
|
|
}
|
|
|
|
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) {
|
|
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
|
|
)
|
|
}
|
|
}
|
|
|
|
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 aacPacketWithAdts = ByteArray(outDataSize + 7)
|
|
|
|
addADTStoPacket(aacPacketWithAdts, outDataSize + 7)
|
|
|
|
outputBuffer.position(audioInfo.offset)
|
|
outputBuffer.limit(audioInfo.offset + outDataSize)
|
|
outputBuffer.get(aacPacketWithAdts, 7, outDataSize)
|
|
|
|
if (flvPacker != null && isRecording) {
|
|
flvPacker!!.encodeFlv(
|
|
aacPacketWithAdts,
|
|
FLVPacker.TYPE_AUDIO,
|
|
Date.now().toLong()
|
|
)
|
|
}
|
|
}
|
|
aacEncoder!!.releaseOutputBuffer(outputBufferId, false)
|
|
outputBufferId = aacEncoder!!.dequeueOutputBuffer(audioInfo, 0)
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log('Record thread error: ' + error.toString())
|
|
} 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()
|
|
})
|
|
.catch(error => {
|
|
console.log('Error in stopRecord(): ' + error.toString())
|
|
})
|
|
}
|
|
|
|
export const stopRecord = async function (): Promise<Result> {
|
|
try {
|
|
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: {}, message: '成功' }
|
|
}
|
|
return { code: 0, data: {}, message: '录音未开始' }
|
|
} catch (error) {
|
|
isRecording = false
|
|
recordThread = null
|
|
return { code: -1, data: {}, message: error.toString() }
|
|
}
|
|
}
|
|
|
|
export const releaseRecord = async function (): Promise<Result> {
|
|
try {
|
|
await stopRecord()
|
|
|
|
const currentRecorder = recorder
|
|
if (currentRecorder != null) {
|
|
try {
|
|
if (currentRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
|
|
currentRecorder.release()
|
|
}
|
|
} catch (e) {}
|
|
recorder = null
|
|
}
|
|
|
|
return { code: 0, data: {}, message: '成功' }
|
|
} catch (error) {
|
|
return { code: -1, data: {}, message: error.toString() }
|
|
}
|
|
}
|
|
// @ts-ignore-end
|