diff --git a/include/rtc/description.hpp b/include/rtc/description.hpp index 7e6e5eb..ed649ef 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; diff --git a/include/rtc/rtc.h b/include/rtc/rtc.h index c8b8856..ac12e1d 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 rtcIsDataChannelOpen(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 bufferSize); + +/// 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..aaa64c7 100644 --- a/src/capi.cpp +++ b/src/capi.cpp @@ -268,6 +268,21 @@ 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; + memcpy(buffer, b.data(), size * sizeof(*buffer)); + return int(b.size()); +} + +string lowercased(string str) { + std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); }); + return str; +} } // namespace void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) { @@ -365,6 +380,12 @@ int rtcCreateDataChannelEx(int pc, const char *label, const rtcDataChannelInit * }); } +int rtcIsDataChannelOpen(int dc) { + return wrap([dc] { + return getDataChannel(dc)->isOpen() ? RTC_ERR_SUCCESS : RTC_ERR_FAILURE; + }); +} + int rtcDeleteDataChannel(int dc) { return wrap([dc] { auto dataChannel = getDataChannel(dc); @@ -380,6 +401,34 @@ int rtcDeleteDataChannel(int dc) { }); } +int rtcIsDataChannelOpen(int dc) { + return wrap([dc] { + return getDataChannel(dc)->isOpen() ? RTC_ERR_SUCCESS : RTC_ERR_FAILURE; + }); +} + +#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 rtcAddTrack(int pc, const char *mediaDescriptionSdp) { return wrap([&] { if (!mediaDescriptionSdp) @@ -503,6 +552,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 +702,23 @@ 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); + }); +} + #endif // RTC_ENABLE_MEDIA #if RTC_ENABLE_WEBSOCKET @@ -784,6 +870,68 @@ int rtcSetLocalDescription(int pc, const char *type) { }); } +int rtcGetSsrcsForTrack(int tr, uint32_t * buffer, int bufferSize) { + return wrap([&] { + auto track = getTrack(tr); + auto ssrcs = track->description().getSSRCs(); + return copyAndReturn(ssrcs, buffer, bufferSize); + }); +} + +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 (auto 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 (auto 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); + }); +} + int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) { return wrap([&] { auto peerConnection = getPeerConnection(pc); diff --git a/src/description.cpp b/src/description.cpp index e7d28b7..fd08ced 100644 --- a/src/description.cpp +++ b/src/description.cpp @@ -858,7 +858,11 @@ 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); + } + mAttributes.emplace_back(attr); } else { Entry::parseSdpLine(line); } @@ -875,6 +879,15 @@ void Description::Media::addRTPMap(const Description::Media::RTPMap &map) { std::vector Description::Media::getSSRCs() { return mSsrcs; } +std::optional Description::Media::getCNameForSsrc(uint32_t ssrc) { + for (auto &val : mAttributes) { + if (val.find("ssrc:") == 0 && val.find("cname:") != std::string::npos) { + return val.substr(val.find("cname:") + 6); + } + } + return std::nullopt; +} + std::map::iterator Description::Media::beginMaps() { return mRtpMap.begin(); }