added hw/sw toggle for android

This commit is contained in:
tapframe 2025-12-23 21:55:46 +05:30
parent 6855a89792
commit 1f3b9413cd
7 changed files with 92 additions and 5 deletions

View file

@ -23,6 +23,9 @@ class MPVView @JvmOverloads constructor(
private var isPaused: Boolean = true
private var surface: Surface? = null
private var httpHeaders: Map<String, String>? = null
// Hardware decoding setting (default: false = software decoding)
var useHardwareDecoding: Boolean = false
// Event listener for React Native
var onLoadCallback: ((duration: Double, width: Int, height: Int) -> Unit)? = null
@ -94,10 +97,11 @@ class MPVView @JvmOverloads constructor(
MPVLib.setOptionString("opengl-es", "yes")
// Hardware decoding configuration
// NOTE: On emulator, mediacodec can cause freezes due to slow GPU translation
// Using 'no' for software decoding which is more reliable on emulator
// For real devices, use 'mediacodec-copy' for hardware acceleration
MPVLib.setOptionString("hwdec", "no")
// 'mediacodec-copy' for hardware acceleration (GPU decoding, copies frames to CPU)
// 'no' for software decoding (more compatible, especially on emulators)
val hwdecValue = if (useHardwareDecoding) "mediacodec-copy" else "no"
Log.d(TAG, "Hardware decoding: $useHardwareDecoding, hwdec value: $hwdecValue")
MPVLib.setOptionString("hwdec", hwdecValue)
MPVLib.setOptionString("hwdec-codecs", "all")
// Audio output

View file

@ -180,4 +180,9 @@ class MpvPlayerViewManager(
view.setHeaders(null)
}
}
@ReactProp(name = "useHardwareDecoding")
fun setUseHardwareDecoding(view: MPVView, useHardwareDecoding: Boolean) {
view.useHardwareDecoding = useHardwareDecoding
}
}

View file

@ -24,6 +24,7 @@ import { useNextEpisode } from './android/hooks/useNextEpisode';
import { useTraktAutosync } from '../../hooks/useTraktAutosync';
import { useMetadata } from '../../hooks/useMetadata';
import { usePlayerGestureControls } from '../../hooks/usePlayerGestureControls';
import { useSettings } from '../../hooks/useSettings';
// Shared Components
import { GestureControls, PauseOverlay, SpeedActivatedOverlay } from './components';
@ -69,6 +70,7 @@ const AndroidVideoPlayer: React.FC = () => {
const playerState = usePlayerState();
const modals = usePlayerModals();
const speedControl = useSpeedControl();
const { settings } = useSettings();
const videoRef = useRef<any>(null);
const mpvPlayerRef = useRef<MpvPlayerRef>(null);
@ -550,6 +552,7 @@ const AndroidVideoPlayer: React.FC = () => {
onPinchGestureEvent={() => { }}
onPinchHandlerStateChange={() => { }}
screenDimensions={playerState.screenDimensions}
useHardwareDecoding={settings.useHardwareDecoding}
/>
{/* Custom Subtitles for addon subtitles */}

View file

@ -25,6 +25,7 @@ export interface MpvPlayerProps {
onEnd?: () => void;
onError?: (error: { error: string }) => void;
onTracksChanged?: (data: { audioTracks: any[]; subtitleTracks: any[] }) => void;
useHardwareDecoding?: boolean;
}
const MpvPlayer = forwardRef<MpvPlayerRef, MpvPlayerProps>((props, ref) => {
@ -103,6 +104,7 @@ const MpvPlayer = forwardRef<MpvPlayerRef, MpvPlayerProps>((props, ref) => {
onEnd={handleEnd}
onError={handleError}
onTracksChanged={handleTracksChanged}
useHardwareDecoding={props.useHardwareDecoding ?? false}
/>
);
});

View file

@ -32,6 +32,7 @@ interface VideoSurfaceProps {
onPinchHandlerStateChange: any;
screenDimensions: { width: number, height: number };
onTracksChanged?: (data: { audioTracks: any[]; subtitleTracks: any[] }) => void;
useHardwareDecoding?: boolean;
}
export const VideoSurface: React.FC<VideoSurfaceProps> = ({
@ -55,6 +56,7 @@ export const VideoSurface: React.FC<VideoSurfaceProps> = ({
onPinchHandlerStateChange,
screenDimensions,
onTracksChanged,
useHardwareDecoding,
}) => {
// Use the actual stream URL
const streamUrl = currentStreamUrl || processedStreamUrl;
@ -113,6 +115,7 @@ export const VideoSurface: React.FC<VideoSurfaceProps> = ({
onEnd={handleEnd}
onError={handleError}
onTracksChanged={onTracksChanged}
useHardwareDecoding={useHardwareDecoding}
/>
{/* Gesture overlay - transparent, on top of the player */}

View file

@ -88,6 +88,8 @@ export interface AppSettings {
streamCacheTTL: number; // Stream cache duration in milliseconds (default: 1 hour)
enableStreamsBackdrop: boolean; // Enable blurred backdrop background on StreamsScreen mobile
useExternalPlayerForDownloads: boolean; // Enable/disable external player for downloaded content
// Android MPV player settings
useHardwareDecoding: boolean; // Enable hardware decoding for MPV player on Android (default: false for software decoding)
}
export const DEFAULT_SETTINGS: AppSettings = {
@ -149,6 +151,8 @@ export const DEFAULT_SETTINGS: AppSettings = {
openMetadataScreenWhenCacheDisabled: true, // Default to StreamsScreen when cache disabled
streamCacheTTL: 60 * 60 * 1000, // Default: 1 hour in milliseconds
enableStreamsBackdrop: true, // Enable by default (new behavior)
// Android MPV player settings
useHardwareDecoding: false, // Default to software decoding (more compatible)
};
const SETTINGS_STORAGE_KEY = 'app_settings';

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import {
View,
Text,
@ -14,6 +14,7 @@ import { useNavigation } from '@react-navigation/native';
import { useSettings, AppSettings } from '../hooks/useSettings';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { useTheme } from '../contexts/ThemeContext';
import CustomAlert from '../components/CustomAlert';
const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0;
@ -95,6 +96,17 @@ const PlayerSettingsScreen: React.FC = () => {
const { currentTheme } = useTheme();
const navigation = useNavigation();
// CustomAlert state
const [alertVisible, setAlertVisible] = useState(false);
const [alertTitle, setAlertTitle] = useState('');
const [alertMessage, setAlertMessage] = useState('');
const openAlert = (title: string, message: string) => {
setAlertTitle(title);
setAlertMessage(message);
setAlertVisible(true);
};
const playerOptions = [
{
id: 'internal',
@ -323,6 +335,53 @@ const PlayerSettingsScreen: React.FC = () => {
</View>
</View>
{/* Hardware Decoding for Android Internal Player */}
{Platform.OS === 'android' && !settings.useExternalPlayer && (
<View style={[styles.settingItem, styles.settingItemBorder, { borderTopColor: 'rgba(255,255,255,0.08)' }]}>
<View style={styles.settingContent}>
<View style={[
styles.settingIconContainer,
{ backgroundColor: 'rgba(255,255,255,0.1)' }
]}>
<MaterialIcons
name="memory"
size={20}
color={currentTheme.colors.primary}
/>
</View>
<View style={styles.settingText}>
<Text
style={[
styles.settingTitle,
{ color: currentTheme.colors.text },
]}
>
Hardware Decoding
</Text>
<Text
style={[
styles.settingDescription,
{ color: currentTheme.colors.textMuted },
]}
>
Use GPU for video decoding. May improve performance but can cause issues on some devices.
</Text>
</View>
<Switch
value={settings.useHardwareDecoding}
onValueChange={(value) => {
updateSetting('useHardwareDecoding', value);
openAlert(
'Restart Required',
'Please restart the app for the decoding change to take effect.'
);
}}
thumbColor={settings.useHardwareDecoding ? currentTheme.colors.primary : undefined}
/>
</View>
</View>
)}
{/* External Player for Downloads */}
{((Platform.OS === 'android' && settings.useExternalPlayer) ||
(Platform.OS === 'ios' && settings.preferredPlayer !== 'internal')) && (
@ -367,6 +426,13 @@ const PlayerSettingsScreen: React.FC = () => {
</View>
</View>
</ScrollView>
<CustomAlert
visible={alertVisible}
title={alertTitle}
message={alertMessage}
onClose={() => setAlertVisible(false)}
/>
</SafeAreaView>
);
};