Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Java How to Program, Fourth Edition - Deitel H., Deitel P.pdf
Скачиваний:
58
Добавлен:
24.05.2014
Размер:
14.17 Mб
Скачать

Chapter 22

Java Media Framework and Java Sound (on CD)

1249

Performance Tip 22.1

Starting the Player takes less time if the Player has already prefetched the media before invoking start.

When the media clip ends, the Player generates a ControllerEvent of type EndOfMediaEvent. Most media players “rewind” the media clip after reaching the end so users can see or hear it again from the beginning. Method endOfMedia (lines 249– 253) handles the EndOfMediaEvent and resets the media clip to its beginning position by invoking Player method setMediaTime with a new Time (package javax.media) of 0 (line 251). Method setMediaTime sets the position of the media to a specific time location in the media, and is useful for “jumping” to a different part of the media. Line 252 invokes Player method stop, which ends media processing and places the Player in state Stopped. Invoking method start on a Stopped Player that has not been closed resumes media playback.

Often, it is desirable to configure the media before presentation. In the next section, we discuss interface Processor, which has more configuration capabilities than interface Player. Processors enable a program to format media and to save it to a file.

22.3 Formatting and Saving Captured Media

The Java Media Framework supports playing and saving media from capture devices such as microphones and video cameras. This type of media is known as captured media. Capture devices convert analog media into digitized media. For example, a program that captures an analog voice from a microphone attached to computer can create a digitized file from a that recording.

The JMF can access video capture devices that use Video for Windows drivers. Also, JMF supports audio capture devices that use the Windows Direct Sound Interface or the Java Sound Interface. The Video for Windows driver provides interfaces that enable Windows applications to access and process media from video devices. Similarly, Direct Sound and Java Sound are interfaces that enable applications to access sound devices such as hardware sound cards. The Solaris Performance Pack provides support for Java Sound and Sun Video capture devices on the Solaris platform. For a complete list of devices supported by JMF, visit JMF’s official Web site.

The SimplePlayer application presented in Fig. 22.1 allowed users to play media from a capture device. A locator string specifies the location of a capture device that the SimplePlayer demo accesses. For example, to test the SimplePlayer’s capturing capabilities, plug a microphone into a sound card’s microphone input jack. Typing the locator string javasound:// in the Open Location input dialog specifies that media should be input from the Java Sound-enabled capture device. The locator string initializes the MediaLocator that the Player needs for the audio material captured from the microphone.

Although SimplePlayer provides access to capture devices, it does not format the media or save captured data. Figure 22.2 presents a program that accomplishes these two new tasks. Class CapturePlayer provides more control over media properties via class

DataSource (package javax.media.protocol). Class DataSource provides the connection to the media source, then abstracts that connection to allow users to manipulate it. This program uses a DataSource to format the input and output media. The DataSource passes the formatted output media to a Controller, which will format it

1250

Java Media Framework and Java Sound (on CD)

Chapter 22

further so that it can be saved to a file. The Controller that handles media is a Processor, which extends interface Player. Finally, an object that implements interface DataSink saves the captured, formatted media. The Processor object handles the flow of data from the DataSource to the DataSink object.

JMF and Java Sound use media sources extensively, so programmers must understand the arrangement of data in the media. The header on a media source specifies the media format and other essential information needed to play the media. The media content usually consists of tracks of data, similar to tracks of music on a CD. Media sources may have one or more tracks that contain a variety of data. For example, a movie clip may contain one track for video, one track for audio, and a third track for closed-captioning data for the hearing-impaired.

1// Fig. 22.2: CapturePlayer.java

2 // Presents and saves captured media

3

4 // Java core packages

5import java.awt.*;

6 import java.awt.event.*;

7import java.io.*;

8 import java.util.*;

9

10// Java extension packages

11import javax.swing.*;

12import javax.swing.event.*;

13import javax.media.*;

14import javax.media.protocol.*;

15import javax.media.format.*;

16import javax.media.control.*;

17import javax.media.datasink.*;

