mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-22 15:15:28 +00:00
Merge pull request #306 from in2core/feature/capi-stream-h264-opus
C api for h264/opus streaming
This commit is contained in:
@ -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 {
|
||||
|
263
src/capi.cpp
263
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)
|
||||
|
Reference in New Issue
Block a user