diff --git a/src/common/types.ts b/src/common/types.ts index 98266600..cdcc71c8 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -9,6 +9,19 @@ export interface KeyBinding { export type FfmpegHwAccel = 'none' | 'auto' | 'vdpau' | 'dxva2' | 'd3d11va' | 'vaapi' | 'qsv' | 'videotoolbox'; +/** + * The parts of an audio stream's ffprobe data needed to decide whether its channel layout + * has to be fixed up before ffmpeg can resample or downmix it. See `getFixChannelLayoutFilter`. + */ +export interface AudioChannelInfo { + channels?: number | undefined, + channelLayout?: string | undefined, +} + +export interface AudioStreamInfo extends AudioChannelInfo { + index: number, +} + export type CaptureFormat = 'jpeg' | 'png' | 'webp'; export type TimecodeFormat = 'timecodeWithDecimalFraction' | 'frameCount' | 'seconds' | 'timecodeWithFramesFraction'; diff --git a/src/common/util.ts b/src/common/util.ts index f1f115f8..d15359ba 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -1,4 +1,4 @@ -import type { FfmpegHwAccel } from './types.ts'; +import type { AudioChannelInfo, FfmpegHwAccel } from './types.ts'; export const parseFfprobeDuration = (durationStr: string | undefined) => ( durationStr != null ? parseFloat(durationStr) : undefined @@ -41,10 +41,7 @@ export const hasCustomChannelLayout = (channelLayout: string | undefined) => ( // The `channelmap` filter re-labels the channels without touching the samples, which turns the layout // into a plain "N channels" (unspecified) layout. swresample handles those by skipping the rematrixing // step entirely, so the stream then behaves exactly as if it had carried no channel layout information. -export function getFixChannelLayoutFilter({ channels, channelLayout }: { - channels?: number | undefined, - channelLayout?: string | undefined, -}) { +export function getFixChannelLayoutFilter({ channels, channelLayout }: AudioChannelInfo) { if (channels == null || channels <= 0 || !hasCustomChannelLayout(channelLayout)) return undefined; return `channelmap=${Array.from({ length: channels }, (_, i) => i).join('|')}`; } diff --git a/src/main/ffmpeg.ts b/src/main/ffmpeg.ts index 22ee0d04..4ef96948 100644 --- a/src/main/ffmpeg.ts +++ b/src/main/ffmpeg.ts @@ -10,7 +10,7 @@ import type { Readable } from 'node:stream'; import { app, clipboard, nativeImage } from 'electron'; import { platform, arch, isWindows, isLinux } from './util.js'; -import type { CaptureFormat, FfmpegHwAccel } from '../common/types.js'; +import type { AudioStreamInfo, CaptureFormat, FfmpegHwAccel } from '../common/types.js'; import type { FFprobeFormat } from '../common/ffprobe.js'; import isDev from './isDev.js'; import logger from './logger.js'; @@ -594,7 +594,7 @@ const encode = true; export function createMediaSourceProcess({ path, videoStreamIndex, audioStreams, seekTo, size, fps, rotate, forceColorspace, ffmpegHwaccel }: { path: string, videoStreamIndex?: number | undefined, - audioStreams: { index: number, channels?: number | undefined, channelLayout?: string | undefined }[], + audioStreams: AudioStreamInfo[], seekTo: number, size?: number | undefined, fps?: number | undefined, @@ -666,7 +666,7 @@ export function createMediaSourceProcess({ path, videoStreamIndex, audioStreams, if (audioStreams.length > 0) { // some streams have a channel layout that ffmpeg cannot resample or downmix, so relabel it first - const getAudioFilters = (stream: typeof audioStreams[number], rest: string[]) => { + const getAudioFilters = (stream: AudioStreamInfo, rest: string[]) => { const filters = [getFixChannelLayoutFilter(stream), ...rest].filter((filter) => filter != null); return filters.length > 0 ? filters.join(',') : 'anull'; }; diff --git a/src/renderer/src/MediaSourcePlayer.tsx b/src/renderer/src/MediaSourcePlayer.tsx index 979c0cc9..7f8cfb92 100644 --- a/src/renderer/src/MediaSourcePlayer.tsx +++ b/src/renderer/src/MediaSourcePlayer.tsx @@ -8,7 +8,7 @@ import isDev from './isDev'; import type { ChromiumHTMLVideoElement } from './types'; import type { FFprobeStream } from '../../common/ffprobe'; import { getFrameDuration } from './util'; -import type { FfmpegHwAccel } from '../../common/types'; +import type { AudioStreamInfo, FfmpegHwAccel } from '../../common/types'; const { compatPlayer: { createMediaSourceStream } } = window.require('@electron/remote').require('./index.js'); @@ -18,7 +18,7 @@ async function startPlayback({ path, slaveVideo, masterVideo, videoStreamIndex, slaveVideo: ChromiumHTMLVideoElement, masterVideo: ChromiumHTMLVideoElement, videoStreamIndex?: number | undefined, - audioStreams: { index: number, channels?: number | undefined, channelLayout?: string | undefined }[], + audioStreams: AudioStreamInfo[], seekTo: number, signal: AbortSignal, size?: number | undefined,