Skip to content

Frame 播放器接入与初始化

面向业务开发者:本页说明怎样嵌入和驱动已经部署好的 iframe 播放器。运维如何构建、托管、验真和回滚,请读iframe 构建、部署与升级

先选对接入面

选择适合什么需要接受的边界
React / Vue Inline最在意首帧,且可与宿主页面同进程运行播放器与宿主共享 DOM、样式和运行时
React Frame / Vue Frame需要 CSS、第三方内容或故障隔离,也需要命令和完整事件iframe 建立和握手有额外成本;命令经通信通道异步完成
静态 iframe URLCMS、Markdown 或没有 npm 构建链只能订阅之后发生的事件,没有 SDK 命令句柄和加载期聚合状态

Frame 是隔离边界,不是“更安全”或“更快”的绝对选项。视频源签名、CORS、业务鉴权和品牌 UI 仍属于宿主和服务端。

React Frame:最小可播放接入

tsx
import { useRef } from 'react'
import { VideoPlayerFrame, type VideoPlayerFrameHandle } from '@sentinel-lab/video-react-frame'

const source = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'

export function ArticlePlayer() {
  const playerRef = useRef<VideoPlayerFrameHandle>(null)

  return (
    <>
      <VideoPlayerFrame
        ref={playerRef}
        source={source}
        controls
        onReady={({ duration }) => console.info('metadata ready', duration)}
        onPlayableChange={({ playable, recoverable, reason }) => {
          console.info({ playable, recoverable, reason })
        }}
        onError={(error) => console.error(error.code, error.message)}
      />
      <button type="button" onClick={() => void playerRef.current?.play()}>
        播放
      </button>
    </>
  )
}

play()pause()seek()、全屏、清晰度、字幕和 reconnect() 都返回 Promise:它们要跨 iframe 发送命令,必须处理失败或至少避免把未处理 Promise 留给浏览器。完整句柄见Methods 参考

Vue Frame:最小可播放接入

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VideoPlayerFrame } from '@sentinel-lab/video-vue-frame'

const source = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'
const player = ref<InstanceType<typeof VideoPlayerFrame> | null>(null)

async function play() {
  await player.value?.play()
}
</script>

<template>
  <VideoPlayerFrame
    ref="player"
    :source="source"
    controls
    @ready="({ duration }) => console.info('metadata ready', duration)"
    @playable-change="({ playable, recoverable, reason }) => console.info({ playable, recoverable, reason })"
    @error="(error) => console.error(error.code, error.message)"
  />
  <button type="button" @click="play">播放</button>
</template>

Vue 模板使用 kebab-case 事件名,例如 @playable-change;暴露在模板 ref 上的方法同样是异步命令。组件自带 SSR 客户端守卫,不必额外套一层 <ClientOnly>

静态 iframe:手工权限与事件过滤

静态嵌入不经过 Frame SDK。给 iframe 有意义的标题、所需权限和 allowfullscreen,先订阅事件,再设置 src,否则早期事件不会回放。

html
<iframe
  id="article-player"
  title="文章视频"
  allow="autoplay; fullscreen; picture-in-picture; encrypted-media; screen-wake-lock"
  allowfullscreen
></iframe>
<script src="https://sentinel-video.pages.dev/embed/v1.0.25/helper.js"></script>
<script>
  const iframe = document.getElementById('article-player')
  const player = SentinelEmbed.createEmbedListener({
    origin: 'https://sentinel-video.pages.dev',
    iframe,
  })

  player.on('ended', () => console.info('show next article'))
  player.on('error', (error) => console.error(error.code, error.message))

  iframe.src = 'https://sentinel-video.pages.dev/embed/v1.0.25/?src=https%3A%2F%2Ftest-streams.mux.dev%2Fx36xhzz%2Fx36xhzz.m3u8'
</script>

