【Android FrameWork】第二十九天:MediaPlayer和MediaRecorder

Android MediaPlayer/MediaRecorder

在Android多媒体开发中,MediaPlayer和MediaRecorder是最核心的两个API:前者负责音视频的播放,后者负责音视频的录制。

这两个组件看似上层接口简单,但其底层串联了Android多媒体框架的核心模块(编解码、数据采集/输出、封装/解封装、硬件交互等)。

本文将从架构分层、状态机模型、底层组件、核心流程等维度,全面解析MediaPlayer和MediaRecorder的实现原理。

Android多媒体框架

要理解MediaPlayer/MediaRecorder的底层逻辑,首先需掌握Android多媒体框架的分层设计。

整个框架从上层到下层分为5层,各层职责清晰且通过标准化接口交互:

层级 核心组件/作用
应用层 MediaPlayer、MediaRecorder、ExoPlayer(第三方)等面向开发者的API
框架层 基于Java的MediaPlayer/Recorder核心逻辑、JNI桥接、MediaCodec封装等
原生层(Native) C/C++实现的核心逻辑:Stagefright框架、nuPlayer、MediaExtractor/Muxer、MediaCodec原生接口
硬件抽象层(HAL) 音频HAL(Audio HAL)、视频编解码HAL(Codec HAL)、相机HAL(Camera HAL)等,屏蔽硬件差异
内核层 音频驱动、视频驱动、摄像头驱动、存储驱动等,与硬件直接交互

核心依赖:

  • Stagefright:Android原生的多媒体处理框架,是MediaPlayer/Recorder的核心实现载体;
  • OpenMAX IL:跨平台的多媒体编解码标准接口,连接原生层与HAL层,适配不同厂商的硬件编解码器;
  • AudioFlinger:Android音频系统的核心,负责音频数据的混音、路由和输出/采集。

MediaPlayer

1 MediaPlayer的核心特性与状态机

MediaPlayer的上层设计严格遵循状态机模型(开发中最易出错的点),所有操作必须符合状态转换规则,否则会抛出IllegalStateException。其核心状态及转换关系如下:

复制代码
Idle(空闲)→ Initialized(已初始化)→ Prepared(已准备)→ Started(播放中)
                  ↑                     ↓                   ↓
                Error(错误)← Stopped(已停止)← Paused(暂停)←
                  ↑                                           ↓
                Released(已释放)← End(播放完成)←─────────────

关键规则:

  • 刚创建的MediaPlayer处于Idle状态,调用setDataSource()后进入Initialized;
  • 必须通过prepare()(同步)或prepareAsync()(异步)进入Prepared状态,才能调用start()
  • 播放完成后进入End状态,需调用seekTo()start()重新激活;
  • 任何状态下发生错误都会进入Error状态,需调用reset()恢复Idle。

2 从Java层到Native层的调用链路

MediaPlayer的Java层只是"壳",核心逻辑在Native层实现,调用链路如下:

  1. Java层android.media.MediaPlayer,暴露setDataSource()prepare()start()等API;
  2. JNI层android_media_MediaPlayer.cpp,将Java方法映射为Native方法(如native_setup()native_setDataSource());
  3. Native层核心MediaPlayer.cpp(Native层MediaPlayer封装),通过IMediaPlayer接口与多媒体服务(MediaServer)通信;
  4. 多媒体服务层 :MediaServer进程中的StagefrightPlayer/nuPlayer(播放引擎),负责实际的解封装、解码、渲染。

注:Android 4.1后主推nuPlayer(支持流媒体、自适应播放),替代旧的StagefrightPlayer。

3 底层核心组件解析

MediaPlayer的播放能力依赖以下核心组件的协同工作:

(1)MediaExtractor:解封装器

作用:将封装格式(MP4、MP3、FLV等)的音视频文件解封装,分离出音频流、视频流(裸流)。

  • 支持的封装格式由底层解析器(如MP4Extractor、MP3Extractor)决定;
  • 核心接口:setDataSource()(设置数据源)、getTrackCount()(获取流数量)、selectTrack()(选择要解析的流)、readSampleData()(读取裸流数据)。
