License

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) License. To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.

Copyright 2021 Sveriges Television AB

Introduction

This documentation is in a work-in-progress state. Not all features are yet described.

This is a living, breathing guide. If you’d like to contribute, fork us on GitHub!

What is Encore?

Encore is a scalable video transcoding[1] tool, built on Open Source giants like FFmpeg and Redisson.

Why does it exist?

Encore was created to scale, and abstract the transcoding power of FFmpeg, and to offer a simple solution for Transcoding - Transcoding-as-a-Service.

See the Project History for more about that.

Who is it for?

Encore is aimed at the advanced technical user that needs a scalable video transcoding tool - for example, as a part of their VOD (Video On Demand) transcoding pipeline.

How can you use it?

For example, you could deploy a number of Encore instances within your existing Kubernetes/Docker/other container solution and let the running instances communicate with a Redis cluster (as a Queue message broker). Every instance can than pick the next job lot, and you can scale your transcoding as needed.

Features

  • Scalable - queuing and concurrency options

  • Flexible profile configuration

  • Possibility to extend FFmpeg functionality

  • Tested and tried in production

Encore is not

Built with

  • Kotlin

  • Gradle

  • Spring Boot

  • FFmpeg

  • Redisson

  • and many other great projects

API, Profiles, and other formats will change now and then before we reach version 1.0.0 - Encore is currently considered a work-in-progress tool. It is used in daily production though.

Encore - built on Open Source Giants

svt encore at a glance

The User Guide

This part of the documentation describes how to build the project, and/or just use the pre-built jar to run it directly.

The appendix gives a few other examples of how you can run Encore

Building the project

Note: This step is not needed if you are trying out Encore with the Docker Compose example.

  • Requires:

    • JDK 11 or later

    • An available Redis instance (default configuration: locally installed port 6379)

    • FFmpeg and Mediainfo (we recommend the versions present in Homebrew-AVTools repository, which has been tested to work with Encore).

To build Encore, with all tests, do:

  $ ./gradlew clean build
  $ ./gradlew clean build -x test

Run Encore as a runnable jar

You can run Encore on your local machine by starting up a local developer Application Profile. Get the pre-built encore jar, or first build the project and get the jar from ./build/libs/

  foobar@foo:~$ SPRING_PROFILES_ACTIVE=local java -jar encore-x.y.z.jar
This will use the src/main/resources/application-local.yml configuration profile in Encore.

If the startup fails, verify that you are fulfilling the requirements

Next, Continue to Posting your first EncoreJob

Posting your first EncoreJob

  • This step requires:

    • A running instance of Encore

    • A inputdir - where you put source files

    • A outputdir - where you want the encoded files to end up

1) Use one of the files from the test resources in the Encore project and copy this to your inputdir (Verify that you have access permissions to the inputdir and outputdir.)

  $ cp src/test/resources/input/test.mp4 /tmp/input

2) Fire up a browser and http://<ENCORE_IP>:8080/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config#/ - i.e directly to the Encore Swagger Interface for Posting an EncoreJob.

3) Finally, Under POST, hit the "Try it out" button and POST this example. (Replace the outputFolder and uri with your outputdir and inputdir/filename.)

  {
  	"profile": "program",
  	"outputFolder": "/tmp/output",
  	"baseName": "quicktest_",
  	"inputs": [{
  		"uri": "/tmp/input/test.mp4",
  		"useFirstAudioStreams": 2,
  		"type": "AudioVideo"
  	}]
  }

You can now see some output in your terminal, and after a while you should see a couple of encoded files in the outputdir. Congratulations, you did your first encoding with Encore!

Now, find out more about specific options, see Dynamically generated Endpoint documentation, or see the Concepts.

The Concept/Technical Guide

If you are looking for concepts, structures and API descriptions, this part of the documentation is for you.

Concepts

Workflow

The workflow for an Encore Job

svt encore workflow
  1. An Encore Job is created

  2. It is offered to a Queue[2]. At some point, the Encore Job is picked up from the Queue

  3. Metadata needed for the coming transcoding task is gathered - for example, the input file is analyzed, the Profile is read.

  4. The transcoding is started - (FFmpeg transcoding starts)

  5. Output files are written to the configured destination

The Encore Job status field will roughly tell the observer where in the workflow it is currently (click to expand)
  // SPDX-FileCopyrightText: 2020 Sveriges Television AB
  //
  // SPDX-License-Identifier: EUPL-1.2

  package se.svt.oss.encore.model

  enum class Status(val isCompleted: Boolean) {
      NEW(false),
      QUEUED(false),
      IN_PROGRESS(false),
      SUCCESSFUL(true),
      FAILED(true),
      CANCELLED(true);

      val isCancelled: Boolean
          get() = this == CANCELLED
  }

Encore Job

An Encore Job is the aggregate for the information needed to transcode an input file - information like input files, profile and priority.

  • An Encore Job describes how an input file should be processed.

  • An Encore Job can have several output files, and one input file.

  • An Encore Job has a profile, which essentially describes how the job should configure it’s transcoding.

  • An Encore Job has a priority, which essentially describes how the job should be prioritized.