19 public class CapturePlayer extends JFrame {

20

21// capture and save button

22private JButton captureButton;

24// component for save capture GUI

25private Component saveProgress;

27// formats of device's media, user-chosen format

28private Format formats[], selectedFormat;

29

30// controls of device's media formats

31private FormatControl formatControls[];

33// specification information of device

34private CaptureDeviceInfo deviceInfo;

36// vector containing all devices' information

37private Vector deviceList;

38

Fig. 22.2 Formatting and saving media from capture devices (part 1 of 9).

Chapter 22

Java Media Framework and Java Sound (on CD)

1251

39// input and output data sources

40private DataSource inSource, outSource;

42// file writer for captured media

43private DataSink dataSink;

44

45// processor to render and save captured media

46private Processor processor;

47

48// constructor for CapturePlayer

49public CapturePlayer()

50{

51super( "Capture Player" );

52

53// panel containing buttons

54JPanel buttonPanel = new JPanel();

55getContentPane().add( buttonPanel );

57// button for accessing and initializing capture devices

58captureButton = new JButton( "Capture and Save File" );

59buttonPanel.add( captureButton, BorderLayout.CENTER );

61// register an ActionListener for captureButton events

62captureButton.addActionListener( new CaptureHandler() );

64// turn on light rendering to enable compatibility

65// with lightweight GUI components

66Manager.setHint( Manager.LIGHTWEIGHT_RENDERER,

67

Boolean.TRUE );

68

 

69// register a WindowListener to frame events

70addWindowListener(

71

 

72

// anonymous inner class to handle WindowEvents

73

new WindowAdapter() {

74

 

75

// dispose processor

76

public void windowClosing(

77

WindowEvent windowEvent )

78

{

79

if ( processor != null )

80

processor.close();

81

}

82

 

83

} // end WindowAdapter

84

 

85

); // end call to method addWindowListener

86

 

87

} // end constructor

88

 

89// action handler class for setting up device

90private class CaptureHandler implements ActionListener {

Fig. 22.2 Formatting and saving media from capture devices (part 2 of 9).

1252

Java Media Framework and Java Sound (on CD)

Chapter 22

92// initialize and configure capture device

93public void actionPerformed( ActionEvent actionEvent )

94{

95

// put available devices' information into vector

96

deviceList =

97

CaptureDeviceManager.getDeviceList( null );

98

 

99

// if no devices found, display error message

100

if ( ( deviceList == null ) ||

101

( deviceList.size() == 0 ) ) {

102

 

103

showErrorMessage( "No capture devices found!" );

104

 

105

return;

106

}

107

 

108

// array of device names

109

String deviceNames[] = new String[ deviceList.size() ];

110

 

111

// store all device names into array of

112

// string for display purposes

113

for ( int i = 0; i < deviceList.size(); i++ ){

114

 

115

deviceInfo =

116

( CaptureDeviceInfo ) deviceList.elementAt( i );

117

 

118

deviceNames[ i ] = deviceInfo.getName();

119

}

120

 

121

// get vector index of selected device

122

int selectDeviceIndex =

123

getSelectedDeviceIndex( deviceNames );

124

 

125

if ( selectDeviceIndex == -1 )

126

return;

127

 

128

// get device information of selected device

129

deviceInfo = ( CaptureDeviceInfo )

130

deviceList.elementAt( selectDeviceIndex );

131

 

132

formats = deviceInfo.getFormats();

133

 

134

// if previous capture device opened, disconnect it

135

if ( inSource != null )

136

inSource.disconnect();

137

 

138

// obtain device and set its format

139

try {

140

 

141

// create data source from MediaLocator of device

142

inSource = Manager.createDataSource(

143

deviceInfo.getLocator() );

144

 

 

 

Fig. 22.2 Formatting and saving media from capture devices (part 3 of 9).

Chapter 22

Java Media Framework and Java Sound (on CD)

1253

 

 

 

 

145

 

// get format setting controls for device

 

146

 

formatControls = ( ( CaptureDevice )

 

147

 

inSource ).getFormatControls();

 

148

 

 

 

149

 

// get user's desired device format setting

 

150

 

selectedFormat = getSelectedFormat( formats );

 

151

 

 

 

152

 

if ( selectedFormat == null )

 

153

 

return;

 

154

 

 

 

155

 

setDeviceFormat( selectedFormat );

 

156

 

 

 

157

 

captureSaveFile();

 

158

 

 

 

159

 

} // end try

 

160

 

 

 

161

 

// unable to find DataSource from MediaLocator

 

162

 

catch ( NoDataSourceException noDataException ) {

 

163

 

noDataException.printStackTrace();

 

164

 

}

 

165

 

 

 

166

 

// device connection error

 

167

 

catch ( IOException ioException ) {

 

168

 

ioException.printStackTrace();

 

169

 

}

 

170

 

 

 

171

}

// end method actionPerformed

 

172

 

 

 

173

} // end inner class CaptureHandler

 

174

 

 

 

175// set output format of device-captured media

176public void setDeviceFormat( Format currentFormat )

177{

178// set desired format through all format controls

179for ( int i = 0; i < formatControls.length; i++ ) {

181

// make sure format control is configurable

182

if ( formatControls[ i ].isEnabled() ) {

183

 

184

formatControls[ i ].setFormat( currentFormat );

185

 

186

System.out.println (

187

"Presentation output format currently set as " +

188

formatControls[ i ].getFormat() );

189

}

190

 

191} // end for loop

192}

193

194// get selected device vector index

195public int getSelectedDeviceIndex( String[] names )

196{

Fig. 22.2 Formatting and saving media from capture devices (part 4 of 9).

1254

Java Media Framework and Java Sound (on CD)

Chapter 22

197// get device name from dialog box of device choices

198String name = ( String ) JOptionPane.showInputDialog(

199this, "Select a device:", "Device Selection",

200

JOptionPane.QUESTION_MESSAGE,

201

null, names, names[ 0 ] );

202

 

203// if format selected, get index of name in array names

204if ( name != null )

205 return Arrays.binarySearch( names, name );

206

207// else return bad selection value

208else

209return -1;

210}

211

212// return user-selected format for device

213public Format getSelectedFormat( Format[] showFormats )

214{

215return ( Format ) JOptionPane.showInputDialog( this,

216"Select a format: ", "Format Selection",

217 JOptionPane.QUESTION_MESSAGE,

218null, showFormats, null );

219}

220

221// pop up error messages

222public void showErrorMessage( String error )

223{

224JOptionPane.showMessageDialog( this, error, "Error",

225JOptionPane.ERROR_MESSAGE );

226

}

227

 

228// get desired file for saved captured media

229public File getSaveFile()

230{

231JFileChooser fileChooser = new JFileChooser();

233 fileChooser.setFileSelectionMode(

234JFileChooser.FILES_ONLY );

235int result = fileChooser.showSaveDialog( this );

237

if (

result == JFileChooser.CANCEL_OPTION )

238

return null;

239

 

 

240

else

 

241return fileChooser.getSelectedFile();

242}

243

244// show saving monitor of captured media

245public void showSaveMonitor()

246{

Fig. 22.2 Formatting and saving media from capture devices (part 5 of 9).

Chapter 22

Java Media Framework and Java Sound (on CD)

1255

247// show saving monitor dialog

248int result = JOptionPane.showConfirmDialog( this,

249saveProgress, "Save capture in progress...",

250

JOptionPane.DEFAULT_OPTION,

251

JOptionPane.INFORMATION_MESSAGE );

252

 

253// terminate saving if user presses "OK" or closes dialog

254if ( ( result == JOptionPane.OK_OPTION ) ||

255

( result == JOptionPane.CLOSED_OPTION ) ) {

256

 

257

processor.stop();

258

processor.close();

259

 

260System.out.println ( "Capture closed." );

261}

262}

263

264// process captured media and save to file

265public void captureSaveFile()

266{

267// array of desired saving formats supported by tracks

268Format outFormats[] = new Format[ 1 ];

269

270 outFormats[ 0 ] = selectedFormat;

271

272// file output format

273FileTypeDescriptor outFileType =

274 new FileTypeDescriptor( FileTypeDescriptor.QUICKTIME );

275

276// set up and start processor and monitor capture

277try {

278

 

279

// create processor from processor model

280

// of specific data source, track output formats,

281

// and file output format

282

processor = Manager.createRealizedProcessor(

283

new ProcessorModel( inSource, outFormats,

284

outFileType ) );

285

 

286

// try to make a data writer for media output

287

if ( !makeDataWriter() )

288

return;

289

 

290

// call start on processor to start captured feed

291

processor.start();

292

 

293

// get monitor control for capturing and encoding

294

MonitorControl monitorControl =

295

( MonitorControl ) processor.getControl(

296

"javax.media.control.MonitorControl" );

297

 

298

// get GUI component of monitoring control

299

saveProgress = monitorControl.getControlComponent();

 

 

Fig. 22.2 Formatting and saving media from capture devices (part 6 of 9).

1256

Java Media Framework and Java Sound (on CD)

Chapter 22

300

301 showSaveMonitor();

302

303 } // end try

304

305// no processor could be found for specific

306// data source

307catch ( NoProcessorException processorException ) {

308processorException.printStackTrace();

309}

310

311// unable to realize through

312// createRealizedProcessor method

313catch ( CannotRealizeException realizeException ) {

314realizeException.printStackTrace();

315}

316

317// device connection error

318catch ( IOException ioException ) {

319ioException.printStackTrace();

320}

321

322 } // end method captureSaveFile

323

324// method initializing media file writer

325public boolean makeDataWriter()

326{

327File saveFile = getSaveFile();

328

329 if ( saveFile == null )

330 return false;

331

332// get output data source from processor

333outSource = processor.getDataOutput();

335 if ( outSource == null ) {

336 showErrorMessage( "No output from processor!" );

337return false;

338}

339

340// start data writing process

341try {

342

 

343

// create new MediaLocator from saveFile URL

344

MediaLocator saveLocator =

345

new MediaLocator ( saveFile.toURL() );

346

 

347

// create DataSink from output data source

348

// and user-specified save destination file

349

dataSink = Manager.createDataSink(

350

outSource, saveLocator );

351

 

 

 

Fig. 22.2 Formatting and saving media from capture devices (part 7 of 9).

Chapter 22

Java Media Framework and Java Sound (on CD)

1257

 

 

 

 

352

 

// register a DataSinkListener for DataSinkEvents

 

353

 

dataSink.addDataSinkListener(

 

354

 

 

 

355

 

// anonymous inner class to handle DataSinkEvents

356

 

new DataSinkListener () {

 

357

 

 

 

358

 

// if end of media, close data writer

 

359

 

public void dataSinkUpdate(

 

360

 

DataSinkEvent dataEvent )

 

361

 

{

 

362

 

// if capturing stopped, close DataSink

 

363

 

if ( dataEvent instanceof EndOfStreamEvent )

364

 

dataSink.close();

 

365

 

}

 

366

 

 

 

367

 

} // end DataSinkListener

 

368

 

 

 

369

 

); // end call to method addDataSinkListener

 

370

 

 

 

371

 

// start saving

 

372

 

dataSink.open();

 

373

 

dataSink.start();

 

374

 

 

 

375

}

// end try

 

376

 

 

 

377// DataSink could not be found for specific

378// save file and data source

379catch ( NoDataSinkException noDataSinkException ) {

380 noDataSinkException.printStackTrace();

381return false;

382}

383

384// violation while accessing MediaLocator

385// destination

386catch ( SecurityException securityException ) {

387 securityException.printStackTrace();

388return false;

389}

390

391// problem opening and starting DataSink

392catch ( IOException ioException ) {

393 ioException.printStackTrace();

394return false;

395}

396

397 return true;

398

399 } // end method makeDataWriter

400

401// main method

402public static void main( String args[] )

403{

404CapturePlayer testPlayer = new CapturePlayer();

Fig. 22.2 Formatting and saving media from capture devices (part 8 of 9).

1258

Java Media Framework and Java Sound (on CD)

Chapter 22

405

406testPlayer.setSize( 200, 70 );

407testPlayer.setLocation( 300, 300 );

408testPlayer.setDefaultCloseOperation( EXIT_ON_CLOSE );

409testPlayer.setVisible( true );

410}

411

412 } // end class CapturePlayer

