原文链接:https://zhuanlan.zhihu.com/p/82130600

Android的视频相关的开发,大概一直是整个Android生态,以及Android API中,最为分裂以及兼容性问题最为突出的一部分。摄像头,以及视频编码相关的API,Google一直对这方面的控制力非常差,导致不同厂商对这两个API的实现有不少差异,而且从API的设计来看,一直以来优化也相当有限,甚至有人认为这是“Android上最难用的API之一”

以微信为例,我们录制一个540p的mp4文件,对于Android来说,大体上是遵循这么一个流程:

大体上就是从摄像头输出的YUV帧经过预处理之后,送入编码器,获得编码好的h264视频流。

上面只是针对视频流的编码,另外还需要对音频流单独录制,最后再将视频流和音频流进行合成出最终视频。

这篇文章主要将会对视频流的编码中两个常见问题进行分析:

1.视频编码器的选择(硬编 or 软编)?
2.如何对摄像头输出的YUV帧进行快速预处理(镜像,缩放,旋转)?

视频编码器的选择

对于录制视频的需求,不少app都需要对每一帧数据进行单独处理,因此很少会直接用到MediaRecorder来直接录取视频,一般来说,会有这么两个选择:
1.MediaCodec
2.FFMpeg+x264/openh264

我们来逐个解析一下

一.MediaCodec(硬编)

MediaCodec是API 16之后Google推出的用于音视频编解码的一套偏底层的API,可以直接利用硬件加速进行视频的编解码。调用的时候需要先初始化MediaCodec作为视频的编码器,然后只需要不停传入原始的YUV数据进入编码器就可以直接输出编码好的h264流,整个API设计模型来看,就是同时包含了输入端和输出端的两条队列:

因此,作为编码器,输入端队列存放的就是原始YUV数据,输出端队列输出的就是编码好的h264流,作为解码器则对应相反。在调用的时候,MediaCodec提供了同步和异步两种调用方式,但是异步使用Callback的方式是在API 21之后才加入的,以同步调用为例,一般来说调用方式大概是这样(摘自官方例子):

 MediaCodec codec = MediaCodec.createByCodecName(name);codec.configure(format, …);MediaFormat outputFormat = codec.getOutputFormat(); // option Bcodec.start();for (;;) {int inputBufferId = codec.dequeueInputBuffer(timeoutUs);if (inputBufferId >= 0) {ByteBuffer inputBuffer = codec.getInputBuffer(…);// fill inputBuffer with valid data…codec.queueInputBuffer(inputBufferId, …);}int outputBufferId = codec.dequeueOutputBuffer(…);if (outputBufferId >= 0) {ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A// bufferFormat is identical to outputFormat// outputBuffer is ready to be processed or rendered.…codec.releaseOutputBuffer(outputBufferId, …);} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {// Subsequent data will conform to new format.// Can ignore if using getOutputFormat(outputBufferId)outputFormat = codec.getOutputFormat(); // option B}}codec.stop();codec.release();

  

简单解释一下,通过getInputBuffers获取输入队列,然后调用dequeueInputBuffer获取输入队列空闲数组下标,注意dequeueOutputBuffer会有几个特殊的返回值表示当前编解码状态的变化,然后再通过queueInputBuffer把原始YUV数据送入编码器,而在输出队列端同样通过getOutputBuffersdequeueOutputBuffer获取输出的h264流,处理完输出数据之后,需要通过releaseOutputBuffer把输出buffer还给系统,重新放到输出队列中。

从上面例子来看的确是非常原始的API,由于MediaCodec底层是直接调用了手机平台硬件的编解码能力,所以速度非常快,但是因为Google对整个Android硬件生态的掌控力非常弱,所以这个API有很多问题:

1.颜色格式问题

MediaCodec在初始化的时候,在configure的时候,需要传入一个MediaFormat对象,当作为编码器使用的时候,我们一般需要在MediaFormat中指定视频的宽高,帧率,码率,I帧间隔等基本信息,除此之外,还有一个重要的信息就是,指定编码器接受的YUV帧的颜色格式。这个是因为由于YUV根据其采样比例,UV分量的排列顺序有很多种不同的颜色格式,而对于Android的摄像头在onPreviewFrame输出的YUV帧格式,如果没有配置任何参数的情况下,基本上都是NV21格式,但Google对MediaCodec的API在设计和规范的时候,显得很不厚道,过于贴近Android的HAL层了,导致了NV21格式并不是所有机器的MediaCodec都支持这种格式作为编码器的输入格式! 因此,在初始化MediaCodec的时候,我们需要通过codecInfo.getCapabilitiesForType来查询机器上的MediaCodec实现具体支持哪些YUV格式作为输入格式,一般来说,起码在4.4+的系统上,这两种格式在大部分机器都有支持:

MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar

  

两种格式分别是YUV420P和NV21,如果机器上只支持YUV420P格式的情况下,则需要先将摄像头输出的NV21格式先转换成YUV420P,才能送入编码器进行编码,否则最终出来的视频就会花屏,或者颜色出现错乱

这个算是一个不大不小的坑,基本上用上了MediaCodec进行视频编码都会遇上这个问题

2.编码器支持特性相当有限

如果使用MediaCodec来编码H264视频流,对于H264格式来说,会有一些针对压缩率以及码率相关的视频质量设置,典型的诸如Profile(baseline, main, high),Profile Level, Bitrate mode(CBR, CQ, VBR),合理配置这些参数可以让我们在同等的码率下,获得更高的压缩率,从而提升视频的质量,Android也提供了对应的API进行设置,可以设置到MediaFormat中这些设置项:

MediaFormat.KEY_BITRATE_MODE
MediaFormat.KEY_PROFILE
MediaFormat.KEY_LEVEL

  

但问题是,对于Profile,Level, Bitrate mode这些设置,在大部分手机上都是不支持的,即使是设置了最终也不会生效,例如设置了Profile为high,最后出来的视频依然还会是Baseline,Shit....
这个问题,在7.0以下的机器几乎是必现的,其中一个可能的原因是,Android在源码层级hardcode了profile的的设置:

// XXX
if (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline) {ALOGW("Use baseline profile instead of %d for AVC recording",h264type.eProfile);h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
}

  

Android直到7.0之后才取消了这段地方的Hardcode

if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) {....
} else if (h264type.eProfile == OMX_VIDEO_AVCProfileMain ||h264type.eProfile == OMX_VIDEO_AVCProfileHigh) {.....
}

  

这个问题可以说间接导致了MediaCodec编码出来的视频质量偏低,同等码率下,难以获得跟软编码甚至iOS那样的视频质量。

3.16位对齐要求

前面说到,MediaCodec这个API在设计的时候,过于贴近HAL层,这在很多Soc的实现上,是直接把传入MediaCodec的buffer,在不经过任何前置处理的情况下就直接送入了Soc中。而在编码h264视频流的时候,由于h264的编码块大小一般是16x16,于是乎在一开始设置视频的宽高的时候,如果设置了一个没有对齐16的大小,例如960x540,在某些cpu上,最终编码出来的视频就会直接花屏!

很明显这还是因为厂商在实现这个API的时候,对传入的数据缺少校验以及前置处理导致的,目前来看,华为,三星的Soc出现这个问题会比较频繁,其他厂商的一些早期Soc也有这种问题,一般来说解决方法还是在设置视频宽高的时候,统一设置成对齐16位之后的大小就好了。

二.FFMpeg+x264/openh264(软编)

除了使用MediaCodec进行编码之外,另外一种比较流行的方案就是使用ffmpeg+x264/openh264进行软编码,ffmpeg是用于一些视频帧的预处理。这里主要是使用x264/openh264作为视频的编码器。

x264基本上被认为是当今市面上最快的商用视频编码器,而且基本上所有h264的特性都支持,通过合理配置各种参数还是能够得到较好的压缩率和编码速度的,限于篇幅,这里不再阐述h264的参数配置

openh264则是由思科开源的另外一个h264编码器,项目在2013年开源,对比起x264来说略显年轻,不过由于思科支付满了h264的年度专利费,所以对于外部用户来说,相当于可以直接免费使用了,另外,firefox直接内置了openh264,作为其在webRTC中的视频的编解码器使用。

但对比起x264,openh264在h264高级特性的支持比较差:

  • Profile只支持到baseline, level 5.2
  • 多线程编码只支持slice based,不支持frame based的多线程编码

从编码效率上来看,openh264的速度也并不会比x264快,不过其最大的好处,还是能够直接免费使用吧。

软硬编对比

从上面的分析来看,

1.硬编的好处主要在于速度快,而且系统自带不需要引入外部的库,但是特性支持有限,而且硬编的压缩率一般偏低,
2.而对于软编码来说,虽然速度较慢,但是压缩率比较高,而且支持的H264特性也会比硬编码多很多,相对来说比较可控。就可用性而言,
3.在4.4+的系统上,MediaCodec的可用性是能够基本保证的,但是不同等级的机器的编码器能力会有不少差别,建议可以根据机器的配置,选择不同的编码器配置。视频流合流然后包装到mp4文件,这部分我们可以通过

转载于:https://www.cnblogs.com/1157760522ch/p/11506674.html

