I'm trying to combine video and audio in the same stream. I do see posts were 
you just add a subsession, but it doesn't work if the subsession is an rtsp 
url. It also doesn't work the other way around where I do the RTSP URL audio 
first and then add the MPEG4 video subsession.

When I do the following it works and the Audio comes thru with no video:
//This is just audio, there's no cameras hooked up to the encoder.
inputAudioStr = "rtsp://193.168.5.11:554/axis-media/media.amp?video=0&audio=1";
sms = ProxyServerMediaSession::createNew(*env, rtspServer, inputAudioStr, 
streamName);

When I add the video from the UDP it doesn't work:
//This is just audio, there's no cameras hooked up to the encoder.
inputAudioStr = "rtsp://193.168.5.11:554/axis-media/media.amp?video=0&audio=1";
//This is where the video comes from
inputAddressStr = "239.255.0.1";
inputPortNum = "11841";
intr_addr = "0.0.0.17";
isTransportingStream = false;
sms = ProxyServerMediaSession::createNew(*env, rtspServer, inputAudioStr, 
streamName);
sms->addSubsession(UDPServerMediaSubsession::createNew(*env, inputAddressStr, 
inputPortNum, intr_addr, isTransportingStream));

If I just do the video from the UDP it also works:
//This is where the video comes from
inputAddressStr = "239.255.0.1";
inputPortNum = "11841";
intr_addr = "0.0.0.17";
isTransportingStream = false;
sms = ServerMediaSession::createNew(*env, streamName, streamName, 
descriptionString);
sms->addSubsession(UDPServerMediaSubsession::createNew(*env, inputAddressStr, 
inputPortNum, intr_addr, isTransportingStream));

Any help or direction would be appreciated.

Thanks,

Eric Eastman

P.S. see attached code
#pragma once

#define STRINGIZE2(s) #s
#define STRINGIZE(s) STRINGIZE2(s)

#define VERSION_MAJOR               1
#define VERSION_MINOR               0
#define VERSION_REVISION            0
#define VERSION_BUILD               0

#define VER_FILE_DESCRIPTION_STR    "An RTSP server that proxies exisitng RTSP 
Servers or takes in raw MPEGTS UDP streams as input."
#define VER_FILE_VERSION            VERSION_MAJOR, VERSION_MINOR, 
VERSION_REVISION, VERSION_BUILD
#define VER_FILE_VERSION_STR        STRINGIZE(VERSION_MAJOR)        \
                                    "." STRINGIZE(VERSION_MINOR)    \
                                    "." STRINGIZE(VERSION_REVISION) \
                                    "." STRINGIZE(VERSION_BUILD)    \

#define VER_PRODUCTNAME_STR         "RtspProxyServer"
#define VER_PRODUCT_VERSION         VER_FILE_VERSION
#define VER_PRODUCT_VERSION_STR     VER_FILE_VERSION_STR
#define VER_ORIGINAL_FILENAME_STR   VER_PRODUCTNAME_STR ".exe"
#define VER_INTERNAL_NAME_STR       VER_ORIGINAL_FILENAME_STR
#define VER_COPYRIGHT_STR           "Copyright (C) 2021"

//Adding pause for debug only
#define USE_PAUSE
#ifndef _DEBUG
#undef USE_PAUSE
#endif

#ifdef _DEBUG
#define VER_VER_DEBUG             VS_FF_DEBUG
#else
#define VER_VER_DEBUG             0
#endif

#define VER_FILEOS                  VOS_NT_WINDOWS32
#define VER_FILEFLAGS               VER_VER_DEBUG
#define VER_FILETYPE                VFT_APP
#include "OnDemandMediaSubsession.h"
#include <GroupsockHelper.hh>

OnDemandMediaSubsession
::OnDemandMediaSubsession(UsageEnvironment& env,
        Boolean reuseFirstSource,
        portNumBits initialPortNum,
        Boolean multiplexRTCPWithRTP)
        : ServerMediaSubsession(env),
        fSDPLines(NULL), fReuseFirstSource(reuseFirstSource),
        fMultiplexRTCPWithRTP(multiplexRTCPWithRTP), fLastStreamToken(NULL),
        fAppHandlerTask(NULL), fAppHandlerClientData(NULL) {
        fDestinationsHashTable = HashTable::create(ONE_WORD_HASH_KEYS);
        if (fMultiplexRTCPWithRTP) {
                fInitialPortNum = initialPortNum;
        }
        else {
                // Make sure RTP ports are even-numbered:
                fInitialPortNum = (initialPortNum + 1)&~1;
        }
        gethostname(fCNAME, sizeof fCNAME);
        fCNAME[sizeof fCNAME - 1] = '\0'; // just in case
}

OnDemandMediaSubsession::~OnDemandMediaSubsession() {
        delete[] fSDPLines;

        // Clean out the destinations hash table:
        while (1) {
                Destinations* destinations
                        = (Destinations*)(fDestinationsHashTable->RemoveNext());
                if (destinations == NULL) break;
                delete destinations;
        }
        delete fDestinationsHashTable;
}

char const*
OnDemandMediaSubsession::sdpLines(int addressFamily) {
        if (fSDPLines == NULL) {
                // We need to construct a set of SDP lines that describe this
                // subsession (as a unicast stream).  To do so, we first create
                // dummy (unused) source and "RTPSink" objects,
                // whose parameters we use for the SDP lines:
                unsigned estBitrate;
                FramedSource* inputSource = createNewStreamSource(0, 
estBitrate);
                if (inputSource == NULL) return NULL; // file not found

                Groupsock* dummyGroupsock = 
createGroupsock(nullAddress(addressFamily), 0);
                unsigned char rtpPayloadType = 96 + trackNumber() - 1; // if 
dynamic
                RTPSink* dummyRTPSink = createNewRTPSink(dummyGroupsock, 
rtpPayloadType, inputSource);
                if (dummyRTPSink != NULL && dummyRTPSink->estimatedBitrate() > 
0) estBitrate = dummyRTPSink->estimatedBitrate();

                setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate);
                Medium::close(dummyRTPSink);
                delete dummyGroupsock;
                closeStreamSource(inputSource);
        }

        return fSDPLines;
}

