feat(player): enhance technical info overlay with codec details

This commit is contained in:
Fredrik Burmester
2026-01-22 08:15:02 +01:00
parent 3f882ecade
commit be92b5d75e
3 changed files with 108 additions and 2 deletions

View File

@@ -481,6 +481,7 @@ export const Controls: FC<Props> = ({
getTechnicalInfo={getTechnicalInfo}
playMethod={playMethod}
transcodeReasons={transcodeReasons}
mediaSource={mediaSource}
/>
)}
<Animated.View

View File

@@ -788,6 +788,9 @@ export const Controls: FC<Props> = ({
getTechnicalInfo={getTechnicalInfo}
playMethod={playMethod}
transcodeReasons={transcodeReasons}
mediaSource={mediaSource}
currentAudioIndex={audioIndex}
currentSubtitleIndex={subtitleIndex}
/>
)}

View File

@@ -1,4 +1,12 @@
import { type FC, memo, useCallback, useEffect, useState } from "react";
import type { MediaSourceInfo } from "@jellyfin/sdk/lib/generated-client";
import {
type FC,
memo,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import { Platform, StyleSheet, Text, View } from "react-native";
import Animated, {
Easing,
@@ -20,6 +28,9 @@ interface TechnicalInfoOverlayProps {
getTechnicalInfo: () => Promise<TechnicalInfo>;
playMethod?: PlayMethod;
transcodeReasons?: string[];
mediaSource?: MediaSourceInfo | null;
currentSubtitleIndex?: number;
currentAudioIndex?: number;
}
const formatBitrate = (bitsPerSecond: number): string => {
@@ -48,10 +59,51 @@ const formatCodec = (codec: string): string => {
flac: "FLAC",
opus: "Opus",
mp3: "MP3",
// Subtitle codecs
srt: "SRT",
subrip: "SRT",
ass: "ASS",
ssa: "SSA",
webvtt: "WebVTT",
vtt: "WebVTT",
pgs: "PGS",
hdmv_pgs_subtitle: "PGS",
dvd_subtitle: "VobSub",
dvdsub: "VobSub",
mov_text: "MOV Text",
cc_dec: "CC",
eia_608: "CC",
};
return codecMap[codec.toLowerCase()] || codec.toUpperCase();
};
const formatAudioChannels = (channels: number): string => {
switch (channels) {
case 1:
return "Mono";
case 2:
return "Stereo";
case 6:
return "5.1";
case 8:
return "7.1";
default:
return `${channels}ch`;
}
};
const formatVideoRange = (range?: string | null): string | null => {
if (!range || range === "SDR") return null;
const rangeMap: Record<string, string> = {
HDR10: "HDR10",
HDR10Plus: "HDR10+",
HLG: "HLG",
"Dolby Vision": "Dolby Vision",
DolbyVision: "Dolby Vision",
};
return rangeMap[range] || range;
};
const formatFps = (fps: number): string => {
// Common frame rates
if (Math.abs(fps - 23.976) < 0.01) return "23.976";
@@ -127,6 +179,9 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
getTechnicalInfo,
playMethod,
transcodeReasons,
mediaSource,
currentSubtitleIndex,
currentAudioIndex,
}) => {
const { settings } = useSettings();
const insets = useSafeAreaInsets();
@@ -134,6 +189,39 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
const opacity = useSharedValue(0);
// Extract stream info from media source
const streamInfo = useMemo(() => {
if (!mediaSource?.MediaStreams) return null;
const videoStream = mediaSource.MediaStreams.find(
(s) => s.Type === "Video",
);
const audioStream = mediaSource.MediaStreams.find(
(s) =>
s.Type === "Audio" &&
(currentAudioIndex !== undefined
? s.Index === currentAudioIndex
: s.IsDefault),
);
const subtitleStream = mediaSource.MediaStreams.find(
(s) =>
s.Type === "Subtitle" &&
currentSubtitleIndex !== undefined &&
currentSubtitleIndex >= 0 &&
s.Index === currentSubtitleIndex,
);
return {
container: mediaSource.Container,
videoRange: videoStream?.VideoRangeType,
bitDepth: videoStream?.BitDepth,
audioChannels: audioStream?.Channels,
audioCodecFromSource: audioStream?.Codec,
subtitleCodec: subtitleStream?.Codec,
subtitleTitle: subtitleStream?.DisplayTitle,
};
}, [mediaSource, currentAudioIndex, currentSubtitleIndex]);
// Animate visibility based on visible prop only (stays visible regardless of controls)
useEffect(() => {
opacity.value = withTiming(visible ? 1 : 0, {
@@ -214,6 +302,10 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
{info?.videoWidth && info?.videoHeight && (
<Text style={textStyle}>
{info.videoWidth}x{info.videoHeight}
{streamInfo?.bitDepth ? ` ${streamInfo.bitDepth}bit` : ""}
{formatVideoRange(streamInfo?.videoRange)
? ` ${formatVideoRange(streamInfo?.videoRange)}`
: ""}
</Text>
)}
{info?.videoCodec && (
@@ -223,7 +315,17 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
</Text>
)}
{info?.audioCodec && (
<Text style={textStyle}>Audio: {formatCodec(info.audioCodec)}</Text>
<Text style={textStyle}>
Audio: {formatCodec(info.audioCodec)}
{streamInfo?.audioChannels
? ` ${formatAudioChannels(streamInfo.audioChannels)}`
: ""}
</Text>
)}
{streamInfo?.subtitleCodec && (
<Text style={textStyle}>
Subtitle: {formatCodec(streamInfo.subtitleCodec)}
</Text>
)}
{(info?.videoBitrate || info?.audioBitrate) && (
<Text style={textStyle}>