The JavaTV Service Selection API

We've already seen how we can use JMF to change the video that we're displaying on the screen. The JavaTV service selection API also lets us change the video that is being displayed, but the purpose of the service selection API is much wider than that. Changing the video is a side-effect of using this API, and so we need to be very careful how we use this API because of the dramatic effect it can have on our applications. Service selection is the technical term for changing the channel in a digital TV system, which is the reason why the effects of this API are so widespread. In analog TV, changing the channel means that the TV is showing different audio and video than it was before you changed it. In digital TV, you can change the audio and video that you're presenting without changing the channel. Changing the channel in a digital TV system (or selecting a new service as it's more properly known in this case) may kill our application as well.

To understand this properly, you need to look at a digital TV platform from the perspective of services. A service in digital TV terminology means a single collection of service information, video and audio streams that can be presented together as a coherent entity. So, one TV channel (e.g. CNN or BBC) is a service. Services may have more than one audio or video stream, and the receiver will choose which ones to play. All clear so far?

JavaTV extends this to say that a service means a collection of service information, video and audio streams, and applications that can be presented together as a coherent entity (as shown in the diagram below). Now, what happens when you select a different service is that you select a different set of video and audio to present. The downside is that you may also select a different set of applications to present, and that set of applications may not include the current application. So, an application may end up killing itself as the result of a service selection operation.

The components of an MHP or OCAP service.

The components of an MHP or OCAP service

Services and service contexts

You'll notice that in that last paragraph, I used the word may a lot. There's a reason for that. Each service is presented within a service context, which is used both so that applications can determine what service they are a part of, and so that applications can select new services. The simple way of thinking about this is to imagine that a digital TV receiver that supports JavaTV has as many service contexts as it has tuners - this isn't entirely correct, but it helps illustrate the point. Since every service is presented within a service context, you have to choose which service context you want to present a service in. In most cases, the only service context that you can use is your own and this means that you may end up killing yourself.

So, from the perspective of presenting services and managing applications, the best (and probably only) place to start is the service context. The relationships are basically as follows:

  • A receiver has one or more service contexts. Each receiver will usually be a fixed maximum number of service contexts that can exist at any time, but this is not required.
  • Each service context that exists within a receiver may present a service. It is usually, but not always, the case that any service context that exists will be presenting a service.
  • Each service context that is presenting a service will contain two sets of objects. One of these will be a set of JMF Player objects presenting part of the A/V section of the service. The other will be a set of applications that run in that service.

It's best not to think about an MHP or OCAP receiver as running a set of services and a separate set of applications - the two are very much linked, and thinking about one outside the context of the other is a good way to confuse yourself and get in trouble.

The relationship between service contexts, Xlets and JMF players.

The relationship between service contexts, Xlets and JMF players

Since the set of running applications is linked to a service, selecting a new service will cause this set of application to change. Selecting a new service in the service context that contains the current application is a risky operation, and will lead to the application being terminated unless it is also signalled on the new service.

Using the service selection API

So, now that we've got that straightened out, how do you use this API? The first step is to get a javax.tv.service.selection.ServiceContext object that you can actually manipulate. Service contexts are created by the javax.tv.service.selection.ServiceContextFactory class. This class has the following interface:

public abstract class ServiceContextFactory
    extends java.lang.Object {

  public abstract ServiceContext createServiceContext();
  public static ServiceContextFactory getInstance();

  public abstract ServiceContext getServiceContext(
    XletContext ctx);
  public abstract ServiceContext[] getServiceContexts();
}

The ServiceContextFactory is a singleton object, and a reference to it is obtained by calling the getInstance() method. The other methods on the factory all allow an application to get access to a service context. An application can create a new service context using the createServiceContext() method, although this may fail if the receiver can't support any more service contexts.

In practise, this means that the maximum number of service contexts is usually equal to the number of separate tuners and MPEG decoders in the receiver (since presenting a service requires one tuner and one MPEG decoder). A receiver is guaranteed to have at least one of each, but any more than that may not be available.

The getServiceContexts() method returns an array of all the service contexts that the application has access to - typically, this means the service context that the application is running in and any other service contexts that have been created by the application. The final method allows an application to get a service context that a specific Xlet is executing in. This is occasionally useful, but cooperating applications are still uncommon (and multiple service contexts are even more uncommon) so it's not common to see this method used in current applications.

Once you have a reference to a service context, you can start to do useful things with it. The interface for a service context looks like this:

public interface ServiceContext {

  public void select(Locator[] components)
    throws InvalidLocatorException,
           InvalidServiceComponentException,
           java.lang.SecurityException;