Fig. 22.2 Formatting and saving media from capture devices (part 9 of 9).

Chapter 22 Java Media Framework and Java Sound (on CD) 1259

Class CapturePlayer (Fig. 22.2) illustrates capturing, setting media formats and saving media from capture devices supported by JMF. The simplest test of the program uses a microphone for audio input. Initially, the GUI has only one button, which users click to begin the configuration process. Users then select a capture device from a pull-down menu dialog. The next dialog box has options for the format of the capture device and file output. The third dialog box asks users to save the media to a specific file. The final dialog box provides a volume control and the option of monitoring the data. Monitoring allows users to hear or see the media as it is captured and saved without modifying it in any way. Many media capture technologies offer monitoring capabilities. For example, many video recorders have a screen attachment to let users see what the camera is capturing without looking through the viewfinder. In a recording studio producers can listen to live music being recorded through headphones in another room. Monitoring data is different from playing data in that it does not make any changes to the format of the media or affect the data being sent to the Processor.

In the CapturePlayer program, lines 14–16 import the JMF Java extension packages javax.media.protocol, javax.media.format and javax.media.control, which contain classes and interfaces for media control and device formatting. Line 17 imports the JMF package javax.media.datasink, which contains classes for outputting formatted media. The program uses the classes and interfaces provided by these packages to obtain the desired capture device information, set the format of the capture device, create a Processor to handle the captured media data, create a DataSink to write the media data to a file and monitor the saving process.

CapturePlayer is capable of setting the media format. JMF provides class Format to describe the attributes of a media format, such as the sampling rate (which controls the quality of the sound) or whether the media should be in stereo or mono format. Each media format is encoded differently and can be played only with a media handler that supports its particular format. Line 28 declares an array of the Formats that the capture device supports and a Format reference for the user-selected format (selectedFormat).

After obtaining the Format objects, the program needs access to the formatting controls of the capture device. Line 31 declares an array to hold the FormatControls which will set the capture-device format. Class CapturePlayer sets the desired Format for the media source through the device’s FormatControls (line 31). The CaptureDeviceInfo reference deviceInfo (line 34) stores the capture device information, which will be placed in a Vector containing all of the device’s information.

Class DataSource connects programs to media data sources, such as capture devices. The SimplePlayer of Figure 22.1 accessed a DataSource object by invoking Manager method createPlayer, passing in a MediaLocator. However, class CapturePlayer accesses the DataSource directly. Line 40 declares two DataSources—inSource connects to the capture device’s media and outSource connects to the output data source to which the captured media will be saved.

An object that implements interface Processor provides the primary function that controls and processes the flow of media data in class CapturePlayer (line 46). The class also creates an object that implements interface DataSink to write the captured data to a file (line 43).

Clicking the Capture and Save File button configures the capture device by invoking method actionPerformed (lines 93–171) in private inner class Cap-

1260

Java Media Framework and Java Sound (on CD)

Chapter 22

tureHandler (lines 90–173). An instance of inner class CaptureHandler is registered to listen for ActionEvents from captureButton (line 62). The program provides users with a list of available capture devices when lines 96–97 invoke CaptureDeviceManager static method getDeviceList. Method getDeviceList obtains all of the computer’s available devices that support the specified Format. Specifying null as the Format parameter returns a complete list of available devices. Class CaptureDeviceManager enables a program to access this list of devices.

