揭秘HarmonyOS APP直播推流:RTMP推流、推流协议、推流参数配置、推流稳定性、直播延迟优化
核心要点:掌握HarmonyOS直播推流全链路技术,从RTMP协议原理到推流参数调优,从稳定性保障到延迟优化,打造流畅稳定的直播体验。
一、背景与动机
你有没有这样的经历——打开手机直播,画面卡得像PPT,声音断断续续,弹幕都在刷"主播卡了",那种尴尬简直想找个地缝钻进去。
直播推流,就是整个直播链路的起点。推流质量不行,后面再怎么优化播放端都是白搭。就像自来水管,源头的水压不够,你家里装再好的花洒也出不来水。
在HarmonyOS上做直播推流,和Android/iOS的思路有相似之处,但API体系完全不同。HarmonyOS提供了AVRecorder作为核心录制引擎,配合网络传输能力,可以实现从摄像头采集到RTMP推流的完整链路。但这里面坑不少——推流参数怎么配?弱网环境下怎么保稳定?延迟怎么压到最低?这些都是实打实的技术活。
本文就从原理到实战,把直播推流这件事给你讲透。
二、核心原理
2.1 直播推流全链路
直播推流的本质,就是把音视频数据"打包"后通过特定协议发送到流媒体服务器。整个链路可以概括为:采集 → 编码 → 封装 → 传输。
flowchart TD
classDef primary fill:#4FC3F7,stroke:#0288D1,color:#000
classDef warning fill:#FFB74D,stroke:#F57C00,color:#000
classDef error fill:#EF5350,stroke:#C62828,color:#fff
classDef info fill:#81C784,stroke:#388E3C,color:#000
classDef purple fill:#CE93D8,stroke:#7B1FA2,color:#000
A[摄像头/麦克风采集]:::primary --> B[音视频编码]:::warning
B --> C[FLV/TS封装]:::info
C --> D{推流协议选择}:::purple
D -->|RTMP| E[RTMP推流到服务器]:::primary
D -->|SRT| F[SRT推流到服务器]:::info
D -->|RIST| G[RIST推流到服务器]:::warning
E --> H[CDN分发]:::purple
F --> H
G --> H
H --> I[观众拉流播放]:::primary
style A stroke-width:3px
style E stroke-width:3px
style H stroke-width:3px
2.2 推流协议对比
| 协议 | 全称 | 底层协议 | 延迟 | 安全性 | 适用场景 |
|---|---|---|---|---|---|
| RTMP | Real-Time Messaging Protocol | TCP | 1-3s | 一般 | 主流直播推流 |
| SRT | Secure Reliable Transport | UDP | 0.5-2s | 高(加密) | 弱网/远距离传输 |
| RIST | Reliable Internet Stream Transport | UDP | 0.5-2s | 中 | 广播级传输 |
| RTMPS | RTMP over TLS | TCP+TLS | 1-3s | 高 | 安全推流 |
| WebRTC | Web Real-Time Communication | UDP/SRTP | 0.2-0.5s | 高 | 超低延迟互动 |
RTMP仍然是当前最主流的推流协议,几乎所有CDN和流媒体服务器都支持。它的核心流程是:三次握手 → 建立流 → 发布音视频数据。
2.3 RTMP推流流程详解
RTMP推流的核心步骤:
- TCP连接:客户端与服务器建立TCP连接(默认端口1935)
- RTMP握手:C0/S0 → C1/S1 → C2/S2 三次握手
- 建立连接:发送
connect命令,携带app、tcUrl等参数 - 创建流:发送
createStream命令,获取流ID - 发布流:发送
publish命令,开始推送音视频数据 - 推送数据:按时间戳顺序发送音频(AAC)和视频(H.264/H.265)数据
2.4 推流参数核心指标
flowchart LR
classDef primary fill:#4FC3F7,stroke:#0288D1,color:#000
classDef warning fill:#FFB74D,stroke:#F57C00,color:#000
classDef error fill:#EF5350,stroke:#C62828,color:#fff
classDef info fill:#81C784,stroke:#388E3C,color:#000
classDef purple fill:#CE93D8,stroke:#7B1FA2,color:#000
A[推流参数配置]:::primary --> B[视频参数]:::warning
A --> C[音频参数]:::info
A --> D[网络参数]:::purple
B --> B1[分辨率: 1080p/720p]:::primary
B --> B2[帧率: 25/30fps]:::primary
B --> B3[码率: 2000-6000kbps]:::primary
B --> B4[编码器: H.264/H.265]:::primary
B --> B5[GOP: 1-2s关键帧间隔]:::primary
C --> C1[采样率: 44100/48000Hz]:::info
C --> C2[码率: 128-192kbps]:::info
C --> C3[声道: 立体声/单声道]:::info
C --> C4[编码器: AAC]:::info
D --> D1[缓冲区大小]:::purple
D --> D2[重连策略]:::purple
D --> D3[自适应码率]:::purple
三、代码实战
3.1 基础RTMP推流实现
最基础的推流流程:创建AVRecorder → 配置参数 → 开始录制 → 推流到RTMP服务器。
import { media } from '@kit.MediaKit';
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 直播推流管理器
* 负责音视频采集、编码、推流的完整生命周期管理
*/
class LiveStreamManager {
private avRecorder: media.AVRecorder | null = null;
private cameraManager: camera.CameraManager | null = null;
private isStreaming: boolean = false;
// 推流服务器地址(替换为实际的RTMP推流地址)
private rtmpUrl: string = 'rtmp://your-server/live/stream_key';
/**
* 初始化推流环境
* 包括相机权限获取、AVRecorder创建、参数配置
*/
async initStream(): Promise<void> {
try {
// 第一步:创建AVRecorder实例
this.avRecorder = await media.createAVRecorder();
console.info('[LiveStream] AVRecorder创建成功');
// 第二步:配置录制参数
const config: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_ES,
profile: this.buildStreamProfile(),
url: this.rtmpUrl, // RTMP推流地址
};
// 第三步:准备录制
await this.avRecorder.prepare(config);
console.info('[LiveStream] 推流参数配置完成,准备就绪');
} catch (error) {
const err = error as BusinessError;
console.error(`[LiveStream] 初始化失败: ${err.code} - ${err.message}`);
}
}
/**
* 构建推流参数配置
* 包含视频编码参数、音频编码参数、封装格式
*/
private buildStreamProfile(): media.AVRecorderProfile {
return {
audioBitrate: 128000, // 音频码率 128kbps
audioChannels: 2, // 双声道
audioCodec: media.CodecMimeType.AUDIO_AAC, // AAC编码
audioSampleRate: 44100, // 采样率44.1kHz
fileFormat: media.ContainerFormatType.CFT_MPEG_4, // 封装格式
videoBitrate: 4000000, // 视频码率 4Mbps
videoCodec: media.CodecMimeType.VIDEO_AVC, // H.264编码
videoFrameWidth: 1920, // 视频宽度
videoFrameHeight: 1080, // 视频高度
videoFrameRate: 30, // 帧率30fps
isHdr: false, // 非HDR
};
}
/**
* 开始推流
*/
async startStream(): Promise<void> {
if (!this.avRecorder) {
console.error('[LiveStream] AVRecorder未初始化');
return;
}
try {
await this.avRecorder.start();
this.isStreaming = true;
console.info('[LiveStream] 推流已开始');
} catch (error) {
const err = error as BusinessError;
console.error(`[LiveStream] 推流启动失败: ${err.code} - ${err.message}`);
}
}
/**
* 停止推流
*/
async stopStream(): Promise<void> {
if (!this.avRecorder || !this.isStreaming) {
return;
}
try {
await this.avRecorder.stop();
await this.avRecorder.release();
this.avRecorder = null;
this.isStreaming = false;
console.info('[LiveStream] 推流已停止,资源已释放');
} catch (error) {
const err = error as BusinessError;
console.error(`[LiveStream] 停止推流失败: ${err.code} - ${err.message}`);
}
}
/**
* 获取当前推流状态
*/
getStreamState(): boolean {
return this.isStreaming;
}
}
3.2 自适应码率推流
在弱网环境下,固定码率推流容易导致卡顿和断流。自适应码率(ABR)可以根据网络状况动态调整推流参数,保证流畅度。
import { media } from '@kit.MediaKit';
import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 自适应码率推流管理器
* 根据网络质量动态调整视频码率和分辨率
*/
class AdaptiveStreamManager {
private avRecorder: media.AVRecorder | null = null;
private currentQuality: StreamQuality = StreamQuality.HIGH;
private networkMonitor: connection.NetConnection | null = null;
private bitrateHistory: number[] = []; // 码率历史记录
// 不同画质等级的参数配置
private qualityProfiles: Map<StreamQuality, media.AVRecorderProfile> = new Map([
[StreamQuality.HIGH, {
audioBitrate: 192000,
audioChannels: 2,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 48000,
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
videoBitrate: 6000000, // 6Mbps 高码率
videoCodec: media.CodecMimeType.VIDEO_AVC,
videoFrameWidth: 1920,
videoFrameHeight: 1080,
videoFrameRate: 30,
isHdr: false,
}],
[StreamQuality.MEDIUM, {
audioBitrate: 128000,
audioChannels: 2,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 44100,
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
videoBitrate: 3000000, // 3Mbps 中码率
videoCodec: media.CodecMimeType.VIDEO_AVC,
videoFrameWidth: 1280,
videoFrameHeight: 720,
videoFrameRate: 25,
isHdr: false,
}],
[StreamQuality.LOW, {
audioBitrate: 96000,
audioChannels: 1,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 44100,
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
videoBitrate: 1500000, // 1.5Mbps 低码率
videoCodec: media.CodecMimeType.VIDEO_AVC,
videoFrameWidth: 854,
videoFrameHeight: 480,
videoFrameRate: 20,
isHdr: false,
}],
]);
/**
* 启动网络质量监听
* 根据网络类型自动切换推流画质
*/
startNetworkMonitor(): void {
this.networkMonitor = connection.createNetConnection();
// 监听网络可用事件
this.networkMonitor.on('netAvailable', (data: connection.NetHandle) => {
console.info(`[AdaptiveStream] 网络可用: netId=${data.netId}`);
this.evaluateNetworkAndAdjust(data);
});
// 监听网络丢失事件
this.networkMonitor.on('netLost', (data: connection.NetHandle) => {
console.warn(`[AdaptiveStream] 网络丢失: netId=${data.netId}`);
this.adjustQuality(StreamQuality.LOW);
});
// 注册默认网络监听
this.networkMonitor.register(() => {
console.info('[AdaptiveStream] 网络监听已注册');
});
}
/**
* 评估网络质量并调整推流参数
*/
private async evaluateNetworkAndAdjust(netHandle: connection.NetHandle): Promise<void> {
try {
// 获取网络能力信息
const capabilities = await connection.getNetCapabilities(netHandle);
if (capabilities.linkDownstreamBandwidth >= 10000) {
// 下行带宽 >= 10Mbps,使用高画质
this.adjustQuality(StreamQuality.HIGH);
} else if (capabilities.linkDownstreamBandwidth >= 4000) {
// 下行带宽 4-10Mbps,使用中画质
this.adjustQuality(StreamQuality.MEDIUM);
} else {
// 下行带宽 < 4Mbps,使用低画质
this.adjustQuality(StreamQuality.LOW);
}
} catch (error) {
console.warn('[AdaptiveStream] 获取网络能力失败,使用中画质');
this.adjustQuality(StreamQuality.MEDIUM);
}
}
/**
* 调整推流画质
* 需要重新配置AVRecorder参数
*/
private adjustQuality(quality: StreamQuality): void {
if (this.currentQuality === quality) {
return; // 画质未变,无需调整
}
const previousQuality = this.currentQuality;
this.currentQuality = quality;
console.info(`[AdaptiveStream] 画质调整: ${StreamQuality[previousQuality]} → ${StreamQuality[quality]}`);
// 记录码率变化
const profile = this.qualityProfiles.get(quality);
if (profile) {
this.bitrateHistory.push(profile.videoBitrate);
}
}
/**
* 获取当前画质对应的推流配置
*/
getCurrentProfile(): media.AVRecorderProfile | undefined {
return this.qualityProfiles.get(this.currentQuality);
}
/**
* 获取码率变化历史(用于调试分析)
*/
getBitrateHistory(): number[] {
return [...this.bitrateHistory];
}
/**
* 停止网络监听
*/
stopNetworkMonitor(): void {
if (this.networkMonitor) {
this.networkMonitor.unregister(() => {});
this.networkMonitor = null;
}
}
}
/**
* 推流画质等级枚举
*/
enum StreamQuality {
HIGH = 'HIGH', // 高清 1080p 6Mbps
MEDIUM = 'MEDIUM', // 标清 720p 3Mbps
LOW = 'LOW', // 流畅 480p 1.5Mbps
}
3.3 推流稳定性保障与断线重连
直播推流最怕的就是断流。网络抖动、服务器重启、甚至用户切到后台,都可能导致推流中断。一个健壮的推流系统必须有完善的断线重连机制。
import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 推流稳定性管理器
* 提供断线重连、心跳检测、异常恢复等能力
*/
class StreamStabilityManager {
private avRecorder: media.AVRecorder | null = null;
private rtmpUrl: string = '';
private isStreaming: boolean = false;
// 重连配置
private maxRetryCount: number = 5; // 最大重连次数
private retryCount: number = 0; // 当前重连次数
private baseRetryDelay: number = 1000; // 基础重连延迟(毫秒)
private maxRetryDelay: number = 30000; // 最大重连延迟(毫秒)
private retryTimer: number = -1; // 重连定时器
// 心跳配置
private heartbeatInterval: number = 5000; // 心跳间隔(毫秒)
private heartbeatTimer: number = -1; // 心跳定时器
private missedHeartbeats: number = 0; // 连续丢失心跳次数
private maxMissedHeartbeats: number = 3; // 最大允许丢失心跳次数
// 推流状态回调
private onStreamError?: (error: StreamError) => void;
private onStreamReconnected?: (retryCount: number) => void;
private onStreamFatalError?: () => void;
/**
* 配置推流稳定性参数
*/
configure(config: StabilityConfig): void {
this.maxRetryCount = config.maxRetryCount ?? 5;
this.baseRetryDelay = config.baseRetryDelay ?? 1000;
this.maxRetryDelay = config.maxRetryDelay ?? 30000;
this.heartbeatInterval = config.heartbeatInterval ?? 5000;
this.maxMissedHeartbeats = config.maxMissedHeartbeats ?? 3;
this.onStreamError = config.onStreamError;
this.onStreamReconnected = config.onStreamReconnected;
this.onStreamFatalError = config.onStreamFatalError;
}
/**
* 启动推流(带稳定性保障)
*/
async startStableStream(rtmpUrl: string, profile: media.AVRecorderProfile): Promise<boolean> {
this.rtmpUrl = rtmpUrl;
this.retryCount = 0;
try {
// 创建AVRecorder
this.avRecorder = await media.createAVRecorder();
// 监听状态变化
this.avRecorder.on('stateChange', (state: media.AVRecorderState) => {
console.info(`[Stability] 推流状态变化: ${state}`);
this.handleStateChange(state);
});
// 监听错误事件
this.avRecorder.on('error', (error: BusinessError) => {
console.error(`[Stability] 推流错误: ${error.code} - ${error.message}`);
this.handleStreamError(error);
});
// 配置并准备
const config: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_ES,
profile: profile,
url: rtmpUrl,
};
await this.avRecorder.prepare(config);
await this.avRecorder.start();
this.isStreaming = true;
this.startHeartbeat();
console.info('[Stability] 推流启动成功');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[Stability] 推流启动失败: ${err.code} - ${err.message}`);
this.scheduleReconnect();
return false;
}
}
/**
* 处理推流状态变化
*/
private handleStateChange(state: media.AVRecorderState): void {
switch (state) {
case media.AVRecorderState.IDLE:
// 空闲状态,可能推流已停止
if (this.isStreaming) {
console.warn('[Stability] 推流意外进入IDLE状态,尝试重连');
this.scheduleReconnect();
}
break;
case media.AVRecorderState.RELEASED:
// 已释放
this.isStreaming = false;
this.stopHeartbeat();
break;
default:
break;
}
}
/**
* 处理推流错误
*/
private handleStreamError(error: BusinessError): void {
const streamError: StreamError = {
code: error.code,
message: error.message,
timestamp: Date.now(),
isRecoverable: this.isRecoverableError(error.code),
};
this.onStreamError?.(streamError);
if (streamError.isRecoverable) {
this.scheduleReconnect();
} else {
// 不可恢复的错误,终止推流
console.error('[Stability] 不可恢复的错误,终止推流');
this.onStreamFatalError?.();
this.stopStableStream();
}
}
/**
* 判断错误是否可恢复
*/
private isRecoverableError(errorCode: number): boolean {
// 网络相关错误通常可恢复
const recoverableCodes = [
5400101, // 网络连接失败
5400102, // 网络超时
5400103, // 服务器断开连接
];
return recoverableCodes.includes(errorCode);
}
/**
* 调度重连(指数退避策略)
*/
private scheduleReconnect(): void {
if (this.retryCount >= this.maxRetryCount) {
console.error(`[Stability] 已达最大重连次数(${this.maxRetryCount}),放弃重连`);
this.onStreamFatalError?.();
this.stopStableStream();
return;
}
// 指数退避计算延迟:min(baseDelay * 2^retryCount, maxDelay)
const delay = Math.min(
this.baseRetryDelay * Math.pow(2, this.retryCount),
this.maxRetryDelay
);
this.retryCount++;
console.info(`[Stability] 将在${delay}ms后进行第${this.retryCount}次重连`);
this.retryTimer = setTimeout(async () => {
await this.attemptReconnect();
}, delay) as unknown as number;
}
/**
* 执行重连
*/
private async attemptReconnect(): Promise<void> {
try {
// 先释放旧的AVRecorder
if (this.avRecorder) {
try {
await this.avRecorder.release();
} catch (e) {
// 忽略释放错误
}
this.avRecorder = null;
}
// 重新创建并启动
this.avRecorder = await media.createAVRecorder();
this.avRecorder.on('error', (error: BusinessError) => {
this.handleStreamError(error);
});
const config: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_ES,
profile: this.getDefaultProfile(),
url: this.rtmpUrl,
};
await this.avRecorder.prepare(config);
await this.avRecorder.start();
// 重连成功,重置计数器
this.isStreaming = true;
this.retryCount = 0;
this.missedHeartbeats = 0;
this.startHeartbeat();
console.info('[Stability] 重连成功');
this.onStreamReconnected?.(this.retryCount);
} catch (error) {
console.error('[Stability] 重连失败,继续尝试');
this.scheduleReconnect();
}
}
/**
* 启动心跳检测
*/
private startHeartbeat(): void {
this.stopHeartbeat(); // 先清除旧的定时器
this.missedHeartbeats = 0;
this.heartbeatTimer = setInterval(() => {
this.checkStreamHealth();
}, this.heartbeatInterval) as unknown as number;
}
/**
* 停止心跳检测
*/
private stopHeartbeat(): void {
if (this.heartbeatTimer !== -1) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = -1;
}
}
/**
* 检查推流健康状态
*/
private checkStreamHealth(): void {
if (!this.avRecorder || !this.isStreaming) {
this.missedHeartbeats++;
if (this.missedHeartbeats >= this.maxMissedHeartbeats) {
console.warn('[Stability] 心跳检测失败,触发重连');
this.scheduleReconnect();
}
return;
}
// 检查AVRecorder当前状态
try {
const state = this.avRecorder.state;
if (state === media.AVRecorderState.STARTED || state === media.AVRecorderState.PAUSED) {
this.missedHeartbeats = 0; // 重置心跳计数
} else {
this.missedHeartbeats++;
if (this.missedHeartbeats >= this.maxMissedHeartbeats) {
console.warn('[Stability] 推流状态异常,触发重连');
this.scheduleReconnect();
}
}
} catch (error) {
this.missedHeartbeats++;
}
}
/**
* 获取默认推流配置
*/
private getDefaultProfile(): media.AVRecorderProfile {
return {
audioBitrate: 128000,
audioChannels: 2,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 44100,
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
videoBitrate: 4000000,
videoCodec: media.CodecMimeType.VIDEO_AVC,
videoFrameWidth: 1920,
videoFrameHeight: 1080,
videoFrameRate: 30,
isHdr: false,
};
}
/**
* 停止推流(主动停止,不触发重连)
*/
async stopStableStream(): Promise<void> {
this.isStreaming = false;
this.stopHeartbeat();
if (this.retryTimer !== -1) {
clearTimeout(this.retryTimer);
this.retryTimer = -1;
}
if (this.avRecorder) {
try {
await this.avRecorder.stop();
await this.avRecorder.release();
} catch (e) {
// 忽略
}
this.avRecorder = null;
}
console.info('[Stability] 推流已安全停止');
}
}
/**
* 推流错误信息
*/
interface StreamError {
code: number;
message: string;
timestamp: number;
isRecoverable: boolean;
}
/**
* 稳定性配置参数
*/
interface StabilityConfig {
maxRetryCount?: number;
baseRetryDelay?: number;
maxRetryDelay?: number;
heartbeatInterval?: number;
maxMissedHeartbeats?: number;
onStreamError?: (error: StreamError) => void;
onStreamReconnected?: (retryCount: number) => void;
onStreamFatalError?: () => void;
}
四、踩坑与注意事项
4.1 推流参数配置的坑
| 坑点 | 现象 | 解决方案 |
|---|---|---|
| 码率过高 | 弱网环境下频繁卡顿 | 根据网络质量动态调整,初始码率建议3000kbps |
| GOP过大 | 首帧加载慢、seek延迟高 | GOP设置为1-2秒,关键帧间隔25-60帧 |
| 分辨率不匹配 | 画面拉伸或黑边 | 确保采集分辨率与推流分辨率一致 |
| 帧率过高 | 设备发热、编码跟不上 | 30fps足够,不要盲目追求60fps |
| 音频采样率错误 | 声音变调或爆音 | AAC推荐44100Hz或48000Hz |
4.2 RTMP推流常见问题
问题1:推流延迟高(>5秒)
延迟来源分析:
- 采集延迟:50-100ms
- 编码延迟:50-200ms
- 网络传输:100-500ms
- 服务器处理:50-200ms
- CDN分发:100-300ms
- 播放端缓冲:1-3s(最大延迟来源)
优化方案:
// 延迟优化配置
const lowLatencyProfile: media.AVRecorderProfile = {
audioBitrate: 128000,
audioChannels: 2,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 44100,
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
videoBitrate: 2500000, // 适当降低码率
videoCodec: media.CodecMimeType.VIDEO_AVC,
videoFrameWidth: 1280, // 720p降低编码压力
videoFrameHeight: 720,
videoFrameRate: 25, // 25fps平衡流畅度与延迟
isHdr: false,
};
问题2:推流画面花屏
原因通常是关键帧(I帧)丢失或时间戳不连续。确保:
- GOP间隔合理(1-2秒)
- 时间戳单调递增
- 网络稳定,避免丢包
问题3:后台推流中断
HarmonyOS应用切到后台后,媒体资源可能被回收。需要在onBackground中保存状态,在onForeground中恢复推流。
4.3 性能优化建议
- 使用硬件编码:确保
videoCodec使用VIDEO_AVC(H.264),硬件编码效率远高于软编 - 合理设置缓冲区:过大的缓冲区增加延迟,过小容易卡顿
- 避免频繁切换画质:每次切换都需要重新配置编码器,至少间隔10秒
- 监控CPU和内存:推流是CPU密集型操作,注意设备温度和内存占用
五、HarmonyOS 6适配
5.1 API变更
| 变更项 | HarmonyOS 5.0 | HarmonyOS 6 |
|---|---|---|
| AVRecorder状态机 | 6个状态 | 新增FLUSHED状态,共7个状态 |
| H.265编码支持 | 部分设备支持 | 全面支持VIDEO_HEVC编码 |
| 推流协议 | 仅RTMP | 新增SRT协议原生支持 |
| 自适应码率 | 需手动实现 | 内置AdaptiveBitrate模块 |
| 后台推流 | 需Background Task | 新增LiveStreaming长时任务类型 |
5.2 迁移指南
// HarmonyOS 6 新增的SRT推流支持
import { media } from '@kit.MediaKit';
// HarmonyOS 6 可以直接使用SRT协议推流
const srtConfig: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_ES,
profile: {
audioBitrate: 128000,
audioChannels: 2,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 48000,
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
videoBitrate: 4000000,
videoCodec: media.CodecMimeType.VIDEO_HEVC, // HarmonyOS 6支持H.265
videoFrameWidth: 1920,
videoFrameHeight: 1080,
videoFrameRate: 30,
isHdr: false,
},
url: 'srt://your-server:9000?streamid=live/stream_key', // SRT推流地址
};
5.3 HarmonyOS 6后台推流
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { wantAgent } from '@kit.AbilityKit';
// HarmonyOS 6 新增 LiveStreaming 长时任务类型
async function requestLiveStreamingBackground(): Promise<void> {
const wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [],
requestCode: 0,
wantAgentFlags: [],
bundleName: 'com.example.livestream',
abilityName: 'LiveStreamAbility',
};
const agent = await wantAgent.getWantAgent(wantAgentInfo);
backgroundTaskManager.startBackgroundRunning(
'com.example.livestream',
backgroundTaskManager.BackgroundMode.LIVE_STREAMING, // 新增类型
agent
);
}
六、总结
mindmap
root((直播推流))
推流协议
RTMP 主流协议
SRT 弱网优化
RIST 广播级
RTMPS 安全推流
推流参数
视频参数
分辨率 1080p/720p
帧率 25/30fps
码率 2000-6000kbps
编码 H.264/H.265
GOP 1-2s
音频参数
采样率 44.1/48kHz
码率 128-192kbps
编码 AAC
稳定性保障
断线重连
指数退避策略
最大重连次数限制
心跳检测
定时健康检查
异常自动恢复
自适应码率
网络质量评估
动态参数调整
延迟优化
降低编码延迟
减少缓冲区
合理GOP设置
SRT协议替代
HarmonyOS 6
H.265全面支持
SRT原生推流
内置ABR模块
LiveStreaming后台任务
关键要点回顾:
- RTMP是推流主流:虽然协议老,但生态最完善,所有CDN都支持,入门首选
- 参数配置是核心:码率、帧率、GOP三个参数直接决定推流质量和延迟,需要根据场景精细调整
- 稳定性是底线:断线重连(指数退避)+ 心跳检测 + 自适应码率,三重保障缺一不可
- 延迟优化是系统工程:从采集到播放,每个环节都有优化空间,播放端缓冲是最大延迟源
- HarmonyOS 6带来新能力:H.265编码、SRT协议、内置ABR,推流体验大幅提升
下一篇我们将深入视频缩略图技术,看看如何高效地提取视频封面和预览图。
- 点赞
- 收藏
- 关注作者
评论(0)