Hello, In my previous mail I have suggested how to prevent memory leaks in DarwinInjector.
I want to correct a possible crash in my solution and add some more. 1) My solution could cause NULL pointer memory access error when a DarwinInjector was deleted before a session was created. So a "if (session)" clause was added before my use of "session" in DarwinInjector's destructor. 2) I have added a deletion of MediaSubsession's sessionId in the MediaSubsession destructor. This way it is safe to delete a session without causing a memory leak because of sessionId. MediaSubsession::~MediaSubsession() { ... delete[] sessionId;//allocated by RTSP, freed only by RTSP Tear-Session ... } 3) When using a couple of DarwinInjector instances/sessions, the first stream of each does not get to have a track-id of "1", but the value is being kept incremented. This is because the track-id used is a static variable in the injector module. I had changed the variable to be a member of the injector, to be init as 0 in the constructor, and be passes by reference to be incremented by SubstreamDescriptor in DarwinInjector::addStream(). Attached in DarwinInjector cpp and h files, with my previous and last modifications, marked with "/**/" on each modified line's start. Eyal Beit-Halachmi
/********** This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along 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-2007 Live Networks, Inc. All rights reserved. // An object that redirects one or more RTP/RTCP streams - forming a single // multimedia session - into a 'Darwin Streaming Server' (for subsequent // reflection to potentially arbitrarily many remote RTSP clients). // Implementation. #include "DarwinInjector.hh" #include <GroupsockHelper.hh> ////////// SubstreamDescriptor definition ////////// class SubstreamDescriptor { public: /**/ SubstreamDescriptor(RTPSink* rtpSink, RTCPInstance* rtcpInstance,unsigned &lastTrackId); ~SubstreamDescriptor(); SubstreamDescriptor*& next() { return fNext; } RTPSink* rtpSink() const { return fRTPSink; } RTCPInstance* rtcpInstance() const { return fRTCPInstance; } char const* sdpLines() const { return fSDPLines; } private: SubstreamDescriptor* fNext; RTPSink* fRTPSink; RTCPInstance* fRTCPInstance; char* fSDPLines; }; ////////// DarwinInjector implementation ////////// DarwinInjector* DarwinInjector::createNew(UsageEnvironment& env, char const* applicationName, int verbosityLevel) { return new DarwinInjector(env, applicationName, verbosityLevel); } Boolean DarwinInjector::lookupByName(UsageEnvironment& env, char const* name, DarwinInjector*& result) { result = NULL; // unless we succeed Medium* medium; if (!Medium::lookupByName(env, name, medium)) return False; if (!medium->isDarwinInjector()) { env.setResultMsg(name, " is not a 'Darwin injector'"); return False; } result = (DarwinInjector*)medium; return True; } DarwinInjector::DarwinInjector(UsageEnvironment& env, char const* applicationName, int verbosityLevel) : Medium(env), fApplicationName(strDup(applicationName)), fVerbosityLevel(verbosityLevel), fRTSPClient(NULL), fSubstreamSDPSizes(0), /**/ fHeadSubstream(NULL), fTailSubstream(NULL), session(NULL), lastTrackId(0) { } DarwinInjector::~DarwinInjector() { /**/ // Tear down media subsessions for gracefull closure and mem leaks prevention /**/ if(session) /**/ { /**/ MediaSubsessionIterator iter(*session); /**/ MediaSubsession* subsession; /**/ while ((subsession = iter.next()) != NULL) /**/ fRTSPClient->teardownMediaSubsession(*subsession);// Inform other side. Release subsessions ids /**/ Medium::close(session);// Also free subsessions ids /**/ session=NULL; /**/ } delete fHeadSubstream; delete[] (char*)fApplicationName; Medium::close(fRTSPClient); } void DarwinInjector::addStream(RTPSink* rtpSink, RTCPInstance* rtcpInstance) { if (rtpSink == NULL) return; // "rtpSink" should be non-NULL /**/ SubstreamDescriptor* newDescriptor = new SubstreamDescriptor(rtpSink, rtcpInstance, lastTrackId); if (fHeadSubstream == NULL) { fHeadSubstream = fTailSubstream = newDescriptor; } else { fTailSubstream->next() = newDescriptor; fTailSubstream = newDescriptor; } fSubstreamSDPSizes += strlen(newDescriptor->sdpLines()); } Boolean DarwinInjector ::setDestination(char const* remoteRTSPServerNameOrAddress, char const* remoteFileName, char const* sessionName, char const* sessionInfo, portNumBits remoteRTSPServerPortNumber, char const* remoteUserName, char const* remotePassword, char const* sessionAuthor, char const* sessionCopyright) { char* sdp = NULL; char* url = NULL; /**/ //MediaSession* session = NULL;//moved to be a member var Boolean success = False; // until we learn otherwise do { // Begin by creating our RTSP client object: fRTSPClient = RTSPClient::createNew(envir(), fVerbosityLevel, fApplicationName); if (fRTSPClient == NULL) break; // Get the remote RTSP server's IP address: struct in_addr addr; { NetAddressList addresses(remoteRTSPServerNameOrAddress); if (addresses.numAddresses() == 0) break; NetAddress const* address = addresses.firstAddress(); addr.s_addr = *(unsigned*)(address->data()); } char const* remoteRTSPServerAddressStr = our_inet_ntoa(addr); // Construct a SDP description for the session that we'll be streaming: char const* const sdpFmt = "v=0\r\n" "o=- %u %u IN IP4 127.0.0.1\r\n" "s=%s\r\n" "i=%s\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "a=x-qt-text-nam:%s\r\n" "a=x-qt-text-inf:%s\r\n" "a=x-qt-text-cmt:source application:%s\r\n" "a=x-qt-text-aut:%s\r\n" "a=x-qt-text-cpy:%s\r\n"; // plus, %s for each substream SDP unsigned sdpLen = strlen(sdpFmt) + 20 /* max int len */ + 20 /* max int len */ + strlen(sessionName) + strlen(sessionInfo) + strlen(remoteRTSPServerAddressStr) + strlen(sessionName) + strlen(sessionInfo) + strlen(fApplicationName) + strlen(sessionAuthor) + strlen(sessionCopyright) + fSubstreamSDPSizes; unsigned const sdpSessionId = our_random(); unsigned const sdpVersion = sdpSessionId; sdp = new char[sdpLen]; sprintf(sdp, sdpFmt, sdpSessionId, sdpVersion, // o= line sessionName, // s= line sessionInfo, // i= line remoteRTSPServerAddressStr, // c= line sessionName, // a=x-qt-text-nam: line sessionInfo, // a=x-qt-text-inf: line fApplicationName, // a=x-qt-text-cmt: line sessionAuthor, // a=x-qt-text-aut: line sessionCopyright // a=x-qt-text-cpy: line ); char* p = &sdp[strlen(sdp)]; SubstreamDescriptor* ss; for (ss = fHeadSubstream; ss != NULL; ss = ss->next()) { sprintf(p, "%s", ss->sdpLines()); p += strlen(p); } // Construct a RTSP URL for the remote stream: char const* const urlFmt = "rtsp://%s:%u/%s"; unsigned urlLen = strlen(urlFmt) + strlen(remoteRTSPServerNameOrAddress) + 5 /* max short len */ + strlen(remoteFileName); url = new char[urlLen]; sprintf(url, urlFmt, remoteRTSPServerNameOrAddress, remoteRTSPServerPortNumber, remoteFileName); // Do a RTSP "ANNOUNCE" with this SDP description: Boolean announceSuccess; if (remoteUserName[0] != '\0' || remotePassword[0] != '\0') { announceSuccess = fRTSPClient->announceWithPassword(url, sdp, remoteUserName, remotePassword); } else { announceSuccess = fRTSPClient->announceSDPDescription(url, sdp); } if (!announceSuccess) break; // Tell the remote server to start receiving the stream from us. // (To do this, we first create a "MediaSession" object from the SDP description.) session = MediaSession::createNew(envir(), sdp); if (session == NULL) break; ss = fHeadSubstream; MediaSubsessionIterator iter(*session); MediaSubsession* subsession; ss = fHeadSubstream; unsigned streamChannelId = 0; while ((subsession = iter.next()) != NULL) { if (!subsession->initiate()) break; if (!fRTSPClient->setupMediaSubsession(*subsession, True /*streamOutgoing*/, True /*streamUsingTCP*/)) { break; } // Tell this subsession's RTPSink and RTCPInstance to use // the RTSP TCP connection: ss->rtpSink()->setStreamSocket(fRTSPClient->socketNum(), streamChannelId++); if (ss->rtcpInstance() != NULL) { ss->rtcpInstance()->setStreamSocket(fRTSPClient->socketNum(), streamChannelId++); } ss = ss->next(); } if (subsession != NULL) break; // an error occurred above // Tell the RTSP server to start: if (!fRTSPClient->playMediaSession(*session)) break; // Finally, make sure that the output TCP buffer is a reasonable size: increaseSendBufferTo(envir(), fRTSPClient->socketNum(), 100*1024); success = True; } while (0); delete[] sdp; delete[] url; /**/ //Medium::close(session);//moved to the destructor return success; } Boolean DarwinInjector::isDarwinInjector() const { return True; } ////////// SubstreamDescriptor implementation ////////// /**///static unsigned lastTrackId = 0;//moved to be a member of DarwinInjector SubstreamDescriptor::SubstreamDescriptor(RTPSink* rtpSink, /**/ RTCPInstance* rtcpInstance, /**/ unsigned &lastTrackId) : fNext(NULL), fRTPSink(rtpSink), fRTCPInstance(rtcpInstance) { // Create the SDP description for this substream char const* mediaType = fRTPSink->sdpMediaType(); unsigned char rtpPayloadType = fRTPSink->rtpPayloadType(); char const* rtpPayloadFormatName = fRTPSink->rtpPayloadFormatName(); unsigned rtpTimestampFrequency = fRTPSink->rtpTimestampFrequency(); unsigned numChannels = fRTPSink->numChannels(); char* rtpmapLine; if (rtpPayloadType >= 96) { char* encodingParamsPart; if (numChannels != 1) { encodingParamsPart = new char[1 + 20 /* max int len */]; sprintf(encodingParamsPart, "/%d", numChannels); } else { encodingParamsPart = strDup(""); } char const* const rtpmapFmt = "a=rtpmap:%d %s/%d%s\r\n"; unsigned rtpmapFmtSize = strlen(rtpmapFmt) + 3 /* max char len */ + strlen(rtpPayloadFormatName) + 20 /* max int len */ + strlen(encodingParamsPart); rtpmapLine = new char[rtpmapFmtSize]; sprintf(rtpmapLine, rtpmapFmt, rtpPayloadType, rtpPayloadFormatName, rtpTimestampFrequency, encodingParamsPart); delete[] encodingParamsPart; } else { // Static payload type => no "a=rtpmap:" line rtpmapLine = strDup(""); } unsigned rtpmapLineSize = strlen(rtpmapLine); char const* auxSDPLine = fRTPSink->auxSDPLine(); if (auxSDPLine == NULL) auxSDPLine = ""; unsigned auxSDPLineSize = strlen(auxSDPLine); char const* const sdpFmt = "m=%s 0 RTP/AVP %u\r\n" "%s" // "a=rtpmap:" line (if present) "%s" // auxilliary (e.g., "a=fmtp:") line (if present) "a=control:trackID=%u\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 3 /* max char len */ + rtpmapLineSize + auxSDPLineSize + 20 /* max int len */; char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> rtpPayloadType, // m= <fmt list> rtpmapLine, // a=rtpmap:... (if present) auxSDPLine, // optional extra SDP line ++lastTrackId); // a=control:<track-id> fSDPLines = strDup(sdpLines); /**/ delete[] rtpmapLine; //added to prevent memory leak !!! delete[] sdpLines; } SubstreamDescriptor::~SubstreamDescriptor() { delete fSDPLines; delete fNext; }
DarwinInjector.hh
Description: Binary data
_______________________________________________ live-devel mailing list live-devel@lists.live555.com http://lists.live555.com/mailman/listinfo/live-devel