我们在做Android平台RTMP推送、轻量级RTSP服务和GB28181设备接入模块的时候,有一个点是逃不掉的:如何高效率的实现视频数据编码?
为此,我们设计了软编码、基于MediaCodec的硬编码和MediaCodec native层硬编,尽可能的减少数据拷贝和交互,确保高效率的完成视频编码,目前,编码前数据类型,已经涵盖了YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型,无论是自己采集的数据,还是Android摄像头和camera2和屏幕采集的数据,均可实现高效率的编码。
尽管软编码有各种好处,但受限于设备配置,超过720p的,在一些特定设备,软编总是差强人意,所以Android平台,在设备允许的前提下,还是需要精细化的硬编解决方案。
为此,我们设计了以下接口:
/** * Set Video H.264 HW Encoder, if support HW encoder, it will return 0(设置H.264硬编码) * * @param kbps: the kbps of different resolution. * * @return {0} if successful */ public native int SetSmartPublisherVideoHWEncoder(long handle, int kbps); /** * Set Video H.265(hevc) hardware encoder, if support H.265(hevc) hardware encoder, it will return 0(设置H.265硬编码) * * @param kbps: the kbps of different resolution. * * @return {0} if successful */ public native int SetSmartPublisherVideoHevcHWEncoder(long handle, int kbps);
两个接口,分别对应设置264、265硬编,如果不支持,自动切换到软编。
码率模式选择:
/* * 设置视频硬编码码率控制模式 * @param hw_bitrate_mode: -1表示使用默认值, 不设置也会使用默认值, 0:CQ, 1:VBR, 2:CBR, 3:CBR_FD, 请参考:android.media.MediaCodecInfo.EncoderCapabilities * 注意硬编码和手机硬件有关,多数手机只支持部分码率模式, 另外硬编码设备差异很大,不同设备同一码率控制模式效果可能不一样 * @return {0} if successful */ public native int SetVideoHWEncoderBitrateMode(long handle, int hw_bitrate_mode);
视频硬编码复杂度设置:
/* * 设置视频硬编码复杂度, 安卓5.0及以上支持 * @param hw_complexity: -1表示不设置, 请参考:android.media.MediaCodecInfo.EncoderCapabilities.getComplexityRange() 和 android.media.MediaFormat.KEY_COMPLEXITY * 注意硬编码和手机硬件有关,部分手机可能不支持此设置 * @return {0} if successful */ public native int SetVideoHWEncoderComplexity(long handle, int hw_complexity);
视频硬编码质量设置:
/* * 设置视频硬编码质量, 安卓9及以上支持, 仅当硬编码器码率控制模式(BitrateMode)是CQ(constant-quality mode)时才有效 * @param hw_quality: -1表示不设置, 请参考:android.media.MediaCodecInfo.EncoderCapabilities.getQualityRange() 和 android.media.MediaFormat.KEY_QUALITY * 注意硬编码和手机硬件有关,部分手机可能不支持此设置 * @return {0} if successful */ public native int SetVideoHWEncoderQuality(long handle, int hw_quality);
H.264硬编码Profile设置:
/* * 设置H.264硬编码Profile, 安卓7及以上支持 * @param hw_avc_profile: 0表示使用默认值, 0x01: Baseline, 0x02: Main, 0x08: High, 0x10000: ConstrainedBaseline, 0x80000: ConstrainedHigh; * 注意: ConstrainedBaseline 和 ConstrainedHigh 可能多数设备不支持, * H.264推荐使用 High 或者 ConstrainedHigh, 如果您使用的手机硬解码解不了,那还是设置Baseline * 如果设置的Profile硬编码器不支持,应编码器会使用默认值 * 具体参考:android.media.MediaCodecInfo.CodecProfileLevel * @return {0} if successful */ public native int SetAVCHWEncoderProfile(long handle, int hw_avc_profile);
H.264硬编码Level设置:
/* * 设置H.264硬编码Level, 这个只有在设置了Profile的情况下才有效, 安卓7及以上支持 * @param hw_avc_level: 0表示使用默认值, 0x100: Level3, 0x200: Level3.1, 0x400: Level3.2, * 0x800: Level4, 0x1000: Level4.1, 0x2000: Level4.2, * 0x4000: Level5, 0x8000: Level5.1, 0x10000: Level5.2, * 0x20000: Level6, 0x40000: Level6.1, 0x80000: Level6.2, * 如果设置的level太高硬编码器不支持,SDK内部会做相应调整 * 注意: 640*480@25fps最小支持的是Level3, 720p最小支持的是Level3.1, 1080p最小支持的是Level4 * 具体参考:android.media.MediaCodecInfo.CodecProfileLevel * @return {0} if successful */ public native int SetAVCHWEncoderLevel(long handle, int hw_avc_level);
视频硬编码最大码率设置:
/* * 设置视频硬编码最大码率, 安卓没有相关文档说明, 所以不建议设置, * @param hw_max_bitrate: 每秒最大码率, 单位bps * @return {0} if successful */ public native int SetVideoHWEncoderMaxBitrate(long handle, long hw_max_bitrate);
上层调用代码如下:
if(videoEncodeType == 1) { int h264HWKbps = setHardwareEncoderKbps(true, videoWidth, videoHeight); h264HWKbps = h264HWKbps*fps/25; Log.i(TAG, "h264HWKbps: " + h264HWKbps); int isSupportH264HWEncoder = libPublisher .SetSmartPublisherVideoHWEncoder(publisherHandle, h264HWKbps); if (isSupportH264HWEncoder == 0) { libPublisher.SetNativeMediaNDK(publisherHandle, 0); libPublisher.SetVideoHWEncoderBitrateMode(publisherHandle, 1); // 0:CQ, 1:VBR, 2:CBR libPublisher.SetVideoHWEncoderQuality(publisherHandle, 39); libPublisher.SetAVCHWEncoderProfile(publisherHandle, 0x08); // 0x01: Baseline, 0x02: Main, 0x08: High // libPublisher.SetAVCHWEncoderLevel(publisherHandle, 0x200); // Level 3.1 // libPublisher.SetAVCHWEncoderLevel(publisherHandle, 0x400); // Level 3.2 // libPublisher.SetAVCHWEncoderLevel(publisherHandle, 0x800); // Level 4 libPublisher.SetAVCHWEncoderLevel(publisherHandle, 0x1000); // Level 4.1 多数情况下,这个够用了 //libPublisher.SetAVCHWEncoderLevel(publisherHandle, 0x2000); // Level 4.2 // libPublisher.SetVideoHWEncoderMaxBitrate(publisherHandle, ((long)h264HWKbps)*1300); Log.i(TAG, "Great, it supports h.264 hardware encoder!"); } } else if (videoEncodeType == 2) { int hevcHWKbps = setHardwareEncoderKbps(false, videoWidth, videoHeight); hevcHWKbps = hevcHWKbps*fps/25; Log.i(TAG, "hevcHWKbps: " + hevcHWKbps); int isSupportHevcHWEncoder = libPublisher .SetSmartPublisherVideoHevcHWEncoder(publisherHandle, hevcHWKbps); if (isSupportHevcHWEncoder == 0) { libPublisher.SetNativeMediaNDK(publisherHandle, 0); libPublisher.SetVideoHWEncoderBitrateMode(publisherHandle, 0); // 0:CQ, 1:VBR, 2:CBR libPublisher.SetVideoHWEncoderQuality(publisherHandle, 39); // libPublisher.SetVideoHWEncoderMaxBitrate(publisherHandle, ((long)hevcHWKbps)*1200); Log.i(TAG, "Great, it supports hevc hardware encoder!"); } }
从实际测试来看,基于MediaCodec的硬编码相对软编来说,虽然不一定有软编码率各个方面控制的那么好,但是正确的调用MediaCodec硬编,可以显著提高视频编码能力,让1080P甚至更高分辨率帧率码率的视频编码,效率更高。