Profile

A Profile can be seen as a general abstraction of an FFmpeg configuration - example, bitrate to use, thumbnail generation, the codec to use.

  • A Profile specifies a big part of the configuration used by an Encore Job - metadata, FFmpeg configuration and specific codec configuration.

  • A Profile is specified in the yaml-format.

Scaling, distribution and queues

By using Redis (as a message broker/queue handler) and an internal concurrency scheduler, Encore can be scaled by setting up more instances, and allowing more concurrency.

  • An Encore instance uses Redis through Redisson’s Priority Blocking Queue. Jobs are scheduled on an Encore instance according to a given priority.

  • Furthermore, The Encore concurrency setting affects how many transcoding jobs be run on the same instance at once.

So, From an Encore instance’s point of view, the next Encore Job is chosen for transcoding according to its priority and according to how many concurrent tasks that are allowed on that instance.

svt encore queue

API/Endpoints

Encore is built using the framework Spring Rest Data offering the default endpoints generated from the usage of this framework. However, other custom Endpoints exist.

Encore’s endpoints are described and self-documented in the OpenAPIv3 format.

Dynamically generated Endpoint documentation

To see and interact with the endpoints live, consult your

for example, if you ran it on your localhost:

This is the recommended way - dynamic, powerful and always up-to-date.
svt encore api
Figure 1. Encore OpenAPI

Statically generated Endpoint documentation

To see a static version, refer to the Static Endpoints documentation.

The statically generated documentation needs to be cleaned up, as it is autogenerated, with quite a few quirks. This will happen when the project is mature.

Models

Encore Job

The Encore Job is a central object in Encore. The Encore Job fields are described in the Dynamically generated Endpoint documentation, but also in the

The Encore Job class in Kotlin (click to expand)
  // SPDX-FileCopyrightText: 2020 Sveriges Television AB
  //
  // SPDX-License-Identifier: EUPL-1.2

  package se.svt.oss.encore.model

  import com.fasterxml.jackson.annotation.JsonIgnore
  import io.swagger.v3.oas.annotations.media.Schema
  import io.swagger.v3.oas.annotations.tags.Tag
  import org.springframework.data.annotation.Id
  import org.springframework.data.redis.core.RedisHash
  import org.springframework.data.redis.core.index.Indexed
  import org.springframework.validation.annotation.Validated
  import se.svt.oss.encore.model.input.Input
  import se.svt.oss.mediaanalyzer.file.MediaFile
  import java.net.URI
  import java.time.OffsetDateTime
  import java.util.UUID
  import javax.validation.constraints.Max
  import javax.validation.constraints.Min
  import javax.validation.constraints.NotBlank
  import javax.validation.constraints.NotEmpty
  import javax.validation.constraints.Positive

  @Validated
  @RedisHash("encore-jobs", timeToLive = (60 * 60 * 24 * 7).toLong()) // 1 week ttl
  @Tag(name = "encorejob")
  data class EncoreJob(

      @Schema(
          description = "The Encore Internal EncoreJob Identity", example = "fb2baa17-8972-451b-bb1e-1bc773283476",
          accessMode = Schema.AccessMode.READ_ONLY, hidden = false, defaultValue = "A random UUID"
      )
      @Id val id: UUID = UUID.randomUUID(),

      @Schema(
          description = "External id - for external backreference", example = "any-string",
          nullable = true
      )
      val externalId: String? = null,

      @Schema(
          description = "The name of the encoding profile to use",
          example = "x264-animated", required = true
      )
      @NotBlank
      val profile: String,

      @Schema(
          description = "A directory path to where the output should be written",
          example = "/an/output/path/dir", required = true
      )
      @NotBlank
      val outputFolder: String,

      @Schema(
          description = "Base filename of output files",
          example = "any_file", required = true
      )
      @NotBlank
      val baseName: String,

      @Schema(
          description = "The Creation date for the EncoreJob",
          example = "2021-04-22T03:00:48.759168+02:00", accessMode = Schema.AccessMode.READ_ONLY,
          defaultValue = "now()"
      )
      @Indexed
      val createdDate: OffsetDateTime = OffsetDateTime.now(),

      @Schema(
          description = "An url to which the progress status callback should be directed",
          example = "http://projectx/encorecallback", nullable = true
      )
      val progressCallbackUri: URI? = null,

      @Schema(
          description = "The queue priority of the EncoreJob",
          defaultValue = "0", minimum = "0", maximum = "100"
      )
      @Min(0)
      @Max(100)
      val priority: Int = 0,

      @Schema(
          description = "The exception message, if the EncoreJob failed",
          example = "input/output error", accessMode = Schema.AccessMode.READ_ONLY, nullable = true
      )
      var message: String? = null,

      @Schema(
          description = "The EncoreJob progress",
          example = "57", accessMode = Schema.AccessMode.READ_ONLY, defaultValue = "0"
      )
      var progress: Int = 0,

      @Schema(
          description = "The Encoding speed of the job (compared to it's play speed/input duration)",
          example = "0.334", accessMode = Schema.AccessMode.READ_ONLY, nullable = true
      )
      var speed: Double? = null,

      @Schema(
          description = "The time for when the EncoreJob was picked from the queue)",
          example = "2021-04-19T07:20:43.819141+02:00", accessMode = Schema.AccessMode.READ_ONLY, nullable = true
      )
      var startedDate: OffsetDateTime? = null,

      @Schema(
          description = "The time for when the EncoreJob was completed (fail or success)",
          example = "2021-04-19T07:20:43.819141+02:00", accessMode = Schema.AccessMode.READ_ONLY, nullable = true
      )
      var completedDate: OffsetDateTime? = null,

      @Schema(
          description = "Instruct Encore to overlay encoding metadata on the encoded video stream",
          defaultValue = "false"
      )
      val debugOverlay: Boolean = false,

      @Schema(
          description = "Key/Values to append to the MDC log context", defaultValue = "{}"
      )
      val logContext: Map<String, String> = emptyMap(),

      @Schema(description = "Seek to given time in seconds before encoding output.", nullable = true, example = "60.0")
      val seekTo: Double? = null,

      @Schema(description = "Limit output to given duration.", nullable = true, example = "60.0")
      val duration: Double? = null,

      @Schema(
          description = "Time in seconds for when the thumbnail should be picked. Overrides profile configuration for thumbnails",
          example = "1800.5", nullable = true
      )
      @Positive
      val thumbnailTime: Double? = null,

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

      @Schema(
          description = "Analyzed models of the output files",
          accessMode = Schema.AccessMode.READ_ONLY
      )
      var output = emptyList<MediaFile>()

      @Schema(
          description = "The Job Status",
          accessMode = Schema.AccessMode.READ_ONLY
      )

      @Indexed
      var status = Status.NEW
          set(value) {
              field = value
              if (value.isCompleted) {
                  completedDate = OffsetDateTime.now()
              }
              if (value == Status.IN_PROGRESS) {
                  startedDate = OffsetDateTime.now()
              }
          }

      val contextMap: Map<String, String>
          @JsonIgnore
          get() = mapOf(
              "id" to id.toString(),
              "file" to baseName,
              "externalId" to (externalId ?: ""),
              "profile" to profile
          ) + logContext
  }