Lines 109–119 copy the names of all capture devices into a String array (deviceNames) for display purposes. Lines 122-123 invoke CapturePlayer method getSelectedDeviceIndex (lines 195–210) to show a selector dialog with a list of all the device names stored in array deviceNames. The method call to showInputDialog (lines 198–201) has a different parameter list than earlier examples. The first four parameters are the dialog’s parent component, message, title, and message type, as earlier chapters use. The final three, which are new, specify the icon (in this case, null), the list of values presented to the user (deviceNames), and the default selection (the first element of deviceNames). Once users select a device, the dialog returns the string, which is used to return the integer index of the selected name in deviceNames. This String helps determine the parallel element in the deviceList. This element, which is an instance of CaptureDeviceInfo, creates and configures a new device from which the desired media can be recorded.

A CaptureDeviceInfo object encapsulates the information that the program needs to access and configure a capture device, such as location and format preferences. Calling methods getLocator (line 143) and getFormats (line 132) access these pieces of information. Lines 129–130 access the new CaptureDeviceInfo that the user specified in the deviceList. Next, lines 135–136 call inSource’s disconnect method to disengage any previously opened capture devices before connecting the new device.

Lines 142–143 invoke Manager method createDataSource to obtain the DataSource object that connects to the capture device’s media source, passing in the capture device’s MediaLocator object as an argument. CaptureDeviceInfo method getLocator returns the capture device’s MediaLocator. Method createDataSource in turn invokes DataSource method connect which establishes a connection with the capture device. Method createDataSource throws a NoDataSourceException if it cannot locate a DataSource for the capture device. An IOException occurs if there is an error opening the device.

Before capturing the media, the program needs to format the DataSource as specified by the user in the Format Selection dialog. Lines 146–147 use CaptureDevice method getFormatControls to obtain an array of FormatControls for DataSource inSource. An object that implements interface FormatControl specifies the format of the DataSource. DataSource objects can represent media sources other than capture devices, so for this example the cast operator in line 146 manipulates object inSource as a CaptureDevice and accesses capture device methods such as getFormatControls. Line 150 invokes method getSelectedFormat (lines 213–219) to display an input dialog from which users can select one of the available formats. Lines 176–192 call the method setDeviceFormat to set the media output format for the device to the user-selected Format. Each capture device can have several FormatControls, so setDeviceFormat uses FormatControl method setFormat to specify the format for each FormatControl object.

Chapter 22

Java Media Framework and Java Sound (on CD)

1261

Formatting the DataSource completes the configuration of the capture device. A Processor (object inSource) converts the data to the file format in which it will be saved. The Processor works as a connector between the outSource and method captureSaveFile since DataSource inSource does not play or save the media, it only serves to configure the capture device. Line 157 invokes method captureSaveFile (lines 265–322) to perform the steps needed to save the captured media in a recognizable file format.

To create a Processor, this program first creates a ProcessorModel, a template for the Processor. The ProcessorModel determines the attributes of a Processor through a collection of information which includes a DataSource or MediaLocator, the desired formats for the media tracks which the Processor will handle, and a ContentDescriptor indicating the output content type. Line 268 creates a new Format array (outFormats) that represents the possible formats for each track in the media. Line 270 sets the default format to the first element of the array. To save the captured output to a file, the Processor must first convert the data it receives to a file-enabled format. A new QuickTime FileTypeDescriptor (package javax.media.format) is created to store a description of the content type of the Processor’s output and store it in outFileType (lines 273–274). Lines 282–284 use the DataSource inSource, the array outFormats, and the file type outFileType to instantiate a new ProcessorModel (lines 283–284).

Generally, Processors need to be configured before they can process media, but the one in this application does not since lines 282–284 invoke Manager method createRealizedProcessor. This method creates a configured, realized Processor based on the ProcessorModel object passed in as an argument. Method createRealizedProcessor throws a NoProcessorException if the program cannot locate a Processor for the media or if JMF does not support the media type. The method throws a CannotRealizeException if the Processor cannot be realized. This may occur if another program is already using the media, thus blocking communication with the media source.

Common Programming Error 22.1

Be careful when specifying track formats. Incompatible formats for specific output file types prevent the program from realizing the Processor.

Software Engineering Observation 22.2

Recall that the Processor transitions through several states before being realized. Man- ager method createProcessor allows a program to provide more customized configuration before a Processor is realized.

Performance Tip 22.2

When method createRealizedProcessor configures the Processor, the method blocks until the Processor is realized. This may prevent other parts of the program from executing. In some cases, using a ControllerListener to respond to ControllerEvents may enable a program to operate more efficiently. When the Processor is realized, the listener is notified so the program can begin processing the media.

Having obtained media data in a file format from the Processor, the program can make a “data writer” to write the media output to a file. An object that implements interface DataSink enables media data to be output to a specific location—most commonly a file.

1262

Java Media Framework and Java Sound (on CD)

Chapter 22

Line 287 invokes method makeDataWriter (lines 325–399) to create a DataSink object that can save the file. Manager method createDataSink requires the DataSource of the Processor and the MediaLocator for the new file as arguments. Within makeDataWriter, lines 229–242 invokes method getSaveFile to prompt users to specify the name and location of the file to which the media should be saved. The File object saveFile stores the information. Processor method getDataOutput (line 333) returns the DataSource from which it received the media. Lines 344–345 create a new MediaLocator for saveFile. Using this MediaLocator and the

DataSource, lines 349–350 create a DataSink object which writes the output media from the DataSource to the file in which the data will be saved, as specified by the

MediaLocator. Method createDataSink throws a NoDataSinkException if it cannot create a DataSink that can read data from the DataSource and output the data to the location specified by the MediaLocator. This failure may occur as a result of invalid media or an invalid MediaLocator.

The program needs to know when to stop outputting data, so lines 353–369 register a

DataSinkListener to listen for DataSinkEvents. DataSinkListener method dataSinkUpdate (lines 359–365) is called when each DataSinkEvent occurs. If the DataSinkEvent is an EndOfStreamEvent, indicating that the Processor has been closed because the capture stream connection has closed, line 364 closes the DataSink. Invoking DataSink method close stops the data transfer. A closed DataSink cannot be used again.

Common Programming Error 22.2

The media file output with a DataSink will be corrupted if the DataSink is not closed properly.

Software Engineering Observation 22.3

Captured media may not yield an EndOfMediaEvent if the media’s endpoint cannot be determined.

After setting up the DataSink and registering its listener, line 372 calls DataSink method open to connect the DataSink to the destination that the MediaLocator specifies. Method open throws a SecurityException if the DataSink attempts to write to a destination for which the program does not have write permission, such as a read-only file.

Line 373 calls DataSink method start to initiate data transfer. At this point, the program returns from method makeDataWriter back to method captureSaveFile (lines 265–322). Although the DataSink prepares itself to receive the transfer and indicates that it is ready by calling start, the transfer does not actually take place until the Processor’s start method is called. The invocation of Processor method start begins the flow of data from the capture device, formats the data and transfers that data to the DataSink. The DataSink writes the media to a file, which completes the process performed by class CapturePlayer.

