Skip to content

Commit

Permalink
Merge pull request #343 from THEOplayer/feature/android_rendering_target
Browse files Browse the repository at this point in the history
Feature/android rendering target
  • Loading branch information
tvanlaerhoven authored Jun 27, 2024
2 parents 2119f01 + 03715b9 commit b5bc69a
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 125 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added

- Added `'root'` as an alternative id for the root div element in Web applications, supporting fullscreen presentation mode in Expo-based apps.
- Added `renderingTarget` property to `THEOplayer` API for Android, enabling the option to choose between either rendering to a `SurfaceView` (default) or `TextureView`.

## [7.5.1] - 24-06-20

Expand Down
11 changes: 11 additions & 0 deletions android/src/main/java/com/theoplayer/player/PlayerModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.theoplayer.abr.ABRConfigurationAdapter
import com.theoplayer.android.api.player.AspectRatio
import com.theoplayer.android.api.player.PreloadType
import com.theoplayer.android.api.player.PresentationMode
import com.theoplayer.android.api.player.RenderingTarget
import com.theoplayer.android.api.player.track.mediatrack.MediaTrack
import com.theoplayer.android.api.player.track.mediatrack.quality.VideoQuality
import com.theoplayer.android.api.player.track.texttrack.TextTrackMode
Expand Down Expand Up @@ -228,4 +229,14 @@ class PlayerModule(context: ReactApplicationContext) : ReactContextBaseJavaModul
}
}
}

@ReactMethod
fun setRenderingTarget(tag: Int, target: String) {
viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? ->
view?.player?.setRenderingTarget(when (target) {
"textureView" -> RenderingTarget.TEXTURE_VIEW
else -> RenderingTarget.SURFACE_VIEW
})
}
}
}
2 changes: 2 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { PiPSubMenu } from './custom/PipSubMenu';
import { MediaCacheDownloadButton } from './custom/MediaCacheDownloadButton';
import { MediaCacheMenuButton } from './custom/MediaCacheMenuButton';
import { MediaCachingTaskListSubMenu } from './custom/MediaCachingTaskListSubMenu';
import { RenderingTargetSubMenu } from './custom/RenderingTargetSubMenu';

const playerConfig: PlayerConfiguration = {
// Get your THEOplayer license from https://portal.theoplayer.com/
Expand Down Expand Up @@ -125,6 +126,7 @@ export default function App() {
<PlaybackRateSubMenu />
<BackgroundAudioSubMenu />
<PiPSubMenu />
{Platform.OS === 'android' && <RenderingTargetSubMenu />}
</SettingsMenuButton>
</ControlBar>
}
Expand Down
72 changes: 16 additions & 56 deletions example/src/custom/BackgroundAudioSubMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useContext, useState } from 'react';
import { CustomSubMenu, Option } from './CustomSubMenu';
import * as React from 'react';
import type { StyleProp, ViewStyle } from 'react-native';
import { MenuRadioButton, MenuView, PlayerContext, ScrollableMenu, SubMenuWithButton } from '@theoplayer/react-native-ui';
import { useContext } from 'react';
import { PlayerContext } from '@theoplayer/react-native-ui';

