完全指南:useVideoPlayer、VideoPlayer 与 VideoView 深度解析)
音视频移动开发【免费下载链接】react-native-videoA component for react-native项目地址https://gitcode.com/gh_mirrors/re/react-native-video点击查看免费下载本篇指南系统讲解 react-native-video v7 的核心架构转型——从 v6 的组件 属性component-with-props模型转向播放器对象player-object模型。你将掌握useVideoPlayer/new VideoPlayer创建与管理播放器实例、VideoView作为可选显示面、基于订阅的事件系统以及预加载、DRM、字幕、画中画等完整实战方案并透过仓库源码理解其底层 Nitro 实现。v7 心智模型播放器是对象不是组件v7 最本质的变化是播放器不再是一个Video组件的内部状态而是一个由你创建并持有的VideoPlayer实例。所有播放状态和控制能力都挂在这个实例上视图只是它可选的外接显示器。这一模型由三个核心概念构成对比 v6 一目了然能力v6组件模型v7播放器对象模型创建播放器Video source{...} /隐式创建useVideoPlayer(source, setup?)或new VideoPlayer(source)显式创建控制播放ref.seek()、ref.pause()等命令式 refplayer.seekTo()、player.pause()等实例方法显示画面Video自带画面VideoView player{player} /可选绑定显示面事件JSX 回调属性onProgress{...}播放器上的订阅useEvent/addEventListener音频播放仍需渲染组件无需任何视图创建播放器即可最小可用示例import { useVideoPlayer, VideoView, useEvent } from react-native-video; const player useVideoPlayer({ uri: https://example.com/master.m3u8 }); useEvent(player, onProgress, ({ currentTime }) {}); return VideoView player{player} controls style{{ flex: 1 }} /;v6 用户特别注意Video source、ref.seek()和drm属性在 v7 中不存在。继续沿用 v6 教程的写法将无法工作请使用上方的播放器 API。环境要求React Native ≥ 0.75peer 依赖react-native-nitro-modules≥ 0.35iOS 15Android 7API 24库的minSdkVersion为 24基于 Nitro 实现同时支持New Architecture 与 Old Architecture视图层是一个薄的 Fabric host桥接到 Nitro view managerWeb 端是建立在 video.js v10 之上的并行实现仅原生支持的选项在 Web 上是空操作no-op。创建播放器useVideoPlayer 与 VideoConfiguseVideoPlayer(source, setup?)是 v7 创建播放器的默认入口它创建一个VideoPlayer并托管其生命周期source 变化时重建播放器组件卸载时自动释放。const player useVideoPlayer( { uri: https://example.com/master.m3u8 }, (player) { // setup —— 播放器开始加载时执行一次适合做初始配置 player.loop true; player.volume 0.8; }, );从源码看useVideoPlayer的实现细节印证了这一行为它内部基于useManagedInstancefactory 中创建new VideoPlayer(source)并通过sourceEqualVideoPlayerSource走equals()其余走JSON.stringify比较判断 source 是否变化以决定是否重建见 packages/react-native-video/src/core/hooks/useVideoPlayer.ts。setup 回调在initializeOnCreation为 true 时并非立即调用而是注册在onLoadStart与onStatusChange事件上只调用一次callSetupOnce这样即使视频极小、onLoadStart早于 JS 事件注册触发也能保证 setup 一定被执行。cleanup 阶段调用player.__destroy()释放原生资源。setup 回调 vs 组件体内直接赋值优先使用 setup 回调做初始配置loop、volume、autoplay、muted、playInBackground、showNotificationControls、初始seekTo等。原因很直接在组件体内写player.x ...会在每次渲染时执行setup 回调只执行一次返回的player仅用于运行时交互——按钮事件处理器和渲染期读取。source 的两种形态与 VideoConfigsource可以是URL 字符串https://…或require(...)数字本地资源也可以是VideoConfig对象。仓库中 packages/react-native-video/src/core/types/VideoConfig.ts 对VideoConfig的字段给出了完整定义VideoConfig字段类型说明uristring \| number必填。URL 或require()本地资源headersRecordstring,stringHTTP 请求头iOS 端也用于默认的 DRM 许可证请求drmDrmParamsDRM 参数需要react-native-video/drm插件见下文 DRM 章节bufferConfigBufferConfig缓冲调优参数详见 streaming-sources.mdmetadata{ title?; subtitle?; description?; artist?; imageUri? }锁屏/系统通知展示的媒体信息externalSubtitles{ uri; label; type?; language? }[]外挂字幕iOS 仅支持.vttinitializeOnCreationboolean默认true设为false时需自行调用player.initialize()值得注意的细节源码注释中明确说明externalSubtitles的type支持vtt | srt | ssa | ass | autoauto从文件扩展名推断但 URL 无扩展名时不可用language使用 ISO 639-1/639-2 代码如en、es、zh-CN缺省为undiOS 上label可能被系统播放器覆盖。metadata的类型定义CustomVideoMetadata允许标题、副标题、描述、艺术家和封面图地址。useVideoPlayerHook与 new VideoPlayerClass的取舍两者创建的是同一个VideoPlayer类区别仅在于 hook 替你托管生命周期useVideoPlayer(source, setup?)—— 组件内播放的默认选择约 95% 场景。创建播放器、source 变化时重建、卸载时自动释放。它还完整覆盖了预加载、延迟初始化initializeOnCreation: falsepreload()/initialize()以及信息流/列表场景preload()replaceSourceAsync()——这些都不需要降到 class 去写。new VideoPlayer(source)—— 播放器需要存活在组件生命周期之外时。主要场景是播放器必须比创建它的组件活得更久——跨屏幕/导航保持存活或由应用级状态持有。更罕见的是非 React 上下文没有组件可以挂 hook例如后台音频服务或库/插件代码。此时你需要自己调用player.release()——hook 的自动清理保障不复存在。// 仅用于非 React / 需要比组件存活更久的场景 —— 必须自己调用 release() const player new VideoPlayer({ uri }); // …真正用完时 player.release();经验法则在组件内一律用 hook预加载和信息流也不例外只有播放器需要比组件活得更久或极少数没有组件可挂载的场合才降到 class。初始配置放在 setup 回调hook或new VideoPlayer之后class立即设置永远不要放在渲染体内。VideoPlayer属性与方法全景仓库中VideoPlayer类的实现位于 packages/react-native-video/src/core/VideoPlayer.ts其接口契约定义在 packages/react-native-video/src/core/types/VideoPlayerBase.ts。读写属性volume0–1muted时读回 0.0currentTime秒可读可写写入等效于seekTo会被钳制在 0 ~ duration 范围muted、looprate1 为正常速度0 即暂停设为 0 会暂停视频playInBackground后台播放可覆盖playWhenInactiveplayWhenInactiveiOS应用失活如打开控制中心时是否继续播放showNotificationControls锁屏/控制中心媒体控制mixAudioModemixWithOthers | doNotMix | duckOthers | auto默认autoignoreSilentSwitchModeiOSauto | ignore | obeydisableAudioSessionManagementiOS禁用库对 AVAudioSession 的管理让录音类库接管默认false。只读属性statusidle | loading | readyToPlay | errorduration秒未知时为NaNisPlayingsource不可变selectedTrack当前选中的文字轨道。一个值得了解的源码细节VideoPlayer的source、status、duration等属性都是直接转发到底层 Nitro hybrid 对象this.player而ignoreSilentSwitchMode与disableAudioSessionManagement在非 iOS 平台会有 DEV 模式警告提示见 packages/react-native-video/src/core/VideoPlayer.ts。release()之后再次访问任何属性或方法都会抛出VideoRuntimeError错误码player/released所以释放后应创建新实例而不是复用旧对象。VideoView可选的显示面VideoView是绑定到播放器的显示面它是可选的——纯音频播放完全可以不渲染任何视图。VideoView player{player} controls style{{ flex: 1 }} /VideoView继承 RNViewProps并扩展以下属性定义见 packages/react-native-video/src/core/video-view/VideoViewProps.ts默认值确认于 packages/react-native-video/src/core/video-view/VideoView.tsx 的updatePropsProp类型默认值说明playerVideoPlayer—必填controlsbooleanfalse原生播放控制条resizeModecontain\|cover\|stretch\|nonenonenone近似 containpictureInPicturebooleanfalse显示画中画按钮autoEnterPictureInPicturebooleanfalse退到后台时自动进入画中画keepScreenAwakebooleantrue挂载期间保持屏幕常亮surfaceTypesurface\|texturesurface仅 Androidtexture可动画/变换但性能略低surface性能更佳但不可动画关于resizeMode的精确语义源码注释给了准确定义contain保持宽高比完整放入视图cover保持宽高比填满视图可能裁剪stretch不保持宽高比填满none不做缩放。占位图/海报由于 v7 没有独立的 poster 属性正确做法是在VideoView上层叠一个自己的Image并在onReadyToDisplay事件触发时移除它。命令式能力enterFullscreen、enterPictureInPicture、canEnterPictureInPicture、exitFullscreen、exitPictureInPicture以及视图事件监听addEventListener通过VideoViewRef提供——这正是 packages/react-native-video/src/core/video-view/VideoViewProps.ts 中VideoViewRef接口定义的完整方法集。纯音频播放无需视图react-native-video 本身就是一款合格的纯音频播放器——应用里已有该库时音频播放几乎零成本无需额外依赖。不渲染VideoView创建播放器直接play()即可// 通过 setup 回调自动播放只执行一次—— 而不是在渲染体中修改播放器 const player useVideoPlayer({ uri: https://example.com/audio.m3u8 }, (player) player.play());锁屏/后台音频设置player.playInBackground trueplayer.showNotificationControls true并在VideoConfig.metadata中传入标题等信息——详见 background-playback.md。source 创建后不可变——要换内容必须调用player.replaceSourceAsync(newSource)。播放控制方法都在播放器实例上控制操作发生在播放器实例上不是 props、也不是组件的 ref方法契约见 packages/react-native-video/src/core/types/VideoPlayerBase.ts方法说明play()开始/恢复播放pause()暂停seekTo(seconds)绝对跳转v6 的seek()对应此方法seekBy(seconds)相对跳转如seekBy(-10)后退 10 秒replaceSourceAsync(source \| null)更换 source传null清空。返回 Promisepreload()预缓冲信息流场景的关键返回 Promiseinitialize()仅当initializeOnCreation: false时需要release()释放原生资源之后播放器不可用useVideoPlayer会在卸载时自动调用getAvailableTextTracks()/selectTextTrack(t \| null)文字轨道管理addEventListener(event, cb)事件订阅返回{ remove() }常见模式player.isPlaying ? player.pause() : player.play(); // 播放/暂停切换 player.seekTo(30); // 跳到 0:30 player.seekBy(15); // 前进 15 秒 player.rate 1.5; // 1.5 倍速0 表示暂停 await player.replaceSourceAsync({ uri: nextUrl }); // 换源注意seekBy/seekTo超出0 ~ duration会被自动钳制源码注释明确说明rate 0即暂停。从实现看这些方法都经由VideoPlayer转发到 Nitro 原生对象并把原生错误统一解析为VideoRuntimeError错误解析逻辑见 packages/react-native-video/src/core/types/VideoError.ts 相关的tryParseNativeVideoError。事件系统订阅而非 JSX 回调v7 的事件是播放器上的订阅有两种用法// 1) hook —— 卸载时自动移除推荐 useEvent(player, onProgress, ({ currentTime }) {}); // 2) 命令式 —— 返回 { remove() } const sub player.addEventListener(onEnd, () {}); // 稍后sub.remove();useEvent的内部实现packages/react-native-video/src/core/hooks/useEvent.ts很简单useEffect里调用player.addEventListener清理函数调用subscription.remove()依赖数组为[player, event, callback]。而addEventListener的分发逻辑在 packages/react-native-video/src/core/events/VideoPlayerEvents.native.tsonError是JS-only 事件存入jsEventListeners集合其余事件映射到 Nitro 事件发射器的addOnXxxListener方法。播放器事件与载荷完整事件清单定义在 packages/react-native-video/src/core/types/Events.tsVideoPlayerEvents JS 层的onError事件载荷说明onLoad{ currentTime, duration, width, height, orientation }元数据就绪onLoadStart{ sourceType: local\|network, source }开始加载onProgress{ currentTime, bufferDuration }秒为单位onPlaybackStateChange{ isPlaying, isBuffering }onPlaybackRateChangerate: numberonBufferbuffering: booleanonSeekseekTime: numberonVolumeChange{ volume, muted }onEnd—播放到结尾onReadyToDisplay—首帧就绪onStatusChangestatus: idle\|loading\|readyToPlay\|erroronErrorerror: VideoRuntimeErrorJS-only订阅后运行时错误改走回调而非抛出。务必处理onTimedMetadata{ metadata: { value, identifier }[] }iOS/AndroidonTextTrackDataChangedstring[]当前显示的字幕文本onTrackChangeTextTrack \| null选中的文字轨道变化onBandwidthUpdate{ bitrate, width?, height? }width/height仅 AndroidonControlsVisibleChangevisible: booleanonAudioBecomingNoisy—AndroidonAudioFocusChangehasAudioFocus: booleanAndroidonExternalPlaybackChangeactive: booleaniOSAirPlay视图事件在VideoView或其 ref 上onFullscreenChange(boolean)、onPictureInPictureChange(boolean)、willEnterFullscreen、willExitFullscreen、willEnterPictureInPicture、willExitPictureInPicture——这些属于视图生命周期事件与播放器事件分离。VideoView player{player} onFullscreenChange{(full) {}} /错误处理要点订阅onError会把 v7 从抛出异常切换到回调模式——务必接上避免播放错误直接崩溃。错误重试收到onError后用player.replaceSourceAsync(currentSource)重新加载源或用key重挂载配合自己的退避策略和重试按钮。这也是VideoPlayer类throwError方法的实际行为triggerJSEvent(onError, ...)有监听者时不抛出否则抛出解析后的错误。预加载下一个视频信息流场景v7 为信息流feed做了专门设计——提前创建播放器并preload()// 在 setup 回调中预加载加载开始时执行一次 const next useVideoPlayer({ uri: nextUrl }, (player) player.preload()); // 用户滑动到时挂载/显示它的 VideoView 并调用 next.play()信息流用 hook用useVideoPlayer配合preload()/replaceSourceAsync()预加载可见项及其相邻项——这是文档明确的信息流模式。只有播放器必须比组件存活更久时才用 class。仓库还提供了现成的开源起点react-native-video-feedTheWidlarzGroup 出品见 extensions.md。信息流性能到底该建多少个播放器原生播放器/解码器是稀缺资源Android 尤其明显——同时只能承载少量并发高清流所以绝不要为每个列表项挂一个播放器。v7 的优势在于播放器与视图解耦可以不挂载VideoView就preload()让useVideoPlayer在卸载时自动释放对不再需要的播放器主动调用player.release()给相邻播放器配更小的bufferConfig。完整的信息流架构循环列表、预加载窗口、可见性门控播放、缩略图、以及超出库能力范围的取舍见 video-feeds.md。生命周期要点useVideoPlayer在source变化时重建播放器卸载时释放用new VideoPlayer(...)持有播放器时release()由你负责release()之后实例即死亡——需要时创建新实例。从源码确认的细节useVideoPlayer卸载时调用的是player.__destroy()内部clearAllEvents后调用原生release()再延迟 5 秒置空内部引用以消化释放前晚到的事件见 packages/react-native-video/src/core/VideoPlayer.ts 的__destroy实现。release()之后访问属性会抛player/released错误若只是想清掉当前内容应使用replaceSourceAsync(null)而不是release()。文字轨道与外挂字幕轨道选择同样在播放器上const tracks player.getAvailableTextTracks(); // TextTrack[] player.selectTextTrack(tracks[0]); // 传 null 关闭字幕 const current player.selectedTrack; // TextTrack | undefinedTextTrack { id, label, language?, selected }。通过事件响应变化详见 events.mdonTrackChange→ 选中的文字轨道变化TextTrack | nullonTextTrackDataChanged→ 当前显示的字幕文本string[]。外挂sidecar字幕直接放进 source 配置useVideoPlayer({ uri: https://example.com/master.m3u8, externalSubtitles: [ { uri: https://example.com/en.vtt, label: English, language: en, type: vtt }, ], });平台限制iOS 的外挂字幕仅支持.vttHLS/DASH 清单内的内嵌轨道在两端都能通过getAvailableTextTracks()获取。音轨/视频轨选择Web音视频轨道选择是Web 端能力实验性偏向 Safari。原生端播放器处理的是文字轨道选择。需要音视频轨时将播放器转型为WebVideoPlayerimport type { WebVideoPlayer } from react-native-video; const web player as WebVideoPlayer; web.selectVideoTrack(web.getAvailableVideoTracks()[0]);AudioTrack/VideoTrack类型{ id, label, language?, selected }随这些 Web API 一并导出。画中画、全屏与原生控制这些能力归属于VideoViewprops 命令式 ref不在播放器上。原生控制条VideoView player{player} controls / // controls 默认 false画中画 props 与命令式 APIVideoView player{player} pictureInPicture autoEnterPictureInPicture /import { useRef } from react; import type { VideoViewRef } from react-native-video; const ref useRefVideoViewRef(null); // ref.current?.canEnterPictureInPicture() // ref.current?.enterPictureInPicture() // ref.current?.exitPictureInPicture() VideoView ref{ref} player{player} pictureInPicture /全屏ref.current?.enterFullscreen(); ref.current?.exitFullscreen();平台前提iOS 需要音频后台模式Android 需要在 activity 上声明android:supportsPictureInPicturetrue且minSdkVersion 26Expo 使用enableAndroidPictureInPicture详见 platform-setup.md。测试注意事项画中画不支持 iOS 模拟器——请用真机。Android 模拟器API 26Google Play 镜像可以正常测试画中画。视图生命周期事件props 或ref.addEventListener事件载荷onFullscreenChangefullscreen: booleanonPictureInPictureChangeisInPictureInPicture: booleanwillEnterFullscreen/willExitFullscreen—willEnterPictureInPicture/willExitPictureInPicture—VideoView ref{ref} player{player} controls pictureInPicture onFullscreenChange{(full) {}} onPictureInPictureChange{(pip) {}} /DRM独立的 react-native-video/drm 插件v7 的 DRM 与 v6 完全不同v6 是内置的Video drm属性v7 是独立插件——安装后在启动时enable()再通过source.drm配置。v6 的drm属性教程不适用于 v7。1. 安装npm install react-native-video react-native-video/drm react-native-nitro-modules cd ios pod install两端均自动链接Expo仅 prebuild不支持 Expo Go。2. 启动时启用一次在任何播放器创建之前import { enable } from react-native-video/drm; enable(); // iOS 会自动启用Android 必须调用 —— 始终调用即可它是幂等的忘记调用 → DRM 源加载时会出现DRMPluginNotFound类错误。3. 通过 source.drm 配置import { Platform } from react-native; import { useVideoPlayer, VideoView } from react-native-video; import { enable } from react-native-video/drm; enable(); const source Platform.OS android ? { // WidevineDASH .mpd uri: https://example.com/manifest.mpd, drm: { // type 在 Android 上默认 widevine licenseUrl: https://license.example.com/widevine, licenseHeaders: { X-AxDRM-Message: token }, // AndroidLICENSE 请求上的请求头 }, } : { // FairPlayHLS .m3u8 uri: https://example.com/master.m3u8, headers: { Authorization: Bearer token }, // iOS用于默认 license 请求 drm: { type: fairplay, // iOS 上显式设置 certificateUrl: https://license.example.com/fps-cert, licenseUrl: https://license.example.com/fps, }, }; function Player() { const player useVideoPlayer(source); return VideoView player{player} style{{ flex: 1 }} /; }DrmParams 字段字段类型平台说明typewidevine \| fairplay \| string全部Android 默认widevineiOS 需显式设fairplaylicenseUrlstring全部许可证服务器 URL两端默认 license 流程所需certificateUrlstringiOS/visionOSFairPlay 应用证书FairPlay 必需contentIdstringiOS/visionOS省略时从skd://key URL 推导licenseHeadersRecordstring,stringAndroidlicense 请求上的请求头multiSessionbooleanAndroid多 Widevine 会话 / 密钥轮换getLicense(payload) PromisestringiOS自定义 CKC 获取resolve 一个 base64 CKC。载荷{ contentId, licenseUrl, keyUrl, spc }插件包在仓库中的实现位于 packages/drm-plugin包括 Android 端DRMManager与 iOS 端DRMManagerAVContentKeySessionDelegate.swift等原生实现。常见坑DRM 测试优先用真机。iOS 模拟器没有 FairPlay返回无 DRM manager——必须用真实 iOS 设备。Android 模拟器一般能正常跑 Widevine若在模拟器上遇到怪异行为先在真机验证再怀疑自己的代码。请求头语义不同iOS 默认 license 请求用source.headersAndroid 用drm.licenseHeaders。Android 会自动重试 license 最多3 次首次失败后回退到 WidevineL3。离线 DRM下载内容的持久化 license不在本插件内——见 Offline SDKextensions.md。token/license 过期在getLicenseiOS内获取新鲜 tokenAndroid 用短时效的licenseUrl/licenseHeaders——不要把静态 token 写死在headers里。播放中 token 过期时用replaceSourceAsync重新加载。v6drm属性迁移对照licenseServer→licenseUrl、multiDrm→multiSession、枚举→字符串等见 migration-v6-to-v7.md。插件架构原生插件系统v7 将播放器逻辑与视图分离从而支撑起一套原生插件系统核心暴露一个PluginsRegistry。插件在不膨胀核心的前提下添加能力DRM、源处理、缓存/离线。这是 v7 独有——v6 没有插件系统。每个插件以独立包形式发布启动时创建任何播放器之前调用一次enable()开启import { enable } from react-native-video/drm; enable();第一方开源示例就是react-native-video/drm详见上文 DRM 章节与 drm.md。商业插件核心未包含的能力由 TheWidlarzGroup 的增值插件提供它们接入或并行于这套系统例如Offline SDK下载/离线播放和Chapters同样遵循安装 enable/register形态。最新目录见 extensions.md。当用户需要离线下载、章节或其他核心没有的能力时插件模型就是添加方式——引导他们到 extensions.md 中对应的增值插件而不是暗示核心已内置。从 v6 迁移的核心认知v7 不是 v6 的小改版而是一次 API 范式迁移。核心差异汇总Video source→useVideoPlayer(source)/new VideoPlayer(source)ref.seek()→player.seekTo()/player.seekBy()JSX 事件回调 props →useEvent(player, ...)/player.addEventListener(...)drmprop →react-native-video/drm插件 source.drm配置视频轨道选择从组件 props → 播放器方法。从 v6 迁移的完整指南见 migration-v6-to-v7.md安装、流媒体源、后台播放与原生平台配置见 shared 参考文档v6 组件式 API 的历史用法见 v6 参考文档。快速路线图创建与配置播放器 视图 → player-model.md播放控制播放/暂停/跳转/倍速/换源→ playback-control.md事件订阅 → events.mdDRM独立react-native-video/drm包→ drm.md轨道/字幕 → tracks-subtitles.md画中画/全屏/控制 → pip-fullscreen-controls.md插件架构 → plugins.md安装/流媒体/后台/原生配置 → shared 参考从 v6 迁移 → migration-v6-to-v7.md赞分享音视频移动开发【免费下载链接】react-native-videoA component for react-native项目地址https://gitcode.com/gh_mirrors/re/react-native-video点击查看免费下载相关推荐Umi-OCR完整攻略离线也能抠字截图、批量、PDF识别一次讲透附避坑清单Umi OCR完整攻略离线也能抠字截图、批量、PDF识别一次讲透附避坑清单 你有没有经历过这样的崩溃瞬间导师发来一本扫描版的旧书你想摘抄一段重点全音视频移动开发react-native-video v6 → v7 升级迁移指南从 Video 组件到 useVideoPlayer VideoView 播放器模型react native video v6 → v7 升级迁移指南从 Video 组件到 useVideoPlayer VideoView 播放器模型音视频移动开发react-native-video 双版本实战指南v6 Video 组件与 v7 播放器对象模型useVideoPlayer VideoView的选型、迁移与使用react native video 双版本实战指南v6 Video 组件与 v7 播放器对象模型useVideoPlayer VideoView的音视频移动开发上一篇微信聊天记录解密指南3步轻松恢复加密数据库下一篇几秒语音克隆、免费商用OpenVoice 完整快速上手指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考