MediaCodec、OpenGL、OpenSL/AudioTrack 实现一款简单的视频播放器

概述

功能很简单,大致流程为:
1) MediaCodec 解码视频文件得到 YUV、PCM 数据
2) OpenGL 将 YUV 转为 RGB,并渲染到 Surface 上
3) OpenSL/AudoTrack 获取 PCM 数据并播放

需要的前置知识有:
1) YUV、PCM 等基础音视频知识,如 YUV 转 RGB
2) MediaCodec 的使用
3) OpenGL,包括 EGL、纹理等
4) OpenSL 或 AudioTrack 的使用

MediaCodec 解码

之前写过相关的博客 MediaCodec 实现硬件解码,大致流程和普通的解码类似,在编写视频播放器这个功能时,需要注意的地方有两个:

1) 监听解码流程

    public interface OnDecodeListener {
        void onImageDecoded(byte[] data);

        void onSampleDecoded(byte[] data);

        void onDecodeEnded();
    }

也可以加一个 onDecodeError() 的接口,看需要扩展即可。

2) 播放和解码同步

因为视频数据量很大,不可能把解码后的 YUV 数据保存在一个队列里,再慢慢拿出来使用 OpenGL 渲染(很容易就 OOM 了),因此,必须控制解码的速率,最简单的控制方式是和播放同步,如下所示:

                    ByteBuffer outputBuffer = outputBuffers[outIndex];
                    outputBuffer.position(bufferInfo.offset);
                    outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
                    byte[] data = new byte[bufferInfo.size];
                    outputBuffer.get(data);

                    if (mIsDecodeWithPts) {
                        if (startTime == 0) {
                            startTime = System.nanoTime();
                        } else {
                            passTime = (System.nanoTime() - startTime) / 1000;
                            if (passTime < bufferInfo.presentationTimeUs) {
                                TimeUnit.MICROSECONDS.sleep(bufferInfo.presentationTimeUs - passTime);
                            }
                        }
                    }

                    if (mediaType == HWCodec.MEDIA_TYPE_VIDEO && listener != null) {
                        listener.onImageDecoded(data);
                    } else if (listener != null) {
                        listener.onSampleDecoded(data);
                    }

OpenGL 渲染 YUV 数据

和渲染纹理的流程类似,不同的地方在于需要转换 YUV 数据为 RGB,而 YUV 数据又有 YUV420P、YUV420SP 等多种格式,因此在转换 RGB 之前,需要统一 YUV 数据的格式,这里使用的是 YUV420P。

YUV 数据格式之间的转换可以自己写,比如 YUV420SP 转换为 YUV420P,只需要把最后的 U、V 数据分别逐个放入到一个数组里即可,但考虑到视频裁剪、旋转,以及之后可能用到的各种 YUV 数据处理功能,因此这里引入了一个 libyuv 的库,使用非常简单:

Yuv* convertToI420(AVModel *model) {
    if (!model || model->imageLen <= 0 || model->flag != MODEL_FLAG_VIDEO || model->width <= 0
        || model->height <= 0 || model->pixelFormat <= 0 || !model->image) {
        LOGE("convertToARGB failed: invalid argument");
        return nullptr;
    }
    Yuv *yuv = new Yuv(model->
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值