(2)MediaCodec:编解码器

作用:将MediaExtractor分离出的裸流(如H.264、AAC)解码为原始数据(YUV视频帧、PCM音频帧)。

  • 两种解码模式:
    • 软解码:基于CPU的软件解码(如FFmpeg),兼容性好但性能低;
    • 硬解码:基于硬件编解码器(如高通QCOM、联发科MTK的Codec HAL),性能高、功耗低,通过OpenMAX IL调用;
  • 核心流程:configure()(配置解码参数)→ start()queueInputBuffer()(送入待解码数据)→ dequeueOutputBuffer()(取出解码后数据)。
(3)AudioTrack/AudioFlinger:音频输出
  • AudioTrack:接收MediaCodec解码后的PCM音频数据,将数据写入AudioFlinger;
  • AudioFlinger:Android音频系统的核心进程,负责音频混音(多应用音频叠加)、音量控制、路由(扬声器/耳机/蓝牙),最终将数据通过Audio HAL输出到硬件。
(4)Surface:视频渲染

解码后的YUV视频帧通过Surface传递给显示系统:

  • Surface对应底层的ANativeWindow,连接到SurfaceFlinger(Android显示系统核心);
  • 视频帧经SurfaceFlinger合成后,通过Display HAL输出到屏幕;
  • 支持硬件渲染(Overlay)和软件渲染(Canvas),硬件渲染性能更优。
(5)音视频同步模块

播放的核心难点是音视频同步,MediaPlayer通过PTS/DTS时钟同步实现:

  • PTS(Presentation Time Stamp):显示时间戳,指示帧应播放的时间;
  • DTS(Decoding Time Stamp):解码时间戳,指示帧应解码的时间;
  • 核心策略:以音频时钟为基准(音频采样率固定,时钟更稳定),调整视频帧的渲染时间,确保音视频时序一致。

4 MediaPlayer播放流程的底层执行步骤

以本地MP4文件播放为例,底层完整流程如下:

  1. 初始化阶段

    • Java层调用new MediaPlayer(),触发JNI层native_setup(),创建Native层MediaPlayer实例;
    • 绑定MediaServer的播放服务,初始化音视频输出通道(AudioTrack/Surface)。
  2. 数据源配置

    • 调用setDataSource(),Native层通过MediaExtractor打开文件,解析封装格式,分离音视频流。
  3. 准备阶段

    • 调用prepare(),初始化MediaCodec(根据流的编码格式选择软/硬解码器);
    • 配置AudioTrack(匹配音频采样率、声道数、位深),建立与AudioFlinger的连接;
    • 若为视频,绑定Surface,初始化渲染缓冲区。
  4. 播放阶段

    • 调用start(),MediaExtractor循环读取音视频裸流数据,送入MediaCodec解码;
    • 解码后的PCM音频数据写入AudioTrack,由AudioFlinger混音后输出到扬声器;
    • 解码后的YUV视频帧送入Surface,由SurfaceFlinger合成后显示;
    • 同步模块实时调整视频渲染时间,保证音视频同步。
  5. 停止/释放阶段

    • 调用stop(),停止数据读取和解码,清空缓冲区;
    • 调用release(),释放MediaExtractor、MediaCodec、AudioTrack、Surface等资源,断开与MediaServer的连接。

MediaRecorder

1 MediaRecorder的状态机模型

与MediaPlayer类似,MediaRecorder也有严格的状态机,核心状态转换如下:

复制代码
Idle(空闲)→ Initialized(已初始化)→ DataSourceConfigured(数据源配置完成)→ Prepared(已准备)→ Recording(录制中)
                  ↑                          ↑                          ↓                ↓
                Error(错误)←────────────── Stopped(已停止)←────────── Paused(暂停)←
                  ↑
                Released(已释放)←───────────────────────────────────────────────