void OnDemandMediaSubsession
::getStreamParameters(unsigned clientSessionId,
        struct sockaddr_storage const& clientAddress,
        Port const& clientRTPPort,
        Port const& clientRTCPPort,
        int tcpSocketNum,
        unsigned char rtpChannelId,
        unsigned char rtcpChannelId,
        struct sockaddr_storage& destinationAddress,
        u_int8_t& /*destinationTTL*/,
        Boolean& isMulticast,
        Port& serverRTPPort,
        Port& serverRTCPPort,
        void*& streamToken) {
        if (addressIsNull(destinationAddress)) {
                // normal case - use the client address as the destination 
address:
                destinationAddress = clientAddress;
        }
        isMulticast = False;

        if (fLastStreamToken != NULL && fReuseFirstSource) {
                // Special case: Rather than creating a new 
'OnDemandStreamState',
                // we reuse the one that we've already created:
                serverRTPPort = 
((OnDemandStreamState*)fLastStreamToken)->serverRTPPort();
                serverRTCPPort = 
((OnDemandStreamState*)fLastStreamToken)->serverRTCPPort();
                ++((OnDemandStreamState*)fLastStreamToken)->referenceCount();
                streamToken = fLastStreamToken;
        }
        else {
                // Normal case: Create a new media source:
                unsigned streamBitrate;
                FramedSource* mediaSource
                        = createNewStreamSource(clientSessionId, streamBitrate);

                // Create 'groupsock' and 'sink' objects for the destination,
                // using previously unused server port numbers:
                RTPSink* rtpSink = NULL;
                BasicUDPSink* udpSink = NULL;
                Groupsock* rtpGroupsock = NULL;
                Groupsock* rtcpGroupsock = NULL;

                if (clientRTPPort.num() != 0 || tcpSocketNum >= 0) { // Normal 
case: Create destinations
                        portNumBits serverPortNum;
                        if (clientRTCPPort.num() == 0) {
                                // We're streaming raw UDP (not RTP). Create a 
single groupsock:
                                NoReuse dummy(envir()); // ensures that we skip 
over ports that are already in use
                                for (serverPortNum = fInitialPortNum; ; 
++serverPortNum) {
                                        serverRTPPort = serverPortNum;
                                        rtpGroupsock = 
createGroupsock(nullAddress(destinationAddress.ss_family), serverRTPPort);
                                        if (rtpGroupsock->socketNum() >= 0) 
break; // success
                                }

                                udpSink = BasicUDPSink::createNew(envir(), 
rtpGroupsock);
                        }
                        else {
                                // Normal case: We're streaming RTP (over UDP 
or TCP).  Create a pair of
                                // groupsocks (RTP and RTCP), with adjacent 
port numbers (RTP port number even).
                                // (If we're multiplexing RTCP and RTP over the 
same port number, it can be odd or even.)
                                NoReuse dummy(envir()); // ensures that we skip 
over ports that are already in use
                                for (portNumBits serverPortNum = 
fInitialPortNum; ; ++serverPortNum) {
                                        serverRTPPort = serverPortNum;
                                        rtpGroupsock = 
createGroupsock(nullAddress(destinationAddress.ss_family), serverRTPPort);
                                        if (rtpGroupsock->socketNum() < 0) {
                                                delete rtpGroupsock;
                                                continue; // try again
                                        }

                                        if (fMultiplexRTCPWithRTP) {
                                                // Use the RTP 'groupsock' 
object for RTCP as well:
                                                serverRTCPPort = serverRTPPort;
                                                rtcpGroupsock = rtpGroupsock;
                                        }
                                        else {
                                                // Create a separate 
'groupsock' object (with the next (odd) port number) for RTCP:
                                                serverRTCPPort = 
++serverPortNum;
                                                rtcpGroupsock = 
createGroupsock(nullAddress(destinationAddress.ss_family), serverRTCPPort);
                                                if (rtcpGroupsock->socketNum() 
< 0) {
                                                        delete rtpGroupsock;
                                                        delete rtcpGroupsock;
                                                        continue; // try again
                                                }
                                        }

                                        break; // success
                                }

                                unsigned char rtpPayloadType = 96 + 
trackNumber() - 1; // if dynamic
                                rtpSink = createNewRTPSink(rtpGroupsock, 
rtpPayloadType, mediaSource);
                                if (rtpSink != NULL && 
rtpSink->estimatedBitrate() > 0) streamBitrate = rtpSink->estimatedBitrate();
                        }

                        // Turn off the destinations for each groupsock.  
They'll get set later
                        // (unless TCP is used instead):
                        if (rtpGroupsock != NULL) 
rtpGroupsock->removeAllDestinations();
                        if (rtcpGroupsock != NULL) 
rtcpGroupsock->removeAllDestinations();

                        if (rtpGroupsock != NULL) {
                                // Try to use a big send buffer for RTP -  at 
least 0.1 second of
                                // specified bandwidth and at least 50 KB
                                unsigned rtpBufSize = streamBitrate * 25 / 2; 
// 1 kbps * 0.1 s = 12.5 bytes
                                if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 
1024;
                                increaseSendBufferTo(envir(), 
rtpGroupsock->socketNum(), rtpBufSize);
                        }
                }

                // Set up the state of the stream.  The stream will get started 
later:
                streamToken = fLastStreamToken
                        = new OnDemandStreamState(*this, serverRTPPort, 
serverRTCPPort, rtpSink, udpSink,
                                streamBitrate, mediaSource,
                                rtpGroupsock, rtcpGroupsock);
        }

        // Record these destinations as being for this client session id:
        Destinations* destinations;
        if (tcpSocketNum < 0) { // UDP
                destinations = new Destinations(destinationAddress, 
clientRTPPort, clientRTCPPort);
        }
        else { // TCP
                destinations = new Destinations(tcpSocketNum, rtpChannelId, 
rtcpChannelId);
        }
        fDestinationsHashTable->Add((char const*)clientSessionId, destinations);
}

void OnDemandMediaSubsession::startStream(unsigned clientSessionId,
        void* streamToken,
        TaskFunc* rtcpRRHandler,
        void* rtcpRRHandlerClientData,
        unsigned short& rtpSeqNum,
        unsigned& rtpTimestamp,
        ServerRequestAlternativeByteHandler* 
serverRequestAlternativeByteHandler,
        void* serverRequestAlternativeByteHandlerClientData) {
        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;
        Destinations* destinations
                = (Destinations*)(fDestinationsHashTable->Lookup((char 
const*)clientSessionId));
        if (streamState != NULL) {
                streamState->startPlaying(destinations, clientSessionId,
                        rtcpRRHandler, rtcpRRHandlerClientData,
                        serverRequestAlternativeByteHandler, 
serverRequestAlternativeByteHandlerClientData);
                RTPSink* rtpSink = streamState->rtpSink(); // alias
                if (rtpSink != NULL) {
                        rtpSeqNum = rtpSink->currentSeqNo();
                        rtpTimestamp = rtpSink->presetNextTimestamp();
                }
        }
}

void OnDemandMediaSubsession::pauseStream(unsigned /*clientSessionId*/,
        void* streamToken) {
        // Pausing isn't allowed if multiple clients are receiving data from
        // the same source:
        if (fReuseFirstSource) return;

        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;
        if (streamState != NULL) streamState->pause();
}

void OnDemandMediaSubsession::seekStream(unsigned /*clientSessionId*/,
        void* streamToken, double& seekNPT, double streamDuration, u_int64_t& 
numBytes) {
        numBytes = 0; // by default: unknown

        // Seeking isn't allowed if multiple clients are receiving data from 
the same source:
        if (fReuseFirstSource) return;

        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;
        if (streamState != NULL && streamState->mediaSource() != NULL) {
                seekStreamSource(streamState->mediaSource(), seekNPT, 
streamDuration, numBytes);

                streamState->startNPT() = (float)seekNPT;
                RTPSink* rtpSink = streamState->rtpSink(); // alias
                if (rtpSink != NULL) rtpSink->resetPresentationTimes();
        }
}

