Browse Source

Merge pull request #306 from in2core/feature/capi-stream-h264-opus

C api for h264/opus streaming
Paul-Louis Ageneau 4 years ago
parent
commit
3710a96fb9
2 changed files with 369 additions and 1 deletions
  1. 107 0
      include/rtc/rtc.h
  2. 262 1
      src/capi.cpp

+ 107 - 0
include/rtc/rtc.h

@@ -77,6 +77,28 @@ typedef enum { // Don't change, it must match plog severity
 	RTC_LOG_VERBOSE = 6
 } rtcLogLevel;
 
+#if RTC_ENABLE_MEDIA
+
+typedef enum {
+    // video
+    RTC_CODEC_H264,
+    RTC_CODEC_VP8,
+    RTC_CODEC_VP9,
+
+    // audio
+    RTC_CODEC_OPUS
+} rtcCodec;
+
+typedef enum {
+    RTC_DIRECTION_UNKNOWN,
+    RTC_DIRECTION_SENDONLY,
+    RTC_DIRECTION_RECVONLY,
+    RTC_DIRECTION_SENDRECV,
+    RTC_DIRECTION_INACTIVE
+} rtcDirection;
+
+#endif // RTC_ENABLE_MEDIA
+
 #define RTC_ERR_SUCCESS 0
 #define RTC_ERR_INVALID -1   // invalid argument
 #define RTC_ERR_FAILURE -2   // runtime error
@@ -129,6 +151,7 @@ RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb);
 
 // User pointer
 RTC_EXPORT void rtcSetUserPointer(int id, void *ptr);
+RTC_EXPORT void * rtcGetUserPointer(int i);
 
 // PeerConnection
 RTC_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id
@@ -176,6 +199,90 @@ RTC_EXPORT int rtcDeleteTrack(int tr);
 
 RTC_EXPORT int rtcGetTrackDescription(int tr, char *buffer, int size);
 