While Processor encodes the data and the DataSink saves it to a file, CapturePlayer monitors the process. Monitoring provides a method of overseeing data as the capture device collects it. Lines 294–296 obtain an object that implements interface MonitorControl (package javax.media.control) from the Processor by calling method getControl. Line 299 calls MonitorControl method getControlCom-

Chapter 22

Java Media Framework and Java Sound (on CD)

1263

ponent to obtain the GUI component that displays the monitored media. MonitorControls typically have a checkbox to enable or disable displaying media. Also, audio devices have a volume control and video devices have a control for setting the preview frame rate. Line 301 invokes method showSaveMonitor (lines 245–261) to display the monitoring GUI in a dialog box. To terminate capturing, users can press the OK button or close the dialog box (lines 254–261). For some video capture devices, the Processor needs to be in a Stopped state to enable monitoring of the saving and capturing process.

Thus far, we have discussed JMF’s capabilities to access, present and save media content. Our final JMF example demonstrates how to send media between computers using JMF’s streaming capabilities.

22.4 RTP Streaming

Streaming media refers to media that is transferred from a server to a client in a continuous stream of bytes. The client can begin playing the media while still downloading the media from the server. Audio and video media files are often many megabytes in size. Live events, such as concerts or football game broadcasts, may have indeterminable sizes. Users could wait until a recording of a concert or game is posted, then download the entire recording. However, at today’s Internet connection speeds, downloading such a broadcast could take days and typically users prefer to listen to live broadcasts as they occur. Streaming media enables client applications to play media over the Internet or over a network without downloading the entire media file at once.

In a streaming media application, the client typically connects to a server that sends a stream of bytes containing the media back to the client. The client application buffers (i.e. stores locally) a portion of the media, which the client begins playing after a certain portion has been received. The client continually buffers additional media, providing users with an uninterrupted clip, as long as network traffic does not prevent the client application from buffering additional bytes. With buffering, users experience the media within seconds of when the streaming begins, even though all of the material has not been received.

Performance Tip 22.3

Streaming media to a client enables the client to experience the media faster than if the client must wait for an entire media file to download.

Demand for real-time, robust multimedia is rising dramatically as the speed of Internet connections increase. Broadband Internet access, which provides high-speed network connections to the Internet for so many home users, is becoming more popular, though the number of users remains relatively small compared to the total number of Internet users. With faster connections, streaming media can provide a better multimedia experience. Users with slower connections can still experience the multimedia, though with lesser quality. The wide range of applications that use streaming media is growing. Applications that stream video clips to clients have expanded to provide real-time broadcast feeds. Thousands of radio stations stream music continuously over the Internet. Client applications such as RealPlayer have focused on streaming media content with live radio broadcasts. Applications are not limited to audio and video server-to-client streaming. For example, teleconferencing and video conferencing applications increase efficiency in everyday business by reducing the need for business people to travel great distances to attend meetings.

1264

Java Media Framework and Java Sound (on CD)

Chapter 22

JMF provides a streaming media package that enables Java applications to send and receive streams of media in some of the formats discussed earlier in this chapter. For a complete list of formats, see the official JMF Web site:

java.sun.com/products/java-media/jmf/2.1.1/formats.html

JMF uses the industry-standard Real-Time Transport Protocol (RTP) to control media transmission. RTP is designed specifically to transmit real-time media data.

The two mechanisms for streaming RTP-supported media are passing it through a DataSink and buffering it. The easier mechanism to use is a DataSink, which writes the contents of a stream to a host destination (i.e., a client computer), via the same techniques shown in Fig. 22.2 to save captured media to a file. In this case, however, the destination MediaLocator’s URL would be specified in the following format:

rtp://host:port/contentType

where host is the IP address or host name of the server, port is the port number on which the server is streaming the media and contentType is either audio or video.

Using a DataSink as specified allows only one stream to be sent at a time. To send multiple streams (e.g., as a karaoke video with separate tracks for video and audio would) to multiple hosts, a server application must use RTP session managers. An RTPManager (package javax.media.rtp) provides more control over the streaming process, permitting specification of buffer sizes, error checking and streaming reports on the propagation delay (the time it takes for the data to reach its destination).

The program in Fig. 22.3 and Fig. 22.4 demonstrates streaming using the RTP session manager. This example supports sending multiple streams in parallel, so separate clients must be opened for each stream. This example does not show a client that can receive the RTP stream. The program in Fig. 22.1 (SimplePlayer) can test the RTP server by specifying an RTP session address

rtp://127.0.0.1:4000/audio

as the location SimplePlayer should open to begin playing audio. To execute the streaming media server on a different computer, replace 127.0.0.1 with either the IP address or host name of the server computer.

1// Fig. 22.3: RTPServer.java

2 // Provides configuration and sending capabilities

3 // for RTP-supported media files

4

5 // Java core packages

6 import java.io.*;

7 import java.net.*;

8

9 // Java extension packages

10import javax.media.*;

11import javax.media.protocol.*;

12import javax.media.control.*;

13import javax.media.rtp.*;

14import javax.media.format.*;

Fig. 22.3 Serving streaming media with RTP session managers (part 1 of 7).

Chapter 22

Java Media Framework and Java Sound (on CD)

1265

15

