android视频编码优化方法
  p8v5tjHYGXDP 2023年11月02日 62 0


概述

视频编码会影响串流质量的多个指标,包括画质、流畅度和延迟。除了编码参数,采集和图像处理也对最终的编码效果产生影响。它们通常会综合影响多个质量指标。本文将从视频采集、图像处理和编码参数几个方面来分享相关信息。

1、编码参数

编码参数直接影响到视频效果,主要参数有:编码格式、分辨率、码率、码率模式、帧率、I帧间隔、帧内刷新、量化参数、去模糊、Profile和Level、补帧。

1.1 编码格式

H.265通常会提供更好的画质。这是因为H.265使用更高效的压缩算法,能够以更少的比特率实现相同或更好的画质,或者以相同比特率提供更高的画质。

但要注意的是,这并不是绝对的规则。编码画质不仅取决于编码器本身,还取决于视频内容的特性。一些视频可能在H.264下表现良好,而另一些则在H.265下表现良好,这取决于编码算法对特定内容的适应能力。


format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);//设置为H264
 
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_HEVC);//设置为H265


1.2 分辨率

一般情况下,编码分辨率与采集的画面分辨率相同,可保持最好的清晰度;但是,当比特率非常低的情况下,降低分辨率通常会产生更好的画质。这是因为比特率限制了可用的数据量,低比特率下保留高分辨率会导致视频压缩伪影、模糊和失真,从而影响画质。通过降低分辨率,你可以在有限的比特率下更好地保持图像质量。


format.setInteger(MediaFormat.KEY_WIDTH, width);
format.setInteger(MediaFormat.KEY_HEIGHT, height);


1.3 码率

在其它参数相同的情况下,码率越高,清晰度越高,高比特率可以保留更多图像细节,特别是在变化剧烈或复杂场景中,画面更为清晰,但在码率相当高的情况下,再一味的提高码率对清晰度的提升不会太明显。且码率越高延迟会相应的增加,因此需要设置一个比较合适的码率在延迟和画质中保持一个相对好的效果。


format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);


1.4 码率模式

一般来说,现在大部分Android设备支持CQ、VBR、CBR,实际上设备的硬件和软件能力以及制造商的设计决策会造成不同的方案对码率模式的支持稍有不同,Android SDK中定义的码率模式有以下几种:


/** Constant quality mode */
public static final int BITRATE_MODE_CQ = 0;
/** Variable bitrate mode */
public static final int BITRATE_MODE_VBR = 1;
/** Constant bitrate mode */
public static final int BITRATE_MODE_CBR = 2;
/** Constant bitrate mode with frame drops */
public static final int BITRATE_MODE_CBR_FD =  3;


经验证我们的设备默认是不支持CQ和CBR_FD(另外一种叫法是CBR_VFR),后来对修改了系统支持了VFR,但也不是标准值3,而是4,这几种模式的特点如下:

CBR(Constant Bit Rate):恒定比特率

  • 视频编码器会以固定的比特率来编码视频。在不设置QP的情况下,每秒传输的比特数是恒定的;即使设置了QP,码率也相对于VBR更平稳,在传输时码率波动小,会比较平滑。

VBR(Variable Bit Rate):可变比特率

  • 允许编码器根据视频内容的复杂性来动态调整比特率。在视频中,某些部分可能需要更多的比特率以保持高质量,而其他部分可能可以使用较少的比特率。VBR模式下码率波动较大,在背景持续变化或较复杂的场景下码率较高,特别是不同的场景切换或静止到运动时会造成码率瞬时冲高,容易导致卡顿。VBR在相对静止或变化不剧烈的场景码率会明显变小,这种情况下延迟可能会小于CBR,一般情况下VBR的整体码率会小于CBR。

CQ(Constant Quality):恒定质量

  • 不设置特定的比特率,而是尝试在恒定的质量水平下编码视频。编码器会动态分配比特率以保持一致的视觉质量。

CBR_FD(Constant Bit Rate with Frame Dropping):恒定比特率帧丢失

  • CBR_FD 是一种变体的 CBR 模式,又称CBR_VFR模式,在码率有限的情况下使用。与标准的 CBR 不同,CBR_FD 允许在码率不足时丢弃视频帧,以便在固定的比特率情况下维持一定的清晰度。丢帧会造成主观感觉不流畅。
format.setInteger(MediaFormat.KEY_BITRATE_MODE, rateControlMode);


1.5 帧率

