视频标准: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、VQFhttp://baike.baidu.com/link?url=tmkYau9u-YKkCiszIW540P4SBglTEos4fdV_32AXKvGlxEdS_1sDklaV2IVFG3afF2jLRq3Mit7J_9lUkFOMtK
-- 视频格式有:rm,rmvb,mpeg1-4 mov mtv dat wmv avi 3gp amv dmvhttp://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(); } }
