JMF RTP Audio Agent

Dong Heun Lee
Columbia University
New York, NY
USA
dl143@columbia.edu

Abstract

The JMF RTP Agent is an RTP unicast application program that can run on Solaris and Windows. Using Java Media Framework 2.1, it is capable of sending and receiving RTP packets as well as capturing and playing back audio. Furthermore, the destination IP address and port number can be specified and readily changed while the program is running. It can also receive RTP packets from more than one source. This program is part of JMF-MGCP-SIP project and designed for Internet telephony. Buffer control has also been done to reduce the audio latency.

Configuration

No configuration is necessary to run the JMF RTP Agent. If you set the configuration for Java and JMF, you are ready to run the program.

System Requirements

The JMF RTP Agent is guaranteed to work on the following two OS platforms with appropriate software packages:

Windows NT 4.0
Java 1.2.2
Java Media Framework 2.1 Windows Performance Pack

SunOS, sparc, 5.7
Java 1.2.2 Classic VM (build JDK-1.2.2-001, green threads, sunwjit)
Java Media Framework 2.1 Solaris SPARC Performance Pack

SunOS, sparc, 5.7
java 1.2.1 Solaris VM (build Solaris_JDK_1.2.1_03, native threads, sunwjit)
Java Media Framework 2.1 Solaris SPARC Performance Pack

Note: You cannot use Java Media Framework 2.1 Cross-platform Java version. It does not allow you to capture audio through capture devices.

JMF 2.1 was released on May 31, 2000, so JMF 2.0 is no longer available to download. Since JMF 2.1 has numerous bug fixes, JMF 2.1 should be used.

You also need an audio capture device on each computer to run the JMF RTP Agent.

Please check with Hardware and Sotware Requirements for JMF 2.1 for more information.

Interface with MGCP Gateway

The JMF RTP Agent program does not contain the main function. That is because it is supposed to be controlled and invoked by MGCP gateway program. The JMF RTP Agent has four public interface:
   public String getLocalSessionAddress();
   public int getLocalSessionPort();
   public void setRemoteSession(String remoteAddress, int remotePort);
   public boolean stopTransmission();
Here is the way that the interface can be used in MGCP gateway program:
   RTPAgent agent = new RTPAgent(); 

   // GetIPPort
   String localIP = agent.getLocalSessionAddress();
   int localPort = agent.getLocalSessionPort();


   // SetRemote
   String remoteIP;
   int remotePort;
   agent.setRemoteSession(remoteIP, remotePort);


   // StopTransmit()
   agent.stopTransmit();

Architecture

There are four major private fields in the JMF agent program. They are localSessionAddress and localSessionPort for receiving RTP packets and remoteSessionAddress and remoteSessionPort for sending RTP packets. That is, remoteSessionAddress and remoteSessionPort specify the RTP destination.

JMF RTP Agent Diagram

The constructor of JMF RTP Agent calls two private methods initialize() and start():

   public RTPAgent() {
     getLocalIPPort();
     start();
   }

What getLocalIPPort() method does is to obtain a local session IP address and port number. The JMF agent program will use this local session IP address and port number to receive RTP packets from other JMF agents. Rather than fixing the local session port number, we use UDP socket to find an available local port number. This is done using LocalPort.java.

start() method is used to get the JMF agent program to be ready for capturing audio, generating RTP-encoded data and receiving RTP packets. To accomplish this, we use javax.media.Processor. For this project, we used JavaSound audio capture device and set audio format to Linear, 44100 Hz, 16-bit and mono. And RTP format was set to G.711(U-law), 8 kHz and 8-bit.

Once the JMF agent object is contructed, the MGCP gateway program can invoke getLocalSessionAddress() and getLocalSessionPort() to obtain the local session address and port number of the JMF agent. These two pieces of information will be used by the MGCP gateway on the other side to set its remote session address by calling setRemoteSession(String remoteAddress, int remotePort). Then a JMF agent on the other side should be able to transmit RTP packets to this JMF agent. In this way, setRemoteSession(String remoteAddress, int remotePort) is used for INVITE, calling the transmit() private method in the JMF agent program.

setRemoteSession(String remoteAddress, int remotePort) is also used for REINVITE. In REINVITE, the local IP address and port number of a JMF agent will change. Therefore the JMF agent that is sending RTP packets to this RTP address should stop doing so and should be notified of new remote session address so that it can start sending RTP packets to the new address. This is accomplished by the handoff() private method in the agent program. What this private method does is to stop sending RTP packets to the old destination and start sending to the new destination. In order to use setRemoteSession method for both INVITE and REINVITE, started boolean variable was used. It is set to true when the JMF agent constructor is called so that transmit() is invoked in the first call and handoff method is called in subsequent calls:

   private boolean started = false;
 
   private void start() {
     
     // ...
     started = true;
   }

   public void setRemoteSession(String remoteAddress, int remotePort) {

     // ...
     if (!started) 
        transmit();
     else
        handoff();
  }