void OnDemandMediaSubsession::seekStream(unsigned /*clientSessionId*/,
        void* streamToken, char*& absStart, char*& absEnd) {
        // Seeking isn't allowed if multiple clients are receiving data from 
the same source:
        if (fReuseFirstSource) return;

        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;
        if (streamState != NULL && streamState->mediaSource() != NULL) {
                seekStreamSource(streamState->mediaSource(), absStart, absEnd);
        }
}

void OnDemandMediaSubsession::nullSeekStream(unsigned /*clientSessionId*/, 
void* streamToken,
        double streamEndTime, u_int64_t& numBytes) {
        numBytes = 0; // by default: unknown

        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;
        if (streamState != NULL && streamState->mediaSource() != NULL) {
                // Because we're not seeking here, get the current NPT, and 
remember it as the new 'start' NPT:
                streamState->startNPT() = getCurrentNPT(streamToken);

                double duration = streamEndTime - streamState->startNPT();
                if (duration < 0.0) duration = 0.0;
                setStreamSourceDuration(streamState->mediaSource(), duration, 
numBytes);

                RTPSink* rtpSink = streamState->rtpSink(); // alias
                if (rtpSink != NULL) rtpSink->resetPresentationTimes();
        }
}

void OnDemandMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,
        void* streamToken, float scale) {
        // Changing the scale factor isn't allowed if multiple clients are 
receiving data
        // from the same source:
        if (fReuseFirstSource) return;

        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;
        if (streamState != NULL && streamState->mediaSource() != NULL) {
                setStreamSourceScale(streamState->mediaSource(), scale);
        }
}

float OnDemandMediaSubsession::getCurrentNPT(void* streamToken) {
        do {
                if (streamToken == NULL) break;

                OnDemandStreamState* streamState = 
(OnDemandStreamState*)streamToken;
                RTPSink* rtpSink = streamState->rtpSink();
                if (rtpSink == NULL) break;

                return streamState->startNPT()
                        + (rtpSink->mostRecentPresentationTime().tv_sec - 
rtpSink->initialPresentationTime().tv_sec)
                        + (rtpSink->mostRecentPresentationTime().tv_usec - 
rtpSink->initialPresentationTime().tv_usec) / 1000000.0f;
        } while (0);

        return 0.0;
}

FramedSource* OnDemandMediaSubsession::getStreamSource(void* streamToken) {
        if (streamToken == NULL) return NULL;

        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;
        return streamState->mediaSource();
}

void OnDemandMediaSubsession
::getRTPSinkandRTCP(void* streamToken,
        RTPSink const*& rtpSink, RTCPInstance const*& rtcp) {
        if (streamToken == NULL) {
                rtpSink = NULL;
                rtcp = NULL;
                return;
        }

        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;
        rtpSink = streamState->rtpSink();
        rtcp = streamState->rtcpInstance();
}

void OnDemandMediaSubsession::deleteStream(unsigned clientSessionId,
        void*& streamToken) {
        OnDemandStreamState* streamState = (OnDemandStreamState*)streamToken;

        // Look up (and remove) the destinations for this client session:
        Destinations* destinations
                = (Destinations*)(fDestinationsHashTable->Lookup((char 
const*)clientSessionId));
        if (destinations != NULL) {
                fDestinationsHashTable->Remove((char const*)clientSessionId);

                // Stop streaming to these destinations:
                if (streamState != NULL) streamState->endPlaying(destinations, 
clientSessionId);
        }

        // Delete the "OnDemandStreamState" structure if it's no longer being 
used:
        if (streamState != NULL) {
                if (streamState->referenceCount() > 0) 
--streamState->referenceCount();
                if (streamState->referenceCount() == 0) {
                        delete streamState;
                        streamToken = NULL;
                }
        }

        // Finally, delete the destinations themselves:
        delete destinations;
}

char const* OnDemandMediaSubsession
::getAuxSDPLine(RTPSink* rtpSink, FramedSource* /*inputSource*/) {
        // Default implementation:
        return rtpSink == NULL ? NULL : rtpSink->auxSDPLine();
}

void OnDemandMediaSubsession::seekStreamSource(FramedSource* /*inputSource*/,
        double& /*seekNPT*/, double /*streamDuration*/, u_int64_t& numBytes) {
        // Default implementation: Do nothing
        numBytes = 0;
}

void OnDemandMediaSubsession::seekStreamSource(FramedSource* /*inputSource*/,
        char*& absStart, char*& absEnd) {
        // Default implementation: do nothing (but delete[] and assign 
"absStart" and "absEnd" to NULL, to show that we don't handle this)
        delete[] absStart; absStart = NULL;
        delete[] absEnd; absEnd = NULL;
}

void OnDemandMediaSubsession
::setStreamSourceScale(FramedSource* /*inputSource*/, float /*scale*/) {
        // Default implementation: Do nothing
}

void OnDemandMediaSubsession
::setStreamSourceDuration(FramedSource* /*inputSource*/, double 
/*streamDuration*/, u_int64_t& numBytes) {
        // Default implementation: Do nothing
        numBytes = 0;
}

void OnDemandMediaSubsession::closeStreamSource(FramedSource *inputSource) {
        Medium::close(inputSource);
}

Groupsock* OnDemandMediaSubsession
::createGroupsock(struct sockaddr_storage const& addr, Port port) {
        // Default implementation; may be redefined by subclasses:
        return new Groupsock(envir(), addr, port, 255);
}

RTCPInstance* OnDemandMediaSubsession
::createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */
        unsigned char const* cname, RTPSink* sink) {
        // Default implementation; may be redefined by subclasses:
        return RTCPInstance::createNew(envir(), RTCPgs, totSessionBW, cname, 
sink, NULL/*we're a server*/);
}

void OnDemandMediaSubsession
::setRTCPAppPacketHandler(RTCPAppHandlerFunc* handler, void* clientData) {
        fAppHandlerTask = handler;
        fAppHandlerClientData = clientData;
}

void OnDemandMediaSubsession
::sendRTCPAppPacket(u_int8_t subtype, char const* name,
        u_int8_t* appDependentData, unsigned appDependentDataSize) {
        OnDemandStreamState* streamState = 
(OnDemandStreamState*)fLastStreamToken;
        if (streamState != NULL) {
                streamState->sendRTCPAppPacket(subtype, name, appDependentData, 
appDependentDataSize);
        }
}