svt encore job processing

Profile

See the Profiles section in the Appendix for concrete Profile examples.

A Profile consists of toplevel metadata, and a list of encoding configuration types - encodes.

Table 1. Profile metadata
Field Description Constraint Default

name

profile name

description

a helpful description of the profile

scaling

directly matching the FFmpeg scaling algorithm options

if given, one of the FFmpeg Scaling algorithm options

lanczos

encodes

a list of encode types (configurations) for the profile

AudioEncode X264Encode X265Encode ThumbnailEncode ThumbnailMapEncode

Profile Encode Types

Audio

The AudioEncode type is, as the name implies, for audio encoding.

The AudioEncode type can exist at two levels in a Profile configuration:

  1. As a field, audioEncode, in encodes: VideoEncode - the audio stream will be embedded in the output video container.

  2. As a separate Encode type in the encodes list - the audio stream will be written to a separate output filestream.

TODO: Describe the new mapping logic


Table 2. AudioEncode
Field Description Constraint Default

codec

FFmpeg audio codec library

libfdk_aac

bitrate

optional

samplerate

The audio sample rate in hz

48000

channels

number of channels

2

suffix

suffix for the audio output file

{codec]_{nrofchannels}.ch

params

map of FFmpeg parameters to the given codec - cutoff etc

filters

any optional filters to FFmpeg.

audioMixPreset

the name of the default AudioMixPreset to use, if any

"default"

skipIfNoAudioMixPreset

don’t encode audio if no mix was found

boolean

false

format

output file extension

mp4


Video

The VideoEncode type is, as the name implies, for video encoding.

VideoEncode is a base type, for building concrete (n)Encode type implementations on. Existing examples are X264Encode and X265Encode. It is not intended for direct usage.

Table 3. VideoEncode
Field Description Constraint Default

width

adds the scale filter (if scaling enabled)

null, or -2 if only height is given

height

adds the scale filter (if scaling enabled)

null, or -2 if only height is given

twoPass

true, false

If FFmpeg transcoding should be twoPass

true

params

general FFmpeg Encoding parameters (see examples, vsync etc)

filters

for adding extra FFmpeg Filters

audioEncode

AudioEncode or null

suffix

suffix added to the output filename

format

The file output format

codec

codec library to use

example: libx264

The X264Encode will encode to AVC (H.264) video using libx264.

Table 4. X264Encode
Field Description Constraint Default

fields from VideoEncode

x264-params

map of specific x264 codec library parameters

deblock, keyint, etc, see Overwriting default preset settings - FFmpeg x264 and Profiles for examples

format

mp4

codec

"libx264"

The X265Encode will encode to HEVC (H.265) video using libx265.

Table 5. X265Encode
Field Description Constraint Default

fields from VideoEncode

x265-params

map of specific x265 codec library parameters

deblock, scenecut, etc, see Passing Options for FFmpeg x265 and Profiles for examples

format

mp4

codec

"libx265"


Image

ThumbnailMapEncode generates a thumbnailmap from the input video.

Table 6. ThumbnailMapEncode
Field Description Constraint Default

aspectWidth

16

aspectHeight

9

tileHeight

90

cols

12

rows

20

ThumbnailEncode generates an image/images from the video. The extraction point/points are given as either a specific time, or, a list of percentages.

Table 7. ThumbnailEncode
Field Description Constraint Default

percentages

[25, 50, 75]

thumbnailWidth

-2

thumbnailHeight

1080

thumbnailTime

1080

The Community Guide

General community information like the F.A.Q and links to Encore mentions.

Frequently Asked Questions

How can I package my transcoded media for online streaming (DASH and HLS)?

You could have a look at Shaka Packager, Bento4, and configure them to process the output of Encore.

How can I generate my own OpenAPI documentation, for import into X

To create your own OpenAPI-export from Encore so that you can transform it as you wish:

  $ SPRING_PROFILES_ACTIVE=local ./gradlew clean generateOpenApiDocs

which will give you a build/openapi.json file for further processing.

For further information on how you can configure the API-generation, see the Springdoc Project

I have great idea/bugfix/improvement to Encore, how do I submit it?

Can I write a plugin/extension for doing X in Encore?

Currently, there are no extensions interface endpoints, but you could, for example, add transcoding functionality by writing an FFMpeg filter - some examples of how that can done can be found at FFmpeg proxy filters - have a peek at the Homebrew AVTools for how to build an FFmpeg with them.

Also, open a discussion issue if you have any ideas regarding contributions about extensions to Encore that you want to discuss.

How can I create a profile doing X?

Have a look at a few profiles from the documentation or project test examples to learn how you can set different options. Also, see if you can do what you can do what you want with pure FFmpeg. It should then be doable to map that configuration to a custom profile.

Articles and talks

Article: 2021 - Encore - licensed to once again transcode - an article about how, and why, we released Encore as Open Source and an introduction to how it basically works.

Video: 2019 - FOSS made us do it - How Encore and FOSS tools enables the Video Encoding at SVT

Article: 2018 Encore - video transcoding at it’s core - while interesting for the project origins, it contains partially non-valid information as the code has been largely rewritten since then.

Third-party extensions

Eyevinn IAF plugin: An IAF plugin for SVT Encore

Support

There is no formal support - none, nada, nothing - for Encore directly from the main contributors / maintainers - but we would love if people use and improve the project, so here are some suggestions if you are stuck.

File an Issue on GitHub

You can always file an issue, ask a question, open a discussion issue, and we will most likely respond to that, when time allows.

Stack Overflow

If your question does not contain sensitive information, please ask a question on Stack Overflow and use the tag 'svtencore'. Maybe some other users will help you.

Project History

The initiative

It was around 2018, and it was time to yet again update the license and the hardware for our proprietary non-flexible transcoding solution. Although that unnamed solution had served us very well, we also had a history of less than optimal transcoding, flexibility and support when things went wrong. Around that period, it happened that, our organisation was preparing the next "Tekniksprint". A "Tekniksprint" is an "innovate for an entire sprint" period, which happens two times a year at our organisation. That time, a small team gathered, with the purpose of seeing how much of a video transcoding solution that could be built in two weeks. So, with warts, hacks and shortcuts, we had a fun Sprint period, and also ended up with something almost usable, see links. And, yes, it even had GUI, so that we could make it pretty demo friendly. However, it was not very robust, and something we could build further upon.

By having a Proof-of-Concept project, we now could now walk the long and winding internal organisation road of convincing everyone that we could, and that we should re-write the project seriously - considering flexibility, features and support in comparison with others.

The development

It almost became a meme for us, but in 2019 we wrote ut, tested it, and ran it in production, with so good results, that we could ditch out proprietary solution. In fact, Encore helped us achieve better quality, better flexibility, and saved us money - the costs for developing the Encore solution was multitudes less than buying just another new stack.

We now had an easier way to work with new codecs and transcoding features that would be impossible, had we chosen another proprietary solution.

The Open Source Encore

We always had the intention to Open Source the solution, as we heard that others, mainly friends in Public Broadcast organisations, had an interest in the product. So during 2019/20 , with almost every Sprint-period, we tried to have at least a task that was directly prepared with Open Sourcing Encore. Slowly, but determined.

From license research, documentation, basic code cleanups, removing the most SVT-specific code, and releasing libraries that would make it possible to release the project itself. Warts and all, finally we got there.

So far, Encore has been a success story for us at SVT. We sincerely hope that it can benefit you, and with time grow to be an even greater transcoding solution.

/The Videocore Team at SVT

The Contributor Guide

We gratefully accept contributions via pull requests.

Use the issue tracker to suggest feature requests, report bugs, and ask questions. This is also a great way to connect with the developers of the project as well as others who are interested in this solution.

Changing the code-base

Generally speaking, you should fork this repository, make changes in your own fork, and then submit a pull-request. This is often called the Fork-and-Pull model

  • All contributions to this project will be released under the inbound=outbound norm, that is, they are submitted under the projects main license.

  • By submitting a pull request or filing a bug, issue, or feature request, you agree to comply with this waiver of copyright interest. Details can be found in the EUPL 1.2 or later.

  • All new code should have associated unit tests that validate implemented features and the presence or lack of defects.

  • Additionally, the code should follow any stylistic and architectural guidelines prescribed by the project. In the absence of such guidelines, mimic the styles and patterns in the existing code-base.

Sign-off and optionally Sign each Commit

As part of filing a pull request you agree to the DCO, Developer Certificate of Origin

A DCO is a lightweight way for a contributor to confirm that they wrote or otherwise have the right to submit code or documentation to a project.

To confirm that you agree to the DCO, you need to sign off your commits when sending us a pull request. Technically, this is done by supplying the -s/--signoff flag to git when committing:

$ git commit -s -m "add fix for the bug"

Optionally, you can also sign the commit with -S which also gives your commit a nice verified button on GitHub, but, it requires that you have a GPG keypair set up.

For mor information, see Sign commit on GitHub with GPG key

$ git commit -s -S -m "add fix for the bug"

For the difference in signoff and signing, see Git signoff vs signing

Git History

In order to maintain a high software quality standard, contributions should aim to follow these rules:

  • We pay more attention to the quality of commit messages. In general, we share the view on how commit messages should be written with the Git project itself:

  • Make separate commits for logically separate changes. For example, pure formatting changes that do not affect software behaviour usually do not belong in the same commit as changes to program logic.

  • Describe your changes well. Do not just repeat in prose what is "obvious" from the code, but provide a rationale explaining why you believe your change is necessary.

  • Describe your changes in the imperative. Instead of writing "Fixes an issue with transcoding" prefer "Fix an transcoding issue". Think about it like the commit only does something if it is applied. This usually results in more concise commit messages.

  • We are picky about whitespaces. Trailing whitespace and duplicate blank lines are simply a superfluous annoyance, and most Git tools flag them red in the diff anyway.

Have a look at basically any of Jeff King’s commits in the Git project.

Thank you for reading and happy contributing!

There are no more guides. You are now on your own. Thank you!.

Appendix

Application Properties

Beside the rich configuration possibilities of Spring Boot and friends, a few custom properties can be specified inside your application.properties file, inside your application.yml file, or as command line switches.

Table 8. Custom Application Properties - prefix: encore-settings
Key Default Description

local-temporary-encode

false

if true, encoding is done in a temporary folder on the Encore instance currently running the job. When done, the results are moved to the where the Encore Job’s output folder path is pointing.

audio-mix-presets

a default AudioMixPreset is created

a list of AudioMixPresets to use

concurrency

2

Max nr of currently running encoding tasks (property starts at 0, so 0 gives 1 task).

poll-initial-delay

10

initial wait time in seconds before task starts.

poll-delay

5

wait before polling, in seconds, for next task after compilation of the current task.

redis-key-prefix

encore

the prefix to identify your redis queue. Redis keys are named "$redisKeyPrefix-queue-$queueNo"

security.enabled

false

enables basic auth, TODO: describe basic security logic

security.user-password

password for regular user

security.admin-password

password for the admin user

open-api.title

SVT Encore OpenAPI

title shown in the OpenAPI url summary header

open-api.description

"Endpoints for Encore"

description shown in the OpenAPI url summary header

open-api.contact-name

contact name shown in the OpenAPI gui summary header

open-api.contact-email

contact email shown in the OpenAPI gui summary header

open-api.contact-url

contact url shown in the OpenAPI gui summary header

Table 9. AudioMixPreset
Field Description Constraint Default

fallback-to-auto

if true, sets the output audio channels to the number given by the AudioEncode field channel.

the following conditions needs to apply for it to take effect:

set to true

nr of audio channels in input file > 0

AudioEncode channels = 2 OR between 1 and the "nr of audio channels in input file"

true

default-pan

if configured, maps "autofound input audio channels" to the configured output channel configuration

input file channels: configured AudioEncode channel: the pan filter configuration to use

See the concrete Profiles for yaml example.

Any configuration found in pan-mapping would take precedence over this configuration

pan-mapping

if configured, maps "found input audio channels" to the configured matching output channel configurations

input file channels: configured AudioEncode channel: the pan filter configuration which should be used.

See the concrete encore-settings configuration example for yaml-examples.

Any configuration here takes precedence over configurations in default-pan

For, clarity, here is how an encore-setting configuration with audio-mix-presets could look like in real life.

encore-settings configuration example
  spring:
    jackson:
      time-zone: Europe/Stockholm

  profile:
    location: url:http://YOURPROFILESERVERURL/encore/prod/master/profiles.yml

  encore-settings:
    local-temporary-encode: true
    audio-mix-presets:
      default:
        fallback-to-auto: false
        default-pan:
          2: stereo| c0 = c0 + 0.707*c2 + 0.707*c4 | c1 = c1 + 0.707*c2 + 0.707*c5

        pan-mapping:
          6:
            2: stereo|c0=1.0*c0+0.707*c2+0.707*c4|c1=1.0*c1+0.707*c2+0.707*c5
      de:
        fallback-to-auto: false
        pan-mapping:
          6:
            2: stereo|c0<0.25*c0+1.5*c2+0.25*c4|c1<0.25*c1+1.5*c2+0.25*c5

  redis:
    subscription-connection-pool-size: 25
    connection-pool-size: 32
    connection-minimum-idle-size: 5
    uri: redis://YOURINSTANCE:6379
    db: 1

Running Encore

As a complement to the UserGuide, here is a few more examples on how you can try out Encore.

  • Using Docker-Compose

  • Using Spring Boot BootRun

  • Building an Docker Image and run Encore in a Docker Container

Example: using Docker Compose

  version: "3.7"

  services:
    redis:
      image: redis:alpine
      networks:
      - encorenet

    encore:
      image: ghcr.io/svt/encore-debian:latest
      depends_on:
        - redis
      environment:
        - SPRING_PROFILES_ACTIVE=local
        - SPRING_REDIS_HOST=redis
        - PROFILE_LOCATION=url:https://raw.githubusercontent.com/svt/encore-doc/main/src/docs/asciidoc/examples/profile/profiles.yml

      ports:
        - "8080:8080"
      volumes:
        - /tmp/input:/tmp/input:rw # where your put your source files
        - /tmp/output:/tmp/output:rw #put your output here
      networks:
      - encorenet

  networks:
   encorenet:
     driver: bridge

and then

Linux

  $ ./docker-compose up

Mac

  $ ./docker compose up

and you should see Encore starting up.

By default, there are two folders mapped to your host system by using the example docker-compose file. /tmp/input and /tmp/output, which we will refer to as the inputdir and the outputdir Change these (in the docker-compose file) to something that suits your environment if you are not running on Linux. For example /Users/<YOURUSER>/input:/tmp/input should be fine on macos.
Get the IP

On Linux: - To find the IP Docker-Compose creates (so that you can access Encore’s api).

  $ docker inspect <nameOfDirectoryYouAreRunningFrom>_encorenet
By design Docker-compose creates a network called <nameOfDirectoryYouAreRunningFrom>_encorenet

On macos: Use 'localhost'

Got the IP? - great! Continue to Posting your first EncoreJob

Example: run Encore with Spring Boot bootRun

You can run Encore on your local machine by starting up a local developer Application Profile:

In the Encore root folder:
  foobar@foo:~$ SPRING_PROFILES_ACTIVE=local ./gradlew clean bootRun
This will use the src/main/resources/application-local.yml configuration profile in Encore.

If the startup fails, verify that you are fulfilling the requirements

Example: run Encore in a Docker Image

  • Create or find a base FFmpeg/Mediainfo Docker image.

The given example installs a recent version of FFmpeg, with a few FFmpeg filters, and mediainfo. Modify as needed, using a tap of Homebrew as an installation base.

A FFmpeg Dockerfile example (click to expand)
  FROM ubuntu:20.04

  ENV LC_ALL C.UTF-8
  ENV LANG C.UTF-8
  ENV TZ Europe/Stockholm
  RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

  ARG USR="user"

  RUN apt-get update && apt-get -y --no-install-recommends install \
  build-essential \
  curl \
  sudo \
  file \
  locales \
  ruby \
  git \
  expect \
  openssh-client  \
  ca-certificates \
  openjdk-11-jre-headless \
  zip \
  unzip

  RUN localedef -i en_US -f UTF-8 en_US.UTF-8 && \
  useradd -ms /bin/bash ${USR} && \
  echo '${USR}  ALL=(ALL) NOPASSWD:ALL' >>/etc/sudoers && \
  echo 'Set disable_coredump false' >> /etc/sudo.conf

  RUN mkdir -m777 /ffmpeg-filters

  RUN bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

  ENV PATH=/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:$PATH
  ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-amd64/

  RUN brew tap svt/avtools && \
      brew install openssl@1.1 && \
      brew unlink openssl@1.1 && \
      brew install openssl@3 && \
      brew install ffmpeg-encore libsvg-proxy-filter libsrf-proxy-filter mediainfo && \
      sudo rm -rf "$(brew --cache)"

  RUN cp $(brew --prefix)/lib/libsvg_filter.so $(brew --prefix)/lib/libsrf_filter.so /ffmpeg-filters/

  USER ${USR}


  CMD ["ffmpeg", "-version"]
With the environment variable DOCKER_BASE_IMAGE pointing to your FFmpeg Docker Image
  foobar@foo:~$ docker build -t encore-docker --build-arg DOCKER_BASE_IMAGE=<yourdockerbaseimage:youjustbuilt> .

  foobar@foo:~$ docker run --network=host -v /tmp/input:/tmp/input -v /tmp/output:/tmp/output -e SPRING_PROFILES_ACTIVE='local' encore-docker

