更新记录

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>

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android.permission.INTERNET,android.permission.RECORD_AUDIO,android.permission.FOREGROUND_SERVICE,android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION,android.permission.POST_NOTIFICATIONS,android.permission.WAKE_LOCK

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

录屏画面与音频仅推流至开发者填写的 RTMP 服务器地址,插件本身不上传任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。