Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/android rendering target #343

Merged
merged 11 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading