接续“增强axmol引擎视频播放之 – windows视频播放支持”,本文将描述如何在Android实现渲染视频到纹理。
一、调研
虚幻引擎实现方案:使用Android系统MediaPlayer,先将视频渲染到TextureSurface,再从TextureSurface读取RGBA像素数据,传给虚幻引擎Native层达到将视频纹理渲染到场景对象的目的。而axmol是MIT licensed开源引擎,自然不会使用虚幻引擎的方案,于是,通过多方调研,发现Google官方出品ExoPlayer2且作为androidx.media3的一部分发布,官方有很快上手入门的播放视频的demo,通过初步研究发现ExoPlayer2更灵活且开源。 于是决定使用ExoPlayer2并借鉴官方demo实现axmol的android视频纹理渲染支持。
google/ExoPlayer
https://github.com/androidx/media
二、实现步骤
获取ExoPlayer解码的原始视频NV12数据可行性
通过初步研究确定,ExoPlayer默认也只支持将视频渲染到Surface或SurfaceTexture, 但Google官方文档说明APIMediaCodec.configure 是支持ByteBuffer模式的,该API定义如下:
public void configure (MediaFormat format,
Surface surface,
MediaCrypto crypto,
int flags)
核心要点:第二个参数surface的解释:
Surface: Specify a surface on which to render the output of this decoder. Passnullassurfaceif the codec does not generate raw video output (e.g. not a video decoder) and/or if you want to configure the codec forByteBufferoutput.
大致意思是,如果想要底层适配解码器,将适配raw数据解码到ByteBuffer,则surface必须传null,这就是关键所在了。也就说明获取NV12是可行的。
- 尝试修改ExoPlayer支持ByteBuffer模式
下载仓库:https://github.com/google/ExoPlayer
通过分析MediaCodecVideoRenderer.java,发现ExoPlayer不持ByteBuffer模式,重写相关方法,获取的bytebuffer数据长度时1字节的原因如下:
MediaCodecVideoRenderer.java做了强制处理,即使通过ExoPlayer接口传入了空surface,内部也会创建PlaceHolderSurface,最后调用MediaCodec.configure时,surface参数始终不为空 。
尝试继承MediaCodecVideoRenderer.java发现有些需要控制MediaCodec模式的变量或方法是private 权限,于是只有完整复制一份MediaCodecVideoRenderer.java 在进行必要修改确保MediaCodec工作在ByteBuffer模式: AxmolVideoRenderer.java 放入引擎java代码目录,这样也能避免修改ExoPlayer package。
- 有了AxmolVideoRenderer.java,就可以通过如下方案实现AndroidMediaEngine:
Java: AxmolMediaEngine: 实现视频播放、暂停等功能,并将NV12 buffer传给Natvie
C++: AndroidMediaEngine实现MediaEngine的所有相关接口,通过JNI调用Java AxmolMediaEngine接口
MedaiEngine的跨平台策略和抽象工程模式,保证了ui::MediaPlayer(原ui::VideoPlayer)平台无关性。
具体代码请访问相关patch提交:
Improve media-engine for android, linux by halx99 · Pull Request #1228 · axmolengine/axmol