16 public class RTPServer {

17

18// IP address, file or medialocator name, port number

19private String ipAddress, fileName;

20private int port;

21

22// processor controlling data flow

23private Processor processor;

24

25// data output from processor to be sent

26private DataSource outSource;

27

28// media tracks' configurable controls

29private TrackControl tracks[];

30

31// RTP session manager

32private RTPManager rtpManager[];

34// constructor for RTPServer

35public RTPServer( String locator, String ip, int portNumber )

36{

37fileName = locator;

38port = portNumber;

39ipAddress = ip;

40}

41

42// initialize and set up processor

43// return true if successful, false if not

44public boolean beginSession()

45{

46// get MediaLocator from specific location

47MediaLocator mediaLocator = new MediaLocator( fileName );

49

if ( mediaLocator == null ) {

50

System.err.println(

51

"No MediaLocator found for " + fileName );

52

 

53return false;

54}

55

56// create processor from MediaLocator

57try {

58

processor = Manager.createProcessor( mediaLocator );

59

 

60

// register a ControllerListener for processor

61

// to listen for state and transition events

62

processor.addControllerListener(

63

new ProcessorEventHandler() );

64

 

65

System.out.println( "Processor configuring..." );

66

 

 

 

Fig. 22.3 Serving streaming media with RTP session managers (part 2 of 7).

1266

Java Media Framework and Java Sound (on CD)

Chapter 22

 

 

 

67

// configure processor before setting it up

 

68processor.configure();

69}

70

71// source connection error

72catch ( IOException ioException ) {

73 ioException.printStackTrace();

74return false;

75}

76

77// exception thrown when no processor could

78// be found for specific data source

79catch ( NoProcessorException noProcessorException ) {

80 noProcessorException.printStackTrace();

81return false;

82}

83

84 return true;

85

86 } // end method beginSession

87

88// ControllerListener handler for processor

89private class ProcessorEventHandler

90extends ControllerAdapter {

91

92// set output format and realize

93// configured processor

94public void configureComplete(

95ConfigureCompleteEvent configureCompleteEvent )

96{

97

System.out.println( "\nProcessor configured." );

98

 

99

setOutputFormat();

100

 

101

System.out.println( "\nRealizing Processor...\n" );

102

 

103processor.realize();

104}

105

106// start sending when processor is realized

107public void realizeComplete(

108RealizeCompleteEvent realizeCompleteEvent )

109{

110

System.out.println(

111

"\nInitialization successful for " + fileName );

112

 

113

if ( transmitMedia() == true )

114

System.out.println( "\nTransmission setup OK" );

115

 

116

else

117System.out.println( "\nTransmission failed." );

118}

119

Fig. 22.3 Serving streaming media with RTP session managers (part 3 of 7).

Chapter 22

Java Media Framework and Java Sound (on CD)

1267

120// stop RTP session when there is no media to send

121public void endOfMedia( EndOfMediaEvent mediaEndEvent )

122{

123 stopTransmission();

124System.out.println ( "Transmission completed." );

125}

126

127 } // end inner class ProcessorEventHandler

128

129// set output format of all tracks in media

130public void setOutputFormat()

131{

132// set output content type to RTP capable format

133processor.setContentDescriptor(

134 new ContentDescriptor( ContentDescriptor.RAW_RTP ) );

135

136// get all track controls of processor

137tracks = processor.getTrackControls();

139// supported RTP formats of a track

140Format rtpFormats[];

141

142// set each track to first supported RTP format

143// found in that track

144for ( int i = 0; i < tracks.length; i++ ) {

145

 

 

146

System.out.println( "\nTrack #" +

147

 

( i + 1 ) + " supports " );

148

 

 

149

if

( tracks[ i ].isEnabled() ) {

150

 

 

151

 

rtpFormats = tracks[ i ].getSupportedFormats();

152

 

 

153

 

// if supported formats of track exist,

154

 

// display all supported RTP formats and set

155

 

// track format to be first supported format

156

 

if ( rtpFormats.length > 0 ) {

157

 

 

158

 

for ( int j = 0; j < rtpFormats.length; j++ )

159

 

System.out.println( rtpFormats[ j ] );

160

 

 

161

 

tracks[ i ].setFormat( rtpFormats[ 0 ] );

162

 

 

163

 

System.out.println ( "Track format set to " +

164

 

tracks[ i ].getFormat() );

165

 

}

166

 

 

167

 

else

168

 

System.err.println (

169

 

"No supported RTP formats for track!" );

170

 

 

171

}

// end if

172

 

 

 

 

 

Fig. 22.3 Serving streaming media with RTP session managers (part 4 of 7).

1268

Java Media Framework and Java Sound (on CD)

Chapter 22

173 } // end for loop

174

175 } // end method setOutputFormat

176

177// send media with boolean success value

178public boolean transmitMedia()

179{

180outSource = processor.getDataOutput();

182 if ( outSource == null ) {

183 System.out.println ( "No data source from media!" );

184

185return false;

186}

187

188// rtp stream managers for each track

189rtpManager = new RTPManager[ tracks.length ];

191// destination and local RTP session addresses

192SessionAddress localAddress, remoteAddress;

194// RTP stream being sent

195SendStream sendStream;

197// IP address

198 InetAddress ip;

199

200// initialize transmission addresses and send out media

201try {

202

 

203

// transmit every track in media

204

for ( int i = 0; i < tracks.length; i++ ) {

205

 

206

// instantiate a RTPManager

207

rtpManager[ i ] = RTPManager.newInstance();

208

 

209

// add 2 to specify next control port number;

210

// (RTP Session Manager uses 2 ports)

211

port += ( 2 * i );

212

 

213

// get IP address of host from ipAddress string

214

ip = InetAddress.getByName( ipAddress );

215

 

216

// encapsulate pair of IP addresses for control and

217

// data with 2 ports into local session address

218

localAddress = new SessionAddress(

219

ip.getLocalHost(), port );

220

 

221

// get remoteAddress session address

222

remoteAddress = new SessionAddress( ip, port );

223

 

224

// initialize the session

225

rtpManager[ i ].initialize( localAddress );

 

 

Fig. 22.3 Serving streaming media with RTP session managers (part 5 of 7).

Chapter 22

Java Media Framework and Java Sound (on CD)

1269

 

 

 

 

226

 

 

 

227

 

// open RTP session for destination

 

228

 

rtpManager[ i ].addTarget( remoteAddress );

 

229

 

 

 

230

 

System.out.println( "\nStarted RTP session: "

 

231

 

+ ipAddress + " " + port);

 

232

 

 

 

233

 

// create send stream in RTP session

 

234

 

sendStream =

 

235

 

rtpManager[ i ].createSendStream( outSource, i );

236

 

 

 

237

 

// start sending the stream

 

238

 

sendStream.start();

 

239

 

 

 

240

 

System.out.println( "Transmitting Track #" +

 

241

 

( i + 1 ) + " ... " );

 

242

 

 

 

243

 

} // end for loop

 

244

 

 

 

245

 

// start media feed

 

246

 

processor.start();

 

247

 

 

 

248

}

// end try

 

249

 

 

 

250// unknown local or unresolvable remote address

251catch ( InvalidSessionAddressException addressError ) {

252 addressError.printStackTrace();

253return false;

254}

255

256// DataSource connection error

257catch ( IOException ioException ) {

258 ioException.printStackTrace();

259return false;

260}

261

262// format not set or invalid format set on stream source

263catch ( UnsupportedFormatException formatException ) {

264 formatException.printStackTrace();

265return false;

266}

267

268// transmission initialized successfully

269return true;

270

271 } // end method transmitMedia

272

273// stop transmission and close resources

274public void stopTransmission()

275{

276if ( processor != null ) {

277

Fig. 22.3 Serving streaming media with RTP session managers (part 6 of 7).

1270

Java Media Framework and Java Sound (on CD)

Chapter 22

 

 

 

 

278

 

// stop processor

 

279

 

processor.stop();

 

280

 

 

 

281

 

// dispose processor

 

282

 

processor.close();

 

283

 

 

 

284

 

if ( rtpManager != null )

 

285

 

 

 

286

 

// close destination targets

 

287

 

// and dispose RTP managers

 

288

 

for ( int i = 0; i < rtpManager.length; i++ ) {

289

 

 

 

290

 

// close streams to all destinations

 

291

 

// with a reason for termination

 

292

 

rtpManager[ i ].removeTargets(

 

293

 

"Session stopped." );

 

294

 

 

 

295

 

// release RTP session resources

 

296

 

rtpManager[ i ].dispose();

 

297

 

}

 

298

 

 

 

299

 

} // end if

 

300

 

 

 

301

 

System.out.println ( "Transmission stopped." );

 

302

 

 

 

303

 

} // end method stopTransmission

 

304

 

 

 

305

}

