热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mediacodec压缩成h264数据_MediaCodec编码OpenGL速度和清晰度均衡

作者:VE视频引擎来源:https:blog.csdn.netweixin_41191739articledetails101210752概述在安卓平台

作者:VE视频引擎

来源:https://blog.csdn.net/weixin_41191739/article/details/101210752

概述

在安卓平台为了实现h264视频编码,我们通常可以使用libx264, ffmpeg等第三方视频编码库,但是如果对编码的速度有一定的要求,要实现实时甚至超实时的高速视频编码,我们并没有太多选项,只能使用Android提供的MediaCodec硬编码模块。

MediaCodec模块在实际使用中会遇到很多问题,本文主要讨论使用MediaCodec来对OpenGL渲染的画面进行编码视频时,如何达到速度快和画面清晰的均衡。

注意,本文将默认你已经熟悉使用MediaCodec,配合SurfaceTexture进行OpenGL画面编码的基本流程

分析影响编码速度的因素

除去设备硬件的因素,影响MediaCodec对视频画面进行编码的速度的其他因素并不多,我们实践探索下来主要发现以下几点:

  • 画面尺寸

画面尺寸就是要编码的视频画面的宽高了,它直接定义了每一帧要编码的画面数据的大小,所以它也主要决定了Mediacodec在进行每帧编码时的任务量。这个很容易理解。

  • 帧速率

帧速率指的是每一秒时长的视频所包含的静止画面的帧数。由于Mediacodec在编码时是以帧为单位,对每一个静止帧画面进行编码,所以,帧速率直接决定了MediaCodec在编码固定时长的视频时需要编码的画面数量。

比如,同样编码一段10秒钟的视频,在不考虑画面内容和其他因素的影响下,如果帧速率是30帧每秒,那么MediaCodec的编码工作量大致就是帧速率15帧每秒的情况的2倍,所以其耗时也大致是其2倍。

  • 同步模式下的Time Out设置

MediaCodec有两种工作模式,同步模式和异步模式,其中异步模式只有在Android 5.0以上的系统才支持。如果要兼容5.0以下的系统,那么就必须用到同步模式,而网上流传的众多对MediaCodec的编码流程讲解(比如经典的bigflake)也都是基于同步模式的。

那么在这些文档和案例指导下,大家在使用MediaCodec进行编码时,也都使用着大致相同的设置。

其中,同步模式下,在编码流程中,我们需要调用MediaCodec的dequeueOutputBuffer接口来从队列里获取编码输出缓冲区的内容。dequeueOutputBuffer接口的第二个参数,timeoutUs是一个以微妙为单位的时间值,它定义了从MediaCodec获取输出缓冲区内容时的超时期限。

目前流传的文档教程和案例中,大多将该值设置为10000微秒,也就是10毫秒。但是当我们把这个值降低到1毫秒甚至更低时,会发现,MediaCodec整个编码流程的速度会得到大幅度的提升,而基于我们的测试,编码结果也并未因此受到太多可视的影响。

当然,Android官方文档中也并未对timeoutUs的合适取值范围给出具体的建议,我这里得出的结论是在我们项目实际开发中测试得出,并且其结果满足我们的需求。

如果你的项目不需要保持对5.0以下系统的支持,我还是建议你使用异步模式来进行编码,这样可以直接规避这种同步等待的问题。

  • Encoder Profile

Profile是对视频压缩特性的描述,它主要是定义了编码工具的集合。Profile越高,就说明其采用了越高级的压缩特性。而通常,更高级的压缩方式通常需要耗费更多的压缩时间,也就是说,当profile设置的越高时,其编码耗费的时间往往更多。

例如,当我们在使用ffmpeg进行编码时,如果我们把编码配置设置为ultra-fast时,profile会被强制设置为baseline。

影响画面清晰度的因素

  • Encoder Profile Level

Profile是对视频压缩特性的描述,它主要是定义了编码工具的集合。Profile越高,就说明其采用了越高级的压缩特性,相应的,同样配置下所编码得到的视频文件的清晰度就越高,并且码流越小。

安卓系统支持的H264编码profile一共有3种

  1. Baseline Profile
  2. Main Profile
  3. High Profile

其中Baseline是从Android 3.0之后开始支持,据我们测试,Main Profile及更高的Profile的自定义设置时是从Android 7.0之后开始支持,之前版本Android系统调用相应接口对其设置均不起作用。而就算Android7.0以后的系统,也存在部分机型(比如部分OPPO和华为机型)不支持的情况。

所以如果你的产品需要保持对主流机型的支持,Profile的可靠选择也只有Baseline了。

  • Biterate

Biterate, 即视频码率,是视频数据(视频色彩量、亮度量、像素量)每秒输出的位数。一般用的单位是kbps。

通常情况下,码率越高则视频清晰度越高。但是由于人肉眼分辨的范围有限,码率设置过高所带来的画面清晰度替身也很难被人眼辨别,所以码率设置过高也没有意义。

通常情况下,我推荐一个公式来计算如何对编码器设置对应的码率:

Biterate = Width * Height * FrameRate * Factor

其中,Width、Height和FrameRate分别代表视频的宽度,高度和帧速率,而Factor则是一个系数,用来控制码率。

通常情况下,在网络流媒体使用场景中,可以将Factor设置为0.1~0.2,这样能在保证画面损失不严重的情况下生成出来的视频文件大小较小;

在普通本地浏览的使用场景中,可以将Factor设置为0.25~0.5,这样可以保证画面清晰度不会因为编码而造成过多肉眼可见的损失,这样生成出来的视频文件也相对较大;

在高清视频处理的使用场景中,可以将Factor设置为0.5以上。