较高的帧率通常会提供更平滑的体验,减少图像卡顿感。通常,60帧每秒(FPS)被认为是很好的目标帧率。不同类型的游戏和应用对帧率的需求有所不同。例如,射击游戏通常需要更高的帧率以获得更快的响应时间,而策略游戏可能可以使用较低的帧率。高帧率通常需要更多的带宽来传输。


format.setInteger(MediaFormat.KEY_FRAME_RATE, maxFps);
format.setFloat(MediaFormat.KEY_MAX_FPS_TO_ENCODER, maxFps);//限制Surface输入的最大帧率


1.6 I帧间隔

I帧通常比P帧和B帧更大,较短的I帧间隔有助于提高视频质量,但会增加比特率和带宽要求,码率会低时频繁的I帧会造成与I帧同步的闪烁。


format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);//单位:秒


1.7 帧内刷新

帧内刷新可以在视频编码中的指定周期内,刷新整个帧,而不是像传统关键帧那样插入完整的I帧,相当于把I帧均分到多个帧。帧内刷新在码率足够的情况下可以提升画质,但在码率非常不足的情况下会造成背景噪点闪烁。


format.setInteger(MediaFormat.KEY_INTRA_REFRESH_PERIOD, maxFps*30);


1.8 量化参数

量化参数(Quantization Parameter),用于控制视频编码的压缩质量和比特率,最终达到调节画面质量的作用。QP值和比特率成反比,QP值越小画面质量越高;反之QP值越大,画面质量越低。而且随着视频源复杂度,这种反比的关系会更加明显。QP调节是改变画面质量最常用的手段之一。QP 的取值范围通常在 0 到 51 之间,值为 0 表示最高质量(无损),而值为 51 表示最低质量。设置QP参数时可以根据I帧,P帧,B帧分别设置,关闭B帧的情况下不需要设置B帧。

最小量化步长(minQP),限制最好的图像质量(重点在静止画面),minQP越小,静止时候码率越大,质量越好。

最大量化步长(maxQP),限制最差的画面(重点在运动的时候),maxQP越小,运动时候码率就越大,质量相对越好。

Android标准接口设置QP(不一定支持):


format.setInteger(MediaFormat.KEY_VIDEO_QP_I_MIN, minQP);
format.setInteger(MediaFormat.KEY_VIDEO_QP_I_MAX, maxQP);

format.setInteger(MediaFormat.KEY_VIDEO_QP_P_MIN, minQP);
format.setInteger(MediaFormat.KEY_VIDEO_QP_P_MAX, maxQP);


高通865芯片设置QP参数方法:


format.setInteger("vendor.qti-ext-enc-initial-qp.qp-i", defaultQP);
format.setInteger("vendor.qti-ext-enc-initial-qp.qp-i-enable", 1);
format.setInteger("vendor.qti-ext-enc-qp-range.qp-i-min", minQP);
format.setInteger("vendor.qti-ext-enc-qp-range.qp-i-max", maxQP);

format.setInteger("vendor.qti-ext-enc-initial-qp.qp-p", defaultQP);
format.setInteger("vendor.qti-ext-enc-initial-qp.qp-p-enable", 1);
format.setInteger("vendor.qti-ext-enc-qp-range.qp-p-min", minQP);
format.setInteger("vendor.qti-ext-enc-qp-range.qp-p-max", maxQP);


1.9 去模糊

Android MediaCodec中没有这个参数,这个是高通865的视频编码器中的参数,禁用模糊效果时能减少场景切换时的模糊,可通过MediaCodec透传到OpenMax:


format.setInteger("vendor.qti-ext-enc-blurinfo.info", 2);//means disable both internal and external blur


1.10 Profile和Level

Profile必须与Level一同设置,它们是用来描述和标识视频编码标准的重要参数,H.264/AVC标准有多个配置文件,如Baseline、Main、High等,它们支持不同的功能集,High配置文件通常支持更高级的功能;Level越高,支持的分辨率,帧率,码率越高。


if(mimeType.equals(MediaFormat.MIMETYPE_VIDEO_AVC)) {
    format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
}else if(mimeType.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)){
    format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.HEVCProfileMain);
}
format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31);


1.11 补帧

当画面停止刷新超过指定的时间后,重复最后一帧,此方法不影响界面显示,但可以提高编码帧率指标,对实际体验没有意义,会造成码率略有增加。


format.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, LIMIT_FRAME_DELAY_MS);


2 图像处理