void OnDemandMediaSubsession
::setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource, unsigned 
estBitrate) {
        if (rtpSink == NULL) return;

        char const* mediaType = rtpSink->sdpMediaType();
        unsigned char rtpPayloadType = rtpSink->rtpPayloadType();
        struct sockaddr_storage const& addressForSDP = 
rtpSink->groupsockBeingUsed().groupAddress();
        portNumBits portNumForSDP = 
ntohs(rtpSink->groupsockBeingUsed().port().num());

        AddressString ipAddressStr(addressForSDP);
        char* rtpmapLine = rtpSink->rtpmapLine();
        char const* rtcpmuxLine = fMultiplexRTCPWithRTP ? "a=rtcp-mux\r\n" : "";
        char const* rangeLine = rangeSDPLine();
        char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource);
        if (auxSDPLine == NULL) auxSDPLine = "";

        char const* const sdpFmt =
                "m=%s %u RTP/AVP %d\r\n"
                "c=IN %s %s\r\n"
                "b=AS:%u\r\n"
                "%s"
                "%s"
                "%s"
                "%s"
                "a=control:%s\r\n";
        unsigned sdpFmtSize = strlen(sdpFmt)
                + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len 
*/
                + 3/*IP4 or IP6*/ + strlen(ipAddressStr.val())
                + 20 /* max int len */
                + strlen(rtpmapLine)
                + strlen(rtcpmuxLine)
                + strlen(rangeLine)
                + strlen(auxSDPLine)
                + strlen(trackId());
        char* sdpLines = new char[sdpFmtSize];
        sprintf(sdpLines, sdpFmt,
                mediaType, // m= <media>
                portNumForSDP, // m= <port>
                rtpPayloadType, // m= <fmt list>
                addressForSDP.ss_family == AF_INET ? "IP4" : "IP6", 
ipAddressStr.val(), // c= address
                estBitrate, // b=AS:<bandwidth>
                rtpmapLine, // a=rtpmap:... (if present)
                rtcpmuxLine, // a=rtcp-mux:... (if present)
                rangeLine, // a=range:... (if present)
                auxSDPLine, // optional extra SDP line
                trackId()); // a=control:<track-id>
        delete[](char*)rangeLine; delete[] rtpmapLine;

        delete[] fSDPLines; fSDPLines = strDup(sdpLines);
        delete[] sdpLines;
}


////////// OnDemandStreamState implementation //////////

static void afterPlayingOnDemandStreamState(void* clientData) {
        OnDemandStreamState* streamState = (OnDemandStreamState*)clientData;
        if (streamState->streamDuration() == 0.0) {
                // When the input stream ends, tear it down.  This will cause a 
RTCP "BYE"
                // to be sent to each client, teling it that the stream has 
ended.
                // (Because the stream didn't have a known duration, there was 
no other
                //  way for clients to know when the stream ended.)
                streamState->reclaim();
        }
        // Otherwise, keep the stream alive, in case a client wants to
        // subsequently re-play the stream starting from somewhere other than 
the end.
        // (This can be done only on streams that have a known duration.)
}

OnDemandStreamState::OnDemandStreamState(OnDemandMediaSubsession& master,
        Port const& serverRTPPort, Port const& serverRTCPPort,
        RTPSink* rtpSink, BasicUDPSink* udpSink,
        unsigned totalBW, FramedSource* mediaSource,
        Groupsock* rtpGS, Groupsock* rtcpGS)
        : fMaster(master), fAreCurrentlyPlaying(False), fReferenceCount(1),
        fServerRTPPort(serverRTPPort), fServerRTCPPort(serverRTCPPort),
        fRTPSink(rtpSink), fUDPSink(udpSink), 
fStreamDuration(master.duration()),
        fTotalBW(totalBW), fRTCPInstance(NULL) /* created later */,
        fMediaSource(mediaSource), fStartNPT(0.0), fRTPgs(rtpGS), 
fRTCPgs(rtcpGS) {
}

OnDemandStreamState::~OnDemandStreamState() {
        reclaim();
}

void OnDemandStreamState
::startPlaying(Destinations* dests, unsigned clientSessionId,
        TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,
        ServerRequestAlternativeByteHandler* 
serverRequestAlternativeByteHandler,
        void* serverRequestAlternativeByteHandlerClientData) {
        if (dests == NULL) return;

        if (fRTCPInstance == NULL && fRTPSink != NULL) {
                // Create (and start) a 'RTCP instance' for this RTP sink:
                fRTCPInstance = fMaster.createRTCP(fRTCPgs, fTotalBW, (unsigned 
char*)fMaster.fCNAME, fRTPSink);
                // Note: This starts RTCP running automatically
                fRTCPInstance->setAppHandler(fMaster.fAppHandlerTask, 
fMaster.fAppHandlerClientData);
        }

        if (dests->isTCP) {
                // Change RTP and RTCP to use the TCP socket instead of UDP:
                if (fRTPSink != NULL) {
                        fRTPSink->addStreamSocket(dests->tcpSocketNum, 
dests->rtpChannelId);
                        RTPInterface
                                
::setServerRequestAlternativeByteHandler(fRTPSink->envir(), dests->tcpSocketNum,
                                        serverRequestAlternativeByteHandler, 
serverRequestAlternativeByteHandlerClientData);
                        // So that we continue to handle RTSP commands from the 
client
                }
                if (fRTCPInstance != NULL) {
                        fRTCPInstance->addStreamSocket(dests->tcpSocketNum, 
dests->rtcpChannelId);

                        struct sockaddr_storage tcpSocketNumAsAddress; // hack
                        tcpSocketNumAsAddress.ss_family = AF_INET;
                        ((sockaddr_in&)tcpSocketNumAsAddress).sin_addr.s_addr = 
dests->tcpSocketNum;
                        
fRTCPInstance->setSpecificRRHandler(tcpSocketNumAsAddress, dests->rtcpChannelId,
                                rtcpRRHandler, rtcpRRHandlerClientData);
                }
        }
        else {
                // Tell the RTP and RTCP 'groupsocks' about this destination
                // (in case they don't already have it):
                if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, 
dests->rtpPort, clientSessionId);
                if (fRTCPgs != NULL && !(fRTCPgs == fRTPgs && 
dests->rtcpPort.num() == dests->rtpPort.num())) {
                        fRTCPgs->addDestination(dests->addr, dests->rtcpPort, 
clientSessionId);
                }
                if (fRTCPInstance != NULL) {
                        fRTCPInstance->setSpecificRRHandler(dests->addr, 
dests->rtcpPort,
                                rtcpRRHandler, rtcpRRHandlerClientData);
                }
        }

        // Some clients dont like the RTCP packet before the initial RTP packet
        //if (fRTCPInstance != NULL) {
        //      // Hack: Send an initial RTCP "SR" packet, before the initial 
RTP packet, so that receivers will (likely) be able to
        //      // get RTCP-synchronized presentation times immediately:
        //      fRTCPInstance->sendReport();
        //}

        if (!fAreCurrentlyPlaying && fMediaSource != NULL) {
                if (fRTPSink != NULL) {
                        fRTPSink->startPlaying(*fMediaSource, 
afterPlayingOnDemandStreamState, this);
                        fAreCurrentlyPlaying = True;
                }
                else if (fUDPSink != NULL) {
                        fUDPSink->startPlaying(*fMediaSource, 
afterPlayingOnDemandStreamState, this);
                        fAreCurrentlyPlaying = True;
                }
        }
}

