【音视频】基于 Java 的媒体播放器

这个系列文章我们来介绍一位海外工程师如何探索安卓音视频基础技术,对于想要开始学习音视频技术的朋友,这些文章是份不错的入门资料,本篇介绍 Java 媒体播放器。

——来自公众号“关键帧Keyframe”的分享

AVSample-Java 模块在 Android 平台提供高、低两级媒体播放能力:

  • 高级MediaPlayer API —— 开箱即用,适合常规场景
  • 低级AudioTrack API —— PCM 流精细控制,适合实时处理与低延迟场景
    整体采用接口驱动 + 模板方法模式,音频与视频两条链路关注点彻底分离,便于后期扩展滤镜、特效、串流协议等能力。

1、架构概览

┌-----------------------------┐
│        业务 UI 层            │
└------------┬----------------┘
             │ 调用
┌------------┴----------------┐
│     IMediaPlayer 抽象契约   │←-------┐
└------------┬----------------┘        │
             │ 实现                    │
┌------------┴----------------┐        │
│       BasePlayer            │        │
│  (生命周期 + 资源管理)       │        │
└-----┬-------------┬---------┘        │
      │继承          │继承              │
┌-----┴------┐ ┌-----┴---------┐       │
│AudioMedia  │ │VideoMedia     │       │
│   Player   │ │   Player       │       │
└-----┬------┘ └-----┬---------┘       │
      │              │                 │
┌-----┴------┐ ┌-----┴----------------┐│
│ AudioTrack │ │SurfaceHolder/OpenGL ES ││
│  实现      │ │  渲染管线               ││
└------------┘ └-----------------------┘│
                                        │
┌---------------------------------------┴┐
│  事件回调(IPlayerListener) & 资源回收   │
└----------------------------------------┘

2、核心组件

2.1、统一契约:IMediaPlayer

interface IMediaPlayer {
    fun prepare(path: String, streamType: Int, holder: SurfaceHolder?)
    fun start()
    fun pause()
    fun seekTo(ms: Int)
    fun release()
    /* 省略异常与监听器相关 */
}

来源:IMediaplayer.kt#L22-L37

2.2、模板基类:BasePlayer

  • 封装AudioAttributes构建、Surface绑定、状态机管理
  • 实现OnPreparedListenerOnCompletionListenerOnErrorListener,子类仅需覆写业务逻辑

来源:BasePlayer.kt#L19-L86

2.3、专用实现

播放器职责关键差异
AudioMediaPlayer纯音频忽略 Surface;自动释放 WakeLock
VideoMediaPlayer视频 + 音频必须传入 SurfaceHolder;支持硬件加速

来源:AudioMediaPlayer.kt#L17-L61 / VideoMediaPlayer.kt#L18-L59

3、低级音频:AudioTrack 方案

适用于实时处理、PCM 注入、音效链路等场景。
接口:IAudioTrack
实现:AudioTrackImpl

3.1、初始化示例

override fun prepare(
    streamType: Int,
    sampleRate: Int,
    channels: Int,
    audioFormat: Int
): Boolean {
    val bufSize = getMinBufferSize(sampleRate, channels, audioFormat)
    mAudioTrack = AudioTrack(
        streamType, sampleRate, channels, audioFormat,
        bufSize, AudioTrack.MODE_STREAM
    )
    if (mAudioTrack.state != AudioTrack.STATE_INITIALIZED) {
        LogHelper.e("AudioTrack", "不能播放,当前播放器未处于初始化状态..")
        return false
    }
    return true
}

来源:AudioTrackImpl.kt#L17-L70

3.2、使用场景

  • 语音对讲、VoIP
  • 离线 PCM 文件注入
  • 自定义音效(变声、重采样)

4、视频渲染:OpenGL ES 管线

类:VideoConsumerGLPreview
核心能力:硬件加速 YUV→RGB 转换 + 三重纹理采样

4.1、着色器关键代码

precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D SamplerY;
uniform sampler2D SamplerU;
uniform sampler2D SamplerV;

const mat3 yuv2rgb = mat3(
    1.0,  0.0,      1.2802,
    1.0, -0.214821, -0.380589,
    1.0,  2.127982, 0.0
);

void main() {
    vec3 yuv = vec3(
        1.1643 * (texture2D(SamplerY, vTextureCoord).r - 0.0625),
        texture2D(SamplerU, vTextureCoord).r - 0.5,
        texture2D(SamplerV, vTextureCoord).r - 0.5
    );
    vec3 rgb = yuv * yuv2rgb;
    gl_FragColor = vec4(rgb, 1.0);
}

4.2、渲染流程

  1. Surface 创建 → EGL 上下文初始化
  2. 编译链接着色器程序
  3. 生成 Y/U/V 三重纹理,逐帧更新
  4. glDrawArrays 全屏 quad → eglSwapBuffers 上屏

5、实战:音频播放最小闭环

class AudioMediaPlayActivity : BaseActivity<Int>(), BasePlayer.IPlayerListener {
    privatelateinitvar mAudioMediaPlayer: AudioMediaPlayer
    privateval AUDIO_PATH = "sdcard/avsample/lame_encode.mp3"

    overridefun init() {
        mAudioMediaPlayer = AudioMediaPlayer()
        mAudioMediaPlayer.addPlayerListener(this)

        btn_start.setOnClickListener {
            mAudioMediaPlayer.prepare(AUDIO_PATH, AudioManager.STREAM_MUSIC, null)
            mAudioMediaPlayer.start()
        }
        btn_stop.setOnClickListener { mAudioMediaPlayer.release() }
    }

    overridefun onPrepared() { startTime(chronometer) }
    overridefun onCompletion() {
        cleanTime(chronometer)
        mAudioMediaPlayer.release()
    }

    overridefun onDestroy() {
        super.onDestroy()
        mAudioMediaPlayer.release()   // 防止泄漏
    }
}

来源:AudioMediaPlayActivity.kt#L20-L78

6、框架优势 & 设计亮点

  1. 模块化:MediaPlayer / AudioTrack / OpenGL 三路实现,接口统一,可自由替换
  2. 资源安全:BasePlayer 统一释放;回调内自动注销;Activity 销毁链式回收
  3. 事件驱动IPlayerListener 将准备/完成/异常实时抛给 UI,方便做状态同步
  4. 性能:OpenGL 零拷贝渲染 + 硬件 YUV 转换,低端机流畅 1080P60
  5. 易扩展
    • 新增串流协议 → 仅实现 IMediaPlayer
    • 插入音效 → 继承 AudioTrackImpl 加算法钩子
    • 视频滤镜 → 在 VideoConsumerGLPreview 片段着色器后级联 FBO

7、下一步可玩方向

  • 接入 ExoPlayer 内核作为第三种 IMediaPlayer 实现
  • 基于 AudioTrack 实现 10-band EQ + 重采样可视化
  • 在现有 OpenGL 管线追加美颜、HDR→SDR 色调映射
  • 将渲染层迁移至 SurfaceFlinger + HardwareBuffer,实现零拷贝录屏

学习和提升音视频开发技术,推荐你加入我们的知识星球:【关键帧的音视频开发圈】

【音视频】基于 Java 的媒体播放器

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论