Sign Up

Stream Conditions for Video Encoding Workflows

Roland Kersche

Written by:
July 4th, 2017

Stream Conditions for your adaptive streaming workflow

When an input file doesn’t fit your encoding profile settings, it can cause problems. Stream Conditions allow you to automatically deal with this on the fly, saving you processing power and encoding time.

In a standard video encoding workflow, the profile configurations tell the encoder which streams to generate for each video. In some situations, such as a video platform, where users deliver the videos, the input might not fit the the output profiles. This will either waste CPU time, stop your encoding workflow, or force you to run an additional analysis cycle before each encoding.

To illustrate the problem, let’s look at some typical real life examples. A full HD profile using H.264 usually looks something like this:

  • 1080p with 7.5Mbps, 5.8Mbps, 4.3Mbps
  • 720p with 3.0Mbps, 2.35Mbps
  • 480p with 1.75Mbps, 1.05Mbps
  • 384p with 0.76Mbps, 0.56Mbps
  • 288p with 0.375Mbps
  • 240p with 0.235Mbps

In some cases it is not always beneficial to encode every representation. Take for example an input file with 720p and 4.0Mbps. Upscaling this file to 1080p does not make sense as upscaling cannot improve quality. Even though the output will be a larger file, it will still look like a 720p profile when it is played on a 1080p screen. AFrame rate is another potential mismatch: let’s say the encoding profile used will generate an output with 30fps. If the input file has 60fps the output would be converted from 60fps down to 30fps, resulting in output that’s less smooth than the original file. Bitrate is another example: think about an input file that has 1080p, but only has a bitrate of 5 Mbps. We definitely want to encode the input with a 1080p representation, but surely not with 7.5Mbps, as the input file has a lower bitrate.

Up until now, solving these input/output problems have usually relied on an extra analysis step in the workflow and/or human intervention, depending on your implementation, which is time consuming and expensive. But the good folks at Bitmovin have created a solution that can automatically resolve these mismatches on the fly without the need to analyse the input file before encoding it.

Stream conditions avoid the necessity to run an extra analysis of your input files

Introducing Stream Conditions

Stream Conditions are a new feature in our encoding stack that allows you to pre-configure the behaviour of your encoder based on the input files that it receives. Before adding streams to an encoding, the conditions will be checked and if the condition evaluates to false the stream will not be encoded. Furthermore all muxings depending on this streams will also be ignored. This makes it quite easy to only have one encoding configuration for all input files.

Depending on the properties of the input file the encoder dynamically decides if this representation should be created, which will have several advantages:

  • Reduce Encoding costs: only streams will be encoded that match the conditions
  • Reduce Storage costs & CDN costs: obviously the streams that are not encoded will also be not stored, therefore there are less storage & CDN costs
  • Avoid upscaling and quality loss
  • Avoid resampling
  • Save time by skipping the analysis process. The conditions will be evaluated during the encoding process which will only take a few milliseconds.
  • Simplify your workflow

Stream conditions make video encoding workflows more efficient

To show you how it works, let’s start with a simple example using our bitmovin-java API client to demonstrate the use of conditions. Let’s assume our input file is a 720p input with a bitrate of 5Mbps. For simplicity let’s not take the full encoding profile as described, but just use two of them:

  • 1080p with 7.0 Mbps
  • 1080p with 4.3 Mbps

It’s pretty clear that we don’t want to encode the 1080p representation as this would result in quality loss from the 720p input. To keep the example simple, we will skip the creating of the encoding, inputs and output and will focus on the creation of the streams and the conditions. However, the complete example can be found here.

Just as a reminder, here is how to set up a stream without conditions, as it has been done before.

 
// Create the stream object
Stream videoStream1080p = new Stream();
// Set the codec configuration (H264, 1080p, 7.0Mbps, 30fps)
videoStream1080p.setCodecConfigId(videoConfiguration1080p.getId());
// Set the input file
videoStream1080p.setInputStreams(Collections.singleton(inputStreamVideo));
// Create the stream on the bitmovin API
videoStream1080p = bitmovinApi.encoding.stream.addStream(encoding, videoStream1080p);

