Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
package se.svt.oss.encore.config

import org.springframework.boot.context.properties.NestedConfigurationProperty
import org.springframework.core.io.Resource
import se.svt.oss.encore.model.profile.ChannelLayout

data class EncodingProperties(
val audioMixPresetLocation: Resource? = null,
@NestedConfigurationProperty
val audioMixPresets: Map<String, AudioMixPreset> = mapOf("default" to AudioMixPreset()),
@NestedConfigurationProperty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import se.svt.oss.encore.model.input.maxDuration
import se.svt.oss.encore.model.mediafile.toParams
import se.svt.oss.encore.process.CommandBuilder
import se.svt.oss.encore.process.createTempDir
import se.svt.oss.encore.service.audiomix.AudiomixPresetService
import se.svt.oss.encore.service.profile.ProfileService
import se.svt.oss.mediaanalyzer.MediaAnalyzer
import se.svt.oss.mediaanalyzer.file.MediaFile
Expand All @@ -28,6 +29,7 @@ private val log = KotlinLogging.logger { }
class FfmpegExecutor(
private val mediaAnalyzer: MediaAnalyzer,
private val profileService: ProfileService,
private val audioMixService: AudiomixPresetService,
private val encoreProperties: EncoreProperties,
) {

Expand All @@ -45,10 +47,14 @@ class FfmpegExecutor(
): List<MediaFile> {
ShutdownHandler.checkShutdown()
val profile = profileService.getProfile(encoreJob)
val audioMixPresets = audioMixService.getAudioMixPresets()
val encodingProperties = encoreProperties.encoding.copy(
audioMixPresets = audioMixPresets,
)
val outputs = profile.encodes.mapNotNull {
it.getOutput(
encoreJob,
encoreProperties.encoding,
encodingProperties,
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package se.svt.oss.encore.service.audiomix

import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper
import com.fasterxml.jackson.module.kotlin.readValue
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding
import org.springframework.stereotype.Service
import se.svt.oss.encore.config.AudioMixPreset
import se.svt.oss.encore.config.EncoreProperties
import java.io.File
import java.util.Locale

private val log = KotlinLogging.logger { }

@Service
@RegisterReflectionForBinding(AudioMixPreset::class)
class AudiomixPresetService(
private val objectMapper: ObjectMapper,
private val encoreProperties: EncoreProperties,
) {
private val yamlMapper: YAMLMapper =
YAMLMapper().findAndRegisterModules()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) as YAMLMapper

private fun mapper() =
if (encoreProperties.encoding.audioMixPresetLocation?.filename?.let {
File(it).extension.lowercase(Locale.getDefault()) in setOf("yml", "yaml")
} == true
) {
yamlMapper
} else {
objectMapper
}

fun getAudioMixPresets(): Map<String, AudioMixPreset> = try {
log.debug { "Reading presets from ${encoreProperties.encoding.audioMixPresetLocation}" }
encoreProperties.encoding.audioMixPresetLocation?.let { location ->
mapper().readValue<Map<String, AudioMixPreset>>(location.inputStream)
} ?: encoreProperties.encoding.audioMixPresets
} catch (e: JsonProcessingException) {
throw RuntimeException("Error parsing audio mix presets ${e.message}")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package se.svt.oss.encore.service.audiomix

import com.fasterxml.jackson.databind.ObjectMapper
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.core.io.ClassPathResource
import se.svt.oss.encore.config.EncodingProperties
import se.svt.oss.encore.config.EncoreProperties
import java.io.IOException

class AudioMixPresetServiceTest {

private lateinit var mixService: AudiomixPresetService
private val objectMapper = ObjectMapper().findAndRegisterModules()
private val encoreProperties =
EncoreProperties(encoding = EncodingProperties(ClassPathResource("audiomixpreset/audio-mix-presets.yml")))

@BeforeEach
internal fun setUp() {
mixService = AudiomixPresetService(
objectMapper,
encoreProperties,
)
}

@Test
fun `successfully parses existing and valid presets`() {
val presets = mixService.getAudioMixPresets()
assertThat(presets).hasSize(2)
assertThat(presets["default"]).isNotNull
assertThat(presets["de"]).isNotNull
}

@Test
fun `nonexistent preset throws error`() {
mixService = AudiomixPresetService(
objectMapper,
encoreProperties.copy(
encoding = encoreProperties.encoding.copy(
audioMixPresetLocation = ClassPathResource(
"i-dont-exist",
),
),
),
)
assertThatThrownBy {
mixService.getAudioMixPresets()
}
.isInstanceOf(IOException::class.java)
.hasMessageStartingWith("class path resource [i-dont-exist] cannot be opened because it does not exist")
}
}
1 change: 1 addition & 0 deletions encore-common/src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ encore-settings:
poll-delay: 1s
shared-work-dir: ${java.io.tmpdir}/encore-shared
encoding:
audio-mix-preset-location: classpath:audiomixpreset/audio-mix-presets.yml
default-channel-layouts:
3: "3.0"
audio-mix-presets:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
default:
default-pan:
stereo: FL=FL+0.707107*FC+0.707107*BL+0.707107*SL|FR=FR+0.707107*FC+0.707107*BR+0.707107*SR
pan-mapping:
mono:
stereo: FL=0.707*FC|FR=0.707*FC
de:
fallback-to-auto: false
default-pan:
stereo: FL<FL+1.5*FC+0.707107*BL+0.707107*SL|FR<FR+1.5*FC+0.707107*BR+0.707107*SR
pan-mapping:
"[5.1]":
"[5.1]": c0=c0|c1=c1|c2<1.5*c2|c3=c3|c4=c4|c5=c5
"[5.1(side)]":
"[5.1]": c0=c0|c1=c1|c2<1.5*c2|c3=c3|c4=c4|c5=c5