应广大读者要求,已经将Video-recorder与Video-Player项目迁移到AndroidStudio环境

https://github.com/zhanxiaokai/Android-as_video_recorder

https://github.com/zhanxiaokai/Android-as_video_player

应广大读者要求,已经将Android、iOS、PC平台的编译脚本上传到github上了

https://github.com/zhanxiaokai/cross_compile_project

购买地址

在线试读地址

为什么要写这本书

整个音视频领域的架构以及开发已经演进了很长的时间,从最开始的广电领域,到PC端的音视频领域,再到本书所介绍的移动端的音视频领域。在这几年中,移动端的音视频领域架构的演进也是巨大的。在移动互联网的发展热潮中,我有幸从事了音视频领域的设计与开发,并且是就职于最时尚的手机KTV—唱吧,这家充满人性化的公司,从而使得我开发出来的东西能够服务于几亿用户。对于音视频的移动端应用,不论是开发还是使用,在近两年内都达到了一个高峰,而作为一名工程师,如何高效地开发出一个音视频App,是一件非常困难的事情,特别是对于对音视频概念不太了解的工程师。我从事软件开发已有7年多的时间,接触音视频领域也已经有5年多的时间,在这5年多的开发过程中,每个时间段都会遇到新的挑战,尤其是在最开始涉足音视频领域的时候,真可谓举步维艰,首先对于音视频的基础概念不是特别清楚,再者在工作中边学边做,很难对整个音视频领域有一个全面的了解,并且市面上没有相关成熟的资料从更高的层次来介绍音视频领域在移动端的演进与发展。这几年的设计实战与开发经验,以及带新人入门的众多感触,让我有了写这本书的动力,同时也是这本书的和核心内容。我希望通过本书可以帮助到更多想要在移动端音视频领域上做出自己想法App的工程师,让大家可以顺利地建立起自己的音视频App。我非常希望能为刚入门的同学或者遇到困难的同学提供帮助,希望大家可以享受整个开发的过程、享受自己开发的产品为人们生活带来便利的成就感。另外从整个音视频开发领域来讲,我也十分希望能够通过本书贡献出自己的绵薄之力。

读者对象

  • 产品经理,这部分读者可以从中了解到音视频在移动端开发实践过程中遇到的很多问题以及对应的优化策略, 例如:如何通过音视频的统计数据为产品提供更加流畅的策略(视频观看的秒开、直播推流的流畅度、视频上传的成功率等)。
  • 项目经理,这部分读者可以从中了解到很多时下流行的名词与概念,不再会因为几个专业名词就让自己不知所措,并且可以有助于更好地评估音视频项目开发中的风险与进度。
  • 测试人员,这部分读者可以从中了解到音视频App中由于处理过程不同而导致的瓶颈问题,当然书中也提到了一些自动化测试相关的命令以及工具,可以对CPU的Load情况、内存的占用情况、内存泄漏问题等进行分析。
  • 架构师与工程师,这部分读者只需要一点移动开发经验就可以阅读本书了;当然如果你已经是一个Senior的移动开发工程师或者架构师的话,那么你读起本书来将更加游刃有余;再进一步,如果你已经是移动领域的音视频开发工程师了,那么恭喜你,我们之间将会有一场关于技术领域内部的对话。
  • 开设相关课程的大学院校。

如何阅读本书

为了避免说教式的讲解给读者带来枯燥乏味的阅读体验,本书会有大量的实例及生产环境下的案例。本书将分为四大部分,第一部分是入门篇,从理论基础开始讲解,最终会产生两个实践项目;第二部分是提高篇,会基于第一部分的项目添加特效,形成一个完整的多媒体项目;第三部分是提高篇,会结合当下比较流行的直播场景进行实际案例分析;第四部分是工具篇,其中介绍了当下大部分可以提高开发以及测试人员效率的工具。下面是各个章节的基本介绍。

第1章,对音视频的基础概念进行介绍,其中包括音视频的基础数据格式,编码后的数据格式以及不同格式之间的相互转换等。

第2章,从零开始讲解如何搭建一个iOS项目和Android项目,并且添加C++支持,因为音视频领域的开发毕竟有相当一部分的代码需要用C++来书写,这样就可以做到两个平台(Android和iOS平台)共用一套代码仓库,以提升开发效率。然后是讲解交叉编译,因为在音视频开发过程中会用到很多第三方开源库,如果将这些库编译到我们的项目中,势必就要进行交叉编译,因此本章会重点讲解这些内容。

