Dolby
  • Up and Running
  • Amazon Web Services
  • Sources
  • Hybrik JSON
  • Basic Transcode Task
  • GOP Control
  • Connections Array
  • User Data
  • Watch Folders
  • Timecode
  • Thumbnail Image Extraction
  • Conditional Actions
  • Video Filters
  • Audio Filters
  • Working with Audio
  • Task Modifiers
  • Package Task
  • Analysis & Quality Control
  • Dolby Technologies
  • Additional Tasks
  • Hybrik Versions
  • QC Player
  • Machine Performance Analysis

    Conditional Processing

    Not all transcoding workflows are as straightforward as “convert format A to format B”. There are many scenarios where you want to do something like “if source has 2 channels of audio, do A, and if source has 6 channels of audio do B”. Hybrik offers several different types of conditional processing that allow you to handle a wide variety of scenarios. These types of conditional operations fall into three categories:

    • Conditionally generating targets or target tracks depending on the presence of specific source tracks. This uses the include_if_source_has object.
    • Conditionally generating targets or executing filters depending on certain conditions. This uses the include_conditions object.
    • And finally, modifying output parameters based on characteristics of the. This uses ternary operators of the form:
        <condition> ? <result if true> : <result if false>
      

    Conditional Track Generation: include_if_source_has

    Some media scenarios require specific output tracks to be created only when the source contains a certain input. In these cases, you can use the include_if_source_has property in the job JSON.

    Let’s suppose that you have input files coming in that either have 1 or 2 tracks of stereo audio. The second track could be the alternate language track, but not all of the input files have the alternate track. You would want to create the second output track conditionally based upon the presence of a second input track. Here’s what that would look like. Remember that the audio tracks are a zero-based array, so the first audio track is audio[0]:

    
    "targets": [
        {
            "file_pattern": "{source_basename}_converted{default_extension}",
            "existing_files": "replace",
            "container": {
                "kind": "mp4"
            },
            "video": {
                "height": 1080,
                "codec": "h264",
                "bitrate_kb": 5000,
                "max_bitrate_kb": 6000
            },
            "audio": [
                {
                    "codec": "aac_lc",
                    "bitrate_kb": 128,
                    "channels": 2
                },
                {
                    "include_if_source_has": [
                        "audio[1]"
                    ],
                    "codec": "aac_lc",
                    "bitrate_kb": 128,
                    "channels": 2
                }
            ]
        }
    ]
    
    

    Note that include_if_source_has is an array, and therefore you can specify multiple conditions that must be met for the output to be generated. In the example below, we want to create a single stereo track from the mono channels in the 7th and 8th input tracks only if both of those tracks exist in the source.

    
    {
        "include_if_source_has": [
            "audio[6]",
            "audio[7]"
        ],
        "codec": "aac_lc",
        "bitrate_kb": 128,
        "channels": 2,
        "source": [
            {
                "track": 6
            },
            {
                "track": 7
            }
        ]
    }
    
    

    You can test a source for the presence and number of the following types of input tracks: audio, subtitles, and timecode. Use the include_if_source_has element to conditionally create these types of outputs:

    • Any target in a transcode task (which may contain multiple tracks)
    • audio tracks in a transcode target
    • subtitle tracks in a transcode target
    • timecode tracks in a transcode target

    Conditional Track and Filter Creation: include_conditions

    The next type of conditional execution allows you to test for more general properties than whether a track exists. The include_conditions element lets you test for general properties of the source when deciding which targets or tracks to create. In addition, it also allows the conditional execution of video or audio filters. The conditional operators can reference many different types of source properties, including dimensions, duration, tracks, channels, etc. It can also reference the results of an analyzer step executed previously in the workflow.

    NOTE: It is possible to use conditions to filter out all targets from a transcode task which will cause the task to fail

    In the following example, if our input video is less than 1000 pixels high, we want to create only one output target at 576 pixels high. If the input video is greater than 1000 pixels high, we want to create two output targets – one at 720 and one at 1080.

    
    "targets": [
        {
            "file_pattern": "{source_basename}_576.mp4",
            "include_conditions": [
                "source.video.height < 1000"
            ],
            "existing_files": "replace",
            "container": "{{container}}",
            "video": {
                "codec": "h264",
                "bitrate_mode": "vbr",
                "bitrate_kb": 2000,
                "max_bitrate_kb": 2400,
                "height": 576,
                "width": 1024
            },
            "audio": [
                {
                    "channels": 2,
                    "codec": "aac_lc",
                    "bitrate_kb": 96
                }
            ]
        },
        {
            "file_pattern": "{source_basename}_720.mp4",
            "include_conditions": [
                "source.video.height >= 1000"
            ],
            "existing_files": "replace",
            "container": "{{container}}",
            "video": {
                "codec": "h264",
                "bitrate_mode": "vbr",
                "bitrate_kb": 3200,
                "max_bitrate_kb": 3640,
                "height": 720,
                "width": 1280
            },
            "audio": [
                {
                    "channels": 2,
                    "codec": "aac_lc",
                    "bitrate_kb": 96
                }
            ]
        },
        {
            "file_pattern": "{source_basename}_1080.mp4",
            "include_conditions": [
                "source.video.height >= 1000"
            ],
            "existing_files": "replace",
            "container": "{{container}}",
            "video": {
                "codec": "h264",
                "bitrate_mode": "vbr",
                "bitrate_kb": 4000,
                "max_bitrate_kb": 4800,
                "height": 1080,
                "width": 1920
            },
            "audio": [
                {
                    "channels": 2,
                    "codec": "aac_lc",
                    "bitrate_kb": 96
                }
            ]
        }
    ]
    
    

    The include_conditions array can also be used to conditionally apply a filter. In the example below, we want to apply a timecode-printing filter to the output, but only if the duration of the file is longer than 5 minutes. The JSON would look like this:

    
    "targets": [
        {
            "file_pattern": "{source_basename}_converted.mp4",
            "existing_files": "replace",
            "container": {
                "kind": "mp4"
            },
            "video": {
                "codec": "h264",
                "width": 720,
                "height": 480,
                "bitrate_kb": 6000,
                "profile": "baseline",
                "filters": [
                    {
                        "include_conditions": [
                            "source.duration_sec >= 600"
                        ],
                        "kind": "print_timecode",
                        "payload": {
                            "y": 100,
                            "x": 100,
                            "font": "sans",
                            "font_color": "white",
                            "background_color": "black",
                            "font_size": 32,
                            "timecode_kind": "timecode_auto",
                            "timecode_source": "media"
                        }
                    }
                ]
            }
        }
    ]
    
    

    Overrides and Ternary Operators

    The third and last type of conditional processing that Hybrik offers uses something called ternary operators. A ternary operator consists of a condition, a desired result if the condition evaluates true, and a result if the condition evaluates false. The basic structure looks like this: <condition> ? <result if true> : <result if false>.

    Suppose that we wanted to set the output bitrate based on the bitrate of the source. For example, if our input is less than 3Mbps, we want the output to be 3Mbps, but if the input is greater than 3Mbps, we want the output to be 8Mbps. This would look like:

    "overrides": {
        "bitrate_kb": "source.video.bitrate_kb >= 5000 ? 5000 : 3000",
    }
    

    This expression can be read as “is the source.video.bitrate_kb greater than or equal to 5000? If yes, then the video bitrate is set to 5000. If not (and the bitrate is below 5000), the video bitrate is set to 3000.”

    When submitting a JSON job to Hybrik, it will verify that any value specified for bitrate_kb is an integer, and seeing a bitrate_kb value such as source.video.bitrate_kb >= 3000 ? 8000 : 3000 would generate data validation errors like err": "Invalid operation" and "message": "should be <= 1000001". We therefore need to use the special object called “overrides”. This will cause expressions in the overrides object to override corresponding items in the enclosing object:

    
    "targets": [
        {
            "file_pattern": "{source_basename}_converted{default_extension}",
            "existing_files": "replace",
            "container": {
                "kind": "mp4"
            },
            "video": {
                "height": 1080,
                "codec": "h264",
                "bitrate_mode": "vbr",
                "overrides": {
                    "bitrate_kb": "source.video.bitrate_kb >= 5000 ? 5000 : 3000",
                    "max_bitrate_kb": "bitrate_kb * 1.20"
                }
            },
            "audio": [
                {
                    "codec": "aac_lc",
                    "bitrate_kb": 128,
                    "channels": 2
                }
            ]
        }
    ]
    
    

    The example sets the output bitrate depending on what the input bitrate is. It also sets the maximum output bitrate to be 20% higher than the base bitrate.

    You can’t use ternary operators directly as settings, they must be used in the special overrides object as shown above. If a parameter is specified in the video object itself AND in the overrides object, the overrides value will take precedent.

    Target Overrides based on Analyze Task Results

    If an analyze task is run prior to a transcode task, the resulting values from the analyze task can be accessed and used in a target override.

    The example below has an analyze task for detecting areas of silence in the source. This is followed by a transcode task in which an override exists to set trim values. In this case, the override will apply:

    1. a trim inpoint for the target at one second before the source’s first section of silence ends
      "trim.inpoint_sec": "deep_properties.audio[0].silence.leading_sec - 1"
    2. a trim outpoint after one second of silence has passed in the final section of silence in the source.
      "trim.outpoint_sec": "deep_properties.audio[0].silence.events.last()['begin'] + 1"

    The resulting output file will have 1-second “handles” of silence before and after audio content.

    
    {
            "uid": "analyze_task",
            "kind": "analyze",
            "payload": {
              "options": {
                "response_version": 2
              },
              "general_properties": {
                "enabled": true
              },
              "deep_properties": {
                "audio": {
                  "silence": {
                    "enabled": true,
                    "is_optional": true,
                    "noise_db": -60,
                    "duration_sec": 3
                  }
                }
              }
            }
          },
          {
            "uid": "transcode_task",
            "kind": "transcode",
            "task": {
              "retry_method": "fail"
            },
            "payload": {
              "location": {
                "storage_provider": "s3",
                "path": "{{destination_path}}"
              },
              "targets": [
                {
                  "overrides": {
                    "trim.inpoint_sec": "deep_properties.audio[0].silence.leading_sec - 1",
                    "trim.outpoint_sec": "deep_properties.audio[0].silence.events.last()['begin'] + 1"
                  },
                  "container": {
                    "kind": "mp4"  ...
    
    

    Example Conditions

    The include_conditions and ternary operators use conditions that evaluate as true or false. There are many different conditions that can be evaluated. Conditions can use math expressions from the math.js library (https://mathjs.org).

    Combining Multiple Conditions and Making Comparisons

    You can use logical operators like || and && (OR and AND) to combine multiple conditions.

    You can use the following to make comparisons:

    • == (equals)
    • != (does not equal)
    • > (greater than)
    • < (less than)
    • >= (greater than or equal to)
    • <= (less than or equal to)
    • ft_compare(A, B) (compare to floating point values)
    • equalText(A, B) (compare two string values)

    See examples below.

    Video Conditions

    NOTE: Depending on container type and codec type, some options may not be available

    • source.video.bitrate_kb == 30000
      • Is the source video’s bitrate exactly 30 mbps
    • (abs(source.video.bitrate_kb - 30000)<3000)
      • Is the source video’s bitrate within 3000k of 30000k
    • source.video.bitrate_kb >= 20000 && source.video.bitrate_kb <= 30000
      • Is the source video’s bitrate between 20 mbps and 30mbps
    • source.video.width == 720
      • Is the source video’s with 720 pixels
    • source.video.height == 512
      • Is the source video’s height exactly 512 pixels
    • ft_compare(source.video.frame_rate, 29.97) == 0
      • This is how you compare frame rates. This will round floating point values. If the frame rates match, ft_compare returns 0 so this expression is true if the source frame rate is 29.97
    • equalText(source.container.kind, 'mxf')
      • Is the container of kind mxf. equalText is the way to compare 2 string values
    • equalText(source.video.codec, 'mpeg2')
      • Is the video codec mpeg2
    • equalText(source.video.chroma_format, 'yuv422p')
      • Is the pixel format yuv422p
    • exists(source.video.bitrate_mode) && equalText(source.video.bitrate_mode, 'cbr')
      • Is the source video constant bitrate
    • general_properties.container.duration_sec >= 30
      • Is the source video 30 seconds or greater in duration
    • equalText(source.video.interlace_mode, 'progressive')
      • Is the source video progressive scan

    Audio Conditions

    NOTE: We often talk about audio tracks as “track 1” for the first track. In programming, we start with a 0-index so the first track is the 0th track

    • exists(source.audio)
      • Does the source contain audio
    • source.audio.length == 2
      • (where length represents the number of audio tracks present)
    • exists(source.audio) && source.audio.length == 2
      • Does the source’s audio have 2 tracks
      • NOTE: It is important to first check if the source has audio, otherwise this check will error on video-only sources
    • source.audio[0].channels == 2
      • Does the source’s first audio track have 2 channels
    • source.audio[0].sample_size == 16
      • Is the source’s first audio track 16 bit
    • source.audio[0].sample_rate == 48000
      • Is the source’s first audio sample rate 48k
    • equalText(source.audio[0].codec, 'pcm')
      • Is the first audio track’s codec pcm. The [0] indicates the first audio track

    Captions

    NOTE: These are based on metadata in the file. It is possible that captions may exist even if the checks fail

    • source.subtitle.length > 0
      • are text tracks present?
    • equalText(source.subtitle[0].format, 'EIA-608')
      • the [0] indicates the first text track)

    Timecode

    • source.timecode.length > 0
      • Does this source contain 1 or more timecodes. length represents the number of timecode tracks present
    • equalText(source.timecode[0].start_value, '01:00:00;00')
      • Does the source have timecode starting at 1 hour (dropframe). The [0] indicates the first timecode track
      • NOTE: the semicolon seconds/frames separator matters. If the timecode is ndf, the character will be a colon

    Quality Control

    The same conditions can be used in a qc task. See our Tutorial on Analysis and QC for further examples.

    Examples

    In the following example, based on the include_conditions example above, will behave differently depending on the source. On a 720p source, only 2 outputs will be created. With a 1080p or larger source, 3 outputs will be created. Additionally, the second track of audio is conditionally added to the outputs based on the source. The same logic could be applied discrete audio and video output as well.

    • Example Create targets from a source file with a frame height of 720 pixels
    • Example Make a decision about bitrate based on the source