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

Kotlin构建音频播放捕获录制器时遇IllegalArgumentException:未知混音规则类型

解决AudioPlaybackCapture录制音频时的"Unknown mixing rule type"错误

问题背景

开发Android应用录制设备播放音频时,创建AudioRecord对象时抛出IllegalArgumentException: Unknown mixing rule type错误,尝试多种方法未解决。

错误原因分析

从堆栈跟踪和代码来看,问题出在三个关键地方:

  • 不支持的音频编码格式:使用了AudioFormat.ENCODING_MP3,但AudioRecord仅支持PCM类原始编码(如ENCODING_PCM_16BIT),MP3是压缩编码,无法直接被AudioRecord捕获。
  • 未配置音频捕获规则:AudioPlaybackCaptureConfiguration未指定要捕获的音频来源(如音频使用场景、目标应用UID),系统无法确定要混合的音频流类型。
  • 缓冲区大小不合理:设置的缓冲区大小(20字节)远小于系统要求的最小缓冲区,会导致初始化失败。

修复后的代码

1. 修复后的服务代码

class AudioPlaybackRecorderService : Service() {
    private lateinit var notificationManager: NotificationManager
    private lateinit var notificationChannel: NotificationChannel
    private lateinit var notification: Notification

    private lateinit var mediaProjectionManager: MediaProjectionManager
    private lateinit var audioPlaybackCapConfig: AudioPlaybackCaptureConfiguration
    private lateinit var audioFormat: AudioFormat
    private var audioRecord: AudioRecord? = null

    @RequiresApi(Build.VERSION_CODES.R)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 前台服务初始化
        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationChannel = NotificationChannel(
            "foregroundNotificationChannel",
            "Foreground Service Channel",
            NotificationManager.IMPORTANCE_LOW
        )
        notificationManager.createNotificationChannel(notificationChannel)

        notification = NotificationCompat.Builder(this, notificationChannel.id)
            .setSmallIcon(R.mipmap.ic_launcher_round)
            .setContentTitle("Audio Playback Recorder")
            .setContentText("Recording device audio...")
            .build()

        startForeground(startId, notification, FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)

        // 初始化MediaProjection
        mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
        val resultCode = intent?.getIntExtra("resCode", -1) ?: -1
        val data = intent?.getParcelableExtra<Intent>("data") ?: return START_STICKY

        val mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data)
            ?: return START_STICKY

        // 配置AudioPlaybackCapture:指定要捕获的音频使用场景(比如媒体音频)
        audioPlaybackCapConfig = AudioPlaybackCaptureConfiguration.Builder(mediaProjection)
            .addMatchingUsage(AudioAttributes.USAGE_MEDIA) // 捕获媒体类音频
            .addMatchingUsage(AudioAttributes.USAGE_GAME) // 可选:捕获游戏音频
            .build()

        // 配置正确的AudioFormat:使用PCM编码
        audioFormat = AudioFormat.Builder()
            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
            .setSampleRate(44100)
            .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
            .build()

        // 检查权限并初始化AudioRecord
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
            == PackageManager.PERMISSION_GRANTED
        ) {
            // 计算最小缓冲区大小
            val minBufferSize = AudioRecord.getMinBufferSize(
                44100,
                AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT
            )
            // 缓冲区大小建议使用最小大小的2倍,避免溢出
            val bufferSize = minBufferSize * 2

            audioRecord = AudioRecord.Builder()
                .setAudioPlaybackCaptureConfig(audioPlaybackCapConfig)
                .setAudioFormat(audioFormat)
                .setBufferSizeInBytes(bufferSize)
                .build()

            // 可选:开始录制
            audioRecord?.startRecording()
        }

        return START_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        // 释放资源
        audioRecord?.stop()
        audioRecord?.release()
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }
}

2. 调用服务的代码(适配Android版本)

// 确保已经通过MediaProjectionManager获取到resultCode和data
val serviceIntent = Intent(this, AudioPlaybackRecorderService::class.java)
serviceIntent.putExtra("resCode", resultCode)
serviceIntent.putExtra("data", data)
// Android 8.0+需使用startForegroundService
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(serviceIntent)
} else {
    startService(serviceIntent)
}

额外注意事项

  • 权限要求:必须在Manifest中声明RECORD_AUDIO权限,并且动态申请;Android 10+还需要MEDIA_CONTENT_CONTROL权限,同时启动MediaProjection时必须获取用户授权。
  • 版本兼容:AudioPlaybackCapture功能从Android 10(API 29)开始支持,注意代码的版本适配。
  • 音频过滤规则:可根据需求使用addMatchingUid指定捕获特定应用的音频,或excludeUsage排除不需要的音频类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 17:27:35