📄 mediasession.cpp
字号:
/**********This library is free software; you can redistribute it and/or modify it underthe terms of the GNU Lesser General Public License as published by theFree Software Foundation; either version 2.1 of the License, or (at youroption) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)This library is distributed in the hope that it will be useful, but WITHOUTANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESSFOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License formore details.You should have received a copy of the GNU Lesser General Public Licensealong with this library; if not, write to the Free Software Foundation, Inc.,59 Temple Place, Suite 330, Boston, MA 02111-1307 USA**********/// "liveMedia"// Copyright (c) 1996-2004 Live Networks, Inc. All rights reserved.// A data structure that represents a session that consists of// potentially multiple (audio and/or video) sub-sessions// Implementation#include "liveMedia.hh"#ifdef SUPPORT_REAL_RTSP#include "../RealRTSP/include/RealRTSP.hh"#endif#include "GroupsockHelper.hh"#include <ctype.h>////////// MediaSession //////////MediaSession* MediaSession::createNew(UsageEnvironment& env, char const* sdpDescription) { MediaSession* newSession = new MediaSession(env); if (newSession != NULL) { if (!newSession->initializeWithSDP(sdpDescription)) { delete newSession; return NULL; } } return newSession;}Boolean MediaSession::lookupByName(UsageEnvironment& env, char const* instanceName, MediaSession*& resultSession) { resultSession = NULL; // unless we succeed Medium* medium; if (!Medium::lookupByName(env, instanceName, medium)) return False; if (!medium->isMediaSession()) { env.setResultMsg(instanceName, " is not a 'MediaSession' object"); return False; } resultSession = (MediaSession*)medium; return True;}MediaSession::MediaSession(UsageEnvironment& env) : Medium(env), fSubsessionsHead(NULL), fSubsessionsTail(NULL), fConnectionEndpointName(NULL), fMaxPlayEndTime(0.0f), fScale(1.0f) {#ifdef SUPPORT_REAL_RTSP RealInitSDPAttributes(this);#endif fSourceFilterAddr.s_addr = 0; // Get our host name, and use this for the RTCP CNAME: const unsigned maxCNAMElen = 100; char CNAME[maxCNAMElen+1];#ifndef CRIS gethostname((char*)CNAME, maxCNAMElen);#else // "gethostname()" isn't defined for this platform sprintf(CNAME, "unknown host %d", (unsigned)(our_random()*0x7FFFFFFF));#endif CNAME[maxCNAMElen] = '\0'; // just in case fCNAME = strDup(CNAME);}MediaSession::~MediaSession() { delete fSubsessionsHead; delete[] fCNAME; delete[] fConnectionEndpointName;#ifdef SUPPORT_REAL_RTSP RealReclaimSDPAttributes(this);#endif}Boolean MediaSession::isMediaSession() const { return True;}Boolean MediaSession::initializeWithSDP(char const* sdpDescription) { if (sdpDescription == NULL) return False; // Begin by processing all SDP lines until we see the first "m=" char const* sdpLine = sdpDescription; char const* nextSDPLine; while (1) { if (!parseSDPLine(sdpLine, nextSDPLine)) return False; //##### We should really check for: // - "a=control:" attributes (to set the URL for aggregate control) // - the correct SDP version (v=0) if (sdpLine[0] == 'm') break; sdpLine = nextSDPLine; if (sdpLine == NULL) break; // there are no m= lines at all // Check for various special SDP lines that we understand: if (parseSDPLine_c(sdpLine)) continue; if (parseSDPAttribute_range(sdpLine)) continue; if (parseSDPAttribute_source_filter(sdpLine)) continue;#ifdef SUPPORT_REAL_RTSP if (RealParseSDPAttributes(this, sdpLine)) continue;#endif } while (sdpLine != NULL) { // We have a "m=" line, representing a new sub-session: MediaSubsession* subsession = new MediaSubsession(*this); if (subsession == NULL) { envir().setResultMsg("Unable to create new MediaSubsession"); return False; } // Parse the line as "m=<medium_name> <client_portNum> RTP/AVP <fmt>" // or "m=<medium_name> <client_portNum>/<num_ports> RTP/AVP <fmt>" // (Should we be checking for >1 payload format number here?)##### char* mediumName = strDupSize(sdpLine); // ensures we have enough space char* protocolName = NULL; unsigned payloadFormat; if ((sscanf(sdpLine, "m=%s %hu RTP/AVP %u", mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 || sscanf(sdpLine, "m=%s %hu/%*u RTP/AVP %u", mediumName, &subsession->fClientPortNum, &payloadFormat) == 3) && payloadFormat <= 127) { protocolName = "RTP"; } else if ((sscanf(sdpLine, "m=%s %hu UDP %u", mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 || sscanf(sdpLine, "m=%s %hu udp %u", mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 || sscanf(sdpLine, "m=%s %hu RAW/RAW/UDP %u", mediumName, &subsession->fClientPortNum, &payloadFormat) == 3) && payloadFormat <= 127) { // This is a RAW UDP source protocolName = "UDP"; } else { // This "m=" line is bad; output an error message saying so: char* sdpLineStr; if (nextSDPLine == NULL) { sdpLineStr = (char*)sdpLine; } else { sdpLineStr = strDup(sdpLine); sdpLineStr[nextSDPLine-sdpLine] = '\0'; } envir() << "Bad SDP \"m=\" line: " << sdpLineStr << "\n"; if (sdpLineStr != (char*)sdpLine) delete[] sdpLineStr; delete[] mediumName; delete subsession; // Skip the following SDP lines, up until the next "m=": while (1) { sdpLine = nextSDPLine; if (sdpLine == NULL) break; // we've reached the end if (!parseSDPLine(sdpLine, nextSDPLine)) return False; if (sdpLine[0] == 'm') break; // we've reached the next subsession } continue; } // Insert this subsession at the end of the list: if (fSubsessionsTail == NULL) { fSubsessionsHead = fSubsessionsTail = subsession; } else { fSubsessionsTail->setNext(subsession); fSubsessionsTail = subsession; } subsession->serverPortNum = subsession->fClientPortNum; // by default char const* mStart = sdpLine; subsession->fSavedSDPLines = strDup(mStart); subsession->fMediumName = strDup(mediumName); delete[] mediumName; subsession->fProtocolName = strDup(protocolName); subsession->fRTPPayloadFormat = payloadFormat; // Process the following SDP lines, up until the next "m=": while (1) { sdpLine = nextSDPLine; if (sdpLine == NULL) break; // we've reached the end if (!parseSDPLine(sdpLine, nextSDPLine)) return False; if (sdpLine[0] == 'm') break; // we've reached the next subsession // Check for various special SDP lines that we understand: if (subsession->parseSDPLine_c(sdpLine)) continue; if (subsession->parseSDPAttribute_rtpmap(sdpLine)) continue; if (subsession->parseSDPAttribute_control(sdpLine)) continue; if (subsession->parseSDPAttribute_range(sdpLine)) continue; if (subsession->parseSDPAttribute_fmtp(sdpLine)) continue; if (subsession->parseSDPAttribute_source_filter(sdpLine)) continue; if (subsession->parseSDPAttribute_x_mct_slap(sdpLine)) continue; if (subsession->parseSDPAttribute_x_dimensions(sdpLine)) continue; if (subsession->parseSDPAttribute_x_framerate(sdpLine)) continue;#ifdef SUPPORT_REAL_RTSP if (RealParseSDPAttributes(subsession, sdpLine)) continue;#endif // (Later, check for malformed lines, and other valid SDP lines#####) } if (sdpLine != NULL) subsession->fSavedSDPLines[sdpLine-mStart] = '\0'; // If we don't yet know the codec name, try looking it up from the // list of static payload types: if (subsession->fCodecName == NULL) { subsession->fCodecName = lookupPayloadFormat(subsession->fRTPPayloadFormat, subsession->fRTPTimestampFrequency, subsession->fNumChannels); if (subsession->fCodecName == NULL) { char typeStr[20]; sprintf(typeStr, "%d", subsession->fRTPPayloadFormat); envir().setResultMsg("Unknown codec name for RTP payload type ", typeStr); return False; } } // If we don't yet know this subsession's RTP timestamp frequency // (because it uses a dynamic payload type and the corresponding // SDP "rtpmap" attribute erroneously didn't specify it), // then guess it now: if (subsession->fRTPTimestampFrequency == 0) { subsession->fRTPTimestampFrequency = guessRTPTimestampFrequency(subsession->fMediumName, subsession->fCodecName); } } return True;}Boolean MediaSession::parseSDPLine(char const* inputLine, char const*& nextLine){ // Begin by finding the start of the next line (if any): nextLine = NULL; for (char const* ptr = inputLine; *ptr != '\0'; ++ptr) { if (*ptr == '\r' || *ptr == '\n') { // We found the end of the line ++ptr; while (*ptr == '\r' || *ptr == '\n') ++ptr; nextLine = ptr; if (nextLine[0] == '\0') nextLine = NULL; // special case for end break; } } // Then, check that this line is a SDP line of the form <char>=<etc> // (However, we also accept blank lines in the input.) if (inputLine[0] == '\r' || inputLine[0] == '\n') return True; if (strlen(inputLine) < 2 || inputLine[1] != '=' || inputLine[0] < 'a' || inputLine[0] > 'z') { envir().setResultMsg("Invalid SDP line: ", inputLine); return False; } return True;}static char* parseCLine(char const* sdpLine) { char* resultStr = NULL; char* buffer = strDupSize(sdpLine); // ensures we have enough space if (sscanf(sdpLine, "c=IN IP4 %[^/ ]", buffer) == 1) { // Later, handle the optional /<ttl> and /<numAddresses> ##### resultStr = strDup(buffer); } delete[] buffer; return resultStr;}Boolean MediaSession::parseSDPLine_c(char const* sdpLine) { // Check for "c=IN IP4 <connection-endpoint>" // or "c=IN IP4 <connection-endpoint>/<ttl+numAddresses>" // (Later, do something with <ttl+numAddresses> also #####) char* connectionEndpointName = parseCLine(sdpLine); if (connectionEndpointName != NULL) { delete[] fConnectionEndpointName; fConnectionEndpointName = connectionEndpointName; return True; } return False;}static Boolean parseRangeAttribute(char const* sdpLine, float& endTime) { return sscanf(sdpLine, "a=range: npt = %*g - %g", &endTime) == 1;}Boolean MediaSession::parseSDPAttribute_range(char const* sdpLine) { // Check for a "a=range:npt=<startTime>-<endTime>" line: // (Later handle other kinds of "a=range" attributes also???#####) Boolean parseSuccess = False; float playEndTime; if (parseRangeAttribute(sdpLine, playEndTime)) { parseSuccess = True; if (playEndTime > fMaxPlayEndTime) { fMaxPlayEndTime = playEndTime; } } return parseSuccess;}static Boolean parseSourceFilterAttribute(char const* sdpLine, struct in_addr& sourceAddr) { // Check for a "a=source-filter:incl IN IP4 <something> <source>" line. // Note: At present, we don't check that <something> really matches // one of our multicast addresses. We also don't support more than // one <source> ##### Boolean result = False; // until we succeed char* sourceName = strDupSize(sdpLine); // ensures we have enough space do { if (sscanf(sdpLine, "a=source-filter: incl IN IP4 %*s %s", sourceName) != 1) break; // Now, convert this name to an address, if we can: NetAddressList addresses(sourceName); if (addresses.numAddresses() == 0) break; netAddressBits sourceAddrBits = *(netAddressBits*)(addresses.firstAddress()->data()); if (sourceAddrBits == 0) break; sourceAddr.s_addr = sourceAddrBits; result = True; } while (0); delete[] sourceName; return result;}Boolean MediaSession::parseSDPAttribute_source_filter(char const* sdpLine) { return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr);}char* MediaSession::lookupPayloadFormat(unsigned char rtpPayloadType, unsigned& freq, unsigned& nCh) { // Look up the codec name and timestamp frequency for known (static) // RTP payload formats. char* temp = NULL; switch (rtpPayloadType) { case 0: {temp = "PCMU"; freq = 8000; nCh = 1; break;} case 2: {temp = "G726-32"; freq = 8000; nCh = 1; break;} case 3: {temp = "GSM"; freq = 8000; nCh = 1; break;} case 4: {temp = "G723"; freq = 8000; nCh = 1; break;} case 5: {temp = "DVI4"; freq = 8000; nCh = 1; break;} case 6: {temp = "DVI4"; freq = 16000; nCh = 1; break;} case 7: {temp = "LPC"; freq = 8000; nCh = 1; break;} case 8: {temp = "PCMA"; freq = 8000; nCh = 1; break;} case 9: {temp = "G722"; freq = 8000; nCh = 1; break;} case 10: {temp = "L16"; freq = 44100; nCh = 2; break;} case 11: {temp = "L16"; freq = 44100; nCh = 1; break;} case 12: {temp = "QCELP"; freq = 8000; nCh = 1; break;} case 14: {temp = "MPA"; freq = 90000; nCh = 1; break;} // 'number of channels' is actually encoded in the media stream case 15: {temp = "G728"; freq = 8000; nCh = 1; break;} case 16: {temp = "DVI4"; freq = 11025; nCh = 1; break;} case 17: {temp = "DVI4"; freq = 22050; nCh = 1; break;} case 18: {temp = "G729"; freq = 8000; nCh = 1; break;} case 25: {temp = "CELB"; freq = 90000; nCh = 1; break;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -