Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 0.2.6 #1

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
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
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true


[*.{kt,kts}]
ktlint_code_style = intellij_idea
ktlint_standard_import-ordering = disabled
ktlint_standard_trailing-comma-on-declaration-site = disabled
ktlint_standard_trailing-comma-on-call-site = disabled
#ktlint_standard_function-signature = disabled
#ktlint_standard_multiline-expression-wrapping = disabled
#ktlint_standard_string-template-indent = disabled
#ktlint_standard_parameter-list-wrapping = disabled
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
implementation("org.jetbrains.kotlin:kotlin-allopen")
implementation("org.springframework.boot:spring-boot-gradle-plugin:3.1.3")
implementation("org.jmailen.gradle:kotlinter-gradle:3.13.0")
implementation("org.jmailen.gradle:kotlinter-gradle:4.1.1")
implementation("pl.allegro.tech.build:axion-release-plugin:1.14.3")
implementation("org.graalvm.buildtools:native-gradle-plugin:0.9.25")
implementation("com.github.fhermansson:assertj-gradle-plugin:1.1.5")
Expand Down
10 changes: 3 additions & 7 deletions buildSrc/src/main/kotlin/encore.kotlin-conventions.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.jmailen.gradle.kotlinter.KotlinterExtension

plugins {
idea
jacoco
Expand Down Expand Up @@ -29,13 +31,7 @@ tasks.lintKotlinTest {
tasks.formatKotlinTest {
source = (source - fileTree("src/test/generated-java")).asFileTree
}
kotlinter {
disabledRules = arrayOf(
"import-ordering",
"trailing-comma-on-declaration-site",
"trailing-comma-on-call-site"
)
}

assertjGenerator {
classOrPackageNames = arrayOf(
"se.svt.oss.encore.model",
Expand Down
13 changes: 13 additions & 0 deletions checks.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ jacocoTestCoverageVerification {
'*.static {...}',
'*.model.*.get*',
'*.service.localencode.LocalEncodeService.moveFile*',
'*.S3Properties*.get*()',
'*RemoteFileService.DefaultHandler.*',
'*.log$lambda$0()',
'se.svt.oss.encore.service.remotefiles.RemoteFileService.getHandler$lambda$4(java.net.URI)',
'se.svt.oss.encore.service.EncoreService.encodeSegmented$lambda$10(java.util.concurrent.CancellationException)',
'se.svt.oss.encore.service.EncoreService.encodeSegmented$lambda$11(java.lang.Exception)',
'se.svt.oss.encore.service.EncoreService.encodeSegment$lambda$16(int, java.lang.Exception)',
'se.svt.oss.encore.service.FfmpegExecutor.runFfmpeg$lambda$17$lambda$16$lambda$14(java.lang.String)',
'se.svt.oss.encore.service.EncoreService.handleProgress.1.1.emit$lambda$1()',
'se.svt.oss.encore.service.queue.QueueService.poll$lambda$2()',
'se.svt.oss.encore.service.queue.QueueService.poll$lambda$3()',
'se.svt.oss.encore.service.queue.QueueService.handleOrphanedQueues$lambda$16(java.lang.Exception)',
'se.svt.oss.encore.process.CommandBuilder.MapName.getEntries()'
]
limit {
counter = 'LINE'
Expand Down
4 changes: 4 additions & 0 deletions encore-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ dependencies {

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.7.3")
implementation(platform("software.amazon.awssdk:bom:2.29.2"))
implementation("software.amazon.awssdk:s3")

testImplementation(project(":encore-web"))
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.awaitility:awaitility")
testImplementation("com.github.tomakehurst:wiremock-jre8-standalone:2.35.0")
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
testImplementation("org.springframework.boot:spring-boot-starter-data-rest")

testFixturesImplementation(platform("org.springframework.boot:spring-boot-dependencies:3.1.3"))
testFixturesImplementation("com.redis:testcontainers-redis:2.2.0")
testFixturesImplementation("io.github.microutils:kotlin-logging:3.0.5")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api")
testFixturesImplementation("org.testcontainers:localstack:1.20.3")
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2024 Eyevinn Technology AB
//
// SPDX-License-Identifier: EUPL-1.2

package se.svt.oss.encore

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import se.svt.oss.encore.service.remotefiles.s3.S3Properties
import se.svt.oss.encore.service.remotefiles.s3.S3RemoteFileHandler
import se.svt.oss.encore.service.remotefiles.s3.S3UriConverter
import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.s3.S3AsyncClient
import software.amazon.awssdk.services.s3.S3Configuration
import software.amazon.awssdk.services.s3.presigner.S3Presigner
import java.net.URI

@ConditionalOnProperty("remote-files.s3.enabled", havingValue = "true")
@EnableConfigurationProperties(S3Properties::class)
@Configuration
class S3RemoteFilesConfiguration {

@Bean
fun s3Region() =
Region.of(System.getProperty("aws.region") ?: System.getenv("AWS_REGION") ?: "us-east-1")

@Bean
fun s3Client(s3Region: Region, s3Properties: S3Properties) = S3AsyncClient.builder()
.region(s3Region)
.crossRegionAccessEnabled(true)
.multipartEnabled(!s3Properties.anonymousAccess) // Multipart upload requires credentials
.serviceConfiguration(
S3Configuration.builder()
.pathStyleAccessEnabled(true)
.build()
)
.credentialsProvider(
if (s3Properties.anonymousAccess) {
AnonymousCredentialsProvider.create()
} else {
DefaultCredentialsProvider.create()
}
)
.apply {
if (!s3Properties.endpoint.isNullOrBlank()) {
endpointOverride(URI.create(s3Properties.endpoint))
}
}
.build()

@Bean
fun s3Presigner(s3Region: Region, s3Properties: S3Properties) = S3Presigner.builder()
.region(s3Region)
.serviceConfiguration(
S3Configuration.builder()
.pathStyleAccessEnabled(true)
.build()
)
.apply {
if (!s3Properties.endpoint.isNullOrBlank()) {
endpointOverride(URI.create(s3Properties.endpoint))
}
}
.build()

@Bean
fun s3UriConverter(s3Properties: S3Properties, s3Region: Region) = S3UriConverter(s3Properties, s3Region)

@Bean
fun s3RemoteFileHandler(
s3Client: S3AsyncClient,
s3Presigner: S3Presigner,
s3Properties: S3Properties,
s3UriConverter: S3UriConverter
) =
S3RemoteFileHandler(s3Client, s3Presigner, s3Properties, s3UriConverter)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.Pattern
import jakarta.validation.constraints.PositiveOrZero
import se.svt.oss.encore.model.mediafile.toParams
import se.svt.oss.encore.model.profile.ChannelLayout
import se.svt.oss.mediaanalyzer.file.FractionString
import se.svt.oss.mediaanalyzer.file.MediaContainer
import se.svt.oss.mediaanalyzer.file.MediaFile
import se.svt.oss.mediaanalyzer.file.VideoFile
import jakarta.validation.constraints.Pattern
import jakarta.validation.constraints.PositiveOrZero

const val TYPE_AUDIO_VIDEO = "AudioVideo"
const val TYPE_AUDIO = "Audio"
Expand All @@ -35,6 +35,8 @@ sealed interface Input {
@get:Schema(description = "URI of input file", required = true, example = "/path/to/file.mp4")
val uri: String

var accessUri: String

@get:Schema(description = "Input params required to properly decode input", example = """{ "ac": "2" }""")
val params: LinkedHashMap<String, String?>

Expand Down Expand Up @@ -167,6 +169,9 @@ data class AudioInput(
override val type: String
get() = TYPE_AUDIO

@JsonIgnore
override var accessUri: String = uri

override fun withSeekTo(seekTo: Double) = copy(seekTo = seekTo)

val duration: Double
Expand All @@ -188,6 +193,9 @@ data class VideoInput(
override val seekTo: Double? = null,
override val copyTs: Boolean = false
) : VideoIn {
@JsonIgnore
override var accessUri: String = uri

override val analyzedVideo: VideoFile
@JsonIgnore
get() = analyzed as? VideoFile ?: throw RuntimeException("Analyzed video for $uri is ${analyzed?.type}")
Expand Down Expand Up @@ -220,6 +228,9 @@ data class AudioVideoInput(
override val seekTo: Double? = null,
override val copyTs: Boolean = false
) : VideoIn, AudioIn {
@JsonIgnore
override var accessUri: String = uri

override val analyzedVideo: VideoFile
@JsonIgnore
get() = analyzed as? VideoFile ?: throw RuntimeException("Analyzed audio/video for $uri is ${analyzed?.type}")
Expand All @@ -244,7 +255,7 @@ fun List<Input>.inputParams(readDuration: Double?): List<String> =
(readDuration?.let { listOf("-t", "$it") } ?: emptyList()) +
(input.seekTo?.let { listOf("-ss", "$it") } ?: emptyList()) +
(if (input.copyTs) listOf("-copyts") else emptyList()) +
listOf("-i", input.uri)
listOf("-i", input.accessUri)
}

fun List<Input>.maxDuration(): Double? = maxOfOrNull {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package se.svt.oss.encore.model.mediafile

enum class AudioLayout {
NONE,
INVALID,
MIXED_MONO_MULTI,
MONO_STREAMS,
MULTI_TRACK
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,22 @@ 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<Int, ChannelLayout>): ChannelLayout {
return 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()]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
package se.svt.oss.encore.model.profile

import se.svt.oss.encore.config.EncodingProperties
import se.svt.oss.encore.model.input.DEFAULT_AUDIO_LABEL
import se.svt.oss.encore.model.EncoreJob
import se.svt.oss.encore.model.input.DEFAULT_AUDIO_LABEL
import se.svt.oss.encore.model.input.audioInput
import se.svt.oss.encore.model.mediafile.AudioLayout
import se.svt.oss.encore.model.mediafile.audioLayout
Expand Down Expand Up @@ -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!")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,9 @@ class CommandBuilder(
}

private fun globalAudioFilters(input: AudioIn, analyzed: MediaContainer): List<String> {
return if (analyzed.audioLayout() == AudioLayout.MONO_STREAMS) {
return 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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import se.svt.oss.encore.service.callback.CallbackService
import se.svt.oss.encore.service.localencode.LocalEncodeService
import se.svt.oss.encore.service.mediaanalyzer.MediaAnalyzerService
import se.svt.oss.encore.service.queue.QueueService
import se.svt.oss.encore.service.remotefiles.RemoteFileService
import se.svt.oss.mediaanalyzer.file.MediaContainer
import se.svt.oss.mediaanalyzer.file.MediaFile
import java.io.File
Expand All @@ -60,6 +61,7 @@ class EncoreService(
private val localEncodeService: LocalEncodeService,
private val encoreProperties: EncoreProperties,
private val queueService: QueueService,
private val remoteFileService: RemoteFileService
) {

private val log = KotlinLogging.logger {}
Expand Down Expand Up @@ -225,7 +227,7 @@ class EncoreService(
repository.save(encoreJob)
cancelTopic?.removeListener(cancelListener)
callbackService.sendProgressCallback(encoreJob)
localEncodeService.cleanup(outputFolder)
localEncodeService.cleanup(outputFolder, encoreJob)
}
}

Expand Down Expand Up @@ -268,6 +270,10 @@ class EncoreService(
}

private fun initJob(encoreJob: EncoreJob) {
encoreJob.inputs.forEach { input ->
input.accessUri = remoteFileService.getAccessUri(input.uri)
}

encoreJob.inputs.forEach { input ->
mediaAnalyzerService.analyzeInput(input)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ class FfmpegExecutor(
errorLines.add(line)
throw RuntimeException(
"Coding might not be compatible on all devices:\n${
errorLines.joinToString(
"\n"
)
errorLines.joinToString(
"\n"
)
}"
)
}
Expand Down
Loading