feat: add typescript (#3266)
* chore: update dependencies * chore: add typescript config * feat: add types * chore: add build command * chore: fix types * fix: update linters * chore: add display name to component * chore: fix types * chore: remove re-declare name variables * docs: update changelog
This commit is contained in:
449
src/Video.tsx
Normal file
449
src/Video.tsx
Normal file
@@ -0,0 +1,449 @@
|
||||
import React, {
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
type ComponentRef,
|
||||
} from "react";
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
Image,
|
||||
Platform,
|
||||
} from "react-native";
|
||||
import NativeVideoComponent, { RCTVideoConstants } from "./VideoNativeComponent";
|
||||
import type { NativeVideoResizeMode, OnAudioFocusChangedData, OnAudioTracksData, OnPlaybackStateChangedData, OnTextTracksData, OnTimedMetadataData, OnVideoErrorData, OnVideoTracksData } from './VideoNativeComponent'
|
||||
|
||||
import type { StyleProp, ImageStyle, NativeSyntheticEvent } from "react-native";
|
||||
import {
|
||||
type VideoComponentType,
|
||||
type OnLoadData,
|
||||
type OnGetLicenseData,
|
||||
type OnLoadStartData,
|
||||
type OnProgressData,
|
||||
type OnSeekData,
|
||||
type OnPictureInPictureStatusChangedData,
|
||||
type OnBandwidthUpdateData,
|
||||
type OnBufferData,
|
||||
type OnExternalPlaybackChangeData,
|
||||
type OnReceiveAdEventData,
|
||||
VideoManager,
|
||||
} from "./VideoNativeComponent";
|
||||
import type { ReactVideoProps } from "./types/video";
|
||||
import { getReactTag, resolveAssetSourceForVideo } from "./utils";
|
||||
|
||||
export interface VideoRef {
|
||||
seek: (time: number, tolerance?: number) => void;
|
||||
resume: () => void;
|
||||
pause: () => void;
|
||||
presentFullscreenPlayer: () => void;
|
||||
dismissFullscreenPlayer: () => void;
|
||||
restoreUserInterfaceForPictureInPictureStopCompleted: (restore: boolean) => void;
|
||||
}
|
||||
|
||||
const Video = forwardRef<VideoRef, ReactVideoProps>(
|
||||
(
|
||||
{
|
||||
source,
|
||||
style,
|
||||
resizeMode,
|
||||
posterResizeMode,
|
||||
poster,
|
||||
fullscreen,
|
||||
drm,
|
||||
textTracks,
|
||||
selectedAudioTrack,
|
||||
selectedTextTrack,
|
||||
onLoadStart,
|
||||
onLoad,
|
||||
onError,
|
||||
onProgress,
|
||||
onSeek,
|
||||
onEnd,
|
||||
onBuffer,
|
||||
onBandwidthUpdate,
|
||||
onExternalPlaybackChange,
|
||||
onFullscreenPlayerWillPresent,
|
||||
onFullscreenPlayerDidPresent,
|
||||
onFullscreenPlayerWillDismiss,
|
||||
onFullscreenPlayerDidDismiss,
|
||||
onReadyForDisplay,
|
||||
onPlaybackRateChange,
|
||||
onAudioBecomingNoisy,
|
||||
onPictureInPictureStatusChanged,
|
||||
onRestoreUserInterfaceForPictureInPictureStop,
|
||||
onReceiveAdEvent,
|
||||
onPlaybackStateChanged,
|
||||
onAudioFocusChanged,
|
||||
onIdle,
|
||||
onTimedMetadata,
|
||||
onAudioTracks,
|
||||
onTextTracks,
|
||||
onVideoTracks,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const nativeRef = useRef<ComponentRef<VideoComponentType>>(null);
|
||||
const [showPoster, setShowPoster] = useState(!!poster);
|
||||
const [isFullscreen, setIsFullscreen] = useState(fullscreen);
|
||||
const [
|
||||
_restoreUserInterfaceForPIPStopCompletionHandler,
|
||||
setRestoreUserInterfaceForPIPStopCompletionHandler,
|
||||
] = useState<boolean | undefined>();
|
||||
|
||||
const posterStyle = useMemo<StyleProp<ImageStyle>>(
|
||||
() => ({
|
||||
...StyleSheet.absoluteFillObject,
|
||||
resizeMode:
|
||||
posterResizeMode && posterResizeMode !== "none"
|
||||
? posterResizeMode
|
||||
: "contain",
|
||||
}),
|
||||
[posterResizeMode]
|
||||
);
|
||||
|
||||
const src = useMemo(() => {
|
||||
if (!source) return undefined;
|
||||
|
||||
const resolvedSource = resolveAssetSourceForVideo(source);
|
||||
let uri = resolvedSource.uri || "";
|
||||
if (uri && uri.match(/^\//)) uri = `file://${uri}`;
|
||||
if (!uri) console.warn("Trying to load empty source");
|
||||
const isNetwork = !!(uri && uri.match(/^https?:/));
|
||||
const isAsset = !!(
|
||||
uri &&
|
||||
uri.match(
|
||||
/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
uri,
|
||||
isNetwork,
|
||||
isAsset,
|
||||
shouldCache: resolvedSource.shouldCache || false,
|
||||
type: resolvedSource.type || "",
|
||||
mainVer: resolvedSource.mainVer || 0,
|
||||
patchVer: resolvedSource.patchVer || 0,
|
||||
requestHeaders: resolvedSource?.headers || {},
|
||||
startTime: resolvedSource.startTime || 0,
|
||||
endTime: resolvedSource.endTime
|
||||
};
|
||||
}, [source]);
|
||||
|
||||
const _resizeMode: NativeVideoResizeMode = useMemo(() => {
|
||||
switch (resizeMode) {
|
||||
case "contain":
|
||||
return RCTVideoConstants.ScaleAspectFit;
|
||||
case "cover":
|
||||
return RCTVideoConstants.ScaleAspectFill;
|
||||
case "stretch":
|
||||
return RCTVideoConstants.ScaleToFill;
|
||||
default:
|
||||
return RCTVideoConstants.ScaleNone;
|
||||
}
|
||||
}, [resizeMode]);
|
||||
|
||||
const _drm = useMemo(() => {
|
||||
if (!drm) return;
|
||||
return {
|
||||
drmType: drm.type,
|
||||
licenseServer: drm.licenseServer,
|
||||
headers: drm.headers,
|
||||
contentId: drm.contentId,
|
||||
certificateUrl: drm.certificateUrl,
|
||||
base64Certificate: drm.base64Certificate,
|
||||
useExternalGetLicense: !!drm.getLicense,
|
||||
};
|
||||
}, [drm]);
|
||||
|
||||
|
||||
const _selectedTextTrack = useMemo(() => {
|
||||
if (!selectedTextTrack) return;
|
||||
if (typeof selectedTextTrack?.value === 'number') return {
|
||||
seletedTextType: selectedTextTrack?.type,
|
||||
index: selectedTextTrack?.value,
|
||||
}
|
||||
return {
|
||||
selectedTextType: selectedTextTrack?.type,
|
||||
value: selectedTextTrack?.value,
|
||||
}
|
||||
}, [selectedTextTrack]);
|
||||
|
||||
const _selectedAudioTrack = useMemo(() => {
|
||||
if (!selectedAudioTrack) return;
|
||||
if (typeof selectedAudioTrack?.value === 'number') return {
|
||||
selectedAudioType: selectedAudioTrack?.type,
|
||||
index: selectedAudioTrack?.value,
|
||||
}
|
||||
return {
|
||||
selectedAudioType: selectedAudioTrack?.type,
|
||||
value: selectedAudioTrack?.value,
|
||||
}
|
||||
}, [selectedAudioTrack]);
|
||||
|
||||
|
||||
const seek = useCallback(
|
||||
async (time: number, tolerance?: number) => {
|
||||
if (isNaN(time)) throw new Error("Specified time is not a number");
|
||||
|
||||
if (!nativeRef.current) {
|
||||
console.warn("Video Component is not mounted");
|
||||
return;
|
||||
}
|
||||
|
||||
Platform.select({
|
||||
ios: () => {
|
||||
nativeRef.current?.setNativeProps({
|
||||
seek: {
|
||||
time,
|
||||
tolerance,
|
||||
},
|
||||
});
|
||||
},
|
||||
default: () => {
|
||||
nativeRef.current?.setNativeProps({
|
||||
seek: time,
|
||||
});
|
||||
},
|
||||
})();
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const presentFullscreenPlayer = useCallback(() => {
|
||||
setIsFullscreen(true);
|
||||
}, [setIsFullscreen]);
|
||||
|
||||
const dismissFullscreenPlayer = useCallback(() => {
|
||||
setIsFullscreen(false);
|
||||
}, [setIsFullscreen]);
|
||||
|
||||
const save = useCallback(async () => {
|
||||
await VideoManager.save(getReactTag(nativeRef));
|
||||
}, []);
|
||||
|
||||
const pause = useCallback(async () => {
|
||||
await VideoManager.setPlayerPauseState(true, getReactTag(nativeRef));
|
||||
}, []);
|
||||
|
||||
const resume = useCallback(async () => {
|
||||
await VideoManager.setPlayerPauseState(false, getReactTag(nativeRef));
|
||||
}, []);
|
||||
|
||||
const restoreUserInterfaceForPictureInPictureStopCompleted = useCallback(
|
||||
(restored: boolean) => {
|
||||
setRestoreUserInterfaceForPIPStopCompletionHandler(restored);
|
||||
},
|
||||
[setRestoreUserInterfaceForPIPStopCompletionHandler]
|
||||
);
|
||||
|
||||
const onVideoLoadStart = useCallback(
|
||||
(e: NativeSyntheticEvent<OnLoadStartData>) => {
|
||||
onLoadStart?.(e.nativeEvent);
|
||||
},
|
||||
[onLoadStart]
|
||||
);
|
||||
|
||||
const onVideoLoad = useCallback(
|
||||
(e: NativeSyntheticEvent<OnLoadData>) => {
|
||||
if (Platform.OS === "windows") setShowPoster(false);
|
||||
onLoad?.(e.nativeEvent);
|
||||
},
|
||||
[onLoad, setShowPoster]
|
||||
);
|
||||
|
||||
const onVideoError = useCallback(
|
||||
(e: NativeSyntheticEvent<OnVideoErrorData>) => {
|
||||
onError?.(e.nativeEvent);
|
||||
},
|
||||
[onError]
|
||||
);
|
||||
|
||||
const onVideoProgress = useCallback(
|
||||
(e: NativeSyntheticEvent<OnProgressData>) => {
|
||||
onProgress?.(e.nativeEvent);
|
||||
},
|
||||
[onProgress]
|
||||
);
|
||||
|
||||
const onVideoSeek = useCallback(
|
||||
(e: NativeSyntheticEvent<OnSeekData>) => {
|
||||
onSeek?.(e.nativeEvent);
|
||||
},
|
||||
[onSeek]
|
||||
);
|
||||
|
||||
// android only
|
||||
const onVideoPlaybackStateChanged = useCallback((e: NativeSyntheticEvent<OnPlaybackStateChangedData>) => {
|
||||
onPlaybackStateChanged?.(e.nativeEvent);
|
||||
}, [onPlaybackStateChanged])
|
||||
|
||||
// android only
|
||||
const onVideoIdle = useCallback(() => {
|
||||
onIdle?.()
|
||||
}, [onIdle])
|
||||
|
||||
const _onTimedMetadata = useCallback(
|
||||
(e: NativeSyntheticEvent<OnTimedMetadataData>) => {
|
||||
onTimedMetadata?.(e.nativeEvent);
|
||||
},
|
||||
[onTimedMetadata]
|
||||
);
|
||||
|
||||
const _onAudioTracks = useCallback((e: NativeSyntheticEvent<OnAudioTracksData>) => {
|
||||
onAudioTracks?.(e.nativeEvent)
|
||||
}, [onAudioTracks])
|
||||
|
||||
const _onTextTracks = useCallback((e: NativeSyntheticEvent<OnTextTracksData>) => {
|
||||
onTextTracks?.(e.nativeEvent)
|
||||
}, [onTextTracks])
|
||||
|
||||
const _onVideoTracks = useCallback((e: NativeSyntheticEvent<OnVideoTracksData>) => {
|
||||
onVideoTracks?.(e.nativeEvent)
|
||||
}, [onVideoTracks])
|
||||
|
||||
const _onPlaybackRateChange = useCallback(
|
||||
(e: NativeSyntheticEvent<Readonly<{ playbackRate: number }>>) => {
|
||||
onPlaybackRateChange?.(e.nativeEvent);
|
||||
},
|
||||
[onPlaybackRateChange]
|
||||
);
|
||||
|
||||
const _onReadyForDisplay = useCallback(() => {
|
||||
setShowPoster(false);
|
||||
onReadyForDisplay?.();
|
||||
}, [setShowPoster, onReadyForDisplay]);
|
||||
|
||||
const _onPictureInPictureStatusChanged = useCallback(
|
||||
(e: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>) => {
|
||||
onPictureInPictureStatusChanged?.(e.nativeEvent);
|
||||
},
|
||||
[onPictureInPictureStatusChanged]
|
||||
);
|
||||
|
||||
const _onAudioFocusChanged = useCallback((e: NativeSyntheticEvent<OnAudioFocusChangedData>) => {
|
||||
onAudioFocusChanged?.(e.nativeEvent)
|
||||
}, [onAudioFocusChanged])
|
||||
|
||||
const onVideoBuffer = useCallback((e: NativeSyntheticEvent<OnBufferData>) => {
|
||||
onBuffer?.(e.nativeEvent);
|
||||
}, [onBuffer]);
|
||||
|
||||
const onVideoExternalPlaybackChange = useCallback((e: NativeSyntheticEvent<OnExternalPlaybackChangeData>) => {
|
||||
onExternalPlaybackChange?.(e.nativeEvent);
|
||||
}, [onExternalPlaybackChange])
|
||||
|
||||
const _onBandwidthUpdate = useCallback((e: NativeSyntheticEvent<OnBandwidthUpdateData>) => {
|
||||
onBandwidthUpdate?.(e.nativeEvent);
|
||||
}, [onBandwidthUpdate]);
|
||||
|
||||
const _onReceiveAdEvent = useCallback((e: NativeSyntheticEvent<OnReceiveAdEventData>) => {
|
||||
onReceiveAdEvent?.(e.nativeEvent);
|
||||
}, [onReceiveAdEvent]);
|
||||
|
||||
const onGetLicense = useCallback(
|
||||
(event: NativeSyntheticEvent<OnGetLicenseData>) => {
|
||||
if (drm && drm.getLicense instanceof Function) {
|
||||
const data = event.nativeEvent;
|
||||
if (data && data.spcBase64) {
|
||||
const getLicenseOverride = drm.getLicense(data.spcBase64, data.contentId, data.licenseUrl);
|
||||
const getLicensePromise = Promise.resolve(getLicenseOverride); // Handles both scenarios, getLicenseOverride being a promise and not.
|
||||
getLicensePromise.then((result => {
|
||||
if (result !== undefined) {
|
||||
nativeRef.current && VideoManager.setLicenseResult(result, data.licenseUrl, getReactTag(nativeRef));
|
||||
} else {
|
||||
nativeRef.current && VideoManager.setLicenseResultError('Empty license result', data.licenseUrl, getReactTag(nativeRef));
|
||||
}
|
||||
})).catch(() => {
|
||||
nativeRef.current && VideoManager.setLicenseResultError('fetch error', data.licenseUrl, getReactTag(nativeRef));
|
||||
});
|
||||
} else {
|
||||
VideoManager.setLicenseResultError('No spc received', data.licenseUrl, getReactTag(nativeRef));
|
||||
}
|
||||
}
|
||||
},
|
||||
[drm]
|
||||
);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
seek,
|
||||
presentFullscreenPlayer,
|
||||
dismissFullscreenPlayer,
|
||||
save,
|
||||
pause,
|
||||
resume,
|
||||
restoreUserInterfaceForPictureInPictureStopCompleted,
|
||||
}),
|
||||
[
|
||||
seek,
|
||||
presentFullscreenPlayer,
|
||||
dismissFullscreenPlayer,
|
||||
save,
|
||||
pause,
|
||||
resume,
|
||||
restoreUserInterfaceForPictureInPictureStopCompleted,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
<NativeVideoComponent
|
||||
ref={nativeRef}
|
||||
{...rest}
|
||||
src={src}
|
||||
drm={_drm}
|
||||
style={StyleSheet.absoluteFill}
|
||||
resizeMode={_resizeMode}
|
||||
fullscreen={isFullscreen}
|
||||
restoreUserInterfaceForPIPStopCompletionHandler={
|
||||
_restoreUserInterfaceForPIPStopCompletionHandler
|
||||
}
|
||||
textTracks={textTracks}
|
||||
selectedTextTrack={_selectedTextTrack}
|
||||
selectedAudioTrack={_selectedAudioTrack}
|
||||
onGetLicense={onGetLicense}
|
||||
onVideoLoad={onVideoLoad}
|
||||
onVideoLoadStart={onVideoLoadStart}
|
||||
onVideoError={onVideoError}
|
||||
onVideoProgress={onVideoProgress}
|
||||
onVideoSeek={onVideoSeek}
|
||||
onVideoEnd={onEnd}
|
||||
onVideoBuffer={onVideoBuffer}
|
||||
onVideoPlaybackStateChanged={onVideoPlaybackStateChanged}
|
||||
onBandwidthUpdate={_onBandwidthUpdate}
|
||||
onTimedMetadata={_onTimedMetadata}
|
||||
onAudioTracks={_onAudioTracks}
|
||||
onTextTracks={_onTextTracks}
|
||||
onVideoTracks={_onVideoTracks}
|
||||
onVideoFullscreenPlayerDidDismiss={onFullscreenPlayerDidDismiss}
|
||||
onVideoFullscreenPlayerDidPresent={onFullscreenPlayerDidPresent}
|
||||
onVideoFullscreenPlayerWillDismiss={onFullscreenPlayerWillDismiss}
|
||||
onVideoFullscreenPlayerWillPresent={onFullscreenPlayerWillPresent}
|
||||
onVideoExternalPlaybackChange={onVideoExternalPlaybackChange}
|
||||
onVideoIdle={onVideoIdle}
|
||||
onAudioFocusChanged={_onAudioFocusChanged}
|
||||
onReadyForDisplay={_onReadyForDisplay}
|
||||
onPlaybackRateChange={_onPlaybackRateChange}
|
||||
onVideoAudioBecomingNoisy={onAudioBecomingNoisy}
|
||||
onPictureInPictureStatusChanged={_onPictureInPictureStatusChanged}
|
||||
onRestoreUserInterfaceForPictureInPictureStop={
|
||||
onRestoreUserInterfaceForPictureInPictureStop
|
||||
}
|
||||
onReceiveAdEvent={_onReceiveAdEvent}
|
||||
/>
|
||||
{showPoster ? (
|
||||
<Image style={posterStyle} source={{ uri: poster }} />
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Video.displayName = "Video";
|
||||
export default Video;
|
323
src/VideoNativeComponent.ts
Normal file
323
src/VideoNativeComponent.ts
Normal file
@@ -0,0 +1,323 @@
|
||||
import type { HostComponent, NativeSyntheticEvent, ViewProps } from 'react-native';
|
||||
import { NativeModules, requireNativeComponent } from 'react-native';
|
||||
import { getViewManagerConfig } from './utils';
|
||||
|
||||
// -------- 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, any>
|
||||
|
||||
type VideoSrc = Readonly<{
|
||||
uri?: string;
|
||||
isNetwork?: boolean;
|
||||
isAsset?: boolean;
|
||||
shouldCache?: boolean;
|
||||
type?: string;
|
||||
mainVer?: number;
|
||||
patchVer?: number;
|
||||
requestHeaders?: Headers;
|
||||
startTime?: number;
|
||||
endTime?: number;
|
||||
}>
|
||||
|
||||
|
||||
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 Drm = Readonly<{
|
||||
drmType?: 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<{
|
||||
selectedTextType: TextTrackType;
|
||||
value?: string;
|
||||
index?: number;
|
||||
}>
|
||||
|
||||
type AudioTrackType = 'system' | 'disabled' | 'title' | 'language' | 'index';
|
||||
|
||||
type SelectedAudioTrack = Readonly<{
|
||||
selectedAudioType: AudioTrackType;
|
||||
value?: string;
|
||||
index?: 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: 'portrait' | 'landscape';
|
||||
}>;
|
||||
}>
|
||||
|
||||
export type OnLoadStartData = Readonly<{
|
||||
isNetwork: boolean;
|
||||
type: string;
|
||||
uri: string;
|
||||
}>
|
||||
|
||||
export type OnBufferData = Readonly<{ isBuffering: boolean }>;
|
||||
|
||||
|
||||
export type OnProgressData = Readonly<{
|
||||
currentTime: number;
|
||||
playableDuration: number;
|
||||
seekableDuration: number;
|
||||
}>
|
||||
|
||||
export type OnBandwidthUpdateData = Readonly<{
|
||||
bitrate: number;
|
||||
}>;
|
||||
|
||||
export type OnSeekData = Readonly<{
|
||||
currentTime: number;
|
||||
seekTime: number;
|
||||
finished: boolean;
|
||||
}>
|
||||
|
||||
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?: 'srt' | 'ttml' | 'vtt'
|
||||
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 OnExternalPlaybackChangeData = Readonly<{
|
||||
isExternalPlaybackActive: boolean;
|
||||
}>
|
||||
|
||||
export type OnGetLicenseData = Readonly<{
|
||||
licenseUrl: string;
|
||||
contentId: string;
|
||||
spcBase64: string;
|
||||
}>
|
||||
|
||||
export type OnPictureInPictureStatusChangedData = Readonly<{
|
||||
isActive: boolean;
|
||||
}>
|
||||
|
||||
export type OnReceiveAdEventData = Readonly<{
|
||||
event: string;
|
||||
}>
|
||||
|
||||
export type OnVideoErrorData = Readonly<{
|
||||
error: string;
|
||||
}>
|
||||
|
||||
export type OnAudioFocusChangedData = Readonly<{
|
||||
hasFocus: boolean;
|
||||
}>
|
||||
|
||||
export type NativeVideoResizeMode = 'ScaleNone' | 'ScaleToFill' | 'ScaleAspectFit' | 'ScaleAspectFill';
|
||||
export interface VideoNativeProps extends ViewProps {
|
||||
src?: VideoSrc;
|
||||
drm?: Drm;
|
||||
adTagUrl?: string;
|
||||
allowsExternalPlayback?: boolean; // ios, true
|
||||
maxBitRate?: number;
|
||||
resizeMode?: NativeVideoResizeMode;
|
||||
repeat?: boolean;
|
||||
automaticallyWaitsToMinimizeStalling?: boolean
|
||||
textTracks?: TextTracks;
|
||||
selectedTextTrack?: SelectedTextTrack;
|
||||
selectedAudioTrack?: SelectedAudioTrack;
|
||||
paused?: boolean;
|
||||
muted?: boolean;
|
||||
controls?: boolean;
|
||||
filter?: Filter;
|
||||
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;
|
||||
|
||||
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
|
||||
trackId?: string; // Android
|
||||
useTextureView?: boolean; // Android
|
||||
useSecureView?: boolean; // Android
|
||||
|
||||
onVideoLoad?: (event: NativeSyntheticEvent<OnLoadData>) => void;
|
||||
onVideoLoadStart?: (event: NativeSyntheticEvent<OnLoadStartData>) => void;
|
||||
onVideoBuffer?: (event: NativeSyntheticEvent<OnBufferData>) => void;
|
||||
onVideoError?: (event: NativeSyntheticEvent<OnVideoErrorData>) => void;
|
||||
onVideoProgress?: (event: NativeSyntheticEvent<OnProgressData>) => void;
|
||||
onBandwidthUpdate?: (event: NativeSyntheticEvent<OnBandwidthUpdateData>) => void;
|
||||
onVideoSeek?: (event: NativeSyntheticEvent<OnSeekData>) => void;
|
||||
onVideoEnd?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // all
|
||||
onVideoAudioBecomingNoisy?: (event: NativeSyntheticEvent<Readonly<{}>>) => void;
|
||||
onVideoFullscreenPlayerWillPresent?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // ios, android
|
||||
onVideoFullscreenPlayerDidPresent?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // ios, android
|
||||
onVideoFullscreenPlayerWillDismiss?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // ios, android
|
||||
onVideoFullscreenPlayerDidDismiss?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // ios, android
|
||||
onReadyForDisplay?: (event: NativeSyntheticEvent<Readonly<{}>>) => void;
|
||||
onPlaybackRateChange?: (event: NativeSyntheticEvent<OnPlaybackData>) => void; // all
|
||||
onVideoExternalPlaybackChange?: (event: NativeSyntheticEvent<OnExternalPlaybackChangeData>) => void;
|
||||
onGetLicense?: (event: NativeSyntheticEvent<OnGetLicenseData>) => void;
|
||||
onPictureInPictureStatusChanged?: (event: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>) => void;
|
||||
onRestoreUserInterfaceForPictureInPictureStop?: (event: NativeSyntheticEvent<Readonly<{}>>) => void;
|
||||
onReceiveAdEvent?: (event: NativeSyntheticEvent<OnReceiveAdEventData>) => void;
|
||||
onVideoPlaybackStateChanged?: (event: NativeSyntheticEvent<OnPlaybackStateChangedData>) => void; // android only
|
||||
onVideoIdle?: (event: NativeSyntheticEvent<{}>) => 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
|
||||
onVideoTracks?: (event: NativeSyntheticEvent<OnVideoTracksData>) => void; // android
|
||||
}
|
||||
|
||||
export type VideoComponentType = HostComponent<VideoNativeProps>;
|
||||
|
||||
export interface VideoManagerType {
|
||||
save: (reactTag: number) => Promise<void>;
|
||||
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 type VideoViewManagerConfig = {
|
||||
Constants: {
|
||||
ScaleNone: any;
|
||||
ScaleToFill: any;
|
||||
ScaleAspectFit: any;
|
||||
ScaleAspectFill: any;
|
||||
};
|
||||
Commands: { [key: string]: number; };
|
||||
};
|
||||
|
||||
export const VideoManager = NativeModules.VideoManager as VideoManagerType;
|
||||
export const VideoDecoderProperties = NativeModules.VideoDecoderProperties as VideoDecoderPropertiesType;
|
||||
export const RCTVideoConstants = (getViewManagerConfig('RCTVideo') as VideoViewManagerConfig).Constants;
|
||||
|
||||
export default requireNativeComponent<VideoNativeProps>('RCTVideo') as VideoComponentType;
|
11
src/index.ts
Normal file
11
src/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import Video from "./Video";
|
||||
|
||||
export { default as FilterType } from './lib/FilterType';
|
||||
export { default as VideoResizeMode } from './lib/VideoResizeMode';
|
||||
export { default as TextTrackType } from './lib/TextTrackType';
|
||||
export { default as DRMType } from './lib/DRMType';
|
||||
export { VideoDecoderProperties } from './VideoNativeComponent';
|
||||
|
||||
export type { VideoRef } from './Video';
|
||||
|
||||
export default Video;
|
6
src/lib/DRMType.ts
Normal file
6
src/lib/DRMType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
WIDEVINE: 'widevine',
|
||||
PLAYREADY: 'playready',
|
||||
CLEARKEY: 'clearkey',
|
||||
FAIRPLAY: 'fairplay',
|
||||
} as const;
|
18
src/lib/FilterType.ts
Normal file
18
src/lib/FilterType.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export default {
|
||||
NONE: '',
|
||||
INVERT: 'CIColorInvert',
|
||||
MONOCHROME: 'CIColorMonochrome',
|
||||
POSTERIZE: 'CIColorPosterize',
|
||||
FALSE: 'CIFalseColor',
|
||||
MAXIMUMCOMPONENT: 'CIMaximumComponent',
|
||||
MINIMUMCOMPONENT: 'CIMinimumComponent',
|
||||
CHROME: 'CIPhotoEffectChrome',
|
||||
FADE: 'CIPhotoEffectFade',
|
||||
INSTANT: 'CIPhotoEffectInstant',
|
||||
MONO: 'CIPhotoEffectMono',
|
||||
NOIR: 'CIPhotoEffectNoir',
|
||||
PROCESS: 'CIPhotoEffectProcess',
|
||||
TONAL: 'CIPhotoEffectTonal',
|
||||
TRANSFER: 'CIPhotoEffectTransfer',
|
||||
SEPIA: 'CISepiaTone',
|
||||
} as const;
|
5
src/lib/TextTrackType.ts
Normal file
5
src/lib/TextTrackType.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
SRT: 'application/x-subrip',
|
||||
TTML: 'application/ttml+xml',
|
||||
VTT: 'text/vtt',
|
||||
} as const;
|
5
src/lib/VideoResizeMode.ts
Normal file
5
src/lib/VideoResizeMode.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
contain: 'contain',
|
||||
cover: 'cover',
|
||||
stretch: 'stretch',
|
||||
} as const;
|
30
src/types/events.ts
Normal file
30
src/types/events.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { OnBandwidthUpdateData, OnBufferData, OnLoadData, OnLoadStartData, OnProgressData, OnSeekData, OnPlaybackData, OnExternalPlaybackChangeData, OnPictureInPictureStatusChangedData, OnReceiveAdEventData, OnVideoErrorData, OnPlaybackStateChangedData, OnAudioFocusChangedData, OnTimedMetadataData, OnAudioTracksData, OnTextTracksData, OnVideoTracksData } from "../VideoNativeComponent";
|
||||
|
||||
export interface ReactVideoEvents {
|
||||
onAudioBecomingNoisy?: () => void //Android, iOS
|
||||
onAudioFocusChanged?: (e: OnAudioFocusChangedData) => void // Android
|
||||
onIdle?: () => void // Android
|
||||
onBandwidthUpdate?: (e: OnBandwidthUpdateData) => void //Android
|
||||
onBuffer?: (e: OnBufferData) => void //Android, iOS
|
||||
onEnd?: () => void //All
|
||||
onError?: (e: OnVideoErrorData) => void //Android, iOS
|
||||
onExternalPlaybackChange?: (e: OnExternalPlaybackChangeData) => void //iOS
|
||||
onFullscreenPlayerWillPresent?: () => void //Android, iOS
|
||||
onFullscreenPlayerDidPresent?: () => void //Android, iOS
|
||||
onFullscreenPlayerWillDismiss?: () => void //Android, iOS
|
||||
onFullscreenPlayerDidDismiss?: () => void //Android, iOS
|
||||
onLoad?: (e: OnLoadData) => void //All
|
||||
onLoadStart?: (e: OnLoadStartData) => void //All
|
||||
onPictureInPictureStatusChanged?: (e: OnPictureInPictureStatusChangedData) => void //iOS
|
||||
onPlaybackRateChange?: (e: OnPlaybackData) => void //All
|
||||
onProgress?: (e: OnProgressData) => void //All
|
||||
onReadyForDisplay?: () => void //Android, iOS, Web
|
||||
onReceiveAdEvent?: (e: OnReceiveAdEventData) => void //Android, iOS
|
||||
onRestoreUserInterfaceForPictureInPictureStop?: () => void //iOS
|
||||
onSeek?: (e: OnSeekData) => void //Android, iOS, Windows UWP
|
||||
onPlaybackStateChanged?: (e: OnPlaybackStateChangedData) => void // Android
|
||||
onTimedMetadata?: (e: OnTimedMetadataData) => void //Android, iOS
|
||||
onAudioTracks?: (e: OnAudioTracksData) => void // Android
|
||||
onTextTracks?: (e: OnTextTracksData) => void //Android
|
||||
onVideoTracks?: (e: OnVideoTracksData) => void //Android
|
||||
}
|
184
src/types/language.ts
Normal file
184
src/types/language.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
export type ISO639_1 =
|
||||
| 'aa'
|
||||
| 'ab'
|
||||
| 'ae'
|
||||
| 'af'
|
||||
| 'ak'
|
||||
| 'am'
|
||||
| 'an'
|
||||
| 'ar'
|
||||
| 'as'
|
||||
| 'av'
|
||||
| 'ay'
|
||||
| 'az'
|
||||
| 'ba'
|
||||
| 'be'
|
||||
| 'bg'
|
||||
| 'bi'
|
||||
| 'bm'
|
||||
| 'bn'
|
||||
| 'bo'
|
||||
| 'br'
|
||||
| 'bs'
|
||||
| 'ca'
|
||||
| 'ce'
|
||||
| 'ch'
|
||||
| 'co'
|
||||
| 'cr'
|
||||
| 'cs'
|
||||
| 'cu'
|
||||
| 'cv'
|
||||
| 'cy'
|
||||
| 'da'
|
||||
| 'de'
|
||||
| 'dv'
|
||||
| 'dz'
|
||||
| 'ee'
|
||||
| 'el'
|
||||
| 'en'
|
||||
| 'eo'
|
||||
| 'es'
|
||||
| 'et'
|
||||
| 'eu'
|
||||
| 'fa'
|
||||
| 'ff'
|
||||
| 'fi'
|
||||
| 'fj'
|
||||
| 'fo'
|
||||
| 'fr'
|
||||
| 'fy'
|
||||
| 'ga'
|
||||
| 'gd'
|
||||
| 'gl'
|
||||
| 'gn'
|
||||
| 'gu'
|
||||
| 'gv'
|
||||
| 'ha'
|
||||
| 'he'
|
||||
| 'hi'
|
||||
| 'ho'
|
||||
| 'hr'
|
||||
| 'ht'
|
||||
| 'hu'
|
||||
| 'hy'
|
||||
| 'hz'
|
||||
| 'ia'
|
||||
| 'id'
|
||||
| 'ie'
|
||||
| 'ig'
|
||||
| 'ii'
|
||||
| 'ik'
|
||||
| 'io'
|
||||
| 'is'
|
||||
| 'it'
|
||||
| 'iu'
|
||||
| 'ja'
|
||||
| 'jv'
|
||||
| 'ka'
|
||||
| 'kg'
|
||||
| 'ki'
|
||||
| 'kj'
|
||||
| 'kk'
|
||||
| 'kl'
|
||||
| 'km'
|
||||
| 'kn'
|
||||
| 'ko'
|
||||
| 'kr'
|
||||
| 'ks'
|
||||
| 'ku'
|
||||
| 'kv'
|
||||
| 'kw'
|
||||
| 'ky'
|
||||
| 'la'
|
||||
| 'lb'
|
||||
| 'lg'
|
||||
| 'li'
|
||||
| 'ln'
|
||||
| 'lo'
|
||||
| 'lt'
|
||||
| 'lu'
|
||||
| 'lv'
|
||||
| 'mg'
|
||||
| 'mh'
|
||||
| 'mi'
|
||||
| 'mk'
|
||||
| 'ml'
|
||||
| 'mn'
|
||||
| 'mr'
|
||||
| 'ms'
|
||||
| 'mt'
|
||||
| 'my'
|
||||
| 'na'
|
||||
| 'nb'
|
||||
| 'nd'
|
||||
| 'ne'
|
||||
| 'ng'
|
||||
| 'nl'
|
||||
| 'nn'
|
||||
| 'no'
|
||||
| 'nr'
|
||||
| 'nv'
|
||||
| 'ny'
|
||||
| 'oc'
|
||||
| 'oj'
|
||||
| 'om'
|
||||
| 'or'
|
||||
| 'os'
|
||||
| 'pa'
|
||||
| 'pi'
|
||||
| 'pl'
|
||||
| 'ps'
|
||||
| 'pt'
|
||||
| 'qu'
|
||||
| 'rm'
|
||||
| 'rn'
|
||||
| 'ro'
|
||||
| 'ru'
|
||||
| 'rw'
|
||||
| 'sa'
|
||||
| 'sc'
|
||||
| 'sd'
|
||||
| 'se'
|
||||
| 'sg'
|
||||
| 'si'
|
||||
| 'sk'
|
||||
| 'sl'
|
||||
| 'sm'
|
||||
| 'sn'
|
||||
| 'so'
|
||||
| 'sq'
|
||||
| 'sr'
|
||||
| 'ss'
|
||||
| 'st'
|
||||
| 'su'
|
||||
| 'sv'
|
||||
| 'sw'
|
||||
| 'ta'
|
||||
| 'te'
|
||||
| 'tg'
|
||||
| 'th'
|
||||
| 'ti'
|
||||
| 'tk'
|
||||
| 'tl'
|
||||
| 'tn'
|
||||
| 'to'
|
||||
| 'tr'
|
||||
| 'ts'
|
||||
| 'tt'
|
||||
| 'tw'
|
||||
| 'ty'
|
||||
| 'ug'
|
||||
| 'uk'
|
||||
| 'ur'
|
||||
| 'uz'
|
||||
| 've'
|
||||
| 'vi'
|
||||
| 'vo'
|
||||
| 'wa'
|
||||
| 'wo'
|
||||
| 'xh'
|
||||
| 'yi'
|
||||
| 'yo'
|
||||
| 'za'
|
||||
| 'zh'
|
||||
| 'zu';
|
135
src/types/video.ts
Normal file
135
src/types/video.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import type { ISO639_1 } from './language';
|
||||
import type { ReactVideoEvents } from './events';
|
||||
import type { StyleProp, ViewStyle } from 'react-native'
|
||||
|
||||
type Filter = | 'None'
|
||||
| 'CIColorInvert'
|
||||
| 'CIColorMonochrome'
|
||||
| 'CIColorPosterize'
|
||||
| 'CIFalseColor'
|
||||
| 'CIMaximumComponent'
|
||||
| 'CIMinimumComponent'
|
||||
| 'CIPhotoEffectChrome'
|
||||
| 'CIPhotoEffectFade'
|
||||
| 'CIPhotoEffectInstant'
|
||||
| 'CIPhotoEffectMono'
|
||||
| 'CIPhotoEffectNoir'
|
||||
| 'CIPhotoEffectProcess'
|
||||
| 'CIPhotoEffectTonal'
|
||||
| 'CIPhotoEffectTransfer'
|
||||
| 'CISepiaTone'
|
||||
|
||||
|
||||
|
||||
type Headers = Record<string, string>;
|
||||
|
||||
export type ReactVideoSource = Readonly<{
|
||||
uri?: string;
|
||||
isNetwork?: boolean;
|
||||
isAsset?: boolean;
|
||||
shouldCache?: boolean;
|
||||
type?: string;
|
||||
mainVer?: number;
|
||||
patchVer?: number;
|
||||
headers?: Headers;
|
||||
startTime?: number;
|
||||
endTime?: number;
|
||||
}>;
|
||||
|
||||
export type ReactVideoDrm = Readonly<{
|
||||
type?: 'widevine' | 'playready' | 'clearkey' | 'fairplay';
|
||||
licenseServer?: string;
|
||||
headers?: Headers;
|
||||
contentId?: string; // ios
|
||||
certificateUrl?: string; // ios
|
||||
base64Certificate?: boolean; // ios default: false
|
||||
getLicense?: (licenseUrl: string, contentId: string, spcBase64: string) => void; // ios
|
||||
}>
|
||||
|
||||
type BufferConfig = {
|
||||
minBufferMs?: number;
|
||||
maxBufferMs?: number;
|
||||
bufferForPlaybackMs?: number;
|
||||
bufferForPlaybackAfterRebufferMs?: number;
|
||||
maxHeapAllocationPercent?: number;
|
||||
minBackBufferMemoryReservePercent?: number;
|
||||
minBufferMemoryReservePercent?: number;
|
||||
}
|
||||
|
||||
type SelectedTrack = {
|
||||
type: 'system' | 'disabled' | 'title' | 'language' | 'index';
|
||||
value?: string | number;
|
||||
}
|
||||
|
||||
type SelectedVideoTrack = {
|
||||
type: 'auto' | 'disabled' | 'resolution' | 'index'
|
||||
value?: number;
|
||||
}
|
||||
|
||||
type SubtitleStyle = {
|
||||
fontSize?: number;
|
||||
paddingTop?: number;
|
||||
paddingBottom?: number;
|
||||
paddingLeft?: number;
|
||||
paddingRight?: number;
|
||||
}
|
||||
|
||||
type TextTracks = {
|
||||
title: string;
|
||||
language: ISO639_1;
|
||||
type: | 'application/x-subrip'
|
||||
| 'application/ttml+xml'
|
||||
| 'text/vtt';
|
||||
uri: string;
|
||||
}[]
|
||||
|
||||
export interface ReactVideoProps extends ReactVideoEvents {
|
||||
source?: ReactVideoSource;
|
||||
drm?: ReactVideoDrm;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
adTagUrl?: string; // iOS
|
||||
audioOnly?: boolean;
|
||||
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
|
||||
backBufferDurationMs?: number; // Android
|
||||
bufferConfig?: BufferConfig; // Android
|
||||
contentStartTime?: number; // Android
|
||||
controls?: boolean;
|
||||
currentPlaybackTime?: number; // Android
|
||||
disableFocus?: boolean;
|
||||
disableDisconnectError?: boolean; // Android
|
||||
filter?: Filter; // iOS
|
||||
filterEnabled?: boolean; // iOS
|
||||
focusable?: boolean; // Android
|
||||
fullscreen?: boolean; // iOS
|
||||
fullscreenAutorotate?: boolean; // iOS
|
||||
fullscreenOrientation?: 'all' | 'landscape' | 'portrait'; // iOS
|
||||
hideShutterView?: boolean; // Android
|
||||
ignoreSilentSwitch?: 'inherit' | 'ignore' | 'obey' // iOS
|
||||
minLoadRetryCount?: number; // Android
|
||||
maxBitRate?: number;
|
||||
mixWithOthers?: 'inherit' | 'mix' | 'duck'; // iOS
|
||||
muted?: boolean;
|
||||
paused?: boolean;
|
||||
pictureInPicture?: boolean // iOS
|
||||
playInBackground?: boolean;
|
||||
playWhenInactive?: boolean // iOS
|
||||
poster?: string;
|
||||
posterResizeMode?: 'contain' | 'center' | 'cover' | 'none' | 'repeat' | 'stretch';
|
||||
preferredForwardBufferDuration?: number// iOS
|
||||
preventsDisplaySleepDuringVideoPlayback?: boolean;
|
||||
progressUpdateInterval?: number;
|
||||
rate?: number;
|
||||
repeat?: boolean;
|
||||
reportBandwidth?: boolean; //Android
|
||||
resizeMode?: 'none' | 'contain' | 'cover' | 'stretch';
|
||||
selectedAudioTrack?: SelectedTrack;
|
||||
selectedTextTrack?: SelectedTrack;
|
||||
selectedVideoTrack?: SelectedVideoTrack; // android
|
||||
subtitleStyle?: SubtitleStyle // android
|
||||
textTracks?: TextTracks;
|
||||
trackId?: string; // Android
|
||||
useTextureView?: boolean; // Android
|
||||
useSecureView?: boolean; // Android
|
||||
volume?: number;
|
||||
localSourceEncryptionKeyScheme?: string;
|
||||
}
|
37
src/utils.ts
Normal file
37
src/utils.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { Component, RefObject, ComponentClass } from 'react';
|
||||
import { Image, UIManager, findNodeHandle } from "react-native";
|
||||
import type { ImageSourcePropType } from 'react-native';
|
||||
import type { ReactVideoSource } from './types/video';
|
||||
|
||||
type Source = ImageSourcePropType | ReactVideoSource;
|
||||
|
||||
export function resolveAssetSourceForVideo(source: Source): ReactVideoSource {
|
||||
if (typeof source === 'number') {
|
||||
return {
|
||||
uri: Image.resolveAssetSource(source).uri,
|
||||
};
|
||||
}
|
||||
return source as ReactVideoSource;
|
||||
}
|
||||
|
||||
export function getReactTag(ref: RefObject<Component<any, any, any> | ComponentClass<any, any> | null>): number {
|
||||
if (!ref.current) {
|
||||
throw new Error("Video Component is not mounted");
|
||||
}
|
||||
|
||||
const reactTag = findNodeHandle(ref.current);
|
||||
|
||||
if (!reactTag) {
|
||||
throw new Error("Cannot find reactTag for Video Component in components tree");
|
||||
}
|
||||
|
||||
return reactTag;
|
||||
}
|
||||
|
||||
export function getViewManagerConfig(name: string) {
|
||||
if('getViewManagerConfig' in UIManager) {
|
||||
return UIManager.getViewManagerConfig(name);
|
||||
}
|
||||
|
||||
return UIManager[name];
|
||||
}
|
Reference in New Issue
Block a user