diff --git a/encore-common/src/main/kotlin/se/svt/oss/encore/model/mediafile/AudioLayout.kt b/encore-common/src/main/kotlin/se/svt/oss/encore/model/mediafile/AudioLayout.kt index c2fcbc6..ae1144a 100644 --- a/encore-common/src/main/kotlin/se/svt/oss/encore/model/mediafile/AudioLayout.kt +++ b/encore-common/src/main/kotlin/se/svt/oss/encore/model/mediafile/AudioLayout.kt @@ -6,7 +6,7 @@ package se.svt.oss.encore.model.mediafile enum class AudioLayout { NONE, - INVALID, + MIXED_MONO_MULTI, MONO_STREAMS, MULTI_TRACK, } diff --git a/encore-common/src/main/kotlin/se/svt/oss/encore/model/mediafile/Extensions.kt b/encore-common/src/main/kotlin/se/svt/oss/encore/model/mediafile/Extensions.kt index 5665495..4012599 100644 --- a/encore-common/src/main/kotlin/se/svt/oss/encore/model/mediafile/Extensions.kt +++ b/encore-common/src/main/kotlin/se/svt/oss/encore/model/mediafile/Extensions.kt @@ -18,18 +18,21 @@ fun MediaContainer.audioLayout() = when { audioStreams.size == 1 -> AudioLayout.MULTI_TRACK audioStreams.all { it.channels == 1 } -> AudioLayout.MONO_STREAMS audioStreams.first().channels > 1 -> AudioLayout.MULTI_TRACK - else -> AudioLayout.INVALID + else -> AudioLayout.MIXED_MONO_MULTI } -fun MediaContainer.channelCount() = if (audioLayout() == AudioLayout.MULTI_TRACK) { - audioStreams.first().channels -} else { - audioStreams.size +fun MediaContainer.channelCount() = when (audioLayout()) { + AudioLayout.MULTI_TRACK -> audioStreams.first().channels + AudioLayout.MONO_STREAMS -> audioStreams.size + // Return number of mono tracks before first multitrack stream, + // effectively ignoring all other streams + AudioLayout.MIXED_MONO_MULTI -> audioStreams.indexOfFirst { it.channels > 1 } + AudioLayout.NONE -> 0 } fun AudioIn.channelLayout(defaultChannelLayouts: Map): ChannelLayout = when (analyzedAudio.audioLayout()) { - AudioLayout.NONE, AudioLayout.INVALID -> null - AudioLayout.MONO_STREAMS -> if (analyzedAudio.channelCount() == channelLayout?.channels?.size) { + AudioLayout.NONE -> null + AudioLayout.MONO_STREAMS, AudioLayout.MIXED_MONO_MULTI -> if (analyzedAudio.channelCount() == channelLayout?.channels?.size) { channelLayout } else { defaultChannelLayouts[analyzedAudio.channelCount()] diff --git a/encore-common/src/main/kotlin/se/svt/oss/encore/model/profile/AudioEncode.kt b/encore-common/src/main/kotlin/se/svt/oss/encore/model/profile/AudioEncode.kt index 7bce78f..8f98dd8 100644 --- a/encore-common/src/main/kotlin/se/svt/oss/encore/model/profile/AudioEncode.kt +++ b/encore-common/src/main/kotlin/se/svt/oss/encore/model/profile/AudioEncode.kt @@ -36,9 +36,6 @@ data class AudioEncode( val audioIn = job.inputs.audioInput(inputLabel) ?: return logOrThrow("Can not generate $outputName! No audio input with label '$inputLabel'.") val analyzed = audioIn.analyzedAudio - if (analyzed.audioLayout() == AudioLayout.INVALID) { - throw RuntimeException("Audio layout of audio input '$inputLabel' is not supported!") - } if (analyzed.audioLayout() == AudioLayout.NONE) { return logOrThrow("Can not generate $outputName! No audio streams in input!") } diff --git a/encore-common/src/main/kotlin/se/svt/oss/encore/process/CommandBuilder.kt b/encore-common/src/main/kotlin/se/svt/oss/encore/process/CommandBuilder.kt index 899023c..a1cac11 100644 --- a/encore-common/src/main/kotlin/se/svt/oss/encore/process/CommandBuilder.kt +++ b/encore-common/src/main/kotlin/se/svt/oss/encore/process/CommandBuilder.kt @@ -217,13 +217,16 @@ class CommandBuilder( return filters + input.videoFilters } - private fun globalAudioFilters(input: AudioIn, analyzed: MediaContainer): List = if (analyzed.audioLayout() == AudioLayout.MONO_STREAMS) { - val channelLayout = input.channelLayout(encodingProperties.defaultChannelLayouts) - val map = channelLayout.channels.withIndex().joinToString("|") { "${it.index}.0-${it.value}" } - listOf("join=inputs=${channelLayout.channels.size}:channel_layout=${channelLayout.layoutName}:map=$map") - } else { - emptyList() - } + input.audioFilters + private fun globalAudioFilters(input: AudioIn, analyzed: MediaContainer): List = + if (analyzed.audioLayout() == AudioLayout.MONO_STREAMS || + analyzed.audioLayout() == AudioLayout.MIXED_MONO_MULTI + ) { + val channelLayout = input.channelLayout(encodingProperties.defaultChannelLayouts) + val map = channelLayout.channels.withIndex().joinToString("|") { "${it.index}.0-${it.value}" } + listOf("join=inputs=${channelLayout.channels.size}:channel_layout=${channelLayout.layoutName}:map=$map") + } else { + emptyList() + } + input.audioFilters private fun firstPassParams(output: Output): List { if (output.video == null) { diff --git a/encore-common/src/test/kotlin/se/svt/oss/encore/model/mediafile/MediaFileExtensionsTest.kt b/encore-common/src/test/kotlin/se/svt/oss/encore/model/mediafile/MediaFileExtensionsTest.kt index 0ce8d10..216abb3 100644 --- a/encore-common/src/test/kotlin/se/svt/oss/encore/model/mediafile/MediaFileExtensionsTest.kt +++ b/encore-common/src/test/kotlin/se/svt/oss/encore/model/mediafile/MediaFileExtensionsTest.kt @@ -18,7 +18,7 @@ import se.svt.oss.mediaanalyzer.file.MediaFile internal class MediaFileExtensionsTest { private val noAudio = defaultVideoFile.copy(audioStreams = emptyList()) - private val invalidAudio = defaultVideoFile.copy( + private val mixedMonoMultiAudio = defaultVideoFile.copy( audioStreams = defaultVideoFile.audioStreams.mapIndexed { index, audioStream -> if (index == 0) audioStream else audioStream.copy(channels = 2) }, @@ -53,9 +53,9 @@ internal class MediaFileExtensionsTest { } @Test - @DisplayName("Audio layout is invalid if first stream has one channel and the rest has two channels or more") - fun testAudioLayoutInvalid() { - assertThat(invalidAudio.audioLayout()).isEqualTo(AudioLayout.INVALID) + @DisplayName("Audio layout is MIXED_MONO_MULTI if first stream has one channel and the rest has two channels or more") + fun testAudioLayoutMonotracksFollowedByMultitrack() { + assertThat(mixedMonoMultiAudio.audioLayout()).isEqualTo(AudioLayout.MIXED_MONO_MULTI) } @Test @@ -72,6 +72,12 @@ internal class MediaFileExtensionsTest { assertThat(defaultVideoFile.channelCount()).isEqualTo(8) } + @Test + @DisplayName("Channel count for mono stream followed by multitrack streams is equal to number of leading mono streams") + fun testChannelCountMixedMonoMultiStreams() { + assertThat(mixedMonoMultiAudio.channelCount()).isEqualTo(1) + } + @Test @DisplayName("Channel count for no streams is 0") fun testChannelCountNoSteams() { @@ -149,13 +155,6 @@ internal class MediaFileExtensionsTest { .hasMessage("Could not determine channel layout for audio input 'main'!") } - @Test - @DisplayName("Channel layout throws when invalid audio") - fun channelLayoutInvalidAudio() { - assertThatThrownBy { audioInput(invalidAudio).channelLayout(defaultChannelLayouts) } - .hasMessage("Could not determine channel layout for audio input 'main'!") - } - @Test @DisplayName("Channel layout is set on input and channel count correct") fun channelLayoutMonoStreamsSetByParam() { diff --git a/encore-common/src/test/kotlin/se/svt/oss/encore/model/profile/AudioEncodeTest.kt b/encore-common/src/test/kotlin/se/svt/oss/encore/model/profile/AudioEncodeTest.kt index 6dda4b3..1860d43 100644 --- a/encore-common/src/test/kotlin/se/svt/oss/encore/model/profile/AudioEncodeTest.kt +++ b/encore-common/src/test/kotlin/se/svt/oss/encore/model/profile/AudioEncodeTest.kt @@ -35,18 +35,6 @@ class AudioEncodeTest { .hasMessageContaining("No audio streams in input") } - @Test - fun `not supported soundtype throws exception`() { - val job = job(getAudioStream(1), getAudioStream(2)) - assertThatThrownBy { - audioEncode.getOutput( - job, - EncodingProperties(audioMixPresets = mapOf("default" to AudioMixPreset(fallbackToAuto = false))), - ) - }.isInstanceOf(RuntimeException::class.java) - .hasMessage("Audio layout of audio input 'main' is not supported!") - } - @Test fun `valid output`() { val output = audioEncode.getOutput(