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 混流输出更贵,详情可咨询销售。 |
适用场景 |
所有场景。 |
推荐在直播和音视频互动场景使用。 |
目前 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。
连麦混流直播包括如下两种实现方式,开发者可以根据实际情况按需接入:
需要联系 ZEGO 技术支持开通 CDN 转码功能。
该场景下,混流服务通过 ZEGO 实时音视频云接收到主播和连麦嘉宾的推流后,直接输出一路 H.265 混流和一路 H.264 混流,并把两路流都推流到 CDN,观众可以根据自身终端设备是否支持 H.265 视频解码,选择从 CDN 拉 H.265 码流或 H.264 码流。
主播端
// 调用 startMixerTask 发起混流
String taskID = ""; // 请输入 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 中的码率
String h264StreamID = ""; // 请输入 h264StreamID
String h265StreamID = ""; // 请输入 h265StreamID
int h264Bitrate = 2244; // 请输入 h264 码率,此码率为当前分辨率帧率(720p, 15fps)的推荐码率。
int h265Bitrate = 1795; // 请输入 h265 码率,此码率为当前分辨率帧率(720p, 15fps)的推荐码率。
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));
String h264StreamID = ""; // h264StreamID
String h265StreamID = ""; // h265StreamID
View view = playView; // 拉流需要渲染的 view
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 发起混流
String taskID = ""; // 请输入 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);
String publishCdnUrl = ""; // 请输入 CDN URL
int h265Bitrate = 1795; // 请输入 h265 码率,此码率为当前分辨率帧率(720p, 15fps)的推荐码率。
// 注意,由于这里需要使用 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 = playView; // 拉流需要渲染的 view
ZegoCanvas playCanvas = new ZegoCanvas(view);
if (h265DecoderSupport) {
// 支持 H.265 解码
String h265Url = ""; // 请填入 H.265 Url 地址
// 注意: 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 解码
String h264Url = ""; // 请填入 H.264 Url 地址
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
ZegoCDNConfig cdnConfig = new ZegoCDNConfig();
// H.264 CDN 拉流地址
cdnConfig.url = h264Url;
ZegoPlayerConfig playerConfig = new ZegoPlayerConfig();
playerConfig.cdnConfig = cdnConfig;
engine.startPlayingStream(playStreamID, playCanvas, playerConfig);
}
该场景具有以下两个特点:
主播端
// 移动端使用 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);
}
String publishStreamID = ""; // 请输入 streamID
String publishCdnUrl = ""; // 请输入 CdnUrl
// 添加 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);
int videoCodecID = 0; // 从 App 的业务服务端获取该条流的编码格式。
String playStreamID = ""; // 请输入 streamID
View view = playView; // 拉流需要渲染的 view
ZegoCanvas playCanvas = new ZegoCanvas(view);
if (videoCodecID == ZegoVideoCodecID.getZegoVideoCodecID(3).value()) {
// 编码格式为 H.265
boolean h265DecoderSupport = engine.isVideoDecoderSupported(ZegoVideoCodecID.getZegoVideoCodecID(3));
// 不支持解码则不拉流
if (h265DecoderSupport) {
// 支持 H.265 解码
String h265Url = ""; // 请填入 H.265 Url 地址
// 注意: 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 解码
String h264Url = ""; // 请填入 H.264 Url 地址
// 注意: 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);
int videoCodecID = 0; // 从 App 的业务服务端获取该条流的编码格式。
String playStreamID = ""; // 请输入 streamID
View view = playView; // 拉流需要渲染的 view
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 回调,需要在录制结束后,收集录制文件存储路径下的其他文件。
方法 | 描述 |
---|---|
createEngine | 创建引擎 |
enableHardwareEncoder | 开/关硬件编码 |
startPublishingStream | 开始推流 |
startPlayingStream | 开始拉流 |
setVideoConfig | 设置视频配置 |
onRoomStreamUpdate | 相同房间内其他用户推的流增加或减少的通知 |
isVideoEncoderSupported | 是否支持指定视频编码类型 |
isVideoDecoderSupported | 是否支持指定视频解码格式 |
enableH265EncodeFallback | 是否开启 H.265 编码自动降级到 H.264 编码 |
onPublisherVideoEncoderChanged | 视频编码器变更回调 |
startMixerTask | 开始混流任务 |
可以调用 enableH265EncodeFallback 接口开启降级功能(默认开启),若 H.265 硬编失败则降级成 H.264,若同时在使用录制功能,则录制文件变为两份。
会的,若解码时发现硬解失败,会自动降级成软解。
客户端开启 H.265 功能不需要收费,但是混流输出 H.265 需要收取混流费用,比混流输出 H.264 价格更贵,详情可咨询销售。
没有变化,按输出 H.264 计费。
目前市场上所有机型都支持 H.265 解码,经测试大概在 2013 年之前的低端机型解码可能会有帧率波动。
联系我们
文档反馈