Skip to content

Commit

Permalink
feat: add support for including complete profile in job
Browse files Browse the repository at this point in the history
A new field inlineProfile has been added to EncoreJob. In this field a complete profile
can be included, in which case this profile is used instead of the one specified in
the profile field. Exactly one of 'profile' and 'inlineProfile' should be specified.

Signed-off-by: Gustav Grusell <[email protected]>
  • Loading branch information
grusell committed Oct 31, 2023
1 parent 996ef2e commit 95d5fcd
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import jakarta.validation.constraints.Min
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotEmpty
import jakarta.validation.constraints.Positive
import se.svt.oss.encore.model.profile.Profile

@Validated
@RedisHash("encore-jobs", timeToLive = (60 * 60 * 24 * 7).toLong()) // 1 week ttl
Expand All @@ -46,10 +47,12 @@ data class EncoreJob(
@Schema(
description = "The name of the encoding profile to use",
example = "x264-animated",
required = true
nullable = true
)
@NotBlank
val profile: String,
val profile: String? = null,

@Schema(description = "Inline transcoding profile. If set, the profile field will be ignored", nullable = true)
val inlineProfile: Profile? = null,

@Schema(
description = "A directory path to where the output should be written",
Expand Down Expand Up @@ -168,7 +171,8 @@ data class EncoreJob(
val thumbnailTime: Double? = null,

@NotEmpty
val inputs: List<Input> = emptyList()
val inputs: List<Input> = emptyList(),

) {

@Schema(
Expand Down Expand Up @@ -199,6 +203,6 @@ data class EncoreJob(
"id" to id.toString(),
"file" to baseName,
"externalId" to (externalId ?: ""),
"profile" to profile
"profile" to (profile ?: inlineProfile?.name ?: "")
) + logContext
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ package se.svt.oss.encore.model.profile
import mu.KotlinLogging
import se.svt.oss.encore.model.output.Output

private val log = KotlinLogging.logger { }

abstract class AudioEncoder : OutputProducer {
private val log = KotlinLogging.logger { }

abstract val optional: Boolean

override val type: String
get() = this.javaClass.simpleName

fun logOrThrow(message: String): Output? {
if (optional) {
log.info { message }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ data class GenericVideoEncode(
override val format: String,
override val codec: String,
override val inputLabel: String = DEFAULT_VIDEO_LABEL
) : VideoEncode
) : VideoEncode {
override val type: String
get() = this.javaClass.simpleName
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import se.svt.oss.encore.config.EncodingProperties
import se.svt.oss.encore.model.EncoreJob
import se.svt.oss.encore.model.output.Output

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes(
JsonSubTypes.Type(value = AudioEncode::class, name = "AudioEncode"),
JsonSubTypes.Type(value = SimpleAudioEncode::class, name = "SimpleAudioEncode"),
Expand All @@ -22,4 +22,6 @@ import se.svt.oss.encore.model.output.Output
)
interface OutputProducer {
fun getOutput(job: EncoreJob, encodingProperties: EncodingProperties): Output?

val type: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import se.svt.oss.encore.model.mediafile.toParams
import se.svt.oss.encore.model.output.Output
import se.svt.oss.encore.model.output.VideoStreamEncode

private val log = KotlinLogging.logger { }

data class ThumbnailEncode(
val percentages: List<Int> = listOf(25, 50, 75),
val thumbnailWidth: Int = -2,
Expand All @@ -26,7 +28,8 @@ data class ThumbnailEncode(
val intervalSeconds: Double? = null
) : OutputProducer {

private val log = KotlinLogging.logger { }
override val type: String
get() = this.javaClass.simpleName

override fun getOutput(job: EncoreJob, encodingProperties: EncodingProperties): Output? {
if (job.segmentLength != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import se.svt.oss.encore.model.output.VideoStreamEncode
import se.svt.oss.mediaanalyzer.file.stringValue
import kotlin.io.path.createTempDirectory

private val log = KotlinLogging.logger { }

data class ThumbnailMapEncode(
val tileWidth: Int = 160,
val tileHeight: Int = 90,
Expand All @@ -28,7 +30,8 @@ data class ThumbnailMapEncode(
val inputLabel: String = DEFAULT_VIDEO_LABEL
) : OutputProducer {

private val log = KotlinLogging.logger { }
override val type: String
get() = this.javaClass.simpleName

override fun getOutput(job: EncoreJob, encodingProperties: EncodingProperties): Output? {
if (job.segmentLength != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ abstract class X26XEncode : VideoEncode {
abstract val codecParams: LinkedHashMap<String, String>
abstract val codecParamName: String

override val type: String
get() = this.javaClass.simpleName

override val params: Map<String, String>
get() = ffmpegParams + if (codecParams.isNotEmpty()) {
mapOf(codecParamName to codecParams.map { "${it.key}=${it.value}" }.joinToString(":"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ class FfmpegExecutor(
outputFolder: String,
progressChannel: SendChannel<Int>?
): List<MediaFile> {
val profile = profileService.getProfile(encoreJob.profile)
val profile = encoreJob.inlineProfile
?: encoreJob.profile?.let { profileService.getProfile(it) }
?: throw java.lang.RuntimeException("Job must have either profile or inlineProfile set")
val outputs = profile.encodes.mapNotNull {
it.getOutput(
encoreJob,
Expand All @@ -51,7 +53,7 @@ class FfmpegExecutor(
}

check(outputs.distinctBy { it.id }.size == outputs.size) {
"Profile ${encoreJob.profile} contains duplicate suffixes: ${outputs.map { it.id }}!"
"Profile ${profile.name} contains duplicate suffixes: ${outputs.map { it.id }}!"
}
val commands =
CommandBuilder(encoreJob, profile, outputFolder, encoreProperties.encoding).buildCommands(outputs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package se.svt.oss.encore

import com.fasterxml.jackson.dataformat.yaml.YAMLMapper
import org.awaitility.Awaitility.await
import org.awaitility.Durations
import org.junit.jupiter.api.Test
Expand All @@ -15,6 +16,7 @@ import se.svt.oss.encore.model.Status
import se.svt.oss.encore.model.input.AudioInput
import se.svt.oss.encore.model.input.VideoInput
import se.svt.oss.encore.model.profile.ChannelLayout
import se.svt.oss.encore.model.profile.Profile
import se.svt.oss.encore.model.queue.QueueItem
import se.svt.oss.mediaanalyzer.file.ImageFile
import se.svt.oss.mediaanalyzer.file.MediaContainer
Expand Down Expand Up @@ -161,6 +163,25 @@ class EncoreIntegrationTest : EncoreIntegrationTestBase() {
awaitJob(highPriorityJob.id) { it.status.isCompleted }
}

@Test
fun jobWithInlineProfileRunsSuccessfully(@TempDir outputDir: File) {
val inlineProfile: Profile = EncoreIntegrationTest::class.java.getResourceAsStream("/profile/program.yml").use {
YAMLMapper().findAndRegisterModules().readValue(it, Profile::class.java)
}

val job = job(outputDir = outputDir, file = testFileSurround)
.copy(profile = null, inlineProfile = inlineProfile)

successfulTest(
job,
defaultExpectedOutputFiles(outputDir, testFileSurround) +
listOf(
expectedFile(outputDir, testFileSurround, "STEREO_DE.mp4"),
expectedFile(outputDir, testFileSurround, "SURROUND.mp4")
)
)
}

@Test
fun jobIsCancelled(@TempDir outputDir: File) {
var createdJob = createAndAwaitJob(
Expand Down

0 comments on commit 95d5fcd

Please sign in to comment.