音视频编解码工程优化新思路

背景技术

所谓高频访问即对程序内存区域有效性进行访问会极大降低了系统的性能,而不对内存区域有效性进行校验,有存在内存越界导致软件不正确,不正常甚至无端的崩溃的问题。这个问题又称之为稳定性。由于系统资源的受限,稳定性和性能方面只能二选一。这个在音视频领域尤其明显。音视频编解码的过程中,对内存有效性进行检验的话,导致了CPU流水线中断,CPU利用率暴涨。如果预先申请大量的内存,又会导致内存资源极大浪费,又不能彻底的根治这个问题。

发明内容

本发明的目的是为了克服现有技术中的不足,为了解决系统性能以及稳定性、灵活性问题,提供一种高频访问内存的程序优化方法。

本发明的目的是通过以下技术方案实现的:

一种用于音视频编码的高频访问内存的程序优化方法,首先设置以下条件:CPU同步检查虚拟页的使用情况;CPU将没有权限的流程控制及时交给操作系统;操作系统通过运行时环境将控制权交给应用程序并提供应用程序支持异常的行为;应用程序申请各类权限组合的虚拟存储区域并改变所述虚拟存储区域的部分权限;

其次依据音视频类型预先配置在一段连续的内存内部,然后将所述连续的内存最后一页设置为无读写权限;通过静态方式和动态方式控制码流以及时返回信息,所述动态方式通过终止编码流程和调整编码参数完成。

所述权限包括无读写权限、只读权限和读写权限。

所述动态方式包括:CPU/GPU利用率高时,调用相应函数降低CPU/GPU利用率;网络带宽紧张的情况下,扩大量化的步长,采用分层思路,减少分层措施,降低音视频的质量,直至调整到视觉与听觉可以接受的区间;网络带宽充足的情况下,降低量化的步长,采用分层思路,增加分层措施,增强音视频的质量,直至调整到视觉与听觉可以接受的区间;CPU/GPU资源空闲情况下,优先确保音视频质量。

依据音视频类型预先申请在一段连续的内存,包括守卫页数,将所述守卫页数设置为无读写权限。

一种用于音视频解码的高频访问内存的程序优化方法,首先检测满足以下条件:CPU同步检查虚拟页的使用情况;CPU将没有权限的流程控制及时交给操作系统;操作系统通过运行时环境将控制权交给应用程序并提供应用程序支持异常的行为;应用程序申请各类权限组合的虚拟存储区域并改变所述虚拟存储区域的部分权限;

解码是编码的逆行工程,其次进行码流分析:若是视频,分析其宽、高及YUV/RGB类型,申请相应内存的同时包括相应的守卫页;若是音频,分析其采样率、声道数和采样位数,申请相应内存的同时包括相应的守卫页;然后开始解码。

静态方式解码可以拦截内存访问越界,而动态方式在基础上,更加优化了内存,因为解码只需申请部分内存即可。

与现有技术相比,本发明的技术方案所带来的有益效果是:

本发明方法极大降低了对内存的需求,同时还增强系统缓存的能力,因为操作系统往往对每一个进程实际内存总数总数受限的,该方式提供了提高高速缓存命中率,虚拟内存的交换,拦截内存访问读写越界,因此提升了性能和稳定性。并可以动态实时统计更新编码相应的信息。

具体实施方式

下面结合具体操作对本发明作进一步的描述:

要实现本发明的功能必须存在以下几个先决条件:1. CPU需要同步的检查虚拟页的使用情况,包括页的权限等等;2 .CPU需要将没有相应的权限的流程控制及时的交给操作系统;3.操作系统通过运行时环境可以将控制权移交给应用程序;4.运行时环境可以提供应用程序需要支持异常的行为;5.应用程序可以申请各类权限组合的虚拟存储区域,包括无读写权限,只读权限,读写权限等等;6.应用程序可以动态的改变虚拟存储区域某一部分的权限;7. 应用程序有机制来实现初始化页面访问权限异常处理方式;

页的大小:往往跟CPU体系和操作系统双方来决定,他们通过存储管理单元来设置每一页的大小 ,一般情况下,32位的操作系统情况下,页的大小往往总是4096记作pagesize。

初始化页面访问权限异常处理方式:

这里重点讲述一下这个问题,如果是windows比较简单用seh即可。linux/mac/ios/andriod采用信号机制。

实现方式如下:

第一种实现方式

首先安装页无权限访问的信号处理例程,然后保存初始化好相应的各个寄存器以及CPU的内部的寄存器。如果发生的页无权限回调,将根据相应的情况进行对各个寄存器进行调整。提前终止,或者重新执行某一条指令。或者跳转到预先设定地址往下执行。

第二种实现方式

1.所有创建的虚拟存储区域应该都是共享的;

2.通过fork方式,通过管道的方式传输数据,创建信号量1,默认为无信号,在子进程中安装页无权限访问的信号处理例程,然后在页无权限访问的信号处理例程判断是否正常,如果不正常,则进程退出,返回-1;