In this example we create an H.264 stream with a resolution of 1080p, 30 frames per second and a bitrate of 7.0Mbps. Let’s remember: we have an input file with only 5 Mbps and 720p. Hence it makes no sense to encode this with the 1080p configuration. To avoid this and still keep the same configuration and code for all of our encodings independent of the input file, we’ll use a condition on the input height.

Stream videoStream1080p = new Stream();
videoStream1080p.setCodecConfigId(videoConfiguration1080p.getId());
videoStream1080p.setInputStreams(Collections.singleton(inputStreamVideo));
// Input height must be at least 1080
videoStream1080p.setConditions(Collections.singletonList(new Condition(ConditionAttribute.HEIGHT, ">=", "1080")));
videoStream1080p = bitmovinApi.encoding.stream.addStream(encoding, videoStream1080p);

With this single line of code we tell the encoder to check the input height of the file and only encode this specific stream when the input height is greater than 1080. Of course we also support other operators, like less than, equal, unequal, etc. For a detailed overview please have a look into our API specifications. At the moment we have included the following attributes of the input file that can be checked:

  • Height
  • Width
  • Frames per Second
  • Bitrate

We will continuously integrate new attributes that can be checked against.

But wait, you probably remember at the beginning we were talking about an input file with 1080p and 5 Mbps. This input file could be encoded into 1080p and 4.3Mbps. Still we want to use one encoding profile for every input file, so we do not only have to check for the bitrate but also for the height of the input file. How can we do this? It turns out, we have also included conjunctions in our latest feature :-). You can combine multiple conditions with an AND/OR conjunction.

Let’s jump right into an example:

 
Stream videoStream1080p_4_3Mbps = new Stream();
videoStream1080p_4_3Mbps.setCodecConfigId(videoConfiguration1080p_4_3Mbps.getId());
videoStream1080p_4_3Mbps.setInputStreams(Collections.singleton(inputStreamVideo));
// Create an AND conjunction
AndConjunction andConjunction1080_4_3Mbps = new AndConjunction();
andConjunction1080_4_3Mbps.setConditions(Arrays.asList(
       // Height should be equal or greater than 1080
       new Condition(ConditionAttribute.HEIGHT, ">=", "1080"),
       // Bitrate should be equal or greater than 4300000
       new Condition(ConditionAttribute.BITRATE, ">=", "4300000"))
);
videoStream1080p_4_3Mbps.setConditions(andConjunction1080_4_3Mbps);
videoStream1080p_4_3Mbps = bitmovinApi.encoding.stream.addStream(encoding, videoStream1080p_4_3Mbps);
 
Stream videoStream1080p_7_0Mbps = new Stream();
videoStream1080p_7_0Mbps.setCodecConfigId(videoConfiguration1080p_7_0Mbps.getId());
videoStream1080p_7_0Mbps.setInputStreams(Collections.singleton(inputStreamVideo));
// Create an AND conjunction
AndConjunction andConjunction1080_7_0Mbps = new AndConjunction();
andConjunction1080_7_0Mbps.setConditions(Arrays.asList(
       // Height should be equal or greater than 1080
       new Condition(ConditionAttribute.HEIGHT, ">=", "1080"),
       // Bitrate should be equal or greater than 4300000
       new Condition(ConditionAttribute.BITRATE, ">=", "7000000"))
);
videoStream1080p_7_0Mbps.setConditions(andConjunction1080_7_0Mbps);
videoStream1080p_7_0Mbps = bitmovinApi.encoding.stream.addStream(encoding, videoStream1080p_7_0Mbps);

Both streams will check the input for a height of at least 1080. Additionally the first stream will check if the input bandwidth is greater than 4.3Mbps, the second one will check if it’s greater than 7 Mbps. As the input file has a bandwidth of 5 Mbps the first one will be created, where the second one won’t.

You can also add more conditions, or even nest multiple conjunctions – it’s totally configurable and you will only have one encoding configuration for all your assets.

Conclusion

With this simple and mighty feature it’s super easy for you to get the best out of the input – and that all without knowing the exact parameters of the input file. The benefits are pretty obvious: Faster encoding times while also reducing the logic in your client. So it’s perfect for every use case where the parameters of the input file are not known in advance.

We haven’t found any disadvantages yet, but if you find some or have any questions please feel free to contact us at [email protected] – we will be happy to answer them :-).