The Tuner Control API

We've already seen APIs that will let us play media clips or even entire DVB services. These APIs are, however, both oriented towards presenting media; and there are times when we want to access another service or transport stream so that our application can read from a data stream rather than a media stream. If this data stream is on the same transport stream, then everything is simple, and we can just use a section filter to get the appropriate packets from the stream. If it's on a different transport stream, however, then things are more complex.

The major reason for this is the way that transport streams are broadcast. Every transport stream is broadcast on a different frequency, so switching transport streams is a much more complex task than switching between services within the same transport stream. First of all, the receiver has to know which frequency the new transport stream is being broadcast on (and this alone is not trivial). Second, the receiver has to tell the tuner to change to the new frequency, which may take a few seconds to switch and lock. Finally, the receiver has to examine the new transport stream and read enough of its service information to be able to navigate the stream.

Before we go any further, however, a word about naming conventions: the tuning API makes a lot of references to 'Network Interfaces'. Anything that's called a 'network interface' in this section refers to an interface to the broadcast network (e.g. a tuner) and not an interface to any other kind of network. This is not exactly true (since we can allow data to be transported over multicast IP), but in that case it is still basically a broadcast-style situation rather than a more conventional computer networking situation. A network interface is not the same as the network card in a PC.

Locators and transport streams

As we saw in the earlier section on locators, a locator can be used to refer to a transport stream as well as a service - we simply ignore the service-specific parts of the locator. However, the tuning API introduces us to some related concepts that we need to know about before we go any further.

The org.davic.mpeg and org.davic.mpeg.dvb packages provide some utility classes that are used by many of the DAVIC APIs. In particular, the classes it provides represent low-level concepts within MPEG such as transport streams, services and elementary streams. The object of most interest to us is the TransportStream object. Like a locator, this also provides a reference to a transport stream, but in a subtly different way. While a locator simply provides an abstract reference to a transport stream (like the URL that it's often written as), the TransportStream class refers to a transport stream as accessed via a specific network interface.

To illustrate the difference in PC terms, it's the difference between an object that refers to http://www.mhp.org (a locator) and an object that refers to http://www.mhp.org as accessed over the second network card in your PC (a TransportStream object).

Why is this important? Well, MHP supports three types of interface to the broadcast network - satellite, cable and terrestrial. Each of these interface types will have different characteristics, so sometimes knowing which interface type is being used is important. Some elements of the tuning API allow both locators and TransportStream objects to be used as arguments - in these cases, it's worth considering how important the network interface that gets used is. Of course, many if not most receivers will only have one network interface, so this may all be a moot point.

An introduction to the tuning API

OK, so switching transport streams is not simple and we can see why we've got an new API. Now, let's actually take a look at the four classes that make up the core of the API. The first of these is the org.davic.net.tuning.StreamTable class. This acts as a database that associates transport streams with the frequency they are broadcast on. This is about as much as you need to know about this class - how the information is acquired is not something the applications cares about (although it's usually done by a very time-consuming scan of the whole frequency range).

The second class that we care about is the org.davic.net.tuning.NetworkInterface. This class actually represents a network interface (tuner) that we can use to access a transport stream. There will be one NetworkInterface instance for every physical tuner in the receiver.

public class NetworkInterface {
  public TransportStream[]
    listAccessibleTransportStreams();
  public TransportStream getCurrentTransportStream();

  public int getDeliverySystemType();
  public Locator getLocator();
  public boolean isLocal();
  public boolean isReserved();

  public void addNetworkInterfaceListener(
    NetworkInterfaceListener listener);

  public void removeNetworkInterfaceListener
    NetworkInterfaceListener listener);
}

This is a read-only interface to the network interface - we will see how we actually tune later. The main purpose for this class is to provide a non-exclusive mechanism for applications to read the current status of the network interface. Most of the methods here are fairly obvious - the getCurrentTransportStream() and listAccessibleTransportStreams() return the current transport stream and a list of all transport streams that are accessible from this network interface respectively.

The getDeliverySystemType() method returns an integer value that identifies the type of network that this interface accesses. For instance, this could be a satellite interface, a cable front-end or a terrestrial interface.

The two most interesting methods are the isLocal() and isReserved() methods. The isLocal() method is a hangover from those optimistic times when home networking seemed like an up-and-coming idea, and where devices could access the functions of other devices over the network. This method was intended to allow a unified interface to tuners both in the receiver and in other devices, while still allowing the application to be able to tell if it was using a local or remote interface.

The isReserved() method tells the applications whether the network interface has been reserved by an application for tuning. The model here is actually quite familiar - many process may have read access to the network interface (i.e. they can read its settings via the NetworkInterface class, but only one can reserve it for read-write access (i.e. tuning).

The NetworkInterfaceManager class is responsible for maintaining the NetworkInterface instances in the receiver, and for providing applications with a means to get the available network interfaces. This is a singleton object, and a reference to it can be obtained using the NetworkInterfaceManager.getInstance() method.

The final class that's of interest to us is also the most useful. The NetworkInterfaceController class provides a mechanism for applications to actually control a network interface and use it to tune to a new transport stream:

public class NetworkInterfaceController
    implements ResourceProxy {

  public NetworkInterfaceController(
    ResourceClient rc);

  public void reserve(NetworkInterface ni,
    java.lang.Object requestData)
    throws NetworkInterfaceException;

  public void reserveFor(Locator locator,
    java.lang.Object requestData)
    throws NetworkInterfaceException;

  public void tune(Locator locator)
    throws NetworkInterfaceException;

  public void tune(TransportStream ts)
    throws NetworkInterfaceException;

  public void release()
    throws NetworkInterfaceException;

  public NetworkInterface getNetworkInterface();
  public ResourceClient getClient();
}

As we can see from the interface, the NetworkInterfaceController has a public constructor that allows instances of it to be created by applications. As such, this is more or less a classic example of an API which uses the resource notification API. Before the NetworkInterfaceController can be used to control a network interface, it must be bound to one of the network interfaces in the system. This can either be done using the reserve() method, which allows a specific network interface to be reserved, or the reserveFor() method, which reserves any free network interface that can access the transport stream referred to by the locator argument. Binding a network interface to the NetworkInterfaceController actually claims one of the scarce resources in the system (the tuner) and so it should only be done when absolutely necessary.

Once a NetworkInterfaceController has been successfully bound to a network interface, an application can actually start tuning. There are two versions of the tune() method, one taking a locator as an argument, the other taking a TransportStream object as an argument. The only difference here is that a TransportStream object refers to a specific transport stream on a specific network interface, while the locator doesn't specify which network interface should be used.

After the application is done with the network interface, the release() method allows the application to un-bind the network interface from the NetworkInterfaceController, thus freeing it for use by other applications.

In addition to these classes, there are several events and exceptions that are defined by the tuning API. We're not going to look at these here because there are no major surprises there, but suffice to say that an application that's using the tuning API should register itself for tuning-related events, because this is the only way that an application will know whether a tuning operation actually succeeded.