📄 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.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA**********/// "liveMedia"// Copyright (c) 1996-2010 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"#include "Locale.hh"#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), fMaxPlayStartTime(0.0f), fMaxPlayEndTime(0.0f), fScale(1.0f), fMediaSessionType(NULL), fSessionName(NULL), fSessionDescription(NULL), fControlPath(NULL) { 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; delete[] fMediaSessionType; delete[] fSessionName; delete[] fSessionDescription; delete[] fControlPath;}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_s(sdpLine)) continue; if (parseSDPLine_i(sdpLine)) continue; if (parseSDPLine_c(sdpLine)) continue; if (parseSDPAttribute_control(sdpLine)) continue; if (parseSDPAttribute_range(sdpLine)) continue; if (parseSDPAttribute_type(sdpLine)) continue; if (parseSDPAttribute_source_filter(sdpLine)) continue; } 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 const* 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->parseSDPLine_b(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_dimensions(sdpLine)) continue; if (subsession->parseSDPAttribute_framerate(sdpLine)) continue; // (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 %[^/\r\n]", buffer) == 1) { // Later, handle the optional /<ttl> and /<numAddresses> ##### resultStr = strDup(buffer); } delete[] buffer; return resultStr;}Boolean MediaSession::parseSDPLine_s(char const* sdpLine) { // Check for "s=<session name>" line char* buffer = strDupSize(sdpLine); Boolean parseSuccess = False; if (sscanf(sdpLine, "s=%[^\r\n]", buffer) == 1) { delete[] fSessionName; fSessionName = strDup(buffer); parseSuccess = True; } delete[] buffer; return parseSuccess;}Boolean MediaSession::parseSDPLine_i(char const* sdpLine) { // Check for "i=<session description>" line char* buffer = strDupSize(sdpLine); Boolean parseSuccess = False; if (sscanf(sdpLine, "i=%[^\r\n]", buffer) == 1) { delete[] fSessionDescription; fSessionDescription = strDup(buffer); parseSuccess = True; } delete[] buffer; return parseSuccess;}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;}Boolean MediaSession::parseSDPAttribute_type(char const* sdpLine) { // Check for a "a=type:broadcast|meeting|moderated|test|H.332|recvonly" line: Boolean parseSuccess = False; char* buffer = strDupSize(sdpLine); if (sscanf(sdpLine, "a=type: %[^ ]", buffer) == 1) { delete[] fMediaSessionType; fMediaSessionType = strDup(buffer); parseSuccess = True; } delete[] buffer; return parseSuccess;}static Boolean parseRangeAttribute(char const* sdpLine, double& startTime, double& endTime) { return sscanf(sdpLine, "a=range: npt = %lg - %lg", &startTime, &endTime) == 2;}Boolean MediaSession::parseSDPAttribute_control(char const* sdpLine) { // Check for a "a=control:<control-path>" line: Boolean parseSuccess = False; char* controlPath = strDupSize(sdpLine); // ensures we have enough space if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) { parseSuccess = True; delete[] fControlPath; fControlPath = strDup(controlPath); } delete[] controlPath; return parseSuccess;}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; double playStartTime; double playEndTime; if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) { parseSuccess = True; if (playStartTime > fMaxPlayStartTime) { fMaxPlayStartTime = playStartTime; } 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -