chore(android): rework view type (#3940)

This commit is contained in:
Olivier Bouillet 2024-06-27 11:58:06 +02:00 committed by GitHub
parent 6e1337689a
commit b431d09e2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 136 additions and 64 deletions

View File

@ -0,0 +1,19 @@
package com.brentvatne.common.api
internal object ViewType {
/**
* View used will be a TextureView.
*/
const val VIEW_TYPE_TEXTURE = 0
/**
* View used will be a SurfaceView.
*/
const val VIEW_TYPE_SURFACE = 1
/**
* View used will be a SurfaceView with secure flag set.
*/
const val VIEW_TYPE_SURFACE_SECURE = 2
annotation class ViewType
}

View File

@ -26,12 +26,14 @@ import android.widget.FrameLayout;
import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.ResizeMode;
import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.api.SubtitleStyle;
import com.brentvatne.common.api.ViewType;
import com.brentvatne.common.toolbox.DebugLog;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
public final class ExoPlayerView extends FrameLayout implements AdViewProvider { public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
private final static String TAG = "ExoPlayerView";
private View surfaceView; private View surfaceView;
private final View shutterView; private final View shutterView;
private final SubtitleView subtitleLayout; private final SubtitleView subtitleLayout;
@ -42,8 +44,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
private final ViewGroup.LayoutParams layoutParams; private final ViewGroup.LayoutParams layoutParams;
private final FrameLayout adOverlayFrameLayout; private final FrameLayout adOverlayFrameLayout;
private boolean useTextureView = true; private @ViewType.ViewType int viewType = ViewType.VIEW_TYPE_SURFACE;
private boolean useSecureView = false;
private boolean hideShutterView = false; private boolean hideShutterView = false;
public ExoPlayerView(Context context) { public ExoPlayerView(Context context) {
@ -81,7 +82,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
subtitleLayout.setUserDefaultStyle(); subtitleLayout.setUserDefaultStyle();
subtitleLayout.setUserDefaultTextSize(); subtitleLayout.setUserDefaultTextSize();
updateSurfaceView(); updateSurfaceView(viewType);
adOverlayFrameLayout = new FrameLayout(context); adOverlayFrameLayout = new FrameLayout(context);
@ -134,21 +135,28 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
shutterView.setBackgroundColor(color); shutterView.setBackgroundColor(color);
} }
private void updateSurfaceView() { public void updateSurfaceView(@ViewType.ViewType int viewType) {
View view; this.viewType = viewType;
if (!useTextureView || useSecureView) { boolean viewNeedRefresh = false;
view = new SurfaceView(context); if (viewType == ViewType.VIEW_TYPE_SURFACE || viewType == ViewType.VIEW_TYPE_SURFACE_SECURE) {
if (useSecureView) { if (!(surfaceView instanceof SurfaceView)) {
((SurfaceView)view).setSecure(true); surfaceView = new SurfaceView(context);
viewNeedRefresh = true;
}
((SurfaceView)surfaceView).setSecure(viewType == ViewType.VIEW_TYPE_SURFACE_SECURE);
} else if (viewType == ViewType.VIEW_TYPE_TEXTURE) {
if (!(surfaceView instanceof TextureView)) {
surfaceView = new TextureView(context);
viewNeedRefresh = true;
} }
} else {
view = new TextureView(context);
// Support opacity properly: // Support opacity properly:
((TextureView) view).setOpaque(false); ((TextureView) surfaceView).setOpaque(false);
} else {
DebugLog.wtf(TAG, "wtf is this texture " + viewType);
} }
view.setLayoutParams(layoutParams); if (viewNeedRefresh) {
surfaceView.setLayoutParams(layoutParams);
surfaceView = view;
if (layout.getChildAt(0) != null) { if (layout.getChildAt(0) != null) {
layout.removeViewAt(0); layout.removeViewAt(0);
} }
@ -158,6 +166,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
setVideoView(); setVideoView();
} }
} }
}
private void updateShutterViewVisibility() { private void updateShutterViewVisibility() {
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE); shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
@ -211,20 +220,6 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
} }
} }
public void setUseTextureView(boolean useTextureView) {
if (useTextureView != this.useTextureView) {
this.useTextureView = useTextureView;
updateSurfaceView();
}
}
public void useSecureView(boolean useSecureView) {
if (useSecureView != this.useSecureView) {
this.useSecureView = useSecureView;
updateSurfaceView();
}
}
public void setHideShutterView(boolean hideShutterView) { public void setHideShutterView(boolean hideShutterView) {
this.hideShutterView = hideShutterView; this.hideShutterView = hideShutterView;
updateShutterViewVisibility(); updateShutterViewVisibility();

View File

@ -589,6 +589,10 @@ public class ReactExoplayerView extends FrameLayout implements
} }
} }
public void setViewType(int viewType) {
exoPlayerView.updateSurfaceView(viewType);
}
private class RNVLoadControl extends DefaultLoadControl { private class RNVLoadControl extends DefaultLoadControl {
private final int availableHeapInBytes; private final int availableHeapInBytes;
private final Runtime runtime; private final Runtime runtime;
@ -2243,15 +2247,6 @@ public class ReactExoplayerView extends FrameLayout implements
updateFullScreenButtonVisibility(); updateFullScreenButtonVisibility();
} }
public void setUseTextureView(boolean useTextureView) {
boolean finallyUseTextureView = useTextureView && drmProps == null;
exoPlayerView.setUseTextureView(finallyUseTextureView);
}
public void useSecureView(boolean useSecureView) {
exoPlayerView.useSecureView(useSecureView);
}
public void setHideShutterView(boolean hideShutterView) { public void setHideShutterView(boolean hideShutterView) {
exoPlayerView.setHideShutterView(hideShutterView); exoPlayerView.setHideShutterView(hideShutterView);
} }

View File

@ -17,6 +17,7 @@ import com.brentvatne.common.api.ResizeMode;
import com.brentvatne.common.api.SideLoadedTextTrackList; import com.brentvatne.common.api.SideLoadedTextTrackList;
import com.brentvatne.common.api.Source; import com.brentvatne.common.api.Source;
import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.api.SubtitleStyle;
import com.brentvatne.common.api.ViewType;
import com.brentvatne.common.react.VideoEventEmitter; import com.brentvatne.common.react.VideoEventEmitter;
import com.brentvatne.common.toolbox.DebugLog; import com.brentvatne.common.toolbox.DebugLog;
import com.brentvatne.common.toolbox.ReactBridgeUtils; import com.brentvatne.common.toolbox.ReactBridgeUtils;
@ -69,8 +70,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_DISABLE_DISCONNECT_ERROR = "disableDisconnectError"; private static final String PROP_DISABLE_DISCONNECT_ERROR = "disableDisconnectError";
private static final String PROP_FOCUSABLE = "focusable"; private static final String PROP_FOCUSABLE = "focusable";
private static final String PROP_FULLSCREEN = "fullscreen"; private static final String PROP_FULLSCREEN = "fullscreen";
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView"; private static final String PROP_VIEW_TYPE = "viewType";
private static final String PROP_SECURE_VIEW = "useSecureView";
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack"; private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type"; private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type";
private static final String PROP_SELECTED_VIDEO_TRACK_VALUE = "value"; private static final String PROP_SELECTED_VIDEO_TRACK_VALUE = "value";
@ -121,7 +121,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
public void setDRM(final ReactExoplayerView videoView, @Nullable ReadableMap drm) { public void setDRM(final ReactExoplayerView videoView, @Nullable ReadableMap drm) {
DRMProps drmProps = DRMProps.parse(drm); DRMProps drmProps = DRMProps.parse(drm);
videoView.setDrm(drmProps); videoView.setDrm(drmProps);
videoView.setUseTextureView(false);
} }
@ReactProp(name = PROP_SRC) @ReactProp(name = PROP_SRC)
@ -301,14 +300,9 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
videoView.setFullscreen(fullscreen); videoView.setFullscreen(fullscreen);
} }
@ReactProp(name = PROP_USE_TEXTURE_VIEW, defaultBoolean = true) @ReactProp(name = PROP_VIEW_TYPE, defaultInt = ViewType.VIEW_TYPE_SURFACE)
public void setUseTextureView(final ReactExoplayerView videoView, final boolean useTextureView) { public void setViewType(final ReactExoplayerView videoView, final int viewType) {
videoView.setUseTextureView(useTextureView); videoView.setViewType(viewType);
}
@ReactProp(name = PROP_SECURE_VIEW, defaultBoolean = true)
public void useSecureView(final ReactExoplayerView videoView, final boolean useSecureView) {
videoView.useSecureView(useSecureView);
} }
@ReactProp(name = PROP_HIDE_SHUTTER_VIEW, defaultBoolean = false) @ReactProp(name = PROP_HIDE_SHUTTER_VIEW, defaultBoolean = false)

View File

