Merge remote-tracking branch 'origin/master' into feat/web

This commit is contained in:
2024-10-12 22:33:11 -06:00
99 changed files with 5602 additions and 4049 deletions

View File

@@ -8,9 +8,17 @@ import React, {
} from 'react';
import type {ElementRef} from 'react';
import {View, StyleSheet, Image, Platform, processColor} from 'react-native';
import type {StyleProp, ImageStyle, NativeSyntheticEvent} from 'react-native';
import type {
StyleProp,
ImageStyle,
NativeSyntheticEvent,
ViewStyle,
ImageResizeMode,
} from 'react-native';
import NativeVideoComponent from './specs/VideoNativeComponent';
import NativeVideoComponent, {
NativeCmcdConfiguration,
} from './specs/VideoNativeComponent';
import type {
OnAudioFocusChangedData,
OnAudioTracksData,
@@ -37,12 +45,14 @@ import {
resolveAssetSourceForVideo,
} from './utils';
import NativeVideoManager from './specs/NativeVideoManager';
import {ViewType, type VideoSaveData} from './types';
import {type VideoSaveData, CmcdMode, ViewType} from './types';
import type {
OnLoadData,
OnTextTracksData,
OnReceiveAdEventData,
ReactVideoProps,
CmcdData,
ReactVideoSource,
} from './types';
export interface VideoRef {
@@ -56,6 +66,7 @@ export interface VideoRef {
) => void;
setVolume: (volume: number) => void;
setFullScreen: (fullScreen: boolean) => void;
setSource: (source?: ReactVideoSource) => void;
save: (options: object) => Promise<VideoSaveData> | void;
getCurrentPosition: () => Promise<number>;
}
@@ -66,8 +77,10 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
source,
style,
resizeMode,
posterResizeMode,
poster,
posterResizeMode,
renderLoader,
contentStartTime,
drm,
textTracks,
selectedVideoTrack,
@@ -77,6 +90,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
useSecureView,
viewType,
shutterColor,
adTagUrl,
adLanguage,
onLoadStart,
onLoad,
onError,
@@ -107,82 +122,152 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
onTextTrackDataChanged,
onVideoTracks,
onAspectRatio,
localSourceEncryptionKeyScheme,
...rest
},
ref,
) => {
const nativeRef = useRef<ElementRef<typeof NativeVideoComponent>>(null);
const [showPoster, setShowPoster] = useState(!!poster);
const isPosterDeprecated = typeof poster === 'string';
const _renderLoader = useMemo(
() =>
!renderLoader
? undefined
: renderLoader instanceof Function
? renderLoader
: () => renderLoader,
[renderLoader],
);
const hasPoster = useMemo(() => {
if (_renderLoader) {
return true;
}
if (isPosterDeprecated) {
return !!poster;
}
return !!poster?.source;
}, [isPosterDeprecated, poster, _renderLoader]);
const [showPoster, setShowPoster] = useState(hasPoster);
const [
_restoreUserInterfaceForPIPStopCompletionHandler,
setRestoreUserInterfaceForPIPStopCompletionHandler,
] = useState<boolean | undefined>();
const hasPoster = !!poster;
const sourceToUnternalSource = useCallback(
(_source?: ReactVideoSource) => {
if (!_source) {
return undefined;
}
const resolvedSource = resolveAssetSourceForVideo(_source);
let uri = resolvedSource.uri || '';
if (uri && uri.match(/^\//)) {
uri = `file://${uri}`;
}
if (!uri) {
console.log('Trying to load empty source');
}
const isNetwork = !!(uri && uri.match(/^(rtp|rtsp|http|https):/));
const isAsset = !!(
uri &&
uri.match(
/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/,
)
);
const posterStyle = useMemo<StyleProp<ImageStyle>>(
() => ({
...StyleSheet.absoluteFillObject,
resizeMode:
posterResizeMode && posterResizeMode !== 'none'
? posterResizeMode
: 'contain',
}),
[posterResizeMode],
const selectedDrm = _source.drm || drm;
const _textTracks = _source.textTracks || textTracks;
const _drm = !selectedDrm
? undefined
: {
type: selectedDrm.type,
licenseServer: selectedDrm.licenseServer,
headers: generateHeaderForNative(selectedDrm.headers),
contentId: selectedDrm.contentId,
certificateUrl: selectedDrm.certificateUrl,
base64Certificate: selectedDrm.base64Certificate,
useExternalGetLicense: !!selectedDrm.getLicense,
multiDrm: selectedDrm.multiDrm,
localSourceEncryptionKeyScheme:
selectedDrm.localSourceEncryptionKeyScheme ||
localSourceEncryptionKeyScheme,
};
let _cmcd: NativeCmcdConfiguration | undefined;
if (Platform.OS === 'android' && source?.cmcd) {
const cmcd = source.cmcd;
if (typeof cmcd === 'boolean') {
_cmcd = cmcd ? {mode: CmcdMode.MODE_QUERY_PARAMETER} : undefined;
} else if (typeof cmcd === 'object' && !Array.isArray(cmcd)) {
const createCmcdHeader = (property?: CmcdData) =>
property ? generateHeaderForNative(property) : undefined;
_cmcd = {
mode: cmcd.mode ?? CmcdMode.MODE_QUERY_PARAMETER,
request: createCmcdHeader(cmcd.request),
session: createCmcdHeader(cmcd.session),
object: createCmcdHeader(cmcd.object),
status: createCmcdHeader(cmcd.status),
};
} else {
throw new Error(
'Invalid CMCD configuration: Expected a boolean or an object.',
);
}
}
const selectedContentStartTime =
_source.contentStartTime || contentStartTime;
const _ad =
_source.ad ||
(adTagUrl || adLanguage
? {adTagUrl: adTagUrl, adLanguage: adLanguage}
: undefined);
return {
uri,
isNetwork,
isAsset,
shouldCache: resolvedSource.shouldCache || false,
type: resolvedSource.type || '',
mainVer: resolvedSource.mainVer || 0,
patchVer: resolvedSource.patchVer || 0,
requestHeaders: generateHeaderForNative(resolvedSource.headers),
startPosition: resolvedSource.startPosition ?? -1,
cropStart: resolvedSource.cropStart || 0,
cropEnd: resolvedSource.cropEnd,
contentStartTime: selectedContentStartTime,
metadata: resolvedSource.metadata,
drm: _drm,
ad: _ad,
cmcd: _cmcd,
textTracks: _textTracks,
textTracksAllowChunklessPreparation:
resolvedSource.textTracksAllowChunklessPreparation,
};
},
[
adLanguage,
adTagUrl,
contentStartTime,
drm,
localSourceEncryptionKeyScheme,
source?.cmcd,
textTracks,
],
);
const src = useMemo<VideoSrc | undefined>(() => {
if (!source) {
return undefined;
}
const resolvedSource = resolveAssetSourceForVideo(source);
let uri = resolvedSource.uri || '';
if (uri && uri.match(/^\//)) {
uri = `file://${uri}`;
}
if (!uri) {
console.log('Trying to load empty source');
}
const isNetwork = !!(uri && uri.match(/^(rtp|rtsp|http|https):/));
const isAsset = !!(
uri &&
uri.match(
/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/,
)
);
const selectedDrm = source.drm || drm;
const _drm = !selectedDrm
? undefined
: {
type: selectedDrm.type,
licenseServer: selectedDrm.licenseServer,
headers: generateHeaderForNative(selectedDrm.headers),
contentId: selectedDrm.contentId,
certificateUrl: selectedDrm.certificateUrl,
base64Certificate: selectedDrm.base64Certificate,
useExternalGetLicense: !!selectedDrm.getLicense,
multiDrm: selectedDrm.multiDrm,
};
return {
uri,
isNetwork,
isAsset,
shouldCache: resolvedSource.shouldCache || false,
type: resolvedSource.type || '',
mainVer: resolvedSource.mainVer || 0,
patchVer: resolvedSource.patchVer || 0,
requestHeaders: generateHeaderForNative(resolvedSource.headers),
startPosition: resolvedSource.startPosition ?? -1,
cropStart: resolvedSource.cropStart || 0,
cropEnd: resolvedSource.cropEnd,
metadata: resolvedSource.metadata,
drm: _drm,
textTracksAllowChunklessPreparation:
resolvedSource.textTracksAllowChunklessPreparation,
};
}, [drm, source]);
return sourceToUnternalSource(source);
}, [sourceToUnternalSource, source]);
const _selectedTextTrack = useMemo(() => {
if (!selectedTextTrack) {
@@ -304,6 +389,16 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
);
}, []);
const setSource = useCallback(
(_source?: ReactVideoSource) => {
return NativeVideoManager.setSourceCmd(
getReactTag(nativeRef),
sourceToUnternalSource(_source),
);
},
[sourceToUnternalSource],
);
const presentFullscreenPlayer = useCallback(
() => setFullScreen(true),
[setFullScreen],
@@ -501,7 +596,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
[onControlsVisibilityChange],
);
const usingExternalGetLicense = drm?.getLicense instanceof Function;
const selectedDrm = source?.drm || drm;
const usingExternalGetLicense = selectedDrm?.getLicense instanceof Function;
const onGetLicense = useCallback(
async (event: NativeSyntheticEvent<OnGetLicenseData>) => {
@@ -509,33 +605,43 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
return;
}
const data = event.nativeEvent;
let result;
if (data?.spcBase64) {
try {
// Handles both scenarios, getLicenseOverride being a promise and not.
const license = await drm.getLicense(
try {
if (!data?.spcBase64) {
throw new Error('No spc received');
}
// Handles both scenarios, getLicenseOverride being a promise and not.
const license = await Promise.resolve(
selectedDrm.getLicense(
data.spcBase64,
data.contentId,
data.licenseUrl,
data.loadedLicenseUrl,
);
result =
typeof license === 'string' ? license : 'Empty license result';
} catch {
result = 'fetch error';
),
).catch(() => {
throw new Error('fetch error');
});
if (typeof license !== 'string') {
throw Error('Empty license result');
}
if (nativeRef.current) {
NativeVideoManager.setLicenseResultCmd(
getReactTag(nativeRef),
license,
data.loadedLicenseUrl,
);
}
} catch (e) {
const msg = e instanceof Error ? e.message : 'fetch error';
if (nativeRef.current) {
NativeVideoManager.setLicenseResultErrorCmd(
getReactTag(nativeRef),
msg,
data.loadedLicenseUrl,
);
}
} else {
result = 'No spc received';
}
if (nativeRef.current) {
NativeVideoManager.setLicenseResultErrorCmd(
getReactTag(nativeRef),
result,
data.loadedLicenseUrl,
);
}
},
[drm, usingExternalGetLicense],
[selectedDrm, usingExternalGetLicense],
);
useImperativeHandle(
@@ -551,6 +657,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
setVolume,
getCurrentPosition,
setFullScreen,
setSource,
}),
[
seek,
@@ -563,6 +670,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
setVolume,
getCurrentPosition,
setFullScreen,
setSource,
],
);
@@ -573,42 +681,120 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
const shallForceViewType =
hasValidDrmProp && (viewType === ViewType.TEXTURE || useTextureView);
if (shallForceViewType) {
console.warn(
'cannot use DRM on texture view. please set useTextureView={false}',
);
}
if (useSecureView && useTextureView) {
console.warn(
'cannot use SecureView on texture view. please set useTextureView={false}',
);
}
return shallForceViewType
? useSecureView
? ViewType.SURFACE_SECURE
: ViewType.SURFACE // check if we should force the type to Surface due to DRM
: viewType
? viewType // else use ViewType from source
: useSecureView // else infer view type from useSecureView and useTextureView
? ViewType.SURFACE_SECURE
: useTextureView
? ViewType.TEXTURE
: ViewType.SURFACE;
if (shallForceViewType) {
console.warn(
'cannot use DRM on texture view. please set useTextureView={false}',
);
return useSecureView ? ViewType.SURFACE_SECURE : ViewType.SURFACE;
}
if (viewType !== undefined && viewType !== null) {
return viewType;
}
if (useSecureView) {
return ViewType.SURFACE_SECURE;
}
if (useTextureView) {
return ViewType.TEXTURE;
}
return ViewType.SURFACE;
}, [drm, useSecureView, useTextureView, viewType]);
const _renderPoster = useCallback(() => {
if (!hasPoster || !showPoster) {
return null;
}
// poster resize mode
let _posterResizeMode: ImageResizeMode = 'contain';
if (!isPosterDeprecated && poster?.resizeMode) {
_posterResizeMode = poster.resizeMode;
} else if (posterResizeMode && posterResizeMode !== 'none') {
_posterResizeMode = posterResizeMode;
}
// poster style
const baseStyle: StyleProp<ImageStyle> = {
...StyleSheet.absoluteFillObject,
resizeMode: _posterResizeMode,
};
let posterStyle: StyleProp<ImageStyle> = baseStyle;
if (!isPosterDeprecated && poster?.style) {
const styles = Array.isArray(poster.style)
? poster.style
: [poster.style];
posterStyle = [baseStyle, ...styles];
}
// render poster
if (_renderLoader && (poster || posterResizeMode)) {
console.warn(
'You provided both `renderLoader` and `poster` or `posterResizeMode` props. `renderLoader` will be used.',
);
}
// render loader
if (_renderLoader) {
return (
<View style={StyleSheet.absoluteFill}>
{_renderLoader({
source: source,
style: posterStyle,
resizeMode: resizeMode,
})}
</View>
);
}
return (
<Image
{...(isPosterDeprecated ? {} : poster)}
source={isPosterDeprecated ? {uri: poster} : poster?.source}
style={posterStyle}
/>
);
}, [
hasPoster,
isPosterDeprecated,
poster,
posterResizeMode,
_renderLoader,
showPoster,
source,
resizeMode,
]);
const _style: StyleProp<ViewStyle> = useMemo(
() => ({
...StyleSheet.absoluteFillObject,
...(showPoster ? {display: 'none'} : {}),
}),
[showPoster],
);
return (
<View style={style}>
<NativeVideoComponent
ref={nativeRef}
{...rest}
src={src}
style={StyleSheet.absoluteFill}
style={_style}
resizeMode={resizeMode}
restoreUserInterfaceForPIPStopCompletionHandler={
_restoreUserInterfaceForPIPStopCompletionHandler
}
textTracks={textTracks}
selectedTextTrack={_selectedTextTrack}
selectedAudioTrack={_selectedAudioTrack}
selectedVideoTrack={_selectedVideoTrack}
@@ -678,9 +864,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
}
viewType={_viewType}
/>
{hasPoster && showPoster ? (
<Image style={posterStyle} source={{uri: poster}} />
) : null}
{_renderPoster()}
</View>
);
},