void OnDemandStreamState::pause() {
        if (fRTPSink != NULL) fRTPSink->stopPlaying();
        if (fUDPSink != NULL) fUDPSink->stopPlaying();
        if (fMediaSource != NULL) fMediaSource->stopGettingFrames();
        fAreCurrentlyPlaying = False;
}

void OnDemandStreamState::endPlaying(Destinations* dests, unsigned 
clientSessionId) {
#if 0
        // The following code is temporarily disabled, because it erroneously 
sends RTCP "BYE"s to all clients if multiple
        // clients are streaming from the same data source (i.e., if 
"reuseFirstSource" is True), and we don't want that to happen
        // if we're being called as a result of a single one of these clients 
having sent a "TEARDOWN" (rather than the whole stream
        // having been closed, for all clients).
        // This will be fixed for real later.
        if (fRTCPInstance != NULL) {
                // Hack: Explicitly send a RTCP "BYE" packet now, because the 
code below will prevent that from happening later,
                // when "fRTCPInstance" gets deleted:
                fRTCPInstance->sendBYE();
        }
#endif

        if (dests->isTCP) {
                if (fRTPSink != NULL) {
                        // Comment out the following, because it prevents the 
"RTSPClientConnection" object
                        // from being closed after handling a "TEARDOWN": #####
                        
//RTPInterface::clearServerRequestAlternativeByteHandler(fRTPSink->envir(), 
dests->tcpSocketNum);
                        fRTPSink->removeStreamSocket(dests->tcpSocketNum, 
dests->rtpChannelId);
                }
                if (fRTCPInstance != NULL) {
                        fRTCPInstance->removeStreamSocket(dests->tcpSocketNum, 
dests->rtcpChannelId);

                        struct sockaddr_storage tcpSocketNumAsAddress; // hack
                        tcpSocketNumAsAddress.ss_family = AF_INET;
                        ((sockaddr_in&)tcpSocketNumAsAddress).sin_addr.s_addr = 
dests->tcpSocketNum;
                        
fRTCPInstance->unsetSpecificRRHandler(tcpSocketNumAsAddress, 
dests->rtcpChannelId);
                }
        }
        else {
                // Tell the RTP and RTCP 'groupsocks' to stop using these 
destinations:
                if (fRTPgs != NULL) fRTPgs->removeDestination(clientSessionId);
                if (fRTCPgs != NULL && fRTCPgs != fRTPgs) 
fRTCPgs->removeDestination(clientSessionId);
                if (fRTCPInstance != NULL) {
                        fRTCPInstance->unsetSpecificRRHandler(dests->addr, 
dests->rtcpPort);
                }
        }
}

void OnDemandStreamState::sendRTCPAppPacket(u_int8_t subtype, char const* name,
        u_int8_t* appDependentData, unsigned appDependentDataSize) {
        if (fRTCPInstance != NULL) {
                fRTCPInstance->sendAppPacket(subtype, name, appDependentData, 
appDependentDataSize);
        }
}

void OnDemandStreamState::reclaim() {
        // Delete allocated media objects
        Medium::close(fRTCPInstance) /* will send a RTCP BYE */; fRTCPInstance 
= NULL;
        Medium::close(fRTPSink); fRTPSink = NULL;
        Medium::close(fUDPSink); fUDPSink = NULL;

        fMaster.closeStreamSource(fMediaSource); fMediaSource = NULL;
        if (fMaster.fLastStreamToken == this) fMaster.fLastStreamToken = NULL;

        delete fRTPgs;
        if (fRTCPgs != fRTPgs) delete fRTCPgs;
        fRTPgs = NULL; fRTCPgs = NULL;
}
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include <GroupsockHelper.hh> 
#include "UDPServerMediaSubsession.h"
#include <tclap/CmdLine.h>
#include "version.h"

UsageEnvironment* env;

static void announceURL(RTSPServer* rtspServer, ServerMediaSession* sms); // 
forward

