@tuya-oh/react-native-video
v5.2.1-rc-0.2.42
Published
A <Video /> element for react-native
Keywords
Readme
模板版本:v0.2.2
This project is based on react-native-video
[!TIP] Gitee 地址
安装与使用
请到三方库的 Releases 发布地址查看配套的版本信息:@tuya-oh/react-native-video Releases 。对于未发布到npm的旧版本,请参考安装指南安装tgz包。
进入到工程目录并输入以下命令:
npm
npm install @tuya-oh/react-native-videoyarn
yarn add @tuya-oh/react-native-video下面的代码展示了这个库的基本使用场景:
[!WARNING] 使用时 import 的库名不变。
import React, { useState, useRef } from "react"; import { View, ScrollView, StyleSheet, Text, TextInput } from "react-native"; import RNCVideo from "react-native-video"; function RNCVideoDemo() { const [muted, setMuted] = useState(true); const [paused, setPaused] = useState(false); const [repeat, setRepeat] = useState(true); const [controls, setControls] = useState(false); const [disableFocus, setDisableFocus] = useState(false); const [uri, setUri] = useState( "https://res.vmallres.com//uomcdn/CN/cms/202210/C75C7E20060F3E909F2998E13C3ABC03.mp4" ); const [txt, setTxt] = useState("empty"); const [resizeMode, setResizeMode] = useState("none"); const [posterResizeMode, setPosterResizeMode] = useState("cover"); const [seekSec, setSeekSec] = useState(5000); const [onVideoLoad, setOnVideoLoad] = useState("onVideoLoad"); const [onVideoLoadStart, setOnVideoLoadStart] = useState("onVideoLoadStart"); const [onVideoProgress, setOnVideoProgress] = useState("onVideoProgress"); const videoRef = React.useRef<typeof RNCVideo>(); const toggleMuted = () => { setMuted((prevMuted) => !prevMuted); }; const toggleControls = () => { setControls((prevControls) => !prevControls); }; const togglePaused = () => { setPaused((prevPaused) => !prevPaused); }; const toggleRepeat = () => { setRepeat((prevRepeat) => !prevRepeat); }; const toggleDisableFocus = () => { setDisableFocus((prevDisableFocus) => !prevDisableFocus); }; const firstVideo = () => { setUri((prevRepeat) => "https://vjs.zencdn.net/v/oceans.mp4"); }; const secondVideo = () => { // setUri((prevRepeat) => 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4'); setUri( (prevRepeat) => "https://res.vmallres.com//uomcdn/CN/cms/202210/C75C7E20060F3E909F2998E13C3ABC03.mp4" ); }; const changeResizeMode = (resizeMode) => { setResizeMode((prevResizeMode) => resizeMode); }; return ( <ScrollView style={styles.container}> <View style={styles.container}> <Text style={styles.title}>网络视频demo</Text> <Text style={styles.labelB}>{onVideoLoad}</Text> <Text style={styles.label}>{onVideoLoadStart}</Text> <Text style={styles.labelB}>{onVideoProgress}</Text> <Text style={styles.title}>update source </Text> <View style={{ flexDirection: "row", height: 40, padding: 0, }} > <Text style={{ backgroundColor: "blue", flex: 0.25 }} onPress={() => { setUri( "https://res.vmallres.com//uomcdn/CN/cms/202210/C75C7E20060F3E909F2998E13C3ABC03.mp4" ); setPosterResizeMode("stretch"); }} > 切换net:vmallres </Text> <Text style={{ backgroundColor: "red", flex: 0.25 }} onPress={() => { setUri("https://vjs.zencdn.net/v/oceans.mp4"); setPosterResizeMode("contain"); }} > 切换net:oceans </Text> </View> <Text style={styles.title}>set resizeMode </Text> <View style={{ flexDirection: "row", height: 40, padding: 0, }} > <Text style={{ backgroundColor: "blue", flex: 0.25 }} onPress={() => { setResizeMode("none"); }} > none </Text> <Text style={{ backgroundColor: "red", flex: 0.25 }} onPress={() => { setResizeMode("contain"); }} > contain </Text> <Text style={{ backgroundColor: "yellow", flex: 0.25 }} onPress={() => { setResizeMode("stretch"); }} > stretch </Text> <Text style={{ backgroundColor: "green", flex: 0.25 }} onPress={() => { setResizeMode("cover"); }} > cover </Text> </View> <View style={{ flexDirection: "row", flexWrap: "wrap", padding: 0, }} > <Text style={styles.title}>操作 </Text> <TextInput style={styles.prop_input} placeholder="input seek sec number:" multiline={false} maxLength={30} keyboardType="numeric" onChangeText={(text) => { const newText = text.replace(/[^\d]+/, ""); setSeekSec(Number(newText)); }} autoFocus={false} /> </View> <View style={{ flexDirection: "row", flexWrap: "wrap", padding: 0, }} > <Text style={styles.button_b} onPress={() => { videoRef.current?.seek(seekSec); }} > seek:{seekSec.toString()} </Text> <Text style={styles.button_b} onPress={() => { togglePaused(); }} > paused:{paused.toString()} </Text> <Text style={styles.button_b} onPress={() => { toggleMuted(); }} > muted:{muted.toString()} </Text> <Text style={styles.button_b} onPress={() => { toggleControls() }} >controls:{controls.toString()}</Text> <Text style={styles.button_b} onPress={() => { toggleRepeat(); }} > repeat:{repeat.toString()} </Text> <Text style={styles.button_b} onPress={() => { toggleDisableFocus(); }} > disableFocus:{disableFocus.toString()} </Text> <Text style={styles.button_b}> ReSizeMode:{resizeMode.toString()} </Text> </View> <RNCVideo style={styles.video} ref={videoRef} source={{ uri: uri, isNetwork: true }} paused={paused} muted={muted} resizeMode={resizeMode} controls={controls} repeat={repeat} volume={1} disableFocus={disableFocus} poster={ "https://res.vmallres.com/pimages/uomcdn/CN/pms/202304/sbom/4002010007801/group/800_800_9B1356F1330EADDCB20D35D2AE1F46E0.jpg" } posterResizeMode={posterResizeMode} onLoad={(e) => { setOnVideoLoad( "onVideoLoad currentTime =" + e.currentPosition + "s duration =" + e.duration + "s width =" + e.naturalSize.width + " orientation =" + e.naturalSize.orientation ); setOnVideoError("onVideoError error = ok"); }} onLoadStart={(e) => { setOnVideoLoadStart( "onVideoLoadStart isNetwork =" + e.isNetwork + " type=" + e.type + " uri=" + e.uri ); }} onProgress={(e) => { setOnVideoProgress( "onVideoProgress currentTime =" + e.currentTime + " playableDuration=" + e.playableDuration + " seekableDuration=" + e.seekableDuration ); }} /> </View> </ScrollView> ); } const styles = StyleSheet.create({ video: { width: "100%", height: 260, }, container: { width: "100%", height: "100%", backgroundColor: "#f8f8f8", }, title: { color: "white", width: "30%", // hack height: 30, // hack }, label: { color: "gray", width: "100%", // hack minHeight: 20, }, labelB: { color: "gray", width: "100%", // hack minHeight: 40, }, button: { width: 160, height: 36, backgroundColor: "hsl(190, 50%, 70%)", paddingHorizontal: 16, paddingVertical: 8, borderRadius: 8, }, buttonText: { width: "100%", height: "100%", fontWeight: "bold", }, button_b: { paddingHorizontal: 8, paddingVertical: 6, borderRadius: 4, backgroundColor: "oldlace", alignSelf: "flex-start", marginHorizontal: "1%", marginBottom: 6, minWidth: "25%", minHeight: 20, textAlign: "center", }, prop_input: { width: 160, height: 40, borderWidth: 1, backgroundColor: "white", color: "black", borderRadius: 8, }, }); export default RNCVideoDemo;Link
目前 HarmonyOS 暂不支持 AutoLink,所以 Link 步骤需要手动配置。
首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程
harmony1.在工程根目录的
oh-package.json5添加 overrides 字段{ ... "overrides": { "@rnoh/react-native-openharmony" : "./react_native_openharmony" } }2.引入原生端代码
目前有两种方法:
- 通过 har 包引入(在 IDE 完善相关功能后该方法会被遗弃,目前首选此方法);
- 直接链接源码。
方法一:通过 har 包引入(推荐)
[!TIP] har 包位于三方库安装路径的
harmony文件夹下。打开
entry/oh-package.json5,添加以下依赖"dependencies": { ... "@rnoh/react-native-openharmony": "file:../react_native_openharmony", "@tuya-oh/react-native-video": "file:../../node_modules/@tuya-oh/react-native-video/harmony/rn_video.har" }点击右上角的
sync按钮或者在终端执行:
cd entry ohpm install方法二:直接链接源码
[!TIP] 如需使用直接链接源码,请参考直接链接源码说明
3.配置 CMakeLists 和引入 RNCVideoPackage
打开
entry/src/main/cpp/CMakeLists.txt,添加:project(rnapp) cmake_minimum_required(VERSION 3.4.1) set(CMAKE_SKIP_BUILD_RPATH TRUE) set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules") + set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp") set(LOG_VERBOSITY_LEVEL 1) set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments") set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie") set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use add_compile_definitions(WITH_HITRACE_SYSTRACE) add_subdirectory("${RNOH_CPP_DIR}" ./rn) # RNOH_BEGIN: manual_package_linking_1 add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package) + add_subdirectory("${OH_MODULES}/@tuya-oh/react-native-video/src/main/cpp" ./video) # RNOH_BEGIN: manual_package_linking_1 file(GLOB GENERATED_CPP_FILES "./generated/*.cpp") add_library(rnoh_app SHARED ${GENERATED_CPP_FILES} "./PackageProvider.cpp" "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp" ) target_link_libraries(rnoh_app PUBLIC rnoh) # RNOH_BEGIN: manual_package_linking_2 target_link_libraries(rnoh_app PUBLIC rnoh_sample_package) + target_link_libraries(rnoh_app PUBLIC rnoh_video) # RNOH_END: manual_package_linking_2打开
entry/src/main/cpp/PackageProvider.cpp,添加:#include "RNOH/PackageProvider.h" #include "generated/RNOHGeneratedPackage.h" #include "SamplePackage.h" + #include "RNCVideoPackage.h" using namespace rnoh; std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) { return { std::make_shared<RNOHGeneratedPackage>(ctx), std::make_shared<SamplePackage>(ctx), + std::make_shared<RNCVideoPackage>(ctx) }; }4.在 ArkTs 侧引入 RNCVideo 组件
找到 function buildCustomComponent(),一般位于
entry/src/main/ets/pages/index.ets或entry/src/main/ets/rn/LoadBundle.ets,添加:... + import { RNCVideo, RNC_VIDEO_TYPE } from "@tuya-oh/react-native-video" @Builder function buildCustomRNComponent(ctx: ComponentBuilderContext) { ... + if (ctx.componentName === RNC_VIDEO_TYPE) { + RNCVideo({ + ctx: ctx.rnComponentContext, + tag: ctx.tag + }) + } ... } ...[!TIP] 本库使用了混合方案,需要添加组件名。
在
entry/src/main/ets/pages/index.ets或entry/src/main/ets/rn/LoadBundle.ets找到常量arkTsComponentNames在其数组里添加组件名const arkTsComponentNames: Array<string> = [ ... + RNC_VIDEO_TYPE ];5.在 ArkTs 侧引入 RNCVideoPackage
打开
entry/src/main/ets/RNPackagesFactory.ts,添加:... + import { RNCVideoPackage } from '@tuya-oh/react-native-video/ts'; export function createRNPackages(ctx: RNPackageContext): RNPackage[] { return [ new SamplePackage(ctx), + new RNCVideoPackage(ctx) ]; }6.运行
点击右上角的
sync按钮或者在终端执行:
cd entry ohpm install然后编译、运行即可。
兼容性
要使用此库,需要使用正确的 React-Native 和 RNOH 版本。另外,还需要使用配套的 DevEco Studio 和 手机 ROM。
请到三方库相应的 Releases 发布地址查看 Release 配套的版本信息:@tuya-oh/ty-react-native-video Releases
权限与要求
由于此库涉及后台播放,需要配置后台运行权限,权限需配置在entry/src/main目录下module.json5文件中。具体权限配置见文档:程序访问控制。
此库后台播放功能需要添加权限:ohos.permission.KEEP_BACKGROUND_RUNNING,还需配置后台播放模式:
"abilities": [ { "backgroundModes": ["audioPlayback"] } ]属性
[!TIP] "Platform"列表示该属性在原三方库上支持的平台。
[!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。
| Name | Descriptio | Type | Required | Platform | HarmonyOS Support | | -------------------------------------- | ------------------------------------------------------------ | :------ | -------- | -------------------------------------------------------- | ----------------- | |
source| Sets the media source. You can pass an asset loaded via require or an object with a uri. | object | Yes | All | yes | |muted| Controls whether the audio is muted.false (default) - Don't mute audio | boolean | No | All | yes | |paused| Controls whether the media is paused.false (default) - Don't pause the media | boolean | No | All | yes | |repeat| Determine whether to repeat the video when the end is reached.false (default) - Don't repeat the video | boolean | No | All | yes | |resizeMode| Determines how to resize the video when the frame doesn't match the raw video dimensions."none" (default) - Don't apply resize | string | No | Android ExoPlayer, Android MediaPlayer, iOS, Windows UWP | yes | |volume| Adjust the volume.1.0 (default) - Play at full volume | number | No | All | yes | |poster| An image to display while the video is loadingValue: string with a URL for the poster, e.g. "https://baconmockup.com/300/200/" | string | No | All | yes | |posterResizeMode| Determines how to resize the poster image when the frame doesn't match the raw video dimensions.."contain" (default)- Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding). | string | No | All | yes | |allowsExternalPlayback| Indicates whether the player allows switching to external playback mode such as AirPlay or HDMI. | boolean | No | iOS | No | |audioOnly| Indicates whether the player should only play the audio track and instead of displaying the video track, show the poster instead. | boolean | No | All | No | |automaticallyWaitsToMinimizeStalling| A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling. For clients linked against iOS 10.0 and later | boolean | No | iOS | No | |bufferConfig| Adjust the buffer settings. This prop takes an object with one or more of the properties listed below. | object | No | Android | No | |controls| Determines whether to show player controls. | boolean | No | All | Yes | |ignoreSilentSwitch| Controls the iOS silent switch behavior | string | No | iOS | No | |playInBackground| Determine whether the media should continue playing while the app is in the background. This allows customers to continue listening to the audio. | boolean | No | All | Yes | |playWhenInactive| Determine whether the media should continue playing when notifications or the Control Center are in front of the video. | boolean | No | iOS | No | |progressUpdateInterval| Delay in milliseconds between onProgress events in milliseconds. | number | No | iOS | No | |rate| Speed at which the media should play. | number | No | All | Yes | |selectedAudioTrack| Configure which audio track, if any, is played. | object | No | All | No | |selectedTextTrack| Configure which text track (caption or subtitle), if any, is shown. | object | No | All | No | |stereoPan| Adjust the balance of the left and right audio channels. Any value between –1.0 and 1.0 is accepted. | number | No | Android | No | |textTracks| Load one or more "sidecar" text tracks. This takes an array of objects representing each track. | object | No | All | No | |useTextureView| Controls whether to output to a TextureView or SurfaceView. | boolean | No | Android | No |事件回调
[!TIP] "Platform"列表示该属性在原三方库上支持的平台。
[!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。
| Name | Description | Type | Required | Platform | HarmonyOS Support | | :------------------------------ | ------------------------------------------------------------ | :------- | -------- | -------- | ----------------- | |
onLoad| Callback function that is called when the media is loaded and ready to play. | function | No | All | yes | |onLoadStart| Callback function that is called when the media starts loading. | function | No | All | yes | |onProgress| Callback function that is called every progressUpdateInterval seconds with info about which position the media is currently playing. | function | No | All | yes | |onAudioBecomingNoisy| Callback function that is called when the audio is about to become 'noisy' due to a change in audio outputs. Typically this is called when audio output is being switched from an external source like headphones back to the internal speaker. It's a good idea to pause the media when this happens so the speaker doesn't start blasting sound. | function | No | All | No | |onFullscreenPlayerWillPresent| Callback function that is called when the player is about to enter fullscreen mode. | function | No | All | No | |onFullscreenPlayerDidPresent| Callback function that is called when the player has entered fullscreen mode. | function | No | All | Yes | |onFullscreenPlayerWillDismiss| Callback function that is called when the player is about to exit fullscreen mode. | function | No | All | No | |onFullscreenPlayerDidDismiss| Callback function that is called when the player has exited fullscreen mode. | function | No | All | Yes | |onTimedMetadata| Callback function that is called when timed metadata becomes available | function | No | All | No |静态方法
[!TIP] "Platform"列表示该属性在原三方库上支持的平台。
[!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。
| Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------- | ------------------------------------------------------------ | -------- | -------- | -------- | ----------------- | |
seek()| Seek to the specified position represented by seconds. seconds is a float value. | function | No | All | yes | |dismissFullscreenPlayer()| Take the player out of fullscreen mode. | function | No | All | yes | |presentFullscreenPlayer()| Put the player in fullscreen mode. | function | No | All | yes |遗留问题
其他
开源协议
本项目基于 The MIT License (MIT) ,请自由地享受和参与开源。