// end class RTPServer

 

 

 

 

 

Fig. 22.3 Serving streaming media with RTP session managers (part 7 of 7).

Class RTPServer’s purpose is to stream media content. As in previous examples, the RTPServer (Fig. 22.3) sets up the media, processes and formats it, then outputs it. ControllerEvents and the various states of the streaming process drive this process. Processing of the media has three distinct parts—Processor initialization, Format configuration and data transmission. The code in this example contains numerous confirmation messages displayed to the command prompt and an emphasis on error checking. A problem during streaming will most likely end the entire process and it will need to be restarted.

To test RTPServer, class RTPServerTest (Fig. 22.4) creates a new RTPServer object and passes its constructor (lines 35–40) three arguments—a String representing the media’s location, a String representing the IP address of the client and a port number for streaming content. These arguments contain the information class RTPServer needs to obtain the media and set up the streaming process. Following the general approach outlined in SimplePlayer (Fig. 22.1) and CapturePlayer (Fig. 22.2), class RTPServer obtains a media source, configures the source through a type of Controller and outputs the data to a specified destination.

RTPServerTest calls RTPServer method beginSession (lines 44–86) to set up the Processor that controls the data flow. Line 47 creates a MediaLocator and initializes it with the media location stored in fileName. Line 58 creates a Processor for the data specified by that MediaLocator.

Chapter 22

Java Media Framework and Java Sound (on CD)

1271

Unlike the program in Fig. 22.2, this Processor is not preconfigured by a Manager. Until class RTPServer configures and realizes the Processor, the media cannot be formatted. Lines 62–63 register a ProcessorEventHandler to react to the processor’s ControllerEvents. The methods of class ProcessorEventHandler (lines 89–127) control the media setup as the Processor changes states. Line 68 invokes Processor method configure to place the Processor in the Configuring state. Configuration is when the Processor inquires of the system and the media the format information needed to program the Processor to perform the correct task. A ConfigureCompleteEvent occurs when the Processor completes configuration. The ProcessorEventHandler method configureComplete (lines 94–104) responds to this transition. Method configureComplete calls method setOutputFormat (lines 130–175) then realizes the Processor (line 103). When line 99 invokes method setOutputFormat, it sets each media track to an RTP streaming media format. Lines 133–134 in method setOutputFormat specify the output content-type by calling Processor method setContentDescriptor. The method takes as an argument a

ContentDescriptor initialized with constant ContentDescriptor.RAW_RTP. The RTP output content-type restricts the Processor to only support RTP-enabled media track formats. The Processor’s output content-type should be set before the media tracks are configured.

Once the Processor is configured, the media track formats need to be set. Line 137 invokes Processor method getTrackControls to obtain an array that contains the corresponding TrackControl object (package javax.media.control) for each media track. For each enabled TrackControl, lines 144–173 obtain an array of all supported RTP media Formats (line 151), then set the first supported RTP format as the preferred RTP-streaming media format for that track (line 161). When method setOutputFormat returns, line 103 in method configureComplete realizes the

Processor.

As with any controller realization, the Processor can output media as soon as it has finished realizing itself. When the Processor enters the Realized state, the ProcessorEventHandler invokes method realizeComplete (lines 107–118). Line 113 invokes method transmitMedia (lines 178–271) which creates the structures needed to transmit the media to the Processor. This method obtains the DataSource from the Processor (line 180), then declares an array of RTPManagers that are able to start and control an RTP session (line 189). RTPManagers use a pair of SessionAddress objects with identical IP addresses, but different port numbers—one for stream control and one for streaming media data. An RTPManager receives each IP address and port number as a SessionAddress object. Line 192 declares the SessionAddress objects used in the streaming process. An object that implements interface SendStream (line 195) performs the RTP streaming.

Software Engineering Observation 22.4

For videos that have multiple tracks, each SendStream must have its own RTPManager managing its session. Each Track has its own SendStream.

The try block (lines 201–248) of method transmitMedia sends out each track of the media as an RTP stream. First, managers must be created for the sessions. Line 207 invokes RTPManager method newInstance to instantiate an RTPManager for each

1272

Java Media Framework and Java Sound (on CD)

Chapter 22

track stream. Line 211 assigns the port number to be two more than the previous port number, because the each track uses one port number for the stream control and one to actually stream the data. Lines 218–219 instantiate a new local session address where the stream is located (i.e., the RTP address that clients use to obtain the media stream) with the local IP address and a port number as parameters. Line 219 invokes InetAddress method getLocalHost to get the local IP address. Line 222 instantiates the client’s session address, which the RTPManager uses as the stream’s target destination. When line 225 calls RTPManager method initialize, the method directs the local streaming session to use the local session address. Using object remoteAddress as a parameter, line 228 calls RTPManager method addTarget to open the destination session at the specified address. To stream media to multiple clients, call RTPManager method addTarget for each destination address. Method addTarget must be called after the session is initialized and before any of the streams are created on the session.

The program can now create the streams on the session and start sending data. The streams are created in the current RTP session with the DataSource outSource (obtained at line 180) and the source stream index (i.e. media track index) in lines 234–235. Invoking method start on the SendStream (line 238) and on the Processor (line 246) starts transmission of the media streams, which may cause exceptions. An InvalidSessionAddressException occurs when the specified session address is invalid. An UnsupportedFormatException occurs when an unsupported media format is specified or if the DataSource’s Format has not been set. An IOException occurs if the application encounters networking problems. During the streaming process, RTPManagers can be used with related classes in package javax.media.rtp and package javax.media.rtp.event controls the streaming process and send reports to the application.