int main(int argc, char** argv) {
#ifdef USE_PAUSE
        // Debug pause used to help attach remote debugger.
        std::cout << "Press Enter to continue . . .";
        std::cin.get();
#endif
        // Begin by setting up our usage environment:
        TaskScheduler* scheduler = BasicTaskScheduler::createNew();
        env = BasicUsageEnvironment::createNew(*scheduler);

        char const* descriptionString = "Session streamed by \"" 
VER_PRODUCTNAME_STR "\"";

        try {

                // Set up the argument parser
                TCLAP::CmdLine cmd(VER_FILE_DESCRIPTION_STR, ' ', 
VER_PRODUCT_VERSION_STR);

                TCLAP::MultiArg<std::string> inputArgs("i", "input", "The input 
for the RTSP server, " 
                        "can be either an RTSP URL or a Raw MPEGTS stream.\n\n" 
                        "RTSP input: an rtsp url using either the rtsp or rtsps 
protocol prefix\n\n" 
                        "MPEGTS input: <[interface@]host:port> for the UDP 
stream." 
                        " Interface is optional and can either be the IP 
addresss or the interface Id. Ex: 192.168.1.1@239.255.0.0:1234", true,"string");
                cmd.add(inputArgs);

                TCLAP::ValueArg<portNumBits> portArg("p", "port", "The port to 
host the RTSP server on. Default is 554", false, 554, "int");
                cmd.add(portArg);

                TCLAP::ValueArg<std::string> streamNameArg("s", "streamNames", 
"Stream name(s) that will be appended to the end of the RTSP url." 
                        "To specify a unique stream name for each input url 
provide a comma(,) seperated list.", false, "stream", "string");
                cmd.add(streamNameArg);

                TCLAP::SwitchArg isRawMPEG4("r", "raw", "Does this video need 
to be transported? -r if not and it's raw MPEG4 and there's no need to 
transport.");
                cmd.add(isRawMPEG4);

                TCLAP::MultiArg<std::string> inputSeparateAudio("a", "audio", 
                        "(optional) The audio input for the RTSP server, can be 
either an RTSP URL or\n"
                        "a Raw MPEGTS stream.The audio is mixed with the MPEGTS 
stream or RTSP URL\n"
                        "specified in the input parameter.If left blank none is 
used..", false, "string");
                cmd.add(inputSeparateAudio);

                cmd.parse(argc, argv);

                // Parse arguments
                std::vector<std::string> inputUrls = inputArgs.getValue();
                std::vector<std::string> inputAudioUrls = 
inputSeparateAudio.getValue();
                portNumBits rtspPort = portArg.getValue();
                std::string streamNamePrefix = streamNameArg.getValue().c_str();
                std::vector<std::string> streamNamePrefixes;
                std::stringstream ss(streamNamePrefix);
                bool isTransportingStream = !isRawMPEG4.getValue();

                // Parse stream names, either 1 or equal to input urls
                while (ss.good()) {
                        std::string substr;
                        std::getline(ss, substr, ',');
                        streamNamePrefixes.push_back(substr);
                }
                std::vector<std::string>::size_type namePefixSize = 
streamNamePrefixes.size();
                if (namePefixSize > 1 && namePefixSize != inputUrls.size()) {
                        throw TCLAP::ArgException("Number of string name 
prefixes must be 1 or match the number of input urls.", "streamName");
                }

                UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
                // To implement client access control to the RTSP server, do 
the following:
                authDB = new UserAuthenticationDatabase;
                authDB->addUserRecord("username1", "password1"); // replace 
these with real strings
                // Repeat the above with each <username>, <password> that you 
wish to allow
                // access to the server.
#endif

                // Create the RTSP server:
                RTSPServer* rtspServer = RTSPServer::createNew(*env, rtspPort, 
authDB);
                if (rtspServer == NULL) {
                        *env << "Failed to create RTSP server: " << 
env->getResultMsg() << "\n";
                        exit(1);
                }

                char const* rtspPrefix = "rtsp://";
                unsigned const prefix1Length = 7;
                char const* rtspsPrefix = "rtsps://";
                unsigned const prefix2Length = 8;

                int i = 1;
                // Parse input urls
                for (std::string inputUrl : inputUrls) {

                        //Should be big enough
                        char streamName[128];

                        //Get stream name
                        if (namePefixSize > 1) {
                                sprintf(streamName, "%s", 
streamNamePrefixes.at(i-1).c_str());
                        }
                        else {
                                if (inputUrls.size() == 1) {
                                        sprintf(streamName, "%s", 
streamNamePrefixes.front().c_str()); // there's just one stream; give it this 
name
                                }
                                else {
                                        sprintf(streamName, "%s-%d", 
streamNamePrefixes.front().c_str(), i); // there's more than one stream; 
distinguish them by name
                                }
                        }

                        ServerMediaSession* sms = NULL;

                        // Handle Proxying RTSP stream
                        if (inputUrl.find(rtspPrefix) == 0) {

                                char const* inputAddressStr = inputUrl.c_str();

#pragma warning(suppress: 4018)
                                if (inputAudioUrls.size() >= i && 
!inputAudioUrls.at(i - 1).empty()) {
                                        char const* inputAudioStr = 
inputAudioUrls.at(i - 1).c_str();
                                        //This works fine by itself and plays 
video only.
                                        sms = 
ProxyServerMediaSession::createNew(*env, rtspServer, inputAddressStr, 
streamName);
                                        //TODO: How do I add the audio?
                                        
//sms->addSubsession(ProxyServerMediaSession::createNew(*env, rtspServer, 
inputAudioStr, streamName));
                                }
                                else {
                                        //This works fine by itself and plays 
the video stream with no audio.
                                        sms = 
ProxyServerMediaSession::createNew(*env, rtspServer, inputAddressStr, 
streamName);
                                }

                                *env << "\n RTSP input source\n";
                                *env << "\t(proxying the rtsp stream \""<< 
inputAddressStr <<")\n";
                        }
                        // Else Handle MPEGTS stream input
                        else {

                                // Parse interface IP
                                size_t indx = inputUrl.find_first_of("@");
                                std::string interface_addr = "";
                                if (indx < inputUrl.length()) {
                                        interface_addr = inputUrl.substr(0, 
indx);
                                        inputUrl = inputUrl.substr(indx + 1);
                                }

                                //Parse IP:Port from remaining Input URL
                                indx = inputUrl.find_first_of(":");
                                std::string inputHost = inputUrl.substr(0, 
indx);
                                std::string inputPort = inputUrl.substr(indx + 
1);

                                if (inputHost.empty() || inputPort.empty()) {
                                        throw TCLAP::ArgException("Invalid 
input url", "input");
                                }

                                portNumBits inputPortNum;
                                if (sscanf(inputPort.c_str(), "%hu", 
&inputPortNum) != 1 || inputPortNum <= 0) {
                                        throw TCLAP::ArgException("Invalid 
input url", "input");
                                }

                                // Set up each of the possible streams that can 
be served by the
                                // RTSP server.  Each such stream is 
implemented using a
                                // "ServerMediaSession" object, plus one or more
                                // "ServerMediaSubsession" objects for each 
audio/video substream.

                                char const* inputAddressStr = inputHost.c_str();

                                char const* intr_addr = interface_addr.c_str();

#pragma warning(suppress: 4018)
                                if (inputAudioUrls.size() >= i && 
!inputAudioUrls.at(i - 1).empty()) {
                                        char const* inputAudioStr = 
inputAudioUrls.at(i - 1).c_str();
                                        //This works fine by itself and plays 
audio only. If I add any video subsessions then this doesn't work.
                                        sms = 
ProxyServerMediaSession::createNew(*env, rtspServer, inputAudioStr, streamName);
                                        //This breaks the audio and, bonus 
feature, doesn't add the video.
                                        
sms->addSubsession(UDPServerMediaSubsession::createNew(*env, inputAddressStr, 
inputPortNum, intr_addr, isTransportingStream));
                                }
                                else {
                                        //This works fine by itself and plays 
the video stream with no audio.
                                        sms = 
ServerMediaSession::createNew(*env, streamName, streamName, descriptionString);
                                        
sms->addSubsession(UDPServerMediaSubsession::createNew(*env, inputAddressStr, 
inputPortNum, intr_addr, isTransportingStream));
                                }

                                *env << "\n UDP Transport Stream input source 
\n\t(";
                                *env << "IP multicast address " << 
inputAddressStr << ",";
                                *env << " port " << inputPortNum;
#pragma warning(suppress: 4018)
                                if (inputAudioUrls.size() >= i && 
!inputAudioUrls.at(i - 1).empty()) {
                                        *env << ", Audio ";
                                        *env << inputAudioUrls.at(i - 
1).c_str();
                                }
                                if (intr_addr != NULL && intr_addr[0] != '\0') {
                                        *env << ", Listening on interface " << 
intr_addr << ")\n";
                                }
                                else {
                                        *env << ")\n";
                                }
                        }

                        // Add the session to our media server
                        rtspServer->addServerMediaSession(sms);
                        announceURL(rtspServer, sms);
                        i++;
                }


                // Also, attempt to create a HTTP server for RTSP-over-HTTP 
tunneling.
                // Try first with the default HTTP port (80), and then with the 
alternative HTTP
                // port numbers (8000 and 8080).

                if (rtspServer->setUpTunnelingOverHTTP(80) || 
rtspServer->setUpTunnelingOverHTTP(8000) || 
rtspServer->setUpTunnelingOverHTTP(8080)) {
                        *env << "\n(We use port " << 
rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n";
                }
                else {
                        *env << "\n(RTSP-over-HTTP tunneling is not 
available.)\n";
                }


                env->taskScheduler().doEventLoop(); // does not return

        }
        catch (TCLAP::ArgException &e)  // catch exceptions
        {
                std::cerr << "error: " << e.error() << " for arg " << e.argId() 
<< std::endl;
        }

        return 0; // only to prevent compiler warning
}

static void announceURL(RTSPServer* rtspServer, ServerMediaSession* sms) {
        UsageEnvironment& env = rtspServer->envir();

        env << " Play this stream using the URL ";
        if (weHaveAnIPv4Address(env)) {
                char* url = rtspServer->ipv4rtspURL(sms);
                env << "\"" << url << "\"";
                delete[] url;
                if (weHaveAnIPv6Address(env)) env << " or ";
        }
        if (weHaveAnIPv6Address(env)) {
                char* url = rtspServer->ipv6rtspURL(sms);
                env << "\"" << url << "\"";
                delete[] url;
        }
        env << "\n";
}
#include "UDPServerMediaSubsession.h"
#include "BasicUDPSource.hh"
#include "SimpleRTPSource.hh"
#include "MPEG2TransportStreamFramer.hh"
#include "SimpleRTPSink.hh"
#include "GroupsockHelper.hh"

//      Media Subsession for receiving a live MPEGTS multicast stream 

UDPServerMediaSubsession* UDPServerMediaSubsession::createNew(UsageEnvironment& 
env,
        char const* inputAddressStr, Port const& inputPort, char const* 
interfaceAddressStr, bool isTransported) {
        return new UDPServerMediaSubsession(env, inputAddressStr, inputPort, 
interfaceAddressStr, isTransported);
}

UDPServerMediaSubsession::UDPServerMediaSubsession(UsageEnvironment& env,
        char const* inputAddressStr, Port const& inputPort, char const* 
interfaceAddressStr, bool isTransported)
        : OnDemandMediaSubsession(env, True/*reuseFirstSource*/),
        fInputPort(inputPort), fInputGroupsock(NULL) {
        fInputAddressStr = strDup(inputAddressStr);
        fInterfaceAddressStr = strDup(interfaceAddressStr);
        isStreamTransported = isTransported;
}

UDPServerMediaSubsession::~UDPServerMediaSubsession() {
        delete fInputGroupsock;
        delete[](char*)fInputAddressStr;
        delete[](char*)fInterfaceAddressStr;
}

FramedSource* UDPServerMediaSubsession::createNewStreamSource(unsigned/* 
clientSessionId*/, unsigned& estBitrate) {
        estBitrate = 5000; // kbps, estimate
        // Clean up old socket
        if (fInputGroupsock != NULL) {
                delete fInputGroupsock;
        }

        struct sockaddr_storage inputAddress;
        if (fInputAddressStr == NULL) {
                inputAddress = nullAddress();
        }
        else {
                NetAddressList inputAddresses(fInputAddressStr);
                if (inputAddresses.numAddresses() == 0) return NULL;
                copyAddress(inputAddress, inputAddresses.firstAddress());
        }

        if (fInterfaceAddressStr != NULL && fInterfaceAddressStr[0] != '\0') {
                // Set receiving address to the one defined for this 
subsession, 
                // this gets used in the Groupsock class when creating the 
socket
                ReceivingInterfaceAddr = inet_addr(fInterfaceAddressStr);
        }

        // Create group sock with appropriate ReceivingInterfaceAddr
        fInputGroupsock = new Groupsock(envir(), inputAddress, fInputPort, 255);

        // Set it back to the default
        ReceivingInterfaceAddr = INADDR_ANY;

        // Create the MPEGTS stream framer
        if (isStreamTransported)
        {
                return MPEG2TransportStreamFramer::createNew(envir(), 
BasicUDPSource::createNew(envir(), fInputGroupsock));
        }
        else
        {
                return BasicUDPSource::createNew(envir(), fInputGroupsock);
        }
}

RTPSink* UDPServerMediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock, 
unsigned char /*rtpPayloadTypeIfDynamic*/, FramedSource* /*inputSource*/) {
        return SimpleRTPSink::createNew(envir(), rtpGroupsock,
                33, 90000, "video", "MP2T",
                1, True, False /*no 'M' bit*/);
}
#pragma once

#ifndef _ON_DEMAND_SERVER_MEDIA_SUBSESSION_HH
#include "OnDemandServerMediaSubsession.hh"
#endif // !_ON_DEMAND_SERVER_MEDIA_SUBSESSION_HH


#ifndef _SERVER_MEDIA_SESSION_HH
#include "ServerMediaSession.hh"
#endif
#ifndef _RTP_SINK_HH
#include "RTPSink.hh"
#endif
#ifndef _BASIC_UDP_SINK_HH
#include "BasicUDPSink.hh"
#endif
#ifndef _RTCP_HH
#include "RTCP.hh"
#endif

class OnDemandMediaSubsession : public ServerMediaSubsession {
protected: // we're a virtual base class
        OnDemandMediaSubsession(UsageEnvironment& env, Boolean reuseFirstSource,
                portNumBits initialPortNum = 6970,
                Boolean multiplexRTCPWithRTP = False);
        virtual ~OnDemandMediaSubsession();

protected: // redefined virtual functions
        virtual char const* sdpLines(int addressFamily);
        virtual void getStreamParameters(unsigned clientSessionId,
                struct sockaddr_storage const& clientAddress,
                Port const& clientRTPPort,
                Port const& clientRTCPPort,
                int tcpSocketNum,
                unsigned char rtpChannelId,
                unsigned char rtcpChannelId,
                struct sockaddr_storage& destinationAddress,
                u_int8_t& destinationTTL,
                Boolean& isMulticast,
                Port& serverRTPPort,
                Port& serverRTCPPort,
                void*& streamToken);
        virtual void startStream(unsigned clientSessionId, void* streamToken,
                TaskFunc* rtcpRRHandler,
                void* rtcpRRHandlerClientData,
                unsigned short& rtpSeqNum,
                unsigned& rtpTimestamp,
                ServerRequestAlternativeByteHandler* 
serverRequestAlternativeByteHandler,
                void* serverRequestAlternativeByteHandlerClientData);
        virtual void pauseStream(unsigned clientSessionId, void* streamToken);
        virtual void seekStream(unsigned clientSessionId, void* streamToken, 
double& seekNPT, double streamDuration, u_int64_t& numBytes);
        virtual void seekStream(unsigned clientSessionId, void* streamToken, 
char*& absStart, char*& absEnd);
        virtual void nullSeekStream(unsigned clientSessionId, void* streamToken,
                double streamEndTime, u_int64_t& numBytes);
        virtual void setStreamScale(unsigned clientSessionId, void* 
streamToken, float scale);
        virtual float getCurrentNPT(void* streamToken);
        virtual FramedSource* getStreamSource(void* streamToken);
        virtual void getRTPSinkandRTCP(void* streamToken,
                RTPSink const*& rtpSink, RTCPInstance const*& rtcp);
        virtual void deleteStream(unsigned clientSessionId, void*& streamToken);

protected: // new virtual functions, possibly redefined by subclasses
        virtual char const* getAuxSDPLine(RTPSink* rtpSink,
                FramedSource* inputSource);
        virtual void seekStreamSource(FramedSource* inputSource, double& 
seekNPT, double streamDuration, u_int64_t& numBytes);
        // This routine is used to seek by relative (i.e., NPT) time.
        // "streamDuration", if >0.0, specifies how much data to stream, past 
"seekNPT".  (If <=0.0, all remaining data is streamed.)
        // "numBytes" returns the size (in bytes) of the data to be streamed, 
or 0 if unknown or unlimited.
        virtual void seekStreamSource(FramedSource* inputSource, char*& 
absStart, char*& absEnd);
        // This routine is used to seek by 'absolute' time.
        // "absStart" should be a string of the form "YYYYMMDDTHHMMSSZ" or 
"YYYYMMDDTHHMMSS.<frac>Z".
        // "absEnd" should be either NULL (for no end time), or a string of the 
same form as "absStart".
        // These strings may be modified in-place, or can be reassigned to a 
newly-allocated value (after delete[]ing the original).
        virtual void setStreamSourceScale(FramedSource* inputSource, float 
scale);
        virtual void setStreamSourceDuration(FramedSource* inputSource, double 
streamDuration, u_int64_t& numBytes);
        virtual void closeStreamSource(FramedSource* inputSource);

protected: // new virtual functions, defined by all subclasses
        virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
                unsigned& estBitrate) = 0;
        // "estBitrate" is the stream's estimated bitrate, in kbps
        virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
                unsigned char rtpPayloadTypeIfDynamic,
                FramedSource* inputSource) = 0;

protected: // new virtual functions, may be redefined by a subclass:
        virtual Groupsock* createGroupsock(struct sockaddr_storage const& addr, 
Port port);
        virtual RTCPInstance* createRTCP(Groupsock* RTCPgs, unsigned 
totSessionBW, /* in kbps */
                unsigned char const* cname, RTPSink* sink);

public:
        void multiplexRTCPWithRTP() { fMultiplexRTCPWithRTP = True; }
        // An alternative to passing the "multiplexRTCPWithRTP" parameter as 
True in the constructor

        void setRTCPAppPacketHandler(RTCPAppHandlerFunc* handler, void* 
clientData);
        // Sets a handler to be called if a RTCP "APP" packet arrives from any 
future client.
        // (Any current clients are not affected; any "APP" packets from them 
will continue to be
        // handled by whatever handler existed when the client sent its first 
RTSP "PLAY" command.)
        // (Call with (NULL, NULL) to remove an existing handler - for future 
clients only)

        void sendRTCPAppPacket(u_int8_t subtype, char const* name,
                u_int8_t* appDependentData, unsigned appDependentDataSize);
        // Sends a custom RTCP "APP" packet to the most recent client (if 
"reuseFirstSource" was False),
        // or to all current clients (if "reuseFirstSource" was True).
        // The parameters correspond to their
        // respective fields as described in the RTP/RTCP definition (RFC 3550).
        // Note that only the low-order 5 bits of "subtype" are used, and only 
the first 4 bytes
        // of "name" are used.  (If "name" has fewer than 4 bytes, or is NULL,
        // then the remaining bytes are '\0'.)

protected:
        void setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource,
                unsigned estBitrate);
        // used to implement "sdpLines()"

