Back to Uni App

Live Player

docs/component/live-player.md

2.3.323.0 KB
Original Source

uni直播是DCloud与七牛云合作推出的直播服务,七牛云直播依托云边一体化架构和海量节点资源,构建高效的流媒体服务。通过多维度的节点监控与弹性的节点调度管理,确保高质量服务与成本效益的完美平衡。

live-player 组件是uni直播服务中的拉流(播放)组件,在 Android/iOS 平台使用需要申请绑定包名/Bundle ID(AppID),详情咨询

<!-- ## live-player -->

::: sourceCode

live-player

:::

实时音视频播放

兼容性

Web微信小程序AndroidiOSHarmonyOSHarmonyOS(Vapor)
<a style="color:unset;" href="https://vote.dcloud.net.cn/#/?name=uni-app%20x">x</a>4.414.814.81<a style="color:unset;" href="https://vote.dcloud.net.cn/#/?name=uni-app%20x">x</a>-

属性

名称类型默认值兼容性描述
srcstring(string.VideoURIString)-Web: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -音视频地址。微信小程序支持 flv, rtmp 格式,app平台支持 rtmp, hls 协议
modestring-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: -; HarmonyOS(Vapor): -live(直播),RTC(实时通话)
autoplaybooleanfalseWeb: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -自动播放
mutedbooleanfalseWeb: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -是否静音
orientationstring"vertical"Web: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -画面方向,可选值有 vertical,horizontal
object-fitstring"contain"Web: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -填充模式,可选值有 contain,fillCrop
background-mutebooleanfalseWeb: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -进入后台时是否静音
min-cachestring-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: -; HarmonyOS(Vapor): -最小缓冲区,单位s
max-cachestring-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: -; HarmonyOS(Vapor): -最大缓冲区,单位s
sound-modestring-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(string)
声音输出方式
auto-pause-if-navigateboolean-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(boolean)
当跳转到本小程序的其他页面时,是否自动暂停本页面的实时音视频播放
auto-pause-if-open-nativeboolean-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(boolean)
当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
picture-in-picture-modestring/Array-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(string/Array)
设置小窗模式: push, pop,空字符串或通过数组形式设置多种模式(如: ["push", "pop"]
picture-in-picture-init-position=string-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(string)
小窗模式下小窗的初始显示位置,格式为 (alignment, y),其中 alignment 表示小窗吸附屏幕左侧还是右侧,可选值为 left、right,y 代表小窗最顶部所在的屏幕高度百分比
enable-auto-rotationboolean-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(boolean)
是否开启手机横屏时自动全屏,当系统设置开启自动旋转时生效
referrer-policystring-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(string)
格式固定为 https://servicewechat.com/{appid}/{version}/page-frame.html,其中 {appid} 为小程序的 appid,{version} 为小程序的版本号,版本号为 0 表示为开发版、体验版以及审核版本,版本号为 devtools 表示为开发者工具,其余为正式版本;
enable-castingboolean-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(boolean)
是否支持投屏。开启后,可以通过 LivePlayerContext 上相关方法进行操作。
@statechange(event: UniLivePlayerStatechangeEvent) => void-Web: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -播放状态变化事件,event.detail = {code}
@fullscreenchange(event: UniLivePlayerFullscreenchangeEvent) => void-Web: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -全屏变化事件,event.detail = {direction, fullScreen}
@error(event: UniLivePlayerErrorEvent) => void-Web: x; 微信小程序: x; Android: 4.81; iOS: 4.81; HarmonyOS: -; HarmonyOS(Vapor): -错误事件,event.detail = {errCode, errMsg}
@netstatus(event: UniEvent) => void-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: -; HarmonyOS(Vapor): -网络状态通知,detail = {info}
@audiovolumenotifyeventhandler-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(eventhandler)
播放音量大小通知,detail = {}
@enterpictureinpictureeventhandler-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(eventhandler)
播放器进入小窗
@leavepictureinpictureeventhandler-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(eventhandler)
播放器退出小窗
@castinguserselecteventhandler-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(eventhandler)
用户选择投屏设备时触发 detail = { state: "success"/"fail" }
@castingstatechangeeventhandler-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(eventhandler)
投屏成功/失败时触发 detail = { type, state: "success"/"fail" }
@castinginterrupteventhandler-Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -(eventhandler)
投屏被中断时触发

mode 的属性描述

合法值兼容性描述
RTCWeb: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): --
liveWeb: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): --

