--- /dev/null
+diff -ur -x Makefile live/liveMedia/BasicUDPSource.cpp live.2004.10.28a/liveMedia/BasicUDPSource.cpp
+--- live/liveMedia/BasicUDPSource.cpp 2004-10-28 09:12:03.000000000 +0100
++++ live.2004.10.28a/liveMedia/BasicUDPSource.cpp 2004-10-28 14:47:18.000000000 +0100
+@@ -33,6 +33,11 @@
+ envir().taskScheduler().turnOffBackgroundReadHandling(fInputGS->socketNum());
+ }
+
++Groupsock* BasicUDPSource::groupSock()
++{
++ return fInputGS;
++}
++
+ void BasicUDPSource::doGetNextFrame() {
+ // Await the next incoming packet:
+ envir().taskScheduler().turnOnBackgroundReadHandling(fInputGS->socketNum(),
+diff -ur -x Makefile live/liveMedia/include/BasicUDPSource.hh live.2004.10.28a/liveMedia/include/BasicUDPSource.hh
+--- live/liveMedia/include/BasicUDPSource.hh 2004-10-28 09:12:02.000000000 +0100
++++ live.2004.10.28a/liveMedia/include/BasicUDPSource.hh 2004-10-28 14:47:45.000000000 +0100
+@@ -33,6 +33,7 @@
+ static BasicUDPSource* createNew(UsageEnvironment& env, Groupsock* inputGS);
+
+ virtual ~BasicUDPSource();
++ virtual Groupsock* groupSock();
+
+ private:
+ BasicUDPSource(UsageEnvironment& env, Groupsock* inputGS);
+diff -ur -x Makefile live/liveMedia/include/RTSPClient.hh live.2004.10.28a/liveMedia/include/RTSPClient.hh
+--- live/liveMedia/include/RTSPClient.hh 2004-10-28 09:12:02.000000000 +0100
++++ live.2004.10.28a/liveMedia/include/RTSPClient.hh 2004-10-29 14:40:37.000000000 +0100
+@@ -43,7 +43,7 @@
+ char const* sourceName,
+ RTSPClient*& resultClient);
+
+- char* describeURL(char const* url, Authenticator* authenticator = NULL);
++ char* describeURL(char const* url, Authenticator* authenticator = NULL, bool kasennaFlag = false);
+ // Issues a RTSP "DESCRIBE" command
+ // Returns the SDP description of a session, or NULL if none
+ // (This is dynamically allocated, and must later be freed
+@@ -178,6 +178,9 @@
+ #endif
+ unsigned fDescribeStatusCode;
+ // 0: OK; 1: connection failed; 2: stream unavailable
++
++ bool kasennaServer;
++ char kasennaContentType[10];
+ };
+
+ #endif
+diff -ur -x Makefile live/liveMedia/RTSPClient.cpp live.2004.10.28a/liveMedia/RTSPClient.cpp
+--- live/liveMedia/RTSPClient.cpp 2004-10-28 09:12:02.000000000 +0100
++++ live.2004.10.28a/liveMedia/RTSPClient.cpp 2004-11-01 15:06:47.026406304 +0000
+@@ -127,7 +127,7 @@
+ return NULL;
+ }
+
+-char* RTSPClient::describeURL(char const* url, Authenticator* authenticator) {
++char* RTSPClient::describeURL(char const* url, Authenticator* authenticator, bool kasennaFlag) {
+ char* cmd = NULL;
+ fDescribeStatusCode = 0;
+ do {
+@@ -149,13 +149,21 @@
+ char* authenticatorStr
+ = createAuthenticatorString(authenticator, "DESCRIBE", url);
+
++ char * acceptStr;
++
++ if (kasennaFlag)
++ acceptStr = "Accept: application/sdp\r\n";
++ else
++ acceptStr = "Accept: application/x-rtsp-mh, application/sdp\r\n";
++
++
+ // (Later implement more, as specified in the RTSP spec, sec D.1 #####)
+ char* const cmdFmt =
+ "DESCRIBE %s RTSP/1.0\r\n"
+ "CSeq: %d\r\n"
+- "Accept: application/sdp\r\n"
+ "%s"
+- "%s"
++ "%s"
++ "%s"
+ #ifdef SUPPORT_REAL_RTSP
+ REAL_DESCRIBE_HEADERS
+ #endif
+@@ -163,12 +171,14 @@
+ unsigned cmdSize = strlen(cmdFmt)
+ + strlen(url)
+ + 20 /* max int len */
++ + strlen(acceptStr)
+ + strlen(authenticatorStr)
+ + fUserAgentHeaderStrSize;
+ cmd = new char[cmdSize];
+ sprintf(cmd, cmdFmt,
+ url,
+ ++fCSeq,
++ acceptStr,
+ authenticatorStr,
+ fUserAgentHeaderStr);
+ delete[] authenticatorStr;
+@@ -214,6 +224,7 @@
+ // "Content-location", "CSeq", etc. #####
+ int contentLength = -1;
+ char* lineStart;
++ char serverType[20];
+ while (1) {
+ lineStart = nextLineStart;
+ if (lineStart == NULL) break;
+@@ -221,6 +232,10 @@
+ nextLineStart = getLine(lineStart);
+ if (lineStart[0] == '\0') break; // this is a blank line
+
++ if (sscanf(lineStart, "Server: %s", serverType) == 1)
++ if (strncmp(serverType, "Kasenna", 7) == 0)
++ kasennaServer = true;
++
+ if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1
+ || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) {
+ if (contentLength < 0) {
+@@ -318,6 +333,94 @@
+ bodyStart[to] = '\0'; // trims any extra data
+ }
+
++ if (kasennaServer && strncmp(bodyStart, "<MediaDescription>", 18) == 0)
++ {
++ // Translate from x-rtsp-mh to sdp
++
++ int videoPid, audioPid;
++ unsigned mh_duration;
++ char sdpBuf[2048], currentWord[20], ipAddressBuf[20];
++
++ char * currentPos = bodyStart;
++
++ while (strcmp(currentWord, "</MediaDescription>") != 0)
++ {
++ sscanf(currentPos, "%s", currentWord);
++
++ if (strcmp(currentWord, "VideoPid") == 0) {
++ currentPos += strlen(currentWord) + 1;
++ sscanf(currentPos, "%s", currentWord);
++ currentPos += strlen(currentWord) + 1;
++ sscanf(currentPos, "%d", &videoPid);
++ currentPos += 3;
++ }
++
++ if (strcmp(currentWord, "AudioPid") == 0) {
++ currentPos += strlen(currentWord) + 1;
++ sscanf(currentPos, "%s", currentWord);
++ currentPos += strlen(currentWord) + 1;
++ sscanf(currentPos, "%d", &audioPid);
++ currentPos += 3;
++ }
++
++ if (strcmp(currentWord, "Duration") == 0) {
++ currentPos += strlen(currentWord) + 1;
++ sscanf(currentPos, "%s", currentWord);
++ currentPos += strlen(currentWord) + 1;
++ sscanf(currentPos, "%d", &mh_duration);
++ currentPos += 3;
++ }
++
++
++ if (strcmp(currentWord, "TypeSpecificData") == 0) {
++ currentPos += strlen(currentWord) + 1;
++ sscanf(currentPos, "%s", currentWord);
++ currentPos += strlen(currentWord) + 1;
++ sscanf(currentPos, "%s", kasennaContentType);
++ currentPos += 3;
++ printf("Kasenna Content Type: %s\n", kasennaContentType);
++ }
++
++ currentPos += strlen(currentWord) + 1;
++ }
++
++ if (strcmp(kasennaContentType, "PARTNER_41_MPEG-4") == 0)
++ {
++ char * describeSDP = describeURL(url, authenticator, true);
++
++ delete[] cmd;
++ return describeSDP;
++ }
++
++ unsigned char byte1 = fServerAddress & 0x000000ff;
++ unsigned char byte2 = (fServerAddress & 0x0000ff00) >> 8;
++ unsigned char byte3 = (fServerAddress & 0x00ff0000) >> 16;
++ unsigned char byte4 = (fServerAddress & 0xff000000) >> 24;
++
++ sprintf(ipAddressBuf, "%u%s%u%s%u%s%u%s%c",
++ byte1, ".",
++ byte2, ".",
++ byte3, ".",
++ byte4, " ",
++ '\0');
++
++ sprintf(sdpBuf, "%s\r\n%s%s\r\n%s%s\r\n%s%s\r\n%s\r\n%s\r\n%s%u\r\n%s\r\n%s%d\r\n",
++ "v=0",
++ "o=NoSpacesAllowed 1 1 IN IP4 ", ipAddressBuf,
++ "s=", url,
++ "c=IN IP4 ", ipAddressBuf,
++ "t=0 0",
++ "a=control:*",
++ "a=range:npt=0-",
++ (unsigned)(mh_duration / 1000000),
++ "m=video 1554 RAW/RAW/UDP 33",
++ "a=control:trackID=", videoPid);
++
++ delete[] cmd;
++ return strDup(sdpBuf);
++ }
++
++
+ delete[] cmd;
+ return strDup(bodyStart);
+ } while (0);
+@@ -569,6 +672,11 @@
+ Boolean streamOutgoing,
+ Boolean streamUsingTCP) {
+ char* cmd = NULL;
++
++ char* setupStr = NULL;
++ char* setupFmt = NULL;
++ char* transportFmt = NULL;
++
+ do {
+ // Construct the SETUP command:
+
+@@ -613,6 +721,32 @@
+ rdtSource->setInputSocket(fSocketNum);
+ }
+ #endif
++
++ char const *prefix, *separator, *suffix;
++ constructSubsessionURL(subsession, prefix, separator, suffix);
++
++ if (kasennaServer && (strncmp(kasennaContentType, "MPEG-2", 6) == 0) ||
++ (strncmp(kasennaContentType, "MPEG-1", 6) == 0))
++ {
++ setupFmt = "SETUP %s%s RTSP/1.0\r\n";
++ transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n";
++
++ unsigned setupSize = strlen(setupFmt)
++ + strlen(prefix) + strlen (separator);
++
++ setupStr = new char[setupSize];
++ sprintf(setupStr, setupFmt, prefix, separator);
++ } else {
++ setupFmt = "SETUP %s%s%s RTSP/1.0\r\n";
++ transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n";
++
++ unsigned setupSize = strlen(setupFmt)
++ + strlen(prefix) + strlen (separator) + strlen(suffix);
++
++ setupStr = new char[setupSize];
++ sprintf(setupStr, setupFmt, prefix, separator, suffix);
++ }
++
+ if (transportStr == NULL) {
+ // Use a standard "Transport:" header.
+ char const* transportTypeStr;
+@@ -637,7 +771,7 @@
+ }
+ rtcpNumber = rtpNumber + 1;
+ }
+- char const* transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n";
++
+ unsigned transportSize = strlen(transportFmt)
+ + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 /* max port len */;
+ transportStr = new char[transportSize];
+@@ -647,7 +781,7 @@
+
+ // (Later implement more, as specified in the RTSP spec, sec D.1 #####)
+ char* const cmdFmt =
+- "SETUP %s%s%s RTSP/1.0\r\n"
++ "%s"
+ "CSeq: %d\r\n"
+ "%s"
+ "%s"
+@@ -655,11 +789,8 @@
+ "%s"
+ "\r\n";
+
+- char const *prefix, *separator, *suffix;
+- constructSubsessionURL(subsession, prefix, separator, suffix);
+-
+ unsigned cmdSize = strlen(cmdFmt)
+- + strlen(prefix) + strlen(separator) + strlen(suffix)
++ + strlen(setupStr)
+ + 20 /* max int len */
+ + strlen(transportStr)
+ + strlen(sessionStr)
+@@ -667,7 +798,7 @@
+ + fUserAgentHeaderStrSize;
+ cmd = new char[cmdSize];
+ sprintf(cmd, cmdFmt,
+- prefix, separator, suffix,
++ setupStr,
+ ++fCSeq,
+ transportStr,
+ sessionStr,
+@@ -896,7 +1027,7 @@
+
+ char const *prefix, *separator, *suffix;
+ constructSubsessionURL(subsession, prefix, separator, suffix);
+- if (hackForDSS) {
++ if (hackForDSS || kasennaServer) {
+ // When "PLAY" is used to inject RTP packets into a DSS
+ // (violating the RTSP spec, btw; "RECORD" should have been used)
+ // the DSS can crash (or hang) if the '/trackid=...' portion of
+@@ -1068,6 +1199,10 @@
+
+ char const *prefix, *separator, *suffix;
+ constructSubsessionURL(subsession, prefix, separator, suffix);
++
++ if (kasennaServer)
++ separator = suffix = "";
++
+ unsigned cmdSize = strlen(cmdFmt)
+ + strlen(prefix) + strlen(separator) + strlen(suffix)
+ + 20 /* max int len */