第3章,将探讨ffmpeg开源库,对于音视频开发来讲,ffmpeg开源库是众所周知也是普遍使用的,本章首先从编译开始,接着是命令行使用,再到源码结构介绍,以及最后的API调用,以层层递进的方式对ffmpeg开源库展开探讨。

第4章,将讲解如何利用各自平台的API进行声音与画面的渲染以及解码,对于画面的渲染,推荐使用OpenGL ES,并且两个平台可以使用同一个代码仓库。

第5章,有了前四章的基础,我们已经完全可以构建起一个视频播放器了,本书最大的特点就是经过几章基础知识的学习就能立即开始一个项目的实践,通过本章的视频播放器项目,我们将会熟悉播放器是如何工作的。

第6章,本章会重点介绍音视频的采集与编码器。特别是硬件编解码器在各个平台上的使用,使得我们的应用能够更高效(耗电更少、发热更少、界面更流畅)地运行在用户的手机之上。

第7章,有了之前的基础,本章可以继续开发一个视频录制的新项目,该项目可以使得我们更加熟悉音视频应用在各个平台下的实现。

第8章,将讲解如何处理音频流,毕竟让别人听采集出来的干声是很不礼貌的,本章将利用各种特效来美化采集的声音。

第9章,将讲解如何处理视频流,使视频中的颜值变得更高,毕竟爱美之心人皆有之,本章将介绍如何对视频实现美颜滤镜。

第10章,将在第7章的项目基础之上,增加第8章的音频特效,第9章的视频特效,从而构建一个实际生产过程中的多媒体应用。

第11章,继续以项目作为驱动,详细讲解如何基于之前学习的内容构建一个直播的应用,本章将重点介绍推流以及拉流端,同时还会涉及礼物特效、聊天以及第三方云服务的内容介绍。

第12章,由于一个直播应用是很难在一章的篇幅中讲解完毕的,所以本书会将一些核心的处理放在本章中逐个进行讲解。

第13章,将介绍我和我的同事们经常用到的工具和排错方法,与大家分享在日常开发中如何才能更有效率地解决问题,本章并不仅限于音视频的开发领域。

附录里面会有本书的代码以及很多工具的参考内容。

勘误和支持

由于作者的水平有限,编写的时间也很仓促,书中难免会出现一些错误或者不准确的地方,不妥之处在所难免,恳请读者批评指正。为此,我特意创建了一个在线支持与应急方案的二级站点http://music-video.cn。你可以将书中的错误,发布在Bug勘误表页面中,同时如果你遇到任何问题,也可以访问Q&A页面,我将尽量在线上为你提供最满意的解答。书中的全部源文件都将发布在这个网站上,我也会将相应的功能更新及时更正出来。如果你有更多的宝贵意见,也欢迎你发送邮件至我的邮箱zhanxiaokai2008@126.com,我很期待能够听到你们的真挚反馈。

致谢

感谢唱吧与唱吧的每一位同事,是这个公司让我的职业生涯发展到了今天,也是这个公司让我能在音视频领域达到今天的成就,可以说没有唱吧就不会有这本书的问世。

感谢唱吧里的每一位用户,感谢你们对唱吧的长期支持和贡献,没有你们就不会有唱吧的今天,也就不会有这本书的问世。

感谢我的老婆,感谢你对我工作以及写作的支持,是你在我背后默默做了很多事情,才让我把更多的时间和经历放到工作以及写作中。

感谢机械工业出版社华章公司的编辑Lisa老师,感谢你的魄力和远见,在这一年多的时间中始终支持我的写作,正是你的鼓励和帮助引导了我顺利完成全部书稿。

感谢互联网,我从不怀疑我们在互联网上迈出的任何一步都是人类历史向前迈进的一步,感谢众多互联网人的辛苦工作,为我们创造了这么多机遇。

谨以此书,献给我最亲爱的家人、同事,以及众多互联网从业者们。

展晓凯

中国,北京,2017年9月

“抖音”式的酷炫短视频开发进阶

原文链接

开始前先跟大家分享一个视频,这个Demo是基于iOS平台实现的,我们今天的分享也将聚焦在视频中多种特效的实现方法和经验总结。

应该如何实现

在实现Demo中特效前,我借鉴了funimate产品,利用它提供的功能生成视频,对它进行逐帧分析,并从中找出可能的实现方法。在视频分析中,使用了FFmpeg将视频分解成一帧一帧的图片从而进行分析。观察具体某一帧或者某几帧使用了怎样的特效。

ffmpeg –i output.mp4 –r 0.25 frames_%04d.png

