更新记录
1.0.2(2026-10-24)
- 新增 requestStreamPermissions 提前申请推流运行时权限
- 修复停止推流后系统投屏指示灯不消失
1.0.1(2026-10-19)
- 修复 Android 14/15 无法推流
1.0.0(2026-09-29)
- 实现录屏投屏直播 RTMP 推流
- 新增 initRtmpEngine / startScreenStream / getStreamStats / stopScreenStream
- 原生胶水层统一参数兜底(屏幕尺寸取偶、30fps、自适应码率 1~8Mbps、关键帧 2s、44100Hz / 1ch / 64000bps)
平台兼容性
uni-app(4.72)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | Android插件版本 | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | × | × | √ | 1.0.0 | √ | 1.0.0 | 5.0 | 1.0.0 | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(4.72)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | 5.0 | × | × | × |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | × | √ |
zy-rtmp-screen
安卓屏幕录制 / 投屏直播 RTMP 推流 UTS 插件。基于 MediaProjection 采集整屏画面 + H.264/AAC 硬编码 + 原生 RTMP 推流 + 前台服务常驻,可选叠加麦克风声音。
平台支持:仅 Android 6.0+(minSdk 23)的 App 端(app-vue / app-nvue / uni-app x app-android);iOS、小程序、H5 均不支持。
功能特性
- 整屏采集:系统「开始录制或投射」授权弹窗,支持整个屏幕(含其他应用、横竖屏切换)。
- 硬编码:MediaCodec H.264 视频 + AAC 音频,低 CPU 占用。
- 参数可配:分辨率、码率、帧率、关键帧间隔、音频码率/采样率/声道、通知栏文案,缺省值均有兜底。
- 前台服务:推流期间常驻通知栏(
mediaProjection|microphone类型),退到后台继续推流,已适配 Android 14/15。 - 实时统计:已推时长、已发送字节、帧数、实时帧率、连接状态,可每秒轮询做 UI 展示。
引入
import {
requestStreamPermissions,
initRtmpEngine,
startScreenStream,
getStreamStats,
stopScreenStream
} from '@/uni_modules/zy-rtmp-screen'
接口风格:所有方法均为单 options 对象入参,异步回调通过 success / fail / complete 承载。
fail回调参数为 UniError 对象:err.errSubject = 'zy-rtmp-screen'、err.errCode(见错误码)、err.errMsg(具体原因)。- 失败时
complete也会收到同一个错误对象。
API 一览
| API | 作用 | 必需参数 |
|---|---|---|
requestStreamPermissions(options) |
提前申请推流所需的运行时权限 | permissions |
initRtmpEngine(options) |
初始化推流引擎(可省略) | 无 |
startScreenStream(options) |
开始录屏推流(弹系统投屏授权) | rtmpUrl |
getStreamStats(options) |
查询推流状态与统计(不中断推流) | 无 |
stopScreenStream(options) |
停止推流并释放资源 | 无 |
推荐调用顺序
requestStreamPermissions(建议页面 onShow 提前申请,可省略)
→ initRtmpEngine(建议显式调用一次,提前暴露 .so 缺失等环境问题)
→ startScreenStream(弹系统投屏授权 → 前台服务启动 → success)
→ getStreamStats(推流中按需轮询,如 1s)
→ stopScreenStream(停止并释放 MediaProjection / 编码器 / RTMP)
API 说明
requestStreamPermissions(options)
提前申请推流所需的运行时权限。推流授权页会把录音与通知权限放进同一次申请且要求全部授予,任缺其一都会以「麦克风权限被拒绝,无法采集音频」结束,因此建议在 startScreenStream 之前先申请。
| 参数 | 类型 | 说明 |
|---|---|---|
permissions |
string[] |
必填,待申请的权限数组,为空时 fail 返回 9050001。推流需要 ['android.permission.RECORD_AUDIO', 'android.permission.POST_NOTIFICATIONS'] |
success |
function |
申请流程结束(全部授予 / 被拒绝两个终态都会回调) |
fail |
function |
9050001 参数错误、9050011 应用环境不可用 |
complete |
function |
成功或失败都会触发 |
success 返回:
| 字段 | 类型 | 说明 |
|---|---|---|
code |
number |
恒为 1 |
msg |
string |
结果描述 |
granted |
boolean |
是否已获得请求的全部权限 |
deniedPermissions |
string[] |
被拒绝的权限列表,全部授予时为空数组 |
doNotAskAgain |
boolean |
被拒绝项中是否存在「不再询问」,为 true 时需引导用户去系统设置手动开启 |
initRtmpEngine(options)
初始化推流引擎(加载原生 .so、创建原生推流上下文)。可省略:startScreenStream 内部发现未初始化会自动初始化。
success 返回 { initialized: boolean, errMsg: string };.so 缺失等环境问题会以 9050002 走 fail。
startScreenStream(options)
开始屏幕录制 RTMP 推流。流程:申请录音/通知权限 → 弹出系统投屏授权 → 启动前台服务采集编码并推流;success 在推流真正启动后才回调。
| 参数 | 说明 | 缺省值 |
|---|---|---|
rtmpUrl |
必填,rtmp:// 开头的推流地址 |
— |
width / height |
编码分辨率(像素,自动取偶) | 屏幕尺寸 |
videoBitrate |
视频码率(bps) | 按分辨率自适应,约 0.07 bit/px/帧,限制在 1~8Mbps |
fps |
帧率 | 30(最大 60) |
iframeInterval |
关键帧间隔(秒) | 2 |
audioEnabled |
是否采集麦克风声音 | true |
audioBitrate |
音频码率(bps) | 64000 |
audioSampleRate |
音频采样率(Hz) | 44100 |
audioChannels |
声道数(1~2) | 1 |
notificationTitle |
前台服务通知栏标题 | 应用名称 |
notificationContent |
前台服务通知栏内容 | 「应用名 正在录屏直播」 |
success / fail / complete |
回调 | — |
除 rtmpUrl 外全部可省略;省略或传 0 均使用上表缺省值(原生层统一兜底,不会因 0 值导致编码器创建失败)。
success 返回:
| 字段 | 类型 | 说明 |
|---|---|---|
rtmpUrl |
string |
推流地址 |
width / height |
number |
实际生效的编码分辨率 |
audioEnabled |
boolean |
是否采集了麦克风声音 |
errMsg |
string |
结果描述 |
已有推流在进行时返回 9050003,需先 stopScreenStream。
getStreamStats(options)
查询当前推流状态与统计信息,不会中断推流,适合每秒轮询刷新 UI。success 返回:
| 字段 | 类型 | 说明 |
|---|---|---|
streaming |
boolean |
是否正在推流 |
connected |
boolean |
RTMP 连接是否在线 |
durationMs |
number |
已推流时长(毫秒) |
bytesSent |
number |
已发送字节数 |
frameCount |
number |
已发送视频帧数 |
fps |
number |
实时帧率(近 500ms 平均值) |
errMsg |
string |
结果描述 |
统计值由原生侧持久化,应用进程被杀后会重置。
stopScreenStream(options)
停止推流:停止前台服务、释放 MediaProjection 与编解码器、断开 RTMP,系统投屏指示灯随之消失。success 返回 { errMsg }。
错误码
失败统一经 fail(err.errCode, err.errMsg) 返回,错误码段为 9050xxx:
| 错误码 | 含义 | 常见原因 / 处理 |
|---|---|---|
9050001 |
参数错误 | rtmpUrl 为空或非 rtmp:// 开头、permissions 为空 |
9050002 |
引擎初始化失败 | 原生推流库加载失败(librtmp_stream.so) |
9050003 |
已有推流在进行 | 先调用 stopScreenStream |
9050004 |
投屏权限被拒绝 | 用户在系统弹窗点了取消,可再次调用重新授权 |
9050005 |
录音权限被拒绝 | 检查 RECORD_AUDIO;Android 13+ 还需通知权限,见权限说明 |
9050006 |
RTMP 连接失败 | 地址/端口/推流密钥错误、服务器未开播、网络不通 |
9050007 |
视频编码器创建失败 | 分辨率或码率超出设备硬编能力,降低 width/height/videoBitrate |
9050008 |
采集启动失败 | 屏幕尺寸获取异常、投影会话被占用 |
9050009 |
停止推流失败 | — |
9050010 |
获取推流状态失败 | — |
9050011 |
应用上下文获取失败 | Activity 未就绪(如 App 退到后台过久),回到页面后重试 |
9050012 |
推流引擎未初始化 | 先 initRtmpEngine(startScreenStream 内也会自动初始化) |
9050013 |
通用错误 | 查看 errMsg |
权限与环境要求
插件 config.json / AndroidManifest.xml 已内置以下权限,无需在 manifest.json 手动补充:
| 权限 | 用途 | 申请方式 |
|---|---|---|
INTERNET |
RTMP 推流 | 安装授予 |
RECORD_AUDIO |
麦克风采集(audioEnabled: true) |
运行时申请 |
POST_NOTIFICATIONS |
前台服务通知(Android 13+) | 运行时申请 |
FOREGROUND_SERVICE |
前台服务常驻推流 | 安装授予 |
FOREGROUND_SERVICE_MEDIA_PROJECTION |
Android 14+ 前台服务类型 | 安装授予 |
FOREGROUND_SERVICE_MICROPHONE |
Android 14+ 前台服务内麦克风录音 | 安装授予 |
WAKE_LOCK |
推流期间保持唤醒 | 安装授予 |
申请权限示例
两个运行时权限建议在进入推流页面时(如 onShow)用 requestStreamPermissions 提前申请,否则推流授权页会一次申请全部并要求全部授予,任一被拒即无法开始。
import { requestStreamPermissions } from '@/uni_modules/zy-rtmp-screen'
// POST_NOTIFICATIONS 仅在 Android 13+ 存在,低版本系统申请会被判为拒绝,需按版本裁剪
const osVersion = parseInt(uni.getSystemInfoSync().osVersion) || 0
const permissions = ['android.permission.RECORD_AUDIO']
if (osVersion >= 13) {
permissions.push('android.permission.POST_NOTIFICATIONS')
}
requestStreamPermissions({
permissions,
success: (res) => {
if (res.granted) return
// 被拒绝也是 success,用 granted / deniedPermissions 判断
console.log('被拒绝的权限', res.deniedPermissions)
if (res.doNotAskAgain) {
// 已勾选「不再询问」,再申请不会弹窗,只能引导用户去系统设置手动开启
uni.showModal({
title: '需要录音/通知权限',
content: '请在系统设置中允许「麦克风」与「通知」权限后重试',
confirmText: '去设置',
success: (r) => { if (r.confirm) uni.openAppAuthorizeSetting({}) }
})
} else {
uni.showToast({ title: '未授予录音/通知权限,无法开始推流', icon: 'none' })
}
},
fail: (err) => console.error('申请权限失败', err.errCode, err.errMsg)
})
- 从系统设置页返回会再次触发
onShow,重新调用即可刷新授权状态。
安卓14+
已适配 Android 14 及以上版本,应用 targetSdkVersion 需设置 34 及以上(manifest.json → app-plus → distribute → sdkConfigs → targetSdkVersion),修改后需重新打包安装才生效。
使用示例
<template>
<view class="page">
<button :disabled="streaming" @click="doStart">开始投屏直播</button>
<button :disabled="!streaming" @click="doStop">停止推流</button>
<view class="status">
{{ statusText }} · {{ stats.connected ? '在线' : '离线' }}
</view>
<view class="status">
{{ formatDuration(stats.durationMs) }} · {{ (stats.bytesSent / 1024).toFixed(1) }}KB ·
{{ stats.frameCount }}帧 / {{ stats.fps }}fps
</view>
</view>
</template>
<script>
import {
requestStreamPermissions,
initRtmpEngine,
startScreenStream,
getStreamStats,
stopScreenStream
} from '@/uni_modules/zy-rtmp-screen'
export default {
data() {
return {
streaming: false,
statusText: '未推流',
timer: null,
stats: { streaming: false, connected: false, durationMs: 0, bytesSent: 0, frameCount: 0, fps: 0 }
}
},
onLoad() {
initRtmpEngine({
fail: (e) => uni.showToast({ title: e.errCode + ': ' + e.errMsg, icon: 'none' })
})
},
onShow() {
// 提前授予录音 + 通知权限,避免推流授权页因通知权限被拒而直接失败
const osVersion = parseInt(uni.getSystemInfoSync().osVersion) || 0
const permissions = ['android.permission.RECORD_AUDIO']
if (osVersion >= 13) permissions.push('android.permission.POST_NOTIFICATIONS')
requestStreamPermissions({
permissions,
success: (res) => {
if (!res.granted) {
uni.showToast({
title: res.doNotAskAgain ? '权限已被永久拒绝,请到系统设置中开启' : '未授予录音/通知权限',
icon: 'none'
})
}
},
fail: (err) => console.error('申请权限失败', err.errCode, err.errMsg)
})
},
onUnload() {
this.clearTimer()
if (this.streaming) stopScreenStream({})
},
methods: {
clearTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
formatDuration(ms) {
const s = Math.floor((ms || 0) / 1000)
const m = Math.floor(s / 60)
return `${m}:${String(s % 60).padStart(2, '0')}`
},
refresh() {
getStreamStats({
success: (res) => {
this.stats = res
if (!res.streaming && this.streaming) {
// 服务被系统回收或推流异常中断
this.streaming = false
this.statusText = '已断开'
this.clearTimer()
uni.setKeepScreenOn({ keepScreenOn: false })
}
}
})
},
doStart() {
startScreenStream({
rtmpUrl: 'rtmp://your-server/live/stream',
width: 720,
height: 1280,
videoBitrate: 2000000,
fps: 24,
audioEnabled: true,
notificationTitle: '投屏直播中',
notificationContent: '点击返回应用可停止推流',
success: (res) => {
this.streaming = true
this.statusText = `推流中 ${res.width}x${res.height}`
uni.setKeepScreenOn({ keepScreenOn: true })
this.clearTimer()
this.timer = setInterval(() => this.refresh(), 1000)
},
fail: (err) => {
this.streaming = false
this.statusText = '未推流'
uni.showToast({ title: err.errCode + ': ' + err.errMsg, icon: 'none' })
}
})
},
doStop() {
stopScreenStream({
success: () => {
this.streaming = false
this.statusText = '未推流'
this.clearTimer()
uni.setKeepScreenOn({ keepScreenOn: false })
},
fail: (err) => console.error('停止失败', err.errCode, err.errMsg)
})
}
}
}
</script>

收藏人数:
购买源码授权版(
试用
使用 HBuilder 导入示例项目
赞赏(0)
下载 69
赞赏 0
下载 12764433
赞赏 1959
赞赏
京公网安备:11010802035340号