@ -888,6 +888,9 @@ To customize the notification controls you can use `metadata` property in the `s
### `useSecureView` ### `useSecureView`
> [!WARNING]
> deprecated, use viewType instead
<PlatformsList types={['Android']} /> <PlatformsList types={['Android']} />
Force the output to a SurfaceView and enables the secure surface. Force the output to a SurfaceView and enables the secure surface.
@ -899,8 +902,13 @@ SurfaceView is is the only one that can be labeled as secure.
- **true** - Use security - **true** - Use security
- **false (default)** - Do not use security - **false (default)** - Do not use security
### `useTextureView` ### `useTextureView`
> [!WARNING]
> deprecated, use viewType instead
<PlatformsList types={['Android']} /> <PlatformsList types={['Android']} />
Controls whether to output to a TextureView or SurfaceView. Controls whether to output to a TextureView or SurfaceView.
@ -915,6 +923,18 @@ useTextureView can only be set at same time you're setting the source.
- **true (default)** - Use a TextureView - **true (default)** - Use a TextureView
- **false** - Use a SurfaceView - **false** - Use a SurfaceView
### `viewType`
<PlatformsList types={['Android']} />
Allow to explicitly specify view type.
This flag replace `useSecureView` and `useTextureView` fields.
There are 3 available values:
- 'textureView': The video is rendered in a texture view. it allows mapping the view on a texture (useful for 3D).
DRM playback is not supported on textureView, if drm prop is provided, the suface will be transformed to a SurfaceView.
- 'surfaceView' (default): The video is rendered in a surface. take less resources to be rendered.
- 'secureView': The video is rendered in a surface which disallow taking screenshot of the video
### `volume` ### `volume`
<PlatformsList types={['All']} /> <PlatformsList types={['All']} />

View File

@ -44,11 +44,12 @@ import {
resolveAssetSourceForVideo, resolveAssetSourceForVideo,
} from './utils'; } from './utils';
import {VideoManager} from './specs/VideoNativeComponent'; import {VideoManager} from './specs/VideoNativeComponent';
import type { import {
OnLoadData, type OnLoadData,
OnTextTracksData, type OnTextTracksData,
OnReceiveAdEventData, type OnReceiveAdEventData,
ReactVideoProps, type ReactVideoProps,
ViewType,
} from './types'; } from './types';
export type VideoSaveData = { export type VideoSaveData = {
@ -84,6 +85,9 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
selectedVideoTrack, selectedVideoTrack,
selectedAudioTrack, selectedAudioTrack,
selectedTextTrack, selectedTextTrack,
useTextureView,
useSecureView,
viewType,
onLoadStart, onLoadStart,
onLoad, onLoad,
onError, onError,
@ -571,6 +575,37 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
], ],
); );
const _viewType = useMemo(() => {
const hasValidDrmProp =
drm !== undefined && Object.keys(drm).length !== 0;
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;
}, [drm, useSecureView, useTextureView, viewType]);
return ( return (
<View style={style}> <View style={style}>
<NativeVideoComponent <NativeVideoComponent
@ -651,6 +686,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
onControlsVisibilityChange={ onControlsVisibilityChange={
onControlsVisibilityChange ? _onControlsVisibilityChange : undefined onControlsVisibilityChange ? _onControlsVisibilityChange : undefined
} }
viewType={_viewType}
/> />
{hasPoster && showPoster ? ( {hasPoster && showPoster ? (
<Image style={posterStyle} source={{uri: poster}} /> <Image style={posterStyle} source={{uri: poster}} />

View File

@ -337,8 +337,7 @@ export interface VideoNativeProps extends ViewProps {
minLoadRetryCount?: Int32; // Android minLoadRetryCount?: Int32; // Android
reportBandwidth?: boolean; //Android reportBandwidth?: boolean; //Android
subtitleStyle?: SubtitleStyle; // android subtitleStyle?: SubtitleStyle; // android
useTextureView?: boolean; // Android viewType?: Int32; // Android
useSecureView?: boolean; // Android
bufferingStrategy?: BufferingStrategyType; // Android bufferingStrategy?: BufferingStrategyType; // Android
controlsStyles?: ControlsStyles; // Android controlsStyles?: ControlsStyles; // Android
onControlsVisibilityChange?: DirectEventHandler<OnControlsVisibilityChange>; onControlsVisibilityChange?: DirectEventHandler<OnControlsVisibilityChange>;

11
src/types/ViewType.ts Normal file
View File

@ -0,0 +1,11 @@
/**
* Define Available view type for android
* these values shall match android spec, see ViewType.kt
*/
enum ResizeMode {
TEXTURE = 0,
SURFACE = 1,
SURFACE_SECURE = 2,
}
export default ResizeMode;

View File

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

View File

@ -3,6 +3,7 @@ import type {ReactVideoEvents} from './events';
import type {StyleProp, ViewProps, ViewStyle} from 'react-native'; import type {StyleProp, ViewProps, ViewStyle} from 'react-native';
import type VideoResizeMode from './ResizeMode'; import type VideoResizeMode from './ResizeMode';
import type FilterType from './FilterType'; import type FilterType from './FilterType';
import type ViewType from './ViewType';
export type Headers = Record<string, string>; export type Headers = Record<string, string>;
@ -256,8 +257,9 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
shutterColor?: string; // Android shutterColor?: string; // Android
textTracks?: TextTracks; textTracks?: TextTracks;
testID?: string; testID?: string;
useTextureView?: boolean; // Android viewType?: ViewType;
useSecureView?: boolean; // Android useTextureView?: boolean; // Android // deprecated
useSecureView?: boolean; // Android // deprecated
volume?: number; volume?: number;
localSourceEncryptionKeyScheme?: string; localSourceEncryptionKeyScheme?: string;
debug?: DebugConfig; debug?: DebugConfig;