| 1 | /********** |
| 2 | This library is free software; you can redistribute it and/or modify it under |
| 3 | the terms of the GNU Lesser General Public License as published by the |
| 4 | Free Software Foundation; either version 3 of the License, or (at your |
| 5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| 6 | |
| 7 | This library is distributed in the hope that it will be useful, but WITHOUT |
| 8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| 10 | more details. |
| 11 | |
| 12 | You should have received a copy of the GNU Lesser General Public License |
| 13 | along with this library; if not, write to the Free Software Foundation, Inc., |
| 14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 15 | **********/ |
| 16 | // "liveMedia" |
| 17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
| 18 | // A data structure that represents a session that consists of |
| 19 | // potentially multiple (audio and/or video) sub-sessions |
| 20 | // (This data structure is used for media *receivers* - i.e., clients. |
| 21 | // For media streamers, use "ServerMediaSession" instead.) |
| 22 | // C++ header |
| 23 | |
| 24 | /* NOTE: To support receiving your own custom RTP payload format, you must first define a new |
| 25 | subclass of "MultiFramedRTPSource" (or "BasicUDPSource") that implements it. |
| 26 | Then define your own subclass of "MediaSession" and "MediaSubsession", as follows: |
| 27 | - In your subclass of "MediaSession" (named, for example, "myMediaSession"): |
| 28 | - Define and implement your own static member function |
| 29 | static myMediaSession* createNew(UsageEnvironment& env, char const* sdpDescription); |
| 30 | and call this - instead of "MediaSession::createNew()" - in your application, |
| 31 | when you create a new "MediaSession" object. |
| 32 | - Reimplement the "createNewMediaSubsession()" virtual function, as follows: |
| 33 | MediaSubsession* myMediaSession::createNewMediaSubsession() { return new myMediaSubsession(*this); } |
| 34 | - In your subclass of "MediaSubsession" (named, for example, "myMediaSubsession"): |
| 35 | - Reimplement the "createSourceObjects()" virtual function, perhaps similar to this: |
| 36 | Boolean myMediaSubsession::createSourceObjects(int useSpecialRTPoffset) { |
| 37 | if (strcmp(fCodecName, "X-MY-RTP-PAYLOAD-FORMAT") == 0) { |
| 38 | // This subsession uses our custom RTP payload format: |
| 39 | fReadSource = fRTPSource = myRTPPayloadFormatRTPSource::createNew( <parameters> ); |
| 40 | return True; |
| 41 | } else { |
| 42 | // This subsession uses some other RTP payload format - perhaps one that we already implement: |
| 43 | return ::createSourceObjects(useSpecialRTPoffset); |
| 44 | } |
| 45 | } |
| 46 | */ |
| 47 | |
| 48 | #ifndef _MEDIA_SESSION_HH |
| 49 | #define _MEDIA_SESSION_HH |
| 50 | |
| 51 | #ifndef _RTCP_HH |
| 52 | #include "RTCP.hh" |
| 53 | #endif |
| 54 | #ifndef _FRAMED_FILTER_HH |
| 55 | #include "FramedFilter.hh" |
| 56 | #endif |
| 57 | |
| 58 | class MediaSubsession; // forward |
| 59 | |
| 60 | class MediaSession: public Medium { |
| 61 | public: |
| 62 | static MediaSession* createNew(UsageEnvironment& env, |
| 63 | char const* sdpDescription); |
| 64 | |
| 65 | static Boolean lookupByName(UsageEnvironment& env, char const* sourceName, |
| 66 | MediaSession*& resultSession); |
| 67 | |
| 68 | Boolean hasSubsessions() const { return fSubsessionsHead != NULL; } |
| 69 | |
| 70 | char* connectionEndpointName() const { return fConnectionEndpointName; } |
| 71 | char const* CNAME() const { return fCNAME; } |
| 72 | struct in_addr const& sourceFilterAddr() const { return fSourceFilterAddr; } |
| 73 | float& scale() { return fScale; } |
| 74 | float& speed() { return fSpeed; } |
| 75 | char* mediaSessionType() const { return fMediaSessionType; } |
| 76 | char* sessionName() const { return fSessionName; } |
| 77 | char* sessionDescription() const { return fSessionDescription; } |
| 78 | char const* controlPath() const { return fControlPath; } |
| 79 | |
| 80 | double& playStartTime() { return fMaxPlayStartTime; } |
| 81 | double& playEndTime() { return fMaxPlayEndTime; } |
| 82 | char* absStartTime() const; |
| 83 | char* absEndTime() const; |
| 84 | // Used only to set the local fields: |
| 85 | char*& _absStartTime() { return fAbsStartTime; } |
| 86 | char*& _absEndTime() { return fAbsEndTime; } |
| 87 | |
| 88 | Boolean initiateByMediaType(char const* mimeType, |
| 89 | MediaSubsession*& resultSubsession, |
| 90 | int useSpecialRTPoffset = -1); |
| 91 | // Initiates the first subsession with the specified MIME type |
| 92 | // Returns the resulting subsession, or 'multi source' (not both) |
| 93 | |
| 94 | protected: // redefined virtual functions |
| 95 | virtual Boolean isMediaSession() const; |
| 96 | |
| 97 | protected: |
| 98 | MediaSession(UsageEnvironment& env); |
| 99 | // called only by createNew(); |
| 100 | virtual ~MediaSession(); |
| 101 | |
| 102 | virtual MediaSubsession* createNewMediaSubsession(); |
| 103 | |
| 104 | Boolean initializeWithSDP(char const* sdpDescription); |
| 105 | Boolean parseSDPLine(char const* input, char const*& nextLine); |
| 106 | Boolean parseSDPLine_s(char const* sdpLine); |
| 107 | Boolean parseSDPLine_i(char const* sdpLine); |
| 108 | Boolean parseSDPLine_c(char const* sdpLine); |
| 109 | Boolean parseSDPAttribute_type(char const* sdpLine); |
| 110 | Boolean parseSDPAttribute_control(char const* sdpLine); |
| 111 | Boolean parseSDPAttribute_range(char const* sdpLine); |
| 112 | Boolean parseSDPAttribute_source_filter(char const* sdpLine); |
| 113 | |
| 114 | static char* lookupPayloadFormat(unsigned char rtpPayloadType, |
| 115 | unsigned& rtpTimestampFrequency, |
| 116 | unsigned& numChannels); |
| 117 | static unsigned guessRTPTimestampFrequency(char const* mediumName, |
| 118 | char const* codecName); |
| 119 | |
| 120 | protected: |
| 121 | friend class MediaSubsessionIterator; |
| 122 | char* fCNAME; // used for RTCP |
| 123 | |
| 124 | // Linkage fields: |
| 125 | MediaSubsession* fSubsessionsHead; |
| 126 | MediaSubsession* fSubsessionsTail; |
| 127 | |
| 128 | // Fields set from a SDP description: |
| 129 | char* fConnectionEndpointName; |
| 130 | double fMaxPlayStartTime; |
| 131 | double fMaxPlayEndTime; |
| 132 | char* fAbsStartTime; |
| 133 | char* fAbsEndTime; |
| 134 | struct in_addr fSourceFilterAddr; // used for SSM |
| 135 | float fScale; // set from a RTSP "Scale:" header |
| 136 | float fSpeed; |
| 137 | char* fMediaSessionType; // holds a=type value |
| 138 | char* fSessionName; // holds s=<session name> value |
| 139 | char* fSessionDescription; // holds i=<session description> value |
| 140 | char* fControlPath; // holds optional a=control: string |
| 141 | }; |
| 142 | |
| 143 | |
| 144 | class MediaSubsessionIterator { |
| 145 | public: |
| 146 | MediaSubsessionIterator(MediaSession const& session); |
| 147 | virtual ~MediaSubsessionIterator(); |
| 148 | |
| 149 | MediaSubsession* next(); // NULL if none |
| 150 | void reset(); |
| 151 | |
| 152 | private: |
| 153 | MediaSession const& fOurSession; |
| 154 | MediaSubsession* fNextPtr; |
| 155 | }; |
| 156 | |
| 157 | |
| 158 | class MediaSubsession { |
| 159 | public: |
| 160 | MediaSession& parentSession() { return fParent; } |
| 161 | MediaSession const& parentSession() const { return fParent; } |
| 162 | |
| 163 | unsigned short clientPortNum() const { return fClientPortNum; } |
| 164 | unsigned char rtpPayloadFormat() const { return fRTPPayloadFormat; } |
| 165 | char const* savedSDPLines() const { return fSavedSDPLines; } |
| 166 | char const* mediumName() const { return fMediumName; } |
| 167 | char const* codecName() const { return fCodecName; } |
| 168 | char const* protocolName() const { return fProtocolName; } |
| 169 | char const* controlPath() const { return fControlPath; } |
| 170 | Boolean isSSM() const { return fSourceFilterAddr.s_addr != 0; } |
| 171 | |
| 172 | unsigned short videoWidth() const { return fVideoWidth; } |
| 173 | unsigned short videoHeight() const { return fVideoHeight; } |
| 174 | unsigned videoFPS() const { return fVideoFPS; } |
| 175 | unsigned numChannels() const { return fNumChannels; } |
| 176 | float& scale() { return fScale; } |
| 177 | float& speed() { return fSpeed; } |
| 178 | |
| 179 | RTPSource* rtpSource() { return fRTPSource; } |
| 180 | RTCPInstance* rtcpInstance() { return fRTCPInstance; } |
| 181 | unsigned rtpTimestampFrequency() const { return fRTPTimestampFrequency; } |
| 182 | Boolean rtcpIsMuxed() const { return fMultiplexRTCPWithRTP; } |
| 183 | FramedSource* readSource() { return fReadSource; } |
| 184 | // This is the source that client sinks read from. It is usually |
| 185 | // (but not necessarily) the same as "rtpSource()" |
| 186 | void addFilter(FramedFilter* filter); |
| 187 | // Changes "readSource()" to "filter" (which must have just been created with "readSource()" as its input) |
| 188 | |
| 189 | double playStartTime() const; |
| 190 | double playEndTime() const; |
| 191 | char* absStartTime() const; |
| 192 | char* absEndTime() const; |
| 193 | // Used only to set the local fields: |
| 194 | double& _playStartTime() { return fPlayStartTime; } |
| 195 | double& _playEndTime() { return fPlayEndTime; } |
| 196 | char*& _absStartTime() { return fAbsStartTime; } |
| 197 | char*& _absEndTime() { return fAbsEndTime; } |
| 198 | |
| 199 | Boolean initiate(int useSpecialRTPoffset = -1); |
| 200 | // Creates a "RTPSource" for this subsession. (Has no effect if it's |
| 201 | // already been created.) Returns True iff this succeeds. |
| 202 | void deInitiate(); // Destroys any previously created RTPSource |
| 203 | Boolean setClientPortNum(unsigned short portNum); |
| 204 | // Sets the preferred client port number that any "RTPSource" for |
| 205 | // this subsession would use. (By default, the client port number |
| 206 | // is gotten from the original SDP description, or - if the SDP |
| 207 | // description does not specfy a client port number - an ephemeral |
| 208 | // (even) port number is chosen.) This routine must *not* be |
| 209 | // called after initiate(). |
| 210 | void receiveRawMP3ADUs() { fReceiveRawMP3ADUs = True; } // optional hack for audio/MPA-ROBUST; must not be called after initiate() |
| 211 | void receiveRawJPEGFrames() { fReceiveRawJPEGFrames = True; } // optional hack for video/JPEG; must not be called after initiate() |
| 212 | char*& connectionEndpointName() { return fConnectionEndpointName; } |
| 213 | char const* connectionEndpointName() const { |
| 214 | return fConnectionEndpointName; |
| 215 | } |
| 216 | |
| 217 | // 'Bandwidth' parameter, set in the "b=" SDP line: |
| 218 | unsigned bandwidth() const { return fBandwidth; } |
| 219 | |
| 220 | // General SDP attribute accessor functions: |
| 221 | char const* attrVal_str(char const* attrName) const; |
| 222 | // returns "" if attribute doesn't exist (and has no default value), or is not a string |
| 223 | char const* attrVal_strToLower(char const* attrName) const; |
| 224 | // returns "" if attribute doesn't exist (and has no default value), or is not a string |
| 225 | unsigned attrVal_int(char const* attrName) const; |
| 226 | // also returns 0 if attribute doesn't exist (and has no default value) |
| 227 | unsigned attrVal_unsigned(char const* attrName) const { return (unsigned)attrVal_int(attrName); } |
| 228 | Boolean attrVal_bool(char const* attrName) const { return attrVal_int(attrName) != 0; } |
| 229 | |
| 230 | // Old, now-deprecated SDP attribute accessor functions, kept here for backwards-compatibility: |
| 231 | char const* fmtp_config() const; |
| 232 | char const* fmtp_configuration() const { return fmtp_config(); } |
| 233 | char const* fmtp_spropparametersets() const { return attrVal_str("sprop-parameter-sets" ); } |
| 234 | char const* fmtp_spropvps() const { return attrVal_str("sprop-vps" ); } |
| 235 | char const* fmtp_spropsps() const { return attrVal_str("sprop-sps" ); } |
| 236 | char const* fmtp_sproppps() const { return attrVal_str("sprop-pps" ); } |
| 237 | |
| 238 | netAddressBits connectionEndpointAddress() const; |
| 239 | // Converts "fConnectionEndpointName" to an address (or 0 if unknown) |
| 240 | void setDestinations(netAddressBits defaultDestAddress); |
| 241 | // Uses "fConnectionEndpointName" and "serverPortNum" to set |
| 242 | // the destination address and port of the RTP and RTCP objects. |
| 243 | // This is typically called by RTSP clients after doing "SETUP". |
| 244 | |
| 245 | char const* sessionId() const { return fSessionId; } |
| 246 | void setSessionId(char const* sessionId); |
| 247 | |
| 248 | // Public fields that external callers can use to keep state. |
| 249 | // (They are responsible for all storage management on these fields) |
| 250 | unsigned short serverPortNum; // in host byte order (used by RTSP) |
| 251 | unsigned char rtpChannelId, rtcpChannelId; // used by RTSP (for RTP/TCP) |
| 252 | MediaSink* sink; // callers can use this to keep track of who's playing us |
| 253 | void* miscPtr; // callers can use this for whatever they want |
| 254 | |
| 255 | // Parameters set from a RTSP "RTP-Info:" header: |
| 256 | struct { |
| 257 | u_int16_t seqNum; |
| 258 | u_int32_t timestamp; |
| 259 | Boolean infoIsNew; // not part of the RTSP header; instead, set whenever this struct is filled in |
| 260 | } rtpInfo; |
| 261 | |
| 262 | double getNormalPlayTime(struct timeval const& presentationTime); |
| 263 | // Computes the stream's "Normal Play Time" (NPT) from the given "presentationTime". |
| 264 | // (For the definition of "Normal Play Time", see RFC 2326, section 3.6.) |
| 265 | // This function is useful only if the "rtpInfo" structure was previously filled in |
| 266 | // (e.g., by a "RTP-Info:" header in a RTSP response). |
| 267 | // Also, for this function to work properly, the RTP stream's presentation times must (eventually) be |
| 268 | // synchronized via RTCP. |
| 269 | // (Note: If this function returns a negative number, then the result should be ignored by the caller.) |
| 270 | |
| 271 | protected: |
| 272 | friend class MediaSession; |
| 273 | friend class MediaSubsessionIterator; |
| 274 | MediaSubsession(MediaSession& parent); |
| 275 | virtual ~MediaSubsession(); |
| 276 | |
| 277 | UsageEnvironment& env() { return fParent.envir(); } |
| 278 | void setNext(MediaSubsession* next) { fNext = next; } |
| 279 | |
| 280 | void setAttribute(char const* name, char const* value = NULL, Boolean valueIsHexadecimal = False); |
| 281 | |
| 282 | Boolean parseSDPLine_c(char const* sdpLine); |
| 283 | Boolean parseSDPLine_b(char const* sdpLine); |
| 284 | Boolean parseSDPAttribute_rtpmap(char const* sdpLine); |
| 285 | Boolean parseSDPAttribute_rtcpmux(char const* sdpLine); |
| 286 | Boolean parseSDPAttribute_control(char const* sdpLine); |
| 287 | Boolean parseSDPAttribute_range(char const* sdpLine); |
| 288 | Boolean parseSDPAttribute_fmtp(char const* sdpLine); |
| 289 | Boolean parseSDPAttribute_source_filter(char const* sdpLine); |
| 290 | Boolean parseSDPAttribute_x_dimensions(char const* sdpLine); |
| 291 | Boolean parseSDPAttribute_framerate(char const* sdpLine); |
| 292 | |
| 293 | virtual Boolean createSourceObjects(int useSpecialRTPoffset); |
| 294 | // create "fRTPSource" and "fReadSource" member objects, after we've been initialized via SDP |
| 295 | |
| 296 | protected: |
| 297 | // Linkage fields: |
| 298 | MediaSession& fParent; |
| 299 | MediaSubsession* fNext; |
| 300 | |
| 301 | // Fields set from a SDP description: |
| 302 | char* fConnectionEndpointName; // may also be set by RTSP SETUP response |
| 303 | unsigned short fClientPortNum; // in host byte order |
| 304 | // This field is also set by initiate() |
| 305 | unsigned char fRTPPayloadFormat; |
| 306 | char* fSavedSDPLines; |
| 307 | char* fMediumName; |
| 308 | char* fCodecName; |
| 309 | char* fProtocolName; |
| 310 | unsigned fRTPTimestampFrequency; |
| 311 | Boolean fMultiplexRTCPWithRTP; |
| 312 | char* fControlPath; // holds optional a=control: string |
| 313 | struct in_addr fSourceFilterAddr; // used for SSM |
| 314 | unsigned fBandwidth; // in kilobits-per-second, from b= line |
| 315 | |
| 316 | double fPlayStartTime; |
| 317 | double fPlayEndTime; |
| 318 | char* fAbsStartTime; |
| 319 | char* fAbsEndTime; |
| 320 | unsigned short fVideoWidth, fVideoHeight; |
| 321 | // screen dimensions (set by an optional a=x-dimensions: <w>,<h> line) |
| 322 | unsigned fVideoFPS; |
| 323 | // frame rate (set by an optional "a=framerate: <fps>" or "a=x-framerate: <fps>" line) |
| 324 | unsigned fNumChannels; |
| 325 | // optionally set by "a=rtpmap:" lines for audio sessions. Default: 1 |
| 326 | float fScale; // set from a RTSP "Scale:" header |
| 327 | float fSpeed; |
| 328 | double fNPT_PTS_Offset; // set by "getNormalPlayTime()"; add this to a PTS to get NPT |
| 329 | HashTable* fAttributeTable; // for "a=fmtp:" attributes. (Later an array by payload type #####) |
| 330 | |
| 331 | // Fields set or used by initiate(): |
| 332 | Groupsock* fRTPSocket; Groupsock* fRTCPSocket; // works even for unicast |
| 333 | RTPSource* fRTPSource; RTCPInstance* fRTCPInstance; |
| 334 | FramedSource* fReadSource; |
| 335 | Boolean fReceiveRawMP3ADUs, fReceiveRawJPEGFrames; |
| 336 | |
| 337 | // Other fields: |
| 338 | char* fSessionId; // used by RTSP |
| 339 | }; |
| 340 | |
| 341 | #endif |
| 342 | |