在日常生活中,视频类应用占据了我们越来越多的时间,各大公司也纷纷杀入这个战场,不管是抖音、快手等短视频类型,虎牙、斗鱼等直播类型,腾讯视频、爱奇艺、优酷等长视频类型,还是Vue、美拍等视频编辑美颜类型,总有一款适合你。
未来随着5G普及以及网络资费的下降,音视频的前景是非常广阔的。但是另一方面,无论是音视频的编解码和播放器、视频编辑和美颜的各种算法,还是视频与人工智能的结合(AI剪片、视频修复、超清化等),它们都涉及了方方面面的底层知识,学习曲线比较陡峭,门槛相对比较高,所以也造成了目前各大公司音视频相关人才的紧缺。如果你对音视频开发感兴趣,我也非常建议你去往这个方向尝试,我个人是非常看好音视频开发这个领域的。
当然音视频开发的经验是靠着一次又一次的“填坑”成长起来的,下面我们一起来看看俊杰同学关于音视频的认识和思考。
不管作为开发者还是用户,现在我们每天都会接触到各种各样的短视频、直播类的App,与之相关的音视频方向的开发也变得越来越重要。但是对于大多数Android开发者来说,从事Android音视频相关的开发可能目前还算是个小众领域,虽然可能目前深入这个领域的开发者还不是太多,但这个方向涉及的知识点可一点都不少。
1. 音视频相关的概念
说到音视频,先从我们熟悉也陌生的视频格式说起。
对于我们来说,最常见的视频格式就MP4格式,这是一个通用的容器格式。所谓容器格式,就意味内部要有对应的数据流用来承载内容。而且既然是一个视频,那必然有音轨和视轨,而音轨、视轨本身也有对应的格式。常见的音轨、视轨格式包括:
对于编码本身,上面提到的这些格式都是有损编码,因此压缩编码本身还需要一个衡量压缩之后,数据量多少的指标,这个标准就是码率。同一个压缩格式下,码率越高质量也就越好。更多Android本身支持的编解码格式,你可以参考官方文档。
小结一下,要拍摄一个MP4视频,我们需要将视轨 + 音轨分别编码,然后作为MP4的数据流,再合成出一个MP4文件。
2. 音视频编码的流程
接下来,我们再来看看一个视频是怎么拍摄出来的。首先,既然是拍摄,少不了跟摄像头、麦克风打交道。从流程来说,以H.264/AAC编码为例,录制视频的总体流程是:

我们分别从摄像头/录音设备采集数据,将数据送入编码器,分别编码出视轨/音轨之后,再送入合成器(MediaRemuxer或者类似mp4v2、FFmpeg之类的处理库),最终输出MP4文件。接下来,我主要以视轨为例,来介绍下编码的流程。
首先,直接使用系统的MediaRecorder录制整个视频,这是最简单的方法,直接就能输出MP4文件。但是这个接口可定制化很差,比如我们想录制一个正方形的视频,除非摄像头本身支持宽高一致的分辨率,否则只能后期处理或者各种Hack。另外,在实际App中,除非对视频要求不是特别高,一般也不会直接使用MediaRecorder。
视轨的处理是录制视频中相对比较复杂的部分,输入源头是Camera的数据,最终输出是编码的H.264/H.265数据。下面我来介绍两种处理模型。

第一种方法是利用Camera获取摄像头输出的原始数据接口(例如onPreviewFrame),经过预处理,例如缩放、裁剪之后,送入编码器,输出H.264/H.265。
摄像头输出的原始数据格式为NV21,这是YUV颜色格式的一种。区别于RGB颜色,YUV数据格式占用空间更少,在视频编码领域使用十分广泛。
一般来说,因为摄像头直接输出的NV21格式大小跟最终视频不一定匹配,而且编码器往往也要求输入另外一种YUV格式(一般来说是YUV420P),因此在获取到NV21颜色格式之后,还需要进行各种缩放、裁剪之类的操作,一般会使用FFmpeg、libyuv这样的库处理YUV数据。
最后会将数据送入到编码器。在视频编码器的选择上,我们可以直接选择系统的MediaCodec,利用手机本身的硬件编码能力。但如果对最终输出的视频大小要求比较严格的话,使用的码率会偏低,这种情况下大部分手机的硬件编码器输出的画质可能会比较差。另外一种常见的选择是利用x264来进行编码,画质表现相对较好,但是比起硬件编码器,速度会慢很多,因此在实际使用时最好根据场景进行选择。
除了直接处理摄像头原始数据以外,还有一种常见的处理模型,利用Surface作为编码器的输入源。
对于Android摄像头的预览,需要设置一张Surface/SurfaceTexture来作为摄像头预览数据的输出,而MediaCodec在API 18+之后,可以通过createInputSurface来创建一张Surface作为编码器的输入。这里所说的另外一种方式就是,将摄像头预览Surface的内容,输出到MediaCodec的InputSurface上。
而在编码器的选择上,虽然InputSurface是通过MediaCodec来创建的,乍看之下似乎只能通过MediaCodec来进行编码,无法使用x264来编码,但利用PreviewSurface,我们可以创建一个OpenGL的上下文,这样所有绘制的内容,都可以通过glReadPixel来获取,然后再讲读取数据转换成YUV再输入到x264即可(另外,如果是在GLES 3.0的环境,我们还可以利用PBO来加速glReadPixles的速度)。

