前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android视频系列:视频解码篇--android上视频播放的实现

android视频系列:视频解码篇--android上视频播放的实现

作者头像
天天P图攻城狮
发布2018-02-02 16:05:25
4K0
发布2018-02-02 16:05:25
举报
文章被收录于专栏:天天P图攻城狮

前言

要开始正儿八经地写视频系列文章了。思来想去,从播放器入手,再合适不过了。视频文件,只有播放出来,才显示出了意义;只有播放出来,才暴露出各种问题。先理解播放的场景,才能更好地理解视频处理时所选取的策略。

播放器做了什么

播放器播放视频,就是一步步剖开视频的内容,显示在屏幕上。

最简单的理解方式,是把视频文件看做一个容纳了很多图片的容器。播放时,从容器里取出一张图片,放到屏幕上显示,隔一点时间后,再从容器里取出下一张图,放到屏幕上。按次序把图片一张一张显示到屏幕上,等到最后一张也显示到屏幕上后,播放就完成了。

现在面临第一个问题:

隔多长时间去取下一张图呢? 人眼观看画面,限于视神经的反应速度,存在视觉暂留现象,其时值约是1/16秒,对于不同频率的光有不同的暂留时间。在暂留时间结束前,放入下一张图,人就感觉不出来是一张张的图,而是连续的动画了。在移动终端上观看的视频,每秒25帧图像,就很流畅了。一秒钟放的图像数,被称为帧率。

紧接着下个问题就来了:

一秒钟25帧图像,那么100秒的视频,容器里需要放置2500张图像,这是很大的数据量。无论存储还是传播,都是无法接受的。需要想办法减小数据量。从理论上分析,确实存在冗余信息,提供了压缩的可能性。而且,冗余信息还特别多,于是数据量可以大大地被压缩。

所以,视频容器里,放置的是压缩后的图像数据。那么播放器播放,就需要先解压缩成图像,再放到屏幕上。所以,播放器的两个核心功能,一个是解码,一个是显示。

我们来看看,Android为我们提供了哪些对象,可以让我们做视频的播放。

Android播放视频

下面我们介绍3种在Android上播放视频的方法。

1. 使用VideoView播放视频

VideoView把解码和显示工作全部都封装起来,简单地设置视频路径,就可以进行播放了。 在显示方面,它就是一个View,可以在代码里创建,也可以在layout xml里直接定义。在解码方面,它支持常用的解码控制操作,如start(), pause(), resume(), seek(), seekTo()等。

看看它的内部实现,我们发现,解码使用了MediaPlayer,显示使用了SurfaceView。

那么,自己直接用SurfaceView和MediaPlayer,要怎么做?

2. 使用MediaPlayer和SurfaceView播放视频

Android系统,已经在底层我们打通了一条MediaPlayer到SurfaceView的数据通路,那就是Surface。 当SurfaceView被创建完成,就可以通过它的SurfaceHolder获取到它的Surface。把Surface传递给MediaPlayer,MediaPlayer解码的数据就会源源不断地输送到SurfaceView里。MediaPlayer有节奏地往Surface输入解码数据,SurfaceView会相应有节奏把Surface里的数据显示到屏幕上。

这种实现方式,解码和显示分别在两个对象中,可以分别控制。但是,我们无法控制它们的数据通路。要牢牢控制每一帧的数据,就要使用下面这种实现。

3. 使用MediaPlayer和GLSurfaceView播放视频

GLSurfaceView继承自SurfaceView,它实现了把opengl的渲染结果,绘制到给定的Surface里,进而可以显示在屏幕上。

它的几个主要特点:

  • 内部管理了一个EGL display,用于把opengl渲染的结果输出到Surface里。
  • 提供Renderer接口,使用者可以通过实现这个接口,来控制opengl渲染的行为和内容。
  • opengl渲染工作在特定一个线程里,与UI线程解耦开来。
  • 支持on-demand和continuous两种渲染模式。

让我们来看看,如何使用GLSurfaceView来实现视频的播放。

首先创建好GLSurfaceView。

setEGLContextClientVersion(2),指定EGLContext client version,2代表使用OpenGL ES 2.0。注意,它的调用必须要在setRenderer()之前。

setRenderer()指定用户自定义的renderer。这里指定为VideoRenderer,它通过实现GLSurfaceView.Renderer接口,控制了opengl渲染。

这个接口定义的三个方法,都执行在GLSurfaceView创建的gl线程中。

onSurfaceCreated()的调用发生在surface的创建或者重建时。gl线程的EGL context发生lost时,也会调用该方法。如手机从睡眠状态唤醒,会lost EGL context,此时onSurfaceCreated()方法会被调用。所以,渲染开始的资源申请和初始化工作,包括texture等资源的创建,都实现在这个方法中。 gl线程的EGL context发生lost后,和该context关联的所有opengl资源都会自动清除,使用者也无需专门去实现对应的glDelete*函数来清除已经lost的资源。

onSurfaceChanged()的调用发生在每次的onSurfaceCreated()之后,和每次surface size发生改变时。官方推荐在此处做投影和视口变换,但是,通常情形下,不会发生size变化,所以为了简化实现,往往保持该方法为空。

onDrawFrame()的调用发生在绘制当前帧时。每一次要显示的内容,都在这个方法里完成opengl渲染。

下面我们来看具体如何定义VideoRenderer,来实现视频播放。

在onSurfaceCreated()里做了三件事:

1)initProgram()

创建和编译好opengl shader program,用于把图像数据渲染出来。

2)initSurfaceTexture()

为视频解码器MediaPlayer和opengl对象texture的连接,创建数据通路。

把opengl的一个texture,封装到SurfaceTexture中。为该SurfaceTexture设置数据获取的回调onFrameAvailableListener。当SurfaceTexture获取到数据,该回调就会被执行。

那么,如何往SurfaceTexture里放入数据呢?接着看下面的实现。

3)mediaPlay()

把SurfaceTexture封装在Surface对象中,赋给MediaPlayer。MediaPlayer就会把解码数据源源不断地放入SurfaceTexture中了。

放入到SurfaceTexture中的数据,我们要如何来使用呢?

需要把数据从SurfaceTexture中取出来,放到opengl texture中。实现如下:

SurfaceTexture的updateTexImage()方法,把SurfaceTexture中的图像图像数据取到opengl texture中。getTransformMatrix()告诉opengl需要对该图像做一个基本的变换,通常为上下翻转。

至此,opengl拿到了解码的图像数据后,就可以自如的做任何图像相关的处理,渲染到屏幕上。

总结

以上在Android上实现的三种播放视频方法,从简单到复杂,可以根据自己功能的需要,灵活进行选择。如果只是简单地播放视频,可以使用VideoView。如果对播放有更多的控制需求,可以使用MediaPlayer和SurfaceView。如果要对每一帧图像做处理,可以使用MediaPlayer和GLSurfaceView。


作者简介:taoxiong(熊涛),天天P图Android工程师

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 天天P图攻城狮 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 播放器做了什么
  • Android播放视频
    • 1. 使用VideoView播放视频
      • 2. 使用MediaPlayer和SurfaceView播放视频
        • 3. 使用MediaPlayer和GLSurfaceView播放视频
        • 总结
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档