本文档适用于以下平台: Android、iOS、Windows。
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 拉流。
请参考 下载示例源码 获取源码。
相关源码请查看 “lib\topics\StreamAdvanced\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 发起混流
String taskID = ""; // 请输入 taskID
var task = ZegoMixerTask(taskID);
// 请自行设置 videoConfig
var videoConfig = ZegoMixerVideoConfig(720, 1280, 15, 1500);
task.videoConfig = videoConfig;
task.audioConfig = ZegoMixerAudioConfig.defaultConfig();
// 注意,混流输入中流的编码格式支持 H.264 和 H.265, 请自行处理流布局及输入
List<ZegoMixerInput> inputList = [];
task.inputList = 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)的推荐码率
List<ZegoMixerOutput> outputList = [];
var outputH264 = ZegoMixerOutput(h264StreamID);
var outputH264VideoConfig = ZegoMixerOutputVideoConfig(ZegoVideoCodecID.Default, h264Bitrate);
outputH264.videoConfig = outputH264VideoConfig;
outputList.add(outputH264);
var outputH265 = ZegoMixerOutput(h265StreamID);
var outputH265VideoConfig = ZegoMixerOutputVideoConfig(ZegoVideoCodecID.H265, h265Bitrate);
outputH265.videoConfig = outputH265VideoConfig;
outputList.add(outputH265);
task.outputList = outputList;
// 开始混流
ZegoExpressEngine.instance.startMixerTask(task).then((ZegoMixerStartResult result) {
// 混流任务回调
});
// 开发者通知 App 的业务服务端流已新增
// 从 App 的业务服务端收到流新增通知
bool h265DecoderSupport = await ZegoExpressEngine.instance.isVideoDecoderSupported(ZegoVideoCodecID.H265);
String h264StreamID = ""; // h264StreamID
String h265StreamID = ""; // h265StreamID
int viewID = playViewID; // 拉流需要渲染的 viewID, 通过sdk的 createCanvasView 获取
var playCanvas = ZegoCanvas(viewID);
if (h265DecoderSupport) {
// 支持 H.265 解码
ZegoExpressEngine.instance.startPlayingStream(h265StreamID, canvas: playCanvas);
}
else {
// 不支持 H.265 解码
ZegoExpressEngine.instance.startPlayingStream(h264StreamID, canvas: playCanvas);
}
该场景下,混流服务通过 ZEGO 实时音视频云接收到主播和连麦嘉宾的推流后,直接输出一路 H.265 混流,并把这路混流推流到 CDN,通过 CDN 的转码能力,观众可以根据自身终端设备是否支持 H.265 视频解码,选择从 CDN 拉 H.265 码流或 H.264 码流。
// 调用 startMixerTask 发起混流
String taskID = ""; // 请输入 taskID
var task = ZegoMixerTask(taskID);
// 请自行设置 videoConfig
var videoConfig = ZegoMixerVideoConfig(720, 1280, 15, 1500);
task.videoConfig = videoConfig;
task.audioConfig = ZegoMixerAudioConfig.defaultConfig();
// 注意,混流输入中流的编码格式支持 H.264 和 H.265, 请自行处理流布局及输入
List<ZegoMixerInput> inputList = [];
task.inputList = inputList;
String publishCdnUrl = ""; // 请输入 CDN URL
int h265Bitrate = 1795; // 请输入 h265 码率,此码率为当前分辨率帧率(720p, 15fps)的推荐码率
// 注意,由于这里需要使用 CDN 转码, target 需要传入 CDN URL
List<ZegoMixerOutput> outputList = [];
var outputH265 = ZegoMixerOutput(publishCdnUrl);
var outputH265VideoConfig = ZegoMixerOutputVideoConfig(ZegoVideoCodecID.H265, h265Bitrate);
outputH265.videoConfig = outputH265VideoConfig;
outputList.add(outputH265);
task.outputList = outputList;
// 开始混流
ZegoExpressEngine.instance.startMixerTask(task).then((ZegoMixerStartResult result) {
// 混流任务回调
});
// 开发者通知 App 的业务服务端流已新增
// 从 App 的业务服务端收到流新增通知
bool h265DecoderSupport = await ZegoExpressEngine.instance.isVideoDecoderSupported(ZegoVideoCodecID.H265);
String playStreamID = "";
int viewID = playViewID; // 拉流需要渲染的 viewID, 通过sdk的 createCanvasView 获取
var playCanvas = ZegoCanvas(view);
if (h265DecoderSupport) {
// 支持 H.265 解码
String h265Url = ""; // 请填入 H.265 Url 地址
// H.265 CDN 拉流地址
String url = h265Url;
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
var cdnConfig = ZegoCDNConfig(url, "");
var playerConfig = ZegoPlayerConfig.defaultConfig();
playerConfig.cdnConfig = cdnConfig;
ZegoExpressEngine.instance.startPlayingStream(playStreamID, canvas: playCanvas, config: playerConfig);
}
else {
// 不支持 H.265 解码
String h264Url = ""; // 请填入 H.264 Url 地址
// H.264 CDN 拉流地址
String url = h264Url;
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
var cdnConfig = ZegoCDNConfig(url, "");
var playerConfig = ZegoPlayerConfig.defaultConfig();
playerConfig.cdnConfig = cdnConfig;
ZegoExpressEngine.instance.startPlayingStream(playStreamID, canvas: playCanvas, config: playerConfig);
}
该场景具有以下两个特点:
// 移动端使用 H.265 编码需要开启硬件编码
ZegoExpressEngine.instance.enableHardwareEncoder(true);
// 查询是否支持 H.265 编码
bool h265EncoderSupport = await ZegoExpressEngine.instance.isVideoEncoderSupported(ZegoVideoCodecID.H265);
var videoConfig = new ZegoVideoConfig.preset(ZegoVideoConfigPreset.Preset720P);
if (h265EncoderSupport) {
// 支持 H.265 编码
videoConfig.codecID = ZegoVideoCodecID.H265;
} else {
// 不支持 H.265 编码
videoConfig.codecID = ZegoVideoCodecID.Default;
}
ZegoExpressEngine.instance.setVideoConfig(videoConfig);
if (h265EncoderSupport) {
// 通过 enableH265EncodeFallback 选择是否开启 H.265 编码失败自动降级能力。
ZegoExpressEngine.instance.enableH265EncodeFallback(true);
}
String publishStreamID = ""; // 请输入 streamID
String publishCdnUrl = ""; // 请输入 CdnUrl
// 添加 CDN 转推地址
ZegoExpressEngine.instance.addPublishCdnUrl(publishStreamID, publishCdnUrl).then((ZegoPublisherUpdateCdnUrlResult result) {
// 判断转推 CDN 地址是否添加成功
});
ZegoExpressEngine.instance.startPublishingStream(publishStreamID);
// 开发者通知 App 的业务服务端该条推流的编码格式,以便通知拉流端根据不同的推流编码格式做相应的处理
//收到流新增通知 onRoomStreamUpdate
ZegoExpressEngine.onRoomStreamUpdate = (String roomID, ZegoUpdateType updateType, List<ZegoStream> streamList, Map<String, dynamic> extendedData) {
ZegoVideoCodecID videoCodecID = ZegoVideoCodecID.Default; // 从 App 的业务服务端获取该条流的编码格式。
String playStreamID = ""; // 请输入 streamID
int viewID = playViewID; // 拉流需要渲染的 viewID, 通过sdk的 createCanvasView 获取
var playCanvas = ZegoCanvas(viewID);
if (videoCodecID == ZegoVideoCodecID.H265) {
// 编码格式为 H.265
bool h265DecoderSupport = await ZegoExpressEngine.instance.isVideoDecoderSupported(ZegoVideoCodecID.H265);
// 不支持解码则不拉流
if (h265DecoderSupport) {
// 支持 H.265 解码
String h265Url = ""; // 请填入 H.265 Url 地址
// H.265 CDN 拉流地址
String url = h265Url;
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
var cdnConfig = ZegoCDNConfig(url, "");
var playerConfig = new ZegoPlayerConfig.defaultConfig();
playerConfig.cdnConfig = cdnConfig;
ZegoExpressEngine.instance.startPlayingStream(playStreamID, canvas: playCanvas, config: playerConfig);
} else {
// 不支持 H.265 解码
String h264Url = ""; // 请填入 H.264 Url 地址
// H.264 CDN 拉流地址
String url = h264Url;
// 注意: ZegoCDNConfig 和 ZegoPlayerConfig 中有其他的可配置项
var cdnConfig = new ZegoCDNConfig(url, "");
var playerConfig = new ZegoPlayerConfig.defaultConfig();
playerConfig.cdnConfig = cdnConfig;
ZegoExpressEngine.instance.startPlayingStream(playStreamID, canvas: playCanvas, config: playerConfig);
}
}
else if (videoCodecID == ZegoVideoCodecID.Default) {
// 编码格式为 H.264
ZegoExpressEngine.instance.startPlayingStream(playStreamID, canvas: playCanvas);
}
};
//收到流新增通知 onRoomStreamUpdate
ZegoExpressEngine.onRoomStreamUpdate = (String roomID, ZegoUpdateType updateType, List<ZegoStream> streamList, Map<String, dynamic> extendedData) {
ZegoVideoCodecID videoCodecID = ZegoVideoCodecID.Default; // 从 App 的业务服务端获取该条流的编码格式
String playStreamID = ""; // 请输入 streamID
int viewID = playViewID; // 拉流需要渲染的 viewID, 通过sdk的 createCanvasView 获取
var playCanvas = ZegoCanvas(viewID);
if (videoCodecID == ZegoVideoCodecID.H265) {
// 编码格式为 H.265
bool h265DecoderSupport = await ZegoExpressEngine.instance.isVideoDecoderSupported(ZegoVideoCodecID.H265);
// 不支持解码则不拉流
if (h265DecoderSupport) {
// 仅从 RTC 拉流
var config = ZegoPlayerConfig(ZegoStreamResourceMode.OnlyRTC, ZegoVideoCodecID.H265);
// 支持 H.265 解码
ZegoExpressEngine.instance.startPlayingStream(playStreamID, canvas: playCanvas, config: config);
}
}
else if (videoCodecID == ZegoVideoCodecID.Default) {
// 仅从 RTC 拉流
var config = ZegoPlayerConfig(ZegoStreamResourceMode.OnlyRTC, ZegoVideoCodecID.Default);
// 编码格式为 H.264
ZegoExpressEngine.instance.startPlayingStream(playStreamID, canvas: playCanvas, config: 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 年之前的低端机型解码可能会有帧率波动。
联系我们
文档反馈