+// Media
+#if RTC_ENABLE_MEDIA
+
+/// Add track
+/// @param pc Peer connection id
+/// @param codec Codec
+/// @param payloadType Payload type
+/// @param ssrc SSRC
+/// @param _mid MID
+/// @param _direction Direction
+/// @param _name Name (optional)
+/// @param _msid MSID (optional)
+/// @returns Track id
+RTC_EXPORT int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid, rtcDirection direction, const char *_name, const char *_msid);
+
+/// Set H264PacketizationHandler for track
+/// @param tr Track id
+/// @param ssrc SSRC
+/// @param cname CName
+/// @param payloadType Payload Type
+/// @param clockRate Clock rate
+/// @param _sequenceNumber Sequence number
+/// @param _timestamp Timestamp
+RTC_EXPORT int rtcSetH264PacketizationHandler(int tr, uint32_t ssrc, const char * cname, uint8_t payloadType, uint32_t clockRate, uint16_t _sequenceNumber, uint32_t _timestamp);
+
+/// Set OpusPacketizationHandler for track
+/// @param tr Track id
+/// @param ssrc SSRC
+/// @param cname CName
+/// @param payloadType Payload Type
+/// @param clockRate Clock rate
+/// @param _sequenceNumber Sequence number
+/// @param _timestamp Timestamp
+RTC_EXPORT int rtcSetOpusPacketizationHandler(int tr, uint32_t ssrc, const char * cname, uint8_t payloadType, uint32_t clockRate, uint16_t _sequenceNumber, uint32_t _timestamp);
+
+/// Set start time for RTP stream
+/// @param startTime_s Start time in seconds
+/// @param timeIntervalSince1970 Set true if `startTime_s` is time interval since 1970, false if `startTime_s` is time interval since 1900
+/// @param _timestamp Start timestamp
+int rtcSetRtpConfigurationStartTime(int id, double startTime_s, bool timeIntervalSince1970, uint32_t _timestamp);
+
+/// Start stats recording for RTCP Sender Reporter
+/// @param id Track identifier
+int rtcStartRtcpSenderReporterRecording(int id);
+
+
+/// Transform seconds to timestamp using track's clock rate
+/// @param id Track id
+/// @param seconds Seconds
+/// @param timestamp Pointer to result
+int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t * timestamp);
+
+/// Transform timestamp to seconds using track's clock rate
+/// @param id Track id
+/// @param timestamp Timestamp
+/// @param seconds Pointer for result
+int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double * seconds);
+
+/// Get current timestamp
+/// @param id Track id
+/// @param timestamp Pointer for result
+int rtcGetCurrentTrackTimestamp(int id, uint32_t * timestamp);
+
+/// Get start timestamp for track identified by given id
+/// @param id Track id
+/// @param timestamp Pointer for result
+int rtcGetTrackStartTimestamp(int id, uint32_t * timestamp);
+
+/// Set RTP timestamp for track identified by given id
+/// @param id Track id
+/// @param timestamp New timestamp
+int rtcSetTrackRTPTimestamp(int id, uint32_t timestamp);
+
+/// Get timestamp of previous RTCP SR
+/// @param id Track id
+/// @param timestamp Pointer for result
+int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t * timestamp);
+
+/// Set `NeedsToReport` flag in RTCPSenderReportable handler identified by given track id
+/// @param id Track id
+int rtcSetNeedsToSendRTCPSR(int id);
+
+#endif // RTC_ENABLE_MEDIA
+
 // WebSocket
 #if RTC_ENABLE_WEBSOCKET
 typedef struct {

+ 262 - 1
src/capi.cpp

@@ -44,6 +44,7 @@
 using namespace rtc;
 using std::shared_ptr;
 using std::string;
+using std::optional;
 using std::chrono::milliseconds;
 
 namespace {
@@ -51,6 +52,10 @@ namespace {
 std::unordered_map<int, shared_ptr<PeerConnection>> peerConnectionMap;
 std::unordered_map<int, shared_ptr<DataChannel>> dataChannelMap;
 std::unordered_map<int, shared_ptr<Track>> trackMap;
+#if RTC_ENABLE_MEDIA
+std::unordered_map<int, shared_ptr<RTCPSenderReportable>> rtcpSenderMap;
+std::unordered_map<int, shared_ptr<RTPPacketizationConfig>> rtpConfigMap;
+#endif
 #if RTC_ENABLE_WEBSOCKET
 std::unordered_map<int, shared_ptr<WebSocket>> webSocketMap;
 #endif
@@ -135,9 +140,66 @@ void eraseTrack(int tr) {
 	std::lock_guard lock(mutex);
 	if (trackMap.erase(tr) == 0)
 		throw std::invalid_argument("Track ID does not exist");
+#if RTC_ENABLE_MEDIA
+    rtcpSenderMap.erase(tr);
+    rtpConfigMap.erase(tr);
+#endif
 	userPointerMap.erase(tr);
 }
 
+#if RTC_ENABLE_MEDIA
+
+shared_ptr<RTCPSenderReportable> getRTCPSender(int id) {
+    std::lock_guard lock(mutex);
+    if (auto it = rtcpSenderMap.find(id); it != rtcpSenderMap.end())
+        return it->second;
+    else
+        throw std::invalid_argument("RTCPSenderReportable ID does not exist");
+}
+
+void emplaceRTCPSender(shared_ptr<RTCPSenderReportable> ptr, int tr) {
+    std::lock_guard lock(mutex);
+    rtcpSenderMap.emplace(std::make_pair(tr, ptr));
+}
+
+shared_ptr<RTPPacketizationConfig> getRTPConfig(int id) {
+    std::lock_guard lock(mutex);
+    if (auto it = rtpConfigMap.find(id); it != rtpConfigMap.end())
+        return it->second;
+    else
+        throw std::invalid_argument("RTPConfiguration ID does not exist");
+}
+
+void emplaceRTPConfig(shared_ptr<RTPPacketizationConfig> ptr, int tr) {
+    std::lock_guard lock(mutex);
+    rtpConfigMap.emplace(std::make_pair(tr, ptr));
+}
+
+Description::Direction rtcDirectionToDirection(rtcDirection direction) {
+    switch (direction) {
+        case RTC_DIRECTION_SENDONLY:
+            return Description::Direction::SendOnly;
+        case RTC_DIRECTION_RECVONLY:
+            return Description::Direction::RecvOnly;
+        case RTC_DIRECTION_SENDRECV:
+            return Description::Direction::SendRecv;
+        case RTC_DIRECTION_INACTIVE:
+            return Description::Direction::Inactive;
+        default:
+            return Description::Direction::Unknown;
+    }
+}
+
+shared_ptr<RTPPacketizationConfig> getNewRTPPacketizationConfig(uint32_t ssrc, const char * cname, uint8_t payloadType, uint32_t clockRate, uint16_t sequenceNumber, uint32_t timestamp) {
+    if (!cname) {
+        throw std::invalid_argument("Unexpected null pointer for cname");
+    }
+
+    return std::make_shared<RTPPacketizationConfig>(ssrc, cname, payloadType, clockRate, sequenceNumber, timestamp);
+}
+
+#endif // RTC_ENABLE_MEDIA
+
 #if RTC_ENABLE_WEBSOCKET
 shared_ptr<WebSocket> getWebSocket(int id) {
 	std::lock_guard lock(mutex);
@@ -276,6 +338,10 @@ void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
 
 void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
 
+void * rtcGetUserPointer(int i) {
+    return getUserPointer(i).value_or(nullptr);
+}
+
 int rtcCreatePeerConnection(const rtcConfiguration *config) {
 	return WRAP({
 		Configuration c;
@@ -295,7 +361,8 @@ int rtcCreatePeerConnection(const rtcConfiguration *config) {
 int rtcDeletePeerConnection(int pc) {
 	return WRAP({
 		auto peerConnection = getPeerConnection(pc);
-		peerConnection->onDataChannel(nullptr);
+        peerConnection->onDataChannel(nullptr);
+        peerConnection->onTrack(nullptr);
 		peerConnection->onLocalDescription(nullptr);
 		peerConnection->onLocalCandidate(nullptr);
 		peerConnection->onStateChange(nullptr);
@@ -365,6 +432,200 @@ int rtcDeleteDataChannel(int dc) {
 	});
 }
 
+#if RTC_ENABLE_MEDIA
+
+void setSSRC(Description::Media * description, uint32_t ssrc, const char *_name, const char *_msid) {
+
+    optional<string> name = nullopt;
+    if (_name) {
+        name = string(_name);
+    }
+
+    optional<string> msid = nullopt;
+    if (_msid) {
+        msid = string(_msid);
+    }
+
+    description->addSSRC(ssrc, name, msid);
+}
+
+int rtcAddTrackEx(int pc, rtcCodec codec, int payloadType, uint32_t ssrc, const char *_mid, rtcDirection _direction, const char *_name, const char *_msid) {
+    return WRAP( {
+        auto peerConnection = getPeerConnection(pc);
+
+        auto direction = rtcDirectionToDirection(_direction);
+
+        string mid = "video";
+        switch (codec) {
+            case RTC_CODEC_H264:
+            case RTC_CODEC_VP8:
+            case RTC_CODEC_VP9:
+                mid = "video";
+                break;
+            case RTC_CODEC_OPUS:
+                mid = "audio";
+                break;
+        }
+
+        if (_mid) {
+            mid = string(_mid);
+        }
+
+        optional<Description::Media> optDescription = nullopt;
+
+        switch (codec) {
+            case RTC_CODEC_H264:
+            case RTC_CODEC_VP8:
+            case RTC_CODEC_VP9: {
+                auto desc = Description::Video(mid, direction);
+                switch (codec) {
+                    case RTC_CODEC_H264:
+                        desc.addH264Codec(payloadType);
+                        break;
+                    case RTC_CODEC_VP8:
+                        desc.addVP8Codec(payloadType);
+                        break;
+                    case RTC_CODEC_VP9:
+                        desc.addVP8Codec(payloadType);
+                        break;
+                    default:
+                        break;
+                }
+                optDescription = desc;
+                break;
+            }
+            case RTC_CODEC_OPUS: {
+                auto desc = Description::Audio(mid, direction);
+                switch (codec) {
+                    case RTC_CODEC_OPUS:
+                        desc.addOpusCodec(payloadType);
+                        break;
+                    default:
+                        break;
+                }
+                optDescription = desc;
+                break;
+            }
+            default:
+                break;
+        }
+
+        if (!optDescription.has_value()) {
+            throw std::invalid_argument("Unexpected codec");
+        } else {
+            auto description = optDescription.value();
+            setSSRC(&description, ssrc, _name, _msid);
+
+            int tr = emplaceTrack(peerConnection->addTrack(std::move(description)));
+            if (auto ptr = getUserPointer(pc)) {
+                rtcSetUserPointer(tr, *ptr);
+            }
+            return tr;
+        }
+    });
+}
+
+int rtcSetH264PacketizationHandler(int tr, uint32_t ssrc, const char * cname, uint8_t payloadType, uint32_t clockRate, uint16_t sequenceNumber, uint32_t timestamp) {
+    return WRAP({
+        auto track = getTrack(tr);
+        // create RTP configuration
+        auto rtpConfig = getNewRTPPacketizationConfig(ssrc, cname, payloadType, clockRate, sequenceNumber, timestamp);
+        // create packetizer
+        auto packetizer = shared_ptr<H264RTPPacketizer>(new H264RTPPacketizer(rtpConfig));
+        // create H264 and RTCP SP handler
+        shared_ptr<H264PacketizationHandler> h264Handler(new H264PacketizationHandler(H264PacketizationHandler::Separator::Length, packetizer));
+        emplaceRTCPSender(h264Handler, tr);
+        emplaceRTPConfig(rtpConfig, tr);
+        // set handler
+        track->setRtcpHandler(h264Handler);
+    });
+}
+
+int rtcSetOpusPacketizationHandler(int tr, uint32_t ssrc, const char * cname, uint8_t payloadType, uint32_t clockRate, uint16_t sequenceNumber, uint32_t timestamp) {
+    return WRAP({
+        auto track = getTrack(tr);
+        // create RTP configuration
+        auto rtpConfig = getNewRTPPacketizationConfig(ssrc, cname, payloadType, clockRate, sequenceNumber, timestamp);
+        // create packetizer
+        auto packetizer = shared_ptr<OpusRTPPacketizer>(new OpusRTPPacketizer(rtpConfig));
+        // create Opus and RTCP SP handler
+        shared_ptr<OpusPacketizationHandler> opusHandler(new OpusPacketizationHandler(packetizer));
+        emplaceRTCPSender(opusHandler, tr);
+        emplaceRTPConfig(rtpConfig, tr);
+        // set handler
+        track->setRtcpHandler(opusHandler);
+    });
+}
+
+int rtcSetRtpConfigurationStartTime(int id, double startTime_s, bool timeIntervalSince1970, uint32_t timestamp) {
+    return WRAP({
+        auto config = getRTPConfig(id);
+        auto epoch = RTPPacketizationConfig::EpochStart::T1900;
+        if (timeIntervalSince1970) {
+            epoch = RTPPacketizationConfig::EpochStart::T1970;
+        }
+        config->setStartTime(startTime_s, epoch, timestamp);
+    });
+}
+
+int rtcStartRtcpSenderReporterRecording(int id) {
+    return WRAP({
+        auto sender = getRTCPSender(id);
+        sender->startRecording();
+    });
+}
+
+int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t * timestamp) {
+    return WRAP({
+        auto config = getRTPConfig(id);
+        *timestamp = config->secondsToTimestamp(seconds);
+    });
+}
+
+int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double * seconds) {
+    return WRAP({
+        auto config = getRTPConfig(id);
+        *seconds = config->timestampToSeconds(timestamp);
+    });
+}
+
+int rtcGetCurrentTrackTimestamp(int id, uint32_t * timestamp) {
+    return WRAP({
+        auto config = getRTPConfig(id);
+        *timestamp = config->timestamp;
+    });
+}
+
+int rtcGetTrackStartTimestamp(int id, uint32_t * timestamp) {
+    return WRAP({
+        auto config = getRTPConfig(id);
+        *timestamp = config->startTimestamp;
+    });
+}
+
+int rtcSetTrackRTPTimestamp(int id, uint32_t timestamp) {
+    return WRAP({
+        auto config = getRTPConfig(id);
+        config->timestamp = timestamp;
+    });
+}
+
+int rtcGetPreviousTrackSenderReportTimestamp(int id, uint32_t * timestamp) {
+    return WRAP({
+        auto sender = getRTCPSender(id);
+        *timestamp = sender->previousReportedTimestamp;
+    });
+}
+
+int rtcSetNeedsToSendRTCPSR(int id) {
+    return WRAP({
+        auto sender = getRTCPSender(id);
+        sender->setNeedsToReport();
+    });
+}
+
+#endif // RTC_ENABLE_MEDIA
+
 int rtcAddTrack(int pc, const char *mediaDescriptionSdp) {
 	return WRAP({
 		if (!mediaDescriptionSdp)