Conserving Media

It is often desirable to keep a permanent copy of media by saving it to a file. The media is then available for subsequent playback, processing, or broadcast. The original media to be saved might be captured from a microphone or camera, the result of some processing (such as transcoding), or a broadcast streaming across the network. In JMF, all these instances are represented as DataSources, and the class used to save media is known as a DataSink. Figure 8.20 shows these possible applications of a DataSink.

Figure 8.20. Uses of a DataSink object to save media to a file.


DataSink

The DataSink interface specifies an object that accepts media from a DataSource and renders it to some destination. Most commonly, that destination is a local file, but it could equally be writing or broadcasting across the network. Hence DataSinks are important objects, and are often seen in JMF programs.

As discussed earlier, a DataSink object is created through the Manager class with the static createDataSink() method. The method expects two parameters: the DataSource to which the DataSink will be connected and a MediaLocator specifying the destination that is the sink. The Manager class returns a DataSink object or throws a NoDataSinkException if it was unable to create the DataSink. The following code fragment shows the typical creation process:

DataSource source;
MediaLocator destination;
DataSink sink;
:        :
// Code that would see source and destination with valid values
:        :
try { sink = Manager.createDataSink(source,destination); }
catch (NoDataSinkException nde) { // Do something }

Figure 8.21 shows the methods of DataSink (excluding those inherited from MediaHandler and Controls which DataSink extends). Transfer is managed through the open(), start(), stop(), and close() methods. The open() method opens a connection to the destination (specified by the MediaLocator when the DataSource was created). The method might throw an IOException or a SecurityException (for example, not allowed to write to file system when an applet). After an output connection has been established with open(), transfer can be initiated with the start() method. This method also might throw an IOException. Transfer is halted with the stop() method (which can throw an IOException), whereas all resources are freed and the connection closed down with the close() method.

Figure 8.21. The DataSink interface.


DataSink objects generated DataSinkEvent events and can be listened to by those classes that implement DataSinkListener. The methods associated with events are addDataSinkListener() and removeDataSinkListener(). These events generated by DataSink objects are discussed in the next subsection.

setOutputLocator() and getOutputLocator(), as their names imply, are used as a means of specifying or obtaining the output MediaLocator—where the DataSink writes its output. The setOutputLocator() method is rarely used by user programs because this action is performed by the Manager class as part of the DataSink creation process. It is an error (an error is thrown) to call the method more than once. The getContentType() method returns a String specifying the content type of the media that is being consumed by the DataSink.

Employing a DataSink usually follows a number of simple steps:

1.
Create the DataSink (from a DataSource).

2.
Listen for events from the DataSink.

3.
open() and start() the DataSink.

4.
When the transfer is complete (for example, end of media reached), stop() and close() the DataSink.

DataSink Events

DataSink objects generate DataSinkEvent events in order to communicate the status of the DataSink. DataSinkEvent objects have two subclasses that indicate the two types of events a DataSink generates: DataSinkErrorEvent and EndOfStreamEvent. As should be evident from their names, these events either indicate an error with the DataSink (DataSinkErrorEvent) or the DataSource feeding the DataSink has signaled an end-of-stream (no more data).

Those objects wanting to receive events from a DataSink must implement the DataSinkListener interface. The interface consists of a single method:

void dataSinkUpdate(DataSinkEvent e)

Listing 8.8 shows a typical use of a DataSink object (sink) to preserve media coming from a DataSource object (source). Note the use of an anonymous class to listen to events generated by the DataSink and close it when the end of media stream has been detected. The anonymous listener class also performs error detection.

Listing 8.8 Use of a DataSink and Its Event Handling
DataSource    source;            // Assumed to already exist.
DataSink    sink;
MediaLocator    destinationLocation = ...;  // Create destination appropriately
try { sink = Manager.createDataSink(source,destinationLocation); }
catch (NoDataSinkException nde) {
  // Print an appropriate error message then rethrow exception
  throw nde;
}
sink.addDataSinkListener(new DataSinkListener() {
    public void dataSinkUpdate(DataSinkEvent e) {
      if (e instanceof EndOfStreamEvent) {
        sink.close();        // Will also stop the sink first
        source.disconnect();
      }
      else if (e instanceof DataSinkErrorEvent) {
        if (sink!=null)
          sink.close();
        if (source!=null)
          source.disconnect();
      }
    }    // End of dataSinkUpdate() method
  });    // End of addDataSinkListener() method

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset