mirror of https://github.com/mifi/lossless-cut
Fix playback and conversion of audio with unknown channel layout
DV/DVCPRO .mov files (e.g. captured by Final Cut / iMovie) carry a 'chan' atom that labels every audio channel as "unused", so ffmpeg describes the stream as "4 channels (UNSD+UNSD+UNSD+UNSD)". swresample only accepts native or fully specified custom layouts, so as soon as anything needs to resample or downmix such a stream it fails with: [SWR] Input channel layout '4 channels (UNSD+UNSD+UNSD+UNSD)' is not supported [af#0:1] Error reinitializing filters! Nothing was written into output file This broke both preview playback and "convert to supported format" for these files. Prepend a `channelmap` filter that re-labels the channels, which turns the layout into a plain "N channels" (unspecified) layout that swresample does support. It only re-labels, the samples are untouched, and the stream then behaves exactly as if it had carried no channel layout information at all. Only applied to streams that actually have an unsupported layout, so files with a proper layout keep their correct downmix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCf7BBiPqNLUjVoN7rTCF6pull/2874/merge
parent
bd9ef59049
commit
ca6813f9a4
@ -0,0 +1,40 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { getFixChannelLayoutFilter, hasUnsupportedChannelLayout } from './util.js';
|
||||
|
||||
describe('hasUnsupportedChannelLayout', () => {
|
||||
test('detects unused/unknown channels', () => {
|
||||
// e.g. DV/DVCPRO .mov captured by Final Cut / iMovie
|
||||
expect(hasUnsupportedChannelLayout('4 channels (UNSD+UNSD+UNSD+UNSD)')).toBe(true);
|
||||
expect(hasUnsupportedChannelLayout('3 channels (FL+FR+UNK)')).toBe(true);
|
||||
});
|
||||
|
||||
test('accepts layouts that swresample supports', () => {
|
||||
expect(hasUnsupportedChannelLayout('stereo')).toBe(false);
|
||||
expect(hasUnsupportedChannelLayout('mono')).toBe(false);
|
||||
expect(hasUnsupportedChannelLayout('5.1(side)')).toBe(false);
|
||||
expect(hasUnsupportedChannelLayout('2.1')).toBe(false);
|
||||
// unspecified layouts are fine, ffmpeg just uses a default downmix
|
||||
expect(hasUnsupportedChannelLayout('4 channels')).toBe(false);
|
||||
expect(hasUnsupportedChannelLayout(undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFixChannelLayoutFilter', () => {
|
||||
test('relabels all channels of an unsupported layout', () => {
|
||||
expect(getFixChannelLayoutFilter({ channels: 4, channelLayout: '4 channels (UNSD+UNSD+UNSD+UNSD)' })).toBe('channelmap=0|1|2|3');
|
||||
expect(getFixChannelLayoutFilter({ channels: 3, channelLayout: '3 channels (FL+FR+UNK)' })).toBe('channelmap=0|1|2');
|
||||
expect(getFixChannelLayoutFilter({ channels: 1, channelLayout: '1 channels (UNSD)' })).toBe('channelmap=0');
|
||||
});
|
||||
|
||||
test('leaves supported layouts alone', () => {
|
||||
expect(getFixChannelLayoutFilter({ channels: 2, channelLayout: 'stereo' })).toBeUndefined();
|
||||
expect(getFixChannelLayoutFilter({ channels: 6, channelLayout: '5.1' })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('needs a channel count to build the map', () => {
|
||||
expect(getFixChannelLayoutFilter({ channels: undefined, channelLayout: '4 channels (UNSD+UNSD+UNSD+UNSD)' })).toBeUndefined();
|
||||
expect(getFixChannelLayoutFilter({ channels: 0, channelLayout: '4 channels (UNSD+UNSD+UNSD+UNSD)' })).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue