大疆MSDK V5为喊话器封装了MegaphoneManager管理类,简化了各种型号的喊话器对接工作。MegaphoneManager对音频统一采用opus编码,V5 Sample中也提供了录音音频opus编码的方法,但是缺失mp3转opus编码的相关方法。本文提供一种mp3转opus的方案:mp3->pcm->opus。
FFmpeg命令
implementation 'com.arthenica:mobile-ffmpeg-full:4.4'
mp3转pcm
val file1 = "${PathUtils.getExternalStoragePath()}/record/AudioFile/test.mp3"
val file2 = "${PathUtils.getExternalStoragePath()}/record/AudioFile/ffmpegConvert.pcm"
//利用ffmpeg命令将mp3转为pcm,并将采样率修改为16000
val cmd = "-i $file1 -f s16le -ar 16000 -ac 1 -acodec pcm_s16le -y $file2"
FFmpeg.executeAsync(cmd,object :ExecuteCallback{
override fun apply(executionId: Long, returnCode: Int){
}
})
pcm转opus
- 读取pcm文件
private fun convertMP3() {
val file1 = "${PathUtils.getExternalStoragePath()}/record/AudioFile/test_le.pcm"
launch(Dispatchers.IO) {
readPcmFrames(file1)
.flowOn(Dispatchers.IO)
.buffer()
.collect { bytes->
convertPcmData(bytes)
}
}
}
//opus编码对每一帧的数据大小做了限制,固定为1280
fun readPcmFrames(filePath: String, frameSize: Int = 1280): Flow<ByteArray> = flow {
val file = File(filePath)
val inputStream = file.inputStream()
val buffer = ByteArray(frameSize)
var bytesRead:Int
while ((inputStream.read(buffer).also { bytesRead = it }) != -1) {
emit(buffer.copyOf(bytesRead)) // 发射当前帧的数据
delay(35) //opus编码接受能力有限,利用延迟防止数据丢失
}
inputStream.close()
}
- opus编码
fun initEncoder(dstPath:String) {
//初始化opus文件存储位置
mAudioBos = BufferedOutputStream(FileOutputStream(dstPath))
//初始化opus编码器
opusEncoder = OpusEncoder()
val audioConfig = AudioConfig()
audioConfig.audioChannelCount = 1 //通道数
audioConfig.audioSampleRate = 16000 //采样率
audioConfig.audioFormat = AudioFormat.ENCODING_PCM_16BIT
audioConfig.bufferSize = 2560
opusEncoder!!.config(audioConfig)
opusEncoder!!.setEncodedDataCallback(object : EncodedDataCallback {
override fun onAudioEncodedCallback(
data: ByteArray?,size: Int) {
//opus编码后的数据大小为160
mAudioBos?.write(data)
}
})
opusEncoder!!.start()
}
//转换pcm数据
fun convertPcmData(bytes: ByteArray) {
opusEncoder?.putData(bytes)
}