Android16进阶之MediaRecorder.getPreferredDevice调用流程与实战(二百六十九)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列原创干货持续更新中...... 】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

      • [🌻1. 前言](#🌻1. 前言)
      • [🌻2. 用法与应用场景](#🌻2. 用法与应用场景)
      • [🌻3. 调用流程剖析](#🌻3. 调用流程剖析)
        • [3.1 核心步骤](#3.1 核心步骤)
        • [3.2 涉及核心时序图](#3.2 涉及核心时序图)
      • [🌻4. 实战应用案例](#🌻4. 实战应用案例)
      • [🌻5. 用法总结](#🌻5. 用法总结)

🌻1. 前言

本篇目的:Android16音频深度解析之 MediaRecorder.getPreferredDevice 调用流程与实战。

在 Android 录音开发中,精准控制音频输入源(Input Source)是保证通话质量或录音效果的前提。与播放端的 MediaPlayer 类似,MediaRecorder 也实现了 AudioRouting 接口。通过 getPreferredDevice,开发者可以获取当前为录音任务手动指定的 首选输入设备。这在多麦克风阵列或外接专业声卡的场景下,是确保数据流从正确物理端口进入系统的核心手段。


🌻2. 用法与应用场景

MediaRecorder.getPreferredDevice 方法用于返回通过 setPreferredDevice(AudioDeviceInfo) 显式绑定的输入设备信息。

  • 用法说明 :该方法返回一个 AudioDeviceInfo 对象。如果开发者未曾手动指定输入设备,或者调用了 setPreferredDevice(null),则该方法返回 null,意味着录音路由将由 AudioPolicyManager 根据系统默认策略(如:外接麦克风优先)自动决定。
  • 运行结果:明确当前录音实例"期望"绑定的物理输入端口。
  • 应用场景
    1. 输入源锁定校验:在启动采访录音前,确认系统是否已正确锁定到外接的指向性麦克风。
    2. 双路录音区分:在同时开启两个录音实例时(如:通话录音与环境音录制),通过此接口确保两个实例各自绑定了不同的物理设备。
    3. UI 动态提示:实时告知用户当前生效的录音设备(如:手机底部麦克风、顶部麦克风或蓝牙耳机麦克风)。

🌻3. 调用流程剖析

3.1 核心步骤
  1. Java 层拦截 :应用层调用 MediaRecorder.getPreferredDevice()。Java 框架层维护着一个 mPreferredDevice 的变量,作为指令的初步缓存。
  2. Native 实例同步 :调用通过 JNI 进入 android_media_MediaRecorder.cppMediaRecorder 在底层实际上是管理着一个 AudioRecord 实例或类似的音频流节点。
  3. AudioPolicy 句柄查询 :指令下发至 MediaServer 进程。MediaRecorder 会询问 AudioPolicyService:当前该 Session ID 绑定的手动路由句柄(Handle)是什么。
  4. 设备信息解封AudioPolicyManager 返回对应的物理端口 ID。Native 层将其映射为设备描述符,最终转换回 Java 层的 AudioDeviceInfo
  5. 状态一致性 :需要注意的是,getPreferredDevice 获取的是"设置值",而真正物理生效的设备应通过监听 onRoutingChanged 并调用 getRoutedDevice() 获取。
3.2 涉及核心时序图

AudioPolicyManager AudioPolicyService MediaRecorder Native MediaRecorder Java 应用代码层 AudioPolicyManager AudioPolicyService MediaRecorder Native MediaRecorder Java 应用代码层 alt 缓存有效 需同步底层 调用 getPreferredDevice() 检查内部 mPreferredDevice 变量 返回 AudioDeviceInfo 调用 native_get_preferred_device 跨进程请求当前 Session 路由绑定 查询指定 Client 的 Preferred Device 返回设备端口 ID 返回设备 Handle 转换并返回设备信息 返回 AudioDeviceInfo


🌻4. 实战应用案例

本案例展示了如何为 MediaRecorder 指定特定的麦克风,并验证其绑定状态。

java 复制代码
public class RecordingRoutingManager {
    private MediaRecorder recorder;
    private AudioManager audioManager;

    public void initRecorder(Context context) {
        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        recorder = new MediaRecorder(context);
    }

    /**
     * 锁定特定输入设备并校验
     */
    public void setupInputDevice(int targetDeviceType) {
        if (recorder == null) return;

        // 1. 获取所有输入设备(麦克风)
        AudioDeviceInfo[] inputDevices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
        AudioDeviceInfo selectedDevice = null;

        for (AudioDeviceInfo device : inputDevices) {
            if (device.getType() == targetDeviceType) {
                selectedDevice = device;
                break;
            }
        }

        if (selectedDevice != null) {
            // 2. 设置首选录音设备
            boolean isSet = recorder.setPreferredDevice(selectedDevice);
            
            if (isSet) {
                // 3. 立即通过 getPreferredDevice 确认
                AudioDeviceInfo currentPreferred = recorder.getPreferredDevice();
                if (currentPreferred != null && currentPreferred.getId() == selectedDevice.getId()) {
                    System.out.println("录音设备锁定成功: " + currentPreferred.getProductName());
                }
            }
        }
    }

    public void startRecording(String path) {
        try {
            recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            recorder.setOutputFile(path);
            
            // 注册路由变更监听,观察物理切换
            recorder.addOnRoutingChangedListener(routing -> {
                AudioDeviceInfo realDevice = routing.getRoutedDevice();
                System.out.println("录音实际流向设备: " + (realDevice != null ? realDevice.getProductName() : "未知"));
            }, new Handler(Looper.getMainLooper()));

            recorder.prepare();
            recorder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stop() {
        if (recorder != null) {
            recorder.stop();
            recorder.release();
            recorder = null;
        }
    }
}

🌻5. 用法总结

调用层级 核心职责 关键特性/影响
应用框架层 维护首选设备变量与接口映射 决定了开发者对输入源的"控制意图"
系统服务层 跨进程传递路由控制指令 建立 MediaRecorderAudioPolicy 的关联
音频策略层 执行录音路径决策与冲突处理 优先满足 PreferredDevice 的手动请求
音频混音层 管理录音数据流的 Patch 链路 确保采样数据从指定物理 Port 读入
硬件抽象层 激活物理麦克风通路 影响增益(Gain)与偏置设置

最优实战方案落地步骤:

  1. 明确输入需求 :根据业务(如:降噪录音、立体声录制)从 AudioManager.getDevices 中筛选合适的 AudioDeviceInfo
  2. 时机控制setPreferredDevice 可以在 MediaRecorder 实例化后的任何时候调用,但建议在 prepare() 之前完成绑定。
  3. 结果校验 :通过 getPreferredDevice 确认逻辑层绑定成功。
  4. 闭环监控 :必须结合 getRoutedDeviceOnRoutingChangedListener,以防手动指定的设备(如蓝牙麦克风)在录音中途意外断开。
相关推荐
Android系统攻城狮3 天前
Linux Pulseaudio深度解析之pa_stream_set_overflow_callback用流程与实战(三十七)
linux·运维·服务器·音频进阶·pulseaudio进阶
Android系统攻城狮24 天前
Android tinyalsa深度解析之pcm_plugin_write调用流程与实战(一百七十九)
android·pcm·tinyalsa·android16·音频进阶·android音频进阶
Android系统攻城狮1 个月前
Android16进阶之BassBoost.setStrength调用流程与实战(三百零四)
android16·音频进阶·android音频进阶·bassbosst
longji2 个月前
android 01 AOSP android16 aaos 编译及webview升级
android·aaos·aosp·android16
Android系统攻城狮2 个月前
Android16进阶之MediaRecorder.setAudioEncodingBitRate调用流程与实战(二百六十四)
android16·音频进阶·mediarecoder
Android系统攻城狮2 个月前
Android16进阶之MediaRecorder.setVideoSource调用流程与实战(二百五十六)
android16·音频进阶·mediarecord
Android系统攻城狮2 个月前
Android16进阶之MediaRecorder.setVideoEncoder调用流程与实战(二百五十八)
mediarecorder·android16·android音频进阶
Android系统攻城狮2 个月前
Android tinyalsa深度解析之pcm_params_set_max调用流程与实战(一百七十)
android·pcm·tinyalsa·android音频进阶
Android系统攻城狮2 个月前
Android tinyalsa深度解析之pcm_params_get_period_size_max调用流程与实战(一百七十二)
android·pcm·tinyalsa·音频进阶