javax.media.rtp.SessionManager is used to transmit RTP data. Even though DataSink can also be used to transmit RTP data, SessionManager has some advantages over DataSink. Using SessionManager, one can set source description items used in RTCP SDES reports. SessionManager is also necessary to transmit RTP data in multiple sesssions (by cloning DataSource) or to monitor session statistics. By using SessionManager, we could display source description for each sender, such as CNAME, name and email, on simple GUI. In fact, we could show that the CNAME changed in REINVITE.

SessionManager is also used to receive RTP data. In doing so, the JMF agent can listen to NewReceiveStreamEvent, ByeEvent and SenderReportEvent. NewReceiveStreamEvent is important, becuase it informs the program that RTP data packets are received from an SSRC that has not been sending data (i.e., new session participant). Thus, the program can create javax.media.Player, which plays out received RTP data through the speaker. That is when we start hearing captured audio from the other (sending) side. SenderReportEvent is used to display RTCP sender report(SR) on the GUI. And ByeEvent enables the JMF agent to display correct CNAME on the source description GUI in REINVITE.

stopTransmission() method is used for BYE. It stops transmitting to the current destination.

Restriction Due to JMF Bugs

Using JMF 2.0, the JMF RTP agent could not be properly run on Columbia CS account (in both CLIC lab and IRT lab). The CPU usage went up to 98%. We found that the Processor that captures audio seemed to use too much CPU. By comparing Java VM in Columbia Engineering Computer Lab (251 Engineering Terrace) with that on Columbia CS account, we concluded that the following Java VM could not be used to run the JMF RTP Agent:
   java version "1.2.1"
   Solaris VM (build Solaris_JDK_1.2.1_03, native threads, sunwjit)
This was one of many JMF bugs.

Fortunately, in JMF 2.1, Sun corrected the CPU usage problem. Therefore, the CPU usage is no longer a problem. We are very happy to announce that Java 1.2.1 Solaris VM (build Solaris_JDK_1.2.1_03, native threads, sunwjit) can be used to run the JMF agent program.

Another serious JMF 2.0 bug was high audion latency when transmitting RTP audio data between two computers. The delay was about 800 msec even with buffer control. Sun MicroSystems was aware of this latency problem which was unacceptable in telephony applications. Marc Owerfeldt of Sun MicroSystems wrote to Discussion list for Java Media Framework API <JMF-INTEREST@JAVA.SUN.COM>, saying "We are very aware of latency issues and try to optimize where we can, but overall it will be hard to achieve a total system latency of less than one second." Michael Bundschuh of Sun MicroSystems (Manager of Java Media Products) said "the high latency in JMF 2.0 will make this [video conferencing] pretty unusable." Ivan Wong of Sun MicroSystems warned against audio conferencing system using JMF, saying "I will also mention that currently JMF has fairly large audio latency for capture RTP. So bear that in mind when you are doing your VoIP app." Both Michael Bundschuh's and Ivan Wong's comments on JMF 2.0 can be found on the JMF web site.

Again, Sun fixed the high audio latency problem to some extent in JMF 2.1. With some buffer control, the audio latency is about 450 msec using JMF 2.1.

Possible Enhancements

Despite some of JMF's problems, we can make some enhancements in the design. As Sun claims, JMF can be used for audio conferencing in which there are more than two participants engage in a phone conversation. In this project, we only considered unicast case where there are only two people talking in a phone call. One possible way to achieve audio conferencing using JMF is to use SessionManager's two methods, addPeer and removePeer. Yet, both methods currently have bugs as reported in Sun's Java bug report. (We have not yet checked whether Sun has fixed this bug in JMF 2.1). Thus, we have to use an alternative way which is to transmit RTP data in multiple session. The idea is to clone DataSource and transmit the cloned DataSource in each session. The source code for a simple audio conference is available here.

In this project, the audio format for capture device, the source description for RTCP SDES report and RTP format are hard-coded in RTPAgent.java. In other words, they cannot be changed. To change them, the program must be re-compiled. In fact, only two pieces of information (the IP address and port number of the JMF agent on the other side) are given to the JMF agent by the MGCP Gateway. It is better for the MGCP Gateway and Call Agent in sipc to be changed so that these pararamers can specified using GUI rather than hard coded in the source code.

JMF is a very promising technology to develop multimedia applications. There is high demand for such a technology in the industry and Sun MicroSystems is trying hard to meet the industry needs. Even with JMF 2.1 release, there is still some known bugs, but there is no doubt that JMF is still a great choice for both audio and video streaming. This project was limited to audio application, but it can be extended for video application.

Task List / Source Code

Acknowledgement

I want to thank Rose Alappat(rka13@columbia.edu) for helping me with AgentTester.java (to test RTPAgent.java) and LocalPort.java. I also want to thank Xiaotao Wu (xiaotaow@cs.columbia.edu) for programming ideas.

The source code is based on JMF Programmer's Guide and sample code on JMF web site.

References

1
IETF RFC 1889, RTP: A Transport Protocol for Real-Time Applications .
2
Java Media Framework API Guide.

Last updated: 2000-06-01 by Dong Heun Lee