export interface BackgroundAudioSubMenuProps {
/**
Expand All @@ -9,61 +11,19 @@ export interface BackgroundAudioSubMenuProps {
menuStyle?: StyleProp<ViewStyle>;
}

interface Option {
label: string;
value: boolean;
}

const OptionDisabled = { label: 'Disabled', value: false };
const OptionEnabled = { label: 'Enabled', value: true };
const options: Option[] = [OptionDisabled, OptionEnabled];

/**
* A button component that opens a backgroundAudio selection menu for the `react-native-theoplayer` UI.
*/
export const BackgroundAudioSubMenu = (props: BackgroundAudioSubMenuProps) => {
const { menuStyle } = props;
const createMenu = () => {
return <BackgroundAudioSelectionView style={menuStyle} />;
};
const player = useContext(PlayerContext).player;
const selectedLabel = player.backgroundAudioConfiguration.enabled ? OptionEnabled.label : OptionDisabled.label;
return <SubMenuWithButton menuConstructor={createMenu} label={'Bg Audio'} preview={selectedLabel} />;
};

export interface BackgroundAudioSelectionViewProps {
style?: StyleProp<ViewStyle>;
export const BackgroundAudioSubMenu = (props?: BackgroundAudioSubMenuProps) => {
const ctx = useContext(PlayerContext);

return <CustomSubMenu
title={'Background Audio'}
menuStyle={props?.menuStyle}
label={'Bg Audio'}
options={[{ label: 'Disabled', value: false }, { label: 'Enabled', value: true }]}
onOptionSelected={(option: Option<boolean>) => {
ctx.player.backgroundAudioConfiguration = { enabled: option.value };
}}
currentOption={() => ctx.player.backgroundAudioConfiguration.enabled ?? false} />;
}

const BackgroundAudioSelectionView = (props: BackgroundAudioSelectionViewProps) => {
const { style } = props;
const player = useContext(PlayerContext).player;

const [selected, setSelected] = useState<Option>(player.backgroundAudioConfiguration.enabled ? OptionEnabled : OptionDisabled);

const onSelectOption = (id: number | undefined): void => {
if (id !== undefined) {
player.backgroundAudioConfiguration = { enabled: options[id].value };
setSelected(options[id]);
}
};

return (
<MenuView
style={style}
menu={
<ScrollableMenu
title={'Background Audio'}
items={options.map((option, id) => (
<MenuRadioButton
key={id}
label={option.label}
uid={id}
onSelect={onSelectOption}
selected={selected.value === option.value}></MenuRadioButton>
))}
/>
}
/>
);
};
87 changes: 87 additions & 0 deletions example/src/custom/CustomSubMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useState } from 'react';
import type { StyleProp, ViewStyle } from 'react-native';
import { MenuRadioButton, MenuView, ScrollableMenu, SubMenuWithButton } from '@theoplayer/react-native-ui';

export interface Option<T> {
label: string;
value: T;
}

export interface CustomSubMenuProps<T> {
/**
* Overrides for the style of the menu.
*/
menuStyle?: StyleProp<ViewStyle>;

/**
* The label displayed in the menu.
*/
label: string;

/**
* The title displayed when opening the menu.
*/
title: string;

/**
* List of available options.
*/
options: Option<T>[];

/**
* Get the current active option.
*/
currentOption: () => T;

/**
* Called when a new option was selected.
*/
onOptionSelected?: (option: Option<T>) => void;
}

/**
* A button component that opens a custom selection menu for the `react-native-theoplayer` UI.
*/
export function CustomSubMenu<T>(props: CustomSubMenuProps<T>) {
const { options, label } = props;
const currentLabel = options.find(v => v.value == props.currentOption())?.label ?? "";
const createMenu = () => {
return <CustomSelectionView {...props}/>;
};
return <SubMenuWithButton menuConstructor={createMenu} label={label} preview={currentLabel} />;
}

function CustomSelectionView<T>(props: CustomSubMenuProps<T>) {
const { menuStyle, options } = props;
const currentOption = options.find(v => v.value == props.currentOption());
const [selectedOption, setSelectedOption] = useState<Option<T> | undefined>(currentOption);

const onSelectOption = (id: number | undefined): void => {
if (id !== undefined) {
const newSelection = id < options.length ? options[id] : undefined;
if (newSelection) {
setSelectedOption(newSelection);
props.onOptionSelected?.(newSelection);
}
}
};

return (
<MenuView
style={menuStyle}
menu={
<ScrollableMenu
title={props.title}
items={options.map((option, id) => (
<MenuRadioButton
key={id}
label={option.label}
uid={id}
onSelect={onSelectOption}
selected={selectedOption?.value === option.value}></MenuRadioButton>
))}
/>
}
/>
);
}
75 changes: 16 additions & 59 deletions example/src/custom/PipSubMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useContext, useState } from 'react';
import { CustomSubMenu, Option } from './CustomSubMenu';
import * as React from 'react';
import type { StyleProp, ViewStyle } from 'react-native';
import { MenuRadioButton, MenuView, PlayerContext, ScrollableMenu, SubMenuWithButton } from '@theoplayer/react-native-ui';
import { useContext } from 'react';
import { PlayerContext } from '@theoplayer/react-native-ui';