The program should close connections and stop streaming transmission when it reaches the end of the streaming media or when the program terminates. When the Processor encounters the end of the media, it generates an EndOfMediaEvent. In response, the program calls method endOfMedia (lines 121–125). Line 123 invokes method stopTransmission (lines 274–303) to stop and close the Processor (lines 279–282). After calling stopTransmission, streaming cannot resume because it disposes of the Processor and the RTP session resources. Lines 288–297 invoke RTPManager method removeTargets (lines 292–293) to close streaming to all destinations. RTPManager method dispose (line 296) is also invoked, releasing the resources held by the RTP sessions. The class RTPServerTest (Fig. 22.4), explicitly invokes method stopTransmission when users terminates the server application (line 40).

1 // Fig. 22.4: RTPServerTest.java

2 // Test class for RTPServer

3

4// Java core packages

5 import java.awt.event.*;

6 import java.io.*;

7 import java.net.*;

8

Fig. 22.4 Application to test class RTPServer from Fig. 22.3 (part 1 of 6).

Chapter 22

Java Media Framework and Java Sound (on CD)

1273

9 // Java extension packages

10 import javax.swing.*;

11

12 public class RTPServerTest extends JFrame {

13

14// object handling RTP streaming

15private RTPServer rtpServer;

16

17// media sources and destination locations

18private int port;

19private String ip, mediaLocation;

20private File mediaFile;

21

22// GUI buttons

23private JButton transmitFileButton, transmitUrlButton;

25// constructor for RTPServerTest

26public RTPServerTest()

27{

28super( "RTP Server Test" );

30// register a WindowListener for frame events

31addWindowListener(

32

 

33

// anonymous inner class to handle WindowEvents

34

new WindowAdapter() {

35

 

36

public void windowClosing(

37

WindowEvent windowEvent )

38

{

39

if ( rtpServer != null )

40

rtpServer.stopTransmission();

41

}

42

 

43

} // end WindowAdpater

44

 

45

); // end call to method addWindowListener

46

 

47// panel containing button GUI

48JPanel buttonPanel = new JPanel();

49getContentPane().add( buttonPanel );

51// transmit file button GUI

52transmitFileButton = new JButton( "Transmit File" );

53buttonPanel.add( transmitFileButton );

54

55// register ActionListener for transmitFileButton events

56transmitFileButton.addActionListener(

57

new ButtonHandler() );

58

 

59// transmit URL button GUI

60transmitUrlButton = new JButton( "Transmit Media" );

61buttonPanel.add( transmitUrlButton );

Fig. 22.4 Application to test class RTPServer from Fig. 22.3 (part 2 of 6).

1274

Java Media Framework and Java Sound (on CD)

Chapter 22

62

63// register ActionListener for transmitURLButton events

64transmitUrlButton.addActionListener(

65

new ButtonHandler() );

66

 

67

} // end constructor

68

 

69// inner class handles transmission button events

70private class ButtonHandler implements ActionListener {

72// open and try to send file to user-input destination

73public void actionPerformed( ActionEvent actionEvent )

74{

75

//

if transmitFileButton invoked, get file URL string

76

if

( actionEvent.getSource() == transmitFileButton ) {

77

 

 

78

 

mediaFile = getFile();

79

 

 

80

 

if ( mediaFile != null )

81

 

 

82

 

// obtain URL string from file

83

 

try {

84

 

mediaLocation = mediaFile.toURL().toString();

85

 

}

86

 

 

87

 

// file path unresolvable

88

 

catch ( MalformedURLException badURL ) {

89

 

badURL.printStackTrace();

90

 

}

91

 

 

92

 

else

93

 

return;

94

 

 

95

}

// end if

96

 

 

97

// else transmitMediaButton invoked, get location

98

else

99

 

mediaLocation = getMediaLocation();

100

 

 

101

if

( mediaLocation == null )

102

 

return;

103

 

 

104

// get IP address

105

ip = getIP();

106

 

 

107

if ( ip == null )

108

 

return;

109

 

 

110

// get port number

111

port = getPort();

112

 

 

 

 

 

Fig. 22.4 Application to test class RTPServer from Fig. 22.3 (part 3 of 6).

Chapter 22

Java Media Framework and Java Sound (on CD)

1275

 

 

 

 

113

 

// check for valid positive port number and input

 

114

 

if ( port <= 0 ) {

 

115

 

 

 

116

 

if ( port != -999 )

 

117

 

System.err.println( "Invalid port number!" );

 

118

 

 

 

119

 

return;

 

120

 

}

 

121

 

 

 

122

 

// instantiate new RTP streaming server

 

123

 

rtpServer = new RTPServer( mediaLocation, ip, port );

124

 

 

 

125

 

rtpServer.beginSession();

 

126

 

 

 

127

}

// end method actionPeformed

 

128

 

 

 

129

} // end inner class ButtonHandler

 

130

 

 

 

131// get file from computer

132public File getFile()

133{

134JFileChooser fileChooser = new JFileChooser();

136

fileChooser.setFileSelectionMode(

137

JFileChooser.FILES_ONLY );

138

 

139

int result = fileChooser.showOpenDialog( this );

140

 

141

if ( result == JFileChooser.CANCEL_OPTION )

142

return null;

143

 

144

else

145return fileChooser.getSelectedFile();

146}

147

148// get media location from user

149public String getMediaLocation()

150{

151String input = JOptionPane.showInputDialog(

152this, "Enter MediaLocator" );

154// if user presses OK with no input

155if ( input != null && input.length() == 0 ) {

156System.err.println( "No input!" );

157return null;

158}

159

160return input;

161}

162

163// method getting IP string from user

164public String getIP()

165{

Fig. 22.4 Application to test class RTPServer from Fig. 22.3 (part 4 of 6).

1276

Java Media Framework and Java Sound (on CD)

Chapter 22

166 String input = JOptionPane.showInputDialog(

167 this, "Enter IP Address: " );

168

169// if user presses OK with no input

170if ( input != null && input.length() == 0 ) {

171System.err.println( "No input!" );

172return null;

173}

174

175return input;

176}

177

178// get port number

179public int getPort()

180{

181String input = JOptionPane.showInputDialog(

182this, "Enter Port Number: " );

184// return flag value if user clicks OK with no input

185if ( input != null && input.length() == 0 ) {

186 System.err.println( "No input!" );

187return -999;

188}

189

190// return flag value if user clicked CANCEL

191if ( input == null )

192

return -999;

193

 

194// else return input

195return Integer.parseInt( input );

197 } // end method getPort

198

199// execute application

200public static void main( String args[] )

201{

202RTPServerTest serverTest = new RTPServerTest();

204serverTest.setSize( 250, 70 );

205serverTest.setLocation( 300, 300 );

206serverTest.setDefaultCloseOperation( EXIT_ON_CLOSE );

207serverTest.setVisible( true );

208}

209

210 } // end class RTPServerTest

Fig. 22.4 Application to test class RTPServer from Fig. 22.3 (part 5 of 6).