Fabric (New Architecture) codegen support (#3487)

* feat: implemented codegenConfig on package.json

* chore: moved directory location of Fabric component

* fix: typefix FabricExample

* chore: pod instaslled FabricExample iOS app

* feat: implemented codegen config on package.json

* feat: implemented codegen of specs/VideoNativeComponent

* chore: removed not using type Filter

* feat: removed unnecessary export on codegen tyepes

* Revert "feat: removed unnecessary export on codegen tyepes"

This reverts commit fc243b0ac5c565eda4886cd865c32ba4e812d7ff.

* refactor: fixed types on Video component and modified types with codegen types

* feat: modified codegenNativeComponent naming (RCTVideo)

* feat: pod installed example basic app

* feat: bump up react-native dev dependency version to 0.73.2 for supporting codegen array event params

* feat: support array param types on event callback function codegen types

* chore: pod installed ios basic example

* feat: modified source prop as optional type

* feat: add original src/VideoComponent.ts again

* Revert "feat: add original src/VideoComponent.ts again"

This reverts commit d63ac94e5330f7c7fb50374f65f8f3f4e0a225d7.

* feat: add original src/VideoComponent.ts again with original file name

* feat: git rm src/specs/VideoNativeComponent.ts

* feat: git mv VideoNativeComponent.ts

* feat: git mv src/specs/VideoNativeComponent.ts

* feat: git mv src/VideoNativeComponent.ts src/specs/VideoNativeComponent.ts

* feat: implemented array type handling on android JAVA

* feat: updated iOS requestHeaders parsing native

* feat: use safeGetArray on android, removed not using import too

* feat: temporary commit - reusing enum types for remaining docs types

* feat: implemented mixed type of SelectedTrack.value for JS layer
This commit is contained in:
yungblud 2024-03-07 19:35:17 +09:00 committed by GitHub
parent 16f3cdbd9a
commit b33e6df496
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1287 additions and 1181 deletions

View File

@ -18,7 +18,6 @@ import com.brentvatne.common.toolbox.ReactBridgeUtils;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
@ -128,18 +127,19 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
if (drm != null && drm.hasKey(PROP_DRM_TYPE)) {
String drmType = ReactBridgeUtils.safeGetString(drm, PROP_DRM_TYPE);
String drmLicenseServer = ReactBridgeUtils.safeGetString(drm, PROP_DRM_LICENSESERVER);
ReadableMap drmHeaders = ReactBridgeUtils.safeGetMap(drm, PROP_DRM_HEADERS);
ReadableArray drmHeadersArray = ReactBridgeUtils.safeGetArray(drm, PROP_DRM_HEADERS);
if (drmType != null && drmLicenseServer != null && Util.getDrmUuid(drmType) != null) {
UUID drmUUID = Util.getDrmUuid(drmType);
videoView.setDrmType(drmUUID);
videoView.setDrmLicenseUrl(drmLicenseServer);
if (drmHeaders != null) {
if (drmHeadersArray != null) {
ArrayList<String> drmKeyRequestPropertiesList = new ArrayList<>();
ReadableMapKeySetIterator itr = drmHeaders.keySetIterator();
while (itr.hasNextKey()) {
String key = itr.nextKey();
for (int i = 0; i < drmHeadersArray.size(); i++) {
ReadableMap current = drmHeadersArray.getMap(i);
String key = current.hasKey("key") ? current.getString("key") : null;
String value = current.hasKey("value") ? current.getString("value") : null;
drmKeyRequestPropertiesList.add(key);
drmKeyRequestPropertiesList.add(drmHeaders.getString(key));
drmKeyRequestPropertiesList.add(value);
}
videoView.setDrmLicenseHeader(drmKeyRequestPropertiesList.toArray(new String[0]));
}
@ -157,7 +157,20 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
int cropEndMs = ReactBridgeUtils.safeGetInt(src, PROP_SRC_CROP_END, -1);
String extension = ReactBridgeUtils.safeGetString(src, PROP_SRC_TYPE, null);
Map<String, String> headers = src.hasKey(PROP_SRC_HEADERS) ? ReactBridgeUtils.toStringMap(src.getMap(PROP_SRC_HEADERS)) : new HashMap<>();
Map<String, String> headers = new HashMap<>();
ReadableArray propSrcHeadersArray = ReactBridgeUtils.safeGetArray(src, PROP_SRC_HEADERS);
if (propSrcHeadersArray != null) {
if (propSrcHeadersArray.size() > 0) {
for (int i = 0; i < propSrcHeadersArray.size(); i++) {
ReadableMap current = propSrcHeadersArray.getMap(i);
String key = current.hasKey("key") ? current.getString("key") : null;
String value = current.hasKey("value") ? current.getString("value") : null;
if (key != null && value != null) {
headers.put(key, value);
}
}
}
}
if (TextUtils.isEmpty(uriString)) {
videoView.clearSrc();

View File

@ -571,6 +571,7 @@
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION,
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -597,6 +598,10 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
};
@ -638,6 +643,10 @@
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION,
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@ -661,6 +670,10 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ import {
View,
} from 'react-native';
import Video, {FilterType, VideoRef} from 'react-native-video';
import Video, {FilterType, VideoRef, ResizeMode, IgnoreSilentSwitchType, MixWithOthersType} from 'react-native-video';
const filterTypes = [
FilterType.NONE,
@ -32,7 +32,26 @@ const filterTypes = [
FilterType.SEPIA,
];
class VideoPlayer extends Component {
type SkinType = 'custom' | 'native' | 'embed'
type State = {
rate: number,
volume: number,
muted: boolean,
resizeMode: ResizeMode,
duration: number,
currentTime: number,
controls: boolean,
paused: boolean,
skin: SkinType,
ignoreSilentSwitch: IgnoreSilentSwitchType,
mixWithOthers: MixWithOthersType,
isBuffering: boolean,
filter: FilterType,
filterEnabled: boolean,
}
class VideoPlayer extends Component<{}, State> {
controlRef: React.RefObject<TouchableOpacity>;
videoRef: React.RefObject<VideoRef>;
constructor(props: any) {
@ -44,18 +63,18 @@ class VideoPlayer extends Component {
this.controlRef = createRef();
this.videoRef = createRef();
}
state = {
state: State = {
rate: 1,
volume: 1,
muted: false,
resizeMode: 'contain',
resizeMode: ResizeMode.CONTAIN,
duration: 0.0,
currentTime: 0.0,
controls: false,
paused: true,
skin: 'custom',
ignoreSilentSwitch: null,
mixWithOthers: null,
ignoreSilentSwitch: IgnoreSilentSwitchType.IGNORE,
mixWithOthers: MixWithOthersType.DUCK,
isBuffering: false,
filter: FilterType.NONE,
filterEnabled: true,
@ -110,7 +129,7 @@ class VideoPlayer extends Component {
});
}
renderSkinControl(skin) {
renderSkinControl(skin: 'custom' | 'native' | 'embed') {
const isSelected = this.state.skin == skin;
const selectControls = skin == 'native' || skin == 'embed';
return (
@ -151,7 +170,7 @@ class VideoPlayer extends Component {
);
}
renderResizeModeControl(resizeMode: string) {
renderResizeModeControl(resizeMode: ResizeMode) {
const isSelected = this.state.resizeMode == resizeMode;
return (
@ -189,7 +208,7 @@ class VideoPlayer extends Component {
);
}
renderIgnoreSilentSwitchControl(ignoreSilentSwitch: string) {
renderIgnoreSilentSwitchControl(ignoreSilentSwitch: IgnoreSilentSwitchType) {
const isSelected = this.state.ignoreSilentSwitch == ignoreSilentSwitch;
return (
@ -208,7 +227,7 @@ class VideoPlayer extends Component {
);
}
renderMixWithOthersControl(mixWithOthers: string) {
renderMixWithOthersControl(mixWithOthers: MixWithOthersType) {
const isSelected = this.state.mixWithOthers == mixWithOthers;
return (
@ -302,21 +321,21 @@ class VideoPlayer extends Component {
</View>
<View style={styles.resizeModeControl}>
{this.renderResizeModeControl('cover')}
{this.renderResizeModeControl('contain')}
{this.renderResizeModeControl('stretch')}
{this.renderResizeModeControl(ResizeMode.COVER)}
{this.renderResizeModeControl(ResizeMode.CONTAIN)}
{this.renderResizeModeControl(ResizeMode.STRETCH)}
</View>
</View>
<View style={styles.generalControls}>
{Platform.OS === 'ios' ? (
<>
<View style={styles.ignoreSilentSwitchControl}>
{this.renderIgnoreSilentSwitchControl('ignore')}
{this.renderIgnoreSilentSwitchControl('obey')}
{this.renderIgnoreSilentSwitchControl(IgnoreSilentSwitchType.IGNORE)}
{this.renderIgnoreSilentSwitchControl(IgnoreSilentSwitchType.OBEY)}
</View>
<View style={styles.mixWithOthersControl}>
{this.renderMixWithOthersControl('mix')}
{this.renderMixWithOthersControl('duck')}
{this.renderMixWithOthersControl(MixWithOthersType.MIX)}
{this.renderMixWithOthersControl(MixWithOthersType.DUCK)}
</View>
</>
) : null}
@ -410,21 +429,21 @@ class VideoPlayer extends Component {
</View>
<View style={styles.resizeModeControl}>
{this.renderResizeModeControl('cover')}
{this.renderResizeModeControl('contain')}
{this.renderResizeModeControl('stretch')}
{this.renderResizeModeControl(ResizeMode.COVER)}
{this.renderResizeModeControl(ResizeMode.CONTAIN)}
{this.renderResizeModeControl(ResizeMode.STRETCH)}
</View>
</View>
<View style={styles.generalControls}>
{Platform.OS === 'ios' ? (
<>
<View style={styles.ignoreSilentSwitchControl}>
{this.renderIgnoreSilentSwitchControl('ignore')}
{this.renderIgnoreSilentSwitchControl('obey')}
{this.renderIgnoreSilentSwitchControl(IgnoreSilentSwitchType.IGNORE)}
{this.renderIgnoreSilentSwitchControl(IgnoreSilentSwitchType.OBEY)}
</View>
<View style={styles.mixWithOthersControl}>
{this.renderMixWithOthersControl('mix')}
{this.renderMixWithOthersControl('duck')}
{this.renderMixWithOthersControl(MixWithOthersType.MIX)}
{this.renderMixWithOthersControl(MixWithOthersType.DUCK)}
</View>
</>
) : null}

View File

@ -4,9 +4,10 @@
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
"paths": {
"react-native-video": ["../../Video.js"]
"react-native-video": ["../../src/index.ts"],
"react": [ "./node_modules/@types/react" ]
},
/* Completeness */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
}
}

View File

@ -889,10 +889,10 @@ PODS:
- React-Mapbuffer (0.73.2):
- glog
- React-debug
- react-native-video (6.0.0-beta.3):
- react-native-video (6.0.0-beta.4):
- React-Core
- react-native-video/Video (= 6.0.0-beta.3)
- react-native-video/Video (6.0.0-beta.3):
- react-native-video/Video (= 6.0.0-beta.4)
- react-native-video/Video (6.0.0-beta.4):
- PromisesSwift
- React-Core
- React-nativeconfig (0.73.2)
@ -1235,11 +1235,11 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost: d3f49c53809116a5d38da093a8aa78bf551aed09
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953
FBLazyVector: fbc4957d9aa695250b55d879c1d86f79d7e69ab4
FBReactNativeSpec: 86de768f89901ef6ed3207cd686362189d64ac88
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
hermes-engine: b361c9ef5ef3cda53f66e195599b47e1f84ffa35
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4
@ -1265,7 +1265,7 @@ SPEC CHECKSUMS:
React-jsinspector: 03644c063fc3621c9a4e8bf263a8150909129618
React-logger: 66b168e2b2bee57bd8ce9e69f739d805732a5570
React-Mapbuffer: f40e0ea0df8161075390332dfcb0496442c09371
react-native-video: 60ecad11b7179ec0e2012dea643775b4fca9b224
react-native-video: 6078b448c21630b0a2937436527ad54fd2a1fdd8
React-nativeconfig: 4dda3cbbdd1c4ce38450bb10b34b3e54120e4a91
React-NativeModulesApple: d25a530c61e94fb317a0124b1cde1c459e4a47d3
React-perflogger: 29efe63b7ef5fbaaa50ef6eaa92482f98a24b97e

View File

@ -591,6 +591,15 @@
" ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
);
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
LD_RUNPATH_SEARCH_PATHS = (
@ -611,11 +620,7 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = (
"$(inherited)",
" ",
"-Wl -ld_classic ",
);
OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
@ -678,6 +683,15 @@
" ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
" ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
);
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
LD_RUNPATH_SEARCH_PATHS = (
@ -697,11 +711,7 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = (
"$(inherited)",
" ",
"-Wl -ld_classic ",
);
OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;

View File

@ -40,7 +40,17 @@ struct VideoSource {
self.isNetwork = json["isNetwork"] as? Bool ?? false
self.isAsset = json["isAsset"] as? Bool ?? false
self.shouldCache = json["shouldCache"] as? Bool ?? false
self.requestHeaders = json["requestHeaders"] as? [String: Any]
if let requestHeaders = json["requestHeaders"] as? [[String: Any]] {
var _requestHeaders: [String: Any] = [:]
for requestHeader in requestHeaders {
if let key = requestHeader["key"] as? String, let value = requestHeader["value"] {
_requestHeaders[key] = value
}
}
self.requestHeaders = _requestHeaders
} else {
self.requestHeaders = nil
}
self.startPosition = json["startPosition"] as? Int64
self.cropStart = json["cropStart"] as? Int64
self.cropEnd = json["cropEnd"] as? Int64

View File

@ -28,7 +28,7 @@
"jest": "^29.7.0",
"prettier": "^2.4.1",
"react": "18.2.0",
"react-native": "0.72.5",
"react-native": "0.73.2",
"react-native-windows": "^0.61.0-0",
"release-it": "^16.2.1",
"typescript": "5.1.6"
@ -47,7 +47,8 @@
"release": "release-it",
"test": "echo no test available",
"check-ios": "scripts/swift-format.sh && scripts/swift-lint.sh && scripts/clang-format.sh",
"check-android": "scripts/kotlin-lint.sh"
"check-android": "scripts/kotlin-lint.sh",
"codegen": "node ./node_modules/react-native/scripts/generate-codegen-artifacts.js --path ./ ./output"
},
"files": [
"android",
@ -63,5 +64,10 @@
"!**/*.tsbuildinfo",
"!docs",
"!examples"
]
],
"codegenConfig": {
"name": "RNCVideo",
"type": "components",
"jsSrcsDir": "./src/specs"
}
}

View File

@ -9,34 +9,37 @@ import React, {
} from 'react';
import {View, StyleSheet, Image, Platform} from 'react-native';
import NativeVideoComponent, {
type OnAudioFocusChangedData,
type OnAudioTracksData,
type OnBandwidthUpdateData,
type OnBufferData,
type OnExternalPlaybackChangeData,
type OnGetLicenseData,
type OnLoadData,
type OnLoadStartData,
type OnPictureInPictureStatusChangedData,
type OnPlaybackStateChangedData,
type OnProgressData,
type OnReceiveAdEventData,
type OnSeekData,
type OnTextTrackDataChangedData,
type OnTextTracksData,
type OnTimedMetadataData,
type OnVideoAspectRatioData,
type OnVideoErrorData,
type OnVideoTracksData,
type VideoComponentType,
} from './VideoNativeComponent';
type VideoSrc,
} from './specs/VideoNativeComponent';
import type {StyleProp, ImageStyle, NativeSyntheticEvent} from 'react-native';
import {getReactTag, resolveAssetSourceForVideo} from './utils';
import {VideoManager} from './VideoNativeComponent';
import type {
OnAudioFocusChangedData,
OnAudioTracksData,
OnBandwidthUpdateData,
OnBufferData,
OnExternalPlaybackChangeData,
OnGetLicenseData,
OnLoadData,
OnLoadStartData,
OnPictureInPictureStatusChangedData,
OnPlaybackStateChangedData,
OnProgressData,
OnReceiveAdEventData,
OnSeekData,
OnTextTrackDataChangedData,
OnTextTracksData,
OnTimedMetadataData,
OnVideoAspectRatioData,
OnVideoErrorData,
OnVideoTracksData,
ReactVideoProps,
} from './types';
import {
generateHeaderForNative,
getReactTag,
resolveAssetSourceForVideo,
} from './utils';
import {VideoManager} from './specs/VideoNativeComponent';
import type {ReactVideoProps} from './types/video';
export type VideoSaveData = {
uri: string;
@ -120,11 +123,10 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
[posterResizeMode],
);
const src = useMemo(() => {
const src = useMemo<VideoSrc | undefined>(() => {
if (!source) {
return undefined;
}
const resolvedSource = resolveAssetSourceForVideo(source);
let uri = resolvedSource.uri || '';
if (uri && uri.match(/^\//)) {
@ -149,7 +151,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
type: resolvedSource.type || '',
mainVer: resolvedSource.mainVer || 0,
patchVer: resolvedSource.patchVer || 0,
requestHeaders: resolvedSource.headers || {},
requestHeaders: generateHeaderForNative(resolvedSource.headers),
startPosition: resolvedSource.startPosition ?? -1,
cropStart: resolvedSource.cropStart || 0,
cropEnd: resolvedSource.cropEnd,
@ -168,7 +170,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
return {
type: drm.type,
licenseServer: drm.licenseServer,
headers: drm.headers,
headers: generateHeaderForNative(drm.headers),
contentId: drm.contentId,
certificateUrl: drm.certificateUrl,
base64Certificate: drm.base64Certificate,
@ -180,10 +182,13 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
if (!selectedTextTrack) {
return;
}
const value = selectedTextTrack.value
? `${selectedTextTrack.value}`
: undefined;
return {
type: selectedTextTrack?.type,
value: selectedTextTrack?.value,
value,
};
}, [selectedTextTrack]);
@ -191,10 +196,13 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
if (!selectedAudioTrack) {
return;
}
const value = selectedAudioTrack.value
? `${selectedAudioTrack.value}`
: undefined;
return {
type: selectedAudioTrack?.type,
value: selectedAudioTrack?.value,
value,
};
}, [selectedAudioTrack]);

View File

@ -1,418 +0,0 @@
import type {
HostComponent,
NativeSyntheticEvent,
ViewProps,
} from 'react-native';
import {NativeModules, requireNativeComponent} from 'react-native';
import type ResizeMode from './types/ResizeMode';
import type FilterType from './types/FilterType';
import type Orientation from './types/Orientation';
import type {
AdEvent,
EnumValues,
OnTextTrackDataChangedData,
OnTextTracksTypeData,
} from './types';
// -------- There are types for native component (future codegen) --------
// if you are looking for types for react component, see src/types/video.ts
type Headers = Record<string, string>;
type VideoSrc = Readonly<{
uri?: string;
isNetwork?: boolean;
isAsset?: boolean;
shouldCache?: boolean;
type?: string;
mainVer?: number;
patchVer?: number;
requestHeaders?: Headers;
startPosition?: number;
cropStart?: number;
cropEnd?: number;
title?: string;
subtitle?: string;
description?: string;
customImageUri?: string;
}>;
export type Filter =
| 'None'
| 'CIColorInvert'
| 'CIColorMonochrome'
| 'CIColorPosterize'
| 'CIFalseColor'
| 'CIMaximumComponent'
| 'CIMinimumComponent'
| 'CIPhotoEffectChrome'
| 'CIPhotoEffectFade'
| 'CIPhotoEffectInstant'
| 'CIPhotoEffectMono'
| 'CIPhotoEffectNoir'
| 'CIPhotoEffectProcess'
| 'CIPhotoEffectTonal'
| 'CIPhotoEffectTransfer'
| 'CISepiaTone';
export type DRMType = 'widevine' | 'playready' | 'clearkey' | 'fairplay';
type DebugConfig = Readonly<{
enable?: boolean;
thread?: boolean;
}>;
type Drm = Readonly<{
type?: DRMType;
licenseServer?: string;
headers?: Headers;
contentId?: string; // ios
certificateUrl?: string; // ios
base64Certificate?: boolean; // ios default: false
useExternalGetLicense?: boolean; // ios
}>;
type TextTracks = ReadonlyArray<
Readonly<{
title: string;
language: string;
type: string;
uri: string;
}>
>;
type TextTrackType = 'system' | 'disabled' | 'title' | 'language' | 'index';
type SelectedTextTrack = Readonly<{
type: TextTrackType;
value?: string | number;
}>;
type AudioTrackType = 'system' | 'disabled' | 'title' | 'language' | 'index';
type SelectedAudioTrack = Readonly<{
type: AudioTrackType;
value?: string | number;
}>;
export type Seek = Readonly<{
time: number;
tolerance?: number;
}>;
type BufferConfig = Readonly<{
minBufferMs?: number;
maxBufferMs?: number;
bufferForPlaybackMs?: number;
bufferForPlaybackAfterRebufferMs?: number;
maxHeapAllocationPercent?: number;
minBackBufferMemoryReservePercent?: number;
minBufferMemoryReservePercent?: number;
}>;
type SelectedVideoTrack = Readonly<{
type: 'auto' | 'disabled' | 'resolution' | 'index';
value?: number;
}>;
type SubtitleStyle = Readonly<{
fontSize?: number;
paddingTop?: number;
paddingBottom?: number;
paddingLeft?: number;
paddingRight?: number;
}>;
export type OnLoadData = Readonly<{
currentTime: number;
duration: number;
naturalSize: Readonly<{
width: number;
height: number;
orientation: Orientation;
}>;
}> &
OnAudioTracksData &
OnTextTracksData;
export type OnLoadStartData = Readonly<{
isNetwork: boolean;
type: string;
uri: string;
}>;
export type OnVideoAspectRatioData = Readonly<{
width: number;
height: number;
}>;
export type OnBufferData = Readonly<{isBuffering: boolean}>;
export type OnProgressData = Readonly<{
currentTime: number;
playableDuration: number;
seekableDuration: number;
}>;
export type OnBandwidthUpdateData = Readonly<{
bitrate: number;
width?: number;
height?: number;
trackId?: number;
}>;
export type OnSeekData = Readonly<{
currentTime: number;
seekTime: number;
}>;
export type OnPlaybackStateChangedData = Readonly<{
isPlaying: boolean;
}>;
export type OnTimedMetadataData = Readonly<{
metadata: ReadonlyArray<
Readonly<{
value?: string;
identifier: string;
}>
>;
}>;
export type OnAudioTracksData = Readonly<{
audioTracks: ReadonlyArray<
Readonly<{
index: number;
title?: string;
language?: string;
bitrate?: number;
type?: string;
selected?: boolean;
}>
>;
}>;
export type OnTextTracksData = Readonly<{
textTracks: ReadonlyArray<
Readonly<{
index: number;
title?: string;
language?: string;
/**
* iOS only supports VTT, Android supports all 3
*/
type?: OnTextTracksTypeData;
selected?: boolean;
}>
>;
}>;
export type OnVideoTracksData = Readonly<{
videoTracks: ReadonlyArray<
Readonly<{
trackId: number;
codecs?: string;
width?: number;
height?: number;
bitrate?: number;
selected?: boolean;
}>
>;
}>;
export type OnPlaybackData = Readonly<{
playbackRate: number;
}>;
export type onVolumeChangeData = Readonly<{
volume: number;
}>;
export type OnExternalPlaybackChangeData = Readonly<{
isExternalPlaybackActive: boolean;
}>;
export type OnGetLicenseData = Readonly<{
licenseUrl: string;
contentId: string;
spcBase64: string;
}>;
export type OnPictureInPictureStatusChangedData = Readonly<{
isActive: boolean;
}>;
export type OnReceiveAdEventData = Readonly<{
data?: Record<string, string>;
event: AdEvent;
}>;
export type OnVideoErrorData = Readonly<{
error: OnVideoErrorDataDetails;
target?: number; // ios
}>;
export type OnVideoErrorDataDetails = Readonly<{
errorString?: string; // android
errorException?: string; // android
errorStackTrace?: string; // android
errorCode?: string; // android
error?: string; // ios
code?: number; // ios
localizedDescription?: string; // ios
localizedFailureReason?: string; // ios
localizedRecoverySuggestion?: string; // ios
domain?: string; // ios
}>;
export type OnAudioFocusChangedData = Readonly<{
hasAudioFocus: boolean;
}>;
export interface VideoNativeProps extends ViewProps {
src?: VideoSrc;
drm?: Drm;
adTagUrl?: string;
allowsExternalPlayback?: boolean; // ios, true
maxBitRate?: number;
resizeMode?: EnumValues<ResizeMode>;
repeat?: boolean;
automaticallyWaitsToMinimizeStalling?: boolean;
textTracks?: TextTracks;
selectedTextTrack?: SelectedTextTrack;
selectedAudioTrack?: SelectedAudioTrack;
paused?: boolean;
muted?: boolean;
controls?: boolean;
filter?: EnumValues<FilterType>;
filterEnabled?: boolean;
volume?: number; // default 1.0
playInBackground?: boolean;
preventsDisplaySleepDuringVideoPlayback?: boolean;
preferredForwardBufferDuration?: number; //ios, 0
playWhenInactive?: boolean; // ios, false
pictureInPicture?: boolean; // ios, false
ignoreSilentSwitch?: 'inherit' | 'ignore' | 'obey'; // ios, 'inherit'
mixWithOthers?: 'inherit' | 'mix' | 'duck'; // ios, 'inherit'
rate?: number;
fullscreen?: boolean; // ios, false
fullscreenAutorotate?: boolean;
fullscreenOrientation?: 'all' | 'landscape' | 'portrait';
progressUpdateInterval?: number;
restoreUserInterfaceForPIPStopCompletionHandler?: boolean;
localSourceEncryptionKeyScheme?: string;
debug?: DebugConfig;
backBufferDurationMs?: number; // Android
bufferConfig?: BufferConfig; // Android
contentStartTime?: number; // Android
currentPlaybackTime?: number; // Android
disableDisconnectError?: boolean; // Android
focusable?: boolean; // Android
hideShutterView?: boolean; // Android
minLoadRetryCount?: number; // Android
reportBandwidth?: boolean; //Android
selectedVideoTrack?: SelectedVideoTrack; // android
subtitleStyle?: SubtitleStyle; // android
shutterColor?: string; // Android
trackId?: string; // Android
useTextureView?: boolean; // Android
useSecureView?: boolean; // Android
onVideoLoad?: (event: NativeSyntheticEvent<OnLoadData>) => void;
onVideoLoadStart?: (event: NativeSyntheticEvent<OnLoadStartData>) => void;
onVideoAspectRatio?: (
event: NativeSyntheticEvent<OnVideoAspectRatioData>,
) => void;
onVideoBuffer?: (event: NativeSyntheticEvent<OnBufferData>) => void;
onVideoError?: (event: NativeSyntheticEvent<OnVideoErrorData>) => void;
onVideoProgress?: (event: NativeSyntheticEvent<OnProgressData>) => void;
onVideoBandwidthUpdate?: (
event: NativeSyntheticEvent<OnBandwidthUpdateData>,
) => void;
onVideoSeek?: (event: NativeSyntheticEvent<OnSeekData>) => void;
onVideoEnd?: (event: NativeSyntheticEvent<Readonly<object>>) => void; // all
onVideoAudioBecomingNoisy?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void;
onVideoFullscreenPlayerWillPresent?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void; // ios, android
onVideoFullscreenPlayerDidPresent?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void; // ios, android
onVideoFullscreenPlayerWillDismiss?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void; // ios, android
onVideoFullscreenPlayerDidDismiss?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void; // ios, android
onReadyForDisplay?: (event: NativeSyntheticEvent<Readonly<object>>) => void;
onPlaybackRateChange?: (event: NativeSyntheticEvent<OnPlaybackData>) => void; // all
onVolumeChange?: (event: NativeSyntheticEvent<onVolumeChangeData>) => void; // android, ios
onVideoExternalPlaybackChange?: (
event: NativeSyntheticEvent<OnExternalPlaybackChangeData>,
) => void;
onGetLicense?: (event: NativeSyntheticEvent<OnGetLicenseData>) => void;
onPictureInPictureStatusChanged?: (
event: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>,
) => void;
onRestoreUserInterfaceForPictureInPictureStop?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void;
onReceiveAdEvent?: (
event: NativeSyntheticEvent<OnReceiveAdEventData>,
) => void;
onVideoPlaybackStateChanged?: (
event: NativeSyntheticEvent<OnPlaybackStateChangedData>,
) => void; // android only
onVideoIdle?: (event: NativeSyntheticEvent<object>) => void; // android only (nowhere in document, so do not use as props. just type declaration)
onAudioFocusChanged?: (
event: NativeSyntheticEvent<OnAudioFocusChangedData>,
) => void; // android only (nowhere in document, so do not use as props. just type declaration)
onTimedMetadata?: (event: NativeSyntheticEvent<OnTimedMetadataData>) => void; // ios, android
onAudioTracks?: (event: NativeSyntheticEvent<OnAudioTracksData>) => void; // android
onTextTracks?: (event: NativeSyntheticEvent<OnTextTracksData>) => void; // android
onTextTrackDataChanged?: (
event: NativeSyntheticEvent<OnTextTrackDataChangedData>,
) => void; // iOS
onVideoTracks?: (event: NativeSyntheticEvent<OnVideoTracksData>) => void; // android
}
export type VideoComponentType = HostComponent<VideoNativeProps>;
export type VideoSaveData = {
uri: string;
};
export interface VideoManagerType {
save: (option: object, reactTag: number) => Promise<VideoSaveData>;
setPlayerPauseState: (paused: boolean, reactTag: number) => Promise<void>;
setLicenseResult: (
result: string,
licenseUrl: string,
reactTag: number,
) => Promise<void>;
setLicenseResultError: (
error: string,
licenseUrl: string,
reactTag: number,
) => Promise<void>;
}
export interface VideoDecoderPropertiesType {
getWidevineLevel: () => Promise<number>;
isCodecSupported: (
mimeType: string,
width: number,
height: number,
) => Promise<'unsupported' | 'hardware' | 'software'>;
isHEVCSupported: () => Promise<'unsupported' | 'hardware' | 'software'>;
}
export const VideoManager = NativeModules.VideoManager as VideoManagerType;
export const VideoDecoderProperties =
NativeModules.VideoDecoderProperties as VideoDecoderPropertiesType;
export default requireNativeComponent<VideoNativeProps>(
'RCTVideo',
) as VideoComponentType;

View File

@ -1,5 +1,5 @@
import Video from './Video';
export {VideoDecoderProperties} from './VideoNativeComponent';
export {VideoDecoderProperties} from './specs/VideoNativeComponent';
export * from './types';
export type {VideoRef} from './Video';
export default Video;

View File

@ -0,0 +1,567 @@
/* eslint-disable @typescript-eslint/ban-types */
import type {HostComponent, ViewProps} from 'react-native';
import {NativeModules} from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
import type {
DirectEventHandler,
Double,
Float,
Int32,
WithDefault,
} from 'react-native/Libraries/Types/CodegenTypes';
// -------- There are types for native component (future codegen) --------
// if you are looking for types for react component, see src/types/video.ts
type Headers = ReadonlyArray<
Readonly<{
key: string;
value: string;
}>
>;
export type VideoSrc = Readonly<{
uri?: string;
isNetwork?: boolean;
isAsset?: boolean;
shouldCache?: boolean;
type?: string;
mainVer?: Int32;
patchVer?: Int32;
requestHeaders?: Headers;
startPosition?: Float;
cropStart?: Float;
cropEnd?: Float;
title?: string;
subtitle?: string;
description?: string;
customImageUri?: string;
}>;
type DRMType = WithDefault<
'widevine' | 'playready' | 'clearkey' | 'fairplay',
'widevine'
>;
type DebugConfig = Readonly<{
enable?: boolean;
thread?: boolean;
}>;
type Drm = Readonly<{
type?: DRMType;
licenseServer?: string;
headers?: Headers;
contentId?: string; // ios
certificateUrl?: string; // ios
base64Certificate?: boolean; // ios default: false
useExternalGetLicense?: boolean; // ios
}>;
type TextTracks = ReadonlyArray<
Readonly<{
title: string;
language: string;
type: string;
uri: string;
}>
>;
type SelectedTextTrackType = WithDefault<
'system' | 'disabled' | 'title' | 'language' | 'index',
'system'
>;
type SelectedAudioTrackType = WithDefault<
'system' | 'disabled' | 'title' | 'language' | 'index',
'system'
>;
type SelectedTextTrack = Readonly<{
type?: SelectedTextTrackType;
value?: string;
}>;
type SelectedAudioTrack = Readonly<{
type?: SelectedAudioTrackType;
value?: string;
}>;
type SelectedVideoTrackType = WithDefault<
'auto' | 'disabled' | 'resolution' | 'index',
'auto'
>;
type SelectedVideoTrack = Readonly<{
type?: SelectedVideoTrackType;
value?: Int32;
}>;
export type Seek = Readonly<{
time: Float;
tolerance?: Float;
}>;
type BufferConfig = Readonly<{
minBufferMs?: Float;
maxBufferMs?: Float;
bufferForPlaybackMs?: Float;
bufferForPlaybackAfterRebufferMs?: Float;
maxHeapAllocationPercent?: Float;
minBackBufferMemoryReservePercent?: Float;
minBufferMemoryReservePercent?: Float;
}>;
type SubtitleStyle = Readonly<{
fontSize?: Float;
paddingTop?: WithDefault<Float, 0>;
paddingBottom?: WithDefault<Float, 0>;
paddingLeft?: WithDefault<Float, 0>;
paddingRight?: WithDefault<Float, 0>;
}>;
export type OnLoadData = Readonly<{
currentTime: Float;
duration: Float;
naturalSize: Readonly<{
width: Float;
height: Float;
orientation: WithDefault<'landscape' | 'portrait', 'landscape'>;
}>;
audioTracks: {
index: Int32;
title?: string;
language?: string;
bitrate?: Float;
type?: string;
selected?: boolean;
}[];
textTracks: {
index: Int32;
title?: string;
language?: string;
/**
* iOS only supports VTT, Android supports all 3
*/
type?: WithDefault<'srt' | 'ttml' | 'vtt', 'srt'>;
selected?: boolean;
}[];
}>;
export type OnLoadStartData = Readonly<{
isNetwork: boolean;
type: string;
uri: string;
}>;
export type OnVideoAspectRatioData = Readonly<{
width: Float;
height: Float;
}>;
export type OnBufferData = Readonly<{isBuffering: boolean}>;
export type OnProgressData = Readonly<{
currentTime: Float;
playableDuration: Float;
seekableDuration: Float;
}>;
export type OnBandwidthUpdateData = Readonly<{
bitrate: Int32;
width?: Float;
height?: Float;
trackId?: Int32;
}>;
export type OnSeekData = Readonly<{
currentTime: Float;
seekTime: Float;
}>;
export type OnPlaybackStateChangedData = Readonly<{
isPlaying: boolean;
}>;
export type OnTimedMetadataData = Readonly<{
metadata: {
value?: string;
identifier: string;
}[];
}>;
export type OnAudioTracksData = Readonly<{
audioTracks: {
index: Int32;
title?: string;
language?: string;
bitrate?: Float;
type?: string;
selected?: boolean;
}[];
}>;
export type OnTextTracksData = Readonly<{
textTracks: {
index: Int32;
title?: string;
language?: string;
/**
* iOS only supports VTT, Android supports all 3
*/
type?: WithDefault<'srt' | 'ttml' | 'vtt', 'srt'>;
selected?: boolean;
}[];
}>;
export type OnTextTrackDataChangedData = Readonly<{
subtitleTracks: string;
}>;
export type OnVideoTracksData = Readonly<{
videoTracks: {
trackId: Int32;
codecs?: string;
width?: Float;
height?: Float;
bitrate?: Float;
selected?: boolean;
}[];
}>;
export type OnPlaybackData = Readonly<{
playbackRate: Float;
}>;
export type OnVolumeChangeData = Readonly<{
volume: Float;
}>;
export type OnExternalPlaybackChangeData = Readonly<{
isExternalPlaybackActive: boolean;
}>;
export type OnGetLicenseData = Readonly<{
licenseUrl: string;
contentId: string;
spcBase64: string;
}>;
export type OnPictureInPictureStatusChangedData = Readonly<{
isActive: boolean;
}>;
export type OnReceiveAdEventData = Readonly<{
data?: {};
event: WithDefault<
/**
* iOS only: Fired the first time each ad break ends. Applications must reenable seeking when this occurs (only used for dynamic ad insertion).
*/ | 'AD_BREAK_ENDED'
/**
* Fires when an ad rule or a VMAP ad break would have played if autoPlayAdBreaks is false.
*/
| 'AD_BREAK_READY'
/**
* iOS only: Fired first time each ad break begins playback. If an ad break is watched subsequent times this will not be fired. Applications must disable seeking when this occurs (only used for dynamic ad insertion).
*/
| 'AD_BREAK_STARTED'
/**
* Android only: Fires when the ad has stalled playback to buffer.
*/
| 'AD_BUFFERING'
/**
* Android only: Fires when the ad is ready to play without buffering, either at the beginning of the ad or after buffering completes.
*/
| 'AD_CAN_PLAY'
/**
* Android only: Fires when an ads list is loaded.
*/
| 'AD_METADATA'
/**
* iOS only: Fired every time the stream switches from advertising or slate to content. This will be fired even when an ad is played a second time or when seeking into an ad (only used for dynamic ad insertion).
*/
| 'AD_PERIOD_ENDED'
/**
* iOS only: Fired every time the stream switches from content to advertising or slate. This will be fired even when an ad is played a second time or when seeking into an ad (only used for dynamic ad insertion).
*/
| 'AD_PERIOD_STARTED'
/**
* Android only: Fires when the ad's current time value changes. The event `data` will be populated with an AdProgressData object.
*/
| 'AD_PROGRESS'
/**
* Fires when the ads manager is done playing all the valid ads in the ads response, or when the response doesn't return any valid ads.
*/
| 'ALL_ADS_COMPLETED'
/**
* Fires when the ad is clicked.
*/
| 'CLICK'
/**
* Fires when the ad completes playing.
*/
| 'COMPLETED'
/**
* Android only: Fires when content should be paused. This usually happens right before an ad is about to cover the content.
*/
| 'CONTENT_PAUSE_REQUESTED'
/**
* Android only: Fires when content should be resumed. This usually happens when an ad finishes or collapses.
*/
| 'CONTENT_RESUME_REQUESTED'
/**
* iOS only: Cuepoints changed for VOD stream (only used for dynamic ad insertion).
*/
| 'CUEPOINTS_CHANGED'
/**
* Android only: Fires when the ad's duration changes.
*/
| 'DURATION_CHANGE'
/**
* Fires when an error is encountered and the ad can't be played.
*/
| 'ERROR'
/**
* Fires when the ad playhead crosses first quartile.
*/
| 'FIRST_QUARTILE'
/**
* Android only: Fires when the impression URL has been pinged.
*/
| 'IMPRESSION'
/**
* Android only: Fires when an ad triggers the interaction callback. Ad interactions contain an interaction ID string in the ad data.
*/
| 'INTERACTION'
/**
* Android only: Fires when the displayed ad changes from linear to nonlinear, or the reverse.
*/
| 'LINEAR_CHANGED'
/**
* Fires when ad data is available.
*/
| 'LOADED'
/**
* Fires when a non-fatal error is encountered. The user need not take any action since the SDK will continue with the same or next ad playback depending on the error situation.
*/
| 'LOG'
/**
* Fires when the ad playhead crosses midpoint.
*/
| 'MIDPOINT'
/**
* Fires when the ad is paused.
*/
| 'PAUSED'
/**
* Fires when the ad is resumed.
*/
| 'RESUMED'
/**
* Android only: Fires when the displayed ads skippable state is changed.
*/
| 'SKIPPABLE_STATE_CHANGED'
/**
* Fires when the ad is skipped by the user.
*/
| 'SKIPPED'
/**
* Fires when the ad starts playing.
*/
| 'STARTED'
/**
* iOS only: Stream request has loaded (only used for dynamic ad insertion).
*/
| 'STREAM_LOADED'
/**
* iOS only: Fires when the ad is tapped.
*/
| 'TAPPED'
/**
* Fires when the ad playhead crosses third quartile.
*/
| 'THIRD_QUARTILE'
/**
* iOS only: An unknown event has fired
*/
| 'UNKNOWN'
/**
* Android only: Fires when the ad is closed by the user.
*/
| 'USER_CLOSE'
/**
* Android only: Fires when the non-clickthrough portion of a video ad is clicked.
*/
| 'VIDEO_CLICKED'
/**
* Android only: Fires when a user clicks a video icon.
*/
| 'VIDEO_ICON_CLICKED'
/**
* Android only: Fires when the ad volume has changed.
*/
| 'VOLUME_CHANGED'
/**
* Android only: Fires when the ad volume has been muted.
*/
| 'VOLUME_MUTED',
'AD_BREAK_ENDED'
>;
}>;
export type OnVideoErrorData = Readonly<{
error: Readonly<{
errorString?: string; // android
errorException?: string; // android
errorStackTrace?: string; // android
errorCode?: string; // android
error?: string; // ios
code?: Int32; // ios
localizedDescription?: string; // ios
localizedFailureReason?: string; // ios
localizedRecoverySuggestion?: string; // ios
domain?: string; // ios
}>;
target?: Int32; // ios
}>;
export type OnAudioFocusChangedData = Readonly<{
hasAudioFocus: boolean;
}>;
export interface VideoNativeProps extends ViewProps {
src?: VideoSrc;
drm?: Drm;
adTagUrl?: string;
allowsExternalPlayback?: boolean; // ios, true
maxBitRate?: Float;
resizeMode?: WithDefault<'none' | 'contain' | 'cover' | 'stretch', 'none'>;
repeat?: boolean;
automaticallyWaitsToMinimizeStalling?: boolean;
textTracks?: TextTracks;
selectedTextTrack?: SelectedTextTrack;
selectedAudioTrack?: SelectedAudioTrack;
selectedVideoTrack?: SelectedVideoTrack; // android
paused?: boolean;
muted?: boolean;
controls?: boolean;
filter?: WithDefault<
| ''
| 'CIColorInvert'
| 'CIColorMonochrome'
| 'CIColorPosterize'
| 'CIFalseColor'
| 'CIMaximumComponent'
| 'CIMinimumComponent'
| 'CIPhotoEffectChrome'
| 'CIPhotoEffectFade'
| 'CIPhotoEffectInstant'
| 'CIPhotoEffectMono'
| 'CIPhotoEffectNoir'
| 'CIPhotoEffectProcess'
| 'CIPhotoEffectTonal'
| 'CIPhotoEffectTransfer'
| 'CISepiaTone',
''
>;
filterEnabled?: boolean;
volume?: Float; // default 1.0
playInBackground?: boolean;
preventsDisplaySleepDuringVideoPlayback?: boolean;
preferredForwardBufferDuration?: Float; //ios, 0
playWhenInactive?: boolean; // ios, false
pictureInPicture?: boolean; // ios, false
ignoreSilentSwitch?: WithDefault<'inherit' | 'ignore' | 'obey', 'inherit'>; // ios, 'inherit'
mixWithOthers?: WithDefault<'inherit' | 'mix' | 'duck', 'inherit'>; // ios, 'inherit'
rate?: Float;
fullscreen?: boolean; // ios, false
fullscreenAutorotate?: boolean;
fullscreenOrientation?: WithDefault<'all' | 'landscape' | 'portrait', 'all'>;
progressUpdateInterval?: Float;
restoreUserInterfaceForPIPStopCompletionHandler?: boolean;
localSourceEncryptionKeyScheme?: string;
debug?: DebugConfig;
backBufferDurationMs?: Int32; // Android
bufferConfig?: BufferConfig; // Android
contentStartTime?: Int32; // Android
currentPlaybackTime?: Double; // Android
disableDisconnectError?: boolean; // Android
focusable?: boolean; // Android
hideShutterView?: boolean; // Android
minLoadRetryCount?: Int32; // Android
reportBandwidth?: boolean; //Android
subtitleStyle?: SubtitleStyle; // android
trackId?: string; // Android
useTextureView?: boolean; // Android
useSecureView?: boolean; // Android
onVideoLoad?: DirectEventHandler<OnLoadData>;
onVideoLoadStart?: DirectEventHandler<OnLoadStartData>;
onVideoAspectRatio?: DirectEventHandler<OnVideoAspectRatioData>;
onVideoBuffer?: DirectEventHandler<OnBufferData>;
onVideoError?: DirectEventHandler<OnVideoErrorData>;
onVideoProgress?: DirectEventHandler<OnProgressData>;
onVideoBandwidthUpdate?: DirectEventHandler<OnBandwidthUpdateData>;
onVideoSeek?: DirectEventHandler<OnSeekData>;
onVideoEnd?: DirectEventHandler<{}>; // all
onVideoAudioBecomingNoisy?: DirectEventHandler<{}>;
onVideoFullscreenPlayerWillPresent?: DirectEventHandler<{}>; // ios, android
onVideoFullscreenPlayerDidPresent?: DirectEventHandler<{}>; // ios, android
onVideoFullscreenPlayerWillDismiss?: DirectEventHandler<{}>; // ios, android
onVideoFullscreenPlayerDidDismiss?: DirectEventHandler<{}>; // ios, android
onReadyForDisplay?: DirectEventHandler<{}>;
onPlaybackRateChange?: DirectEventHandler<OnPlaybackData>; // all
onVolumeChange?: DirectEventHandler<OnVolumeChangeData>; // android, ios
onVideoExternalPlaybackChange?: DirectEventHandler<OnExternalPlaybackChangeData>;
onGetLicense?: DirectEventHandler<OnGetLicenseData>;
onPictureInPictureStatusChanged?: DirectEventHandler<OnPictureInPictureStatusChangedData>;
onRestoreUserInterfaceForPictureInPictureStop?: DirectEventHandler<{}>;
onReceiveAdEvent?: DirectEventHandler<OnReceiveAdEventData>;
onVideoPlaybackStateChanged?: DirectEventHandler<OnPlaybackStateChangedData>; // android only
onVideoIdle?: DirectEventHandler<{}>; // android only (nowhere in document, so do not use as props. just type declaration)
onAudioFocusChanged?: DirectEventHandler<OnAudioFocusChangedData>; // android only (nowhere in document, so do not use as props. just type declaration)
onTimedMetadata?: DirectEventHandler<OnTimedMetadataData>; // ios, android
onAudioTracks?: DirectEventHandler<OnAudioTracksData>; // android
onTextTracks?: DirectEventHandler<OnTextTracksData>; // android
onTextTrackDataChanged?: DirectEventHandler<OnTextTrackDataChangedData>; // iOS
onVideoTracks?: DirectEventHandler<OnVideoTracksData>; // android
}
export type VideoComponentType = HostComponent<VideoNativeProps>;
export type VideoSaveData = {
uri: string;
};
export interface VideoManagerType {
save: (option: object, reactTag: number) => Promise<VideoSaveData>;
setPlayerPauseState: (paused: boolean, reactTag: number) => Promise<void>;
setLicenseResult: (
result: string,
licenseUrl: string,
reactTag: number,
) => Promise<void>;
setLicenseResultError: (
error: string,
licenseUrl: string,
reactTag: number,
) => Promise<void>;
}
export interface VideoDecoderPropertiesType {
getWidevineLevel: () => Promise<number>;
isCodecSupported: (
mimeType: string,
width: number,
height: number,
) => Promise<'unsupported' | 'hardware' | 'software'>;
isHEVCSupported: () => Promise<'unsupported' | 'hardware' | 'software'>;
}
export const VideoManager = NativeModules.VideoManager as VideoManagerType;
export const VideoDecoderProperties =
NativeModules.VideoDecoderProperties as VideoDecoderPropertiesType;
export default codegenNativeComponent<VideoNativeProps>(
'RCTVideo',
) as VideoComponentType;

View File

@ -1,160 +1,28 @@
import type Orientation from './Orientation';
import type {AdEvent} from './Ads';
import type {
OnAudioFocusChangedData,
OnAudioTracksData,
OnBandwidthUpdateData,
OnBufferData,
OnExternalPlaybackChangeData,
OnLoadData,
OnLoadStartData,
OnPictureInPictureStatusChangedData,
OnPlaybackData,
OnPlaybackStateChangedData,
OnProgressData,
OnReceiveAdEventData,
OnSeekData,
OnTextTrackDataChangedData,
OnTextTracksData,
OnTimedMetadataData,
OnVideoAspectRatioData,
OnVideoErrorData,
OnVideoTracksData,
OnVolumeChangeData,
} from '../specs/VideoNativeComponent';
export type OnLoadData = Readonly<{
currentTime: number;
duration: number;
naturalSize: Readonly<{
width: number;
height: number;
orientation: Orientation;
}>;
}> &
OnAudioTracksData &
OnTextTracksData;
export type OnVideoAspectRatioData = Readonly<{
width: number;
height: number;
}>;
export type OnLoadStartData = Readonly<{
isNetwork: boolean;
type: string;
uri: string;
}>;
export type OnProgressData = Readonly<{
currentTime: number;
playableDuration: number;
seekableDuration: number;
}>;
export type OnSeekData = Readonly<{
currentTime: number;
seekTime: number;
}>;
export type OnPlaybackStateChangedData = Readonly<{
isPlaying: boolean;
}>;
export type OnTimedMetadataData = Readonly<{
metadata: ReadonlyArray<
Readonly<{
value?: string;
identifier: string;
}>
>;
}>;
export type AudioTrack = Readonly<{
index: number;
title?: string;
language?: string;
bitrate?: number;
type?: string;
selected?: boolean;
}>;
export type OnAudioTracksData = Readonly<{
audioTracks: ReadonlyArray<AudioTrack>;
}>;
export enum OnTextTracksTypeData {
SRT = 'srt',
TTML = 'ttml',
VTT = 'vtt',
}
export type TextTrack = Readonly<{
index: number;
title?: string;
language?: string;
type?: OnTextTracksTypeData;
selected?: boolean;
}>;
export type OnTextTracksData = Readonly<{
textTracks: ReadonlyArray<TextTrack>;
}>;
export type OnTextTrackDataChangedData = Readonly<{
subtitleTracks: string;
}>;
export type OnVideoTracksData = Readonly<{
videoTracks: ReadonlyArray<
Readonly<{
trackId: number;
codecs?: string;
width?: number;
height?: number;
bitrate?: number;
selected?: boolean;
}>
>;
}>;
export type OnPlaybackData = Readonly<{
playbackRate: number;
}>;
export type OnVolumeChangeData = Readonly<{
volume: number;
}>;
export type OnExternalPlaybackChangeData = Readonly<{
isExternalPlaybackActive: boolean;
}>;
export type OnGetLicenseData = Readonly<{
licenseUrl: string;
contentId: string;
spcBase64: string;
}>;
export type OnPictureInPictureStatusChangedData = Readonly<{
isActive: boolean;
}>;
export type OnReceiveAdEventData = Readonly<{
data?: Record<string, string>;
event: AdEvent;
}>;
export type OnVideoErrorData = Readonly<{
error: OnVideoErrorDataDetails;
target?: number; // ios
}>;
export type OnVideoErrorDataDetails = Readonly<{
errorString?: string; // android
errorException?: string; // android
errorStackTrace?: string; // android
errorCode?: string; // android
error?: string; // ios
code?: number; // ios
localizedDescription?: string; // ios
localizedFailureReason?: string; // ios
localizedRecoverySuggestion?: string; // ios
domain?: string; // ios
}>;
export type OnAudioFocusChangedData = Readonly<{
hasAudioFocus: boolean;
}>;
export type OnBufferData = Readonly<{isBuffering: boolean}>;
export type OnBandwidthUpdateData = Readonly<
| {
bitrate: number;
width: number;
height: number;
trackId: number;
}
| {bitrate: number}
>;
export type AudioTrack = OnAudioTracksData['audioTracks'][number];
export type TextTrack = OnTextTracksData['textTracks'][number];
export interface ReactVideoEvents {
onAudioBecomingNoisy?: () => void; //Android, iOS

View File

@ -6,3 +6,4 @@ export {default as Orientation} from './Orientation';
export {default as ResizeMode} from './ResizeMode';
export {default as TextTrackType} from './TextTrackType';
export * from './video';
export * from '../specs/VideoNativeComponent';

View File

@ -5,6 +5,14 @@ import type {ReactVideoSource, ReactVideoSourceProperties} from './types/video';
type Source = ImageSourcePropType | ReactVideoSource;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function generateHeaderForNative(obj?: Record<string, any>) {
if (!obj) {
return [];
}
return Object.entries(obj).map(([key, value]) => ({key, value}));
}
export function resolveAssetSourceForVideo(
source: Source,
): ReactVideoSourceProperties {