由于这里我们创建了一个OpenGL的上下文,对于目前的视频类App来说,还有各种各样的滤镜和美颜效果,实际上都可以基于OpenGL来实现。
而至于这种方式录制视频具体实现代码,你可以参考下grafika中示例。
1. 视频编辑
在当下视频类App中,你可以见到各种视频裁剪、视频编辑的功能,例如:
对于视频裁剪、拼接来说,Android直接提供了MediaExtractor的接口,结合seek以及对应读取帧数据readSampleData的接口,我们可以直接获取对应时间戳的帧的内容,这样读取出来的是已经编码好的数据,因此无需重新编码,直接可以输入合成器再次合成为MP4。

我们只需要seek到需要裁剪原视频的时间戳,然后一直读取sampleData,送入MediaMuxer即可,这是视频裁剪最简单的实现方式。
但在实践时会发现,seekTo并不会对所有时间戳都生效。比如说,一个4min左右的视频,我们想要seek到大概2min左右的位置,然后从这个位置读取数据,但实际调用seekTo到2min这个位置之后,再从MediaExtractor读取数据,你会发现实际获取的数据上可能是从2min这里前面一点或者后面一点位置的内容。这是因为MediaExtractor这个接口只能seek到视频关键帧的位置,而我们想要的位置并不一定有关键帧。这个问题还是要回到视频编码,在视频编码时两个关键帧之间是有一定间隔距离的。

如上图所示,关键帧被成为I帧,可以被认为是一帧没有被压缩的画面,解码的时候无需要依赖其他视频帧。但是在两个关键帧之间,还存在这B帧、P帧这样的压缩帧,需要依赖其他帧才能完整解码出一个画面。至于两个关键帧之间的间隔,被称为一个 GOP,在GOP内的帧,MediaExtractor是无法直接seek到的,因为这个类不负责解码,只能seek到前后的关键帧。但如果GOP过大,就会导致视频编辑非常不精准了(实际上部分手机的ROM有改动,实现的MediaExtractor也能精确seek)。
既然如此,那要实现精确裁剪也就只能去依赖解码器了。解码器本身能够解出所有帧的内容,在引入解帧之后,整个裁剪的流程就变成了下面的样子。

我们需要先seek到需要位置的前一I帧上,然后送入解码器,解码器解除一帧之后,判断当前帧的PTS是否在需要的时间戳范围内,如果是的话,再将数据送入编码器,重新编码再次得到H.264视轨数据,然后合成MP4文件。
上面是基础的视频裁剪流程,对于视频拼接,也是类似得到多段H.264数据之后,才一同送入合成器。
另外,在实际视频编辑中,我们还会添加不少视频特效和滤镜。前面在视频拍摄的场景下,我们利用Surface作为MediaCodec的输入源,并且利用Surface创建了OpenGL的上下文。而MediaCodec作为解码器的时候,也可以在configure的时候,指定一张Surface作为其解码的输出。大部分视频特效都是可以通过OpenGL来实现的,因此要实现视频特效,一般的流程是下面这样的。