protected:
        char* fSDPLines;
        HashTable* fDestinationsHashTable; // indexed by client session id

private:
        Boolean fReuseFirstSource;
        portNumBits fInitialPortNum;
        Boolean fMultiplexRTCPWithRTP;
        void* fLastStreamToken;
        char fCNAME[100]; // for RTCP
        RTCPAppHandlerFunc* fAppHandlerTask;
        void* fAppHandlerClientData;
        friend class OnDemandStreamState;
};


class OnDemandStreamState {
public:
        OnDemandStreamState(OnDemandMediaSubsession& master,
                Port const& serverRTPPort, Port const& serverRTCPPort,
                RTPSink* rtpSink, BasicUDPSink* udpSink,
                unsigned totalBW, FramedSource* mediaSource,
                Groupsock* rtpGS, Groupsock* rtcpGS);
        virtual ~OnDemandStreamState();

        void startPlaying(Destinations* destinations, unsigned clientSessionId,
                TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,
                ServerRequestAlternativeByteHandler* 
serverRequestAlternativeByteHandler,
                void* serverRequestAlternativeByteHandlerClientData);
        void pause();
        void sendRTCPAppPacket(u_int8_t subtype, char const* name,
                u_int8_t* appDependentData, unsigned appDependentDataSize);
        void endPlaying(Destinations* destinations, unsigned clientSessionId);
        void reclaim();

