H264视频压缩算法现在无疑是所有视频压缩技术中使用最广泛,最流行的。随着 x264/openh264以及ffmpeg等开源库的推出,大多数使用者无需再对H264的细节做过多的研究,这大降低了人们使用H264的成本。H264压缩技术主要采用了以下几种方法对视频数据进行压缩。包括:
经过压缩后的帧分为:I帧,P帧和B帧:
除了I/P/B帧外,还有图像序列GOP。GOP是两个I帧之间的一个图像序列,在一个图像序列中只有一个I帧。如下图所示:
同样的画质和同样的码率,H265比H264占用的存储空间要少理论50%。如果存储空间一样大,那么意味着,在一样的码率下H.265会比H.264 画质要高一些理论值是30%~40%。
比起H.264,H.265提供了更多不同的工具来降低码率,以编码单位来说,最小的8x8到最大的64x64。信息量不多的区域(颜色变化不明显)划分的宏块较大,编码后的码字较少,而细节多的地方划分的宏块就相应的小和多一些,编码后的码字较多,这样就相当于对图像进行了有重点的编码,从而降低了整体的码率,编码效率就相应提高了。
H.265标准主要是围绕着现有的视频编码标准H.264,在保留了原有的某些技术外,增加了能够改善码流、编码质量、延时及算法复杂度之间的关系等相关的技术。H.265研究的主要内容包括,提高压缩效率、提高鲁棒性和错误恢复能力、减少实时的时延、减少信道获取时间和随机接入时延、降低复杂度。
1、版本
H.265是新的编码协议,也即是H.264的升级版。H.265标准保留H.264原来的某些技术,同时对一些相关的技术加以改进。新技术使用先进的技术用以改善码流、编码质量、延时和算法复杂度之间的关系,达到最优化设置;
2、降码率
比起H.264/AVC,H.265/HEVC提供了更多不同的工具来降低码率,以编码单位来说,H.264中每个宏块(macroblock/MB)大小都是固定的16x16像素,而H.265的编码单位可以选择从最小的8x8到最大的64x64;
3、新技术使用先进的技术用以改善码流、编码质量、延时和算法复杂度之间的关系,达到最优化设置;
4、采用了块的四叉树划分结构
H.265相比H.264最主要的改变是采用了块的四叉树划分结构,采用了从64x64~8x8像素的自适应块划分,并基于这种块划分结构采用一系列自适应的预测和变换等编码技术;
5、算法优化
H264由于算法优化,可以低于1Mbps的速度实现标清数字图像传送;H265则可以实现利用1~2Mbps的传输速度传送720P(分辨率1280*720)普通高清音视频传送;
6、同样的画质和同样的码率,H.265比H2.64 占用的存储空间要少理论50%;
7、占用的存储空间缩小
比起H.264/AVC,H.265/HEVC提供了更多不同的工具来降低码率,以编码单位来说,H.264中每个宏块(macroblock/MB)大小都是固定的16x16像素,而H.265的编码单位可以选择从最小的8x8到最大的64x64。那么,在相同的图象质量下,相比于H.264,通过H.265编码的视频大小将减少大约39-44%;
音视频基础:FLV封装格式介绍及解析 - 知乎 (zhihu.com)
AAC是高级音频编码(Advanced Audio Coding)的缩写,被认为是MP3的继任者,相对MP3有更高的压缩效率。由Fraunhofer IIS、杜比实验室、AT&T、Sony(索尼)等公司共同开发。出现于1997年,最初是基于MPEG-2的音频编码技术,目的是取代MP3格式。2000年,MPEG-4标准出台,AAC重新集成了其它技术包括SBR或PS特性,目前AAC可以定义为⼀种由 MPEG-4 标准定义的有损音频压缩格式。AAC被iPhone、iTunes以及大多数便携式设备所使用。
(1). AAC是一种高压缩比的音频压缩算法,但它的压缩比要远超过较老的音频压缩算法,如AC-3、MP3等。并且其质量可以同未压缩的CD音质相媲美。
(2). 同其他类似的音频编码算法一样,AAC也是采用了变换编码算法,但AAC使用了分辨率更高的滤波器组,因此它可以达到更高的压缩比。
(3). AAC使用了临时噪声重整、后向自适应线性预测、联合立体声技术和量化哈夫曼编码等最新技术,这些新技术的使用都使压缩比得到进一步的提高。
(4). AAC支持更多种采样率和比特率、支持1个到48个音轨、支持多达15个低频音轨、具有多种语言的兼容能力、还有多达15个内嵌数据流。
(5). AAC支持更宽的声音频率范围,最高可达到96kHz,最低可达8KHz,远宽于MP3的16KHz-48kHz的范围。
(6). 不同于MP3及WMA,AAC几乎不损失声音频率中的甚高、甚低频率成分,并且比WMA在频谱结构上更接近于原始音频,因而声音的保真度更好。
(7). AAC采用优化的算法达到了更高的解码效率,解码时只需较少的处理能力。
在如火如荼的直播行业中,RTMP是一个重要的协议,它在实时音视频场景中使用非常广泛,而且目前市占率很高。
RTMP协议是应用层协议,是要靠底层可靠的传输层(TCP)。协议(通常是TCP)来保证信息传输的可靠性的。在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接。播放一个RTMP协议的流媒体需要经过以下几个步骤:握手,建立网络连接,建立网络流,播放。服务器和客户端之间只能建立一个网络连接,但是基于该连接可以创建很多网络流。
为什么传输层已经建立了TCP连接,RTMP还需要再次建立一个连接,有这个必要吗?
因为它们需要商量一些事情,保证以后的传输能正常进行。主要就是两个事情,一个是版本号,如果客户端、服务器的版本号不一致,则不能工作。另一个就是时间戳,视频播放中,时间是很重要的,后面的数据流互通的时候,经常要带上时间戳的差值,因而一开始双方就要知道对方的时间戳。
RTMP拉流的核心流程如下:
推流过程:
RTP全名是Real-time Transport Protocol(实时传输协议)。RTP用来为IP网上的语音、图像、传真等多种需要实时传输的多媒体数据提供端到端的实时传输服务。RTP为Internet上端到端的实时传输提供时间信息和流同步,但并不保证服务质量,服务质量由RTCP来提供。
RTP是一种运行在传输层的协议,通常基于 UDP 协议,但也支持 TCP 协议。RTP数据包由两部分组成,一部分是RTP Header,一部分是RTP Payload,RTP Header占用最少12个字节,最多72个字节;另一部分是RTP Payload,用来封装实际的数据负载,例如h264的裸码流数据。
当应用程序提供RTSP协议建立一个RTP会话时,应用程序将确定一对目的传输地址(一个网络地址和两个端口号),其中两个端口号中的偶数端口是分配给RTP进行裸码流数据传输的,奇数端口则是分配给RTCP进行传输控制的。
RTP需要RTCP为其服务质量提供保证,因此下面介绍一下RTCP的相关知识。
RTCP的主要功能是:服务质量的监视与反馈、媒体间的同步,以及多播组中成员的标识。在RTP会话期间,各参与者周期性地传送RTCP包。RTCP包中含有已发送的数据包的数量、丢失的数据包的数量等统计资料,因此,各参与者可以利用这些信息动态地改变传输速率,甚至改变有效载荷类型。RTP和RTCP配合使用,它们能以有效的反馈和最小的开销使传输效率最佳化,因而特别适合传送网上的实时数据。
RTCP功能:
1、服务质量的监视与反馈
在RTP会话期间,每个会话参与者周期性地向所有其他参与者发送RTCP控制信息包。每个RTCP信息包不封装声音数据或者电视数据,而是封装发送端或接收端的统计报表。这些信息包括发送的信息包数目、丢失的信息包数目和信息包的抖动等情况,这些反馈信息反映了当前的网络状况,对发送端、接收端或者网络管理员都非常有用。
2、确定 RTP用户源
RTCP为每个RTP用户提供了一个全局唯一的规范名称 (Canonical Name)标志符 CNAME,接收者使用它来追踪一个RTP进程的参加者。当发现冲突或程序重新启动时,RTP中的同步源标识符SSRC可能发生改变,接收者可利用CNAME来跟踪参加者。同时,接收者也需要利用CNAME在相关RTP连接中的几个数据流之间建立联系。当 RTP需要进行音视频同步的时候,接受者就需要使用 CNAME来使得同一发送者的音视频数据相关联,然后根据RTCP包中的计时信息(Network time protocol)来实现音频和视频的同步。
3、控制 RTCP传输间隔
由于每个对话成员定期发送RTCP信息包,随着参加者不断增加,RTCP信息包频繁发送将占用过多的网络资源,为了防止拥塞,必须限制RTCP信息包的流量,控制信息所占带宽一般不超过可用带宽的 5%,因此就需要调整 RTCP包的发送速率。由于任意两个RTP终端之间都互发 RTCP包,因此终端的总数很容易估计出来,应用程序根据参加者总数就可以调整RTCP包的发送速率。
4、传输最小进程控制信息
这项功能对于参加者可以任意进入和离开的松散会话进程十分有用,参加者可以自由进入或离开,没有成员控制或参数协调。
好的网络下视音频能够得到及时的发送,不会造成视音频数据在本地的堆积,直播效果流畅,延时较小。而在弱网网络环境下,视音频数据发送不出去,则需要我们对视音频数据进行处理。
差网络环境下对视音频数据一般有四种处理方式:缓存区设计、网络检测、丢帧处理、降码率处理。
在ff_ffplay_options.h中,找到如下代码:
可以通过修改 framedrop 的值来解决不同步的问题,framedrop 是在视频帧处理不过来的时候丢弃一些帧达到同步的效果。具体设置,在上层Java层中IjkVideoView中:
默认ijkplayer中是1,你可以自行,修改这个值。
在Android中,如果使用了硬编进行编码,在差网络环境下,我们可以实时改变硬编的码率,从而使直播更为流畅。当检测到网络环境较差的时候,在丢帧的同时,我们也可以降低视音频的码率。在Android sdk版本大于等于19的时候,可以通过传递参数给MediaCodec,从而改变硬编编码器出来数据的码率。
Bundle bitrate = new Bundle();
bitrate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bps * 1024);
mMediaCodec.setParameters(bitrate);
在做音频播放的时候,使用的是开源的ijkplayer播放器,ijkplayer解码使用的是ffmpeg,声音输出使用的是audiotrack,在某机型上面播放遇到锁屏、返回后台、点击home键的时候会出现声音卡顿的现象,会输出下面的log。
W/AudioTrack: releaseBuffer() track 0xcce8b600 disabled due to previous underrun, restarting
实现播放调用步骤是在AsyncTask中,查阅资料发现是因为在线程中播放造成的问题,经过查看asynctask构造方法发现,asynctask会把线程的优先级设置为THREAD_PRIORITY_BACKGROUND后台线程,于是我将线程的优先级设置为THREAD_PRIORITY_URGENT_AUDIO,解决了播放卡顿的问题,我猜测播放线程优先级降低,系统分配时间片会减少,会导致底层ijk读数据输出数据时得不到及时的回应,audiotrack频繁的releasebuffer,restarting声道,造成卡顿。
通过修改源文件,因为ijkplayer实际上是基于ffplay.c实现的:ijkmedia>ijkplayer>ff_ffplay.c这个文件
static double vp_duration(VideoState *is, Frame *vp, Frame *nextvp) {
if(vp->serial != nextvp->serial) {
double duration = nextvp->pts - vp->pts;
if(isnan(duration) || duration <= 0|| duration > is->max_frame_duration)
return vp->duration;
else
return duration;
}else{
return 0.0;
}
}
直接换成:
static double vp_duration(VideoState*is,Frame*vp,Frame*nextvp) {
return vp->duration;
}
接着改staticintffplay_video_thread这个方法:
static int ffplay_video_thread(void*arg){
FFPlayer*ffp = arg;
VideoState*is = ffp->is;
AVFrame*frame =av_frame_alloc();
doublepts;
doubleduration;
intret;
AVRationaltb = is->video_st->time_base;
//注释如下一行代码
//AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL);
//......省略部分代码
//注释如下一行代码
//duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational) {frame_rate.den, frame_rate.num}) : 0);
//直接这里写出
duration=0.01;
//........
}
改完后发现延迟明显降低,高分辨率开启硬解码,不支持的话会自动切换到软解,就算开启mediacodec,如果设备不支持,显示的解码器也是avcodec软解。
ijkplyer 在播放部分视频时,调用seekTo的时候,会跳回到拖动前的位置,这是因为视频的关键帧的问题(GOP导致的),视频压缩比较高,而seek只支持关键帧,出现这个情况就是原始的视频文件中i帧比较少,播放器会在拖动的位置找最近的关键帧。所以,目前针对此问题ijkPlayer无解。
注:目前最新版0.8.8相对于之前的版本来说,seekTo的问题应该会小很多,但是不可避免的还是会存在。相关代码:
setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1);
ijk是在ijkiocache.c中开启缓存的:
ijkio_cache_read()函数是读取视频数据的方法,包括网络读取和文件读取;下面是ijkio_cache_read函数的主要内容:
if (c->cache_file_close) { //是缓存读关闭了还是写关闭了?应该是写
return wrapped_url_read(h, dest, to_read); //如果缓存关闭了,就从网络读取
}
if (!c->cache_file_forwards_capacity) {
//如果没有设置cache_file_forwards_capacity就开启同步读,否则从本地文件读取
ret = ijkio_cache_sync_read(h, buf, size);
if(ret >=0) {
c->read_logical_pos+= ret;
}
call_inject_statistic(h);
return (int)ret;
}
ijkio_cache_sync_read():
先判读能不能从文件读;如果不能就从网络加载,如果从网络加载会调用wrapped_url_read读取并且会调用sync_add_entry往本地写入,sync_add_entry会调用write(c->fd, buf, size)方法;
cache_file_forwards_capacity:向前缓存的大小,比如1M,如果cache_file_forwards_capacity大于0,那么ijkio_cache_task也会开启写缓存操作,那么上面的缓存写就不生效了。
IjkIOCacheContext和IjkCacheEntry详解:
logical_size-文件大小,read_logical_pos已读缓存大小,read_inner_pos-读的网络的位置,io_eof_reached读到缓存末尾,cache_physical_pos-已经缓存到的位置,last_physical_pos-上一次缓存的位置,only_read_file-初始化时设置只读不写模式。
描述了进入时开始进入缓存的位置,logical_pos-进入时的逻辑位置,physical_pos-进入时的实际位置,size-已经缓存的大小。
ijkio_cache_sync_read是进行读操作的函数: int64_t physical_target = entry->physical_pos + in_block_pos; //计算缓存开始读取的位置。
IjkCacheEntry,IjkCacheTreeInfo,IjkAVTreeNode:
IjkCacheTreeInfo包含一个根root IjkAVTreeNode,IjkAVTreeNode中的elem是IjkCacheEntry类型;IjkCacheTreeInfo中的值分别是hyu文件中tree开头的的,IjkCacheEntry中的值是hyu中entry开头的。