View File

@@ -236,6 +236,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
controls={controls}
loop={repeat}
playsInline
//@ts-ignore
poster={poster}
onCanPlay={() => onBuffer?.({isBuffering: false})}
onWaiting={() => onBuffer?.({isBuffering: true})}

View File

@@ -13,7 +13,7 @@ export const withBackgroundAudio: ConfigPlugin<boolean> = (
if (enableBackgroundAudio) {
if (!modes.includes('audio')) {
modes.push('audio');
config.modResults.UIBackgroundModes = [...modes, 'audio'];
}
} else {
config.modResults.UIBackgroundModes = modes.filter(

View File

@@ -24,6 +24,19 @@ export const withNotificationControls: ConfigPlugin<boolean> = (
application.service = [];
}
// We check if the VideoPlaybackService is already defined in the AndroidManifest.xml
// to prevent adding duplicate service entries. If the service exists, we will remove
// it before adding the updated configuration to ensure there are no conflicts or redundant
// service declarations in the manifest.
const existingServiceIndex = application.service.findIndex(
(service) =>
service?.$?.['android:name'] ===
'com.brentvatne.exoplayer.VideoPlaybackService',
);
if (existingServiceIndex !== -1) {
application.service.splice(existingServiceIndex, 1);
}
application.service.push({
$: {
'android:name': 'com.brentvatne.exoplayer.VideoPlaybackService',

View File

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

View File

@@ -21,6 +21,7 @@ export interface VideoManagerType {
licenseUrl: string,
) => Promise<void>;
setFullScreenCmd: (reactTag: Int32, fullScreen: boolean) => Promise<void>;
setSourceCmd: (reactTag: Int32, source?: UnsafeObject) => Promise<void>;
setVolumeCmd: (reactTag: Int32, volume: number) => Promise<void>;
save: (reactTag: Int32, option: UnsafeObject) => Promise<VideoSaveData>;
getCurrentPosition: (reactTag: Int32) => Promise<Int32>;

View File

@@ -26,6 +26,11 @@ type VideoMetadata = Readonly<{
imageUri?: string;
}>;
export type AdsConfig = Readonly<{
adTagUrl?: string;
adLanguage?: string;
}>;
export type VideoSrc = Readonly<{
uri?: string;
isNetwork?: boolean;
@@ -38,9 +43,13 @@ export type VideoSrc = Readonly<{
startPosition?: Float;
cropStart?: Float;
cropEnd?: Float;
contentStartTime?: Int32; // Android
metadata?: VideoMetadata;
drm?: Drm;
cmcd?: NativeCmcdConfiguration; // android
textTracksAllowChunklessPreparation?: boolean; // android
textTracks?: TextTracks;
ad?: AdsConfig;
}>;
type DRMType = WithDefault<string, 'widevine'>;
@@ -59,6 +68,16 @@ type Drm = Readonly<{
base64Certificate?: boolean; // ios default: false
useExternalGetLicense?: boolean; // ios
multiDrm?: WithDefault<boolean, false>; // android
localSourceEncryptionKeyScheme?: string; // ios
}>;
type CmcdMode = WithDefault<Int32, 1>;
export type NativeCmcdConfiguration = Readonly<{
mode?: CmcdMode; // default: MODE_QUERY_PARAMETER
request?: Headers;
session?: Headers;
object?: Headers;
status?: Headers;
}>;
type TextTracks = ReadonlyArray<
@@ -121,6 +140,7 @@ type SubtitleStyle = Readonly<{
paddingLeft?: WithDefault<Float, 0>;
paddingRight?: WithDefault<Float, 0>;
opacity?: WithDefault<Float, 1>;
subtitlesFollowVideo?: WithDefault<boolean, true>;
}>;
type OnLoadData = Readonly<{
@@ -283,8 +303,20 @@ export type OnAudioFocusChangedData = Readonly<{
}>;
type ControlsStyles = Readonly<{
hideSeekBar?: boolean;
hidePosition?: WithDefault<boolean, false>;
hidePlayPause?: WithDefault<boolean, false>;
hideForward?: WithDefault<boolean, false>;
hideRewind?: WithDefault<boolean, false>;
hideNext?: WithDefault<boolean, false>;
hidePrevious?: WithDefault<boolean, false>;
hideFullscreen?: WithDefault<boolean, false>;
hideSeekBar?: WithDefault<boolean, false>;
hideDuration?: WithDefault<boolean, false>;
hideNavigationBarOnFullScreenMode?: WithDefault<boolean, true>;
hideNotificationBarOnFullScreenMode?: WithDefault<boolean, true>;
hideSettingButton?: WithDefault<boolean, true>;
seekIncrementMS?: Int32;
liveLabel?: string;
}>;
export type OnControlsVisibilityChange = Readonly<{
@@ -293,7 +325,6 @@ export type OnControlsVisibilityChange = Readonly<{
export interface VideoNativeProps extends ViewProps {
src?: VideoSrc;
adTagUrl?: string;
allowsExternalPlayback?: boolean; // ios, true
disableFocus?: boolean; // android
maxBitRate?: Float;
@@ -302,7 +333,6 @@ export interface VideoNativeProps extends ViewProps {
automaticallyWaitsToMinimizeStalling?: boolean;
shutterColor?: Int32;
audioOutput?: WithDefault<string, 'speaker'>;
textTracks?: TextTracks;
selectedTextTrack?: SelectedTextTrack;
selectedAudioTrack?: SelectedAudioTrack;
selectedVideoTrack?: SelectedVideoTrack; // android
@@ -325,11 +355,9 @@ export interface VideoNativeProps extends ViewProps {
fullscreenOrientation?: WithDefault<string, 'all'>;
progressUpdateInterval?: Float;
restoreUserInterfaceForPIPStopCompletionHandler?: boolean;
localSourceEncryptionKeyScheme?: string;
debug?: DebugConfig;
showNotificationControls?: WithDefault<boolean, false>; // Android, iOS
bufferConfig?: BufferConfig; // Android
contentStartTime?: Int32; // Android
currentPlaybackTime?: Double; // Android
disableDisconnectError?: boolean; // Android
focusable?: boolean; // Android

View File

@@ -1,6 +1,15 @@
import type {ISO639_1} from './language';
import type {ReactVideoEvents} from './events';
import type {StyleProp, ViewProps, ViewStyle} from 'react-native';
import type {
ImageProps,
StyleProp,
ViewProps,
ViewStyle,
ImageRequireSource,
ImageURISource,
ImageStyle,
} from 'react-native';
import type {ReactNode} from 'react';
import type VideoResizeMode from './ResizeMode';
import type FilterType from './FilterType';
import type ViewType from './ViewType';
@@ -23,9 +32,13 @@ export type ReactVideoSourceProperties = {
startPosition?: number;
cropStart?: number;
cropEnd?: number;
contentStartTime?: number; // Android
metadata?: VideoMetadata;
drm?: Drm;
cmcd?: Cmcd; // android
textTracksAllowChunklessPreparation?: boolean;
textTracks?: TextTracks;
ad?: AdConfig;
};
export type ReactVideoSource = Readonly<
@@ -34,6 +47,13 @@ export type ReactVideoSource = Readonly<
}
>;
export type ReactVideoPosterSource = ImageURISource | ImageRequireSource;
export type ReactVideoPoster = Omit<ImageProps, 'source'> & {
// prevents giving source in the array
source?: ReactVideoPosterSource;
};
export type VideoMetadata = Readonly<{
title?: string;
subtitle?: string;
@@ -54,6 +74,11 @@ export enum DRMType {
FAIRPLAY = 'fairplay',
}
export type AdConfig = Readonly<{
adTagUrl?: string;
adLanguage?: ISO639_1;
}>;
export type Drm = Readonly<{
type?: DRMType;
licenseServer?: string;
@@ -62,6 +87,7 @@ export type Drm = Readonly<{
certificateUrl?: string; // ios
base64Certificate?: boolean; // ios default: false
multiDrm?: boolean; // android
localSourceEncryptionKeyScheme?: string; // ios
/* eslint-disable @typescript-eslint/no-unused-vars */
getLicense?: (
spcBase64: string,
@@ -72,6 +98,27 @@ export type Drm = Readonly<{
/* eslint-enable @typescript-eslint/no-unused-vars */
}>;
export enum CmcdMode {
MODE_REQUEST_HEADER = 0,
MODE_QUERY_PARAMETER = 1,
}
/**
* Custom key names MUST carry a hyphenated prefix to ensure that there will not be a
* namespace collision with future revisions to this specification. Clients SHOULD
* use a reverse-DNS syntax when defining their own prefix.
*
* @see https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf CTA-5004 Specification (Page 6, Section 3.1)
*/
export type CmcdData = Record<`${string}-${string}`, string | number>;
export type CmcdConfiguration = Readonly<{
mode?: CmcdMode; // default: MODE_QUERY_PARAMETER
request?: CmcdData;
session?: CmcdData;
object?: CmcdData;
status?: CmcdData;
}>;
export type Cmcd = boolean | CmcdConfiguration;
export enum BufferingStrategyType {
DEFAULT = 'Default',
DISABLE_BUFFERING = 'DisableBuffering',
@@ -131,6 +178,7 @@ export type SubtitleStyle = {
paddingLeft?: number;
paddingRight?: number;
opacity?: number;
subtitlesFollowVideo?: boolean;
};
export enum TextTrackType {
@@ -208,20 +256,42 @@ export type AudioOutput = 'speaker' | 'earpiece';
export type ControlsStyles = {
hideSeekBar?: boolean;
hideDuration?: boolean;
hidePosition?: boolean;
hidePlayPause?: boolean;
hideForward?: boolean;
hideRewind?: boolean;
hideNext?: boolean;
hidePrevious?: boolean;
hideFullscreen?: boolean;
hideNavigationBarOnFullScreenMode?: boolean;
hideNotificationBarOnFullScreenMode?: boolean;
hideSettingButton?: boolean;
seekIncrementMS?: number;
liveLabel?: string;
};
export interface ReactVideoRenderLoaderProps {
source?: ReactVideoSource;
style?: StyleProp<ImageStyle>;
resizeMode?: EnumValues<VideoResizeMode>;
}
export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
source?: ReactVideoSource;
/** @deprecated */
/** @deprecated Use source.drm */
drm?: Drm;
style?: StyleProp<ViewStyle>;
/** @deprecated Use source.ad.adTagUrl */
adTagUrl?: string;
/** @deprecated Use source.ad.adLanguage */
adLanguage?: ISO639_1;
audioOutput?: AudioOutput; // Mobile
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
bufferConfig?: BufferConfig; // Android
bufferingStrategy?: BufferingStrategyType;
chapters?: Chapters[]; // iOS
/** @deprecated Use source.contentStartTime */
contentStartTime?: number; // Android
controls?: boolean;
currentPlaybackTime?: number; // Android
@@ -243,12 +313,14 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
pictureInPicture?: boolean; // iOS
playInBackground?: boolean;
playWhenInactive?: boolean; // iOS
poster?: string;
poster?: string | ReactVideoPoster; // string is deprecated
/** @deprecated use **resizeMode** key in **poster** props instead */
posterResizeMode?: EnumValues<PosterResizeModeType>;
preferredForwardBufferDuration?: number; // iOS
preventsDisplaySleepDuringVideoPlayback?: boolean;
progressUpdateInterval?: number;
rate?: number;
renderLoader?: ReactNode | ((arg0: ReactVideoRenderLoaderProps) => ReactNode);
repeat?: boolean;
reportBandwidth?: boolean; //Android
resizeMode?: EnumValues<VideoResizeMode>;
@@ -258,14 +330,16 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
selectedVideoTrack?: SelectedVideoTrack; // android
subtitleStyle?: SubtitleStyle; // android
shutterColor?: string; // Android
/** @deprecated Use source.textTracks */
textTracks?: TextTracks;
testID?: string;
viewType?: ViewType;
/** @deprecated */
/** @deprecated Use viewType */
useTextureView?: boolean; // Android
/** @deprecated */
/** @deprecated Use viewType*/
useSecureView?: boolean; // Android
volume?: number;
/** @deprecated use **localSourceEncryptionKeyScheme** key in **drm** props instead */
localSourceEncryptionKeyScheme?: string;
debug?: DebugConfig;
allowsExternalPlayback?: boolean; // iOS