export interface PipSubMenuProps {
/**
Expand All @@ -9,61 +11,16 @@ export interface PipSubMenuProps {
menuStyle?: StyleProp<ViewStyle>;
}

interface Option {
label: string;
value: boolean;
export function PiPSubMenu(props?: PipSubMenuProps) {
const ctx = useContext(PlayerContext);

return <CustomSubMenu
title={'Automatically Start Picture-in-Picture'}
menuStyle={props?.menuStyle}
label={'Auto PiP'}
options={[{ label: 'Disabled', value: false }, { label: 'Enabled', value: true }]}
onOptionSelected={(option: Option<boolean>) => {
ctx.player.pipConfiguration = { startsAutomatically: option.value };
}}
currentOption={() => ctx.player.pipConfiguration.startsAutomatically ?? false} />;
}

const OptionAutoDisabled = { label: 'Disabled', value: false };
const OptionAutoEnabled = { label: 'Enabled', value: true };
const options: Option[] = [OptionAutoDisabled, OptionAutoEnabled];

/**
* A button component that opens a PiP configuration selection menu for the `react-native-theoplayer` UI.
*/
export const PiPSubMenu = (props: PipSubMenuProps) => {
const { menuStyle } = props;
const createMenu = () => {
return <PiPSelectionView style={menuStyle} />;
};
const player = useContext(PlayerContext).player;
const selectedLabel = player.pipConfiguration.startsAutomatically ? OptionAutoEnabled.label : OptionAutoDisabled.label;
return <SubMenuWithButton menuConstructor={createMenu} label={'Auto PiP'} preview={selectedLabel} />;
};

export interface PiPSelectionViewProps {
style?: StyleProp<ViewStyle>;
}

const PiPSelectionView = (props: PiPSelectionViewProps) => {
const { style } = props;
const player = useContext(PlayerContext).player;

const [selected, setSelected] = useState<Option>(player.pipConfiguration.startsAutomatically ? OptionAutoEnabled : OptionAutoDisabled);

const onSelectOption = (id: number | undefined): void => {
if (id !== undefined) {
player.pipConfiguration = { startsAutomatically: options[id].value };
setSelected(options[id]);
}
};

return (
<MenuView
style={style}
menu={
<ScrollableMenu
title={'Automatically Start Picture-in-Picture'}
items={options.map((option, id) => (
<MenuRadioButton
key={id}
label={option.label}
uid={id}
onSelect={onSelectOption}
selected={selected.value === option.value}></MenuRadioButton>
))}
/>
}
/>
);
};
30 changes: 30 additions & 0 deletions example/src/custom/RenderingTargetSubMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { CustomSubMenu, Option } from './CustomSubMenu';
import { RenderingTarget } from 'react-native-theoplayer';
import * as React from 'react';
import type { StyleProp, ViewStyle } from 'react-native';
import { useContext } from 'react';
import { PlayerContext } from '@theoplayer/react-native-ui';

export interface RenderingTargetSubMenuProps {
/**
* Overrides for the style of the menu.
*/
menuStyle?: StyleProp<ViewStyle>;
}

export function RenderingTargetSubMenu(props?: RenderingTargetSubMenuProps) {
const ctx = useContext(PlayerContext);

return <CustomSubMenu
title={'Rendering Target'}
menuStyle={props?.menuStyle}
label={'Display'}
options={[{ label: 'Surface', value: RenderingTarget.SURFACE_VIEW }, {
label: 'Texture',
value: RenderingTarget.TEXTURE_VIEW,
}]}
onOptionSelected={(option: Option<RenderingTarget>) => {
ctx.player.renderingTarget = option.value;
}}
currentOption={() => ctx.player.renderingTarget ?? RenderingTarget.SURFACE_VIEW} />;
}
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"react": "^18.2.0",
"react-native": "^0.74.1",
"react-native-builder-bob": "^0.23.2",
"theoplayer": "^7.4.1",
"theoplayer": "^7.6.0",
"typedoc": "^0.25.12",
"typedoc-plugin-external-resolver": "^1.0.3",
"typedoc-plugin-mdn-links": "^3.1.18",
Expand Down
Loading

0 comments on commit b5bc69a

Please sign in to comment.