orientation 的属性描述

合法值兼容性描述
verticalWeb: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: x; HarmonyOS(Vapor): --
horizontalWeb: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: x; HarmonyOS(Vapor): --

object-fit 的属性描述

合法值兼容性描述
containWeb: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: x; HarmonyOS(Vapor): --
fillCropWeb: x; 微信小程序: 4.41; Android: 4.81; iOS: 4.81; HarmonyOS: x; HarmonyOS(Vapor): --

sound-mode 的属性描述

合法值兼容性描述
speakerWeb: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -扬声器
earWeb: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -听筒

picture-in-picture-mode 的属性描述

合法值兼容性描述
[]Web: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -取消小窗
pushWeb: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -路由 push 时触发小窗
popWeb: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -路由 pop 时触发小窗

referrer-policy 的属性描述

合法值兼容性描述
originWeb: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -发送完整的referrer
no-referrerWeb: x; 微信小程序: 4.41; Android: x; iOS: x; HarmonyOS: x; HarmonyOS(Vapor): -不发送

事件

UniLivePlayerStatechangeEvent

播放状态变化事件

UniLivePlayerStatechangeEvent 的属性值
名称类型必填默认值兼容性描述
detailUniLivePlayerStatechangeEventDetail---
bubblesboolean---
cancelableboolean---
typestring---
targetUniElement---
currentTargetUniElement---
timeStampLong---

detail 的属性描述

名称类型必备默认值兼容性描述
codenumber-Web: x; 微信小程序: x; Android: 4.81; iOS: 4.81; HarmonyOS: x; HarmonyOS(Vapor): -状态码
code 的属性描述
合法值兼容性描述
10000--
10001-初始化
10002-准备播放
10004-播放中
10006-停止渲染
10007-播放完成
10008-播放进度跳转中
10009-播放停止
10010-播放错误
10011-播放结束
10012--
10013-资源释放
UniLivePlayerStatechangeEvent 的方法
名称类型必填默认值兼容性描述
stopPropagation() => void---
preventDefault() => void---

UniLivePlayerFullscreenchangeEvent

全屏事件

UniLivePlayerFullscreenchangeEvent 的属性值
名称类型必填默认值兼容性描述
detailUniLivePlayerFullscreenchangeEventDetail---
bubblesboolean---
cancelableboolean---
typestring---
targetUniElement---
currentTargetUniElement---
timeStampLong---

detail 的属性描述

名称类型必备默认值兼容性描述
directionstring-Web: x; 微信小程序: x; Android: 4.81; iOS: 4.81; HarmonyOS: x; HarmonyOS(Vapor): -屏幕方向
fullScreenboolean-Web: x; 微信小程序: x; Android: 4.81; iOS: 4.81; HarmonyOS: x; HarmonyOS(Vapor): -是否全屏
UniLivePlayerFullscreenchangeEvent 的方法
名称类型必填默认值兼容性描述
stopPropagation() => void---
preventDefault() => void---

UniLivePlayerErrorEvent

错误事件

UniLivePlayerErrorEvent 的属性值
名称类型必填默认值兼容性描述
detailUniLivePlayerError---
bubblesboolean---
cancelableboolean---
typestring---
targetUniElement---
currentTargetUniElement---
timeStampLong---

detail 的属性描述

名称类型必备默认值兼容性描述
errCodenumber---
errSubjectstring--统一错误主题(模块)名称
dataany--错误信息中包含的数据
causeError--源错误信息,可以包含多个错误,详见SourceError
errMsgstring---
errCode 的属性描述
合法值兼容性描述
3001-当前视频格式不支持,视频无法播放
3002-视频解码失败
3003-不支持的解码格式
3004-重连失败,请检查网络情况
3005-视频播放失败,请检查网络或视频资源
UniLivePlayerErrorEvent 的方法
名称类型必填默认值兼容性描述
stopPropagation() => void---
preventDefault() => void---
<!-- UTSCOMJSON.live-player.component_type -->

音视频协议

  • 支持 rtmp、hls 协议格式。

上下文对象API

uni.createLivePlayerContext()

示例