关键规则:

  • 初始化后需先设置音/视频源(setAudioSource()/setVideoSource()),进入Initialized;
  • 配置输出格式(setOutputFormat())和编码格式(setAudioEncoder()/setVideoEncoder())后,进入DataSourceConfigured;
  • 调用prepare()进入Prepared后,才能调用start()开始录制;
  • 录制中可调用pause()暂停,resume()恢复,最终需调用stop()停止录制。

2 从Java层到Native层的调用链路

MediaRecorder的调用链路与MediaPlayer类似,但核心组件不同:

  1. Java层android.media.MediaRecorder,暴露setAudioSource()setOutputFormat()start()等API;
  2. JNI层android_media_MediaRecorder.cpp,映射Java方法到Native方法(如native_setup()native_setAudioSource());
  3. Native层核心MediaRecorder.cpp,通过IMediaRecorder接口与MediaServer的录制服务通信;
  4. 多媒体服务层 :MediaServer中的MediaRecorderBase,整合音频采集、视频采集、编码、封装逻辑。

3 底层核心组件解析

MediaRecorder的录制能力依赖以下核心组件:

(1)AudioRecord/AudioFlinger:音频采集
  • AudioRecord:从麦克风采集原始PCM音频数据,需配置采样率(如44.1kHz)、声道数(单声道/立体声)、位深(16bit);
  • AudioFlinger:管理音频输入设备,负责麦克风数据的采集和传输,通过Audio HAL与硬件麦克风交互。
(2)Camera/Camera HAL:视频采集
  • 调用Camera API(Android 5.0后为Camera2)获取摄像头数据,原始数据格式为YUV(如NV21);
  • 通过Camera HAL与硬件摄像头交互,配置分辨率、帧率等参数。
(3)MediaCodec:编码器

作用:将采集的原始音视频数据(PCM/YUV)编码为压缩格式(如AAC、H.264)。

  • 硬编码:通过Codec HAL调用硬件编码器(主流方案,性能高);
  • 软编码:基于CPU的软件编码(兼容性好,功耗高);
  • 核心流程:configure()(配置编码参数)→ start()queueInputBuffer()(送入原始数据)→ dequeueOutputBuffer()(取出编码后数据)。
(4)MediaMuxer:封装器

作用:将编码后的音频流和视频流封装为标准格式(MP4、3GP、WebM等),写入文件或网络流。

  • 核心接口:addTrack()(添加音视频轨道)、writeSampleData()(写入编码后数据)、stop()(完成封装)。

4 MediaRecorder录制流程的底层执行步骤

以录制MP4格式的音视频为例,底层完整流程如下:

  1. 初始化阶段

    • Java层调用new MediaRecorder(),触发JNI层native_setup(),创建Native层MediaRecorder实例;
    • 绑定MediaServer的录制服务,初始化音视频采集通道。
  2. 数据源配置

    • 调用setAudioSource(MIC),初始化AudioRecord,建立与AudioFlinger的连接,配置音频采集参数;
    • 调用setVideoSource(CAMERA),初始化Camera,配置视频分辨率、帧率等参数;
    • 调用setOutputFormat(MP4)setAudioEncoder(AAC)/setVideoEncoder(H264),配置封装格式和编码参数。
  3. 准备阶段

    • 调用prepare(),初始化MediaCodec(根据编码格式选择软/硬编码器);
    • 初始化MediaMuxer,创建输出文件,添加音视频轨道;
    • 启动AudioRecord和Camera,准备采集数据。
  4. 录制阶段

    • 调用start(),AudioRecord从麦克风采集PCM数据,Camera采集YUV数据;
    • 原始数据送入MediaCodec编码为AAC/H.264压缩数据;
    • 编码后的数据通过MediaMuxer写入MP4文件,同时记录PTS时间戳(保证音视频同步);
    • 录制中可调用pause()暂停采集/编码,resume()恢复。
  5. 停止/释放阶段

    • 调用stop(),停止音视频采集、编码,MediaMuxer完成文件封装(写入moov原子,保证文件可播放);
    • 调用release(),释放AudioRecord、Camera、MediaCodec、MediaMuxer等资源,断开与MediaServer的连接。

