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
相关产品推荐
相关产品推荐