Homebrew AVTools

So you noticed that the FFmpeg Docker Example in the quickstart used the Repository Manager Brew? The creators of Encore, have released their Brew Formulas on GitHub: Homebrew AVTools.

For example, here is the Encore FFmpeg Brew Formula that with minor modifications, uses formulas for X264, X265, and SVT’s subtitle filter.

To use and install this version of FFmpeg locally, follow the examples given at the Homebrew AVTools README.
The SVT Encore FFmpeg Brew Formula
  # SPDX-FileCopyrightText: 2009-present, Homebrew contributors
  # SPDX-FileCopyrightText: 2021 Sveriges Television AB
  #
  # SPDX-License-Identifier: BSD-2-Clause

  class FfmpegEncore < Formula
    desc "Play, record, convert, and stream audio and video"
    homepage "https://ffmpeg.org/"
    url "https://ffmpeg.org/releases/ffmpeg-4.3.1.tar.xz"
    sha256 "ad009240d46e307b4e03a213a0f49c11b650e445b1f8be0dda2a9212b34d2ffb"
    license "GPL-3.0-or-later"
    revision 1
    head "https://github.com/FFmpeg/FFmpeg.git"

    option "with-ffplay", "Enable ffplay"

    depends_on "nasm" => :build
    depends_on "pkg-config" => :build
    depends_on "aom"
    depends_on "fdk-aac" => :recommended
    depends_on "fontconfig"
    depends_on "freetype"
    depends_on "lame"
    depends_on "libass"
    depends_on "libsoxr"
    depends_on "libssh"
    depends_on "libvmaf"
    depends_on "libvorbis"
    depends_on "libvpx"
    depends_on "openssl@3"
    depends_on "x264-encore"
    depends_on "x265-encore"

    uses_from_macos "bzip2"
    uses_from_macos "zlib"

    conflicts_with "ffmpeg", because: "it also ships with ffmpeg binary"

    resource "proxy_filter" do
      url "https://github.com/SVT/ffmpeg-filter-proxy/archive/v1.0.tar.gz"
      sha256 "9a9ddfe248ea299ffa5bf9643bed95913f00b3a9d4e03f402aebc3224e4f82f3"
    end

    if build.with? "ffplay"
      depends_on "libxv" unless OS.mac?
      depends_on "sdl2"
    end

    def install

      args = %W[
        --prefix=#{prefix}
        --enable-shared
        --enable-pthreads
        --enable-version3
        --enable-hardcoded-tables
        --enable-avresample
        --cc=#{ENV.cc}
        --host-cflags=#{ENV.cflags}
        --host-ldflags=#{ENV.ldflags}
        --enable-gpl
        --enable-libaom
        --enable-libmp3lame
        --enable-libvorbis
        --enable-libvpx
        --enable-libx264
        --enable-libx265
        --enable-lzma
        --enable-libass
        --enable-libfontconfig
        --enable-libfreetype
        --disable-libjack
        --disable-indev=jack
        --enable-libaom
        --enable-openssl
        --enable-libssh
        --enable-libvmaf
        --enable-nonfree
      ]

      if !build.without? "fdk-aac"
        args << "--enable-libfdk-aac"
      end

      args << "--enable-ffplay" if build.with? "ffplay"
      args << "--enable-videotoolbox" if OS.mac?

      # GPL-incompatible libraries, requires ffmpeg to build with "--enable-nonfree" flag, (unredistributable libraries)
      # Openssl IS GPL compatible since 3, but due to this patch
      # https://patchwork.ffmpeg.org/project/ffmpeg/patch/20200609001340.52369-1-rcombs@rcombs.me/
      # not being in this version we build from, we have to enable non-free anyway.
      # When FFmpeg base is upgraded (including that patch), we should only enable-nonfree when
      # fdk-aac is enabled (the default option)
      # args << "--enable-nonfree" if !build.without?("fdk-aac")

      resource("proxy_filter").stage do |stage|
        @proxyfilterpath = Dir.pwd
        stage.staging.retain!
      end
      cp_r Dir.glob("#{@proxyfilterpath}/*.c"), "libavfilter", verbose: true
      inreplace "libavfilter/allfilters.c",
                "extern AVFilter ff_vf_yadif;",
                "extern AVFilter ff_vf_yadif;\nextern AVFilter ff_vf_proxy;\n"
      inreplace "libavfilter/Makefile",
                "# video filters",
                "# video filters\nOBJS-\$(CONFIG_PROXY_FILTER) += vf_proxy.o\n"

      system "./configure", *args
      system "make", "install"

      # Build and install additional FFmpeg tools
      system "make", "alltools"
      bin.install Dir["tools/*"].select { |f| File.executable? f }

      # Fix for Non-executables that were installed to bin/
      mv bin/"python", pkgshare/"python", force: true
    end

    test do
      # Create an example mp4 file
      mp4out = testpath/"video.mp4"
      system bin/"ffmpeg", "-filter_complex", "testsrc=rate=1:duration=1", mp4out
      assert_predicate mp4out, :exist?
    end
  end