        unsigned& referenceCount() { return fReferenceCount; }

        Port const& serverRTPPort() const { return fServerRTPPort; }
        Port const& serverRTCPPort() const { return fServerRTCPPort; }

        RTPSink* rtpSink() const { return fRTPSink; }
        RTCPInstance* rtcpInstance() const { return fRTCPInstance; }

        float streamDuration() const { return fStreamDuration; }

        FramedSource* mediaSource() const { return fMediaSource; }
        float& startNPT() { return fStartNPT; }

private:
        OnDemandMediaSubsession& fMaster;
        Boolean fAreCurrentlyPlaying;
        unsigned fReferenceCount;

        Port fServerRTPPort, fServerRTCPPort;

        RTPSink* fRTPSink;
        BasicUDPSink* fUDPSink;

        float fStreamDuration;
        unsigned fTotalBW;
        RTCPInstance* fRTCPInstance;

        FramedSource* fMediaSource;
        float fStartNPT; // initial 'normal play time'; reset after each seek

        Groupsock* fRTPgs;
        Groupsock* fRTCPgs;
};
#pragma once

#include "OnDemandMediaSubsession.h"

class UDPServerMediaSubsession : public OnDemandMediaSubsession {
public:
        static UDPServerMediaSubsession*
                createNew(UsageEnvironment& env,
                        char const* inputAddressStr, // An IP multicast 
address, or use "0.0.0.0" or NULL for unicast input
                        Port const& inputPort,
                        char const* interfaceAddressStr = NULL,
                        bool isTransported = true);
protected:
        UDPServerMediaSubsession(UsageEnvironment& env,
                char const* inputAddressStr, Port const& inputPort, char const* 
interfaceAddressStr, bool isTransported = true);
        // called only by createNew();
        virtual ~UDPServerMediaSubsession();

protected: // redefined virtual functions
        virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
                unsigned& estBitrate);
        virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
                unsigned char rtpPayloadTypeIfDynamic,
                FramedSource* inputSource);
protected:
        char const* fInputAddressStr;
        char const* fInterfaceAddressStr;
        Port fInputPort;
        Groupsock* fInputGroupsock;
        bool isStreamTransported;
};
_______________________________________________
live-devel mailing list
live-devel@lists.live555.com
http://lists.live555.com/mailman/listinfo/live-devel

Reply via email to