视频标准:MEPG阵营和Google开源阵营。
MPEG-2,VC1,H.263,H.264/AVC,H.265/HEVC,VP9,AV1——所有这些标准都建立在基于块的混合视频编码结构上。
通用视频编码标准(VVC,Versatile Video Coding)。
FFmpeg自带了H264、AAC、MP3的解码器,但却没有(或没有好的)相应的编码器。相应的编码器需要使用第三方库。推荐使用的第三方库为x264(H264编码) 、FDK_AAC(AAC编码),lame(MP3编码)。LAME-MP3编码引擎(音频)。
> 音视频格式
即时通信IM中语音格式可以使用amr;视频格式可以使用mp4。
-- 音频格式有:1、CD;2、OGG;3、MP3;4、ASF、WMA;5、WAV;6、MP3PRO;7、RM;8、REAL;9、APE;10、MODULE;11、MIDI;12、VQF
http://baike.baidu.com/link?url=tmkYau9u-YKkCiszIW540P4SBglTEos4fdV_32AXKvGlxEdS_1sDklaV2IVFG3afF2jLRq3Mit7J_9lUkFOMtK
-- 视频格式有:rm,rmvb,mpeg1-4 mov mtv dat wmv avi 3gp amv dmv
http://zhidao.baidu.com/link?url=JDNs5AynU63rpqUtglCCSyTuEQfG00f3J1lW_Pq5rUfDfrd-YHna8GTGP1VUCIjK1qqkutl1zpSccIKbehv4_a
http://baike.baidu.com/link?url=JDNs5AynU63rpqUtglCCS6jXiHuTdqajUguPF_o54IgNZ4ba4bjWA5vdqlJJCt7R680FtghgUj_nSwAh3qqr5K
-- 直播中可用的音视频数据涉及技术或协议:编码方式:CBR、VBR编码格式
视频:H.265、H.264、MPEG-4等,封装容器有TS、MKV、AVI、MP4等
音频:G.711μ、AAC、Opus等,封装有MP3、OGG、AAC等
常见的音频压缩格式有:MP3、AAC、OGG、WMA、Opus、FLAC、APE、m4a和AMR等。
常见的视频封装格式有:MP4、3GP、AVI、MKV、WMV、MPG、VOB、FLV、SWF、MOV、RMVB和WebM等。
Android录音支持的格式有amr、aac
> 中国的音视频标准
中国XAVS2实时编码器开源地址- https://github.com/pkuvcl/xavs2
中国XAVS2实时编码器开源地址- https://gitee.com/pkuvcl/xavs2
> Google开源阵营
-- YUV 处理库
https://chromium.googlesource.com/libyuv/libyuv/ ,Google 开源的一个 YUV 处理库,上面只针对 1080p->540p 视频帧缩放的算法,而对于通用的压缩处理,可以直接使用这里的实现,对比起 FFmpeg 的速度快上不少。
-- VP8 的开源实现:libvpx:
libvpx 是 VP8 的唯一开源实现,由 On2 Technologies 开发,Google 收购后将其开放源码,License 非常宽松可以自由使用。
编码器的选择之VP8:
VP8 是一个开放的视频压缩格式,最早由 On2 Technologies 开发,随后由 Google 发布。同时 Google 也发布了 VP8 编码的实做库:libvpx,以 BSD 授权条款的方式发行,随后也附加了专利使用权。而在经过一些争论之后,最终 VP8 的授权确认为一个开放源代码授权。
目前支持 VP8 的网页浏览器有 Opera、Firefox 和 Chrome。
-- VP9 的开源实现:ibvpx:
libvpx 是 VP9 的唯一开源实现,由 Google 开发维护,里面有部分代码是 VP8 和 VP9 公用的,其余分别是 VP8 和 VP9 的编解码实现。
vpx则是Google推出的开源视频编码器,它提出的VP9编码标准的性能也不错。
Google已在几年前已经决定Chrome浏览里放弃H.264标准的支持,转而只支持自家的VP8(最新版是VP9)编码格式。而且,2011年被Google收购的著名实时音视频方案GIPS——已被Google开源多年并取名为WebRTC,WebRTC中的实时音视频编码默认使用的就是VP8标准。
H.264(下一代是H.265)和VP8(下一代是VP9)编码两种:
1.采用 H.264 视频编码和 AAC 音频编码的 MP4 文件(H.264/AAC/MP4 组合);
2.采用 VP8 视频编码和 Vorbis 音频编码的 WebM 文件(VP8/Vorbis/WebM 组合);
通常「H.264」指代 H.264/AAC/MP4 这个组合,而「WebM」指代 VP8/Vorbis/WebM 这个组合。
2010 年中的时候 Google 宣布将 VP8 永久免费。Google 又基于开源容器格式 Matroska 开发了 WebM 容器格式,用以封装 VP8 编码的视频和 Vorbis 编码的音频。随后 Google 连同 Mozilla 和 Opera,准备将 VP8/Vorbis/WebM (统称为 WebM)推广为网络视频的通用格式。Google Chrome 浏览器在 WebM 发布后迅速更新为同时支持 Theora、H.264、WebM 三种格式。Mozilla 和 Opera 也宣布将在其浏览器的后续版本(主要是 Firefox 4)中原生支持 WebM。
MPEG LA 将 H.264 专利许可分为两种:H.264 编码器和解码器(不论软硬件)厂商需要购买一种 H.264 的专利许可协议,H.264 编码的视频的分发商(如电视台等)需要购买另外一种 H.264 的专利许可协议。
openh264(https://github.com/cisco/openh264)则是由思科开源的另外一个h264编码器,项目在2013年开源。
> MEPG阵营
-- x264
openh264思科开源的另外一个h264编码器- https://github.com/cisco/openh264
官网x264编码的源代码- http://www.videolan.org/developers/x264.html
Android NDK 编译 FFmpeg + x264 + fdk-aac (arm/x86)的配置脚本- http://blog.csdn.net/panda1234lee/article/details/53099203
x264的开源库在android中的调用方法-- http://www.yanfaw.com/technology/201108/16/435.html
最简单的视频编码器:编译(libx264,libx265,libvpx)- http://blog.csdn.net/leixiaohua1020/article/details/42069383
x264,x265,vpx这三个开源的视频编码器可以说是当今“最火”的视频编码器。x264现在占据着H.264视频编码器的半壁江山;x265则是目前实现H.265标准最好的开源视频编码器,并且在未来可能接替x264;而vpx则是Google推出的开源视频编码器,它提出的VP9编码标准的性能也不错。
视频转码技术就不得不提到两个开源工程:ffmpeg和x264。
-- h264硬编解码(Android)
从技术下游(SDK 使用方)跑到了技术上游(SDK 提供方)。
H264编码片,I/P/B/SP/SI帧;普通视频的I/P/B帧。I帧内预测,P帧间预测,B双向预测帧。
-- OpenH264;x264
H.264 的开源实现:OpenH264;x264
OpenH264 是思科实现的开源 H.264 编码,项目在 2013 年开源,虽然 H.264 需要交纳不菲的专利费用,但是专利费有一个年度上限,思科把 OpenH264 实现的年度专利费交满后,另外,firefox 直接内置了 OpenH264,作为其在 WebRTC 中的视频编解码器使用。OpenH264 事实上就可以免费自由的使用了。
x264 是一个采用 GPL 授权的视频编码自由软件。x264 的主要功能在于进行 H.264/MPEG-4 AVC 的视频编码,而不是作为解码器(decoder)之用。
除去费用问题比较来看:
OpenH264 CPU 的占用相对 x264低很多;OpenH264 只支持 baseline profile,x264 支持更多 profile。
-- H.265 的开源实现:libde265、x265:
libde265 HEVC 由 struktur 公司以开源许可证 GNU LesserGeneral Public License (LGPL) 提供,观众可以较慢的网速下欣赏到最高品质的影像。跟以前基于H.264标准的解码器相比,libde265 HEVC 解码器可以将您的全高清内容带给多达两倍的受众,或者,减少 50%流媒体播放所需要的带宽。高清或者 4K/8K 超高清流媒体播放,低延迟/低带宽视频会议,以及完整的移动设备覆盖。具有「拥塞感知」视频编码的稳定性,十分适合应用在 3/4G 和 LTE 网络。
x265 是由 MulticoreWare 开发,并开源。采用 GPL 协议,但是资助这个项目的几个公司组成了联盟可以在非 GPL 协议下使用这个软件。
-- FFmpeg
FFmpeg 是一个自由软件,可以运行音频和视频多种格式的录影、转换、流功能,包含了 libavcodec -这是一个用于多个项目中音频和视频的解码器库,以及 libavformat -一个音频与视频格式转换库。
FFmpeg 这个单词中的 FF 指的是 Fast Forward。这个项目最初是由 Fabrice Bellard 发起的,而现在是由 Michael Niedermayer 在进行维护。许多 FFmpeg 的开发者同时也是 MPlayer 项目的成员,FFmpeg 在 MPlayer 项目中是被设计为服务器版本进行开发。
所谓容器,就是把编码器生成的多媒体内容(视频,音频,字幕,章节信息等)混合封装在一起的标准。容器使得不同多媒体内容同步播放变得很简单,而容器的另一个作用就是为多媒体内容提供索引,也就是说如果没有容器存在的话一部影片你只能从一开始看到最后,不能拖动进度条(当然这种情况下有的播放器会话比较长的时间临时创建索引),而且如果你不自己去手动另外载入音频就没有声音。
我们在流媒体传输,尤其是直播中主要采用的就是 FLV 和 MPEG2-TS 格式,分别用于 RTMP/HTTP-FLV 和 HLS 协议。
实时音视频通讯 = 音视频处理 + 网络传输。包括采集、编码、网络传输、解码、播放等环节。音视频处理中最为关键的视频编解码是个头等重要的问题,对于开发者来说,以目前所能找到的技术资源以及应用的普及程度,因为背靠巨头,H.264(最新版是H.265,微软和苹果一直都在背后力推)和VP8(最新版是VP9,由Google力推)是最为主流的两种编码。
-- 将H264转为MP4
mp4parser- https://github.com/sannies/mp4parser/releases
-- 实时视频传输的关键技术 H.264 全解析- http://blog.csdn.net/Byeweiyang/article/details/78134674
H.264 码流传输的基本单元是 NAL 单元,NAL 单元内携带的最关键的数据是参数集和片数据;解码的基本单元是宏块,解码器根据预测信息和残差数据,解码出原始数据;宏块解码之后拼接成片,片拼接成图像,而一幅幅图像则构成了视频!
编码基本框架:预测编码、变换编码、熵编码。
无论编码器的结构如何,相应的视频编码的控制都是编码器实现的核心问题。在编码过程中,并没有直接控制编码数据大小的方式,只能通过调整量化过程的量化参数 QP 值间接控制,而由于 QP 和编码数据大小并没有确定的关系,所以编码器的码率控制无法做到很精细,基本都靠试。要么是中途改变后续宏块的质量,要么是重新编码改变所有宏块的质量。
解码过程就是编码的逆过程:熵解码、变换解码、预测解码。
以宏块为单位,依次进行熵解码、反量化、反变换,得到残差数据,再结合宏块里面的预测信息,找到已解码的被参考块,进而结合已解码被参考块和本块残差数据,得到本块的实际数据。宏块解码后,组合出片,片再组合出图像。
-- 可伸缩编码(Scalable Video Coding, SVC)实质上是将视频信息按照重要性分解,对分解的各个部分按照其自身的统计特性进行编码。一般它会将视频编码为一个基本层和一组增强层。基本层包含基本信息,可以独立解码,增强层依赖于基本层,可以对基本层的信息进行增强,增强层越多,视频信息的恢复质量也就越高。
SVC 通常有三种:
1.空域可伸缩:可以解码出多种分辨率的视频;
2.时域可伸缩:可以解码出多种帧率的视频,分辨率相同;
3.质量可伸缩:可以解码出多种码率的视频,分辨率、帧率相同;
> android MediaCodec 实现h264硬编解码全过程- http://download.csdn.net/detail/b_xjie/8744773
MediaCodec 实现h264硬编解码全过程,视频数据(onPreviewFrame)从摄像头读出 yv12格式,转换为I420,投递给encoder,再从encoder取出编码后的h264数据投递给decoder后显示到surfaceView; 实现了udp将h264数据发送到指定主机,可通过vlc播放; 备有可以读取本地264文件流投递给解码器播放; 小米 4.4.2 测试通过.
/**
* H264编码
*
* @param input
* @param output
* @return
*/
private int offerEncoder(byte[] input, byte[] output) {
int pos = 0;
try {
ByteBuffer[] inputBuffers = mMediaEncoder.getInputBuffers();
ByteBuffer[] outputBuffers = mMediaEncoder.getOutputBuffers();
int inputBufferIndex = mMediaEncoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
Log.d(TAG, "offerEncoder InputBufSize: " + inputBuffer.capacity() + " inputSize: " + input.length + " bytes");
inputBuffer.clear();
inputBuffer.put(input);
mMediaEncoder.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mMediaEncoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] data = new byte[bufferInfo.size];
outputBuffer.get(data);
Log.d(TAG, "offerEncoder InputBufSize:" + outputBuffer.capacity() + " outputSize:" + data.length + " bytes written");
if (mMediaHead != null) {
System.arraycopy(data, 0, output, pos, data.length);
pos += data.length;
} else // 保存pps sps 只有开始时 第一个帧里有, 保存起来后面用
{
Log.d(TAG, "offer Encoder save sps head,len:" + data.length);
ByteBuffer spsPpsBuffer = ByteBuffer.wrap(data);
if (spsPpsBuffer.getInt() == 0x00000001) {
mMediaHead = new byte[data.length];
System.arraycopy(data, 0, mMediaHead, 0, data.length);
} else {
Log.e(TAG, "not found media head.");
return -1;
}
}
mMediaEncoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mMediaEncoder.dequeueOutputBuffer(bufferInfo, 0);
}
if (output[4] == 0x65) { // key frame 编码器生成关键帧时只有 00 00 00 01 65 没有pps
// sps, 要加上
System.arraycopy(output, 0, input, 0, pos);
System.arraycopy(mMediaHead, 0, output, 0, mMediaHead.length);
System.arraycopy(input, 0, output, mMediaHead.length, pos);
pos += mMediaHead.length;
}
} catch (Throwable t) {
t.printStackTrace();
}
return pos;
}
/**
* H264解码
*
* @param input
* @param length
*/
private void offerDecoder(byte[] input, int length) {
try {
ByteBuffer[] inputBuffers = mMediaDecoder.getInputBuffers();
int inputBufferIndex = mMediaDecoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
long timestamp = mFrameIndex++ * 1000000 / FRAME_RATE;
Log.d(TAG, "offerDecoder timestamp: " + timestamp + " inputSize: " + length + " bytes");
inputBuffer.clear();
inputBuffer.put(input, 0, length);
mMediaDecoder.queueInputBuffer(inputBufferIndex, 0, length, timestamp, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mMediaDecoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0) {
Log.d(TAG, "offerDecoder OutputBufSize:" + bufferInfo.size + " bytes written");
// If a valid surface was specified when configuring the codec,
// passing true renders this output buffer to the surface.
mMediaDecoder.releaseOutputBuffer(outputBufferIndex, true);
outputBufferIndex = mMediaDecoder.dequeueOutputBuffer(bufferInfo, 0);
}
} catch (Throwable t) {
t.printStackTrace();
}
}