You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在FFmpeg命令中以ByteArray作为输入输出,避免文件存储延迟

如何用FFmpegKit直接处理ByteArray音频数据(无需文件存储)

核心思路

FFmpeg支持通过-指代标准输入(stdin)和标准输出(stdout),FFmpegKit提供了对应重载方法,可直接传入输入流、输出流替代文件路径,彻底避免磁盘IO带来的额外延迟。

步骤1:更新依赖

替换旧的mobile-ffmpeg依赖为最新版FFmpegKit:

implementation 'com.arthenica:ffmpeg-kit-android-full:5.1.0'

步骤2:修正音频处理函数

以下是完整的处理函数,直接基于ShortArray输入输出,无文件操作:

import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.FFmpegSession
import com.arthenica.ffmpegkit.ReturnCode
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import android.util.Log

fun processAudioAsChipmunk(audioData: ShortArray, sampleRate: Int, channelCount: Int): ShortArray {
    // 将ShortArray转为PCM 16位小端格式的ByteArray
    val inputBuffer = ByteBuffer.allocate(audioData.size * 2)
    inputBuffer.order(ByteOrder.nativeOrder())
    inputBuffer.asShortBuffer().put(audioData)
    val inputByteArray = inputBuffer.array()
    val inputStream = ByteArrayInputStream(inputByteArray)
    
    val outputStream = ByteArrayOutputStream()

    // 构造FFmpeg命令:完全匹配输入音频参数,应用花栗鼠音效,输出PCM数据
    val command = arrayOf(
        "-f", "s16le",          // 输入格式:PCM 16位小端
        "-ar", sampleRate.toString(), // 输入采样率,和AudioRecord一致
        "-ac", channelCount.toString(), // 输入通道数,和AudioRecord一致
        "-i", "-",              // 从标准输入读取数据
        "-af", "asetrate=${sampleRate * 2},aresample=${sampleRate},atempo=0.5", // 花栗鼠音效滤镜组合
        "-f", "s16le",          // 输出格式:PCM 16位小端
        "-"                     // 输出到标准输出
    )

    // 执行异步命令,传入输入输出流和回调
    val session: FFmpegSession = FFmpegKit.executeAsync(
        command,
        inputStream,
        outputStream,
        { session ->
            if (!ReturnCode.isSuccess(session.returnCode)) {
                Log.e("FFmpeg", "处理失败:${session.failStackTrace}")
            }
        }
    )

    // 同步等待处理完成,避免返回空数据
    session.wait()

    // 检查执行结果
    if (!ReturnCode.isSuccess(session.returnCode)) {
        return ShortArray(0)
    }

    // 将输出ByteArray转回ShortArray
    val outputByteArray = outputStream.toByteArray()
    val outputBuffer = ByteBuffer.wrap(outputByteArray)
        .order(ByteOrder.nativeOrder())
        .asShortBuffer()
    val outputShortArray = ShortArray(outputBuffer.remaining())
    outputBuffer.get(outputShortArray)

    // 关闭流释放资源
    inputStream.close()
    outputStream.close()

    return outputShortArray
}

步骤3:整合到音频读写流程

在原有AudioRecord/AudioTrack逻辑中调用处理函数:

// 读取麦克风数据
val read = record!!.read(audioData, 0, minBuffer)
if (read > 0) {
    // 仅处理实际读取到的音频片段
    val processedAudioData = processAudioAsChipmunk(
        audioData.copyOfRange(0, read),
        record.sampleRate,
        record.channelCount
    )
    // 播放处理后的音频
    player!!.write(processedAudioData, 0, processedAudioData.size)
}

关键注意事项

  1. 参数匹配:命令中的-f、-ar、-ac必须和AudioRecord的配置完全一致,否则会出现音频失真、速度异常等问题。
  2. 滤镜替换:若需使用rubberband滤镜,直接替换-af参数即可,例如"rubberband=pitch=1.5"。
  3. 流处理:FFmpegKit会自动完成输入流到FFmpeg的喂料,以及FFmpeg输出到输出流的写入,无需手动操作数据流。
  4. 同步等待:session.wait()确保处理完成后再返回结果,避免异步执行导致的空数据问题。

内容的提问来源于stack exchange,提问作者Rohit gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 04:44:56