不过,当视频画面颜色越丰富、画面变化越快时,视频编码需要的码率就更高,如果遇到这种视频画面场景,需要适当提高码率来保证清晰度。

需要注意的是,大多数安卓机型在对Bitrate的支持都有一个上限,如果设置的Bitrate值超过了上限,可能导致编码器抛出异常,进而编码流程失败。

所以在设计Bitrate的值时,需要通过MediaCodecInfo.CodecCapabilities提供的相关接口来检查系统支持的Bitrate上限。

  • Biterate Mode

虽然我们可以通过接口为编码器设置相关的码率,但是编码器在编码过程中如果处理我们设置的码率也是有几种不同的模式的。

  1. BITRATE_MODE_CQ

忽略用户设置的码率,由编码器自己控制码率,并尽可能保证画面清晰度和码率的均衡。

  1. BITRATE_MODE_CBR

无论视频的画面内容如果,尽可能遵守用户设置的码率

  1. BITRATE_MODE_VBR

尽可能遵守用户设置的码率,但是会根据帧画面之间运动矢量(通俗理解就是帧与帧之间的画面变化程度)来动态调整码率,如果运动矢量较大,则在该时间段将码率调高,如果画面变换很小,则码率降低。

所以,我们在设置码率的同时,也要注意对Bitrate Mode的设置。不同的设置对于生成出来的视频文件的大小和清晰度的影响都是不同的。

  1. 时间戳和帧速率

我们知道,在使用MediaCodec对OpenGL渲染内容进行编码的过程中,可以通过设置MediaFormat的MediaFormat.KEY_FRAME_RATE字段来设置编码器的帧速率;也可以通过设置MediaCodec.BufferInfo对象的presentationTimeUs值来设置每一帧画面的时间戳。

但是通常会忽略,我们在运行完OpenGL相关绘制命令,在调用swapbuffer之前需要调用eglPresentationTimeANDROID接口来设置当前帧的时间戳。

如果没有调用eglPresentationTimeANDROID来设置当前帧的时间戳,只设置了MediaCodec.BufferInfo对象的presentationTimeUs,编码产生的视频在播放时不会有任何时间上的异常,所以很多开发者往往忽略了eglPresentationTimeANDROID接口的调用。

但实际上,如果不调用eglPresentationTimeANDROID,编码出的视频在清晰度上会有额外的损失。

由于MediaCodec的设计是面向实时视频画面流编码的使用场景,所以MediaCodec会根据用户向其输入画面的速度来对编码的速度进行调节。如果我们不通过eglPresentationTimeANDROID来在编码之前对画面的时间戳进行设置,那么MediaCodec往往会将我们向其输入画面的速度默认为实时速度,来对编码速度进行调节。

这种调节会造成码率降低,视频画面清晰度降低。

当我们通过eglPresentationTimeANDROID在编码之前对画面的时间戳进行了正确的设置,MediaCodec会以实际每帧的时间戳为依据来对编码素材进行调节。

这样可以保证其不会对码率进行额外的降低,保证画面清晰度设置生效。

解决方案

结合以上的分析,为了当我们在使用MediaCodec进行视频编码时,为了达到编码速度和画面清晰度的平衡,我们需要在通过几个方面进行综合的优化。

  • Profile方法

综合上文的介绍,为了保证app在各个Android机型上的完美适配,其实我们在Profile方面能做的选择不多,最安全的情况是将Profile设置为Baseline。

  • Bitrate方法

Bitrate的设置我推荐使用Biterate = Width * Height * FrameRate * Factor的公式结合产品的使用场景进行设置。

  • Biterate Mode方法

Biterate Mode的默认设置是BITRATE_MODE_VBR,我推荐在系统和机型支持的情况下尽量将Biterate Mode设置为BITRATE_MODE_CQ。

在BITRATE_MODE_CQ情况下,编码器自身对码率和编码速度的调节往往能达到理想的效果,生成的视频文件不至于过多,但是画面清晰度优秀。

当然也要注意做好系统和机型的适配,进行异常处理,因为在某些机型上可能出现不支持BITRATE_MODE_CQ而导致MediaCodec在configure方法时失败,那么此时,我们需要回退到系统默认的Biterate Mode了。

  • 时间戳正确设置

我们在完成每帧OpenGL画面渲染,调用swapbuffer前一定要先调用eglPresentationTimeANDROID方法来正确设置该帧的时间戳。

eglPresentationTimeANDROID默认并不直接暴露,我们在包含相应头文件前先定义拓展宏来载入该函数。下面是载入eglPresentationTimeANDROID函数的相关代码:

#define EGL_EGLEXT_PROTOTYPES
#include 

eglPresentationTimeANDROID(mDisplay, mSurface, (EGLnsecsANDROID) time); // 在调用eglSwapBuffers先正确设置当前时间戳
eglSwapBuffers(mDisplay, mSurface);

086e195ac1caab178fafb0d2c5c79f4b.png

技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

e8883ae6b3d12fb4a740fd27c6d97d8e.png

推荐阅读:

音视频面试基础题

OpenGL ES 学习资源分享

开通专辑 | 细数那些年写过的技术文章专辑

NDK 学习进阶免费视频来了

推荐几个堪称教科书级别的 Android 音视频入门项目

觉得不错,点个在看呗~

b08bedd30ae00f21d9c2a73033af7a34.gif



推荐阅读
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • Windows7 64位系统安装PLSQL Developer的步骤和注意事项
    本文介绍了在Windows7 64位系统上安装PLSQL Developer的步骤和注意事项。首先下载并安装PLSQL Developer,注意不要安装在默认目录下。然后下载Windows 32位的oracle instant client,并解压到指定路径。最后,按照自己的喜好对解压后的文件进行命名和压缩。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
author-avatar
黄自安_725
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有