If you use Homebrew-AVTools, the version of FFmpeg in use might be updated without any notice.

Profiles

Profile example, main
  program: withforcekeyframe.yml
  youtube: video.yml
Profile example, x264 codec with forced keyframes
  name: program
  description: Program profile
  scaling: bicubic
  encodes:
    - type: X264Encode
      suffix: 3100
      twoPass: true
      height: 1080
      params:
        b:v: 3100k
        maxrate: 4700k
        bufsize: 6200k
        r: 25
        vsync: 1
        pix_fmt: yuv420p
        force_key_frames: expr:not(mod(n,96))
        profile:v: high
        level: 4.1
      x264-params:
        deblock: 0,0
        aq-mode: 1
        bframes: 6
        keyint: 192
        keyint_min: 96
        ref: 4
      audioEncode:
        type: AudioEncode
        codec: aac
        bitrate: 192k
        suffix: STEREO
    - type: X264Encode
      suffix: 2069
      twoPass: true
      height: 720
      params:
        b:v: 2069k
        maxrate: 3104k
        bufsize: 4138k
        r: 25
        vsync: 1
        pix_fmt: yuv420p
        force_key_frames: expr:not(mod(n,96))
        profile:v: main
        level: 3.1
      x264-params:
        deblock: 0,0
        aq-mode: 1
        bframes: 6
        keyint: 192
        keyint_min: 96
        ref: 4
      audioEncode:
        type: AudioEncode
        codec: aac
        bitrate: 128k
        suffix: STEREO
    - type: X264Encode
      suffix: 1312
      twoPass: true
      height: 540
      params:
        b:v: 1312k
        maxrate: 1968k
        bufsize: 2524k
        r: 25
        vsync: 1
        pix_fmt: yuv420p
        force_key_frames: expr:not(mod(n,96))
        level: 3.1
        profile:v: main
      x264-params:
        deblock: 0,0
        aq-mode: 1
        bframes: 6
        keyint: 192
        keyint_min: 96
        ref: 4
      audioEncode:
        type: AudioEncode
        codec: aac
        bitrate: 96k
        suffix: STEREO
    - type: X264Encode
      suffix: 806
      twoPass: true
      height: 360
      params:
        b:v: 806121
        maxrate: 1209182
        bufsize: 1612242
        r: 25
        vsync: 1
        pix_fmt: yuv420p
        force_key_frames: expr:not(mod(n,96))
        profile:v: main
        level: 3.1
      x264-params:
        deblock: 0,0
        aq-mode: 1
        bframes: 6
        keyint: 192
        keyint_min: 96
        ref: 4
      audioEncode:
        type: AudioEncode
        codec: aac
        bitrate: 96k
        suffix: STEREO
    - type: X264Encode
      suffix: 320
      twoPass: true
      height: 234
      params:
        b:v: 324051
        maxrate: 486077
        bufsize: 648102
        r: 25
        vsync: 1
        pix_fmt: yuv420p
        force_key_frames: expr:not(mod(n,96))
        profile:v: baseline
        level: 3.1
      x264-params:
        deblock: 0,0
        aq-mode: 1
        keyint: 192
        keyint_min: 96
        ref: 3
      audioEncode:
        type: AudioEncode
        codec: aac
        bitrate: 96k
        suffix: STEREO
    - type: AudioEncode
      codec: aac
      bitrate: 192k
      suffix: STEREO
      params:
        cutoff: 20000
    - type: AudioEncode
      codec: aac
      bitrate: 64k
      suffix: STEREO_LB
      params:
        cutoff: 14000
    - type: ThumbnailMapEncode
    - type: ThumbnailEncode
Profile example, x264 codec
  name: youtube
  description: Youtube upload
  encodes:
    - type: X264Encode
      suffix: 10000
      twoPass: false
      height: 1080
      params:
        crf: 15
        r: 25
        vsync: 1
        pix_fmt: yuv420p
        profile:v: high
        level: 5.1
      x264-params:
        vbv-maxrate: 10000
        vbv-bufsize: 20000
        deblock: 0,0
        aq-mode: 1
        bframes: 4
        keyint: 202
        keyint_min: 100
        ref: 8
      audioEncode:
        type: AudioEncode
        codec: aac
        bitrate: 256k
        suffix: STEREO

The Future

We have a few ideas where we would like to go with Encore. We are focusing on the features we need, but we are always open to suggestion and discussion, if you would like to contribute to the project.

Asciidoc Debug

Built-in
asciidoctor-version

2.0.10

safe-mode-name

unsafe

docdir

/home/runner/work/encore-doc/encore-doc/src/docs/asciidoc

docfile

/home/runner/work/encore-doc/encore-doc/src/docs/asciidoc/encore-documentation.adoc

imagesdir

{imagesdir}


1. Encoding = source file is uncompressed, Transcoding = source file is compressed. The distinction might not matter much in practice, but we prefer to use the term Transcoding in the Encore documentation
2. In reality the Encore Job itself is not offered to, or polled from the queue - it is an Object QueueItem, which holds the needed metadata for the Encore Job