ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-video — 视频播放组件

ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-video — 视频播放组件 欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net开发环境声明本文基于 React Native 0.72.90 版本进行开发适配 一、开篇引言视频播放是现代移动应用中不可或缺的功能之一。react-native-video 是一个功能强大的视频播放组件支持网络视频、本地视频、直播流等多种播放场景。本文将带你深入了解如何在 HarmonyOS 平台上集成和使用 react-native-video实现专业的视频播放功能。1.1 你将学到什么✅ react-native-video 的核心概念与工作原理✅ HarmonyOS 平台的完整集成流程✅ 视频播放控制与状态管理✅ Video API 的深度解析✅ 实际应用场景的最佳实践1.2 适用人群正在进行 React Native 鸿蒙化迁移的开发者需要实现视频播放功能的应用开发者对多媒体应用开发感兴趣的技术爱好者1.3 为什么选择 react-native-video特点说明功能强大支持多种视频格式、直播流、画中画等跨平台一致在 iOS、Android、HarmonyOS 上表现一致高度可定制支持自定义控件、样式、事件回调等社区活跃大量使用案例和社区支持 二、库概览2.1 基本信息项目内容库名称react-native-video版本信息6.13.2 / 6.14.0官方仓库https://github.com/react-native-video/react-native-video鸿蒙仓库https://gitcode.com/openharmony-sig/rntpc_react-native-video开源协议MIT2.2 版本兼容性三方库版本支持RN版本是否支持Autolink6.14.00.77No6.13.20.72Yes 6.13.1deprecated0.72No2.3 核心能力矩阵能力项描述HarmonyOS 支持网络视频播放在线视频流播放✅ 完全支持播放控制播放、暂停、跳转✅ 完全支持音量控制静音、音量调节✅ 完全支持画中画PiP 模式✅ 完全支持全屏播放全屏模式✅ 完全支持播放控件原生控件✅ 完全支持缩放模式contain/cover/stretch✅ 完全支持海报图视频封面✅ 完全支持字幕支持外挂字幕❌ 不支持后台播放应用后台继续播放❌ 不支持2.4 典型应用场景场景描述示例视频点播在线视频播放 电影播放器直播观看直播流播放 直播应用短视频社交媒体短视频 抖音类应用教育视频在线课程播放 学习应用广告视频视频广告播放 广告投放⚡ 三、快速开始3.1 环境要求依赖项版本要求React Native0.72.x / 0.77.xRNOH (鸿蒙框架)0.72.90 / 0.77.18HarmonyOS SDK6.0.0.47 (API 20)DevEco Studio5.0.3 / 6.0Node.js16.18.0 / 18.x3.2 一键安装创建鸿蒙项目的过程不再进行描述不懂得看这篇https://blog.csdn.net/u011178696/article/details/151932277npminstallreact-native-ohos/react-native-video6.13.2-rc.13.3 验证安装# 检查 package.jsontypepackage.json|findstr video# 预期输出# react-native-ohos/react-native-video: 6.13.2-rc.13.4 基础使用import Video from react-native-video; Video source{{ uri: https://example.com/video.mp4 }} style{{ width: 300, height: 200 }} controls{true} / 四、HarmonyOS 原生配置4.1 添加依赖打开harmony/entry/oh-package.json5添加依赖dependencies:{rnoh/react-native-openharmony:0.72.90,react-native-ohos/react-native-video:file:../../node_modules/react-native-ohos/react-native-video/harmony/rn_video.har}4.2 同步依赖点击 DevEco Studio 右上角的 sync 按钮或者在终端执行cdharmony/entry ohpminstall4.3 配置 CMakeLists.txt打开entry/src/main/cpp/CMakeLists.txt添加set(OH_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules) add_subdirectory(${OH_MODULES}/react-native-ohos/react-native-video/src/main/cpp ./video) target_link_libraries(rnoh_app PUBLIC rnoh_video)4.4 配置 PackageProvider.cpp打开entry/src/main/cpp/PackageProvider.cpp添加#includeRNCVideoPackage.hstd::vectorstd::shared_ptrPackagePackageProvider::getPackages(Package::Context ctx){return{std::make_sharedRNCVideoPackage(ctx)};}4.5 配置 ArkTS 组件打开entry/src/main/ets/pages/index.ets或entry/src/main/ets/rn/LoadBundle.ets添加import{RNCVideo,RNC_VIDEO_TYPE}fromreact-native-ohos/react-native-videoBuilderfunctionbuildCustomRNComponent(ctx:ComponentBuilderContext){if(ctx.componentNameRNC_VIDEO_TYPE){RNCVideo({ctx:ctx.rnComponentContext,tag:ctx.tag})}}constarkTsComponentNames:Arraystring[RNC_VIDEO_TYPE];4.6 配置 RNPackagesFactory.ts打开entry/src/main/ets/RNPackagesFactory.ts添加import{RNCVideoPackage}fromreact-native-ohos/react-native-video/ts;exportfunctioncreateRNPackages(ctx:RNPackageContext):RNPackage[]{return[newRNCVideoPackage(ctx)];} 五、API 详解5.1 核心属性属性名说明类型默认值HarmonyOS 支持source视频源object-✅paused是否暂停booleanfalse✅muted是否静音booleanfalse✅volume音量 (0.0-1.0)number1.0✅rate播放速率number1.0✅repeat是否循环播放booleanfalse✅controls是否显示原生控件booleanfalse✅resizeMode缩放模式string‘none’✅调用示例// 网络视频源 Video source{{ uri: https://example.com/video.mp4, isNetwork: true }} style{{ width: 100%, height: 200 }} / // 播放控制 const [paused, setPaused] useState(false); Video source{{ uri: videoUrl }} paused{paused} onPress{() setPaused(!paused)} / // 音量和速率控制 Video source{{ uri: videoUrl }} volume{0.5} rate{1.5} muted{false} / // 循环播放 Video source{{ uri: videoUrl }} repeat{true} / // 显示原生控件 Video source{{ uri: videoUrl }} controls{true} /5.2 视频源配置属性名说明类型默认值uri视频 URLstring-isNetwork是否为网络资源booleanfalseheadersHTTP 请求头object-调用示例// 网络视频 Video source{{ uri: https://example.com/video.mp4, isNetwork: true }} / // 带请求头的视频 Video source{{ uri: https://example.com/protected.mp4, headers: { Authorization: Bearer token } }} / // HLS 直播流 Video source{{ uri: https://example.com/live.m3u8, isNetwork: true }} /5.3 缩放模式模式值说明none不缩放保持原始尺寸contain等比缩放完整显示在容器内stretch拉伸填充容器可能变形cover等比缩放填满容器可能裁剪调用示例Video source{{ uri: videoUrl }} resizeModecontain /5.4 海报图属性属性名说明类型默认值poster海报图 URLstring-posterResizeMode海报图缩放模式string‘contain’调用示例Video source{{ uri: videoUrl }} posterhttps://example.com/poster.jpg posterResizeModecover /5.5 画中画属性属性名说明类型默认值pictureInPicture是否启用画中画booleanfalseenterPictureInPictureOnLeave离开应用时自动进入画中画booleanfalse调用示例const [pipMode, setPipMode] useState(false); Video source{{ uri: videoUrl }} pictureInPicture{pipMode} enterPictureInPictureOnLeave{true} onPictureInPictureStatusChanged{(e) { console.log(PiP status:, e.isActive); setPipMode(e.isActive); }} / 六、事件回调6.1 播放状态事件事件名说明HarmonyOS 支持onLoad视频加载完成✅onLoadStart开始加载✅onProgress播放进度更新✅onEnd播放结束✅onError播放错误✅onBuffer缓冲状态变化✅onPlaybackStateChanged播放状态变化✅onReadyForDisplay首帧准备好显示✅调用示例Video source{{ uri: videoUrl }} onLoad{(e) { console.log(视频时长:, e.duration); console.log(视频尺寸:, e.naturalSize); }} onProgress{(e) { console.log(当前时间:, e.currentTime); console.log(可播放时长:, e.playableDuration); }} onEnd{() { console.log(播放结束); }} onError{(e) { console.log(播放错误:, e.error); }} onBuffer{(e) { console.log(缓冲中:, e.isBuffering); }} onPlaybackStateChanged{(state) { console.log(播放状态:, state); }} /6.2 全屏事件事件名说明HarmonyOS 支持onFullscreenPlayerWillPresent即将进入全屏✅onFullscreenPlayerDidPresent已进入全屏✅onFullscreenPlayerWillDismiss即将退出全屏✅onFullscreenPlayerDidDismiss已退出全屏✅调用示例Video source{{ uri: videoUrl }} onFullscreenPlayerDidPresent{() { console.log(已进入全屏); }} onFullscreenPlayerDidDismiss{() { console.log(已退出全屏); }} / 七、静态方法7.1 播放控制方法方法名说明HarmonyOS 支持seek(seconds)跳转到指定位置✅pause()暂停播放✅resume()恢复播放✅setVolume(value)设置音量✅getCurrentPosition()获取当前播放位置✅调用示例import Video, { VideoRef } from react-native-video; const videoRef useRefVideoRef(null); // 跳转到指定位置 videoRef.current?.seek(30); // 暂停播放 videoRef.current?.pause(); // 恢复播放 videoRef.current?.resume(); // 设置音量 videoRef.current?.setVolume(0.5); // 获取当前播放位置 const position await videoRef.current?.getCurrentPosition(); Video ref{videoRef} source{{ uri: videoUrl }} /7.2 全屏控制方法方法名说明HarmonyOS 支持presentFullscreenPlayer()进入全屏✅dismissFullscreenPlayer()退出全屏✅setFullScreen(fullscreen)切换全屏状态✅调用示例const videoRef useRefVideoRef(null); // 进入全屏 videoRef.current?.presentFullscreenPlayer(); // 退出全屏 videoRef.current?.dismissFullscreenPlayer(); // 切换全屏状态 videoRef.current?.setFullScreen(true);7.3 画中画方法方法名说明HarmonyOS 支持enterPictureInPicture()进入画中画✅exitPictureInPicture()退出画中画✅调用示例const videoRef useRefVideoRef(null); // 进入画中画 videoRef.current?.enterPictureInPicture(); // 退出画中画 videoRef.current?.exitPictureInPicture();⚠️ 八、注意事项8.1 HarmonyOS 限制限制项说明本地视频暂时只支持在线 URL 资源字幕支持不支持外挂字幕后台播放不支持应用后台继续播放音频轨道部分音频轨道功能未实现8.2 常见问题问题1视频无法播放// ❌ 错误未设置 isNetwork Video source{{ uri: https://example.com/video.mp4 }} / // ✅ 正确网络视频需要设置 isNetwork Video source{{ uri: https://example.com/video.mp4, isNetwork: true }} /问题2视频加载后黑屏// ❌ 错误未设置视频尺寸 Video source{{ uri: videoUrl }} / // ✅ 正确设置视频尺寸 Video source{{ uri: videoUrl }} style{{ width: 100%, height: 200 }} /问题3全屏功能不生效// ❌ 错误未配置原生组件 Video source{{ uri: videoUrl }} / // ✅ 正确需要在 ArkTS 侧注册 RNC_VIDEO_TYPE // 参考 4.3 配置 ArkTS 组件8.3 最佳实践视频源配置网络视频务必设置isNetwork: true错误处理始终添加onError回调处理播放错误内存管理组件卸载时暂停播放释放资源加载状态使用onLoad和onBuffer显示加载状态const [isLoading, setIsLoading] useState(true); const [error, setError] useStatestring | null(null); Video source{{ uri: videoUrl, isNetwork: true }} onLoad{() setIsLoading(false)} onError{(e) setError(e.error)} onBuffer{(e) setIsLoading(e.isBuffering)} / useEffect(() { return () { videoRef.current?.pause(); }; }, []); 九、完整示例代码精美视频播放器示例import React, { useState, useRef } from react; import { View, Text, StyleSheet, ScrollView, SafeAreaView, TouchableOpacity, StatusBar, } from react-native; import Video, { VideoRef } from react-native-video; export default function App() { const videoRef useRefVideoRef(null); const [paused, setPaused] useState(false); const [muted, setMuted] useState(true); const [repeat, setRepeat] useState(true); const [controls, setControls] useState(false); const [resizeMode, setResizeMode] useStatenone | contain | stretch | cover(none); const [videoInfo, setVideoInfo] useState({ duration: 0, currentTime: 0, isBuffering: false, }); const handleLoad (data: any) { setVideoInfo((prev) ({ ...prev, duration: data.duration })); }; const handleProgress (data: any) { setVideoInfo((prev) ({ ...prev, currentTime: data.currentTime })); }; const handleBuffer (data: any) { setVideoInfo((prev) ({ ...prev, isBuffering: data.isBuffering })); }; const handleSeek (seconds: number) { videoRef.current?.seek(seconds); }; const formatTime (seconds: number) { const mins Math.floor(seconds / 60); const secs Math.floor(seconds % 60); return ${mins.toString().padStart(2, 0)}:${secs.toString().padStart(2, 0)}; }; const progress videoInfo.duration 0 ? (videoInfo.currentTime / videoInfo.duration) * 100 : 0; return ( SafeAreaView style{styles.container} StatusBar barStylelight-content / View style{styles.header} Text style{styles.headerTitle}动漫播放器/Text Text style{styles.headerSubtitle}Sintel - 开源动漫短片/Text /View View style{styles.videoWrapper} Video ref{videoRef} source{{ uri: https://media.w3.org/2010/05/sintel/trailer.mp4, isNetwork: true }} style{styles.video} paused{paused} muted{muted} repeat{repeat} controls{controls} resizeMode{resizeMode} posterhttps://media.w3.org/2010/05/sintel/poster.png posterResizeModecover onLoad{handleLoad} onProgress{handleProgress} onBuffer{handleBuffer} onEnd{() setPaused(true)} onError{(e) console.log(视频播放错误:, e)} / {videoInfo.isBuffering ( View style{styles.loadingOverlay} View style{styles.loadingSpinner} / Text style{styles.loadingText}加载中.../Text /View )} /View View style{styles.progressSection} Text style{styles.timeText}{formatTime(videoInfo.currentTime)}/Text View style{styles.progressBar} View style{[styles.progressFill, { width: ${progress}% }]} / /View Text style{styles.timeText}{formatTime(videoInfo.duration)}/Text /View View style{styles.controlsSection} TouchableOpacity style{styles.controlButton} onPress{() handleSeek(Math.max(0, videoInfo.currentTime - 10))} Text style{styles.controlIcon}⏪/Text Text style{styles.controlLabel}后退10s/Text /TouchableOpacity TouchableOpacity style{[styles.playButton, paused styles.playButtonActive]} onPress{() setPaused(!paused)} Text style{styles.playIcon}{paused ? ▶ : ⏸}/Text /TouchableOpacity TouchableOpacity style{styles.controlButton} onPress{() handleSeek(Math.min(videoInfo.duration, videoInfo.currentTime 10))} Text style{styles.controlIcon}⏩/Text Text style{styles.controlLabel}前进10s/Text /TouchableOpacity /View ScrollView style{styles.settingsSection} showsVerticalScrollIndicator{false} View style{styles.settingCard} Text style{styles.settingTitle}播放设置/Text View style{styles.settingRow} TouchableOpacity style{[styles.settingButton, muted styles.settingButtonActive]} onPress{() setMuted(!muted)} Text style{styles.settingIcon}{muted ? : }/Text Text style{styles.settingText}{muted ? 已静音 : 声音}/Text /TouchableOpacity TouchableOpacity style{[styles.settingButton, repeat styles.settingButtonActive]} onPress{() setRepeat(!repeat)} Text style{styles.settingIcon}{repeat ? : }/Text Text style{styles.settingText}{repeat ? 循环中 : 单次}/Text /TouchableOpacity TouchableOpacity style{[styles.settingButton, !controls styles.settingButtonActive]} onPress{() setControls(!controls)} Text style{styles.settingIcon}{controls ? : ‍}/Text Text style{styles.settingText}{controls ? 控件开 : 控件关}/Text /TouchableOpacity /View /View View style{styles.settingCard} Text style{styles.settingTitle}画面模式/Text View style{styles.settingRow} TouchableOpacity style{[styles.modeButton, resizeMode contain styles.modeButtonActive]} onPress{() setResizeMode(contain)} Text style{styles.modeIcon}/Text Text style{styles.modeText}适应/Text /TouchableOpacity TouchableOpacity style{[styles.modeButton, resizeMode cover styles.modeButtonActive]} onPress{() setResizeMode(cover)} Text style{styles.modeIcon}/Text Text style{styles.modeText}填充/Text /TouchableOpacity /View /View View style{styles.infoCard} Text style{styles.infoTitle}影片信息/Text Text style{styles.infoContent} 《Sintel》是一部由Blender基金会制作的开源动画短片讲述了一个女孩寻找她心爱的小龙的故事。这部影片采用Blender软件制作画面精美情节感人。 /Text View style{styles.infoTags} View style{styles.tag} Text style{styles.tagText}动漫/Text /View View style{styles.tag} Text style{styles.tagText}开源/Text /View View style{styles.tag} Text style{styles.tagText}短片/Text /View /View /View /ScrollView /SafeAreaView ); } const styles StyleSheet.create({ container: { flex: 1, backgroundColor: #0f0f1a, }, header: { padding: 20, paddingBottom: 15, backgroundColor: #1a1a2e, }, headerTitle: { fontSize: 28, fontWeight: bold, color: #fff, textAlign: center, }, headerSubtitle: { fontSize: 14, color: #888, textAlign: center, marginTop: 5, }, videoWrapper: { width: 100%, height: 260, backgroundColor: #000, position: relative, }, video: { width: 100%, height: 260, }, loadingOverlay: { position: absolute, top: 0, left: 0, right: 0, bottom: 0, justifyContent: center, alignItems: center, backgroundColor: rgba(0, 0, 0, 0.7), }, loadingSpinner: { width: 40, height: 40, borderRadius: 20, borderWidth: 3, borderColor: #666, borderTopColor: #00d4ff, marginBottom: 15, }, loadingText: { color: #fff, fontSize: 14, }, progressSection: { flexDirection: row, alignItems: center, paddingHorizontal: 20, paddingVertical: 15, backgroundColor: #1a1a2e, }, timeText: { color: #888, fontSize: 12, width: 45, textAlign: center, }, progressBar: { flex: 1, height: 4, backgroundColor: #333, borderRadius: 2, marginHorizontal: 10, overflow: hidden, }, progressFill: { height: 100%, backgroundColor: #00d4ff, borderRadius: 2, }, controlsSection: { flexDirection: row, alignItems: center, justifyContent: center, paddingVertical: 20, backgroundColor: #1a1a2e, gap: 30, }, controlButton: { alignItems: center, }, controlIcon: { fontSize: 24, marginBottom: 5, }, controlLabel: { color: #888, fontSize: 11, }, playButton: { width: 70, height: 70, borderRadius: 35, backgroundColor: #333, justifyContent: center, alignItems: center, borderWidth: 3, borderColor: #444, }, playButtonActive: { backgroundColor: #00d4ff, borderColor: #00a8cc, }, playIcon: { fontSize: 28, color: #fff, marginLeft: 4, }, settingsSection: { flex: 1, padding: 15, }, settingCard: { backgroundColor: #1a1a2e, borderRadius: 16, padding: 16, marginBottom: 12, }, settingTitle: { color: #fff, fontSize: 16, fontWeight: 600, marginBottom: 15, }, settingRow: { flexDirection: row, justifyContent: space-around, }, settingButton: { alignItems: center, padding: 12, borderRadius: 12, backgroundColor: #252540, minWidth: 80, }, settingButtonActive: { backgroundColor: #00d4ff, }, settingIcon: { fontSize: 22, marginBottom: 5, }, settingText: { color: #aaa, fontSize: 12, }, modeButton: { alignItems: center, padding: 15, borderRadius: 12, backgroundColor: #252540, minWidth: 100, }, modeButtonActive: { backgroundColor: #00d4ff, }, modeIcon: { fontSize: 26, marginBottom: 5, }, modeText: { color: #aaa, fontSize: 13, }, infoCard: { backgroundColor: #1a1a2e, borderRadius: 16, padding: 16, marginBottom: 20, }, infoTitle: { color: #fff, fontSize: 16, fontWeight: 600, marginBottom: 12, }, infoContent: { color: #888, fontSize: 13, lineHeight: 20, marginBottom: 15, }, infoTags: { flexDirection: row, gap: 8, }, tag: { backgroundColor: #252540, paddingHorizontal: 12, paddingVertical: 6, borderRadius: 20, }, tagText: { color: #00d4ff, fontSize: 12, }, }); 十、相关资源官方文档鸿蒙适配仓库React Native 官方文档 十一、总结本文详细介绍了react-native-video在 HarmonyOS 平台的使用方法。通过 Video 组件你可以轻松实现专业的视频播放功能支持播放控制、画中画、全屏播放等特性。核心要点✅ 支持网络视频播放✅ 支持播放控制播放、暂停、跳转✅ 支持画中画和全屏模式✅ 支持音量、速率、缩放模式调节✅ 支持原生播放控件适用场景视频点播应用直播观看应用短视频应用教育视频应用希望本文能帮助你在 HarmonyOS 项目中顺利集成视频播放组件