去抖音面试被问到硬编码与软编码区别,如何选取硬编与软编?相关推荐

  1. python一键去抖音视频水印工具,请勿用于学习以外的用途!

    一.前言 翻了翻爬虫代码存货,决定把抖音APP视频批量下载的代码拿出来做个文章. 二.实战背景 抖音越来越火,越刷越上瘾,总感觉下一个视频一定会更精彩,根本停不下来.想将抖音里喜欢的小哥哥/×××姐的 ...

  2. (3年工作经验)天天下班刷抖音,不如亲自跑去抖音瞧一瞧!

    面试准备,基础点到为止,只要不答得很差就没事.主要还是算法,基本上每轮两个算法题,白板写代码,要运行的那种,还是挺有压力. 不得不说字节面试效率很高.节奏很快,一下午一面二面连着面试,之后隔两天就三面 ...

  3. 先直播平台后抖音——不要再问用户需要什么

    标题可能并没有多少热度,至今(2019.3.2),直播平台和抖音短视频媒体已经不是什么新鲜事物,甚至前者已经处在资本收敛期,难以称为大繁荣.此短文仅记录在应用发展过程中,个人在产品思维上的感悟. 第一 ...

  4. 去抖音快手水印微信小程序

    最近闲来无事网上找了一个去快手抖音的微信小程序,改了改,还挺好使.欢迎大家尝试一下,点赞超过1000个分享源码和后台接口源代码.

  5. 看抖音快手时显示无法进入服务器,手机突然无法进入抖音直播怎么回事?解决进不去抖音直播的方法...

    我们在抖音中看到了不少人直播,有的是好看的小姐姐,有的是帅气的小哥哥,但是我们总是会遇见各种各样的问题,有的时候我们进不去直播间,这是为什么呢?接下来小编带你们走进抖音直播进不了解决方法. [抖音直播 ...

  6. 走,去抖音上发财!抖音承诺未来一年要帮一千万创作者赚到钱

    今天,抖音宣布推出"创作者成长计划",在未来一年,要帮助一千万名创作者在抖音赚到钱. 具体怎么做,抖音方面给出的回答如下: 首先是流量支持.平台将优化提升关注流量.本地流量的权重占 ...

  7. 如何去掉抖音短视频水印----全网最好用的去抖音视频水印方法

    抖音视频去水印最方便的方法是什么呢?就是直接保存.用去水印版的安装包,安装好之后,喜欢的视频直接保存就可以去水印,全网最好用的方法.不需要复制链接,解析视频再下载等一系列烦琐操作. 视频版操作方法在这 ...

  8. 抖音告白代码java,(新版失效)去抖音水印简单分析教程(附带java版代码)

    [Python] 纯文本查看 复制代码// 20200908095807 // https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_id ...

  9. php获取index.php,index.php · 阿彪/PHP去抖音水印解析-非接口(获取抖音无水印URL地址) - Gitee.com...

    // 你只需要修改这个地方的地址就可以了 $url = ('https://v.douyin.com/WuRMPV/'); $ch = curl_init(); curl_setopt($ch, CU ...

最新文章

  1. RHEL 5服务篇—部署DNS域名解析服务(一)BIND软件
  2. SharePoint KB
  3. 微信小程序函数调用监控
  4. [源码]解析 SynchronousQueue 上界,下界.. 数据保存和数据传递. 堵塞队列. 有无频繁await?...
  5. [QPlugins]学习大纲
  6. java switch小程序,小程序自定义switch组件
  7. CentOS 下 yum(基于rpm的包管理工具) 命令详解
  8. STS安装 activiti-designer-5.18.0插件
  9. java在什么环境变量_Java-环境变量
  10. 软件测试人员的核心竞争力
  11. 华为鸿蒙系统腾讯,腾讯内容开放平台
  12. 服务器重置密码后无法启动不了怎么办,Windows Server 2008 R2 忘记密码的处理方法...
  13. Server.MapPath() 探究
  14. Windows的同步I/O和异步I/O
  15. new 动态分配数组空间 .xml
  16. 厉害了我的Qunar!看我工程师写轮眼!
  17. python正则表达式match方法_python正则表达式match和search用法讲解
  18. Matlab主成分分析法
  19. 关于华为和荣耀手机升级鸿蒙系统之后无法连接magicbook多屏协同的问题
  20. Android的高德地图实现行政区域显示

热门文章

  1. linux 挂载小米路由器,不折腾会死:CentOS7访问小米路由(Samba服务)
  2. AI实战:用Transformer建立数值时间序列预测模型开源代码汇总
  3. 宠物赛道“新玩家” 鼻纹识别迈进新征程
  4. 2、NI-VISA驱动问题解决
  5. ASO优化之关于应用宝的关键词排名
  6. 【Elasticsearch选主流程】
  7. 江苏开放计算机绘图作业1,江苏开放大学计算机绘图形考1
  8. 函数指针的定义和初始化
  9. [Python]指定搜索关键字,通过网页获取bilibili的相关视频信息
  10. 调试Bluetooth时的一些札记