MediaPlayer与MediaRecorder的共性与差异

1 共性

  1. 分层设计:均采用"Java层封装 + JNI桥接 + Native层核心 + HAL层硬件交互"的架构,屏蔽硬件差异;
  2. 依赖核心服务:均基于MediaServer进程提供的多媒体服务,避免应用层直接操作硬件;
  3. 依赖MediaCodec:均使用MediaCodec进行编解码(MediaPlayer解码,MediaRecorder编码);
  4. 音频依赖AudioFlinger:播放时通过AudioFlinger输出音频,录制时通过AudioFlinger采集音频;
  5. 状态机约束:均有严格的状态转换规则,违规操作会抛出异常。

2 差异

维度 MediaPlayer MediaRecorder
数据流方向 存储/网络 → 解封装 → 解码 → 输出 采集(麦克风/摄像头)→ 编码 → 封装 → 存储/网络
核心数据处理 解封装(MediaExtractor)+ 解码 采集 + 编码 + 封装(MediaMuxer)
音频组件 AudioTrack(输出PCM) AudioRecord(采集PCM)
视频组件 Surface(渲染YUV) Camera(采集YUV)
同步机制 基于PTS/DTS的音视频同步(播放端) 基于PTS的音视频同步(录制端)

底层核心技术补充

1 OpenMAX IL:硬件编解码的标准接口

OpenMAX IL(Open Media Acceleration Integration Layer)是Khronos集团制定的多媒体编解码接口标准,Android通过它连接Native层和Codec HAL:

  • 原生层通过OpenMAX IL的OMX_Core加载硬件编解码器;
  • 编解码器厂商(如高通、联发科)实现OpenMAX IL接口,适配自家硬件;
  • Android通过OpenMAX IL屏蔽不同厂商的硬件差异,保证上层API的统一性。

2 硬件加速的实现

MediaPlayer/MediaRecorder的硬件加速核心是硬件编解码:

  • 硬解码:MediaCodec调用硬件解码器,解码过程由GPU/专用解码芯片完成,CPU占用率低;
  • 硬编码:MediaCodec调用硬件编码器,编码过程由专用编码芯片完成,支持高分辨率、高帧率录制;
  • 开发者可通过MediaCodecInfo判断设备是否支持指定格式的硬编解码。

3 异常处理机制

底层通过回调和错误码向上层传递异常:

  • MediaPlayer的OnErrorListenerOnInfoListener
  • MediaRecorder的OnErrorListenerOnInfoListener
  • 常见错误:数据源不存在(MEDIA_ERROR_UNKNOWN)、编解码器不支持(MEDIA_ERROR_CODEC_NOT_SUPPORTED)、状态违规(ILLEGAL_STATE_EXCEPTION)。

开发实践中的注意事项

  1. 严格遵守状态机:避免在错误状态调用API(如Prepared前调用start()),建议封装状态管理工具类;
  2. 资源及时释放 :MediaPlayer/Recorder持有硬件资源(麦克风、摄像头、音频设备),使用后必须调用release(),否则会导致资源泄漏;
  3. 硬编解码兼容性:不同设备支持的编解码格式不同,建议先检测设备支持的格式,降级使用软编解码;
  4. 异步操作处理prepareAsync()setDataSource()(网络数据源)为异步操作,需通过监听器处理回调,避免主线程阻塞;
  5. 性能优化:播放大文件时使用缓存策略,录制时控制分辨率/帧率(如1080P/30fps),避免占用过多内存和存储。

总结

MediaPlayer和MediaRecorder是Android多媒体框架的上层抽象,其底层串联了解封装/封装、编解码、音视频采集/输出、硬件交互等核心能力:

  • MediaPlayer的核心是"解封装→解码→输出",依赖MediaExtractor、MediaCodec、AudioTrack/Surface;
  • MediaRecorder的核心是"采集→编码→封装",依赖AudioRecord/Camera、MediaCodec、MediaMuxer。
相关推荐
阿巴斯甜21 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android