本篇博客代码及资源下载 : https://download.csdn.net/download/han1202012/10382762
文章目录 一. 音视频基础 1. 音频基础 (1) 声音要素 声音要素 :
1.音调 : 声音的频率, 即每秒钟震动的次数 ; 下面举个栗子: ( 1 ) 人声对比 : 声音频率由大到小排列 : 小孩声音频率 > 女人声音频率 > 男人声音频率 ;( 2 ) 音乐领域 : 声音频率由小到大排列 : do (1) < re (2) < mi (3) < fa (4) < so (5) < la (6) < si (7); 2.音量 : 声音震动的幅度决定 , 震动幅度大 音量大, 震动幅度小, 音量小;3.音色 : 音色即声音的材质, 与谐波有关 , 如 钢琴的 C1 音符 与 小提琴的 C1 音符频率是一样的, 都是 261 Hz, 但是明显声音不同, 这就是由于其谐波不同导致的 ; ( 1 ) 声音波形越接近正弦波, 声音越好听, 波形越乱, 越嘈杂; 4.声音要素分析 : ( 1 ) 甲 和 乙 的震动频率是一样的, 音调相同; 丙 的音调 要高于 甲 和 乙; ( 2 ) 乙 和 丙 的震动幅度是一样的, 其 音量 相同, 乙的音量要大于 甲; ( 3 ) 丁的声音没有任何规律, 是噪音, 音质差;
(2) 心理声学模型 心理声学模型 介绍 :
1.人的听觉范围 : 人能听到 20 Hz ~ 20000Hz 之间的声音; ( 1 ) 次声波 : 低于 20Hz 的声波 是 次声波;( 2 ) 超声波 : 高于 20000 Hz 的声波 是 超声波;2.人的发音范围 : 人能发出 85 Hz ~ 1100 Hz 的声音;3.其它动物的发音听觉范围 : 难怪自然灾害动物都比较警觉; ( 1 ) 狗 : 听觉 15 Hz ~ 50000 Hz, 发音 452 Hz ~ 1800 Hz;( 2 ) 猫 : 听觉 60 Hz ~ 65000 Hz, 发音 760 Hz ~ 1500 Hz;( 3 ) 蝙蝠 : 听觉 1000 Hz ~ 120000 Hz, 发音 10000 Hz ~ 120000 Hz;( 4 ) 海豚 : 听觉 150 Hz ~ 150000 Hz, 发音 7000 Hz ~ 120000 Hz;2. 音频信号处理 (1) 音频信号量化过程 音频信号量化过程 :
1.模拟数据 : 自然界中的***连续的模拟数据***;2.采样 : 在模拟数据中设置 若干 个采样时间点, 每个采样点 从模拟数据中 取该采样点 时间 对应的数据值大小; ( 1 ) 采样率 : 每秒钟的采样个数, 44100 Hz 采样率 就是每秒钟有 44100 个采样点 ;( 2 ) 常用采样率 : 16KHz, 32KHz, 44.1KHz, 48K Hz;3.量化 : 每个采样点的值 根据震动幅度的大小, 将震动幅度 分成若干个级别, 如 0 ~ 256, 0~ 65535 等; ( 1 ) 采样位数 : 采样位数 为 8 位, 就是讲震动幅度分成 256 个级别 , 采样位数为 16 位, 就是将震动幅度分为 65536 个级别 ;4.编码 : 将采样值 的大小 根据 震动幅度, 编码成 8位 或 16 位 的数字, 将这些数字按照一定顺序排列起来; ( 1 ) 采样值 : 采样值 是 无符号数, 没有负数, 0 是最小值 ;( 2 ) 声道数量 : 如 单声道, 立体声, 5.1 环绕声 ;( 3 ) 码率( 传输速度 ) : 采样率 * 采样位数 * 声道数 , 其结果是 1 秒钟的 位数, 单位是 bps ( bit per second 比特每秒 ) ;5.数字信号 : 将编码 转为 0 1 组成的 二进制数字信号, 在物理硬件上传输;(2) PCM 音频参数 简介 PCM 音频参数简介 :
1.采样率 : 44100 Hz ( CD 采样率 ), 48000 Hz ( DVD 采样率 ); ( 1 ) 采样率单位 : 44100 Hz 是 1 秒钟 采集 44100 个声音大小样本;( 2 ) 采样率质量 : 采样率 值 越大, 越真实, 质量越高;2.通道 : 左声道 右声道. 如果是双声道 那么 每个样本需要采集 2 个声音样本; ( 1 ) 单声道 : 如果采样率 为 44100 Hz, 单声道, 那么这个音频 1秒钟采集 44100 个样本;( 2 ) 立体声 : 如果采样率 为 44100 Hz, 立体声, 就是分左右声道, 那么 1 秒钟采集 88200 个 样本;3.样本大小 : 每个采样声音样本的大小, 样本格式大小越大, 声音质量越好; ( 1 ) 16 位 : 每个样本 2 字节, AV_SAMPLE_FMT_S16 格式;( 2 ) 32 位 : 每个样本 4 字节, AV_SAMPLE_FMT_FLTP 格式, 一般声卡最高支持到 24 位, 无法播放 32 位的声音;( 3 ) 重采样 : 如果声音样本大小是 32位的, 声卡是播不出 32 位声音的, 需要将其 重采样 为 16 位 , 在传给声卡播放;( 4 ) 查看本电脑的播放设置 : 插图4.样本类型 : PCM 数据有两种存放方式, ① 一种是普通格式, 又叫交错格式, ② 另外一种是平面格式; ( 1 ) 普通格式 ( 交错格式 ) : AV_SAMPLE_FMT_S16 格式, 每个样本 2 字节, b1 b2 分别是第 1 和 第 2 字节, 那么该格式存放格式就是 两个 字节 交替存放, 如 b1 b2 b1 b2 b1 b2 b1 b2 …( 2 ) 平面格式 : AV_SAMPLE_FMT_S16P 格式, 每个样本 2 字节, b1 b2 分别是第 1 和 第 2 字节, 那么该格式存放格式是 第 1 字节存放在一起在前半部分, 第 2 字节 存放在一起, 在后半部分, 如 b1 b1 b1 b1 b1 b1 b2 b2 b2 b2 b2 b2 ;3. 音频压缩 (1) 有损压缩 有损压缩 :
1.压缩方法 : 将采集到的 冗余信息 删除;2.冗余信息简介 : 冗余信息 包括 ① 听不到的音频信息 ② 被遮蔽的音频信号; ( 1 ) 听不到的信息 : 人耳 听觉范围 外的 音频信号 如 ① 超声波 ② 次声波 ;( 2 ) 被遮蔽的音频信号 : 这些被遮蔽的信号分为 ① 频域掩蔽 和 ②时域掩蔽 ;(2) 频域遮蔽效应 频域遮蔽效应 :
1.频率最小可闻域 : 每个 频率都有一个声音强度的阈值, 小于这个阈值就听不到这个频率的声音 了, 每个频率的阈值都不一样 ;2.高音量掩蔽提升阈值 : 如果 有一个能量很大的声音出现, 该声音前后的频率的阈值会提高 , 即 每个 频率在每个时间段的 最小可闻阈值 不一样 ;3.删除冗余信息 : 每个时间段的每个频率 最小可闻阈值 之下的声音 人耳是听不到的, 可以删除; ( 1 ) 横轴说明 : 下图中的横轴是 频率值, 在 频率 最右侧, 即 频率高于 10^4 之后, 不管发出多高的音量, 人耳也听不到, 即超声波音量在高也听不到, 这些听不到的声音可以删除;(3) 时域遮蔽效应 时域遮蔽效应 : 当 强音信号 和 弱音信号 ① 同时发音 或 ② 发音时间接近的时候 , 会出现遮蔽效应;
1.同时掩蔽 : 强音 和 弱音 信号同时发音时, 弱音信号会被掩蔽;2.前掩蔽 : 人耳 在 停到 强音 信号 之前的 一段时间, 发出的弱音信号 会被掩蔽, 这段时间非常短, 大概在 几十毫秒左右;3.后掩蔽 : 人耳 听到 强音信号消失后, 才能听到 弱音信号, 这段时间的弱音信号被掩蔽, 大概 一百 毫秒左右;4. 音频编解码 (1) 音频编解码器 音频编解码器 : 混个脸熟就行, 详细的编解码过程之后再看;
1.OPUS : 最新的编解码器, 新能最好; 但是 RTMP 目前 支持 AAC Speex, 不支持 OPUS 编解码器; 2.AAC : 直播中使用较多, 音质要求比较高. 延迟也高; 其目的是为了替代上一代的 Mp3 编解码;3.Vorbis : Ogg Vorbis, 类似于 MP3.4.Speex : 除了音频编解码之外, 提供 回音消除, 降噪 等高级功能;5.iLBC :6.AMR :7.G.711 : 固话使用就是 该 音频 编解码器;8.性能对比 : OPUS > AAC > Virbis; 下面的两张图说明 OPUS 无论是 音频质量 还是 音频延迟, 在任何码率下 其性能都是最好的; 9.码率 与 延迟 新能 分析图 : 横轴是码率 ( Bitrate 单位 kbps ) , 纵轴是 延迟 ( Delay 单位是 ms );
10.码率 与 声音质量性能分析图 : 横轴是码率 ( Bitrate 单位 kbps ) , 纵轴是 声音质量;
5. AAC 编解码器 (1) AAC 编解码器 简介 AAC 简介 :
1.MP3 格式 : MP3 格式 是***基于上一代 MPEG-2 标准***进行开发的, 该方式压缩是***有损压缩, 无法 100% 还原***;2.AAC 压缩 : 基于 MPEG-4 标准, 使用 SBR 和 PS 技术 , 使压缩率增高, 同时保证音质好;3.应用范围广 : 目前 ① 90% 的直播使用的是 AAC 编码, ② RTMP 支持 AAC 编码; ③ AAC 能保证 音频 的高保真;4.AAC 规格 : ① AAC LC, ② AAC HE v1, ③ AAC HE v2 ;(2) AAC 规格 AAC 规格 :
1.AAC LC 规格 : 单纯的 AAC 编解码技术; ( 1 ) 低复杂度 ( Low Complexity ) : 码流 是 128Kbps. 2.AAC HE V1 规格 : 在 AAC 编解码技术的基础上 , 增加了 SBR 技术; ( 1 ) SBR 技术 : Spectral Band Replication 分频复用技术, 将音频的频带分成 低频 和 高频 分别进行编码, 降低 低频 信号的采样率, 提高高频信号采样率; 码流 64Kbps; 3.AAC HE V2 规格 : 在 AAC 编码技术基础上 增加了 SBR 技术, 又增加了 PS 技术; ( 1 ) PS 技术 : Parametric Stereo 参数立体声 技术, 双声道 一个声道 完整保存, 另一个声道保存差异数据; 码流 32Kbps ;
(3) AAC 格式 AAC 格式 :
1.ADIF ( Audio Data Interchange Format ) 格式 : 将***音频信息 ( 采样率, 采样位数 等 ) 存放在文件头处***, 文件只能从开头播放, 这种格式常用于在磁盘中保存 音频数据 ;2.ADTS ( Audio Data Transport Format ) 格式 : 音频信息 存放在每一帧 数据的开始位置 , 可以再音频流的任意位置解码, 这种格式用于实时音频流传输解码; ( 1 ) 弊端 : 该中格式 每帧 数据都要有一个 同步字 , 其大小要比 ADIF 格式的要大很多;(4) AAC 编解码库 AAC 编解码库 :
1.Libfdk_AAC 编码库 : 性能最好, 推荐使用这个;2.ffmpeg AAC 编码库 :3.libfaac 编码库 :4.libvo_aacenc 编码库 : 已经淘汰;二. 视频基础 1. MPEG-4 标准 (1) MPEG-4 标准 简介 MPEG-4 标准概述 :
1.概念 : MPEG-4 是用于 ①音频 ②视频 信息 的 压缩编码 的 标准;2.用到的 MPEG-4 文档内容 : 在 MPEG-4 文档的 Part 14 定义了 文件格式 相关内容, MPEG-4 文档的 part 15 定义了 AVC 文件格式;3.压缩算法 : H264 压缩算法, AVC 压缩算法 ; 媒体文件播放流程 : 封装 -> 解码 -> 重采样 -> 像素格式转换;
1.封装 : 从 文件 中将 音频 或 视频 读取出来 ;2.解码 : 将读取出来的内容解压出来;3.重采样 : 将视频转换成显卡支持的格式, 音频转换为声卡所支持的格式;4.像素格式转换 : 视频需要做像素格式转换;(2) 封装格式 简介 常用封装格式简介 :
1.AVI 格式 : 该格式的可以存放任意压缩格式的媒体数据, 甚至可以存放没有压缩过的数据;2.FLV 格式 : FLV 是一个流媒体格式, 直播 经常会用到;3.TS 格式 : TS 也是一种流媒体格式, 一般网络电视使用的是这种格式;4.ASF 格式 : 微软支持的流媒体格式, 适合做点播;5.MP4 格式 : MPEG-4 中定义的封装格式;(3) 编码格式 简介 常用的视频编码格式简介 : 视频都是有损压缩格式;
1.H264 格式 : 在文档的 Part10 介绍, 效率很高的一种编码格式;2.WMV 格式 :3.XviD 格式 : 在文档的 Part2 介绍,4.MJPEG 格式 : 这种格式每一帧画面都是独立的, 压缩率很低, 一般摄像机拍摄的原始视频是这种格式的;5.上述格式对比总结 : H264 WMV XviD 三种格式的算法都是根据前后帧的关系进行压缩, 压缩的效率要远远高于 MJPEG 格式, 同样质量大小的视频, MJPEG 的大小是 其它三种压缩格式的好几倍;常用的音频编码格式简介 : 音频格式可以分为 有损压缩 和 无损压缩;
1.AAC 格式 : 有损压缩格式;2.MP3 格式 : 有损压缩格式, 早期的音频格式;3.APE 格式 : 无损压缩, 与原始声音一样;4.FLAC 格式 : 无损压缩;2. 封装格式 和 编码格式简介 (1) 封装 和 编码 格式 简介 封装模型 :
1.封装模型简介 : 按照次序排列 : 封装格式头 -> 视频编码帧 -> 音频编码帧 -> 视频编码帧 -> 音频编码帧 … ( 重复视频音频编码帧 )2.封装格式头 : 主要封装 box 音视频 相关 信息 , ①视频压缩编码格式, 视频关键帧索引, ②音频压缩编码格式 等内容;3.封装视频音频次序 : 视频编码帧 和 音频编码帧 ***帧率不是完全一致***的, 只要音频帧帧率大于等于视频帧即可;4.视频编码帧 : 以 H264 编码规则举例 : ( 1 ) NAL 层 : 网络提取层数据, 包含了网络提取层头部信息, 用于网络传输, 头部信息中包含了该帧的相关信息, 包括是是否是关键帧, B 帧, P 帧 等信息 ;( 2 ) VCL 层 : 视频编码层;( 3 ) SPS : 表示序列参数设置, 如关键帧信息;( 4 ) PPS : 图像参数, 如 图像宽高 等; 如果没有封装头, 视频也能根据 SPS 和 PPS 进行解码播放; 解码的时候会先解析 SPS 和 PPS 参数;( 5 ) 解码为 YUV : 视频编码帧 最终 解码为 YUV 格式, Y 表示灰度( 如果只解析 Y 就是黑白视频图像 ) , UV 表示色彩;( 6 ) YUV 转换为 RGB : YUV 格式的视频 需要 转为 RGB 来进行显示, 解压出来的数据非常大 1 秒钟几百 M 的数据, 这个过程开销很大 1 分钟 几个 G , 注意优化 ;( 7 ) 软解码 : 使用 CPU 实现, 耗电量大, 兼容性强, 性能强(1秒100帧以上) ,( 8 ) 硬解码 : 协处理器 实现, 解码逻辑直固化在硬件上, 但是性能固定限制 (每秒固定帧如60帧), 兼容性差;5.音频编码帧 : ( 1 ) 压缩格式 : AAC 有损压缩格式, APE, FLAC 无损压缩格式;( 2 ) 原始格式 : PCM 原始格式, 音频大小即使是原始的格式 但是比起视频来说数据量也很小;( 3 ) 解码为 PCM FLT 格式 : AAC 解码为 FLT 格式, 方便浮点运算, float 4 字节 32 位, 无损格式解码为 PCM 格式;( 4 ) 重采样 : 将 PCM 或 FLT 格式转为声卡支持的采样位数, 一般声卡支持 16 位, 好的支持 24 位的;3. YUV 和 RGB 像素格式 简介 (1) 像素格式简介 像素 格式 简介 :
1.视频压缩格式 : H264 是压缩格式, 这是解码前的格式, 这些格式都需要①先解压 , 然后②转为像素格式播放 ;2.RGB 格式 : RGBA, BGRA, RGB32, ARGB32, 其中 R 代表红色, G 代表绿色, B 代表蓝色, A 代表透明度,3.YUV 格式 : Y 代表灰度, UV 代表色彩, H264 的算法是基于 YUV 格式的, YUV 要比 RGB 要小, 一个像素 RGB 需要 3 字节 ( 24 Bit), YUV 的话需要 12 Bit ( 位 ), FFMPEG 有转换接口, 推荐使用 显卡 GPU Shader 进行转换 ( GPU 处理像素转换效率很高 ) , 节省 CPU 资源; ( 1 ) YUV -> R 转换公式 : R = Y + 1.4075 * ( V - 128 );( 2 ) YUV -> G 转换公式 : G = Y - 0.3455 * ( U - 128 ) - 0.7169 * ( V - 128 );( 3 ) YUV -> B 转换公式 : B = Y + 1.779 * ( U -128 );(2) RGB 图像 在内存中的 存储方式 RGB 在内存中的存储方式 :
1.存放次序 : RGB 在 内存中是按照从低位到高位 BGR 进行存放的, 是倒着存放的, 如 0 字节存放 B, 1 字节存放 G, 2 字节存放 R ;2.对齐操作干扰读取序列 : 有时候为了提高运算效率, 会让像素值是 4 的倍数, 方便对齐 , 如果此时宽度是 3 个像素, 就会在 每行补一个 RGB 都是 0 值的像素, 这时候 第四个像素值 的索引就是 4 (第一行 0, 1, 2, 3, 将 3 索引补成了 0);3.最佳操作 : 尽量使用 4 的倍数的像素值, 整块内存进行操作 , 运算一次即可拷贝整块内存, 如果出现上述情况, 就需要逐行读取, 可能要拷贝几百到几千次, 根据行数决定;(3) YUV 像素格式 YUV 像素格式简介 :
1.Y 意义 : Y 代表亮度, 是灰度值 , 单纯的使用 Y 就是黑白电视机;2.U V 意义 : U 和 V 代表色度值 , U V 与 Y 结合可以转换为 RGB 颜色值;3.YUV 4:4:4 采样 : 每个像素都有一个 YUV 对应, 一个像素大小也是 3 字节, 与 RGB 一样;4.YUV 4:2:2 采样 : 0 Y 对应 0 UV, 1 Y 对应前一个 0UV, 第二个 Y 使用前一个 UV, 两个灰度 公用一个色度, 每两个像素就节省 2 个字节;5.YUV 4:2:0 采样(最常用的) : 四个 灰度 公用一个 UV 色度值, 四个 灰度 是 上下左右 2 x 2 挨着的色度值. 每四个像素点节省 3 个 UV 值, 即 4 个像素点 使用 6 字节 ( 4 Y 1U 1V ) , 平均每个像素点占用 1字节;6.YUV 4:2:0 P 平面存储方式 : 平面形式存放, 现将 Y 全部存放, 后面再存放 U , 最后存放 V 数据;4. 视频参数简介 (1) MP4 格式封装简介 MP4 格式分析 : 列举一些头部封装信息;
1.MP4 格式文档 : MPEG-4 标准的 Part14 文档, 主要讲解 MP4 文件的格式; ( 1 ) 文档下载地址 : , 解压, 其中的 ISO_IEC_14496-14_2003-11-15.pdf 就是这部分文档 ;2.ftyp 字段 : ***file type ( 文件类型 )***, 表示这个文件属于那种类型;3.moov 字段 : ***metadata container ( 元数据容器 )***, 表示存放媒体信息的位置;4.mvhd 字段 : ***movie header ( 媒体信息头 )***, 存放文件的总信息, 如 视频长度, 创建时间 等信息;5.trak 字段 : ***track of stream container ( 媒体流容器 )***, 该容器存放 视频流 或 音频流;6.mdhd 字段 : ***media header ( 媒体头部信息 )***, 定义时间转换相关信息, ( 1 ) TimeScale 时间 : 该值可以转换成真实的时间;( 2 ) 帧同步作用 : 每帧视频都有显示时间, 根据这个时间进行时间同步运算;(2) H264 | AVC 视频编码标准 H264 编码标准层级 :
1.视频编码层 ( VCL ) : 该层主要负责视频的编码 解码 等操作 ;2.网络抽象层 ( NAL ) : 该层主要进行数据的格式化, 并提供封装的头信息;NAL 单元 :
1.概念 : 每个数据帧就是一个 NAL 单元;2.帧分隔符 : 每帧前一般使用 00 00 00 01 或者 00 00 01 作为分隔符;3.首帧数据 : 通常 编码器 编码 生成的 第一帧数据是 PPS 和 SPS 数据, 接着就是 I 帧;5. 编码帧相关概念 (1) 帧类型简介 帧类型简介 :
1.I 帧 : 关键帧, 使用的压缩技术是 帧内压缩技术;2.P 帧 : 向前依赖, P 帧压缩时 参考 前一帧数据, 使用的压缩技术 是 帧间压缩技术;3.B 帧 : 前后依赖, B 帧压缩时 参考前一帧 同时 也 参考 后一帧 数据, 使用的压缩技术 也是 帧间压缩技术; (2) 帧类型 与 GOF ( Group Of Frame ) 组帧 帧类型 与 GOF :
1.I 帧 : I 类型 帧 是关键帧, 关键帧是一帧的 完整数据, 可以独立解码出来 ; ( 1 ) 可独立播放的帧组 : 从数据中任意抽出连续帧 不一定能够播放, 必须是 关键帧 及 关键帧以后的帧 才能播放出来 ; 关键帧之前的数据如果没有前面的关键帧是解码不出来的 ;( 2 ) 低帧率应用 : 在实时性要求不是很高的监控环境中, 1秒钟一帧, 只要将关键帧解码显示出来即可;( 3 ) 关键帧丢失 : 如果关键帧丢失, 那么依赖于该关键帧的后面的 B 帧 和 P 帧 就会根据上一个关键帧来解码, 可能会出现错误;( 4 ) 设置关键帧的依赖帧数量 : 可设置 一个数量 如 30 帧, 依赖于一个 关键帧, 如果其依赖的关键帧丢失, 那么这 30 帧都出现错误;2.参考帧 : 除了关键帧意外, 其它类型的帧 信息都不全, 需要参考其余的帧进行解码; ( 1 ) P 帧 参考帧 : P 帧 的 参照帧 是 前一帧 ;( 2 ) B 帧 参考帧 : B 帧的 参考帧 是 前一帧 和 后一帧 两帧 数据;2.B 帧 : B 帧解码 是 相对于 前一帧 和 后一帧 的变化 进行解码 , 如果后一帧没有解码出来, 该 B 帧就无法解码出来,3.P 帧 : P 帧 解码是相对于前一帧的变化进行解码 , P 帧的参考帧 是 前一帧, 按照 前后 次序解码 即可;5.解码顺序 和 播放顺序 : 由于 B 帧 是依赖于前一帧 和 后一帧进行解码, 势必无法进行顺序的解码, 解码的帧序号是跳跃进行的 ; ( 1 ) 帧解码 播放 次序举例 : ① I 帧 ② B 帧 ③ B 帧 ④ P 帧 ⑤ B 帧 ⑥ B 帧 ⑦ P 帧 ⑧ B 帧 ⑨ P 帧( 2 ) 解码分析 : 如果 遇到 ② B 帧, 需要 前一帧 和 后一帧 解码, 如果 后一帧 还是 ③B 帧, 那么就先要将 后面的 ③B 帧先解出来, 然后返回来 解码 ② B 帧;与 GOF 相关的 视频 故障 问题分析 :
1.花屏 : GOF 中的 P 帧 或 I 帧 丢失, 会导致解码图像出现错误 ;2.卡顿 : 为了 防止花屏产生, 如果发现 P 或 I 帧丢失, 那么 整个 GOF 内的帧都不显示, 直到下一个 I 帧到来后显示 , 这样就造成了 卡顿;(3) 帧 相关 参数 帧 相关 参数 :
1.SPS ( Sequence Parameter Set ) 序列集参数 : 存放内容 ① 帧数 ② 参考帧数目 ③ 解码图像尺寸 ④ 帧场编码模式选择标识 ;2.PPS ( Picture Parameter Set ) 图像参数集 : 存放内容 : ① 片组数目 ② 初始量化参数 ③ 去方块滤波系数调整标识 ④ 熵编码模式选择标识 ;(4) 视频编码器 简介 视频编码器简介 :
1.x264 : 2.x265 : 3.openH264 : 二. Android Studio 环境安装配置 1. Android Studio 安装 (1) Android Studio 的各种地址 Android Studio 下载 学习 地址 :
具体安装过程就不介绍了, 与普通软件安装过程差不多; 狂点 下一步 即可完成安装 ;
(2) SDK NDK 安装 安装 SDK NDK 等开发环境 :
1.打开 SDK Manager : 安装 SDK, NDK, CMake 三种工具都在 SDK Manager 中进行安装, 点击 图标, 即可打开 SDK Manger;
2.下载 SDK : 在 SDK Manager 中 的 SDK Platform 板块中, 下载任意一个 SDK 即可, 尽量下载高版本的 SDK, 推荐下载 25 以上版本的;
3.下载 NDK 和 CMake : 在 SDK Tools 板块中, 选择 CMake 和 NDK 两个进行下载;
(3) 模拟器安装 Android 模拟器安装 :
2. Android Studio 相关工具介绍 (1) SDK 简介 SDK 目录分析 :
1.platform-tools 目录 : 平台相关的工具, 主要是在操作系统 ( Windows Linux Mac ) 中独立使用的工具 , 如 adb sqlite3 fastboot 等工具;2.tools 目录 : Android 开发环境中使用的工具, 如 性能监控工具, 调试工具. 一般都是在 Android Studio 中打开使用, 很少单独使用;3.platform 目录 : 存放下载的各个版本的 SDK ;4.ndk-bundle 目录 : 交叉编译工具, 用于编译 C/C++ 代码;5.build-tools 目录 : 编译工具, 存放下载的各个版本的 编译工具 ;(2) NDK 简介 NDK 简介 :
1.platforms 目录 : NDK 各个 Android 版本依赖的库, 每个对应 Android 版本都有 各种 CPU 架构对应的库 如 arm, mips, x86 等; ( 2 ) 单个 Android 版本中对应的不同 CPU 架构库的目录 :
( 3 ) 每种 CPU 对应的库不同 : 不同的 CPU 使用的 库 的类型是不一样的, 需要分别进行管理, 在不同的 CPU 架构上执行不同额 库;2.toolchains 目录 : 交叉编译工具链;
( 1 ) 交叉编译 : 在 x86 平台上, 编译出 在 ARM 平台上运行的 库;( 2 ) 交叉编译的执行者 : windows-x86_64 代表交叉编译的执行者是 Windows 系统 x86 64位的 CPU , 每个交叉编译工具下都是 prebuilt 目录, 在每个 prebuilt 目录下都是 windows-x86_64 目录;
( 3 ) 编译的库在哪个平台执行 : 在 arm 平台执行需要使用 aarch64-linux-android-4.9 工具 , 在 mips 平台执行需要使用 mips64el-linux-android-4.9 工具, 不同的工具对应不同的平台 ;(3) 关于 Android 版本的说明 Android 版本采用 : 下图是从 Android Studio 中截取的一张图;
1.蓝牙支持 : 如果你做的软件需要 BLE 蓝牙支持, 那么必须使用 4.3 以上的版本;2.音频软件 : 如果开发的 APP 需要高性能音频, 则必须使用 4.4 以上的;3. 测试 Android 开发环境 ( 测试 包含 C/C++ 的 Android 工程 ) (1) 测试工程 包含 C/C ++ 的 Android 工程 创建 与 运行 :
1.创建工程 : Android Studio 必须要创建一个工程, 才能进入开发界面; ( 1 ) 选择 C++ 11 支持 : 在创建工程界面, 需要勾选 Include C++ 11 选项;
( 2 ) 选择 最小 版本 : 这里选择 4.0.3 版本, 基本是 100% 支持所有现有设备;
( 3 ) 生成一个 Activity 首界面 :
( 4 ) 设置 Activity 启动界面 : 设置 Activity 界面的 名称 和 界面 对应的 布局文件名称;
( 5 ) 定制 C ++ 支持 : 在 定制 C ++ 支持 ( Customize C++ Support ) 界面, 选择 C ++ 11 标准;
( 6 ) 等待应用配置编译 : 等待应用的 配置 编译, 这个过程比较长, 之后会自动进入开发界面;
2.打开虚拟机 : 在 AVD Manager 中, 点击运行即可打开虚拟机;
3.运行项目 : 点击 运行 按钮, 选择 要运行 APP 的设备, 即 刚才启动的虚拟机;
#三. FFMPEG 交叉编译
编译失败大概率是因为版本错误, 确保使用以下版本进行编译 : ① Ubuntu 版本 : Ubuntu 16.04.4 64位 ② NDK 版本 : android-ndk-r14b ③ FFMPEG 版本 : ffmpeg-3.4 ;
##1. NDK 简介
###(1) NDK 安装
交叉编译环境安装 :
1.NDK 简介 : Android 中 NDK 允许 开发者在 Android 中可以使用 C/C++ 进行开发, 调用 C/C++ 库;2.Native 层代码性能很高 : Java 语言的执行效率较低, Native 层的 C 语言代码效率很高 , 做高效率的物理运算需要使用 C/C++ 代码实现;3.NDK 下载 : 在 Android Studio 中可以在 SDK Manager 中可以下载; (2) ndk-build 构建脚本 ( FFMPEG不使用该脚本 使用 CMake )
构建脚本 ndk-build 作用 :
1.启动构建 : ndk-build 是一个脚本文件, 用于启动 构建脚本;2.自动构建 : ndk-build 可以自动查找探测 开发环境 和 项目目录, 找到相应的内容, 进行自动构建;3.编译完成 : 自动构建完成后, 会自动生成一个 二进制文件;4.复制库 : ndk-build 会将生成的二进制文件复制到对应的目录进行使用;5.已过时 : 这是上一个版本的 构建工具, 需要配置 Android.mk 和 Application.mk 文件进行交叉编译;6.当前交叉编译方案 : Android Studio 3.0 以上都使用 CMake 进行交叉编译;(3) JNI 简介 Java 原生接口 ( Java Native Interface ) JNI 简介 :
1.作用 : Java 与 C/C++ 进行交互的接口;2.定义原生接口 : 先在 Java 定义 native 方法, 如 private native void hello(String str);3.导入库 :static{
System.loadLibrary("native-lib");
}
4.动态链接库 ( .so 后缀 ) : 特点是 只把 函数的地址写进入, 运行时才动态加载这个库;5.静态链接库 ( .a 后缀 ) : 特点是 函数 在编译的时候, 直接把源码 复制到 本工程中; ( 1 ) 缺点 : 使用静态库编译, 编译的时间比较长 ;( 2 ) 优点 : 只导出一个库, 可以隐藏自己调用的库 ;2. ABI ( Application Binary Interface ) 应用程序二进制接口 简介 (1) ABI 简介 ABI ( Application Binary Interface ) 应用程序二进制接口 简介 :
1.CPU 指令集 : 程序执行 最终 是转换成 CPU 的指令集来执行, 不同的 CPU 的指令集格式是不同的 ;2.ABI ( Application Binary Interface ) 概念 : ABI 中规定了 机器码 运行的时候 如何 与 系统 进行交互;3.ABI 包含的内容 : ( 1 ) 机器码对应的 CPU 指令集 ;( 2 ) 内存存储 和 读取 的 字节次序 ;( 3 ) 可执行的二进制文件 ( 程序 或 共享库 ) 的格式 ;( 4 ) 对齐方式 ;( 5 ) 堆栈使用的约定, 函数调用的约定 ;(2) NEON 简介 NEON 简介 :
1.概念 : NEON ( ARM架构处理器扩展结构 ) , 是适用于ARM Cortex-A系列处理器的一种128位SIMD(Single Instruction, Multiple Data,单指令、多数据)扩展结构, 提供 标量/矢量 指令 和 寄存器;2.关于 NEON 的编译设置 : 3. 交叉编译环境安装 (1) Ubuntu 虚拟机 下载 Ubuntu 下载 安装 :
编译失败大概率是因为版本错误, 确保使用以下版本进行编译 : ① Ubuntu 版本 : Ubuntu 16.04.4 64位 ② NDK 版本 : android-ndk-r14b ③ FFMPEG 版本 : ffmpeg-3.4 ;
(2) Ubuntu 虚拟机 安装 Ubuntu 虚拟机安装 :
1.虚拟机工具 : VMware 12 或 14 版本 , 这里我使用的是 VMware Workstation 12 PRO 版本, 找个地方下载 安装激活, 推荐购买正版软件 ;2.创建虚拟机 : 安装好虚拟机后, 点击主页的 创建虚拟机, 选择典型安装;
3.选择安装文件 : 选择 安装光盘映像文件, 从目录中选择下载的 文件 ubuntu-16.04.4-desktop-amd64.iso ;
4.设置用户名密码 : 设置用户名 和 密码, 这个密码一定要记住, 之后登陆要使用;
5.设置安装位置 : 设置 虚拟机 在电脑硬盘的位置 ;
6.设置空间大小 : 至少 40 G 以上的空间;
7.自定义硬件 : 选择 自定义硬件 按钮, 然后设置硬件, ① 内存分配 2 G, ② 主要是分配 处理器, 一般是有几个处理器就分配几个; ③ 虚拟机 网络 设置为 桥接网络 ;
8.启动虚拟机 : 设置好以后, 启动虚拟机, 会自动安装 系统;
9.登录操作系统 : 安装完后登录操作系统, 需要之前设置的 密码 ;
(3) 创建 root 用户 并使用 root 用户登录 图形界面 设置 root 用户 :
1.解锁 root 用户 密码 : 使用 sudo passwd -u root 命令解锁 root 用户密码, 期间要输入当前用户的密码;2.设置 root 用户密码 : 使用 sudo passwd root 命令设置 root 用户密码, 期间要 输入 两次 root 用户密码;
3.设置 root 用户界面登录 : 编辑 /usr/share/lightdm/lightdm.conf.d/50-unity-greeter.conf 文件, 在文件最后添加 如下内容;
- ( 1 ) 编辑文件命令 : gedit /usr/share/lightdm/lightdm.conf.d/50-unity-greeter.conf ;
- ( 2 ) 添加的内容 :user-session=ubuntu
greeter-show-manual-login=true
all-guest=false
4.重启系统 : 重启系统后可以看到, 用户名可以自己输入, 输入 root 用户名 和 密码 即可以 root 用户登录用户界面;
5.出现错误 : 此时会出现 /root/.profile 相关错误提示;
6.解决错误 : 使用 gedit /root/.profile 命令 编辑 .profile 配置文件, 在 最后一行最前面加上 “tty -s &&” 内容, 重启系统, 该问题消失;
(4) 虚拟机网络设置 VMware 三种网络设置 : ① 是否能访问外网 ② 是否有独立IP ③ 外部电脑是否可访问虚拟机
1.桥接模式 ( Bridge ) : ① 访问外网 , ② 虚拟机有 独立 IP 地址 , ③ 外部电脑可以访问虚拟机 ;2.网络地址转换模式 ( NAT ) : ① 访问外网, ② 没有独立 IP 地址 , ③ 外部电脑无法访问虚拟机 ; ④ 主机与虚拟机构成局域网可互相访问 ;3.主机模式 ( Host-only ) : ① 不能访问外网 , ② 没有独立 IP 地址 , ③ 外部电脑无法访问虚拟机 ;虚拟机网络初始化 :
2.步骤 2 : 进入虚拟机网络编辑器, 点击 还原默认设置, 即可将网卡恢复到初始状态;
(4) 配置 Ubuntu 的软件环境 设置 Ubuntu :
1.更新数据源 : 使用 apt-get update 命令, 更新数据源, 刷新软件库列表;
2.安装 openssh-server : 使用 ssh 连接时的加密通信工具, 用于Xshell 或 SecureCRT 命令行连接使用; 使用 apt-get install openssh-server 命令安装;
3.查看虚拟机 IP 地址 : 使用 ifconfig 命令查看 虚拟机 局域网 IP 地址;
4.使用 SecureCRT 连接 虚拟机 :
( 4 ) 设置用户密码 : 不能使用 root 用户;
5.安装 VIM : 该编辑工具用于在 命令行 编辑 文件使用;
编译失败大概率是因为版本错误, 确保使用以下版本进行编译 : ① Ubuntu 版本 : Ubuntu 16.04.4 64位 ② NDK 版本 : android-ndk-r14b ③ FFMPEG 版本 : ffmpeg-3.4 ;
4. FFMPEG 编译前的准备工作 (1) FFMPEG 源码下载 FFMPEG 源码下载 :
2.Git 下载最新源码 : 使用 git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg 命令可以使用 Git 下载最新的版本;3.放置位置 : 将 FFMPEG 源码下载后放在 /root/FFMPEG 目录下;(3) FFMPEG 源码编译步骤 FFMPEG 源码编译流程 :
1.编译准备 : ① 源码解压 ② NDK 环境 ③ make 工具安装 ( 1 ) 解码源码 : 使用 tar -xvf ffmpeg-3.4.tar.bz2 命令, 解压源码到 FFMPEG 目录下 ;
( 2 ) 文件准备 : 检查确保 ndk 和 ffmpeg 源码都已解压到 /root/FFMPEG 目录下;
( 3 ) 安装 make 工具 : 一般 Ubuntu 都自带 make 工具, 如果没有使用 apt-get install make 命令安装;
2.配置编译参数 : 使用 ./configure 命令 进行编译配置;3.编译 : 使用 make 命令编译, 使用多线程编译 make -j线程数, 如 make -j8 , 我的电脑是 四核八线程的, 这里设置 j8 是使用 8 个线程编译;4.安装 : make install 命令 安装 编译好的程序, 将编译好的 库 和 头文件 复制到 指定的 目录中; 编译失败大概率是因为版本错误, 确保使用以下版本进行编译 : ① Ubuntu 版本 : Ubuntu 16.04.4 64位 ② NDK 版本 : android-ndk-r14b ③ FFMPEG 版本 : ffmpeg-3.4 ;
(4) FFMPEG 源码编译配置简介 configure 配置参数 :
1.输出目录 : --prefix 参数设置输出路径;2.开启指定模块 : --enable 开启指定的模块, 如 硬解码 neon 等模块;3.禁止模块 : --disable 禁止某些模块, 如 禁止 ffmpeg 工具;4.交叉编译参数 : 给 gcc 指定交叉编译参数, 编译其它平台的库;
##5. 编译详细过程
###(1) 环境变量设置
如果熟悉可以不看本节的讲解内容, 直接在命令行设置环境变量, 设置内容在第 6 点中.
设置环境变量 : 这些设置可以设置到一个 shell 脚本中, 也可以使用
1.设置 NDK 路径环境变量 : export NDK=/root/FFMPEG/android-ndk-r14b ;2.设置 PLATFORM 平台依赖库环境变量 : export PLATFORM=$NDK/platforms/android-21/arch-arm , 编译 基于 android 21 版本, 那么也需要来与 NDK 中的 21 版本下的 so 库 和 头文件 ; ( 1 ) 依赖于 NDK 环境变量 : $NDK 与 /root/FFMPEG//root/FFMPEG/android-ndk-r14b 是等价的;
3.设置 TOOLCHAIN 工具链常量 : export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 , 配置交叉编译工具环境变量; ( 1 ) 使用者 : 其中的 linux-x86_64 目录名称说明了 使用者是 Linux 操作系统 x86 CPU 架构, 64 位的系统 ;( 2 ) 使用位置 : 其中的 arm-linux-androideabi 目录名称说明了 编译出来是在 arm CPU 架构, linux 内核, androideabi 架构 上 运行的 ;( 3 ) 全路径 : /root/FFMPEG/android-ndk-r14b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64, 这里前面使用 NDK 环境变量代替 /root/FFMPEG/android-ndk-r14b 路径;( 4 ) 包含的内容 : 其中包含了 用到的 所有的 交叉编译工具; 下面是部分截图;
4.设置 CPU 架构常量 : export CPU=armv7-a , 主要是指定一个 CPU 架构 环境变量;5.设置输出目录 : export PREFIX=./android/$CPU , 指定编译完成的可执行文件输出到什么位置, 这个目录是 /root/FFMPEG/ffmpeg-3.4/android/armv7-a; ( 1 ) 编译时所在的目录 : 如果使用 该环境变量 作为 输出目录, 那么必须在 /root/FFMPEG/ffmpeg-3.4/ 目录下进行编译 ;6.环境变量设置总结 :export NDK=root/FFMPEG/android-ndk-r14b
export PLATFORM=$NDK/platforms/android-21/arch-arm
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
export CPU=armv7-a
export PREFIX=./android/$CPU
(2) configure 配置详解 configure 编译配置分析 :
1.设置输出路径 : –prefix=$PREFIX , 设置编译出的可执行文件输出到该目录中;2.指定编译完成后要运行的系统 : –target-os=android , 编译完成后在 android 系统中运行;3.指定交叉编译工具链名称前缀 : –cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- , ( 1 ) 默认的编译器 : 一般 C/C ++ 工程的默认编译器 是 gcc 或 g++;( 2 ) 交叉编译编译器 : 交叉编译的编译器在 NDK 目录中, 路径为 /root/FFMPEG/android-ndk-r14b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc , 这里的 /root/FFMPEG/android-ndk-r14b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi- 就是编译器的前缀, $TOOLCHAIN/bin/arm-linux-androideabi- 等价于 /root/FFMPEG/android-ndk-r14b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi- ;4.指定 CPU 架构 : –arch=arm , arm 架构体系;5.指定 CPU 指令集 : –cpu=armv7-a , armv-7 指令集;6.指定系统依赖库位置 : –sysroot=$PLATFORM , 即 NDK 下 指定的 Android 版本号的 库 和 头文件;7.指定 gcc 参数 : –extra-cflags="-I$PLATFORM/usr/include -fPIC -DANDROID -mfpu=neon -mfloat-abi=softfp " , 指定 gcc 编译参数; ( 1 ) 指定编译的头文件地址 : -I$PLATFORM/usr/include ;( 2 ) 编译动态链接库的参数 : -fPIC -DANDROID ;( 3 ) 指定使用的协处理器 : -mfpu=neon ;( 4 ) 指定 软浮点 运算 : -mfloat-abi=softfp ;8.指定编译器 : –cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc ;9.指定符号查看工具 : –nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm ;10.编译成动态库 : –enable-shared , 编译生成的库就是动态链接库;11.开启 CPU 运行时探测 : –enable-runtime-cpudetect ;12.指定公共许可 : –enable-gpl , 开源相关的, 有些库 必须开源才能使用;13.指定编译时压缩库大小 : –enable-small , 后果是性能上有损失;14.指定本次编译为交叉编译 : –enable-cross-compile ;15.开启指令优化 : –enable-asm ;16.支持 neon 协处理器 : –enable-neon ;17.打开 jni : –enable-jni , 通过 jni 调用 java 调用 硬解码;18.指定编码 : –enable-decoder=h264_mediacodec ;19.指定硬件编码 : –enable-hwaccel=h264_mediacodec ;20.减少的编译模块 : 关闭这些模块, 可以更快的编译, 减少不必要的错误, android 中用不到这些模块; ( 1 ) 连接符 : “” 是连接符, 代表 下面的一行 与 本行 属于 一行数据, 同一行写不下 或者 处于格式美观需求 使用 连接符 将一行数据写成 若干行; --disable-debug \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping
21.configure 完整配置命令行 : 复制该命令, 直接在 Linux 中执行即可, 注意要***①先执行环境变量设置的命令, ②再执行配置命令***;①环境变量设置命令 :
export NDK=root/FFMPEG/android-ndk-r14b
export PLATFORM=$NDK/platforms/android-21/arch-arm
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
export CPU=armv7-a
export PREFIX=./android/$CPU
②配置命令 :
./configure \
--prefix=$PREFIX \
--target-os=android \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=arm \
--cpu=armv7-a \
--sysroot=$PLATFORM \
--extra-cflags="-I$PLATFORM/usr/include -fPIC -DANDROID -mfpu=neon -mfloat-abi=softfp " \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
--enable-shared \
--enable-runtime-cpudetect \
--enable-gpl \
--enable-small \
--enable-cross-compile \
--enable-asm \
--enable-neon \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-hwaccel=h264_mediacodec \
--disable-debug \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping
(3) 编写 FFMPEG 编译的自动化 shell 脚本 这里只是简单介绍下 FFMPEG 的编译脚本如何编写, 编译也可以只使用上面的命令行进行编译;
编写编译脚本进行FFMPEG 的编译只是编译方式的一种;
FFMPEG 编译 shell 脚本 :
1.创建脚本文件 : 一定要在 Linux 中创建脚本文件, 在 Ubuntu 中使用 gedit 进行创建编辑, 或者 使用 命令行 中的 vim vi 编辑器进行创建编辑 shell 脚本文件;2.设置执行方式 : #!/bin/bash , 表示该脚本默认使用 bash 执行;3.打印字符串到命令行 : echo "字符串" , 就可以向命令行中打印字符串;4.设置变量 : 变量名称=变量内容 , 在之后就可以使用 变量名称 替代 变量内容, 类似于 宏定义; 这里将 环境变量 设置成 shell 脚本变量;NDK=root/FFMPEG/android-ndk-r14b
PLATFORM=$NDK/platforms/android-21/arch-arm
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
CPU=armv7-a
PREFIX=./android/$CPU
5.配置设置 : 与命令行中的格式一样, 复制到脚本中即可;./configure \
--prefix=$PREFIX \
--target-os=android \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=arm \
--cpu=armv7-a \
--sysroot=$PLATFORM \
--extra-cflags="-I$PLATFORM/usr/include -fPIC -DANDROID -mfpu=neon -mfloat-abi=softfp " \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
--enable-shared \
--enable-runtime-cpudetect \
--enable-gpl \
--enable-small \
--enable-cross-compile \
--enable-asm \
--enable-neon \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-hwaccel=h264_mediacodec \
--disable-debug \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping
6.开始编译 : 使用 make 开始编译, 开启多线程编译使用 make -j2 就是开启双线程编译;#!/bin/bash
echo "FFMPEG 编译脚本开始"
NDK=/root/FFMPEG/android-ndk-r14b
PLATFORM=$NDK/platforms/android-21/arch-arm
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
CPU=armv7-a
PREFIX=./android/$CPU
echo "FFMPEG 编译选项配置"
./configure \
--prefix=$PREFIX \
--target-os=android \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=arm \
--cpu=armv7-a \
--sysroot=$PLATFORM \
--extra-cflags="-I$PLATFORM/usr/include -fPIC -DANDROID -mfpu=neon -mfloat-abi=softfp " \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
--enable-shared \
--enable-runtime-cpudetect \
--enable-gpl \
--enable-small \
--enable-cross-compile \
--enable-asm \
--enable-neon \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-hwaccel=h264_mediacodec \
--disable-debug \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping
echo "FFMPEG 开始编译"
make
echo "FFMPEG 编译结束"
echo "FFMPEG 安装"
make install
echo "FFMPEG 编译脚本执行完毕"
6. 正式编译 (1) 使用命令行进行编译 正式开始编译 :
1.设置环境变量 : 将下面的环境变量复制到命令行执行, 可以整体复制, 也可以逐条复制;export NDK=/root/FFMPEG/android-ndk-r14b
export PLATFORM=$NDK/platforms/android-21/arch-arm
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
export CPU=armv7-a
export PREFIX=./android/$CPU
2.配置编译选项 : 直接复制到命令行, 然后点回车 即可设置成功, 该步骤主要是生成 Makefile 文件;./configure \
--prefix=$PREFIX \
--target-os=android \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=arm \
--cpu=armv7-a \
--sysroot=$PLATFORM \
--extra-cflags="-I$PLATFORM/usr/include -fPIC -DANDROID -mfpu=neon -mfloat-abi=softfp " \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
--enable-shared \
--enable-runtime-cpudetect \
--enable-gpl \
--enable-small \
--enable-cross-compile \
--enable-asm \
--enable-neon \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-hwaccel=h264_mediacodec \
--disable-debug \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping
①开始编译后的命令行内容 :
②编译成功标志 :
3.开始编译 : 执行 make 命令, 开始编译, 单线程编译会进行几分钟, 没有报错的话应该能编译成功, 编译失败大概率是因为版本错误, 确保使用以下版本进行编译 : ① Ubuntu 版本 : Ubuntu 16.04.4 64位 ② NDK 版本 : android-ndk-r14b ③ FFMPEG 版本 : ffmpeg-3.4 ;执行完没报错就是执行成功 .
4.安装 : 执行 make install 命令, 将编译出来的 头文件 和 动态库 复制到 指定的目录中, 即 ffmpeg-3.4/android/armv-75.编译结果 : 安装完成后的 头文件 和 库 , 在 --prefix=$PREFIX 配置选项中配置的结果输出路径是 ffmpeg-3.4/android/armv7-a;
编译失败大概率是因为版本错误, 确保使用以下版本进行编译 : ① Ubuntu 版本 : Ubuntu 16.04.4 64位 ② NDK 版本 : android-ndk-r14b ③ FFMPEG 版本 : ffmpeg-3.4 ;
(2) 使用编译脚本进行编译 执行 FFMPEG 编译脚本 :
1.脚本内容 : 在 Ubuntu 中创建一个shell 脚本, 注意 一定要在 Ubuntu 中创建, 在 Windows 中创建的脚本无法执行; *** Windows 与 Linux 中的换行符不一样 .*** 必须在 Ubuntu 中创建并编辑脚本; #!/bin/bash
echo "FFMPEG 编译脚本开始"
NDK=/root/FFMPEG/android-ndk-r14b
PLATFORM=$NDK/platforms/android-21/arch-arm
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
CPU=armv7-a
PREFIX=./android/$CPU
echo "FFMPEG 编译选项配置"
./configure \
--prefix=$PREFIX \
--target-os=android \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=arm \
--cpu=armv7-a \
--sysroot=$PLATFORM \
--extra-cflags="-I$PLATFORM/usr/include -fPIC -DANDROID -mfpu=neon -mfloat-abi=softfp " \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
--enable-shared \
--enable-runtime-cpudetect \
--enable-gpl \
--enable-small \
--enable-cross-compile \
--enable-asm \
--enable-neon \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-hwaccel=h264_mediacodec \
--disable-debug \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping
echo "FFMPEG 开始编译"
make
echo "FFMPEG 编译结束"
echo "FFMPEG 安装"
make install
echo "FFMPEG 编译脚本执行完毕"
2.脚本使用前提 : ① root 用户 执行脚本, ② NDK 与 FFMPEG 源码都放在 /root/FFMPEG/ 目录下, ③ 版本号都一致.3.在强调一遍版本号 : 编译失败大概率是因为版本错误, 确保使用以下版本进行编译 : ① Ubuntu 版本 : Ubuntu 16.04.4 64位 ② NDK 版本 : android-ndk-r14b ③ FFMPEG 版本 : ffmpeg-3.4 ;4.修改 shell 脚本权限 : chmod 777 ffmpeg_android.sh ;5.执行脚本 : ./ffmpeg_android.sh 开始执行该 shell 脚本;四. Android Studio 中项目导入 FFMPEG 及 配置 1. Android 项目创建 (1) 创建 工程 FFMPEG Android 工程创建 :
1.创建项目 : ① 名称 FFMPEG_ANDROID, ② 域名 ffmpeg.han, ③ 包名 han.ffmpeg, ④ 最重要一点 引入 C ++ 11 支持;
2.设置项目最低版本 : 选择 4.0 最低版本, 兼容 100% 机型; 不用修改 直接点下一步;
3.选择一个默认的空界面 : 不用修改 直接点下一步;
4.设置首界面的布局文件 : 不用修改 直接点下一步;
5.设置 C ++ 标准版本 : 这里选择 C++ 11 版本;
2. Android 项目 配置 ( 重点 ) (1) 项目配置 项目配置 :
1.拷贝 FFMPEG 头文件 到项目中 : 将 在 Ubuntu 编译出的 ffmpeg-3.4\android\armv7-a 目录下 的 include 目录***拷贝到 Android 项目的 app 目录下***, 与 src 目录, CMaleList.txt 是同一级文件;
2.在 native 代码中引入头文件 : 程序自动生成的是 cpp 文件, 这是 C++代码, FFMPEG 是 C 语言的库, 因此这里我们导入头文件的时候需要使用 extern “C” 修饰; 此时头文件 无法 进行提示, 编译也会报错; /*
* 此处在 C++ 文件中引用一个 C 文件库
* 需要使用 extern "C" 来说明, 表明使用和编译其中的代码都按照 C 语言的规范进行
*/
extern "C"
{
#include <libavcodec/avcodec.h>
}
C语言 与 C++ 函数在库中存放的内容是不一致的, 如果要使用 C 语言规范, 需要特别标识出来;
如果只导入了头文件, 编译不会报错, 但是调用方法的时候还是会报错的, 如果调用其中的方法, 还要配置动态库;(再次强调一遍)
3.CMakeList.txt 中配置头文件路径 : 使用 include_directories( 头文件相对路径 ) 进行配置, 配置了头文件路径后, 在 native 层的 C/C++ 代码中就可以导入其中的头文件; 添加了头文件路径后, 才能导入头文件, 此时导入头文件不报错;
如果只导入了头文件, 编译不会报错, 但是调用方法的时候还是会报错的, 如果调用其中的方法, 还要配置动态库;
头文件代码提示 : * ① 执行该配置, 然后 ② Build (菜单) -> Rebuild Project 重新编译后, 头文件代码才可以提示, Ctrl + 鼠标左键 操作即可跳转到头文件代码中;
#添加头文件的路径
#添加了头文件之后, 在 native-lib 目录下使用就不会报错了, 并可以使用 ctrl + 左键 直接跳转到该头文件中
#添加的路径是相对路径, include 就是与 该 CMakeList.txt 同级的 include 目录
include_directories(include)
4.拷贝动态库到项目中 : 将在 Ubuntu 16.04 中 编译后的动态库拷贝到项目中的 libs 目录下; ( 1 ) 拷贝来源 : 在 Ubuntu 16.04 中编译的输出结果在 ffmpeg-3.4/android/armv7-a/lib/ 目录下, 其中有 6 个动态库, 拷贝这 6 个动态库;( 2 ) 拷贝目的地 : 需要在 项目根目录/app/libs 目录下创建一个 Android 的 abi 指令集名称目录, 即 armeabi-v7a, 将 6 个动态库拷贝到 FFMPEG_ANDROID/app/libs/armeabi-v7a/ 目录中 ;
现在还是无法直接调用动态库 : 此时只是将动态库复制到了项目中, 还无法调用, 需要在 CMakeList.txt 中进行一系列的配置才能使用报错;
直接调用动态库的方法会
#添加动态库 (导入步骤 ①)
add_library( avcodec #动态名称
SHARED #库类型, 即 动态库
IMPORTED ) #导入方式
配置动态库需要三个步骤 : ① 添加动态库, ② 配置动态库路径, ③ 链接动态库 ;
6.配置动态库路径 : 使用下面的代码 设置 动态库路径; ${CMAKE_CURRENT_SOURCE_DIR} 变量使用 : 配置的路径必须获取 CMakeList.txt 的当前位置, 即 使用该变量获取 , 再以该当前位置为标准, 扩展到其它相对路径, 不能使用 libs/armeabi-v7a/libavcodec.so 路径进行配置, 否则会报错;
#设置 avcodec 动态库路径属性 (导入步骤 ②)
set_target_properties( avcodec #动态库名称
PROPERTIES #设置属性
IMPORTED_LOCATION #属性类型
${CMAKE_CURRENT_SOURCE_DIR}/libs/armeabi-v7a/libavcodec.so) #动态库的相对路径
7.链接动态库 : 在自动生成的 native-lib 链接配置中添加 avcodec 库的链接, 之后便可以在 代码中调用 avcodec 中的函数了;target_link_libraries( # Specifies the target library.
native-lib
avcodec #(导入步骤 ③)
# Links the target library to the log library
# included in the NDK.
${log-lib} )
8.指定编译指令集 :在 app/build.gradle 中 设定只编译 armeabi-v7a 一个指令集, 设定的位置是 android -> defaultConfig -> externalNativeBuild -> ndk 中;android {
... ...
defaultConfig {
... ...
externalNativeBuild {
... ...
ndk{
abiFilters "armeabi-v7a"
}
}
}
... ...
}
9.gradle 中 设置动态库路径 : 在 app/build.gradle 中设置 动态库路径 ; 使用 sourceSets 任务设置, 设置的路径是 android -> defaultConfig -> sourceSets; 这个路径是相对路径, 即 app/buid.gradle 的同级目录, 即 app/libs ; 注意 : 一定要使用 单引号, 凡是涉及到文件的设置一律使用单引号设置;
双引号会报错;
android {
... ...
defaultConfig {
... ...
sourceSets{
main{
jniLibs.srcDirs=['libs']
}
}
}
... ...
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "han.ffmpeg"
minSdkVersion 14
targetSdkVersion 21
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
//C ++ 标准版本
cmake {
cppFlags "-std=c++11"
}
//设置编译的过滤器, 这里我们只编译 armeabi-v7a 指令集的库
ndk{
abiFilters "armeabi-v7a"
}
}
sourceSets{
main{
jniLibs.srcDirs=['libs']
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#添加头文件的路径
#添加了头文件之后, 在 native-lib 目录下使用就不会报错了, 并可以使用 ctrl + 左键 直接跳转到该头文件中
#添加的路径是相对路径, include 就是与 该 CMakeList.txt 同级的 include 目录
include_directories(include)
#添加动态库 (导入步骤 ①)
add_library( avcodec #动态名称
SHARED #库类型, 即 动态库
IMPORTED ) #导入方式
#设置 avcodec 动态库路径属性 (导入步骤 ②)
set_target_properties( avcodec #动态库名称
PROPERTIES #设置属性
IMPORTED_LOCATION #属性类型
${CMAKE_CURRENT_SOURCE_DIR}/libs/armeabi-v7a/libavcodec.so) #动态库的相对路径
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
avcodec #(导入步骤 ③)
# Links the target library to the log library
# included in the NDK.
${log-lib} )
3. Android 项目 代码分析 (1) JNI 使用流程 简介 JNI 使用流程 :
1.创建 Native 层源文件 : 创建一个 C/C ++ 文件 native-lib.cpp, 该文件作为与 Java 层互动的接口文件;2.CMakeList.txt 中 配置该文件 : 设置 该 文件的 ①编译 配置 , 并***②查找 日志库***, 之后***③进行库的链接操作***;add_library( # 设置库的名称
native-lib
# 编译库的类型是 动态库
SHARED
# C/C++ 源文件的相对路径
src/main/cpp/native-lib.cpp )
find_library( # 设置要查找的库的名称
log-lib
# 指定想要 CMake 去定位的 NDK 库的名称
log )
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
/**
* 本地方法需要在 'native-lib' 本地库中实现,
* 这个本地库必须打包在本应用中.
*/
public native String stringFromJNI();
JNICALL
Java_han_ffmpeg_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello From C++";
return env->NewStringUTF(hello.c_str());
}
5.加载动态库 : 在文件的开头, 使用静态代码块加载动态库; // Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
6.使用 本地 方法 : 在界面中显示 从 本地 传来的 字符串; // Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
(2) 打印 FFMPEG 编译时的配置 打印 FFMPEG 编译配置 : 之前的步骤 ① Linux 平台编译 ② CMake 配置 ③ Gradle 配置 执行完后, 在执行下面的操作;
/*
* 此处在 C++ 文件中引用一个 C 文件库
* 需要使用 extern "C" 来说明, 表明使用和编译其中的代码都按照 C 语言的规范进行
*/
extern "C"
{
#include <libavcodec/avcodec.h>
}
2.获取 编译 配置方法 : 在 libavcodec/avcodec.h 中定义的 avcodec_configuration 方法, 返回一个字符串;/**
* Return the libavcodec build-time configuration.
*/
const char *avcodec_configuration(void);
//将编译时的配置信息返回给 Java 层, 并在界面中显示出来
std::string hello = avcodec_configuration();
4.执行项目 : 打印出的配置 正好是我们在 编译配置 选项中设置的 各种配置;本篇博客代码及资源下载 : https://download.csdn.net/download/han1202012/10382762