【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。
相关推荐
nono牛1 小时前
ps -A|grep gate
android
未知名Android用户2 小时前
Android动态变化渐变背景
android
nono牛3 小时前
Gatekeeper 的精确定义
android
stevenzqzq5 小时前
android启动初始化和注入理解3
android
城东米粉儿6 小时前
compose 状态提升 笔记
android
粤M温同学7 小时前
Android 实现沉浸式状态栏
android
ljt27249606617 小时前
Compose笔记(六十八)--MutableStateFlow
android·笔记·android jetpack
stevenzqzq8 小时前
Android Studio 断点调试核心技巧总结
android·ide·android studio
aqi009 小时前
FFmpeg开发笔记(九十八)基于FFmpeg的跨平台图形用户界面LosslessCut
android·ffmpeg·kotlin·音视频·直播·流媒体