GSYVideoPlayer 视频播放流程详解

1 阅读3分钟

我来详细梳理 GSYVideoPlayer 播放视频的完整流程:

GSYVideoPlayer 视频播放流程详解

1. 启动播放入口

用户点击播放 → startPlayLogic() → prepareVideo()

关键方法链

  • startButtonLogic() - 点击开始按钮
  • startPlayLogic() - 开始播放逻辑
  • prepareVideo() - 准备视频
  • startPrepare() - 开始准备(真正的入口)

2. startPrepare() 阶段

// GSYVideoView.startPrepare()
1. getGSYVideoManager().listener().onCompletion()  // 通知上一个播放完成
2. mVideoAllCallBack.onStartPrepared()             // 回调:开始准备
3. getGSYVideoManager().setListener(this)          // 设置监听器
4. getGSYVideoManager().prepare(url, headers...)   // ★ 核心:调用 prepare
5. setStateAndUi(CURRENT_STATE_PREPAREING)         // 设置状态为"准备中"

3. GSYVideoManager.prepare() 阶段

// GSYVideoBaseManager.prepare()
1. 构建 GSYModel 对象(包含 url, headers, loop, speed 等)
2. 发送 Message: msg.what = HANDLER_PREPARE
3. Handler 接收消息 → initVideo(msg)

4. initVideo() - 播放器初始化

// GSYVideoBaseManager.initVideo()
1. playerManager = getPlayManager()           // 获取播放器管理器(IjkPlayerManager)
2. cacheManager = getCacheManager()           // 获取缓存管理器
3. playerManager.initVideoPlayer(context, msg, options, cacheManager)  // ★ 初始化播放器
4. mediaPlayer.setOnXxxListener(this)         // 设置各种监听器
5. mediaPlayer.prepareAsync()                 // ★ 异步准备

5. IjkPlayerManager.initVideoPlayer() - IJK 播放器创建

// IjkPlayerManager.initVideoPlayer()
1. mediaPlayer = new IjkMediaPlayer()         // 创建 IJK 播放器实例
2. mediaPlayer.setAudioStreamType(STREAM_MUSIC)

// 设置硬解码选项
3. if (GSYVideoType.isMediaCodec()) {
       mediaPlayer.setOption("mediacodec", 1)
       mediaPlayer.setOption("mediacodec-auto-rotate", 1)
       mediaPlayer.setOption("mediacodec-handle-resolution-change", 1)
   }

// 设置数据源
4. mediaPlayer.setDataSource(url, headers)    // 或处理缓存

// 其他设置
5. mediaPlayer.setLooping(loop)
6. mediaPlayer.setSpeed(speed)
7. initIJKOption(mediaPlayer, optionModelList)  // 应用自定义选项

// ★ 回调通知
8. initSuccess(gsyModel)  → mPlayerInitSuccessListener.onPlayerInitSuccess()

这里是我们修复的关键点:在 initSuccess() 回调中设置 Surface,此时 MediaPlayer 已创建但还未 prepareAsync。


6. IjkMediaPlayer.prepareAsync() - Native 层准备

Java: prepareAsync()
  ↓
JNI: _prepareAsync()
  ↓
C: ijkmp_prepare_async()
  ↓
C: ffp_prepare_async_l()
  ↓
创建解码管道 (Pipeline)

Native 层流程

// ff_ffplay.c
ffp_prepare_async_l()
├── stream_open()           // 打开流
├── read_thread()           // 读取线程
│   ├── avformat_open_input()      // 打开输入
│   ├── avformat_find_stream_info() // 查找流信息
│   └── stream_component_open()     // 打开各个流组件
│       ├── audio_thread()          // 音频解码线程
│       └── video_thread()          // 视频解码线程
│           └── ffpipenode_run_sync() // 解码节点

7. MediaCodec 创建 - 关键的 Surface 检查

// ffpipenode_android_mediacodec_vdec.c
func_run_sync()
├── create_codec_l()        // ★ 创建 MediaCodec
│   ├── 检查 jsurface 是否为 NULL
│   │   ├── NULL → 创建 SDL_AMediaCodecDummy(占位符)
│   │   └── 有效 → 创建真实 MediaCodec
│   └── AMediaCodec_configure(codec, format, surface)

这就是黑屏问题的根源:如果此时 Surface 为 null,会创建 Dummy Codec。


8. TextureView 创建流程

onPrepared() 回调
  ↓
GSYVideoView.onPrepared()
  ↓
