H.265 是一种高效的视频编码标准,旨在有限带宽下传输更高质量的网络视频。开发者可以在编码或混流时输出 H.265 格式的视频码流。
H.265 和 H.264 的区别如下:
差异项 | H.264 | H.265 |
---|---|---|
同等画质下的码率 |
- |
H.265 相比于 H.264 可以节约 30% 码率(实测值)。 |
软编性能 |
- |
H.265 消耗的算力是 H.264 的 3 倍左右。 |
软解性能 |
- |
H.265 消耗的算力是 H.264 的 1.5 倍左右。 |
硬件生态 |
所有机型基本都支持硬编和硬解。 |
大部分机型都支持硬编,绝大部分机型都支持硬解。 |
混流输出 |
支持。 |
支持,但价格比 H.264 混流输出更贵,详情可咨询 ZEGO 商务人员。 |
适用场景 |
所有场景。 |
推荐在直播和音视频互动场景使用。 |
在如下应用场景中,可使用 H.265 进行编码:
应用场景类型 | 说明 |
---|---|
秀场直播、电商直播、互动直播、游戏直播 |
通过 H.265 编码,将码率降低 30% 码率(实测值),分发给万千观众,极大降低 CDN 分发成本。 |
视频通话、视频会议、在线教育 |
通过 H.265 编码,在同等码率下,提高画面清晰度,让这些场景通话效果更好。 |
目前 Web 和小程序平台,不支持 H.265 拉流。
请参考 下载示例源码 获取源码。
相关源码请查看 “/ZegoExpressExample/Examples/AdvancedStreaming/H265” 目录下的文件。
在使用 H.265 编解码功能之前,请确保:
一些老的或低端的移动机型,不支持 H.265 视频编码。此时开发者需要在推流前先通过 isVideoEncoderSupported 接口判断本机是否支持 H.265 视频编码能力。如果支持,才能在推流前通过 setVideoConfig 接口设置 H.265 视频编码类型,否则不生效。
一些老的或低端的移动机型,不支持 H.265 视频解码。在支持拉不同视频码流的场景下,如 CDN 场景,开发者需要在拉流前先通过 isVideoDecoderSupported 接口判断本机是否支持 H.265 视频解码能力。如果支持,才能拉 H.265 视频码流,否则只能拉其他格式的视频码流,例如 H.264。
连麦混流直播包括如下两种实现方式,开发者可以根据实际情况按需接入:
混出不同格式的码流(推荐):混流服务直接输出一路 H.265 混流和一路 H.264 混流,该场景只需要在混流服务进行一次转码,无需 CDN 再转码。相比于混流推 CDN 转码,具有更高的清晰度,费用也更便宜,推荐使用。
混流推 CDN 转码:混流服务直接输出一路 H.265 混流,需要通过 CDN 的转码能力,转出一路 H.265 和一路 H.264 码流。
需要联系 ZEGO 技术支持开通 CDN 转码功能。
该场景下,混流服务通过 ZEGO 实时音视频云接收到主播和连麦嘉宾的推流后,直接输出一路 H.265 混流和一路 H.264 混流,并把两路流都推流到 CDN,观众可以根据自身终端设备是否支持 H.265 视频解码,选择从 CDN 拉 H.265 码流或 H.264 码流。
主播端
// 调用 startMixerTask 发起混流
// 请输入 taskID
String taskID = "";
ZegoMixerTask task = new ZegoMixerTask(taskID);
// 请自行设置 videoConfig
ZegoMixerVideoConfig videoConfig = new ZegoMixerVideoConfig(720, 1280, 15, 1500);
task.setVideoConfig(videoConfig);
task.setAudioConfig(new ZegoMixerAudioConfig());
// 注意,混流输入中流的编码格式支持 H.264 和 H.265, 请自行处理流布局及输入
ArrayList<ZegoMixerInput> inputList = new ArrayList<>();
task.setInputList(inputList);
// 混流两路输出
// 注意: 输出 target 可以是 streamID 或 CDN 地址,二者在观众端处理方式不同,该场景推荐直接传入 stremaID
// 注意: ZegoMixerOutput 中的码率优先级高于 ZegoMixerVideoConfig 中的码率
// 请输入 h264StreamID
String h264StreamID = "";
// 请输入 h265StreamID
String h265StreamID = "";
// 请输入 h264 码率,此码率为当前分辨率帧率(720p, 15fps)的推荐码率
int h264Bitrate = 2244;
// 请输入 h265 码率,此码率为当前分辨率帧率(720p, 15fps)的推荐码率
int h265Bitrate = 1795;
ArrayList<ZegoMixerOutput> outputList = new ArrayList<>();
ZegoMixerOutput outputH264 = new ZegoMixerOutput(h264StreamID);
ZegoMixerOutputVideoConfig outputH264VideoConfig = new ZegoMixerOutputVideoConfig
(ZegoVideoCodecID.getZegoVideoCodecID(0), h264Bitrate);
outputH264.setVideoConfig(outputH264VideoConfig);
outputList.add(outputH264);
ZegoMixerOutput outputH265 = new ZegoMixerOutput(h265StreamID);
ZegoMixerOutputVideoConfig outputH265VideoConfig = new ZegoMixerOutputVideoConfig
(ZegoVideoCodecID.getZegoVideoCodecID(3), h265Bitrate);
outputH265.setVideoConfig(outputH265VideoConfig);
outputList.add(outputH265);
task.setOutputList(outputList);
// 开始混流
engine.startMixerTask(task, new IZegoMixerStartCallback() {
@Override
public void onMixerStartResult(int errorCode, JSONObject var2) {
// 混流任务回调
}
});
// 开发者通知 App 的业务服务端流已新增
观众端
// 从 App 的业务服务端收到流新增通知
boolean h265DecoderSupport = engine.isVideoDecoderSupported(ZegoVideoCodecID.getZegoVideoCodecID(3));
// h264StreamID
String h264StreamID = "";
// h265StreamID
String h265StreamID = "";
// 拉流需要渲染的 view
View view = playView;
ZegoCanvas playCanvas = new ZegoCanvas(view);
if (h265DecoderSupport) {
// 支持 H.265 解码
engine.startPlayingStream(h265StreamID, playCanvas);
}
else {
// 不支持 H.265 解码
engine.startPlayingStream(h264StreamID, playCanvas);
}
该场景下,混流服务通过 ZEGO 实时音视频云接收到主播和连麦嘉宾的推流后,直接输出一路 H.265 混流,并把这路混流推流到 CDN,通过 CDN 的转码能力,观众可以根据自身终端设备是否支持 H.265 视频解码,选择从 CDN 拉 H.265 码流或 H.264 码流。
主播端
// 调用 startMixerTask 发起混流
// 请输入 taskID
String taskID = "";
ZegoMixerTask task = new ZegoMixerTask(taskID);
// 请自行设置 videoConfig
ZegoMixerVideoConfig videoConfig = new ZegoMixerVideoConfig(720, 1280, 15, 1500);
task.setVideoConfig(videoConfig);
task.setAudioConfig(new ZegoMixerAudioConfig());
// 注意,混流输入中流的编码格式支持 H.264 和 H.265, 请自行处理流布局及输入
ArrayList<ZegoMixerInput> inputList = new ArrayList<>();
task.setInputList(inputList);
// 请输入 CDN URL
String publishCdnUrl = "";
// 请输入 h265 码率,此码率为当前分辨率帧率(720p, 15fps)的推荐码率
int h265Bitrate = 1795;
// 注意,由于这里需要使用 CDN 转码, target 需要传入 CDN URL
ArrayList<ZegoMixerOutput> outputList = new ArrayList<>();
ZegoMixerOutput outputH265 = new ZegoMixerOutput(publishCdnUrl);
ZegoMixerOutputVideoConfig outputH265VideoConfig = new ZegoMixerOutputVideoConfig
(ZegoVideoCodecID.getZegoVideoCodecID(3), h265Bitrate);
outputH265.setVideoConfig(outputH265VideoConfig);
outputList.add(outputH265);
task.setOutputList(outputList);
// 开始混流
engine.startMixerTask(task, new IZegoMixerStartCallback() {
@Override
public void onMixerStartResult(int errorCode, JSONObject var2) {
// 混流任务回调
}
});
// 开发者通知 App 的业务服务端流已新增
观众端
// 从 App 的业务服务端收到流新增通知
boolean h265DecoderSupport = engine.isVideoDecoderSupported(ZegoVideoCodecID.getZegoVideoCodecID(3));
String playStreamID = "";
// 拉流需要渲染的 view
View view = playView;
ZegoCanvas playCanvas = new ZegoCanvas(view);
if (h265DecoderSupport) {
// 支持 H.265 解码
// 请填入 H.265 Url 地址
String h265Url = "";
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
ZegoCDNConfig cdnConfig = new ZegoCDNConfig();
// H.265 CDN 拉流地址
cdnConfig.url = h265Url;
ZegoPlayerConfig playerConfig = new ZegoPlayerConfig();
playerConfig.cdnConfig = cdnConfig;
engine.startPlayingStream(playStreamID, playCanvas, playerConfig);
}
else {
// 不支持 H.265 解码
// 请填入 H.264 Url 地址
String h264Url = "";
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
ZegoCDNConfig cdnConfig = new ZegoCDNConfig();
// H.264 CDN 拉流地址
cdnConfig.url = h264Url;
ZegoPlayerConfig playerConfig = new ZegoPlayerConfig();
playerConfig.cdnConfig = cdnConfig;
engine.startPlayingStream(playStreamID, playCanvas, playerConfig);
}
该场景具有以下两个特点:
主播端
创建引擎后,调用 enableHardwareEncoder 接口开启硬件编码(若推流后更改配置,需要等下一次推流才生效),开启后会使用 GPU 进行编码,降低 CPU 使用率。
调用 isVideoEncoderSupported 接口查询主播端设备是否支持指定视频编码类型。
推流前调用 setVideoConfig 接口,通过 codecID 设置视频编码格式(若推流后更改配置,需要等下一次推流才生效)。如果设置了 H.265 编码,则:
// 移动端使用 H.265 编码需要开启硬件编码
engine.enableHardwareEncoder(true);
// 查询是否支持 H.265 编码
boolean h265EncoderSupport = engine.isVideoEncoderSupported(ZegoVideoCodecID.getZegoVideoCodecID(3));
ZegoVideoConfig videoConfig = new ZegoVideoConfig();
if (h265EncoderSupport) {
// 支持 H.265 编码
videoConfig.codecID = ZegoVideoCodecID.getZegoVideoCodecID(3);
} else {
// 不支持 H.265 编码
videoConfig.codecID = ZegoVideoCodecID.getZegoVideoCodecID(0);
}
engine.setVideoConfig(videoConfig);
if (h265EncoderSupport) {
// 通过 enableH265EncodeFallback 选择是否开启 H.265 编码失败自动降级能力
engine.enableH265EncodeFallback(true);
}
// 请输入 streamID
String publishStreamID = "";
// 请输入 CdnUrl
String publishCdnUrl = "";
// 添加 CDN 转推地址
engine.addPublishCdnUrl(publishCdnUrl, publishStreamID, new IZegoPublisherUpdateCdnUrlCallback() {
@Override
public void onPublisherUpdateCdnUrlResult(int i) {
// 判断转推 CDN 地址是否添加成功
}
});
engine.startPublishingStream(publishStreamID);
// 开发者通知 App 的业务服务端该条推流的编码格式,以便通知拉流端根据不同的推流编码格式做相应的处理
观众端
//收到流新增通知 onRoomStreamUpdate
@Override
public void onRoomStreamUpdate(String roomID, ZegoUpdateType updateType, ArrayList<ZegoStream> streamList, JSONObject extendedData) {
super.onRoomStreamUpdate(roomID, updateType, streamList, extendedData);
// 从 App 的业务服务端获取该条流的编码格式
int videoCodecID = 0;
// 请输入 streamID
String playStreamID = "";
// 拉流需要渲染的 view
View view = playView;
ZegoCanvas playCanvas = new ZegoCanvas(view);
if (videoCodecID == ZegoVideoCodecID.getZegoVideoCodecID(3).value()) {
// 编码格式为 H.265
boolean h265DecoderSupport = engine.isVideoDecoderSupported(ZegoVideoCodecID.getZegoVideoCodecID(3));
// 不支持解码则不拉流
if (h265DecoderSupport) {
// 支持 H.265 解码
// 请填入 H.265 Url 地址
String h265Url = "";
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
ZegoCDNConfig cdnConfig = new ZegoCDNConfig();
// H.265 CDN 拉流地址
cdnConfig.url = h265Url;
ZegoPlayerConfig playerConfig = new ZegoPlayerConfig();
playerConfig.cdnConfig = cdnConfig;
engine.startPlayingStream(playStreamID, playCanvas, playerConfig);
} else {
// 不支持 H.265 解码
// 请填入 H.264 Url 地址
String h264Url = "";
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
ZegoCDNConfig cdnConfig = new ZegoCDNConfig();
// H.264 CDN 拉流地址
cdnConfig.url = h264Url;
ZegoPlayerConfig playerConfig = new ZegoPlayerConfig();
playerConfig.cdnConfig = cdnConfig;
engine.startPlayingStream(playStreamID, playCanvas, playerConfig);
}
}
else if (videoCodecID == ZegoVideoCodecID.getZegoVideoCodecID(0).value()) {
// 编码格式为 H.264
engine.startPlayingStream(playStreamID, playCanvas);
}
}
连麦嘉宾端
//收到流新增通知 onRoomStreamUpdate
@Override
public void onRoomStreamUpdate(String roomID, ZegoUpdateType updateType, ArrayList<ZegoStream> streamList, JSONObject extendedData) {
super.onRoomStreamUpdate(roomID, updateType, streamList, extendedData);
// 从 App 的业务服务端获取该条流的编码格式
int videoCodecID = 0;
// 请输入 streamID
String playStreamID = "";
// 拉流需要渲染的 view
View view = playView;
ZegoCanvas playCanvas = new ZegoCanvas(view);
ZegoPlayerConfig config = new ZegoPlayerConfig();
config.resourceMode = ZegoStreamResourceMode.ONLY_RTC; // 仅从 RTC 拉流
if (videoCodecID == ZegoVideoCodecID.getZegoVideoCodecID(3).value()) {
// 编码格式为 H.265
boolean h265DecoderSupport = engine.isVideoDecoderSupported(ZegoVideoCodecID.getZegoVideoCodecID(3));
// 不支持解码则不拉流
if (h265DecoderSupport) {
// 支持 H.265 解码
engine.startPlayingStream(playStreamID, playCanvas, config);
}
}
else if (videoCodecID == ZegoVideoCodecID.getZegoVideoCodecID(0).value()) {
// 编码格式为 H.264
engine.startPlayingStream(playStreamID, playCanvas, config);
}
}
如果进行本地服务端录制、云端录制、数据流录制时使用了 H.265 编解码功能,则会影响录制文件的生成,详情如下:
在对推流(H.265 视频编码码流)进行录制时,有可能因为 H.265 编码失败而触发编码降级,此时会收到 onPublisherVideoEncoderChanged 回调,表示视频编码格式发生变更。
该场景下,为了避免对录制文件造成损坏,SDK 内部会结束并保存当前的录制任务,并自动重启一个新的录制任务。新录制任务的录制文件路径会重新生成,避免覆盖原始录制文件。新文件的生成规则为给原始的录制文件名基础上加一个时间戳:
例如,开发者传入的原始录制文件路径为:/user/data/mediarecord.mp4,则新的录制文件为: /user/data/mediarecord_1626880634948.mp4,其中 1626880634948 为当前时间戳。
如果开发者在录制期间收到过 onPublisherVideoEncoderChanged 回调,需要在录制结束后,收集录制文件存储路径下的其他文件。
可以调用 enableH265EncodeFallback 接口开启降级功能(默认开启),若 H.265 硬编失败则降级成 H.264,若同时在使用录制功能,则录制文件变为两份。
会的,若解码时发现硬解失败,会自动降级成软解。
客户端开启 H.265 功能不需要收费,但是混流输出 H.265 需要收取混流费用,比混流输出 H.264 价格更贵,详情可咨询 ZEGO 商务人员。
没有变化,按输出 H.264 计费。
目前市场上所有机型都支持 H.265 解码,经测试大概在 2013 年之前的低端机型解码可能会有帧率波动。
联系我们
文档反馈