createEmbedListener({ origin, iframe }) 同时校验消息来源、该 iframe 的窗口身份和协议结构。它只接收事件,不能 play()seek() 或报告 Frame 的 frame_disconnected 聚合状态;这些需求应选 React / Vue Frame。

URL、版本与调试

字段作用不是什么
originiframe 产物所在源;可指向私有 CDN 或本地 embed-app哪些父页面被允许连接的白名单
version路径段,如 v4 或已留存的精确版本protocol 字段,也不是 npm 版本的替代品
debug打开 Penpal 的握手和消息日志,URL 会带 ?debug=1播放引擎性能诊断或生产遥测

默认 URL 是 https://sentinel-video.pages.dev/embed/v1.0.25/。本地开发可使用 origin="http://localhost:5174"version="" 指向 embed-app 开发服务器;业务环境使用其他地址时,由部署方提供实际 origin 和精确 iframe 应用版本目录,再写进宿主配置。也可在宿主构建期通过 VITE_SENTINEL_EMBED_ORIGINVITE_SENTINEL_EMBED_VERSION 统一覆盖。

Loading、握手与失败处置

playablechange{ playable, recoverable, reason } 驱动 UI,不要自己拼 waiting、重连和错误事件。Frame 在连接中先给出 frame_disconnected,连接完成后通常进入 initializing;可恢复时展示加载或重试提示,不可恢复时展示业务 CTA 并保留错误码给监控。

  • version_incompatible:宿主与 iframe 的契约 major 不兼容;选择匹配的 SDK 与 iframe 版本,不要继续发送命令。
  • csp_denied:父页面的 CSP 拦截了 iframe;让站点管理员检查 frame-src 等策略。
  • frame_disconnected 且不可恢复:iframe 失联或已崩溃;保留业务错误页,必要时由用户动作触发重新挂载。
  • 可恢复的播放失败:在业务 CTA 中调用 await ref.current?.reconnect({ resetCounter: true });不要自动无限重试。

完整事件与错误码只维护一份,见EventsErrors

权限与安全:四层不要混淆

谁配置解决的问题
Frame SDK allowReact / Vue Frame 自动写入 autoplay; fullscreen; picture-in-picture; encrypted-media; screen-wake-lockiframe 可请求这些浏览器能力
宿主静态 iframe allow / allowfullscreen静态嵌入开发者静态 HTML 的同类权限声明
VITE_SENTINEL_ALLOWED_PARENT_ORIGINS运维在 embed-app 构建前设置哪些精确父 origin 可与 iframe Penpal 握手、接收静态事件
CSP frame-ancestors / 父页 Permissions Policy服务端或站点平台谁可嵌入该页面、浏览器是否最终授予功能
视频源 CORS视频 CDN / 源站iframe 内播放器是否能读取 HLS、MP4、FLV 及其分片

自动 allow 不是成功保证:浏览器支持、用户手势、父页面更严格的 Permissions Policy 仍会限制自动播放、全屏或 PiP。也不要直接复制未经实测的 sandbox 配置;它会改变 origin、脚本或表单能力,应按业务安全模型单独设计和验收。

VITE_SENTINEL_ALLOWED_PARENT_ORIGINS 是部署方的构建期值:公开 CDN 不配时允许任意父站,受限部署由部署方写入精确 origin 后重新构建。它不取代 CSP,不是 origin prop,也不替代视频源 CORS。

和运维的交接

开发者交给运维运维回传给开发者
父页面 origin 清单、所需权限(autoplay / fullscreen / PiP)、预期 iframe origin 与 version实际 iframe origin、可用版本路径、白名单已写入的构建版本
业务的 frame-src / Permissions Policy 约束和验收场景已生效的 frame-ancestors / 边缘权限策略、MIME / CORS / 版本路径验真结果
握手、播放、命令、失败 CTA 的测试结果部署 ID、回滚目标和变更窗口

部署、HTTPS、缓存、MIME、SPA fallback 与回滚由iframe 构建、部署与升级负责;播放器的全量参数、事件、方法和错误码由API 按场景查负责。