好多开发者聊到GB28181的时候,不可避免的提到H.265编码国标平台是否支持?实际上,GB/T28181-2016里面,并未提及H.265编解码相关,具体参见以下说明:
视音频编/解码技术要求
联网系统中,对视音频编/解码的技术要求包括编/解码的档次和级别、工具选项、码流语法的规定以及比特流和解码器的一致性测试等。具体要求如下:
视频编码应支持 H.264、SVAC 或 MPEG-4 视频编码标准,视频解码应同时支持 H.264、SVAC 和MPEG-4 视频解码标准。
音频编码应支持 G.711或 G.723.1或 G.729或SVAC音频编码标准,音频解码应同时支持 G.711、G.723.1、G.729和SVAC音频解码标准,可扩展支持ITU-TRec.G.722.1-1999音频解码标准。
基于 H.264的视频编、解码技术要求
E.1.1 H.264的档次和级别
采用 H.264标准的视频编码应至少支持ITU-T Rec.H.264-2005视频标准的基本档次(Baseline Profile),级别(Level)应至少支持到 Level1.3,标清应用宜扩展支持到 Level3,高清应用宜扩展支持到Level4;视频解码所支持的档次和级别应不低于编码支持的最高档次和级别,至少应支持到 H.264视频标准基本档次的 Level3;视频解码宜扩展支持 H.264主档次(MainProfile)中的隔行扫描和 B帧工具,且相邻两 P帧间的 B帧个数不大于2。
采用 H.264标准的高清视频编码应至少支持ITU-TRec.H.264-2005视频标准的基本档次(BaselineProfile),宜扩展支持 H.264 主档次(MainProfile)和高级档次(HighProfile),级别(Level)宜扩展支持到 Level4;视频解码所支持的档次和级别应不低于编码支持的最高档次和级别,至少应支持到H.264 视频标准高级档次(HighProfile)的 Level4。H.264主档次和高级档次视频编码标准的具体描述详见ITU-TRec.H.264-2005中的相关规定。
E.1.1.1 H.264基本档次的选项和工具
H.264基本档次支持的选项和工具主要有:
- a) I片和 P片(Slice);
- b) 基于内容自适应的变长编码 CAVLC;
- c) 容错工具:FMO、ASO、RS;
- d) 去块效应滤波器(DeblockingFilter);
- e) 多参考帧编码。
采用 H.264编码标准的视频流应为 H.264Baseline视频流,编码应支持上述 Baseline选项和工具中的部分或全部,可不支持容错工具;H.264的解码至少应支持上述除容错工具外的全部选项和工具。
多参考帧编码时,P片的参考帧数一般不大于2帧。为了保证码流解析的效率,比特流中应当在每个I帧之前都出现相应的SPS 和 PPS。
E.1.1.2 H.264级别的限制
H.264级别(Level1~4)的限制如表 E.1所示,表中“—”表示未做相应的限制。
GB28181接入端到底要不要支持H.265?
问题来了,如果GB28181平台端上去H.265的数据怎么办?如果做的比较好的平台端,不需要web播放的话,也可以转成其他如RTMP(需要RTMP支持扩展H.265)等播放,由于web端不支持H.265的播放,好多第三方国标平台,是先把H.265转H.264后再播放,这样导致的问题是,平台端性能消耗比较大,而且处理不好的话,容易导致实时音视频延迟或不同步问题。
以Android平台GB28181设备接入为例,我们实现了几种模式的编码,如H.264软编、H.264硬编、H.265硬编,而且硬编码,还实现了基于native madiacodec的硬编,进一步提高了编码效率,相关设置如下:
videoEncodeTypeSelector = (Spinner)findViewById(R.id.videoEncodeTypeSelector); final String[] videoEncodeTypes = new String[]{"软编(H.264)", "硬编(H.264)", "硬编(H.265)"}; ArrayAdapter<String> adapterVideoEncodeType = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, videoEncodeTypes); adapterVideoEncodeType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); videoEncodeTypeSelector.setAdapter(adapterVideoEncodeType); videoEncodeTypeSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (isRTSPPublisherRunning || isPushingRtmp || isGB28181StreamRunning || isRecording) { Log.e(TAG, "Could not switch video encoder type during publishing.."); return; } videoEncodeType = position; Log.i(TAG, "[视频编码类型]Currently choosing: " + videoEncodeTypes[position] + ", videoEncodeType: " + videoEncodeType); } @Override public void onNothingSelected(AdapterView<?> parent) { } });
Init的时候,设置如下:
if(videoEncodeType == 1) { int h264HWKbps = setHardwareEncoderKbps(true, video_width_, video_height_); h264HWKbps = h264HWKbps*fps/25; Log.i(TAG, "h264HWKbps: " + h264HWKbps); int isSupportH264HWEncoder = libPublisher .SetSmartPublisherVideoHWEncoder(publisherHandle, h264HWKbps); if (isSupportH264HWEncoder == 0) { libPublisher.SetNativeMediaNDK(publisherHandle, 1); Log.i(TAG, "Great, it supports h.264 hardware encoder!"); } } else if (videoEncodeType == 2) { int hevcHWKbps = setHardwareEncoderKbps(false, video_width_, video_height_); hevcHWKbps = hevcHWKbps*fps/25; Log.i(TAG, "hevcHWKbps: " + hevcHWKbps); int isSupportHevcHWEncoder = libPublisher .SetSmartPublisherVideoHevcHWEncoder(publisherHandle, hevcHWKbps); if (isSupportHevcHWEncoder == 0) { libPublisher.SetNativeMediaNDK(publisherHandle, 1); Log.i(TAG, "Great, it supports hevc hardware encoder!"); } }
如果GB28181平台端对H.265支持不好,最简单的做法,还是直接让设备接入端设置成H.264软编或硬编码。