Note: All code examples in this post are licensed under a BSD style license.

There are a few players in interactive television in the UK, each using different technology to build interactive experiences. Freeview and Freesat both use profiles of MHEG-5 as the programming language and runtime environment. MHEG-5 is a fairly unusual language in that it uses an event-based programming model, with all state declared outside the scope of the events.

As far as programming languages go, MHEG-5 makes a developer's life difficult. There are no symbolic names so we have to refer to everything by an integer object ID. There are no data structures, so arrays have to be implemented manually by iterating on the object IDs and being very careful about where to stop. Links, the functional blocks of imperative code, can fire asynchronously and have no local state so care must be taken to avoid race conditions. There are only a few basic library calls, referred to as "resident programs", which enable basic string manipulation and limited interaction with the set-top-box. There isn't even an if statement, so conditional behaviour has to be implemented as a convoluted Test instruction which then fires one of two links for the true and false cases. To program MHEG-5 is to live in interesting times.

Fortunately, there are some useful tools for developers. The main one I use is MHEG Plus. MHEG Plus includes a compiler for a superset of the core language which allows developers to write better code faster. There are a lot of useful tools in MHEG Plus, like symbolic variable names, conditional statements, loops, macros, and simplified syntax. The compiler also has some useful error messages for when mistakes are made that helps to speed up the development-test cycle considerably. The project also includes an emulator that implements an MHEG-5 runtime engine as a Java GUI application. It isn't perfectly accurate but it's a lot quicker to get off the ground with an emulator than by having to construct a DSMCC carousel, multiplex it into a DVB transport stream, then modulate it and play it into a set-top-box for testing.

For this post, I have written a simple Hello World application in MHEG Plus. MHEG plus gives us the ability to import headers containing macros and constants, so let's first define some useful constants:

// src/constants.h.mheg+
{:IntegerConst SCREEN_WIDTH :ConstValue 720}
{:IntegerConst SCREEN_HEIGHT :ConstValue 576}

{:OctetStringConst FONT_ATTR_HEADING :ConstValue "plain.36.42.0"}
{:OctetStringConst FONT_ATTR_BODY :ConstValue "plain.26.32.0"}

{:OctetStringConst COLOUR_BLACK :ConstValue '=00=00=00=00'}
{:OctetStringConst COLOUR_WHITE :ConstValue '=FF=FF=FF=00'}

MHEG-5 applications begin with a file containing the definition of the application object. This file sits in the root of the carousel (the service gateway) and is either called startup or simply a.

// src/a
// application startup file
// application startup file

import(constants.h.mheg+)

{:Application
  ( '/a' 0 )
  :Items (
    {:Link lnkMain
      :EventSource 0          // Check this application...
      :EventType IsRunning    // ... for the IsRunning event
      :LinkEffect (
        :TransitionTo(( '~/slate.asn' 0 ) )
      )
    }
  )
  // basic defaults
  :BackgroundColour COLOUR_BLACK
  :TextCHook 10
  :TextColour COLOUR_WHITE
  :Font "rec://font/uk1"
  :FontAttributes FONT_ATTR_BODY
}

The application object contains some default style values and at least a basic link, listening for the application's IsRunning event. This link will be fired when you tune to a channel with this application mapped in its DVB Service Information, and the set-top-box has finished loading and parsing the code. There is only one statement in the body of this link, an instruction to transition to the slate scene. So let's define a slate scene:

// src/slate.mheg+
import(constants.h.mheg+)

{:Scene
  ( "~/slate.asn" 0 )
  :Items
  (
    {:Rectangle rectBackground
      :OrigBoxSize SCREEN_WIDTH SCREEN_HEIGHT
      :OrigPosition 0 0
      :OrigRefLineColour COLOUR_BLACK
      :OrigRefFillColour COLOUR_BLACK
    }

    {:Text txtHeading
      :OrigContent "Hello World!"
      :OrigBoxSize SCREEN_WIDTH 50
      :OrigPosition 0 [SCREEN_HEIGHT / 3]
      :FontAttributes FONT_ATTR_HEADING
      :TextColour COLOUR_WHITE
      :HJustification centre
    }

    {:Text txtExit
      :OrigContent "Press RED to exit"
      :OrigBoxSize SCREEN_WIDTH 30
      :OrigPosition 0 [(SCREEN_HEIGHT / 3) + 60]
      :FontAttributes FONT_ATTR_BODY
      :TextColour COLOUR_WHITE
      :HJustification centre
    }

    // Press RED to quit
    {:Link
      lnkKeyBlue
      :EventSource 0
      :EventType UserInput
      :EventData KeyRed
      :LinkEffect (
        :Quit()
      )
    }
  )
  :InputEventReg 3
  :SceneCS SCREEN_WIDTH SCREEN_HEIGHT
}

There is a bit more going on in the slate scene. First we import the symbolic constants as before. Next we define a scene, give it a name, and populate it with some items.

This scene declares a Rectangle to fill with the background colour and two Text objects containing hard-coded strings. That would be enough for a Hello world example, but for completeness I have also included a link listening for the Red key on the remote control, which simply causes the application to exit. Strictly speaking, a slate should not have exit behaviour, as that would defeat its purpose of hiding any video decoded by the receiver, but it's a good example of how to enable basic user interaction via the remote control.

The complete code, documentation, build system and license are available on GitHub.