From 62110f1c8b61a4b575f193a5443a3af8c0a646d9 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Fri, 25 Jul 2025 17:40:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5opus=E5=8E=8B=E7=BC=A9?= =?UTF-8?q?=EF=BC=8C=E5=9B=9E=E4=BC=A0=E8=AE=BE=E5=A4=87=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yunqiinnovation/ble_service/BleService.kt | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index a883b2452..d0f52906e 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt @@ -12,6 +12,7 @@ import java.util.concurrent.CopyOnWriteArrayList // Import Jieli Opus SDK classes import com.jieli.jl_audio_decode.opus.model.OpusOption import com.jieli.jl_audio_decode.callback.OnDecodeStreamCallback +import com.jieli.jl_audio_decode.callback.OnEncodeStreamCallback import com.jieli.jl_audio_decode.opus.OpusManager import com.jieli.jl_audio_decode.exceptions.OpusException @@ -78,6 +79,7 @@ object BleService { private var audioChar: BluetoothGattCharacteristic? = null private var writeChar1: BluetoothGattCharacteristic? = null var recordfile: RecordingFile? = null + var recordfile1: RecordingFile? = null // 扫描相关 private lateinit var scanHandler: Handler private val scanResults = ArrayList() @@ -173,6 +175,7 @@ object BleService { try { opusManager = OpusManager() startOpusStreamDecoding() + startEncodeStream() Log.d(TAG, "OpusManager初始化成功") } catch (e: OpusException) { Log.e(TAG, "OpusManager初始化失败: ${e.message}", e) @@ -180,6 +183,9 @@ object BleService { } recordfile = RecordingFile(this.context) recordfile!!.fileName = "不拆分" + + recordfile1 = RecordingFile(this.context) + recordfile1!!.fileName = "重新压缩" isInitialized = true Log.d(TAG, "BLE服务初始化成功") return true @@ -953,11 +959,13 @@ object BleService { codecStatus == BleConst.CODEC_CONTROL_A2DP_PLAY || codecStatus == BleConst.CODEC_CONTROL_ENCODE_ON ) { - +recordfile1!!.closeFile() + recordfile1!!.creatingFiles() recordfile!!.closeFile() recordfile!!.creatingFiles() } else if (codecStatus == BleConst.CODEC_CONTROL_CLOSE) { recordfile!!.closeFile() + recordfile1!!.closeFile() } // 声道模式描述 val channelDesc = when (channelMode) { @@ -1152,7 +1160,31 @@ object BleService { override fun onDecodeStream(data: ByteArray?) { if (data != null) { // Log.d(TAG, "Opus解码数据: ${data.size} bytes") - notifyAudioDataReceived(data) + //解码数据再重新编码回去 + val sampleCount = data.size / 4 // 每个样本4字节(左右声道各2字节) + val leftBuffer = ByteArray(sampleCount * 2) // 左声道缓冲区 + val rightBuffer = ByteArray(sampleCount * 2) // 右声道缓冲区 + + // 拆分交错的左右声道数据 + for (i in 0 until sampleCount) { + val stereoIndex = i * 4 + val monoIndex = i * 2 + + // 左声道(低位字节在前,高位字节在后) + leftBuffer[monoIndex] = data[stereoIndex] + leftBuffer[monoIndex + 1] = data[stereoIndex + 1] + + // 右声道 + rightBuffer[monoIndex] = data[stereoIndex + 2] + rightBuffer[monoIndex + 1] = data[stereoIndex + 3] + } + + // // 将左右声道数据分别放入队列 + // leftChannelQueue.offer(leftBuffer) + // rightChannelQueue.offer(rightBuffer) + opusManager?.writeEncodeStream(rightBuffer) + + notifyAudioDataReceived(rightBuffer) } } @@ -1194,7 +1226,60 @@ object BleService { return false } +// ====================================================================================================== + // Opus 编码相关 + // ====================================================================================================== + + private fun startEncodeStream( + hasHeader: Boolean = false, // 通常BLE传输的Opus没有文件头 + channel: Int = 1, + sampleRate: Int = 16000, // 确认设备端Opus编码采样率 + packetSize: Int = 40 // 确认设备端Opus编码帧长,必须与iOS版本frameSize保持一致 + ): Boolean { + if (opusManager == null) { + Log.e(TAG, "OpusManager未初始化,无法开始编码") + return false + } + // 如果已经在解码流,先停止 + if (opusManager?.isEncodeStream == true) { + opusManager?.stopEncodeStream() + Log.d(TAG, "已停止正在进行的Opus编码流") + } + + + var streamStartedSuccessfully = false + opusManager?.startEncodeStream(object : OnEncodeStreamCallback { + override fun onEncodeStream(data: ByteArray?) { + if (data != null) { + // Log.d(TAG, "Opus解码数据: ${data.size} bytes") + //notifyAudioDataReceived(data) + writeChar1!!.value=data + recordfile1!!.saveAudioDataToWav(data) + val isSuccess = bluetoothGatt?.writeCharacteristic(writeChar1) + if (isSuccess == true) { + + Log.d(TAG, "发送opus数据") + } + } + } + override fun onStart() { + streamStartedSuccessfully = true + Log.i(TAG, "Opus数据流编码已开始") + // 可以通过回调通知上层解码已开始 + } + + override fun onComplete(outPath: String?) { + Log.i(TAG, "Opus数据流编码完成: $outPath (通常流式编码不会调用此方法)") + } + + override fun onError(code: Int, message: String?) { + Log.e(TAG, "Opus数据流编码错误: [$code] $message") + // 可以通过回调通知上层解码错误 + } + }) + return true // 暂定为调用即成功 + } // ====================================================================================================== // 公开的命令接口 // ======================================================================================================