diff --git a/include/rtc/description.hpp b/include/rtc/description.hpp index 7e6e5eb..49ab1d3 100644 --- a/include/rtc/description.hpp +++ b/include/rtc/description.hpp @@ -150,6 +150,7 @@ public: optional msid = nullopt, optional trackID = nullopt); bool hasSSRC(uint32_t ssrc); std::vector getSSRCs(); + std::optional getCNameForSsrc(uint32_t ssrc); void setBitrate(int bitrate); int getBitrate() const; @@ -195,6 +196,7 @@ public: std::map mRtpMap; std::vector mSsrcs; + std::map mCNameMap; public: void addRTPMap(const RTPMap &map); diff --git a/include/rtc/rtc.h b/include/rtc/rtc.h index c8b8856..8a9e80c 100644 --- a/include/rtc/rtc.h +++ b/include/rtc/rtc.h @@ -206,6 +206,7 @@ RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb); RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id RTC_EXPORT int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit *init); // returns dc id +RTC_EXPORT int rtcIsOpen(int dc); RTC_EXPORT int rtcDeleteDataChannel(int dc); RTC_EXPORT int rtcGetDataChannelStream(int dc); @@ -254,6 +255,13 @@ typedef struct { uint32_t timestamp; // Start timestamp } rtcStartTime; +typedef struct { + uint32_t ssrc; + const char *name; // optional + const char *msid; // optional + const char *trackId; // optional, track ID used in MSID +} rtcSsrcForTypeInit; + // Set H264PacketizationHandler for track RTC_EXPORT int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init); @@ -298,6 +306,22 @@ RTC_EXPORT int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t *timest // Set NeedsToReport flag in RtcpSrReporter handler identified by given track id RTC_EXPORT int rtcSetNeedsToSendRtcpSr(int id); +/// Get all available payload types for given codec and stores them in buffer, does nothing if buffer is NULL +int rtcGetTrackPayloadTypesForCodec(int tr, const char * ccodec, int * buffer, int size); + +/// Get all SSRCs for given track +int rtcGetSsrcsForTrack(int tr, uint32_t * buffer, int count); + +/// Get CName for SSRC +int rtcGetCNameForSsrc(int tr, uint32_t ssrc, char * cname, int cnameSize); + +/// Get all SSRCs for given media type in given SDP +/// @param mediaType Media type (audio/video) +int rtcGetSsrcsForType(const char * mediaType, const char * sdp, uint32_t * buffer, int bufferSize); + +/// Set SSRC for given media type in given SDP +int rtcSetSsrcForType(const char * mediaType, const char * sdp, char * buffer, const int bufferSize, rtcSsrcForTypeInit * init); + #endif // RTC_ENABLE_MEDIA #if RTC_ENABLE_WEBSOCKET diff --git a/src/capi.cpp b/src/capi.cpp index 194901f..f1e08ec 100644 --- a/src/capi.cpp +++ b/src/capi.cpp @@ -268,6 +268,24 @@ int copyAndReturn(binary b, char *buffer, int size) { return int(b.size()); } +template +int copyAndReturn(std::vector b, T *buffer, int size) { + if (!buffer) + return int(b.size()); + + if (size < int(b.size())) + return RTC_ERR_TOO_SMALL; + std::copy(b.begin(), b.end(), buffer); + return int(b.size()); +} + +#if RTC_ENABLE_MEDIA +// function is used in RTC_ENABLE_MEDIA only +string lowercased(string str) { + std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); }); + return str; +} +#endif // RTC_ENABLE_MEDIA } // namespace void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) { @@ -365,6 +383,12 @@ int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit * }); } +int rtcIsOpen(int cid) { + return wrap([cid] { + return getChannel(cid)->isOpen(); + }); +} + int rtcDeleteDataChannel(int dc) { return wrap([dc] { auto dataChannel = getDataChannel(dc); @@ -503,6 +527,26 @@ int rtcGetTrackDescription(int tr, char *buffer, int size) { #if RTC_ENABLE_MEDIA +void setSSRC(Description::Media *description, uint32_t ssrc, const char *_name, const char *_msid, const char *_trackID) { + + optional name = nullopt; + if (_name) { + name = string(_name); + } + + optional msid = nullopt; + if (_msid) { + msid = string(_msid); + } + + optional trackID = nullopt; + if (_trackID) { + trackID = string(_trackID); + } + + description->addSSRC(ssrc, name, msid, trackID); +} + int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *init) { return wrap([&] { auto track = getTrack(tr); @@ -633,6 +677,85 @@ int rtcSetNeedsToSendRtcpSr(int id) { }); } +int rtcGetTrackPayloadTypesForCodec(int tr, const char * ccodec, int * buffer, int size) { + return wrap([&] { + auto track = getTrack(tr); + auto codec = lowercased(string(ccodec)); + auto description = track->description(); + std::vector payloadTypes{}; + payloadTypes.reserve(std::max(size, 0)); + for (auto it = description.beginMaps(); it != description.endMaps(); it++) { + auto element = *it; + if (lowercased(element.second.format) == codec) { + payloadTypes.push_back(element.first); + } + } + return copyAndReturn(payloadTypes, buffer, size); + }); +} + +int rtcGetSsrcsForTrack(int tr, uint32_t * buffer, int count) { + return wrap([&] { + auto track = getTrack(tr); + auto ssrcs = track->description().getSSRCs(); + return copyAndReturn(ssrcs, buffer, count); + }); +} + +int rtcGetCNameForSsrc(int tr, uint32_t ssrc, char * cname, int cnameSize) { + return wrap([&] { + auto track = getTrack(tr); + auto description = track->description(); + auto optCName = description.getCNameForSsrc(ssrc); + if (optCName.has_value()) { + return copyAndReturn(optCName.value(), cname, cnameSize); + } else { + return 0; + } + }); +} + +int rtcGetSsrcsForType(const char * mediaType, const char * sdp, uint32_t * buffer, int bufferSize) { + return wrap([&] { + auto type = lowercased(string(mediaType)); + auto oldSDP = string(sdp); + auto description = Description(oldSDP, "unspec"); + auto mediaCount = description.mediaCount(); + for (unsigned int i = 0; i < mediaCount; i++) { + if (std::holds_alternative(description.media(i))) { + auto media = std::get(description.media(i)); + auto currentMediaType = lowercased(media->type()); + if (currentMediaType == type) { + auto ssrcs = media->getSSRCs(); + return copyAndReturn(ssrcs, buffer, bufferSize); + } + } + } + return 0; + }); +} + +int rtcSetSsrcForType(const char * mediaType, const char * sdp, char * buffer, const int bufferSize, + rtcSsrcForTypeInit * init) { + return wrap([&] { + auto type = lowercased(string(mediaType)); + auto prevSDP = string(sdp); + auto description = Description(prevSDP, "unspec"); + auto mediaCount = description.mediaCount(); + for (unsigned int i = 0; i < mediaCount; i++) { + if (std::holds_alternative(description.media(i))) { + auto media = std::get(description.media(i)); + auto currentMediaType = lowercased(media->type()); + if (currentMediaType == type) { + setSSRC(media, init->ssrc, init->name, init->msid, init->trackId); + break; + } + } + } + return copyAndReturn(string(description), buffer, bufferSize); + }); +} + #endif // RTC_ENABLE_MEDIA #if RTC_ENABLE_WEBSOCKET diff --git a/src/description.cpp b/src/description.cpp index e7d28b7..8f2d6d9 100644 --- a/src/description.cpp +++ b/src/description.cpp @@ -556,10 +556,12 @@ Description::Entry::removeAttribute(std::vector::iterator it) { void Description::Media::addSSRC(uint32_t ssrc, optional name, optional msid, optional trackID) { - if (name) + if (name) { mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name); - else + mCNameMap.emplace(ssrc, *name); + } else { mAttributes.emplace_back("ssrc:" + std::to_string(ssrc)); + } if (msid) mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " + @@ -858,7 +860,16 @@ void Description::Media::parseSdpLine(string_view line) { } else if (key == "rtcp-mux") { // always added } else if (key == "ssrc") { - mSsrcs.emplace_back(to_integer(value)); + auto ssrc = to_integer(value); + if (!hasSSRC(ssrc)) { + mSsrcs.emplace_back(ssrc); + } + auto cnamePos = value.find("cname:"); + if (cnamePos != std::string::npos) { + auto cname = value.substr(cnamePos + 6); + mCNameMap.emplace(ssrc, cname); + } + mAttributes.emplace_back(attr); } else { Entry::parseSdpLine(line); } @@ -875,6 +886,14 @@ void Description::Media::addRTPMap(const Description::Media::RTPMap &map) { std::vector Description::Media::getSSRCs() { return mSsrcs; } +std::optional Description::Media::getCNameForSsrc(uint32_t ssrc) { + auto it = mCNameMap.find(ssrc); + if (it != mCNameMap.end()) { + return it->second; + } + return std::nullopt; +} + std::map::iterator Description::Media::beginMaps() { return mRtpMap.begin(); }