Android视频开发基础(二)

分类: KMPlayer资讯 发布时间:2018-12-05 23:09

       前一篇文章详细介绍了视频的一些基本概念,这些内容与Android系统本身没有密切关系,只是作为熟悉视频领域的一个基础,便于介绍接下来关于Android上视频处理的其他内容。继续来看Android上视频相关的内容。

Android支持的格式

      先来看一下Android官网上给出的Android系统支持的文件格式。

      从图中我们可以看到

      1、Android支持mp4、3gp、mkv和webm四种视频封装格式。前三种都是比较常见的,最后一种要特别讲一下。

      WebM由Google提出,一个用于网页的开放、免费的媒体文件格式,目前在Youtube上使用最多的格式。WebM 影片格式其实是以 mkv容器格式为基础开发的新容器格式,里面包括了VP8/VP9视轨和 Ogg Vorbis音轨。

      2、Android支持多种的视频编码格式。如H.264 AVC、H.263、VP8、VP9等。每种封装格式可以对应于不同的编码格式。比如mp4格式视频文件可能对应于H.263、H.264、H.265三种格式。

      3、Android对于视频编解码的支持有版本的差异。比如对于VP9的编码是不支持的,而VP9的解码是在4.4版本之后才支持。

      从上面几点看,在Android上做视频处理功能,不仅需要考虑各种视频格式的兼容,还要考虑Android版本的兼容问题,另外,还需要考虑不同Rom厂商对系统做的定制。

Android实现视频编辑

       音视频的编码可以使用硬编码和软编码两种,硬编码就是直接调用GPU进行编码处理,而软编码是使用CPU进行运算。两种编码方式都有各自的优缺点,硬编码的效率更高,对CPU的消耗较少,但是兼容性较差,硬编码就会涉及到硬件,所以当机器不同,可能会有的机器能编码,有的会失败。而软编码的兼容性更好,但是效率较低,会加重CPU的负载。在Android的视频处理中,这两种编码方式分别对应于不同的两种实现。

      MediaCodec

       从API 16开始,Android提供了Mediacodec类以便开发者更加灵活的处理音视频的编解码。MediaCodec就属于硬编码。MediaCodec类可用于访问Android底层的媒体编解码器。它是Android底层多媒体支持基本架构的一部分(通常与MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, 以及AudioTrack一起使用)。

       从广义上讲,一个编解码器通过处理输入数据来产生输出数据。MediaCodec采用异步方式处理数据,并且使用了一组输入输出缓存(buffer)。在简单的层面,你请求或接收到一个空的输入缓存(buffer),向其中填充满数据并将它传递给编解码器处理。编解码器处理完这些数据并将处理结果输出至一个空的输出缓存(buffer)中。最终,你请求或接收到一个填充了数据的输出缓存(buffer),使用完其中的数据,并将其释放回编解码器。

       优点:功耗低,硬件编解码速度快

       缺点:扩展性不强,不同芯片厂商提供的支持方案不同,导致程序移植性差,实测发现很多手机的YUV码流不一致,还会出现硬编码后偏色的问题

       适用场景:有固定的硬件方案,无需移植(如智能家具产品);需要长时间摄像的场景,如手机摄像头实时取景,这种场景软解码的处理速度跟不上。

       FFmpeg

       FFmpeg是一套用来记录、转换数字音频、视频,并能将其转化为流的开源程序。FFmpeg有非常强大的功能,包括视频采集功能、视频格式转换、视频抓图、视频加水印等。FFmpeg在Linux平台下开发,但它也可以在其它操作系统中编译运行,包括Windows、OS X等。

       FFmpeg堪称自由软件中最完备的一套多媒体支持库,它几乎实现了所有当下常见的数据封装格式、多媒体传输协议以及音视频编解码器。可以直接通过命令行使用FFmpeg,它提供了三个命令行工具,ffmpeg用于转码,ffplay用于播放,ffprobe用于查看文件格式。

       实际上,除去部分具备系统软件开发能力的大型公司(Microsoft、Apple等)以及某些著名的音视频技术提供商(Divx、Real等)提供的自有播放器之外,绝大部分第三方开发的播放器都离不开FFmpeg的支持,像Linux桌面环境中的开源播放器VLC、MPlayer,Windows下的KMPlayer、暴风影音以及Android下几乎全部第三方播放器都是基于FFMpeg的。可以这么说,只要做视频音频开发都离不开FFmpeg。

       优点:(1)封装了很多的格式,使用起来比较灵活,兼容性好;(2)命令行的方式使用非常方便,比如视频裁剪的步骤:ffmpeg -ss 10 -t 20 -i INPUT -acodec copy -vcodec copy OUTPUT,相对来说比写函数的方式要简单很多。(3)功能十分强大

       缺点:(1)学习成本有点高(2)软编解码功耗大,占用CPU较多,效率偏低

       适用场景:跨平台使用(如不同芯片厂商的手机);短时间拍摄。

        因此,从整个行业来看,由于Android平台的碎片化,以及各个厂商硬件的差异化,直接使用MediaCodec进行视频硬编码的实现会遇到各种兼容性问题,因此大部分都是直接使用ffmpeg进行视频处理,或多或少进行一些性能和功能上的优化。

获取视频帧

        Android系统提供了统一的MediaMetadataRetriever类,用于从一个输入媒体文件中取得帧和元数据(时间、名字、长度等等)。如果是视频文件,还可以通过getFrameAtTime方法取得指定time位置的Bitmap。

        先看方法原型:public Bitmap getFrameAtTime(long timeUs, int option) ,第一个参数是传入时间,单位是us。

        第二个参数可以设置为下面四个值:

OPTION_CLOSEST    在给定的时间,检索最近一个帧,这个帧不一定是关键帧

OPTION_CLOSEST_SYNC    在给定的时间,检索与当前时间最近的关键帧

OPTION_NEXT_SYNC  在给定时间之后检索一个关键帧

OPTION_PREVIOUS_SYNC   在给定时间之前检索一个关键帧

       MediaMetadataRetriever有几个问题导致使用并不是很方便。

1、获取的Bitmap是与原视频尺寸相同的,图片会比较大,并没有提供获取压缩的图片的接口。

2、获取的帧的速度较慢,测试下来获取一帧的时间为250ms左右。

3、即使使用OPTION_CLOSEST获得的也是有一定偏差的,并不能保证获取指定时间点的帧。

       因此,实际实现中,通常使用ffmpeg来获取视频帧,不仅可以获取任意大小的图片,速度也有比较大的提升,使用ffmpeg通常可以精确获得指定时间的帧。

MediaPlayer seek误差问题

       Android使用VideoView播放视频的时候,常常会调用seek方法去移动视频播放位置,但是有时候却不能准确的定位到我们想要的位置。通常会比我们指定的位置回退几秒(在有的手机上可能会前进几秒)。

       这是由于seek操作跳转的位置其实并不是参数所带的position,而是离position最近的关键帧。为了提高seek的速度,MediaPlayer在seek到指定postion位置时,会去找最近的关键帧,然后从关键帧的位置开始播放。由于不同的手机实现的差别,可能向前也可能向后寻找关键帧,因此就出现了指定seek位置后,播放位置前进或者后退的情况。

       如果要解决seek不准的问题,需要修改播放器seek的策略,seek到关键帧后,然后循环读帧,直到你需要的位置,然后输出这一帧的完整图像信息。但是,这样处理seek的速度相对就会慢一点。