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

Android MediaCodec解析 MediaCodecAndroid平台提供的一个底层的视频解码框架,它是安卓底层多媒体基础框架的重要组成部分。 阅读详情

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 B
 codec.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文件,这部分我们可以通过

面试精选。进阶。视频教程~更多面试详情请关注我的主页查询。

Android MediaCodec简单总结 #.MedaiCodec简介 MediaCodecAndroid中提供的视频编码、解码工具。它主要是完成上层接口的封装,提供给开发者使用,解码功能实际是在native底层服务中完成的。 #.MediaCodec工作的宏观流程: ##.包换两个缓冲区队列 一个输入缓冲区队列,包含一组输入缓冲区(格式ByteBuffer); 一个输出缓冲区队列,包含一组输出缓冲区(格式ByteBuffer); ##.使用中,需要不断重复以下过程: 1.把原始数据放入输入缓冲区队列中一个.. 阅读详情

相关推荐

Android MediaCodec 编码使用介绍

编码篇:1、获取解码器信息首先我们需要获取Codec的信息,通过MediaCodecList这个类来实现获取到MediaCodecInfo,代码示例如下:i++) {if (!continue;j++) {这里我们需要设置mimeType,这是一个字符串比如我们需要设置avc格式的视频那么我们可以将其设置为然后选择合适我们的编码器2、设置编码参数设置编码参数通过mediaFormat来实现,代码·示例如下:首先创建MediaFormat实例。

superjaingchao的专栏 2259

Android MediaCodec

MediaCodecAndroid视频中相当重要的一个API。MediaCodec类可以用于使用一些基本的多媒体解码器(视频解码组件),它是Android基本的多媒体支持基础架构的一部分通常和 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, and AudioTrack 一起使用。广义而言,解码器处理输入数据以生成输出数据。它异步处理数据并使用一组输入和输出缓冲区。

liuning1985622的博客 2388

面试问到硬编码软编码区别,如何选取?(1)

我们来逐个解析一下###一.MediaCodecMediaCodec是API 16之后Google推出的用于视频解码的一套偏底层的API,可以直接利用件加速进行视频的解码。

2401_84545618的博客 1507

硬编码软编码区别

Android 视频 硬编码软编码区别 软编码硬编码如何区分 软编码:使用CPU进行编码 硬编码:使用非CPU进行编码,如显卡GPU、专用的DSP、FPGA、ASIC芯片等 软编码硬编码比较 软编码:实现直接、简单,参数调整方便,升级易,但CPU负载重,性能较硬编码低,低码率下质量通常比硬编码要好一点 硬编码:性能高,低码率下通常质量低于软编码器,但部分产品在GPU件平台移植了优秀的...

初心一点的专栏 1万+

2024阿里、腾讯、字节跳动Android高级面试题小整理,讲的太透彻了

最后,如果大伙有什么好的学习方法或建议欢迎大家在评论中积极留言哈,希望大家能够共同学习、共同努力、共同进步。

m0_61943758的博客 1076

2019年Android 高级面试

Android 高级面试集合 面试地址GitHub地址 喜欢的话 点个start哦 2019年Bat面试集合 架构相关面试 NDK相关面试 算法相关面试 高级UI相关面试 性能优化相关面试 专业领域相关面试 其他 2019年Bat面试集合 阿里巴巴面试集合 谈谈线程池原理 垃圾回收机制是如何实现的 https原理你知道吗 Hadler是如何实现线程通信的 Glide对Bitmap的缓存...

鸿蒙开发知识记录 2732

android热修复视频,带你快速通过字节跳动面试面试心得体会_ 热修复

开发是需要一定的基础的,我是08年开始进入Android这行的,在这期间经历了Android的鼎盛时期,和所谓的Android”凉了“。中间当然也有着,不可说的心酸,看着身边朋友,同事一个个转前端,换行业,其实当时我的心也有过犹豫,但是我还是坚持下来了,这次的疫情就是一个好的机会,大浪淘沙,优胜劣汰。快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2401_84132685的博客 710

Android 开发一比一年难做!面试题都这么难了

再来梳理总结一下:当 Client 进程向 ServiceManager 查询 Server 进程(我要调 用你的某个对象的某个方法了),这个过程也是一个跨进程通信的过程,也经过 了 Binder 驱动,这时 Binder 驱动发挥它的作用,来了个狸猫换太子,将 Server 进程中的真实对象转换成代理对象,返回这个代理对象给 Client 进程。

2401_84132370的博客 649

2019阿里、腾讯、字节跳动Android高级面试题小整理,想转行当程序员的必看

视频同步原理,视频能绝对同步吗 硬编码软编码区别,录屏时如何选取 有做过直播特效吗,说说原理和难点 include、merge、ViewStub的作用和原理 OpenCV中定位人脸的五个点怎么做到 为什么RecyclerView加载首屏会慢一些 View绘制机制,onMeasure、onLayout、onDraw方法的调用机制 为什么Android会出现卡顿 ThreadLocal底层原理和Handler的关系 Flutter为什么会做到处处运行,RN的区别 Flutter的图形引擎和A.

m0_66144992的博客 1014

android常见面试题及答案,,层层深入

点击传送门。

H1767410的博客 868

最全面试考点面试技巧,真香

前言 如果你也学习Android,那么你大概率会看过我的文章。经常有读者给我留言:“该怎么学习Android?”、“日常学习Android的方法是什么”。 所以,今天,我将献上一份《Android知识图谱》,以自身的经验 & 所见所闻,旨在告诉大家,学习Android,实际上需要学习什么内容,希望你们会喜欢。 阿里巴巴面试整理 线程原理 垃圾回收机制的实现 Https原理 Handler实现线程通信 Glide对Bitmap的缓存源码复用如何做到 给你一个Demo如何快速定位ANR

chayel123的博客 110

被大佬问到自闭!Android面试必备的集合源码详解,成功拿下大厂offer

本专栏专注分享大型Bat面试知识,后续会持续更新,喜欢的话麻烦点击一个关注 面试官: ButterKnife为什么执行效率为什么比其他注入框架高?它的原理是什么 心理分析: ButterKnife框架一直都是使用,很少又开发者对butterknife深入研究的,既然你是面试Android高级岗位,自然需要有相应被问到原理的准备,面试官想问你对注解处理器了解多少,Android译流程有多少认识 **求职者:**应该从 注解处理器原理 优势说起,肯定注解处理器对解放生产力的作用。然后可以引申常见的 But

clhcowboy的博客 343
上一篇: 我想跳槽了,该怎么办?
下一篇: Flutter 完整示例
初壹十五a
博客等级 码龄7年 146粉丝 257原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值