具体到技术实现手段,第一种实现方式是把视频每一帧解码出的YUV,利用libyuv库来操作,甚至可以用RGBA来操作,这是通过CPU操作转换YUV来实现;第二种实现方法性能会更好,但开发成本可能也会相对较高,就是在GPU上操作纹理来实现。

由于我们需要在移动平台实现,而在移动平台使用CPU是很难满足需求的,要考虑到性能、耗电、实时观看体验等等因素,因此我们需要使用GPU来实现。

Demo场景设计

我们想要实现这样一个Demo或者简单的App,首先我们需要预览视频的界面,然后给出多种特效的选择菜单,当用户选择其中某种特效时会实时显示该特效的预览效果,并且将特效的开始作用时间和作用时长记录到内存的结构体中,最后当用户点击保存按钮时,可以离线保存为视频文件。

基于现有框架的开发

那么我们需要用到哪些已有的框架或者已有的项目来完成这个功能呢?可以思考下,既然有预览界面,则一定需要视频播放器。播放器的基本功能包括了解码和音视频的渲染,此外再加上逻辑控制、音视频对齐就可以成为一个视频播放器。

视频播放器中视频解码模块是非常重要的,通过它可以将视频文件解码为视频帧,并且输出到解码纹理队列中,接下来就是本App最核心的工作——处理,视频处理模块会按照时间戳将对应的纹理进行处理,并放入到渲染队列,最后输出模块会将渲染队列中的纹理输出到屏幕上,而在离线保存场景下,则是将渲染队列中的纹理编码输出到本地,也就是封装成mp4或者flv等等格式写入本地磁盘。

鉴于处理模块是本App的核心,而我们今天所讲的特效也都是在该模块中完成的,因此接下来我们一起来看下它的具体实现方法。

视频处理

  • 镜像

https://mmbiz.qpic.cn/mmbiz_png/icicA9AEakF1HgCYlA0cM6vyneSqDS6JCQQwgQ1DibG9zYsyY1ib43BTpjAiaiacZ2JTVCpfiamqibZic77R9g1XfIccLibg/640?wx_fmt=png&wxfrom=5&wx_lazy=1

首先跟大家分享一个最简单的特效——镜像,先生成一个16:9的屏幕比例的画布,将它分割为四部分,每部分画一个相同的视频帧,因为屏幕被分割为4部分,我们的物体坐标在渲染时就不能设定为全屏的。在OpenGL中物体坐标,左下角为(-1,-1),右上角为(1,1),这样我们就可以分别计算出4部分的物体坐标。

确认好物体坐标后,我们接下来就要确认画什么?也就是将视频帧以什么样的方式画在物体坐标上,这时就需要控制纹理坐标,我们可以看到OpenGL的纹理坐标定义:从左下角(0,0)到右上角(1,1),实际画的时候左上角是我们完整的纹理,右上角我们需要做镜像处理,左下角需要做横向翻转,右下角则是针对右上角视频帧做横向翻转,这样就可以实现简单的镜像效果。

  • 镜像模糊

https://mmbiz.qpic.cn/mmbiz_png/icicA9AEakF1HgCYlA0cM6vyneSqDS6JCQAwvv71yBo2K5LDdtE9hvS7hx17ibiathjowlaqmFjKzWcMeNOmCS4OBg/640?wx_fmt=png&wxfrom=5&wx_lazy=1

相对于前面的特效,这个特效只需要做一对镜像,但他的背景是需要做高斯模糊的,如果用CPU来做,通过两个大的“for”循环就可以实现,对于GPU也是相同的,不过代码会相对复杂一些。假如我们要计算中间25这个点的高斯模糊,我们需要先得出下图中的像素值,乘上各自点的高斯权重,然后做加权平均,最终把高斯模糊的效果放在下面成为背景,然后再将镜像的纹理画在上面就可以实现了。

  • 电击效果

在了解了两个简单的特效实现之后,我们一起来看一些复杂特效的实现方法,首先是电击效果,实际上它的实现就是反选的处理,只需要使用下面代码就可以:

gl_FragColor  = vec4((1.0 – texture.rgb), texture.w);

但想要达到一个很好的效果,其中还是有一些小技巧,也就是需要把握好节奏。假如我们现在有250ms运动的视频帧,再排上180ms静止的反选视频帧就可以实现了,如下方动图演示:假设50ms为一帧,那么对于10帧总时间为500ms的视频帧来说,前5帧都不变,依旧是正常的效果,从第6帧开始我们做反选并且保证画面是静止的,也就是说第7、8、9帧同样放第6帧,而第10帧时我们渲染正常的第10帧,这样周而复始就可以实现电击效果。

  • 灵魂出窍

