Reading Service Information Using JavaTV

As we've already seen, the* package nd its sub-packages provide the classes that allow us to access service information (amongst other things). This functionality is split into several packages. The top-level package includes the basic concepts that we need for working with services.

For those of you who are familiar with other service information APIs, the JavaTV SI API may seem a little counter-intuitive.  Instead of focusing on SI tables and the internal structure of those tables, the JavaTV SI API takes a more task-based approach to service information access.  Instead of getting data from a particular SI or PSIP table, applications get the data that they need to carry out a specific task.   This approach allows the JavaTV API to be more generic and to avoid any dependencies on a particular service information format.  Because of this, the JavaTV SI API has a number of portability advantages; applications written using the JavaTV SI API for an MHP platform can be ported much more easily to an OCAP or ACAP platform, for instance.  JavaTV assumes nothing about how the underlying service information is organised, and how this information is actually read from the broadcast stream is up to the implementation.

In order to best look at these APIs, we need to look at how they were envisioned to be used. The designers of these APIs realised that access to service information is usually needed to carry out one of a number of specific tasks. These include:

  • Changing to a new service (service selection)
  • Finding out what services are currently available and how those services are organised into transport streams and bouquets
  • Accessing information about a service (such as the service name, type of content and other information)
  • Accessing information about events on a service or about the schedule of the service (for display in an EPG, for instance)

These tasks correspond roughly to the different packages under - this mapping isn't exact, since we also have some packages which describe the basic concepts related to services and transport streams.

Basic concepts

Before we go any further, let's look at how JavaTV handles the process of retrieving service information. Several methods on the API retrieve some type of service information, but the approach taken is always similar. All requests for service information are asynchronous - service information may not be cached in the receiver, and so this allows the receiver to try to retrieve this information from the broadcast stream without blocking the application. Each request takes an instance of the class as a parameter. This interface provides an event listener that is notified when that particular SI query completes. Since there may be several SI queries happening simultaneously, there may be several instances of the SIRequestor class in use by the same application. Unfortunately, there is no way of uniquely identifying an SI request, and so care must be taken to ensure that the application can differentiate between the different SI requests that may be issued.

Any method requesting SI information returns an SIRequest object. This provides one method - cancel() - which allows the application to cancel a pending SI request.

Once the SI request completes, the corresponding SIRequestor object is notified of the success or failure of the request. The interface for this class provides two methods, one which notifies the SIRequestor of a successful request, the other notifying the SIRequestor of an unsuccessful request. The interface for this class is given below:

public interface SIRequestor {
 public void notifyFailure(SIRequestFailureType reason);
 public void notifySuccess(SIRetrievable[] result);

As you can see from this, notification of a successful SI request includes an array of objects. This interface forms the base class for other objects that describe the SI data retrieved from the broadcast stream. We will see some of these subclasses later as we explore the various APIs in more depth. One of the more notable subclasses (and which has a number of important subclasses of its own is the SIElement interface. This is the superclass for all classes that contain data retrieved from the service information, where that service information is directly linked to a specific bouquet, service, transport stream or elementary stream. One of the main elements introduces by this interface is the getLocator() method, which allows an application to get the locator of a particular element. Thus, the best way of deciding whether the results of an SI request have this class as a superclass is to ask: "does this element have a locator?"

The core SI API

Now that we've seen how the basic SI retrieval mechanism works, let's take a look at how we can use the API to get service information. The core of the API is the class. This class is the starting point for an application to get service information, be it service information about services, or available transport streams or (broadcast) network interfaces. The interface for this class is shown below:

public abstract class SIManager extends java.lang.Object {

  protected SIManager();

  static SIManager createInstance();

  abstract ServiceList filterServices(
    ServiceFilter filter);

  abstract java.lang.String getPreferredLanguage();

  abstract RatingDimension getRatingDimension(
    java.lang.String name);

  abstract Service getService(Locator locator);

  abstract java.lang.String[] getSupportedDimensions();

  abstract Transport[] getTransports();

  abstract void registerInterest(
    Locator locator, boolean active);

  abstract SIRequest retrieveProgramEvent(
    Locator locator, SIRequestor requestor);

  abstract SIRequest retrieveServiceDetails(
    Locator locator,
	SIRequestor requestor);

  abstract SIRequest retrieveSIElement(
    Locator locator,
	SIRequestor requestor);

  abstract void setPreferredLanguage(
    java.lang.String language);

An application can use the createInstance() method to create a new SIManager object. This is not a singleton object, and each instance may be independent from other instances. This means that there may be a performance penalty for creating several instances since there may be less memory available to cache SI data and so an application should only create one copy of this class unless there is a good reason to do otherwise.

Access to transport information

The SIManager class can be used to access information about transport streams and available network interfaces. This allows an application to access information about available bouquets, networks and other information (including enumerating the available transport streams). Before we go any further in this section, it's important to stress one thing: in this context, a 'network interface' is an interface to the broadcast network, not to an IP network.

The method getTransports() returns an array of Transport objects that describe how services are grouped. A Transport object itself is not terribly interesting, but its three subclasses are more useful to us. Each of these subclasses groups services in a different way - either by transport stream (the TransportStreamCollection class) , by network (the NetworkCollection class) or by bouquet (the BouquetCollection class). These classes all work in a similar way, and so we will take the TransportStreamCollection class as an example.

public interface Transport {

  void addServiceDetailsChangeListener(
    ServiceDetailsChangeListener listener);

  void removeServiceDetailsChangeListener(
    ServiceDetailsChangeListener listener);

  DeliverySystemType getDeliverySystemType();


public interface TransportStreamCollection
  extends Transport {

  void addTransportStreamChangeListener(
    TransportStreamChangeListener listener);

  void removeTransportStreamChangeListener(
    TransportStreamChangeListener listener);

  SIRequest retrieveTransportStream(
    Locator locator,
    SIRequestor requestor);

  SIRequest retrieveTransportStreams(
    SIRequestor requestor);

The TransportStreamCollection class allows the user to retrieve information about either one transport stream class (using a locator to identify the transport stream of interest) or to retrieve all available transport streams. Each transport stream is represented by a TransportStream object. This encapsulates some of the information about a transport stream, such as a brief description and the transport stream ID. The TransportStreamCollection class also allows the application to register a listener for changes to the list of available transport streams.

As we've already mentioned, the BouquetCollection and NetworkCollection classes operate in similar ways. The only substantial difference is that the Network class enables the application to retrieve a list of transport streams in the network.

The Transport class has one useful feature which should be mentioned at this point. Unlike the DVB SI API, the JavaTV SI API allows applications to monitor changes in service information at the transport level. By registering a ServiceDetailsChangeListener with a Transport object, an application can be notified of any changes to the SI information for any service contained in that Transport object.

Access to information about services

Individual services are represented in JavaTV by the class. This provides the most basic information about the service - it's name, the type of the service (digital TV, digital radio, analog TV, NVOD services or several others) and its locator.

public interface Service {
  public Locator getLocator();
  java.lang.String getName();
  ServiceType getServiceType();

  int hashCode();

  boolean hasMultipleInstances();

  SIRequest retrieveDetails(SIRequestor requestor);

More details about the service can be obtained using the retrieveDetails() method. This issues an SI request that retrieves the other details of the service. As we have just seen, any SI requests ultimately return a subclass of the SIRetrievable interface. In this case, that subclass is the ServiceDetails interface. This includes information such as the long name of the service, the IDs of the CA system used to encrypt it (via the interface, which it implements) and some others. The ServiceDetails interface also allows us to retrieve further information about the program schedule for that service, a description of the service and a list of the components. We will examine some of these elements in more detail later, but for now we will concentrate of getting information about elementary streams. The retrieveComponents() method will retrieve a list of the elementary streams that make up the
service. In this case, the result of the SI request will be an array of objects that represent the elementary streams that make up the service.

public interface ServiceDetails
  extends SIElement, CAIdentification {

  void addServiceComponentChangeListener(
    ServiceComponentChangeListener listener);

  void removeServiceComponentChangeListener(

  DeliverySystemType getDeliverySystemType();
  java.lang.String getLongName();
  ProgramSchedule getProgramSchedule();
  Service getService();
  ServiceType getServiceType();

  SIRequest retrieveComponents(SIRequestor requestor);
  SIRequest retrieveServiceDescription(
    SIRequestor requestor);

The ServiceComponent class provides the basic information that you would expect to get for an elementary stream: the type of stream, a reference to the service with which it is associated and also the language associated with this stream. Of course, not all streams have a language component (video streams or data streams, for instance), and so in this case an empty value is returned. As with any SIElement, it is also possible to get a locator that acts as a reference to the element described by the SI data. in this case, that returns a locator referring to the elementary stream.

public interface ServiceComponent extends SIElement {

  java.lang.String getAssociatedLanguage();
  java.lang.String getName();
  StreamType getStreamType();

  Service getService();

Readers that know something about how SI works in the DVB system may notice a limitation imposed by this API. There appears to be no way for a ServiceComponent to belong to more than one service. Of course, this isn't a major limitation, since multiple ServiceComponent instances may refer to the same underlying elementary stream. The main implication of this is that application should not assume that just because two references to a ServiceComponent point to different Java objects, they don't refer to the same elementary stream.

Access to information about events

For an EPG, knowing about services is not enough - they also need to know about individual events within a service. The ServiceDetails interface includes the method getProgramSchedule(), which returns a ProgramSchedule object. This object provides a number of methods for requesting information about the current and next event, and also about all future events on that service.

public interface ProgramSchedule {

  void addListener(ProgramScheduleListener listener);
  void removeListener(ProgramScheduleListener listener);

  SIRequest retrieveCurrentProgramEvent(
    SIRequestor requestor);

  SIRequest retrieveNextProgramEvent(
    ProgramEvent event,
	SIRequestor requestor);

  SIRequest retrieveFutureProgramEvent(
    java.util.Date time,
	SIRequestor requestor);

  SIRequest retrieveFutureProgramEvents(
    java.util.Date begin,
	java.util.Date end,
	SIRequestor requestor);

  SIRequest retrieveProgramEvent(
    Locator locator,
	SIRequestor requestor);

As you can see from the interface, the methods for retrieving event information are all asynchronous. When they complete, a ProgramEvent object (or an array of them) will be returned to the SIRequestor. This object provides access to information about that specific event: its name, parental rating value, start and end times and duration, amongst other things. Like the ServiceDetails class, it also includes the retrieveComponents() method. in this case, though, the method returns a list of components for the event (if they are available) rather than the service. When retrieveComponents() is called for the current event on a service, the result is the same as if retrieveComponents() was called on the ServiceDetails object for that service.

public interface ProgramEvent extends SIElement {

  java.util.Date getStartTime();
  java.util.Date getEndTime();
  long getDuration();

  java.lang.String getName();
  ContentRatingAdvisory getRating();

  Service getService();

  SIRequest retrieveComponents(SIRequestor requestor);
  SIRequest retrieveDescription(SIRequestor requestor);

Program schedules can change over time, and so the ProgramSchedule interface allows an application to register a ProgramScheduleListener with the middleware.  Applications can use the ProgramScheduleListener to get notifications of changes in the program schedule.  This would allow an EPG, for instance, to be automatically notified when its information becomes outdated and to update its display.