  public void select(Service selection)
    throws java.lang.SecurityException;

  public void stop()
    throws java.lang.SecurityException;

  public void destroy()
    throws java.lang.SecurityException;

  public Service getService();

  public ServiceContentHandler[]
    getServiceContentHandlers()
    throws java.lang.SecurityException;

  public void addListener(
    ServiceContextListener listener);
  public removeListener(
    ServiceContextListener listener);
}

The two incarnations of the select() method are used to select a new service in the current service context. The first version of this method takes a javax.tv.service.Service object as an argument, which makes it useful for applications that use the JavaTV service information API.

Most MHP and OCAP applications will probably use the second version of the method, which takes an array of javax.tv.locator.Locator objects as an argument. Each locator refers to a component of the service, although a single locator can be used to refer to the entire service.

Once we've selected a service, the getService() method will return a javax.tv.service.Service object that corresponds to the service we're presenting.

The getServiceContentHandlers() method returns a set of objects that are responsible for presenting the content associated with the service. Each ServiceContentHandler is an object that presents one or more of the components of the service - these may be audio or video components, or they may be application components. Although MHP and OCAP does not explicitly say how applications are handled via this mechanism, the JavaTV specification is explicit that applications are handled in this way. Typically, a JMF Player will be a subclass of the ServiceContentHandler class, as will one of the classes relating closely to Xlets.

There is no guarantee of how many ServiceContentHandler objects will be returned by this method, though - different receivers may use different approaches for presenting media. For instance, some receivers may use a single JMF player for an entire service (MHP and OCAP receivers must do this, at least from the application's perspective), while others may use one player for video content and another for the accompanying audio track(s).

The service context state model

A service context may be in one of several states during its lifecycle. When a service context is created, it is in the Not Presenting state. Once the select() method has been called, the service context moves to the Presentation Pending state while the receiver carries out any operations needed to get the data for the new service and start presenting its components.

After the new service is actually being presented, the service context moves to the Presenting state. From this state, an application can either select a new service, or call the stop() method to stop all activity in the service context. This involves stopping and closing all JMF players that are presenting content from this service, and stopping all Xlets associated with it.

When the service context is no longer needed, the application calls the destroy() method. This allows the receiver to free any resources that the service context is using, and allows other applications (or other parts of the same application) to create a new service context using the resources that have just been freed.

Explicitly destroying a service context allows for scarce resources (in this case, the tuner that is probably associated with the service context) to be freed at an appropriate time. This makes life easier for the receiver when attempting to balance the needs of multiple concurrent applications, because this level of explicit resource management allows the receiver to make more informed decisions about resource allocation.

Service selection events

As with all of the other MHP or OCAP APIs, the service context allows application to register for events relating to that service context. These events are especially necessary in the case of service selection, because there are several possibilities that an application needs to be aware of when selecting new services.

The first of these possibilities is that everything has succeeded and the receiver is presenting the new service. The NormalContentEvent indicates that everything has succeeded, and that the new content is actually being presented.

Secondly, there is the possibility that some of the service components may be encrypted, and so the receiver has to display some other content from the service. In this case, the receiver generates an AlternativeContentEvent. This tells the application that some content is being presented, but it is not exactly the content that the application requested. This event isn't only generated when a service selection operation partially fails - it may also be generated at the end of a free preview period, for instance, when the encryption mechanism no longer authenticates the user or when the user's language choice is not available.

If everything goes really badly, the receiver may generate a SelectionFailedEvent. This is exactly what it seems - for some reason the receiver can't find the service. It could be that it's not being broadcast at the current time, for instance. A slightly less severe case is the PresentationTerminatedEvent, which is generated when an application calls the ServiceContext.stop() method.

Every application that does any service selection should monitor these events - after all, thee are the only indication that an application may have that something has gone wrong with a service selection operation, and most applications will look an awful lot better if they actually have the service that they're associated with being shown while the application is running. Without these events, an application will never know if something has caused the service to change.

When to use the service selection API

Now that we've seen the two options that we have for presenting media, now do we decide which one to use? The simple solution is that if you only want to change the audio or video that you're using, you should use the JMF APIs. Even if you want to present a different service, but without running any applications, you should use the JMF API.

The service selection API should only be used when you actually, really, absolutely want to present a completely new service, with the associated risk of killing your own application if you select the service in your own service context. The main difference between using the service selection API to select a new service and using JMF to create a new Player that presents the new service is that the service selection API will check the AIT for the new service.

The basic rule for applications when deciding to use service selection or JMF to accomplish a specific task is as follows: If you want to run applications on the new service, use the service selection API. If you don't, or only want to present some elements of a service, use JMF.