3.父进程则一直等待子进程退出和监控信号量1,然后父进程判断子进程退出码来判断是否处理正常然后判断信号量1在超时时间内有无信号,有则正常流程,没有则继续流程3。如果子进程数据已经足够,则触发信号量1,父进程可以正常的执行流程。

本方法可以将fork函数替换为vfork函数,参数同时让父子进程共享共享存储区域,而不必要建立管道,这样的效果是一致的,而且更加简单。

以上是c++的处理流程,不同的语言有不同的实现方式。但是基本原理是一致,另外不同的编译器对异常处理流程也可能稍有不同,linux下面可以用信号捕获的方式。为使描述简洁方便,以下描述中使用的英文字符表示的含义分别有,CPU:中央处理器;GPU:图形处理器;YUV/RGB系列等其他系列:视频解码后存在的数据格式;PCM:音频解码后存在的数据格式;H264/H265/VP8/VP9/svac等其他视频编码:视频压缩编码后的数据格式;ogg、opus、aac、mp3等为音频压缩编码后存在的数据格式。

通过这个方式还有另外方面需要注意:

1.在页面访问权限异常中操作要尽量简洁;

2.如果发生了页面访问权限异常,而且是返回失败的情况下,不应该使用相应的参数,应该要及时的加以初始化;

3.应该在前面一次性都初始化好相应的参数以及申请好相应的资源。

一、进一步的,以音视频编码的处理流程为例:

在音视频编码过程中,需要及时的返回信息,提供编码的灵活性,比如控制码流。图像质量以及视频编码的分层,可伸缩可扩展等等其他特性的功能。也可以用于评估性能,编码各功能的组合,以及系统资源使用情况。

比如控制码流的逻辑:本实施例采用2种方式。一种就是静态的方式。通过指派视频的类型(I/B/P/si/sp)先预先申请好一定内存数量(向上取为页的整数倍)+pagesize(这一段内存必须连续的)。然后将最后的一页设置没有读写的权限。方式2则体现在编码的灵活性、性能、音视频质量的三方面根据权重更加优化。

方式一为静态方式:

1.一次性申请合适的页数(包括守卫页)。可以预先设置若干内存检测点,内存检测点即将内存去掉读写权限,最后一页必须是无读写权限;

2.初始化编码参数;

3.初始化页面访问权限异常处理;

4.正常的编码流程,直到碰到内存检测点或者最后一页;

5.如果是内存检测点,统计系统资源,包括CPU资源,码流预期所需要的带宽,然后修改这些内存页面为可读写,也可以一次性设置若干页为可读写,但是不能修改最后一页;

6.如果是最后一页,说明码流已经超过最大允许的范围,就返回失败;上层可以重新进行是否修改相应的编码参数重新编码,这个最大允许的范围最好根据输出音视频的类型编码(aac/mp3/svac/vp8/vp9/h264 avc/h264 svc/hevc/ogg/opus等等),视频的帧类型(I帧/b帧/p帧/si/sp帧)来划分,这一方式比较简洁简单,不需要太多的技巧,通用性强。

方式二为动态方式,如下:

1.一次性申请若干页,可以预先设置若干内存检测点,内存检测点即将内存去掉读写权限,最后一页必须是无读写权限;

2 .初始化编码参数;

3.初始化页面访问权限异常处理;

4.正常的编码流程,内存检测点或者最后一页;

5.如果是内存检测点;统计系统资源,包括CPU资源,码流预期所需要的带宽,通过调整相应的编码参数,比如调整步长,关闭或者开启相应的功能,改变编码的结构(分层结构,是否只需要基础层),帧场自适应参数设置,是否第二次细致编码(部分);

6.周而复始 5的步骤;在稳定以及系统资源损耗可以接受情况下,灵活的适用于编码的各项技术,根据系统的资源及时调整编码参数,达到音视频质量,系统资源利用率,程序稳定灵活高度一致。

使用动态的方式的话能快速的终止编码的流程,或者及时调整相应的编码参数。初始化编码器重新再编码,如果编码的过程中,在内存检测点通过探测系统的资源,及时的调整编码的参数。一个方面同时应用于音视频编码所有方面,有一定的灵活性。另一方面改变编码的结构,比如对分层结构进行调整,帧场自适应。

动态编码的灵活性还具有以下几点:CPU/GPU利用率较高情况,调用系统函数降低CPU利用率;网络带宽紧张的情况下,适当扩大量化的步长,如果采用分层思路,减少分层等措施,降低音视频的质量。网络带宽充足的情况下,适当降低量化的步长,如果采用分层思路,增加分层等措施,增强音视频的质量。CPU/GPU资源空闲的情况,可以进行第2遍更加细节地编码提供音视频质量。上层应用层可以在整个内存区域设置适当检测点。这样可以在内存检测点动态实时统计更新编解码相应的信息等。

