|
|
|
@ -35,6 +35,8 @@
|
|
|
|
|
#include <srs_kernel_buffer.hpp>
|
|
|
|
|
#include <srs_kernel_rtc_rtp.hpp>
|
|
|
|
|
#include <srs_core_autofree.hpp>
|
|
|
|
|
#include <srs_app_rtc_queue.hpp>
|
|
|
|
|
#include <srs_app_rtc_conn.hpp>
|
|
|
|
|
|
|
|
|
|
#ifdef SRS_FFMPEG_FIT
|
|
|
|
|
#include <srs_app_rtc_codec.hpp>
|
|
|
|
@ -90,6 +92,41 @@ srs_error_t aac_raw_append_adts_header(SrsSharedPtrMessage* shared_audio, SrsFor
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t SrsNtp::kMagicNtpFractionalUnit = 1ULL << 32;
|
|
|
|
|
|
|
|
|
|
SrsNtp::SrsNtp()
|
|
|
|
|
{
|
|
|
|
|
system_ms_ = 0;
|
|
|
|
|
ntp_ = 0;
|
|
|
|
|
ntp_second_ = 0;
|
|
|
|
|
ntp_fractions_ = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsNtp::~SrsNtp()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsNtp SrsNtp::from_time_ms(uint64_t ms)
|
|
|
|
|
{
|
|
|
|
|
SrsNtp srs_ntp;
|
|
|
|
|
srs_ntp.system_ms_ = ms;
|
|
|
|
|
srs_ntp.ntp_second_ = ms / 1000;
|
|
|
|
|
srs_ntp.ntp_fractions_ = (static_cast<double>(ms % 1000 / 1000.0)) * kMagicNtpFractionalUnit;
|
|
|
|
|
srs_ntp.ntp_ = (static_cast<uint64_t>(srs_ntp.ntp_second_) << 32) | srs_ntp.ntp_fractions_;
|
|
|
|
|
return srs_ntp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsNtp SrsNtp::to_time_ms(uint64_t ntp)
|
|
|
|
|
{
|
|
|
|
|
SrsNtp srs_ntp;
|
|
|
|
|
srs_ntp.ntp_ = ntp;
|
|
|
|
|
srs_ntp.ntp_second_ = (ntp & 0xFFFFFFFF00000000ULL) >> 32;
|
|
|
|
|
srs_ntp.ntp_fractions_ = (ntp & 0x00000000FFFFFFFFULL);
|
|
|
|
|
srs_ntp.system_ms_ = (static_cast<uint64_t>(srs_ntp.ntp_second_) * 1000) +
|
|
|
|
|
(static_cast<double>(static_cast<uint64_t>(srs_ntp.ntp_fractions_) * 1000.0) / kMagicNtpFractionalUnit);
|
|
|
|
|
return srs_ntp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcConsumer::SrsRtcConsumer(SrsRtcStream* s)
|
|
|
|
|
{
|
|
|
|
|
source = s;
|
|
|
|
@ -254,6 +291,7 @@ SrsRtcStream::SrsRtcStream()
|
|
|
|
|
#else
|
|
|
|
|
bridger_ = new SrsRtcDummyBridger();
|
|
|
|
|
#endif
|
|
|
|
|
stream_desc_ = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcStream::~SrsRtcStream()
|
|
|
|
@ -264,6 +302,7 @@ SrsRtcStream::~SrsRtcStream()
|
|
|
|
|
|
|
|
|
|
srs_freep(req);
|
|
|
|
|
srs_freep(bridger_);
|
|
|
|
|
srs_freep(stream_desc_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcStream::initialize(SrsRequest* r)
|
|
|
|
@ -423,6 +462,36 @@ srs_error_t SrsRtcStream::on_rtp(SrsRtpPacket2* pkt)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcStream::set_stream_desc(SrsRtcStreamDescription* stream_desc)
|
|
|
|
|
{
|
|
|
|
|
srs_freep(stream_desc_);
|
|
|
|
|
stream_desc_ = stream_desc->copy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<SrsRtcTrackDescription*> SrsRtcStream::get_track_desc(std::string type, std::string media_name)
|
|
|
|
|
{
|
|
|
|
|
std::vector<SrsRtcTrackDescription*> track_descs;
|
|
|
|
|
if (!stream_desc_) {
|
|
|
|
|
return track_descs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type == "audio") {
|
|
|
|
|
if (stream_desc_->audio_track_desc_->media_->name_ == media_name) {
|
|
|
|
|
track_descs.push_back(stream_desc_->audio_track_desc_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type == "video") {
|
|
|
|
|
std::vector<SrsRtcTrackDescription*>::iterator it = stream_desc_->video_track_descs_.begin();
|
|
|
|
|
while (it != stream_desc_->video_track_descs_.end() ){
|
|
|
|
|
track_descs.push_back(*it);
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return track_descs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef SRS_FFMPEG_FIT
|
|
|
|
|
SrsRtcFromRtmpBridger::SrsRtcFromRtmpBridger(SrsRtcStream* source)
|
|
|
|
|
{
|
|
|
|
@ -975,3 +1044,672 @@ void SrsRtcDummyBridger::on_unpublish()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsCodecPayload::SrsCodecPayload()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsCodecPayload::SrsCodecPayload(uint8_t pt, std::string encode_name, int sample)
|
|
|
|
|
{
|
|
|
|
|
pt_ = pt;
|
|
|
|
|
name_ = encode_name;
|
|
|
|
|
sample_ = sample;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsCodecPayload::~SrsCodecPayload()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsCodecPayload* SrsCodecPayload::copy()
|
|
|
|
|
{
|
|
|
|
|
SrsCodecPayload* cp = new SrsCodecPayload();
|
|
|
|
|
|
|
|
|
|
cp->type_ = type_;
|
|
|
|
|
cp->pt_ = pt_;
|
|
|
|
|
cp->name_ = name_;
|
|
|
|
|
cp->sample_ = sample_;
|
|
|
|
|
cp->rtcp_fbs_ = rtcp_fbs_;
|
|
|
|
|
|
|
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsMediaPayloadType SrsCodecPayload::generate_media_payload_type()
|
|
|
|
|
{
|
|
|
|
|
SrsMediaPayloadType media_payload_type(pt_);
|
|
|
|
|
|
|
|
|
|
media_payload_type.encoding_name_ = name_;
|
|
|
|
|
media_payload_type.clock_rate_ = sample_;
|
|
|
|
|
media_payload_type.rtcp_fb_ = rtcp_fbs_;
|
|
|
|
|
|
|
|
|
|
return media_payload_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsVideoPayload::SrsVideoPayload()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsVideoPayload::SrsVideoPayload(uint8_t pt, std::string encode_name, int sample)
|
|
|
|
|
:SrsCodecPayload(pt, encode_name, sample)
|
|
|
|
|
{
|
|
|
|
|
h264_param_.profile_level_id = "";
|
|
|
|
|
h264_param_.packetization_mode = "";
|
|
|
|
|
h264_param_.level_asymmerty_allow = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsVideoPayload::~SrsVideoPayload()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsVideoPayload* SrsVideoPayload::copy()
|
|
|
|
|
{
|
|
|
|
|
SrsVideoPayload* cp = new SrsVideoPayload();
|
|
|
|
|
|
|
|
|
|
cp->type_ = type_;
|
|
|
|
|
cp->pt_ = pt_;
|
|
|
|
|
cp->name_ = name_;
|
|
|
|
|
cp->sample_ = sample_;
|
|
|
|
|
cp->rtcp_fbs_ = rtcp_fbs_;
|
|
|
|
|
cp->h264_param_ = h264_param_;
|
|
|
|
|
|
|
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsMediaPayloadType SrsVideoPayload::generate_media_payload_type()
|
|
|
|
|
{
|
|
|
|
|
SrsMediaPayloadType media_payload_type(pt_);
|
|
|
|
|
|
|
|
|
|
media_payload_type.encoding_name_ = name_;
|
|
|
|
|
media_payload_type.clock_rate_ = sample_;
|
|
|
|
|
media_payload_type.rtcp_fb_ = rtcp_fbs_;
|
|
|
|
|
|
|
|
|
|
std::ostringstream format_specific_param;
|
|
|
|
|
if (!h264_param_.level_asymmerty_allow.empty()) {
|
|
|
|
|
format_specific_param << "level-asymmetry-allowed=" << h264_param_.level_asymmerty_allow;
|
|
|
|
|
}
|
|
|
|
|
if (!h264_param_.packetization_mode.empty()) {
|
|
|
|
|
format_specific_param << ";packetization-mode=" << h264_param_.packetization_mode;
|
|
|
|
|
}
|
|
|
|
|
if (!h264_param_.profile_level_id.empty()) {
|
|
|
|
|
format_specific_param << ";profile-level-id=" << h264_param_.profile_level_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
media_payload_type.format_specific_param_ = format_specific_param.str();
|
|
|
|
|
|
|
|
|
|
return media_payload_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsVideoPayload::set_h264_param_desc(std::string fmtp)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
std::vector<std::string> vec = split_str(fmtp, ";");
|
|
|
|
|
for (size_t i = 0; i < vec.size(); ++i) {
|
|
|
|
|
std::vector<std::string> kv = split_str(vec[i], "=");
|
|
|
|
|
if (kv.size() == 2) {
|
|
|
|
|
if (kv[0] == "profile-level-id") {
|
|
|
|
|
h264_param_.profile_level_id = kv[1];
|
|
|
|
|
} else if (kv[0] == "packetization-mode") {
|
|
|
|
|
// 6.3. Non-Interleaved Mode
|
|
|
|
|
// This mode is in use when the value of the OPTIONAL packetization-mode
|
|
|
|
|
// media type parameter is equal to 1. This mode SHOULD be supported.
|
|
|
|
|
// It is primarily intended for low-delay applications. Only single NAL
|
|
|
|
|
// unit packets, STAP-As, and FU-As MAY be used in this mode. STAP-Bs,
|
|
|
|
|
// MTAPs, and FU-Bs MUST NOT be used. The transmission order of NAL
|
|
|
|
|
// units MUST comply with the NAL unit decoding order.
|
|
|
|
|
// @see https://tools.ietf.org/html/rfc6184#section-6.3
|
|
|
|
|
h264_param_.packetization_mode = kv[1];
|
|
|
|
|
} else if (kv[0] == "level-asymmetry-allowed") {
|
|
|
|
|
h264_param_.level_asymmerty_allow = kv[1];
|
|
|
|
|
} else {
|
|
|
|
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", kv[0].c_str());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", vec[i].c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsAudioPayload::SrsAudioPayload()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsAudioPayload::SrsAudioPayload(uint8_t pt, std::string encode_name, int sample, int channel)
|
|
|
|
|
:SrsCodecPayload(pt, encode_name, sample)
|
|
|
|
|
{
|
|
|
|
|
channel_ = channel;
|
|
|
|
|
opus_param_.minptime = 0;
|
|
|
|
|
opus_param_.use_inband_fec = false;
|
|
|
|
|
opus_param_.usedtx = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsAudioPayload::~SrsAudioPayload()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsAudioPayload* SrsAudioPayload::copy()
|
|
|
|
|
{
|
|
|
|
|
SrsAudioPayload* cp = new SrsAudioPayload();
|
|
|
|
|
|
|
|
|
|
cp->type_ = type_;
|
|
|
|
|
cp->pt_ = pt_;
|
|
|
|
|
cp->name_ = name_;
|
|
|
|
|
cp->sample_ = sample_;
|
|
|
|
|
cp->rtcp_fbs_ = rtcp_fbs_;
|
|
|
|
|
cp->channel_ = channel_;
|
|
|
|
|
cp->opus_param_ = opus_param_;
|
|
|
|
|
|
|
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsMediaPayloadType SrsAudioPayload::generate_media_payload_type()
|
|
|
|
|
{
|
|
|
|
|
SrsMediaPayloadType media_payload_type(pt_);
|
|
|
|
|
|
|
|
|
|
media_payload_type.encoding_name_ = name_;
|
|
|
|
|
media_payload_type.clock_rate_ = sample_;
|
|
|
|
|
media_payload_type.encoding_param_ = srs_int2str(channel_);
|
|
|
|
|
media_payload_type.rtcp_fb_ = rtcp_fbs_;
|
|
|
|
|
|
|
|
|
|
std::ostringstream format_specific_param;
|
|
|
|
|
if (opus_param_.minptime) {
|
|
|
|
|
format_specific_param << "minptime=" << opus_param_.minptime;
|
|
|
|
|
}
|
|
|
|
|
if (opus_param_.use_inband_fec) {
|
|
|
|
|
format_specific_param << ";useinbandfec=1";
|
|
|
|
|
}
|
|
|
|
|
if (opus_param_.usedtx) {
|
|
|
|
|
format_specific_param << ";usedtx=1";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
media_payload_type.format_specific_param_ = format_specific_param.str();
|
|
|
|
|
|
|
|
|
|
return media_payload_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsAudioPayload::set_opus_param_desc(std::string fmtp)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
std::vector<std::string> vec = split_str(fmtp, ";");
|
|
|
|
|
for (size_t i = 0; i < vec.size(); ++i) {
|
|
|
|
|
std::vector<std::string> kv = split_str(vec[i], "=");
|
|
|
|
|
if (kv.size() == 2) {
|
|
|
|
|
if (kv[0] == "minptime") {
|
|
|
|
|
opus_param_.minptime = (int)::atol(kv[1].c_str());
|
|
|
|
|
} else if (kv[0] == "useinbandfec") {
|
|
|
|
|
opus_param_.use_inband_fec = (kv[1] == "1") ? true : false;
|
|
|
|
|
} else if (kv[0] == "usedtx") {
|
|
|
|
|
opus_param_.usedtx = (kv[1] == "1") ? true : false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid opus param=%s", vec[i].c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcTrackDescription::SrsRtcTrackDescription()
|
|
|
|
|
{
|
|
|
|
|
ssrc_ = 0;
|
|
|
|
|
rtx_ssrc_ = 0;
|
|
|
|
|
fec_ssrc_ = 0;
|
|
|
|
|
is_active_ = true;
|
|
|
|
|
|
|
|
|
|
media_ = NULL;
|
|
|
|
|
red_ = NULL;
|
|
|
|
|
rtx_ = NULL;
|
|
|
|
|
ulpfec_ = NULL;
|
|
|
|
|
rsfec_ = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcTrackDescription::~SrsRtcTrackDescription()
|
|
|
|
|
{
|
|
|
|
|
srs_freep(media_);
|
|
|
|
|
srs_freep(red_);
|
|
|
|
|
srs_freep(rtx_);
|
|
|
|
|
srs_freep(ulpfec_);
|
|
|
|
|
srs_freep(rsfec_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SrsRtcTrackDescription::has_ssrc(uint32_t ssrc)
|
|
|
|
|
{
|
|
|
|
|
if (ssrc == ssrc_ || ssrc == rtx_ssrc_ || ssrc == fec_ssrc_) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcTrackDescription::add_rtp_extension_desc(int id, std::string uri)
|
|
|
|
|
{
|
|
|
|
|
extmaps_[id] = uri;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcTrackDescription::set_direction(std::string direction)
|
|
|
|
|
{
|
|
|
|
|
direction_ = direction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcTrackDescription::set_codec_payload(SrsCodecPayload* payload)
|
|
|
|
|
{
|
|
|
|
|
media_ = payload;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcTrackDescription::create_auxiliary_payload(const std::vector<SrsMediaPayloadType> payloads)
|
|
|
|
|
{
|
|
|
|
|
if (!payloads.size()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsMediaPayloadType payload = payloads.at(0);
|
|
|
|
|
if (payload.encoding_name_ == "red"){
|
|
|
|
|
srs_freep(red_);
|
|
|
|
|
red_ = new SrsCodecPayload(payload.payload_type_, "red", payload.clock_rate_);
|
|
|
|
|
} else if (payload.encoding_name_ == "rtx") {
|
|
|
|
|
srs_freep(rtx_);
|
|
|
|
|
rtx_ = new SrsCodecPayload(payload.payload_type_, "rtx", payload.clock_rate_);
|
|
|
|
|
} else if (payload.encoding_name_ == "ulpfec") {
|
|
|
|
|
srs_freep(ulpfec_);
|
|
|
|
|
ulpfec_ = new SrsCodecPayload(payload.payload_type_, "ulpfec", payload.clock_rate_);
|
|
|
|
|
} else if (payload.encoding_name_ == "rsfec") {
|
|
|
|
|
srs_freep(rsfec_);
|
|
|
|
|
rsfec_ = new SrsCodecPayload(payload.payload_type_, "rsfec", payload.clock_rate_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcTrackDescription::set_rtx_ssrc(uint32_t ssrc)
|
|
|
|
|
{
|
|
|
|
|
rtx_ssrc_ = ssrc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcTrackDescription::set_fec_ssrc(uint32_t ssrc)
|
|
|
|
|
{
|
|
|
|
|
fec_ssrc_ = ssrc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcTrackDescription::set_mid(std::string mid)
|
|
|
|
|
{
|
|
|
|
|
mid_ = mid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SrsRtcTrackDescription::get_rtp_extension_id(std::string uri)
|
|
|
|
|
{
|
|
|
|
|
for(std::map<int, std::string>::iterator it = extmaps_.begin(); it != extmaps_.end(); ++it) {
|
|
|
|
|
if(uri == it->second) {
|
|
|
|
|
return it->first;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcTrackDescription* SrsRtcTrackDescription::copy()
|
|
|
|
|
{
|
|
|
|
|
SrsRtcTrackDescription* cp = new SrsRtcTrackDescription();
|
|
|
|
|
|
|
|
|
|
cp->type_ = type_;
|
|
|
|
|
cp->id_ = id_;
|
|
|
|
|
cp->ssrc_ = ssrc_;
|
|
|
|
|
cp->fec_ssrc_ = fec_ssrc_;
|
|
|
|
|
cp->rtx_ssrc_ = rtx_ssrc_;
|
|
|
|
|
cp->extmaps_ = extmaps_;
|
|
|
|
|
cp->direction_ = direction_;
|
|
|
|
|
cp->mid_ = mid_;
|
|
|
|
|
cp->is_active_ = is_active_;
|
|
|
|
|
cp->media_ = media_ ? media_->copy():NULL;
|
|
|
|
|
cp->red_ = red_ ? red_->copy():NULL;
|
|
|
|
|
cp->rtx_ = rtx_ ? rtx_->copy():NULL;
|
|
|
|
|
cp->ulpfec_ = ulpfec_ ? ulpfec_->copy():NULL;
|
|
|
|
|
cp->rsfec_ = rsfec_ ? rsfec_->copy():NULL;
|
|
|
|
|
|
|
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcStreamDescription::SrsRtcStreamDescription()
|
|
|
|
|
{
|
|
|
|
|
audio_track_desc_ = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcStreamDescription::~SrsRtcStreamDescription()
|
|
|
|
|
{
|
|
|
|
|
srs_freep(audio_track_desc_);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < video_track_descs_.size(); ++i) {
|
|
|
|
|
srs_freep(video_track_descs_.at(i));
|
|
|
|
|
}
|
|
|
|
|
video_track_descs_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcStreamDescription* SrsRtcStreamDescription::copy()
|
|
|
|
|
{
|
|
|
|
|
SrsRtcStreamDescription* stream_desc = new SrsRtcStreamDescription();
|
|
|
|
|
|
|
|
|
|
if (audio_track_desc_) {
|
|
|
|
|
stream_desc->audio_track_desc_ = audio_track_desc_->copy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < video_track_descs_.size(); ++i) {
|
|
|
|
|
stream_desc->video_track_descs_.push_back(video_track_descs_.at(i)->copy());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return stream_desc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcTrackDescription* SrsRtcStreamDescription::find_track_description_by_ssrc(uint32_t ssrc)
|
|
|
|
|
{
|
|
|
|
|
if (audio_track_desc_->has_ssrc(ssrc)) {
|
|
|
|
|
return audio_track_desc_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < video_track_descs_.size(); ++i) {
|
|
|
|
|
if (video_track_descs_.at(i)->has_ssrc(ssrc)) {
|
|
|
|
|
return video_track_descs_.at(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ISrsRtcTrack::ISrsRtcTrack()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ISrsRtcTrack::~ISrsRtcTrack()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcRecvTrack::SrsRtcRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc, bool is_audio)
|
|
|
|
|
{
|
|
|
|
|
session_ = session;
|
|
|
|
|
track_desc_ = track_desc->copy();
|
|
|
|
|
if (is_audio) {
|
|
|
|
|
rtp_queue_ = new SrsRtpRingBuffer(100);
|
|
|
|
|
nack_receiver_ = new SrsRtpNackForReceiver(rtp_queue_, 100 * 2 / 3);
|
|
|
|
|
} else {
|
|
|
|
|
rtp_queue_ = new SrsRtpRingBuffer(1000);
|
|
|
|
|
nack_receiver_ = new SrsRtpNackForReceiver(rtp_queue_, 1000 * 2 / 3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcRecvTrack::~SrsRtcRecvTrack()
|
|
|
|
|
{
|
|
|
|
|
srs_freep(rtp_queue_);
|
|
|
|
|
srs_freep(nack_receiver_);
|
|
|
|
|
srs_freep(track_desc_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SrsRtcRecvTrack::has_ssrc(uint32_t ssrc)
|
|
|
|
|
{
|
|
|
|
|
if (track_desc_) {
|
|
|
|
|
return track_desc_->has_ssrc(ssrc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcRecvTrack::update_rtt(int rtt)
|
|
|
|
|
{
|
|
|
|
|
if (nack_receiver_) {
|
|
|
|
|
nack_receiver_->update_rtt(rtt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcRecvTrack::update_send_report_time(const SrsNtp& ntp)
|
|
|
|
|
{
|
|
|
|
|
last_sender_report_ntp = ntp;
|
|
|
|
|
last_sender_report_sys_time = srs_update_system_time();;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcRecvTrack::send_rtcp_rr()
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
if (session_) {
|
|
|
|
|
return session_->send_rtcp_rr(track_desc_->ssrc_, rtp_queue_, last_sender_report_sys_time, last_sender_report_ntp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcRecvTrack::send_rtcp_xr_rrtr()
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
if (track_desc_) {
|
|
|
|
|
return session_->send_rtcp_xr_rrtr(track_desc_->ssrc_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcRecvTrack::on_nack(SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
uint32_t ssrc = pkt->header.get_ssrc();
|
|
|
|
|
uint16_t seq = pkt->header.get_sequence();
|
|
|
|
|
|
|
|
|
|
// TODO: check whether is necessary?
|
|
|
|
|
nack_receiver_->remove_timeout_packets();
|
|
|
|
|
SrsRtpNackInfo* nack_info = nack_receiver_->find(seq);
|
|
|
|
|
if (nack_info) {
|
|
|
|
|
// seq had been received.
|
|
|
|
|
nack_receiver_->remove(seq);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// insert check nack list
|
|
|
|
|
uint16_t nack_first = 0, nack_last = 0;
|
|
|
|
|
if (!rtp_queue_->update(seq, nack_first, nack_last)) {
|
|
|
|
|
srs_warn("too old seq %u, range [%u, %u]", seq, rtp_queue_->begin, rtp_queue_->end);
|
|
|
|
|
}
|
|
|
|
|
if (srs_rtp_seq_distance(nack_first, nack_last) > 0) {
|
|
|
|
|
srs_trace("update seq=%u, nack range [%u, %u]", seq, nack_first, nack_last);
|
|
|
|
|
nack_receiver_->insert(nack_first, nack_last);
|
|
|
|
|
nack_receiver_->check_queue_size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// insert into video_queue and audio_queue
|
|
|
|
|
rtp_queue_->set(seq, pkt->copy());
|
|
|
|
|
// send_nack
|
|
|
|
|
session_->check_send_nacks(nack_receiver_, ssrc);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
return srs_success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcAudioRecvTrack::SrsRtcAudioRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc)
|
|
|
|
|
: SrsRtcRecvTrack(session, track_desc, true)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcAudioRecvTrack::~SrsRtcAudioRecvTrack()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcAudioRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
// uint8_t pt = pkt->header.get_payload_type();
|
|
|
|
|
|
|
|
|
|
// SrsRtcTrackDescription track = rtc_stream_desc_->get_audio_tracks();
|
|
|
|
|
// // process red packet.
|
|
|
|
|
// if (pt == red_pt) {
|
|
|
|
|
|
|
|
|
|
// } else if (pt == rtx_pt) { // process rtx_pt.
|
|
|
|
|
// // restore retranmission packet.
|
|
|
|
|
// } else if (pt == fec_pt) {
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
if (source) {
|
|
|
|
|
if ((err = source->on_rtp(pkt)) != srs_success) {
|
|
|
|
|
return srs_error_wrap(err, "source on rtp");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For NACK to handle packet.
|
|
|
|
|
if ((err = on_nack(pkt)) != srs_success) {
|
|
|
|
|
return srs_error_wrap(err, "on nack");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcVideoRecvTrack::SrsRtcVideoRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc)
|
|
|
|
|
: SrsRtcRecvTrack(session, track_desc, false)
|
|
|
|
|
{
|
|
|
|
|
request_key_frame_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcVideoRecvTrack::~SrsRtcVideoRecvTrack()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcVideoRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
pkt->frame_type = SrsFrameTypeVideo;
|
|
|
|
|
|
|
|
|
|
// TODO: FIXME: add rtp process
|
|
|
|
|
if (request_key_frame_) {
|
|
|
|
|
// TODO: FIXME: add coroutine to request key frame.
|
|
|
|
|
request_key_frame_ = false;
|
|
|
|
|
// TODO: FIXME: Check error.
|
|
|
|
|
session_->send_rtcp_fb_pli(track_desc_->ssrc_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (source) {
|
|
|
|
|
if ((err = source->on_rtp(pkt)) != srs_success) {
|
|
|
|
|
return srs_error_wrap(err, "source on rtp");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For NACK to handle packet.
|
|
|
|
|
if ((err = on_nack(pkt)) != srs_success) {
|
|
|
|
|
return srs_error_wrap(err, "on nack");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SrsRtcVideoRecvTrack::request_keyframe()
|
|
|
|
|
{
|
|
|
|
|
request_key_frame_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SrsRtcSendTrack::SrsRtcSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc, bool is_audio)
|
|
|
|
|
{
|
|
|
|
|
session_ = session;
|
|
|
|
|
track_desc_ = track_desc->copy();
|
|
|
|
|
if (is_audio) {
|
|
|
|
|
rtp_queue_ = new SrsRtpRingBuffer(100);
|
|
|
|
|
} else {
|
|
|
|
|
rtp_queue_ = new SrsRtpRingBuffer(1000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SrsRtcSendTrack::~SrsRtcSendTrack()
|
|
|
|
|
{
|
|
|
|
|
srs_freep(rtp_queue_);
|
|
|
|
|
srs_freep(track_desc_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SrsRtcSendTrack::has_ssrc(uint32_t ssrc)
|
|
|
|
|
{
|
|
|
|
|
if (track_desc_) {
|
|
|
|
|
return track_desc_->has_ssrc(ssrc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
SrsRtpPacket2* SrsRtcSendTrack::fetch_rtp_packet(uint16_t seq)
|
|
|
|
|
{
|
|
|
|
|
if (rtp_queue_) {
|
|
|
|
|
return rtp_queue_->at(seq);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcSendTrack::on_rtp(std::vector<SrsRtpPacket2*>& send_packets, SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
return srs_success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcSendTrack::on_rtcp(SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
return srs_success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcAudioSendTrack::SrsRtcAudioSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc)
|
|
|
|
|
: SrsRtcSendTrack(session, track_desc, true)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcAudioSendTrack::~SrsRtcAudioSendTrack()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcAudioSendTrack::on_rtp(std::vector<SrsRtpPacket2*>& send_packets, SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
pkt->header.set_ssrc(track_desc_->ssrc_);
|
|
|
|
|
pkt->header.set_payload_type(track_desc_->media_->pt_);
|
|
|
|
|
|
|
|
|
|
// Put rtp packet to NACK/ARQ queue
|
|
|
|
|
if (true) {
|
|
|
|
|
SrsRtpPacket2* nack = pkt->copy();
|
|
|
|
|
rtp_queue_->set(nack->header.get_sequence(), nack);
|
|
|
|
|
}
|
|
|
|
|
send_packets.push_back(pkt);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcAudioSendTrack::on_rtcp(SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
// process rtcp
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcVideoSendTrack::SrsRtcVideoSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc)
|
|
|
|
|
: SrsRtcSendTrack(session, track_desc, false)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SrsRtcVideoSendTrack::~SrsRtcVideoSendTrack()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcVideoSendTrack::on_rtp(std::vector<SrsRtpPacket2*>& send_packets, SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
|
|
|
|
|
pkt->header.set_ssrc(track_desc_->ssrc_);
|
|
|
|
|
pkt->header.set_payload_type(track_desc_->media_->pt_);
|
|
|
|
|
|
|
|
|
|
// Put rtp packet to NACK/ARQ queue
|
|
|
|
|
if (true) {
|
|
|
|
|
SrsRtpPacket2* nack = pkt->copy();
|
|
|
|
|
rtp_queue_->set(nack->header.get_sequence(), nack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
send_packets.push_back(pkt);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srs_error_t SrsRtcVideoSendTrack::on_rtcp(SrsRtpPacket2* pkt)
|
|
|
|
|
{
|
|
|
|
|
srs_error_t err = srs_success;
|
|
|
|
|
// process rtcp
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|