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

C api for h264/opus streaming
This commit is contained in:
Paul-Louis Ageneau
2021-01-15 18:00:20 +01:00
committed by GitHub
2 changed files with 369 additions and 1 deletions

View File

@ -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 {

View File

@ -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)