addTextureView()            // ★ 此时才添加 TextureView
  ↓
TextureView.onSurfaceAvailable()
  ↓
GSYTextureRenderView.onSurfaceAvailable()
  ↓
pauseLogic(surface, true)
  ↓
mSurface = surface
setDisplay(mSurface)        // 设置 Surface 到播放器

9. Surface 设置到播放器

// GSYVideoView.setDisplay()
getGSYVideoManager().setDisplay(surface)
  ↓
// GSYVideoBaseManager.setDisplay()
Message msg (HANDLER_SETDISPLAY)
showDisplay(msg)
  ↓
// IjkPlayerManager.showDisplay()
if (mediaPlayer != null && holder.isValid()) {
    mediaPlayer.setSurface(holder)    // ★ 设置到 IjkMediaPlayer
}

10. Native 层 Surface 设置

Java: setSurface(surface)
  ↓
JNI: _setVideoSurface()
  ↓
C: ijkmp_android_set_surface()
  ↓
C: ffpipeline_set_surface()
  ↓
检查是否需要重新配置 MediaCodec
  ↓
如果之前是 Dummy → "quirk: reconfigure with new codec" → 黑屏

11. 视频渲染流程

解码线程输出帧
  ↓
video_refresh_thread()      // 视频刷新线程video_display2()            // 显示视频帧SDL_VoutDisplayYUVOverlay() // 输出到 Surface
  ↓
ANativeWindow_lock/unlock   // 写入 Native Window
  ↓
屏幕显示

12. 时序图总结

┌─────────────────────────────────────────────────────────────────────────────┐
│                          GSYVideoPlayer 播放时序                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  用户点击播放                                                                 │
│       │                                                                     │
│       ▼                                                                     │
│  startPrepare()                                                             │
│       │                                                                     │
│       ├──► GSYVideoManager.prepare()                                        │
│       │         │                                                           │
│       │         ▼                                                           │
│       │    initVideo()                                                      │
│       │         │                                                           │
│       │         ├──► IjkPlayerManager.initVideoPlayer()                     │
│       │         │         │                                                 │
│       │         │         ├──► new IjkMediaPlayer()                         │
│       │         │         ├──► setDataSource()                              │
│       │         │         ├──► setOptions()                                 │
│       │         │         └──► initSuccess() ──► ★ 我们在这里设置 Surface    │
│       │         │                                                           │
│       │         └──► prepareAsync() ──────────────────────┐                 │
│       │                                                    │                │
│       │              ┌─────────── Native 层 ───────────────┤                │
│       │              │                                     │                │
│       │              │  ffp_prepare_async_l()              │                │
│       │              │       │                             │                │
│       │              │       ▼                             │                │
│       │              │  stream_open()                      │                │
│       │              │       │                             │                │
│       │              │       ▼                             │                │
│       │              │  video_thread() ─► create_codec_l() │                │
│       │              │       │              │              │                │
│       │              │       │      检查 Surface 是否存在   │                │
│       │              │       │              │              │                │
│       │              │       │      ├─ 有 → 创建真实 Codec  │                │
│       │              │       │      └─ 无 → Dummy Codec    │                │
│       │              │       │                             │                │
│       │              └───────┼─────────────────────────────┘                │
│       │                      │                                              │
│       │                      ▼                                              │
│       │              onPrepared() 回调                                       │
│       │                      │                                              │
│       │                      ▼                                              │
│       │              addTextureView() ──► TextureView 创建                   │
│       │                      │                                              │
│       │                      ▼                                              │
│       │              onSurfaceAvailable() ──► setDisplay(surface)           │
│       │                      │                                              │
│       │                      ▼                                              │
│       │              如果之前是 Dummy ──► reconfigure ──► 短暂黑屏            │
│       │                      │                                              │
│       │                      ▼                                              │
│       └──────────────► 开始渲染视频帧                                         │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

我们的修复方案

★ 利用 IPlayerInitSuccessListener 回调
  │
  │  时机:MediaPlayer 创建后,prepareAsync() 之前
  │
  ▼
onPlayerInitSuccess(player, model)
  │
  ├── 检查 mSurface 是否存在(重复播放时会存在)
  │
  └── 存在 → player.setSurface(mSurface)
           │
           ▼
      Native 层 create_codec_l() 时 Surface 已存在
           │
           ▼
      直接创建真实 MediaCodec,无需 Dummy
           │
           ▼
      无需 reconfigure,无黑屏 ✓