以上动静态编码只是针对一维度编码,可以适当扩展多维度编码,多维度在互相独立的情况下,每一个维度都用一个异常加以保护,这个比较复杂。往往选择其中一维为主要的,然后在页面访问权限异常处理中,适当的调整了所有维度内存权限的操作或者其他可控的操作。

进一步的,不连续内存的处理方式:

上面的内存访问往往总是连续的,但是这个情况下很少出现。我们一般会出现可控的偏移位置,而这个可控的偏移位置同样应用。现在假设它存在访问偏移 offset,通过这一方法,能大大提高指令的高速缓存的命中率。下面来计算守卫页数:

守卫页数等于偏移量offset除以pagesize的值,如果不是整除的情况,求出的值再加上1。

同样先预先申请好需要内存数量(向上取为页的整数倍),然后加上守卫页数(必须连续一次性申请,无读写权限),然后将后面守卫页数设置为无读写权限,接着流程同上述静态/动态方式。

二、解码的流程

解码是编码的逆行工程,也是最容易导致内存破坏,而内存破坏尤其是堆破坏最为棘手。程序员往往好耗费大量的时间精力去排查此类问题,而到最后往往得到总是码流不对的结论。这样往往显得太过被动而且使程序稳定性,项目预期都不能如意。这个有办法可以主动地拦截这类行为。

首先说明一下数据类型

解码后的数据(YUV,RGB,PCM等等类型):planar跟packed。

对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。

对于packed的YUV格式,每个像素点的Y,U,V是连续交叉存储的,RGB往往同样属于这一类型。

解码前的数据(H264, H265,VP8,VP9,ogg,opus,aac,mp3,jpg等等)。

方式一、静态方式:

1.根据源数据长度,向上取整为页大小的整数倍加1,然后调用系统接口一次性申请相应长度,将最后一页设置为无读写权限,记住第一段存储空间;

2.将数据源拷贝进第一段存储空间,将不及一页剩余数据都清为0;

3.初始化页面访问权限异常处理;

4.开始码流分析;

4.1.如果是视频,解析出宽、高以及解码后的数据类型(YUV/RGB);

4.1.1.如果数据类似于planar多维度,一般为3维,也少数有4维,类似 packed方式也可以直接当成1维,计算出所需要的内存大小,可以分别一次性申请每一维存储空间以及无读写权限的页,每一维最后一页的存储空间都是无读写权限,也可以一次性申请足够大的连续内存以及无读写权限的页,记住第三段存储空间;

4.1.2.若是1维情况:解码到在异常或者信号回调中范围,判断地址为最后一页中,如果是,就表示解码失败;如果不是而且在第三段存储空间中,设置相应的页面为可读写权限,也可以设置连续若干页面可读写,但是不能改变最后一页的权限,前面2种情况都不满足,返回失败;多维情况:多维处理往往总有其中1维为主要的,其余都是次要的;解码到在异常或者信号回调中范围,判断地址为第三段存储空间主要的1维最后一页中,如果是,就表示解码失败;如果不是而且地址位于第三段存储空间中,设置每一维度相应的页面为可读写权限。也可以连续设置每一维度相应的页面,但是不能改变每一维度最后一页的权限。前面2种情况都不满足,返回失败;

4.1.3.异常或者信号回调中,可以判断系统资源,比如CPU资源过高情况下,通过使用系统调用方式降低CPU利用率。统计一下解码速度,适时增加或减少解码线程,调整GPU的块内线程数。

周而复始4.1.2 和4.1.3 步骤,直到解码源越界,或者解码数据越界,或者解码成功。

方式二、动态方式:

动态方式往往是不一次性申请所需解码必须足够的内存,而是申请部分,然后复用这一部分。因为往往解码后的数据都很大,尤其是高清,超高清的视频,这个对节约系统内存资源,高速缓存命中率,较少的虚拟内存的交换有极大地好处。

1.根据源数据长度,向上取整为页大小的整数倍加1,然后调用系统接口一次性申请相应长度,将最后一页设置为无读写权限,记住第一段存储空间;

2.将数据源拷贝进第一段存储空间,将不及一页剩余数据都清为0;

3.初始化页面访问权限异常处理;

4.开始码流分析;

4.1.计算出长度,第三段内存区域一次性申请若干页长度,最后一页为无读写权限,计算出需要循环次数并且向上取整;

4.2.开始解码,解码到在异常或者信号回调中范围,判断是否结束循环;

4.2.1.如果结束循环,直接返回,表示解码成功;

4.2.2.如果还未结束循环,将解码的数据拷贝出去,然后复用第三段内存区域;

4.3.统计一下解码速度,监控系统的资源使用,调整系统资源使用。

周而复始4.2和4. 3 步骤,直到解码源越界,或者解码数据越界,或者解码成功。

推荐阅读
相关专栏
音视频杂谈
159 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。