/*
* @(#)RtspUtil.java 1.12 02/08/21
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.media.content.rtsp;
import java.net.*;
import java.util.*;
import javax.media.*;
import javax.media.format.*;
import javax.media.rtp.*;
import com.sun.media.*;
import com.sun.media.sdp.*;
import com.sun.media.rtsp.*;
import com.sun.media.rtsp.protocol.*;
public class RtspUtil implements RtspListener{
// timer (in ms):
private final int TIMER_1 = 60000; // used for describe msg
private final int TIMER_2 = 30000; // used for all other messages
private RtspManager rtspManager;
private RTPManager mgrs[];
private String mediaTypes[];
private long sequenceNumber;
private int numberOfTracks;
private String userAgent;
private RtspUrl rtspUrl;
private String mediaControls[];
private int server_ports[];
private int client_ports[];
private String session_ids[];
private Message message;
private int connectionId;
private String url;
private double startPos;
private String processError;
private long duration;
private Vector listeners;
private ReceiveStreamListener parent;
public RtspUtil( ReceiveStreamListener parent) {
this.parent= parent;
listeners = new Vector();
rtspManager = new RtspManager(false);
sequenceNumber = (long)(Math.random() * 1000);
userAgent = "User-Agent: JMF RTSP Player " + BasicPlayer.VERSION;
rtspManager.addListener(this);
}
public void setUrl( String url) {
this.url = url;
}
public void setStartPos( double startPos) {
this.startPos = startPos;
}
public RTPManager getRTPManager( int i) {
return mgrs[ i];
}
public RTPManager[] getRTPManagers() {
return mgrs;
}
public String getMediaType( int i) {
return mediaTypes[ i];
}
public String[] getMediaTypes() {
return mediaTypes;
}
public long getDuration() {
return duration;
}
public void removeTrack( int trackId) {
Log.comment( "track removed: " + mediaTypes[ trackId]);
mgrs[ trackId].removeTargets( "media track not supported");
mgrs[ trackId].dispose();
numberOfTracks--;
if( trackId + 1 > mgrs.length) {
int length= mgrs.length - trackId - 1;
System.arraycopy( mgrs, trackId + 1, mgrs, trackId, length);
}
}
public boolean createConnection() {
boolean realized= true;
try {
rtspUrl = new RtspUrl(url);
} catch( MalformedURLException e) {
processError= "Invalid RTSP URL: " + url;
return false;
}
String ipAddress = getServerIpAddress();
if( ipAddress == null) {
Log.error( "Invalid server address:" + url);
processError= "Invalid Server adress: " + url;
return false;
}
connectionId = rtspManager.createConnection(ipAddress, rtspUrl.getPort());
if (connectionId < 0) {
switch (connectionId) {
case - 2:
processError = "Unknown RTSP Host!";
break;
case - 3:
processError = "Can't connect to RTSP Server!";
break;
default:
processError = "Unknown reason";
break;
}
realized = false;
}
return realized;
}
public void closeConnection() {
rtspManager.closeConnection(connectionId);
}
public boolean rtspSetup() {
String msg = "DESCRIBE rtsp://" + rtspUrl.getHost() + "/" + rtspUrl.getFile() +
" RTSP/1.0\r\n" + "CSeq: " + sequenceNumber + "\r\n" +
"Accept: application/sdp\r\n" + userAgent + "\r\n\r\n";
sendMessage(msg);
boolean timeout = waitForResponse(TIMER_1);
if (timeout) {
sendStatusMessage(StatusMessage.TIMEOUT, "Timeout received.");
return false;
}
if( !responseOk()) {
return false;
}
setDuration();
// create the RTP Managers:
numberOfTracks = getNumTracks();
client_ports = new int[ numberOfTracks];
mgrs = new RTPManager[ numberOfTracks];
// get control strings and media types:
mediaControls = new String[ numberOfTracks];
mediaTypes= new String[ numberOfTracks];
String dynamicPayloads[]= new String[ numberOfTracks];
for( int i = 0; i < numberOfTracks; i++) {
mgrs[ i] = createSessionManager( i);
mediaTypes[ i] = getCurMediaType( i);
mediaControls[ i] = getMediaAttributeValue( i, "control");
dynamicPayloads[ i] = getMediaAttributeValue( i, "rtpmap");
// Add the dynamic payloads if there's one.
if (mediaTypes[i] != null && dynamicPayloads[i] != null) {
addDynamicPayload(mgrs[i], mediaTypes[i], dynamicPayloads[i]);
}
}
// get the content base:
String contentBase = getContentBase();
session_ids = new String[ numberOfTracks];
server_ports = new int[ numberOfTracks];
// setup the individual tracks:
for( int i = 0; i < numberOfTracks; i++) {
if( i == 0) {
msg = "SETUP " + contentBase + mediaControls[ i] + " RTSP/1.0\r\n" +
"CSeq: " + sequenceNumber + "\r\n" +
"Transport: RTP/AVP;unicast;client_port=" + client_ports[ i] +
"-" + (client_ports[ i] + 1) + "\r\n" + userAgent + "\r\n\r\n";
} else {
msg = "SETUP " + contentBase + mediaControls[ i] + " RTSP/1.0\r\n" +
"CSeq: " + sequenceNumber + "\r\n" +
"Transport: RTP/AVP;unicast;client_port=" + client_ports[ i] +
"-" + (client_ports[ i] + 1) + "\r\n" + "Session: " +
session_ids[ 0] + "\r\n" + userAgent + "\r\n\r\n";
}
sendMessage(msg);
timeout = waitForResponse(TIMER_2);
if (timeout) {
Log.error("ERROR: Timeout received (1).");
processError= "Server is not responding";
return false;
}
if( !responseOk()) {
return false;
}
String sessionId= getSessionId();
if( sessionId == null) {
processError= "Invalid session ID";
return false;
}
session_ids[ i] = sessionId;
int pos = session_ids[ i].indexOf(';');
if (pos > 0) {
session_ids[ i] = session_ids[ i].substring(0, pos);
}
int serverPort= getServerDataPort();
if( serverPort == -1) {
processError= "Invalid server data port";
return false;
}
server_ports[ i] = serverPort;
}
return true;
}
private boolean responseOk() {
boolean result= false;
int statusCode= getStatusCode();
if (statusCode == com.sun.media.rtsp.protocol.StatusCode.OK) {
result= true;
} else {
processError = "Message from RTSP Server - " + getStatusText(statusCode);
}
return result;
}
private String getSessionId() {
String id= null;
try {
ResponseMessage responseMsg = (ResponseMessage) message.getParameter();
SessionHeader hdr = (SessionHeader) responseMsg.getResponse().getHeader(
Header.SESSION).parameter;
id= hdr.getSessionId();
} catch( Exception e) {
}
return id;
}
private int getServerDataPort() {
int port= -1;
try {
ResponseMessage responseMsg = (ResponseMessage) message.getParameter();
TransportHeader transport_hdr =
(TransportHeader) responseMsg.getResponse()
.getHeader( Header.TRANSPORT).parameter;
port= transport_hdr.getServerDataPort();
} catch( Exception e) {
}
return port;
}
public boolean rtspStart() {
String msg;
// start all tracks in one compound statement:
msg = "PLAY rtsp://" + rtspUrl.getHost() + "/" +
rtspUrl.getFile() + " RTSP/1.0\r\n" + "CSeq: " +
sequenceNumber + "\r\n" + "Range: npt=" +
(startPos / 1000000000) + "-\r\n" + "Session: " +
session_ids[ 0] + "\r\n" + userAgent + "\r\n\r\n";
sendMessage(msg);
boolean timeout = waitForResponse(TIMER_2);
if (timeout) {
processError= "Server is not responding";
return false;
}
int code= getStatusCode();
if( code == -1) {
processError= "Received invalid status code";
return false;
}
if (getStatusCode() == com.sun.media.rtsp.protocol.StatusCode.SESSION_NOT_FOUND) {
for( int i = 0; i < numberOfTracks; i++) {
mgrs[ i].removeTargets( "session not found");
mgrs[ i].dispose();
return false;
}
}
return true;
}
public void rtspStop() {
String msg = "PAUSE rtsp://" + rtspUrl.getHost() +
"/" + rtspUrl.getFile() +
" RTSP/1.0\r\n" + "CSeq: " + sequenceNumber + "\r\n" +
"Session: " + session_ids[ 0] + "\r\n" + userAgent +
"\r\n\r\n";
sendMessage(msg);
boolean timeout = waitForResponse(TIMER_2);
if (timeout) {
sendStatusMessage(StatusMessage.TIMEOUT, "Timeout received.");
return;
}
}
public void rtspTeardown() {
String msg = "TEARDOWN rtsp://" + rtspUrl.getHost() + "/" +
rtspUrl.getFile() + " RTSP/1.0\r\n" + "CSeq: " +
sequenceNumber + "\r\n" + "Session: " + session_ids[ 0] +
"\r\n" + userAgent + "\r\n\r\n";
sendMessage(msg);
boolean timeout= waitForResponse( TIMER_2);
if (timeout) {
sendStatusMessage( StatusMessage.TIMEOUT,
"Timeout received.");
return;
}
}
public RTPManager createSessionManager( int index) {
RTPManager rtpManager= RTPManager.newInstance();
if (rtpManager == null) {
return null;
}
rtpManager.addReceiveStreamListener((ReceiveStreamListener) parent);
try {
// create the local Session Address:
InetAddress localHost = InetAddress.getLocalHost();
// SessionAddress localAddress = new SessionAddress( localHost,
// client_port);
SessionAddress localAddress= new SessionAddress();
// initialize the RTP Manager:
rtpManager.initialize( localAddress);
client_ports[ index] = localAddress.getDataPort();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return rtpManager;
}
/**
* Parse the dynamic payload string and add the payload type to the
* RTPManager.
*/
private void addDynamicPayload(RTPManager mgr, String typeStr,
String dpStr) {
// Parse the dynamic payload.
int payload;
Format fmt;
int c;
String tmpStr;
// Skip leading spaces.
c = 0;
while (dpStr.length() > 0 && dpStr.charAt(c) == ' ') c++;
if (c > 0)
dpStr = dpStr.substring(c);
// Parse the payload number.
c = 0;
while (dpStr.length() > 0 && dpStr.charAt(c) != ' ') c++;
if (c < 0)
return;
tmpStr = dpStr.substring(0, c);
dpStr = dpStr.substring(c);
Integer integer = Integer.valueOf(tmpStr);
if (integer == null)
return;
// Check the payload value.
payload = integer.intValue();
if (payload < 96 || payload > 127)
return;
// Skip leading spaces.
c = 0;
while (dpStr.length() > 0 && dpStr.charAt(c) == ' ') c++;
if (c > 0)
dpStr = dpStr.substring(c);
if (dpStr.length() == 0)
return;
// Parse the format string.
c = 0;
while (dpStr.length() > 0 && dpStr.charAt(c) != '/') c++;
if (c > 0)
tmpStr = dpStr.substring(0, c);
else
tmpStr = dpStr;
if (tmpStr.length() == 0)
return;
// Append "/rtp" to generate a JMF RTP encoding.
tmpStr = tmpStr.toLowerCase() + "/rtp";
// Add the payload type to format mapping the the manager.
if ("video".equalsIgnoreCase(typeStr)) {
mgr.addFormat(new VideoFormat(tmpStr), payload);
Log.comment("Add RTP dynamic payload for video: " + payload + " : " + tmpStr);
} if ("audio".equalsIgnoreCase(typeStr)) {
mgr.addFormat(new AudioFormat(tmpStr), payload);
Log.comment("Add RTP dynamic payload for audio: " + payload + " : " + tmpStr);
}
}
public int getStatusCode() {
int code= -1;
try {
ResponseMessage responseMsg = (ResponseMessage) message.getParameter();
code= responseMsg.getResponse().getStatusLine().getCode();
} catch( Exception e) {
}
return code;
}
private String getStatusText( int code) {
return StatusCode.getStatusText( code);
}
private void setDuration() {
duration= 0;
ResponseMessage msg = (ResponseMessage) message.getParameter();
double start_time = 0.0;
double end_time = 0.0;
SdpParser sdp = msg.getResponse().sdp;
if (sdp != null) {
MediaAttribute attribute = sdp.getSessionAttribute("range");
if (attribute != null) {
String value= attribute.getValue();
if (value.startsWith("npt")) {
int start = value.indexOf('=') + 1;
int end = value.indexOf('-');
String startTime = value.substring(start, end).trim();
String endTime = value.substring(end + 1).trim();
end_time = new Double(endTime).doubleValue();
duration = (long)(end_time * 1000000000);
}
}
}
}
private int getNumTracks() {
int numTracks = 0;
ResponseMessage msg = (ResponseMessage) message.getParameter();
SdpParser sdp = msg.getResponse().sdp;
if (sdp != null) {
numTracks = sdp.getMediaDescriptions().size();
}
return numTracks;
}
private MediaDescription getMediaDescription( String mediaName) {
MediaDescription description = null;
try {
ResponseMessage msg = (ResponseMessage) message.getParameter();
SdpParser sdp = msg.getResponse().sdp;
description = sdp.getMediaDescription(mediaName);
} catch( Exception e) {
}
return description;
}
private String getCurMediaType( int i) {
String type = null;
try {
ResponseMessage msg = (ResponseMessage) message.getParameter();
SdpParser sdp = msg.getResponse().sdp;
MediaDescription md = (MediaDescription) sdp.getMediaDescriptions().elementAt( i);
type = md.name;
} catch( Exception e) {
}
return type;
}
static public String getMediaAttribute( MediaDescription md,
String attribute) {
String mediaAttribute= "";
if( md != null) {
MediaAttribute ma = md.getMediaAttribute( "control");
if( ma != null) {
mediaAttribute= ma.getValue();
}
}
return mediaAttribute;
}
private String getMediaAttributeValue( int i, String attribute) {
String value = null;
try {
ResponseMessage msg = (ResponseMessage) message.getParameter();
SdpParser sdp = msg.getResponse().sdp;
MediaDescription md = (MediaDescription) sdp.getMediaDescriptions().elementAt( i);
MediaAttribute ma= md.getMediaAttribute( attribute);
value= ma.getValue();
} catch( Exception e) {
}
return value;
}
private String getTrackID(String mediaName) {
String trackId = null;
try {
ResponseMessage msg = (ResponseMessage) message.getParameter();
SdpParser sdp = msg.getResponse().sdp;
MediaDescription description = sdp.getMediaDescription(mediaName);
MediaAttribute attribute = description.getMediaAttribute("control");
trackId = attribute.getValue();
} catch( Exception e) {
}
return trackId;
}
private String getContentBase() {
String contentBase= "";
try {
ResponseMessage responseMsg = (ResponseMessage) message.getParameter();
Header header = responseMsg.getResponse().getHeader( Header.CONTENT_BASE);
ContentBaseHeader cbh = (ContentBaseHeader)header.parameter;
contentBase = cbh.getContentBase();
} catch( Exception e) {
}
return contentBase;
}
private void sendMessage(String message) {
responseReceived = false;
boolean success = rtspManager.sendMessage(connectionId, message);
if (!success) {
String ipAddress = getServerIpAddress();
connectionId = rtspManager.createConnection(ipAddress, rtspUrl.getPort());
rtspManager.sendMessage(connectionId, message);
}
}
boolean responseReceived;
boolean dataReceived;
Object responseSync = new Object();
private synchronized boolean waitForResponse(int time) {
boolean timeout = false;
try {
synchronized (responseSync) {
if (!responseReceived) {
responseSync.wait(time);
}
if (responseReceived) {
sequenceNumber++;
} else {
timeout = true;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return timeout;
}
private void processRtspRequest(int connectionId, Message message) {
if (message.getType() == MessageType.OPTIONS) {
OptionsMessage msg = (OptionsMessage) message.getParameter();
sendResponse(connectionId, msg.getRequest());
}
}
private void processRtspResponse(int connectionId, Message message) {
this.message = message;
responseReceived = true;
synchronized (responseSync) {
responseSync.notify();
}
}
private void sendResponse(int connectionId, Request msg) {
String type = null;
Header header = msg.getHeader(Header.CSEQ);
if( header != null) {
CSeqHeader cSeqHeader = (CSeqHeader) header.parameter;
String message = "RTSP/1.0 200 OK\r\n" + "CSeq: " +
cSeqHeader.getSequenceNumber() + "\r\n\r\n";
sendMessage(message);
}
}
private void sendStatusMessage(int code, String message) {
for (int i = 0; i < listeners.size(); i++) {
((RtspAppListener) listeners.elementAt(i)).postStatusMessage(code,
message);
}
}
public void addListener(RtspAppListener listener) {
listeners.addElement(listener);
}
public void removeListener(RtspAppListener listener) {
listeners.removeElement(listener);
}
public int getNumberOfTracks() {
return numberOfTracks;
}
public int[] getServerPorts() {
return server_ports;
}
public void rtspMessageIndication(int connectionId, Message message) {
if (message.getType() == MessageType.RESPONSE) {
processRtspResponse(connectionId, message);
} else {
processRtspRequest(connectionId, message);
}
}
public String getServerIpAddress() {
String ipAddress = null;
try {
if( rtspUrl == null) {
rtspUrl= new RtspUrl( url);
}
String host = rtspUrl.getHost();
if( host != null) {
ipAddress = InetAddress.getByName(host).getHostAddress();
}
} catch( MalformedURLException e) {
// System.err.println( "Malformed URL Exception: " + e);
} catch (UnknownHostException e) {
// System.err.println( "Host unknown: " + name);
}
return ipAddress;
}
public void rtspConnectionTerminated(int connectionId) {
// System.out.println( "RtspPlayer::rtspConnectionTerminated");
}
public String getProcessError() {
return processError;
}
public void setProcessError( String error) {
processError= error;
}
}
|