编码前图像处理,可以通过优化被采集的视频源来提高画质,主要方法有锐化、色彩增强(亮度、对比度、饱和度调节);这些方法是通过GPU对每一帧视频图像进行处理,因此会消耗较多的GPU资源,使用时需要验证是否影响游戏帧率,建议可以在客户端解码后处理。

2.1 锐化

锐化算法有多种,目前验证效果比较好的算法如下(通过sharpLevel控制锐化强度,锐化过度会有锯齿感):


"varying vec2 vTextureCoord;\n"+
"uniform samplerExternalOES uTexture;\n"+
"uniform vec2 mTextureSize;\n"+
"uniform float sharpLevel;\n"+
"void main() {\n"+
"    vec2 offset0 = vec2(1.0, 1.0) / mTextureSize;\n"+
"    vec2 offset1 = vec2(0.0, 1.0) / mTextureSize;\n"+
"    vec2 offset2 = vec2(-1.0, 1.0) / mTextureSize;\n"+
"    vec2 offset3 = vec2(1.0, 0.0) / mTextureSize;\n"+
"    vec4 cTemp0 = texture2D(uTexture, vTextureCoord + offset0);\n"+
"    vec4 cTemp1 = texture2D(uTexture, vTextureCoord + offset1);\n"+
"    vec4 cTemp2 = texture2D(uTexture, vTextureCoord + offset2);\n"+
"    vec4 cTemp3 = texture2D(uTexture, vTextureCoord + offset3);\n"+
"    vec4 cTemp4 = texture2D(uTexture, vTextureCoord);\n"+
"    vec4 cTemp5 = texture2D(uTexture, vTextureCoord - offset3);\n"+
"    vec4 cTemp6 = texture2D(uTexture, vTextureCoord - offset2);\n"+
"    vec4 cTemp7 = texture2D(uTexture, vTextureCoord - offset1);\n"+
"    vec4 cTemp8 = texture2D(uTexture, vTextureCoord - offset0);\n"+
"    vec4 sum = cTemp4 + (cTemp4-(cTemp0+cTemp2+cTemp6+cTemp8+(cTemp1+cTemp3+cTemp5+cTemp7)*2.0+cTemp4*4.0)/16.0)*sharpLevel;\n"+
"    gl_FragColor = vec4(sum.rgb, 1.0);\n"+
"}\n";


2.2 色彩增强

通过对亮度、对比度、饱和度的调节可达到色彩增强的效果,不同风格的场景效果差异较大,需要分别适配,否则可能使用画面效果更差,算法如下(通过uBrightness、uContrast、uSaturation三个参数分别控制亮度、对比度、饱和度):


"varying vec2 vTextureCoord;\n"+
"uniform samplerExternalOES uTexture;\n"+
"uniform float uBrightness;\n" +
"uniform float uContrast;\n" +
"uniform float uSaturation;\n" +
"void main() {\n"+
"   vec4 texColor = texture2D(uTexture, vTextureCoord);\n" +
"   texColor.rgb += uBrightness;\n" +
"   texColor.rgb = (texColor.rgb - 0.5) * max(uContrast, 0.0) + 0.5;\n" +
"   float luminance = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));\n" +
"   texColor.rgb = mix(vec3(luminance), texColor.rgb, uSaturation);\n" +
"   gl_FragColor = texColor;\n" +
"}\n";


3 采集

通过调整屏幕分辨率、刷新帧率、屏密度等可以影响视频编码的最终效果。

3.1 分辨率

采集分辨率与编码分辨率相同时,清晰度最高,但编码过程中调节屏幕分辨率调小会闪一次明显的黑框,而实际应用中调节档位时需要调节编码分辨率,为了避免黑框,可以将采集分辨率设置为最大的编码分辨率,调节方法示例:


wm size 1920x1080


3.2 刷新率

通过修改系统调节vsync可以修改屏幕刷新率,这样刷新的时间间隔就会变小,MOUSE MOVE事件响应就会更及时,从而减小操作延迟。

3.3 屏密度

个别游戏调整屏密度会影响游戏的清晰度,调节方法示例:


wm density 380


3.4 超采

超采是先设置设备分辨率是一个比较大的分辨率,启动游戏后,游戏加载了大分辨的资源,加载完成之后再调回原分辨率,由于游戏没有重新加载资源,因此使用的是一个高分辨率的资源,会更清晰,这些方法对个别游戏有效,比如《原神》的文字和纹理都会变清晰,《逆水寒》的纹理会变清晰,实际就是3.1的叠加使用,调回原分辨率时会闪一下黑框。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
p8v5tjHYGXDP
作者其他文章 更多