https://mmbiz.qpic.cn/mmbiz_png/icicA9AEakF1HgCYlA0cM6vyneSqDS6JCQyG1FOGQuX7SvVYOIqK6bia0F3eTic2TNXibSjmwqCZSRU1JJVQsAhYMrg/640?wx_fmt=png&wxfrom=5&wx_lazy=1

这个特效就是人影有一个向外扩散的效果,同样它的节奏也是非常重要的,尤其是能与音乐的配合才能达到一个完美的效果。那么它的实现过程如下:首先我们每隔15帧拷贝一帧作为“灵魂”并且按照比例放大,这里特别需要提到的是SRT(Scale/Rotate/Translate),基于这三个的组合我们可以写一个TransformEffect,它可以利用通用的SRT矩阵变化纹理。

在得到放大后的“灵魂”(拷贝帧),我们就需要考虑把“灵魂”和“肉体”(原本视频帧)混合起来,这里需要用到GLES的一个内嵌Mix函数将两个纹理进行mix即可。那么同理,我们还可以实现眩晕、影随的效果:眩晕是将每一帧向两侧做位移再与本帧进行mix,而影随则是将之前的帧缓存下来,以一定的间隔和当前帧做mix。

  • 动态晕影

其实晕影效果在GPUImage中也有设置,它的实现首先需要构造一个纯黑色的图片,然后再与原始视频帧做mix就可以,在处理过程中有两点需要注意:首先交界处要做平滑处理,然后非常重要的依旧是节奏,我们Demo中的节奏时间列表如下:

https://mmbiz.qpic.cn/mmbiz_png/icicA9AEakF1HgCYlA0cM6vyneSqDS6JCQeqsWSPEeaK8ictNT6lyX6drDbyY1fLWiaLwzh2o6nY856wRsj69vPZYw/640?wx_fmt=png&wxfrom=5&wx_lazy=1

  • 木头人

木头人效果就是在视频中有一个bar——彩色且可动的区域,在bar区域以外则是静止且高斯模糊的,实现方法是每隔一定时间(Demo中是1.5s)冷冻一帧做高斯模糊处理,并且取灰度值放在后面,按照移动的边框距离将两帧进行mix。

https://mmbiz.qpic.cn/mmbiz_gif/icicA9AEakF1HgCYlA0cM6vyneSqDS6JCQPvtRY8azwbgjiagia2NuOia4WicHhz9hJxDcpJvicKKIH3W8AAoia2ibd8m2w/0?wx_fmt=gif&wxfrom=5&wx_lazy=1

  • 九宫格

九宫格效果中想要实现9个画面的效果可以参考第一个镜像特效的处理,而如何保证移动、放大、缩小时效果的平滑变化是最关键的,首先我们需要构建一个大纹理——相比原画长、宽分别扩大3倍,然后我们通过TransformEffect来进行位移、缩放。

  • 旋转木马

最后为大家介绍旋转木马特效,这也是本次分享中最复杂的,因为它的处理不再是简单的链式结构,而是graph。那么旋转木马特效其实就是四个画面中只有一个画面是彩色且可动的,其余三个都是黑白、静止的。我们假设左上角为1-3帧,右上角为4-6帧,左下角为7-9帧,右下角为10-12帧依次排列,那么在第1帧时,四个画面分别会显示1,4,7,10帧,而此时只有第一帧为彩色的,其余是黑白的,同时除左上角外其余三个画面都是冰冻状态。当左上角画面变为第3帧时,左上角画面变为黑白、静止,右上角的画面变为彩色、可动的,以此类推。

如上述视频所示,它的实现方法如下:首先每个画面都包含一个队列,然后我们把解码出来的视频帧以此按照左上、右上、左下、右下的顺序填充,当然在实现中可能以时间为依据会更加合理,当右下队列中有了第一帧时,我们才会绘制出第一帧效果也就是说特效才会开始,此时视频中显示的是第1,5,9,13帧,当左上绘制出第二帧时,解码器会将解码好的第14帧给到右下的队列中,以此类推。而当左上画面绘制出第4帧后,右上的队列开始绘制,同时解码器解码出来的视频帧将填充到左上的队列中,周而复始就能达到旋转木马的效果。

在绘制阶段有两个关键点:第一,对于活动区域而言,我们需要取出活动队列中的视频帧进行绘制,同时非活动区域取出队列中首帧进行灰度绘制;第二,对于填充区域来说,我们要按照当前时间戳与第一帧时间戳计算出填充区域,并且将当前帧入队到填充区域的队列中。

以上是针对demo中特效实现的讲解,非常感谢。