示例为hello uni-app x alpha分支,与最新HBuilderX Alpha版同步。与最新正式版同步的master分支示例另见

该 API 不支持 Web,请运行 hello uni-app x 到 App 平台体验

::: preview

appRedirect https://hellouniappx.dcloud.net.cn/appredirect.html?path=pages/component/live-player/live-player

uvue
<template>
  <view class="uni-flex-item">
    <live-player id="live-player" class="live-player" :src="src" :autoplay="autoplay" :muted="muted"
      :object-fit="objectFit" :background-mute="backgroundMute" :sound-mode="soundMode" :orientation="orientation"
      @statechange="statechange" @fullscreenchange="fullscreenchange" @error="error">
    </live-player>
    <scroll-view class="uni-padding-wrap uni-common-mt uni-flex-item">
      <view class="uni-title">
        <text class="uni-title-text">API示例</text>
      </View>
      <view class="uni-btn-v">
        <button type="primary" @click="play" :disabled="playState">播放</button>
      </view>
      <view class="uni-btn-v">
        <button type="primary" @click="pause" :disabled="!playState">暂停</button>
      </view>
      <view class="uni-btn-v">
        <button type="primary" @click="resume" :disabled="initState || playState || stopState">恢复</button>
      </view>
      <view class="uni-btn-v">
        <button type="primary" @click="stop" :disabled="!playState">停止</button>
      </view>
      <view class="uni-btn-v">
        <button type="primary" @click="mute" :disabled="!playState">静音</button>
      </view>
      <view class="uni-btn-v">
        <button type="primary" @click="requestFullScreen" :disabled="!playState">进入全屏</button>
      </view>
      <view class="uni-btn-v">
        <button type="primary" @click="exitFullScreen" :disabled="!playState">退出全屏</button>
      </view>
      <view class="uni-title">
        <text class="uni-title-text">属性示例</text>
      </view>
      <input class="input margin-10" type="string" placeholder="设置播放地址" @confirm="onSrcComfirm"></input>
      <boolean-data title="设置是否自动播放" :defaultValue="autoplay" @change="onAutoplayChange"></boolean-data>
      <boolean-data title="设置是否静音" :defaultValue="muted" @change="onMutedChange"></boolean-data>
      <boolean-data title="设置进入后台时是否静音" :defaultValue="backgroundMute" @change="onBackgroundMuteChange"></boolean-data>
      <enum-data title="设置填充模式" :items="objectFitItemTypes" @change="onObjectFitChange"></enum-data>
      <enum-data title="设置声音输出方式" :items="soundModeItemTypes" @change="onSoundModeChange"></enum-data>
      <enum-data title="设置画面方向" :items="orientationItemTypes" @change="onOrientationChange"></enum-data>
    </scroll-view>
  </view>
</template>

