New file |
| | |
| | | /********** |
| | | 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 3 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., |
| | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| | | **********/ |
| | | // "liveMedia" |
| | | // Copyright (c) 1996-2017 Live Networks, Inc. All rights reserved. |
| | | // A data structure that represents a session that consists of |
| | | // potentially multiple (audio and/or video) sub-sessions |
| | | // (This data structure is used for media *receivers* - i.e., clients. |
| | | // For media streamers, use "ServerMediaSession" instead.) |
| | | // C++ header |
| | | |
| | | /* NOTE: To support receiving your own custom RTP payload format, you must first define a new |
| | | subclass of "MultiFramedRTPSource" (or "BasicUDPSource") that implements it. |
| | | Then define your own subclass of "MediaSession" and "MediaSubsession", as follows: |
| | | - In your subclass of "MediaSession" (named, for example, "myMediaSession"): |
| | | - Define and implement your own static member function |
| | | static myMediaSession* createNew(UsageEnvironment& env, char const* sdpDescription); |
| | | and call this - instead of "MediaSession::createNew()" - in your application, |
| | | when you create a new "MediaSession" object. |
| | | - Reimplement the "createNewMediaSubsession()" virtual function, as follows: |
| | | MediaSubsession* myMediaSession::createNewMediaSubsession() { return new myMediaSubsession(*this); } |
| | | - In your subclass of "MediaSubsession" (named, for example, "myMediaSubsession"): |
| | | - Reimplement the "createSourceObjects()" virtual function, perhaps similar to this: |
| | | Boolean myMediaSubsession::createSourceObjects(int useSpecialRTPoffset) { |
| | | if (strcmp(fCodecName, "X-MY-RTP-PAYLOAD-FORMAT") == 0) { |
| | | // This subsession uses our custom RTP payload format: |
| | | fReadSource = fRTPSource = myRTPPayloadFormatRTPSource::createNew( <parameters> ); |
| | | return True; |
| | | } else { |
| | | // This subsession uses some other RTP payload format - perhaps one that we already implement: |
| | | return ::createSourceObjects(useSpecialRTPoffset); |
| | | } |
| | | } |
| | | */ |
| | | |
| | | #ifndef _MEDIA_SESSION_HH |
| | | #define _MEDIA_SESSION_HH |
| | | |
| | | #ifndef _RTCP_HH |
| | | #include "RTCP.hh" |
| | | #endif |
| | | #ifndef _FRAMED_FILTER_HH |
| | | #include "FramedFilter.hh" |
| | | #endif |
| | | |
| | | class MediaSubsession; // forward |
| | | |
| | | class MediaSession: public Medium { |
| | | public: |
| | | static MediaSession* createNew(UsageEnvironment& env, |
| | | char const* sdpDescription); |
| | | |
| | | static Boolean lookupByName(UsageEnvironment& env, char const* sourceName, |
| | | MediaSession*& resultSession); |
| | | |
| | | Boolean hasSubsessions() const { return fSubsessionsHead != NULL; } |
| | | |
| | | char* connectionEndpointName() const { return fConnectionEndpointName; } |
| | | char const* CNAME() const { return fCNAME; } |
| | | struct in_addr const& sourceFilterAddr() const { return fSourceFilterAddr; } |
| | | float& scale() { return fScale; } |
| | | float& speed() { return fSpeed; } |
| | | char* mediaSessionType() const { return fMediaSessionType; } |
| | | char* sessionName() const { return fSessionName; } |
| | | char* sessionDescription() const { return fSessionDescription; } |
| | | char const* controlPath() const { return fControlPath; } |
| | | |
| | | double& playStartTime() { return fMaxPlayStartTime; } |
| | | double& playEndTime() { return fMaxPlayEndTime; } |
| | | char* absStartTime() const; |
| | | char* absEndTime() const; |
| | | // Used only to set the local fields: |
| | | char*& _absStartTime() { return fAbsStartTime; } |
| | | char*& _absEndTime() { return fAbsEndTime; } |
| | | |
| | | Boolean initiateByMediaType(char const* mimeType, |
| | | MediaSubsession*& resultSubsession, |
| | | int useSpecialRTPoffset = -1); |
| | | // Initiates the first subsession with the specified MIME type |
| | | // Returns the resulting subsession, or 'multi source' (not both) |
| | | |
| | | protected: // redefined virtual functions |
| | | virtual Boolean isMediaSession() const; |
| | | |
| | | protected: |
| | | MediaSession(UsageEnvironment& env); |
| | | // called only by createNew(); |
| | | virtual ~MediaSession(); |
| | | |
| | | virtual MediaSubsession* createNewMediaSubsession(); |
| | | |
| | | Boolean initializeWithSDP(char const* sdpDescription); |
| | | Boolean parseSDPLine(char const* input, char const*& nextLine); |
| | | Boolean parseSDPLine_s(char const* sdpLine); |
| | | Boolean parseSDPLine_i(char const* sdpLine); |
| | | Boolean parseSDPLine_c(char const* sdpLine); |
| | | Boolean parseSDPAttribute_type(char const* sdpLine); |
| | | Boolean parseSDPAttribute_control(char const* sdpLine); |
| | | Boolean parseSDPAttribute_range(char const* sdpLine); |
| | | Boolean parseSDPAttribute_source_filter(char const* sdpLine); |
| | | |
| | | static char* lookupPayloadFormat(unsigned char rtpPayloadType, |
| | | unsigned& rtpTimestampFrequency, |
| | | unsigned& numChannels); |
| | | static unsigned guessRTPTimestampFrequency(char const* mediumName, |
| | | char const* codecName); |
| | | |
| | | protected: |
| | | friend class MediaSubsessionIterator; |
| | | char* fCNAME; // used for RTCP |
| | | |
| | | // Linkage fields: |
| | | MediaSubsession* fSubsessionsHead; |
| | | MediaSubsession* fSubsessionsTail; |
| | | |
| | | // Fields set from a SDP description: |
| | | char* fConnectionEndpointName; |
| | | double fMaxPlayStartTime; |
| | | double fMaxPlayEndTime; |
| | | char* fAbsStartTime; |
| | | char* fAbsEndTime; |
| | | struct in_addr fSourceFilterAddr; // used for SSM |
| | | float fScale; // set from a RTSP "Scale:" header |
| | | float fSpeed; |
| | | char* fMediaSessionType; // holds a=type value |
| | | char* fSessionName; // holds s=<session name> value |
| | | char* fSessionDescription; // holds i=<session description> value |
| | | char* fControlPath; // holds optional a=control: string |
| | | }; |
| | | |
| | | |
| | | class MediaSubsessionIterator { |
| | | public: |
| | | MediaSubsessionIterator(MediaSession const& session); |
| | | virtual ~MediaSubsessionIterator(); |
| | | |
| | | MediaSubsession* next(); // NULL if none |
| | | void reset(); |
| | | |
| | | private: |
| | | MediaSession const& fOurSession; |
| | | MediaSubsession* fNextPtr; |
| | | }; |
| | | |
| | | |
| | | class MediaSubsession { |
| | | public: |
| | | MediaSession& parentSession() { return fParent; } |
| | | MediaSession const& parentSession() const { return fParent; } |
| | | |
| | | unsigned short clientPortNum() const { return fClientPortNum; } |
| | | unsigned char rtpPayloadFormat() const { return fRTPPayloadFormat; } |
| | | char const* savedSDPLines() const { return fSavedSDPLines; } |
| | | char const* mediumName() const { return fMediumName; } |
| | | char const* codecName() const { return fCodecName; } |
| | | char const* protocolName() const { return fProtocolName; } |
| | | char const* controlPath() const { return fControlPath; } |
| | | Boolean isSSM() const { return fSourceFilterAddr.s_addr != 0; } |
| | | |
| | | unsigned short videoWidth() const { return fVideoWidth; } |
| | | unsigned short videoHeight() const { return fVideoHeight; } |
| | | unsigned videoFPS() const { return fVideoFPS; } |
| | | unsigned numChannels() const { return fNumChannels; } |
| | | float& scale() { return fScale; } |
| | | float& speed() { return fSpeed; } |
| | | |
| | | RTPSource* rtpSource() { return fRTPSource; } |
| | | RTCPInstance* rtcpInstance() { return fRTCPInstance; } |
| | | unsigned rtpTimestampFrequency() const { return fRTPTimestampFrequency; } |
| | | Boolean rtcpIsMuxed() const { return fMultiplexRTCPWithRTP; } |
| | | FramedSource* readSource() { return fReadSource; } |
| | | // This is the source that client sinks read from. It is usually |
| | | // (but not necessarily) the same as "rtpSource()" |
| | | void addFilter(FramedFilter* filter); |
| | | // Changes "readSource()" to "filter" (which must have just been created with "readSource()" as its input) |
| | | |
| | | double playStartTime() const; |
| | | double playEndTime() const; |
| | | char* absStartTime() const; |
| | | char* absEndTime() const; |
| | | // Used only to set the local fields: |
| | | double& _playStartTime() { return fPlayStartTime; } |
| | | double& _playEndTime() { return fPlayEndTime; } |
| | | char*& _absStartTime() { return fAbsStartTime; } |
| | | char*& _absEndTime() { return fAbsEndTime; } |
| | | |
| | | Boolean initiate(int useSpecialRTPoffset = -1); |
| | | // Creates a "RTPSource" for this subsession. (Has no effect if it's |
| | | // already been created.) Returns True iff this succeeds. |
| | | void deInitiate(); // Destroys any previously created RTPSource |
| | | Boolean setClientPortNum(unsigned short portNum); |
| | | // Sets the preferred client port number that any "RTPSource" for |
| | | // this subsession would use. (By default, the client port number |
| | | // is gotten from the original SDP description, or - if the SDP |
| | | // description does not specfy a client port number - an ephemeral |
| | | // (even) port number is chosen.) This routine must *not* be |
| | | // called after initiate(). |
| | | void receiveRawMP3ADUs() { fReceiveRawMP3ADUs = True; } // optional hack for audio/MPA-ROBUST; must not be called after initiate() |
| | | void receiveRawJPEGFrames() { fReceiveRawJPEGFrames = True; } // optional hack for video/JPEG; must not be called after initiate() |
| | | char*& connectionEndpointName() { return fConnectionEndpointName; } |
| | | char const* connectionEndpointName() const { |
| | | return fConnectionEndpointName; |
| | | } |
| | | |
| | | // 'Bandwidth' parameter, set in the "b=" SDP line: |
| | | unsigned bandwidth() const { return fBandwidth; } |
| | | |
| | | // General SDP attribute accessor functions: |
| | | char const* attrVal_str(char const* attrName) const; |
| | | // returns "" if attribute doesn't exist (and has no default value), or is not a string |
| | | char const* attrVal_strToLower(char const* attrName) const; |
| | | // returns "" if attribute doesn't exist (and has no default value), or is not a string |
| | | unsigned attrVal_int(char const* attrName) const; |
| | | // also returns 0 if attribute doesn't exist (and has no default value) |
| | | unsigned attrVal_unsigned(char const* attrName) const { return (unsigned)attrVal_int(attrName); } |
| | | Boolean attrVal_bool(char const* attrName) const { return attrVal_int(attrName) != 0; } |
| | | |
| | | // Old, now-deprecated SDP attribute accessor functions, kept here for backwards-compatibility: |
| | | char const* fmtp_config() const; |
| | | char const* fmtp_configuration() const { return fmtp_config(); } |
| | | char const* fmtp_spropparametersets() const { return attrVal_str("sprop-parameter-sets"); } |
| | | char const* fmtp_spropvps() const { return attrVal_str("sprop-vps"); } |
| | | char const* fmtp_spropsps() const { return attrVal_str("sprop-sps"); } |
| | | char const* fmtp_sproppps() const { return attrVal_str("sprop-pps"); } |
| | | |
| | | netAddressBits connectionEndpointAddress() const; |
| | | // Converts "fConnectionEndpointName" to an address (or 0 if unknown) |
| | | void setDestinations(netAddressBits defaultDestAddress); |
| | | // Uses "fConnectionEndpointName" and "serverPortNum" to set |
| | | // the destination address and port of the RTP and RTCP objects. |
| | | // This is typically called by RTSP clients after doing "SETUP". |
| | | |
| | | char const* sessionId() const { return fSessionId; } |
| | | void setSessionId(char const* sessionId); |
| | | |
| | | // Public fields that external callers can use to keep state. |
| | | // (They are responsible for all storage management on these fields) |
| | | unsigned short serverPortNum; // in host byte order (used by RTSP) |
| | | unsigned char rtpChannelId, rtcpChannelId; // used by RTSP (for RTP/TCP) |
| | | MediaSink* sink; // callers can use this to keep track of who's playing us |
| | | void* miscPtr; // callers can use this for whatever they want |
| | | |
| | | // Parameters set from a RTSP "RTP-Info:" header: |
| | | struct { |
| | | u_int16_t seqNum; |
| | | u_int32_t timestamp; |
| | | Boolean infoIsNew; // not part of the RTSP header; instead, set whenever this struct is filled in |
| | | } rtpInfo; |
| | | |
| | | double getNormalPlayTime(struct timeval const& presentationTime); |
| | | // Computes the stream's "Normal Play Time" (NPT) from the given "presentationTime". |
| | | // (For the definition of "Normal Play Time", see RFC 2326, section 3.6.) |
| | | // This function is useful only if the "rtpInfo" structure was previously filled in |
| | | // (e.g., by a "RTP-Info:" header in a RTSP response). |
| | | // Also, for this function to work properly, the RTP stream's presentation times must (eventually) be |
| | | // synchronized via RTCP. |
| | | // (Note: If this function returns a negative number, then the result should be ignored by the caller.) |
| | | |
| | | protected: |
| | | friend class MediaSession; |
| | | friend class MediaSubsessionIterator; |
| | | MediaSubsession(MediaSession& parent); |
| | | virtual ~MediaSubsession(); |
| | | |
| | | UsageEnvironment& env() { return fParent.envir(); } |
| | | void setNext(MediaSubsession* next) { fNext = next; } |
| | | |
| | | void setAttribute(char const* name, char const* value = NULL, Boolean valueIsHexadecimal = False); |
| | | |
| | | Boolean parseSDPLine_c(char const* sdpLine); |
| | | Boolean parseSDPLine_b(char const* sdpLine); |
| | | Boolean parseSDPAttribute_rtpmap(char const* sdpLine); |
| | | Boolean parseSDPAttribute_rtcpmux(char const* sdpLine); |
| | | Boolean parseSDPAttribute_control(char const* sdpLine); |
| | | Boolean parseSDPAttribute_range(char const* sdpLine); |
| | | Boolean parseSDPAttribute_fmtp(char const* sdpLine); |
| | | Boolean parseSDPAttribute_source_filter(char const* sdpLine); |
| | | Boolean parseSDPAttribute_x_dimensions(char const* sdpLine); |
| | | Boolean parseSDPAttribute_framerate(char const* sdpLine); |
| | | |
| | | virtual Boolean createSourceObjects(int useSpecialRTPoffset); |
| | | // create "fRTPSource" and "fReadSource" member objects, after we've been initialized via SDP |
| | | |
| | | protected: |
| | | // Linkage fields: |
| | | MediaSession& fParent; |
| | | MediaSubsession* fNext; |
| | | |
| | | // Fields set from a SDP description: |
| | | char* fConnectionEndpointName; // may also be set by RTSP SETUP response |
| | | unsigned short fClientPortNum; // in host byte order |
| | | // This field is also set by initiate() |
| | | unsigned char fRTPPayloadFormat; |
| | | char* fSavedSDPLines; |
| | | char* fMediumName; |
| | | char* fCodecName; |
| | | char* fProtocolName; |
| | | unsigned fRTPTimestampFrequency; |
| | | Boolean fMultiplexRTCPWithRTP; |
| | | char* fControlPath; // holds optional a=control: string |
| | | struct in_addr fSourceFilterAddr; // used for SSM |
| | | unsigned fBandwidth; // in kilobits-per-second, from b= line |
| | | |
| | | double fPlayStartTime; |
| | | double fPlayEndTime; |
| | | char* fAbsStartTime; |
| | | char* fAbsEndTime; |
| | | unsigned short fVideoWidth, fVideoHeight; |
| | | // screen dimensions (set by an optional a=x-dimensions: <w>,<h> line) |
| | | unsigned fVideoFPS; |
| | | // frame rate (set by an optional "a=framerate: <fps>" or "a=x-framerate: <fps>" line) |
| | | unsigned fNumChannels; |
| | | // optionally set by "a=rtpmap:" lines for audio sessions. Default: 1 |
| | | float fScale; // set from a RTSP "Scale:" header |
| | | float fSpeed; |
| | | double fNPT_PTS_Offset; // set by "getNormalPlayTime()"; add this to a PTS to get NPT |
| | | HashTable* fAttributeTable; // for "a=fmtp:" attributes. (Later an array by payload type #####) |
| | | |
| | | // Fields set or used by initiate(): |
| | | Groupsock* fRTPSocket; Groupsock* fRTCPSocket; // works even for unicast |
| | | RTPSource* fRTPSource; RTCPInstance* fRTCPInstance; |
| | | FramedSource* fReadSource; |
| | | Boolean fReceiveRawMP3ADUs, fReceiveRawJPEGFrames; |
| | | |
| | | // Other fields: |
| | | char* fSessionId; // used by RTSP |
| | | }; |
| | | |
| | | #endif |