我们将解码之后的渲染交给OpenGL,然后输出到编码器的InputSurface上,来实现整套编码流程。
2. 视频播放
任何视频类App都会涉及视频播放,从录制、剪辑再到播放,构成完整的视频体验。对于要播放一个MP4文件,最简单的方式莫过于直接使用系统的MediaPlayer,只需要简单几行代码,就能直接播放视频。对于本地视频播放来说,这是最简单的实现方式,但实际上我们可能会有更复杂的需求:
对于第二种场景,我们可以简单配置播放视频的View为一个GLSurfaceView,有了OpenGL的环境,我们就可以在这上实现各种特效、滤镜的效果了。而对于视频编辑常见的快进、倒放之类的播放配置,MediaPlayer也有直接的接口可以设置。
更为常见的是第一种场景,例如一个视频流界面,大部分视频都是在线视频,虽然MediaPlayer也能实现在线视频播放,但实际使用下来,会有两个问题:
对于第一个问题,我们可以通过视频URL代理下载的方式来解决,通过本地使用Local HTTP Server的方式代理下载到一个指定的地方。现在开源社区已经有很成熟的项目实现,例如AndroidVideoCache。
而对于第二个问题来说,没法精确seek的问题在有些App上是致命的,产品可能无法接受这样的体验。那同视频编辑一样,我们只能直接基于MediaCodec来自行实现播放器,这部分内容就比较复杂了。当然你也可以直接使用Google开源的ExoPlayer,简单又快捷,而且也能支持设置在线视频URL。
看似所有问题都有了解决方案,是不是就万事大吉了呢?
常见的网络边下边播视频的格式都是MP4,但有些视频直接上传到服务器上的时候,我们会发现无论是使用MediaPlayer还是ExoPlayer,似乎都只能等待到整个视频都下载完才能开始播放,没有达到边下边播的体验。这个问题的原因实际上是因为MP4的格式导致的,具体来看,是跟MP4格式中的moov有关。

MP4格式中有一个叫作moov的地方存储这当前MP4文件的元信息,包括当前MP4文件的音轨视轨格式、视频长度、播放速率、视轨关键帧位置偏移量等重要信息,MP4文件在线播放的时候,需要moov中的信息才能解码音轨视轨。
而上述问题发生的原因在于,当moov在MP4文件尾部的时候,播放器没有足够的信息来进行解码,因此视频变得需要直接下载完之后才能解码播放。因此,要实现MP4文件的边下边播,则需要将moov放到文件头部。目前来说,业界已经有非常成熟的工具,FFmpeg跟mp4v2都可以将一个MP4文件的moov提前放到文件头部。例如使用FFmpeg,则是如下命令:
ffmpeg -i input.mp4 -movflags faststart -acodec copy -vcodec copy output.mp4
使用-movflags faststart,我们就可以把视频文件中的moov提前了。
另外,如果想要检测一个MP4的moov是否在前面,可以使用类似AtomicParsley的工具来检测。
在视频播放的实践中,除了MP4格式来作为边下边播的格式以外,还有更多的场景需要使用其他格式,例如m3u8、FLV之类,业界在客户端中常见的实现包括ijkplayer、ExoPlayer,有兴趣的同学可以参考下它们的实现。
音视频相关开发涉及面很广,今天我也只是简单介绍一下其中基本的架构,如果想继续深入这个领域发展,从我个人学习的经历来看,想要成为一名合格的开发者,除了基础的Android开发知识以外,还要深入学习,我认为还需要掌握下面的技术栈。
语言
框架
从实际需求出发,基于上述技术栈,我们可以从下面两条路径来深入学习。
1. 视频相关特效开发
直播、小视频相关App目前越来越多,几乎每个App相关的特效,往往都是利用OpenGL本身来实现。对于一些简单的特效,可以使用类似的Color Look Up Table
技术,通过修改素材配合Shader来查找颜色替换就能实现。如果要继续学习更加复杂的滤镜,推荐你可以去shadertoy学习参考,上面有非常多Shader的例子。
而美颜、美型相关的效果,特别是美型,需要利用人脸识别获取到关键点,对人脸纹理进行三角划分,然后再通过Shader中放大、偏移对应关键点纹理坐标来实现。如果想要深入视频特效类的开发,我推荐可以多学习OpenGL相关的知识,这里会涉及很多优化点。
2. 视频编码压缩算法
H.264/H.265都是非常成熟的视频编码标准,如何利用这些视频编码标准,在保证视频质量的前提下,将视频大小最小化,从而节省带宽,这就需要对视频编码标准本身要有非常深刻的理解。这可能是一个门槛相对较高的方向,我也尚处学习阶段,有兴趣的同学可以阅读相关编码标准的文档。
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p