<script setup>
  import { ItemType } from '@/components/enum-data/enum-data-types';

  const context = ref(null as LivePlayerContext | null);
  const src = ref("");
  const autoplay = ref(false);
  const muted = ref(false);
  const objectFit = ref("contain");
  const backgroundMute = ref(false);
  const soundMode = ref("speaker");
  const orientation = ref("vertical");
  const initState = ref(true);
  const playState = ref(false);
  const stopState = ref(false);

  onReady(() => {
    context.value = uni.createLivePlayerContext("live-player", getCurrentInstance()!.proxy);
  });

  const statechange = (e : UniLivePlayerStatechangeEvent) => {
    console.log("statechange", e);
    switch (e.detail.code) {
      case 10004:
        initState.value = false;
        playState.value = true;
        stopState.value = false;
        break;
      case 10009:
        stopState.value = true;
      case 10006:
      case 10007:
      case 10010:
      case 10011:
        playState.value = false;
        break;
    }
  };
  const fullscreenchange = (e : UniLivePlayerFullscreenchangeEvent) => {
    console.log("fullscreenchange", e);
  };
  const error = (e : UniLivePlayerErrorEvent) => {
    console.log("error", e);
  };
  const isSrcValid = () : boolean => {
    const length = src.value.length;
    if (length <= 0) {
      uni.showToast({
        title: "请输入播放地址",
        icon: "none"
      });
    }
    return length > 0;
  };
  const play = () => {
    if (!isSrcValid()) return;
    context.value?.play({
      success: (res) => {
        console.log("play", JSON.stringify(res));
      },
      fail: (err) => {
        console.log("play", JSON.stringify(err));
      },
      complete: (res) => {
        console.log("play", JSON.stringify(res));
      }
    });
  };
  const pause = () => {
    if (!isSrcValid()) return;
    context.value?.pause({
      success: (res) => {
        console.log("pause", JSON.stringify(res));
      },
      fail: (err) => {
        console.log("pause", JSON.stringify(err));
      },
      complete: (res) => {
        console.log("pause", JSON.stringify(res));
      }
    });
  };
  const resume = () => {
    if (!isSrcValid()) return;
    context.value?.resume({
      success: (res) => {
        console.log("resume", JSON.stringify(res));
      },
      fail: (err) => {
        console.log("resume", JSON.stringify(err));
      },
      complete: (res) => {
        console.log("resume", JSON.stringify(res));
      }
    });
  };
  const stop = () => {
    if (!isSrcValid()) return;
    context.value?.stop({
      success: (res) => {
        console.log("stop", JSON.stringify(res));
      },
      fail: (err) => {
        console.log("stop", JSON.stringify(err));
      },
      complete: (res) => {
        console.log("stop", JSON.stringify(res));
      }
    });
  };
  const mute = () => {
    if (!isSrcValid()) return;
    context.value?.mute({
      success: (res) => {
        console.log("mute", JSON.stringify(res));
      },
      fail: (err) => {
        console.log("mute", JSON.stringify(err));
      },
      complete: (res) => {
        console.log("mute", JSON.stringify(res));
      }
    });
  };
  const requestFullScreen = () => {
    if (!isSrcValid()) return;
    context.value?.requestFullScreen({
      success: (res) => {
        console.log("requestFullScreen", JSON.stringify(res));
      },
      fail: (err) => {
        console.log("requestFullScreen", JSON.stringify(err));
      },
      complete: (res) => {
        console.log("requestFullScreen", JSON.stringify(res));
      }
    });
  };
  const exitFullScreen = () => {
    if (!isSrcValid()) return;
    context.value?.exitFullScreen({
      success: (res) => {
        console.log("exitFullScreen", JSON.stringify(res));
      },
      fail: (err) => {
        console.log("exitFullScreen", JSON.stringify(err));
      },
      complete: (res) => {
        console.log("exitFullScreen", JSON.stringify(res));
      }
    });
  };

  const objectFitItemTypes = [{ "value": 0, "name": "contain" }, { "value": 1, "name": "fillCrop" }] as ItemType[];
  const objectFitItems = ["contain", "fillCrop"];
  const soundModeItemTypes = [{ "value": 0, "name": "speaker" }, { "value": 1, "name": "ear" }] as ItemType[];
  const soundModeItems = ["speaker", "ear"];
  const orientationItemTypes = [{ "value": 0, "name": "vertical" }, { "value": 1, "name": "horizontal" }] as ItemType[];
  const orientationItems = ["vertical", "horizontal"];
  const onSrcComfirm = (event : UniInputConfirmEvent) => {
    let value = event.detail.value;
    if (value == '') return;
    src.value = value;
    console.log("src ->", value);
  };
  const onAutoplayChange = (value : boolean) => {
    autoplay.value = value;
    console.log("autoplay ->", autoplay.value);
  };
  const onMutedChange = (value : boolean) => {
    muted.value = value;
    console.log("muted ->", muted.value);
  };
  const onBackgroundMuteChange = (value : boolean) => {
    backgroundMute.value = value;
    console.log("background-mute ->", backgroundMute.value);
  };
  const onObjectFitChange = (value : number) => {
    objectFit.value = objectFitItems[value];
    console.log("object-fit ->", objectFit.value);
  };
  const onSoundModeChange = (value : number) => {
    soundMode.value = soundModeItems[value];
    console.log("sound-mode ->", soundMode.value);
  };
  const onOrientationChange = (value : number) => {
    orientation.value = orientationItems[value];
    console.log("orientation ->", orientation.value);
  };
</script>

<style>
  .live-player {
    width: 100%;
    height: 40%;
  }

  .input {
    height: 40px;
    background: #FFF;
    padding: 8px 13px;
  }

  .margin-10 {
    margin: 10px;
  }
</style>

:::

参见