As television viewers become more sophisticated, the demands for more
interactive technology will increase. To meet this demand, Sun is
releasing the Java TV API.
Java TV applications enhance the broadcast and viewing
experience by providing such features as programming information and
announcements, selectable applications such as the ability to play along
with a game show, broadcast data such as a stock ticker banner running
across the screen, or media control such as an interactive
program-related survey.
Television viewers with Java-enabled digital television receivers will be able to receive and interact with Java TV applications while watching network programming. The tool for interacting with Java TV applications is the viewer's television remote.
The newly released Java TV reference implementation implements of the Java TV specification that includes the Java TV and Java Media Framework (JMF) APIs. It requires a PersonalJava virtual machine and class libraries to run. The source base is currently available to licensed digital receiver manufacturers (Java TV licensees) so Java-enabled digital receivers should be available to consumers soon. Developers working for licensed companies that specialize in creating digital TV content will use the Java TV API to write digital television applications that either reside on or are downloaded to Java-enabled digital TV receivers.
The Java TV API will be available to the public at a later date through Sun's Community Source Licensing program. At that time, developers who work for television networks can use the Java TV API to write digital television applications that accompany network program broadcasts. While there are a wide variety of digital television receivers with varying capabilities, the Java TV API lets developers access their common features and scales across different receiver implementations.
This article introduces the Java TV API and presents short examples to show the structure of a Java TV application. However, unless you have access to a Java TV compliant environment, you cannot run and test the examples.
Another name for Java TV applications is Xlets. Like applets, Xlets are controlled by the software that runs them. In the case of an applet, the underlying software is a browser or the appletviewer tool. In the case of an Xlet, the underlying software is the digital television receiver or set-top box that supports the Java TV platform.
There is no main method and Xlets always implement the
Xlet interface. Like applets, Xlets have a life
cycle, and the life cycle method signatures are defined by the
Xlet interface.
The Xlet interface provides life cycle methods to signal
the following Xlet state changes:
All Java TV implementations have an application manager that calls the life cycle methods to move one or more Xlets through their various application states. For example, the viewer might be playing a game along with a game show and decide to check program listings. If the program listings and game are both Xlets, the software in the receiver is signaled that an Xlet is present when the viewer selects the program listings. At this point, the application manager pauses the game Xlet, and the receiver downloads the program listings Xlet into the receiver. The application manager loads and starts the program listing Xlet. If the viewer goes back to the game, the application manager pauses the program listing Xlet and starts the game Xlet.
The Xlet interface provides no implementations for
its life cycle methods. The developer provides application-specific
implementations for those methods by defining what happens at each point
in the Xlet life cycle. For example, the initXlet method
for the game Xlet might create the user interface components.
An Xlet can initiate some state changes itself and inform the application manager
of those state changes by invoking methods on the XletContext
interface. An XletContext object is passed to an Xlet when the
Xlet is initialized to give the Xlet a way to retrieve properties and signal
internal state changes.
The Java TV API reference implementation requires the PersonalJava Application Environment, Version 3.1, which provides core Java capabilities including the Abstract Window Toolkit (AWT) for building user interfaces. Some core Java packages are included in the PersonalJava platform without change from Java 2 Standard Edition, while others have been subsetted by removing functionality not appropriate for consumer products.
The Java TV API consists of classes and interfaces grouped into the following packages. These packages contain classes and interfaces to process the video, audio, and data sent to the digital receiver through the broadcast stream sent by the television networks. The digital receiver gets video, audio, and data from the broadcast stream and processes it through a broadcast media and data pipeline.
javax.tv.carousel provides access to remote file
and directory data contained in the broadcast.javax.tv.graphics enables simple compositing
(imposing one image over another to create one image) and
provides a container for building a user interfaced with AWT components.javax.tv.locator provides access to data and resources
addressable within a television receiver. Locators can reference broadcast
file systems, portions of service information, sources of audio and
visual content, and interactive applications.javax.tv.media defines extensions to the
Java Media Framework (JMF) to manage the broadcast media pipeline
which contains real-time audio and visual content.javax.tv.media.protocol provides access to generic
streaming data in the television broadcast.javax.tv.net provides access to Internet Protocol
(IP) datagrams transmitted in the broadcast stream.javax.tv.service provides access to the service
information (SI) database and basic SI APIs common to its
subpackages.
javax.tv.service.guide supports electronic
program guides with schedules, events, and ratings.javax.tv.service.navigation supports hierarchical service
and service information navigation.javax.tv.service.selection supports service selection
menus.javax.tv.service.transport lets you query the broadcast
delivery mechanisms.javax.tv.util supports the creation and management
of timer events.javax.tv.xlet provides life cycle and property management
methods.Xlets are typically small programs that perform simple functions such as electronic programming guides (EPGs), interactive games, enhanced content, managing the broadcast media pipeline, or managing the broadcast data signal.
This section presents two example Xlets. The first example displays a selectable list of services (channels); the second example retrieves and rotates through a list of programs and services presenting each service at the command line for 10 seconds. When run on a digital receiver, the second example Xlet presents the programs and services in the broadcast receiver where they can be retrieved by another Xlet and displayed in a user interface.
The SvcDispXlet class displays a list of selectable services. The user can select a service and display details related to the selected service.
The SvcDispXlet implements the Xlet
interface. In the initXlet method, the root
container of the user interface is retrieved and the user interface
created. Additionally, an instance of the SIManager
class is acquired. This object is used to access the SI information
that will be displayed by the Xlet.
The Xlet implements the ActionListener interface
so it can listen for action events generated by the user interface
components. In this example, the Xlet listens for action events
generated by the list of programs and services and by a
Refresh button. The Refresh button
updates the service list by adding new programs and services and
removing old ones.
Refresh button when the viewer uses
the remote to select it.
The underlying Java platform calls the Xlet's actionPerformed
method when action events are generated by user interface components for
which the Xlet has been made an action listener. The actionPerformed
method checks which component generated the event and passes it to the
UpdateList method where the appropriate action is taken based on
whether the list or button generated the event.
The SIManager object created with the call to
SIManager.createInstance() is the entry point
into the SI database.
The SIManager is used in the updateList
method to create and manage the programs and services list.
The Xlet defines a Retriever class, which
implements the SIRequestor interface. This interface is
implemented by application classes to receive the results of
asynchronous SI retrieval requests. The updateList method
calls the getPrograms method in the Retriever
class to retrieve the updated programs and services list.
import javax.tv.xlet.*;
import javax.tv.graphics.*;
import javax.tv.service.*;
import javax.tv.service.guide.*;
import javax.tv.service.navigation.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import java.text.DateFormat;
public class SvcDispXlet implements Xlet,
ActionListener {
private Container root_container = null;
private Panel panel = null;
private List slist = null;
private List plist = null;
private Button button = null;
private SIManager si_manager = null;
private Retriever retriever = new Retriever();
// init method
public void initXlet(XletContext ctx){
root_container = TVContainer.getRootContainer(ctx);
panel = new Panel();
panel.setBackground(Color.black);
root_container.setLayout(null);
root_container.setBounds(0,0,400,300);
root_container.add(panel);
panel.setBounds(0, 0,
root_container.getSize().height,
root_container.getSize().width - 50);
panel.setLayout(new GridLayout(2,1));
slist = new List();
slist.setBackground(Color.lightGray);
slist.addActionListener(this);
panel.add(slist);
plist = new List();
plist.setBackground(Color.lightGray);
panel.add(plist);
button = new Button("Refresh");
button.setBackground(Color.darkGray);
button.setForeground(Color.white);
button.addActionListener(this);
root_container.add(button);
button.setBounds(0,
root_container.getSize().width-50,
root_container.getSize().height,
50);
root_container.validate();
root_container.setVisible(true);
si_manager = SIManager.createInstance();
}
// start method
// Do an initial update of the slist
public void startXlet(){
panel.validate();
updateList(slist);
}
// pause
public void pauseXlet(){}
// destroy
public void destroyXlet(boolean unconditional){}
// called when the refresh button is pressed
// or service is selected in the list
public void actionPerformed(ActionEvent evt){
if(evt.getSource() == button){
this.updateList(slist);
plist.removeAll();
} else if (evt.getSource() == slist) {
this.updateList(plist);
}
}
private void updateList(List list){
//Clear out the old list
list.removeAll();
ServiceList collection =
si_manager.filterServices(null);
ServiceIterator si =
collection.createServiceIterator();
si.toEnd();
if(list == slist) {
while(si.hasPrevious()){
slist.addItem(
si.previousService().getName(), 0);
}
} else {
while(si.hasPrevious()){
Service s = si.previousService();
if(slist.getSelectedItem().
equals(s.getName())) {
retriever.getPrograms(s);
break;
}
}
}
}
class Retriever implements SIRequestor {
DateFormat dfmt =
DateFormat.getDateInstance(DateFormat.SHORT);
DateFormat tfmt =
DateFormat.getTimeInstance(DateFormat.SHORT);
void getPrograms(Service s) {
try{
SIManager.createInstance().
retrieveServiceDetails(
s.getLocator(), this);
} catch (Exception e) {
e.printStackTrace();
}
}
public void notifySuccess(
SIRetrievable[] result) {
if(result[0] instanceof ServiceDetails) {
try{
((ServiceDetails)result[0]).
getProgramSchedule().
retrieveFutureProgramEvents(new Date(),
new Date(System.currentTimeMillis()
+ 600000000), this);
catch (SIException e) {}
} else if (result[0] instanceof ProgramEvent) {
for(int i = 0; i < result.length; i++ ) {
ProgramEvent e = (ProgramEvent)result[i];
plist.addItem(e.getName() + " : "
+ dfmt.format(e.getStartTime())
+ " "
+ tfmt.format(e.getStartTime()));
}
}
}
public void notifyFailure(
SIRequestFailureType reason) {}
} //End Retriever class
} //End SvcDispXlet class
The ServiceSelectorXlet Xlet presents a rotating list of selectable services. Each service presents (is printed to the command line) for 10 seconds before rotating to the next service.
This Xlet implements ServiceContextListener so it
can listen to action events related to service contexts. A service
context represents an environment in which services are presented
in a broadcast receiver. The initXlet method creates a
ServiceContext object, and the Xlet uses the
ServiceContext object to select new services in the
broadcast media to be presented to the digital receiver.
The startXlet method checks for a service context object,
and creates one if none currently exists. Once a service context object
is created either by init or start, the Xlet is made an action listener
for action events generated by the service context. When a service
context event is generated, the underlying Java platform calls the
receiveServiceContextEvent method, which checks which
context event occurred and handles the action event accordingly.
The context event can indicate that the presentation changed, terminated, or failed. A change is a success, and the name of the changed service is printed to the command line with a notice that it succeeded. If the presentation terminated or failed, the reason for the termination or failure is retrieved and printed to the command line.
import javax.tv.locator.*;
import javax.tv.service.*;
import javax.tv.service.navigation.*;
import javax.tv.service.selection.*;
import javax.tv.xlet.*;
public class ServiceSelectorXlet
implements Xlet, ServiceContextListener {
ServiceContextFactory scf;
ServiceContext sc;
public void initXlet(XletContext context) {
scf = ServiceContextFactory.getInstance();
try{
sc = scf.getServiceContext(context);
} catch (Exception e) {}
}
public void pauseXlet() {}
public void destroyXlet( boolean unconditional ) {}
public void startXlet() {
//If fail to get a service context at initXlet
if(sc == null) {
try{
//Get all existing service contexts
ServiceContext[] ctxs = scf.getServiceContexts();
if(ctxs.length > 0) {
sc = ctxs[0];
} else {
// none available, try to create one then.
sc = scf.createServiceContext();
}
} catch (Exception e) {
System.out.println(
"Cannot obtain a valid ServiceContext: "
+ e);
return;
}
}
// Add ServiceContextListener
sc.addListener(this);
//Get all available Services from SIManager
ServiceList list = SIManager.createInstance().
filterServices(null);
//Iterate the list to tune
for(int i = 0; i < list.size(); i++ ) {
try{
Service s = list.getService(i);
System.out.println("selecting: "
+ s.getName());
sc.select(s);
Thread.sleep(20000);
} catch (Exception e) {
System.out.println("selection failed: " + e);
}
}
System.out.println("End of startXlet()");
}
//Invoked when selection completes
public void receiveServiceContextEvent(
ServiceContextEvent event) {
//Selection success
if(event instanceof PresentationChangedEvent) {
Service currentService =
event.getServiceContext().getService();
System.out.println("Selection succeeded for: "
+ currentService.getName());
//Selection terminated
} else if (event instanceof
PresentationTerminatedEvent) {
int reason = ((PresentationTerminatedEvent)event).
getReason();
System.out.println(
"Selection terminated with a reason: "
+ reason);
//Selection failed
} else if (event instanceof SelectionFailedEvent) {
int reason = ((
SelectionFailedEvent)event).getReason();
System.out.println(
"Selection failed with a reason: "
+ reason);
}
}
}
You can find more information on the Java TV platform at the following web sites and forums:
© 1994-2005 Sun Microsystems, Inc.