音视频大合集最终篇;学废了

4,011 阅读36分钟

前言

周五由于开了一下午会议,开完会就快下班了。就没来及更新内容,罪过罪过😂今天来更新下最后一篇。附带一些面试内容

10.19-24音视频中高级52部+面试
10.25-26高级Android组件化强化实战(一二)
10.27-11.3高级Android组件化强化实战(大厂架构演化20章)
中间所有的周六周日都休息😁

关注公众号:Android苦做舟 解锁 《Android十二大板块PDF》
音视频大合集,从初中高到面试应有尽有;让学习更贴近未来实战。已形成PDF版

十二个模块PDF内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试应有尽有
3.Android车载应用大合集,从零开始一起学
4.性能优化大合集,告别优化烦恼
5.Framework大合集,从里到外分析的明明白白
6.Flutter大合集,进阶Flutter高级工程师
7.compose大合集,拥抱新技术
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对工作需求
10.Android基础篇大合集,根基稳固高楼平地起
11.Flutter番外篇:Flutter面试+项目实战+电子书
12.大厂高级Android组件化强化实战

整理不易,关注一下吧。开始进入正题,ღ( ´・ᴗ・` ) 🤔

一丶音视频面试题

1.为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?

参考答案
  • 1)空间冗余:图像相邻像素之间有较强的相关性
  • 2)时间冗余:视频序列的相邻图像之间内容相似
  • 3)编码冗余:不同像素值出现的概率不同
  • 4)视觉冗余:人的视觉系统对某些细节不敏感
  • 5)知识冗余:规律性的结构可由先验知识和背景知识得到

2.怎么做到直播秒开优化?

参考答案
  • DNS 解析慢 为了有效降低 DNS 解析对首开的影响,我们可以提前完成播放域名->IP 地址的解析, 并缓存起来,播放的时候,直接传入带 IP 地址的播放地址,从而省去了 DNS 解析的耗时。 如果要支持用 IP 地址播放,是需要修改底层 ffmpeg 源码的。
  • 播放策略 很多侧重点播的播放器,为了减少卡顿,会有一些缓冲策略,当缓冲足够多的数据之后 ,再送入解码播放。

而为了加快首开效果,需要对播放的缓冲策略做一些调整,如果第一帧还没有渲染出来的情况下, 不要做任何缓冲,直接送入解码器解码播放,这样就可以保证没有任何因为「主动」缓冲带来的首开延时。

  • 播放参数设置 所有基于 ffmpeg 的播放器,都会遇到avformat_find_stream_info这个函数耗时比较久, 从而增大了首开时间,该函数主要作用是通过读取一定字节的码流数据, 来分析码流的基本信息,如编码信息、时长、码率、帧率等等,它由两个参数来控制其读取的数据量大小和时长, 一个是 probesize,一个是 analyzeduration。

减少 probesize 和 analyzeduration 可以有效地减少avformat_find_stream_info的函数耗时, 从而加快首开,但是需要注意的是,设置地太小可能会导致读取的数据量不足,从而无法解析出码流信息,导致播放失败, 或者出现只有音频没有视频,只有视频没有音频的问题。

  • 服务端优化
  • 服务器关键帧缓冲
  • CDN最近策略

3.图像可以提取的特征有哪些?

参考答案

颜色、纹理(粗糙度、方向度、对比度)、形状(曲率、离心率、主轴方向)、色彩等。

4.AAC和PCM的区别?

参考答案

AAC在数据开始时候加了一些参数:采样率、声道、采样大小

5.H264存储的两个形态?

参考答案
  • a. Annex B :

StartCode :NALU单元,开头一般是0001或者001 防竞争字节:为了区分 0 0 0 1,它采用0 0 0 0x3 1作为区分 多用于网络流媒体中:rtmp、rtp格式

  • b. AVCC :

表示NALU长度的前缀,不定长用4、2、1来存储这个NALU的长度 防竞争字节 多用于文件存储中mp4的格式

6.FFMPEG:图片如何合成视频

参考答案

编码流程:

  1. av_register_all
  2. 为AVFormatContext 分配内存
  3. 打开文件
  4. 创建输出码流AVSream
  5. 找到编码器
  6. 打开编码器
  7. 写文件头,没有的就不写入
  8. 循环编码视频像素数据->视频压缩数据
  • 循环编码音频采样数据->音频压缩数据 ———>AVFrame转化为AVPacket
  1. 将编码后的视频码流写入文件 ——>AVPacket转化为AVFormat函数
  2. 关闭编码器
  3. 写文件尾
  4. 关闭资源文件

解码流程:

  1. av_register_all
  2. 创建AVFormatContext的对象上下文
  3. 打开文件
  4. avformat_find_stream_info
  5. 找到解码器
  6. 打开解码器
  7. 创建AVCodecContext上下文
  8. av_read_frame :将avPacket数据转换为avFrame数据

glUniform1i() ——>这个可以设置对应纹理的第几层 glTexSubImage2D() 和glTexImage2D区别————>替换纹理的内容

7.常见的音视频格式有哪些?

参考答案
  1. MPEG(运动图像专家组)是Motion Picture Experts Group 的缩写。这类格式包括了MPEG-1,MPEG-2和MPEG-4在内的多种视频格式。
  2. AVI,音频视频交错(Audio Video Interleaved)的英文缩写。AVI这个由微软公司发布的视频格式,在视频领域可以说是最悠久的格式之一。
  3. MOV,使用过Mac机的朋友应该多少接触过QuickTime。QuickTime原本是Apple公司用于Mac计算机上的一种图像视频处理软件。
  4. ASF(Advanced Streaming format高级流格式)。ASF 是MICROSOFT 为了和的Real player 竞争而发展出来的一种可以直接在网上观看视频节目的文件压缩格式。
  5. WMV,一种独立于编码方式的在Internet上实时传播多媒体的技术标准,Microsoft公司希望用其取代QuickTime之类的技术标准以及WAV、AVI之类的文件扩展名。
  6. NAVI,如果发现原来的播放软件突然打不开此类格式的AVI文件,那你就要考虑是不是碰到了n AVI。n AVI是New AVI 的缩写,是一个名为Shadow Realm 的地下组织发展起来的一种新视频格式。
  7. 3GP是一种3G流媒体的视频编码格式,主要是为了配合3G网络的高传输速度而开发的,也是目前手机中最为常见的一种视频格式。
  8. REAL VIDEO(RA、RAM)格式由一开始就是定位在视频流应用方面的,也可以说是视频流技术的始创者。
  9. MKV,一种后缀为MKV的视频文件频频出现在网络上,它可在一个文件中集成多条不同类型的音轨和字幕轨,而且其视频编码的自由度也非常大,可以是常见的DivX、XviD、3IVX,甚至可以是RealVideo、QuickTime、WMV 这类流式视频。
  10. FLV是FLASH VIDEO的简称,FLV流媒体格式是一种新的视频格式。由于它形成的文件极小、加载速度极快,使得网络观看视频文件成为可能,它的出现有效地解决了视频文件导入Flash后,使导出的SWF文件体积庞大,不能在网络上很好的使用等缺点。
  11. F4V,作为一种更小更清晰,更利于在网络传播的格式,F4V已经逐渐取代了传统FLV,也已经被大多数主流播放器兼容播放,而不需要通过转换等复杂的方式。

8.在MPEG标准中图像类型有哪些?

参考答案

I帧图像, P帧图像, B帧图像

9.列举一些音频编解码常用的实现方案?

参考答案
  • 第一种方案:就是采用专用的音频芯片对 语音信号进行采集和处理,音频编解码算法集成在硬件内部,如 MP3 编解码芯片、语音合成 分析芯片等。使用这种方案的优点就是处理速度块,设计周期短;缺点是局限性比较大,不灵活,难以进行系统升级。
  • 第二种方案:就是利用 A/D 采集卡加上计算机组成硬件平台,音频编解码算法由计算机上的软件来实现。使用这种方案的优点是价格便 宜,开发灵活并且利于系统的升级;缺点是处理速度较慢,开发难度较大。
  • 第三种方案:是使用高精度、高速度 的 A/D 采集芯片来完成语音信号的采集,使用可编程的数据处理能力强的芯片来实现语音信号处理的算法,然后 用 ARM 进行控制。采用这种方案的优点是系统升级能力强,可以兼容多种音频压缩格式甚至未来的音频压缩格 式,系统成本较低;缺点是开发难度较大,设计者需要移植音频的解码算法到相应的 ARM 芯 片中去。

10.请叙述MPEG视频基本码流结构?

参考答案
  1. Sequence Header
  2. Sequence Extention
  3. Group of picture Header
  4. Picture Header
  5. Picture coding extension

11.说一说ffmpeg的数据结构?

参考答案

ffmpeg的数据结构可以分为以下几类:

  • (1)解协议(http,rtsp,rtmp,mms) AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。 URLProtocol存储输入音视频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)

  • (2)解封装(flv,avi,rmvb,mp4) AVFormatContext主要存储视音频封装格式中包含的信息 ffmpeg支持各种各样的音视频输入和输出文件格式(例如FLV, MKV, MP4, AVI),而 AVInputFormat和AVOutputFormat 结构体则保存了这些格式的信息和一些常规设置。

  • (3)解码(h264,mpeg2,aac,mp3) AVStream是存储每一个视频/音频流信息的结构体。 AVCodecContext: 编解码器上下文结构体,存储该视频/音频流使用解码方式的相关数据。 AVCodec: 每种视频(音频)编解码器(例如H.264解码器)对应一 个该结构体。 三者的关系如下图:

image.png

  • (4)存数据 对于视频,每个结构一般是存一帧;音频可能有好几帧

    • 解码前数据:AVPacket

    • 解码后数据:AVFrame

12.说一说AVFormatContext 和 AVInputFormat之间的关系?

参考答案

image.png

  • AVInputFormat被封装在AVFormatContext里

  • AVFormatContext 作为API被外界调用

  • AVInputFormat 主要是FFmpeg内部调用

  • AVFormatContext里保存了视频文件封装格式相关信息,它是负责储存数据的结构体。而AVInputFormat代表了各个封装格式,属于方法,这是一种面向对象的封装。

通过 int avformat_open_input(AVFormatContext **ps, const char filename,AVInputFormat fmt, AVDictionary options)函数装载解封装器. AVFormatContext 和 AVInputFormat之间的关系

13.说一说AVFormatContext, AVStream和AVCodecContext之间的关系?

参考答案

image.png AVStream和AVpacket中都有index字段用于区分不同的码流(视频、音频、字幕等),AVFormatContext中包含输入的AVStream数组用于记录各个码流,nb_streams记录输入码流的数量。AVCodecContext记录着AVStream需要用那种解码器来进行解码。

14.说一说视频拼接处理步骤?(细节处理,比如分辨率大小不一,时间处理等等)

参考答案

解封装、解码、决定分辨率大小、编码、时间处理、封装。

15.编解码处理时遇到什么困难?

参考答案

ffmpeg 在编解码的过程:

  • 使用 ffmpeg 的 libavcoder,libavformat 的库及可能性编解码
  • 编码过程:
    • 采集出来的视频、音频数据使用 ffmpeg 进行压缩
    • 进行按连续的视频,音频进行分组打包,为了区分各种包,也要包加上包头,包头写上显示时间戳 PTS,解码时间戳 DTS,通过网络传输到播放端
  • 解码过程:
    • 通过 TCP 协议接收到媒体流,FFmpeg 解封装,解码
    • 最终获取到原始视频 YUV,音频 PCM 格式,利用播放器进行播放

16.如何秒开视频?什么是秒开视频?

参考答案
  • 1.什么是秒开视频? 秒开是指用户点击播放到看到画面的时间非常短,在 1 秒之内。

  • 2.为什么需要秒开? 目前主流的直播协议是 RTMP,HTTP-FLV 和 HLS,都是基于 TCP 的长连接。在播放的过程中,若播放端所处的网络环境在一个较佳的状态,此时播放会很流畅。若网络环境不是很稳定,经常会发生抖动,如果播放端没有特殊处理,可能会经常发生卡顿,严重的甚至会出现黑屏。而移动直播由于其便捷性,用户可以随时随地发起和观看直播,我们无法保证用户的网络一直处于非常好的状态,所以,在网络不稳定的情况下保证播放的流畅度是非常重要的。

  • 3.解决思路

    • 获取关键帧后显示 改写播放器逻辑让播放器拿到第一个关键帧后就给予显示。 GOP 的第一个帧通常都是关键帧,由于加载的数据较少,可以达到 "首帧秒开"。如果直播服务器支持 GOP 缓存,意味着播放器在和服务器建立连接后可立即拿到数据,从而省却跨地域和跨运营商的回源传输时间。 GOP 体现了关键帧的周期,也就是两个关键帧之间的距离,即一个帧组的最大帧数。假设一个视频的恒定帧率是 24fps(即 1 秒 24 帧图像),关键帧周期为 2s,那么一个 GOP 就是 48 张图像。一般而言,每一秒视频至少需要使用一个关键帧。 增加关键帧个数可改善画质(GOP通常为 FPS 的倍数),但是同时增加了带宽和网络负载。这意味着,客户端播放器下载一个 GOP,毕竟该 GOP 存在一定的数据体积,如果播放端网络不佳,有可能不是能够快速在秒级以内下载完该 GOP,进而影响观感体验。 如果不能更改播放器行为逻辑为首帧秒开,直播服务器也可以做一些取巧处理,比如从缓存 GOP 改成缓存双关键帧(减少图像数量),这样可以极大程度地减少播放器加载 GOP 要传输的内容体积。
    • app 业务逻辑层面优化 比如提前做好 DNS 解析(省却几十毫秒),和提前做好测速选线(择取最优线路)。经过这样的预处理之后,在点击播放按钮时,将极大提高下载性能。

一方面,可以围绕传输层面做性能优化;另一方面,可以围绕客户播放行为做业务逻辑优化。两者可以有效的互为补充,作为秒开的优化空间。

  • 4.秒开视频方案

    • 优化服务器策略 播放器接入服务器请求数据的时间点的视频不一定是关键帧,那么需要等到下一个关键帧的到来,如果关键帧的周期是 2s 的话,那么等待的时间可能会在 0~2s 的范围内,这段等待的时间会影响首屏的加载时间。如果服务器有缓存,则播放端在接入的时候,服务器可以向前找最近的关键帧发给播放端,这样就可以省去等待的时间,可以大大的减少首屏的加载时间。
    • 优化播放端策略 播放端请求到的第一帧数据肯定是关键帧,关键帧能够通过帧内参考进行解码。这样播放端就可以在接收到第一个关键帧的时候就立即开始解码显示,而不需要等到缓存一定数量的视频帧才开始解码,这样也能减少首屏画面显示的时间。
  • 5.播放端首屏时长的优化 播放器的首屏过程中的几个步骤

    • 首屏时间,指的是从进入直播间开始到第一次看到直播画面的时间。首屏时间过长极易导致用户失去对直播的耐心,降低用户的留存。但游戏直播对画面质量和连贯性的要求高,对应推流端编码后的数据量和其他类型直播相比大的多,如何降低首屏时间是一个不小的难题。

    • 在播放端的首屏过程中,主要有以下三个操作需要进行:加载直播间 UI(包括播放器本身)、下载直播数据流(未解码)和解码数据播放。其中数据解码播放又细分为以下几个步骤:

    • 检测传输协议类型(RTMP、RTSP、HTTP 等)并与服务器建立连接接收数据; 视频流解复用得到音视频编码数据(H.264/H.265、AAC 等);

    • 音视频数据解码,音频数据同步至外设,视频数据渲染都屏幕,至此,视频开始播放,首屏时间结束。

总结: 首先,加载 UI 可以以单例的方式进行,能够一定程度地提升首屏展示速度;其次,可以预设解码类型,减少数据类型检测时间;再次,设定合理的下载缓冲区大小,尽可能减少下载的数据量,当检测到 I 帧数据,立即开始解码单帧画面进行播放,提高首屏展示时间。

17.如何降低延迟?如何保证流畅性?如何解决卡顿?解决网络抖动?

参考答案
  • 产生原因: 保证直播的流畅性是指在直播过程中保证播放不发生卡顿,卡顿是指在播放过程中声音和画面出现停滞,非常影响用户体验。造成卡顿的原因有几种情况:

推流端网络抖动导致数据无法发送到服务器,造成播放端卡顿; 播放端网络抖动导致数据累计在服务器上拉不下来,造成博凡卡顿。 由于从服务器到播放器的网络情况复杂,尤其是在 3G 和带宽较差的 WIFI 环境下,抖动和延迟经常发生,导致播放不流畅,播放不流畅带来的负面影响就是延时增大。如何在网络抖动的情况下保证播放的流畅性和实时性是保障直播性能的难点。

  • 流畅度优化: 目前主流的直播协议是 RTMP、HTTP-FLV 和 HLS,都是基于 TCP 的长连接。在播放的过程中,若播放端所处的网络环境在一个较佳的状态,此时播放会很流畅。若网络环境不是很稳定,经常会发生抖动,如果播放端没有做特殊处理,可能会经常发生卡顿,严重的甚至会出现黑屏。而移动直播由于其便捷性,用户可以随时随地发起和观看直播,我们无法保证用户的网络一直处于一个非常好的状态,所以,在网络不稳定的情况下保证播放的流畅度是非常重要的。

为了解决这个问题,首先播放器需要将拉流线程和解码线程分开,并建立一个缓冲队列用于缓冲音视频数据。拉流线程将从服务器上获取到的音视频流放入队列,解码线程从队列中获取音视频数据进行解码播放,队列的长度可以调整。当网络发生抖动时,播放器无法从服务器上获取到数据或获取数据的速度较慢,此时队列中缓存的数据可以起到一个过渡的作用,让用户感觉不到网络发生了抖动。

当然这是对于网络发生抖动的情况所采取的策略,如果播放端的网络迟迟不能恢复或服务器的边缘结点出现宕机,则需要应用层进行重连或调度。

18.H264/H265有什么区别?

参考答案

同样的画质和同样的码率,H.265比H2.64 占用的存储空间要少理论50%。如果存储空间一样大,那么意味着,在一样的码率下H.265会比H.264 画质要高一些理论值是30%~40%。

比起H.264,H.265提供了更多不同的工具来降低码率,以编码单位来说,最小的8x8到最大的64x64。信息量不多的区域(颜色变化不明显)划分的宏块较大,编码后的码字较少,而细节多的地方划分的宏块就相应的小和多一些,编码后的码字较多,这样就相当于对图像进行了有重点的编码,从而降低了整体的码率,编码效率就相应提高了。

H.265标准主要是围绕着现有的视频编码标准H.264,在保留了原有的某些技术外,增加了能够改善码流、编码质量、延时及算法复杂度之间的关系等相关的技术。H.265研究的主要内容包括,提高压缩效率、提高鲁棒性和错误恢复能力、减少实时的时延、减少信道获取时间和随机接入时延、降低复杂度。

19.视频或者音频传输,你会选择TCP协议还是UDP协议?为什么?

参考答案

选择UDP协议,UDP实时性好。TCP要保证丢失的package会被再次重发,确保对方能够收到。 而在视频播放中,如果有一秒钟的信号确实,导致画面出现了一点瑕疵,那么最合适的办法是把这点瑕疵用随便哪些信号补充上,这样虽然画面有一点点瑕疵但是不影响观看。如果用的TCP的话,这点缺失的信号会被一遍又一遍的发送过来直到接收端确认收到。这不是音视频播放所期待的。而UDP就很适合这种情况。UDP不会一遍遍发送丢失的package。

20.平时说的软解和硬解,具体是什么?

参考答案

硬解就是硬件解码,指利用GPU来部分代替CPU进行解码,软解就是软件解码,指利用软件让CPU来进行解码。两者的具体区别如下所示:

硬解码:是将原来全部交由CPU来处理的视频数据的一部分交由GPU来做,而GPU的并行运算能力要远远高于CPU,这样可以大大的降低对CPU的负载,CPU的占用率较低了之后就可以同时运行一些其他的程序了,当然,对于较好的处理器来说,比如i5 2320,或者AMD 任何一款四核心处理器来说,硬解和软件的区别只是个人偏好问题了吧。  

软解码:即通过软件让CPU来对视频进行解码处理;而硬解码:指不借助于CPU,而通过专用的子卡设备来独立完成视频解码任务。曾经的VCD/DVD解压卡、视频压缩卡等都隶属于硬解码这个范畴。而现如今,要完成高清解码已经不再需要额外的子卡,因为硬解码的模块已经被整合到显卡GPU的内部,所以目前的主流显卡(集显)都能够支持硬解码技术。

21.何为直播?何为点播?

参考答案

直播:是一个三方交互(主播、服务器、观众),这个交互式实时的!尽管会根据选择的协议不同而有一些延迟,但我们仍认为它直播是实时的!--->主播在本地发送音视频给服务器(推流),观众从服务器实时解码(拉流)收看收听主播发送给服务器的音视频(直播内容)。直播是不能快进的

点播:首先一定要明确的一点,点播不存在推流这一过程,你本身你的流已经早就推给服务器了,或者这么说也不对,应该是你的音视频早就上传到了服务器,观众只需要在线收看即可,由于你的音视频上传到了服务器,观众则可以通过快进,快退,调整进度条等方式进行收看!

22.简述推流、拉流的工作流程?

参考答案

推流:在直播中,一方向服务器发送请求,向服务器推送自己正在实时直播的数据,而这些内容在推送到服务器的这一过程中是以 “流” 的形式传递的,这就是“推流”,把音视频数据以流的方式推送(或上传)到服务器的过程就是“推流”! 推流方的音视频往往会很大,在推流的过程中首先按照 aac音频-编码 和 h264视【公众平台不能出现视频这两个字,真是坑】频-编码的标准把推过来的音视频压缩 ,然后合并成 MP4或者 FLV格式,然后根据直播的封装协议,最后传给服务器完成推流过程。

拉流:与推流正好相反,拉流是用户从服务器获取推流方给服务器的音视频的过程,这就是“拉流”!拉流首先aac音频-解码 和 h.264视频-解码的内部把推过来的音视频解压缩,然后合成 MP4或者 FLV 格式,再解封装,最后到我们的客户端与观众进行交互。

23.音频测试的测试点,音频时延如何测试?

参考答案

测试点:功能,性能,兼容性,耗电量,安全性,压力测试,客观音质POLQA分,音质主观体验,主播到观众时延,观众到观众时延,3A效果 音频时延:通过音频线将2个被测对象连接在电脑,用PESQ脚本算出音频时延

24.什么是GOP?

参考答案

GOP ( Group of Pictures ) 是一组连续的画面,由一张 I 帧和数张 B / P 帧组成,是视频图像编码器和解码器存取的基本单位。 也就是说GOP组是指一个关键帧I帧所在的组的长度,每个 GOP 组只有 1 个 I 帧。 GOP 组的长度格式也决定了码流的大小。 GOP越大,中间的P帧和B帧的数量就越多,所以解码出来的视频质量就越高,但是会影响编码效率。

25.为什么要有YUV这种数据出来?(YUV相比RGB来说的优点)

参考答案

RGB是指光学三原色红、绿和蓝,通过这3种的数值(0-255)改变可以组成其他颜色,全0时为黑色,全255时为白色。RGB是一种依赖于设备的颜色空间:不同设备对特定RGB值的检测和重现都不一样,因为颜色物质(荧光剂或者染料)和它们对红、绿和蓝的单独响应水平随着制造商的不同而不同,甚至是同样的设备不同的时间也不同。

YUV,是一种颜色编码方法。常使用在各个视频处理组件中。三个字母分别表示亮度信号Y和两个色差信号R-Y(即U)、B-Y(即V),作用是描述影像色彩及饱和度,用于指定像素的颜色。Y'UV的发明是由于彩色电视与黑白电视的过渡时期。黑白视频只有Y视频,也就是灰阶值。与我们熟知的RGB类似,YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。

YUV和RGB是可以相互转换的,基本上所有图像算法都是基于YUV的,所有显示面板都是接收RGB数据。

26.预测编码的基本原理是什么?

参考答案

预测编码是数据压缩理论的一个重要分支。根据离散信号之间存在一定相关性特点,利用前面的一个或多个信号对下一个信号进行预测,然后对实际值和预值的差(预测误差)进行编码。如果预测比较准确,那么误差信号就会很小,就可以用较少的码位进行编码,以达到数据压缩的目的。

原理:利用以往的样本值对新样本值进行预测,将新样本值的实际值与其预测值相减,得到误差值,对该误差值进行编码,传送此编码即可。理论上数据源可以准确地用一个数学模型表示,使其输出数据总是与模型的输出一致,因此可以准确地预测数据,但是实际上预测器不可能找到如此完美的数学模型;预测本身不会造成失真。误差值的編码可以采用无失真压縮法或失真压縮法。

27.DTS与PTS共同点?

参考答案
  • PTS就是Presentation Time Stamp也就说这个帧什么时候会放在显示器上;
  • DTS就是Decode Time Stamp,就是说这个帧什么时候被放在编码器去解。 在没有B帧的情况下,DTS和PTS的输出顺序是一样的。

28.请叙述AMR基本码流结构?

参考答案

AMR文件由文件头和数据帧组成,文件头标识占6个字节,后面紧跟着就是音频帧;

格式如下所示:

文件头(占 6 字节)| :--- | 语音帧1 | 语音帧2 | … |

  • 文件头: 单声道和多声道情况下文件的头部是不一致的,单声道情况下的文件头只包括一个Magic number,而多声道情况下文件头既包含Magic number,在其之后还包含一个32位的Chanel description field。多声道情况下的32位通道描述字符,前28位都是保留字符,必须设置成0,最后4位说明使用的声道个数。
  • 语音数据: 文件头之后就是时间上连续的语音帧块了,每个帧块包含若干个8位组对齐的语音帧,相对于若干个声道,从第一个声道开始依次排列。每一个语音帧都是从一个8位的帧头开始:其中P为填充位必须设为0,每个帧都是8位组对齐的。

29.sps和pps的区别?

参考答案

SPS是序列参数集 0x67 PPS是图像参数集 0x68 在SPS序列参数集中可以解析出图像的宽,高和帧率等信息。而在h264文件中,最开始的两帧数据就是SPS和PPS,这个h264文件只存在一个SPS帧和一个PPS帧。

30.讨论区

何为音视频同步,音视频同步如何同步?
说说你平时在播放过程中做的优化工作?
常见的直播协议有哪些?之间有什么区别?
美颜的实现原理,具体实现步骤?
如何测试一个美颜挂件?

二丶x264和NDK在Centos7上交叉编译

目标: 使用Centos 7编译出Android使用的X264的so库和头文件

准备: 不熟悉的可以看上一篇文章:Centos编译ffmpeg Android NDK: 本文示例用的是版本android-ndk-r20b,选择对应版本的下载就行。 注意:ndk18及以下用的是gcc编译的,ndk19及以上是用clang编译的,版本不一样编译脚本是不一样的。

下载x264

在根目录进入要下载到目录,我的是usr/ffmpeg/

下载x264源码(前提是已经安装git并存在环境变量)

git clone https://code.videolan.org/videolan/x264.git

下载完进入x264目录:

cd x264

修改x264源码,否则之后编译的时候会报错。

在x264目录进入common文件夹,找到cpu.c文件并编辑:

cd common
ls

image.png

修改代码cpu.c的442行:改为1,不然在编译时获取cpu数量会报错;

image.png

配置文件

注意:是在x264目录下新建shell脚本文件:build_android.sh

vim build_android.sh

按“i”进入编辑模式,将以下脚本内容粘贴在build_android.sh中

#!/bin/bash
 
# 修改为自己的NDK的解压路径
NDK=/usr/ffmpeg/android-ndk-r20b
 
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export API=21
 
function build
{
  ./configure \
    --prefix=$PREFIX \
    --disable-cli \
    --disable-static \
    --disable-asm \
    --enable-shared \
    --enable-pic \
    --host=$HOST \
    --cross-prefix=$CROSS_PREFIX \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
 
 
        make clean
        make 
        make install
}
 
 
CPU=armv8-a
HOST=aarch64-linux-android
export CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
export CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
export CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
build
 
 
CPU=armv7-a
HOST=armv7a-linux-android
export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
export CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU"
build
 
 
CPU=x86-64
HOST=x86_64-linux-android
export CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang
export CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
export CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel"
build

注意:记得修改自己的NDK路径

保存并退出:按esc退出编辑模式,输入“:wq” 保存退出,给build_android.sh添加执行权限:

chmod +x build_android.sh

至此配置完成。

编译so文件

执行命令:

./build_android.sh

编译完成后会在faac-1.29.9.2下生成android文件夹,里面有三个平台的so库。

image.png

将对应cpu的so和头文件拷贝到项目中,就可以愉快使用了,注意使用.so结尾的,不要使用.164版本号结尾的;

image.png

三丶faac和NDK在Centos7上交叉编译

目标

使用Centos 7编译出Android使用的faac的so库和头文件

下载faac

在根目录进入要下载到目录,我的是usr/ffmpeg/

cd usr/ffmpeg

下载

wget --no-check-certificate 'https://nchc.dl.sourceforge.net/project/faac/faac-src/faac-1.29/faac-1.29.9.2.tar.gz'

解压:

tar xvf faac-1.29.9.2.tar.gz

进入facc目录:

cd faac-1.29.9.2

配置文件

新建shell脚本文件:build_android.sh

vim build_android.sh

按“i”进入编辑模式,将以下脚本内容粘贴在build_android.sh中

#!/bin/bash
 
# 修改为自己的NDK的解压路径
NDK=/usr/ffmpeg/android-ndk-r20b
# macOS需要修改linux-x86_64为arch什么的,linux-x86_64是centos上用的
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
 
API=21
 
function build_android
{
echo "Compiling faac for $CPU"
./configure \
--prefix=$PREFIX \
--with-pic=yes \
--host=$HOST \
--enable-shared=yes \
--enable-static=no  \
--with-sysroot=$SYSROOT
 
make clean
make -j8
make install
echo "The Compilation of faac for $CPU is completed"
}
 
#armv8-a
CPU=armv8-a
HOST=aarch64-linux-android
export CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
export CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
export CFLAGS="-march=$CPU"
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
PREFIX=$(pwd)/android/$CPU
build_android
 
#armv7-a
CPU=armv7-a
HOST=arm-linux-androideabi
export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
export CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU"
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
PREFIX=$(pwd)/android/$CPU
build_android
 
#x86
# CPU=x86
# HOST=i686-linux-android
# export CC=$TOOLCHAIN/bin/i686-linux-android$API-clang
# export CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++
# SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
# PREFIX=$(pwd)/android/$CPU
# export CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
# build_android
 
#x86_64
CPU=x86-64
HOST=x86_64-linux-android
export CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang
export CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++
export CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel"
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
PREFIX=$(pwd)/android/$CPU
build_android

注意:记得修改自己的NDK路径

保存并退出:按esc退出编辑模式,输入“:wq” 保存退出,给build_android.sh添加执行权限:

chmod +x build_android.sh

至此配置完成。

编译so文件

执行命令:

./build_android.sh

编译过程比较快的,上边配置文件编译3个平台,大约30秒左右。

编译完成后会在faac-1.29.9.2下生成android文件夹,里面有三个平台的so库。

image.png

四丶Webrtc音视频通话

音视频通话难点:

  1. 音视频编解码原理
  2. IP4中,设备在各自的内网,需要p2p打洞
  3. 音频降噪和回声消除

信号服务器:

  1. 设备连接的socket服务器
  2. 传递各个设备之间的信息:传递各个节点的sdp信息,传递ice信息
  3. 包含业务功能:如加入、离开房间等

打洞服务器:

为什么打洞:IP4中,设备在各自的内网,各自的内网不能通信,而想要通信,就需要突破内网限制;

如果用服务器中转,则会加大服务器开销和增加延时;若不用中转,就需要点对点(p2p)打洞,来实现通信;

NAT:

  1. 网络地址转换
  2. 设备若想链接公网,需要经过路由转换,将私有IP转换为公网IP
  3. 在设备和路由中存在IP端口映射表NAPT

如:

1.设备A的某个应用私有IP端口为:192.168.1.10:9000; 2.设备A连接的路由器L的公网IP为:200.180.190.11; 3.那么在路由器L中会有一个端口映射设备A: 200.180.190.11:21111; 4.200.180.190.11:21111就是设备A的某个应用的公网IP地址和端口的映射;

**SDP:**描述了客服端到服务端通信的各个网络地址及端口等信息;

ps:一段文本,就像一个列表,记录了客户端到服务端,中转次数及各个中转点的路由信息;

两个设备实现通信要求:

设备A、设备A的路由AL、设备B、设备B的路由BL;

设备A若想给设备B发消息,AL需要知道B在BL中的IP和端口映射;

同理,设备B若想给设备A发消息,BL需要知道A在AL中的IP和端口映射;

打洞流程: 角色:信令服务器、打洞服务器、设备A、设备A的路由AL、设备B、设备B的路由BL;

  1. 通过信令服务器(socket服务器)将设备A到打洞服务器的各个sdp发送给设备B;
  2. 通过信令服务器(socket服务器)将设备B到打洞服务器的各个sdp发送给设备A;
  3. 设备A尝试发送一条消息到设备B,这条消息是发送不过去的;因为BL里没有A和AL的映射IP和端口;
  4. 但是此时AL会在自己的NAPT表中记录B和BL的映射IP和端口;
  5. 设备B发送一条消息给设备A,这条消息时可以发送到的。因为AL的NAPT表中有B和BL的映射IP和端口;并且BL会在自己的NAPT表中记录A和AL的映射IP和端口;
  6. 此时A和B就可以直接互相发送消息了,不再需要服务器了;

image.png

整个过程就是序号1->2->3->4->5->6->7->8>9 过程一:1->2 此过程为用户B向服务器请求向用户A打洞 过程二:3->4 此过程为服务器相应用户B的打洞请求,告诉用户A用户B想与你打洞(数据包中包含用户B的地址信息)。 过程三:5->6 用户A主动发一条信息给用户B,目的是为了使得路由器A中能够有一条关于路由B的IP的路由信息(注意不是用户B,用户B是私有IP),就如图所示,这条信息会被丢弃的,因为路由B的路由表中没有路由A的IP的信息。 过程四:7->8->9 用户B再发一条信息给用户A,因为此时路由A的路由表中有关于路由B的IP的路由信息,此时路由A就能路由给用户A了,至此,用户A就能直接收到用户B发的信息了。注意,此时用户A发给用户B不需要打洞,因为路由B中已经有关于路由A的IP的路由信息了。

Webrtc业务流程:

音视频一对一通话、多对多会议都是基于房间进行;

角色:信令服务器S、打洞服务器T、设备A(被请求)、设备A的路由AL、设备B(主请求)、设备B的路由BL;

第一种情况:

设备A进入房间时,没有其他设备;

  1. 设备A进入房间,信令服务器S给设备A返回设备A的唯一标识符socketId;
  2. 设备A开启本地预览:设置视频源、音频源、渲染到surface;
  3. 等待获取其他设备发送的请求offer...
  4. 准备sdp交换
  5. 设备A收到信令服务器S发送来的设备B的socketId和sdp;
  6. 设备A请求信令服务器,信令服务器返回设备A到打洞服务器的网络节点各个sdp
  7. 设备A设置本地sdp、设置收到的远端sdp;
  8. 设备A使用信令服务器S通过收到的socketId将自己的sdp发送给其他设备B;sdp交换完成
  9. 开始打洞:ICE交换
  10. 设备A通过异步消息获得打洞服务器返回的自己的ICE信息;
  11. 设备A通过信令服务器S将ICE信息发送给设备B;
  12. 等待设备B连接;
  13. ICE交换完成后
  14. 获取远端设备B的视频流、音频流,进行解码播放;

第二种情况:

设备B进入房间时,已有其他设备,如:设备A...;

此时设备B需要主动去呼叫设备A;

  1. 设备B连接信令服务器S;
  2. 设备B进入房间,信令服务器S给设备B返回设备B的唯一标识符socketId,和其他存在的设备的socketId列表;
  3. 设备B开启本地预览:设置视频源、音频源、渲染到surface;
  4. 开始sdp交换
  5. 设备B请求打洞服务器T,获取设备B到打洞服务器T的网络节点的各个sdp
  6. 设备B设置自己的sdp;
  7. 设备B使用信令服务器S通过socketId列表将自己的sdp发送给其他设备A;
  8. 设备A收到sdp后,设备A使用信令服务器S通过设备B的socketId,将自己的sdp发送给其他设备B;
  9. 设备B设置收到的远端sdp(可能有多个);sdp交换完成
  10. 开始打洞:ICE交换
  11. 设备A通过异步消息获得打洞服务器返回的自己的ICE信息;
  12. 设备A通过信令服务器S将ICE信息发送给设备B;
  13. 设备B根据收到的ICE信息去发送一个请求;此时设备B链接的路由器BL里就有了设备A在路由AL的IP和端口映射信息;ICE交换完成
  14. 设备B链接其他设备的视频流、音频流,进行解码播放;

至此,关于音视频就基本介绍稍微,还有其他的关于openGl ES应用篇,和基础篇的放在单独的openGL ES系列文档里,稍后再说

关注公众号:Android苦做舟 解锁 《Android十二大板块PDF》
音视频大合集,从初中高到面试应有尽有;让学习更贴近未来实战。已形成PDF版

十二个模块PDF内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试应有尽有
3.Android车载应用大合集,从零开始一起学
4.性能优化大合集,告别优化烦恼
5.Framework大合集,从里到外分析的明明白白
6.Flutter大合集,进阶Flutter高级工程师
7.compose大合集,拥抱新技术
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对工作需求
10.Android基础篇大合集,根基稳固高楼平地起
11.Flutter番外篇:Flutter面试+项目实战+电子书
12.大厂高级Android组件化强化实战

整理不易,关注一下吧,ღ( ´・ᴗ・` ) 🤔