mirror of https://github.com/ossrs/srs.git
rtc publish, with debug code
parent
c654f1e06e
commit
a061d5c3db
@ -0,0 +1,390 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2020 John
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <srs_app_rtp_queue.hpp>
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_rtp.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_app_utility.hpp>
|
||||
|
||||
SrsRtpNackInfo::SrsRtpNackInfo()
|
||||
{
|
||||
generate_time_ = srs_update_system_time();
|
||||
pre_req_nack_time_ = 0;
|
||||
req_nack_count_ = 0;
|
||||
}
|
||||
|
||||
SrsRtpNackList::SrsRtpNackList(SrsRtpQueue* rtp_queue)
|
||||
{
|
||||
rtp_queue_ = rtp_queue;
|
||||
pre_check_time_ = 0;
|
||||
|
||||
srs_info("nack opt: max_count=%d, max_alive_time=%us, first_nack_interval=%ld, nack_interval=%ld"
|
||||
opts_.max_count, opts_.max_alive_time, opts.first_nack_interval, opts_.nack_interval);
|
||||
}
|
||||
|
||||
SrsRtpNackList::~SrsRtpNackList()
|
||||
{
|
||||
}
|
||||
|
||||
void SrsRtpNackList::insert(uint16_t seq)
|
||||
{
|
||||
// FIXME: full, drop packet, and request key frame.
|
||||
SrsRtpNackInfo& nack_info = queue_[seq];
|
||||
}
|
||||
|
||||
void SrsRtpNackList::remove(uint16_t seq)
|
||||
{
|
||||
queue_.erase(seq);
|
||||
}
|
||||
|
||||
SrsRtpNackInfo* SrsRtpNackList::find(uint16_t seq)
|
||||
{
|
||||
std::map<uint16_t, SrsRtpNackInfo>::iterator iter = queue_.find(seq);
|
||||
|
||||
if (iter == queue_.end()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &(iter->second);
|
||||
}
|
||||
|
||||
void SrsRtpNackList::dump()
|
||||
{
|
||||
return;
|
||||
srs_verbose("@debug, queue size=%u", queue_.size());
|
||||
for (std::map<uint16_t, SrsRtpNackInfo>::iterator iter = queue_.begin(); iter != queue_.end(); ++iter) {
|
||||
srs_verbose("@debug, nack seq=%u", iter->first);
|
||||
}
|
||||
}
|
||||
|
||||
void SrsRtpNackList::get_nack_seqs(vector<uint16_t>& seqs)
|
||||
{
|
||||
srs_utime_t now = srs_update_system_time();
|
||||
int interval = now - pre_check_time_;
|
||||
if (interval < opts_.nack_interval / 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
pre_check_time_ = now;
|
||||
std::map<uint16_t, SrsRtpNackInfo>::iterator iter = queue_.begin();
|
||||
while (iter != queue_.end()) {
|
||||
const uint16_t& seq = iter->first;
|
||||
SrsRtpNackInfo& nack_info = iter->second;
|
||||
|
||||
int alive_time = now - nack_info.generate_time_;
|
||||
if (alive_time > opts_.max_alive_time || nack_info.req_nack_count_ > opts_.max_count) {
|
||||
srs_verbose("NACK, drop seq=%u alive time %d bigger than max_alive_time=%d OR nack count %d bigger than %d",
|
||||
seq, alive_time, opts_.max_alive_time, nack_info.req_nack_count_, opts_.max_count);
|
||||
|
||||
rtp_queue_->notify_drop_seq(seq);
|
||||
queue_.erase(iter++);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO:Statistics unorder packet.
|
||||
if (now - nack_info.generate_time_ < opts_.first_nack_interval) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (now - nack_info.pre_req_nack_time_ >= opts_.nack_interval && nack_info.req_nack_count_ <= opts_.max_count) {
|
||||
++nack_info.req_nack_count_;
|
||||
nack_info.pre_req_nack_time_ = now;
|
||||
seqs.push_back(seq);
|
||||
srs_verbose("NACK, resend seq=%u, count=%d", seq, nack_info.req_nack_count_);
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void SrsRtpNackList::update_rtt(int rtt)
|
||||
{
|
||||
rtt_ = rtt;
|
||||
srs_verbose("NACK, update rtt from %ld to %d", opts_.nack_interval, rtt);
|
||||
// FIXME: limit min and max value.
|
||||
opts_.nack_interval = rtt_;
|
||||
}
|
||||
|
||||
SrsRtpQueue::SrsRtpQueue(size_t capacity, bool one_packet_per_frame)
|
||||
: nack_(this)
|
||||
{
|
||||
capacity_ = capacity;
|
||||
head_sequence_ = 0;
|
||||
highest_sequence_ = 0;
|
||||
initialized_ = false;
|
||||
start_collected_ = false;
|
||||
queue_ = new SrsRtpSharedPacket*[capacity_];
|
||||
memset(queue_, 0, sizeof(SrsRtpSharedPacket*) * capacity);
|
||||
|
||||
cycle_ = 0;
|
||||
jitter_ = 0;
|
||||
last_trans_time_ = 0;
|
||||
|
||||
pre_number_of_packet_received_ = 0;
|
||||
pre_number_of_packet_lossed_ = 0;
|
||||
|
||||
num_of_packet_received_ = 0;
|
||||
number_of_packet_lossed_ = 0;
|
||||
|
||||
one_packet_per_frame_ = one_packet_per_frame;
|
||||
}
|
||||
|
||||
SrsRtpQueue::~SrsRtpQueue()
|
||||
{
|
||||
srs_freepa(queue_);
|
||||
}
|
||||
|
||||
srs_error_t SrsRtpQueue::insert(SrsRtpSharedPacket* rtp_pkt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
uint16_t seq = rtp_pkt->rtp_header.get_sequence();
|
||||
|
||||
srs_utime_t now = srs_update_system_time();
|
||||
|
||||
// First packet recv, init head_sequence and highest_sequence.
|
||||
if (! initialized_) {
|
||||
initialized_ = true;
|
||||
head_sequence_ = seq;
|
||||
highest_sequence_ = seq;
|
||||
|
||||
++num_of_packet_received_;
|
||||
|
||||
last_trans_time_ = now/1000 - rtp_pkt->rtp_header.get_timestamp()/90;
|
||||
} else {
|
||||
SrsRtpNackInfo* nack_info = NULL;
|
||||
if ((nack_info = nack_.find(seq)) != NULL) {
|
||||
srs_verbose("seq=%u, alive time=%d, nack count=%d, rtx success", seq, now - nack_info->generate_time_, nack_info->req_nack_count_);
|
||||
nack_.remove(seq);
|
||||
} else {
|
||||
// Calc jitter.
|
||||
{
|
||||
int trans_time = now/1000 - rtp_pkt->rtp_header.get_timestamp()/90;
|
||||
|
||||
int cur_jitter = trans_time - last_trans_time_;
|
||||
if (cur_jitter < 0) {
|
||||
cur_jitter = -cur_jitter;
|
||||
}
|
||||
|
||||
last_trans_time_ = trans_time;
|
||||
|
||||
jitter_ = (jitter_ * 15.0 / 16.0) + (static_cast<double>(cur_jitter) / 16.0);
|
||||
srs_verbose("jitter=%.2f", jitter_);
|
||||
}
|
||||
|
||||
++num_of_packet_received_;
|
||||
// seq > highest_sequence_
|
||||
if (seq_cmp(highest_sequence_, seq)) {
|
||||
insert_into_nack_list(highest_sequence_ + 1, seq);
|
||||
|
||||
if (seq < highest_sequence_) {
|
||||
srs_verbose("warp around, cycle=%lu", cycle_);
|
||||
++cycle_;
|
||||
}
|
||||
highest_sequence_ = seq;
|
||||
} else {
|
||||
// Because we don't know the ISN(initiazlie sequence number), the first packet
|
||||
// we received maybe no the first paacet client sented.
|
||||
if (! start_collected_) {
|
||||
if (seq_cmp(seq, head_sequence_)) {
|
||||
srs_info("head seq=%u, cur seq=%u, update head seq because recv less than it.", head_sequence_, seq);
|
||||
head_sequence_ = seq;
|
||||
}
|
||||
insert_into_nack_list(seq + 1, highest_sequence_);
|
||||
} else {
|
||||
srs_verbose("seq=%u, rtx success, too old", seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int delay = highest_sequence_ - head_sequence_ + 1;
|
||||
srs_verbose("seqs range=[%u-%u], delay=%d", head_sequence_, highest_sequence_, delay);
|
||||
|
||||
// Check seqs out of range.
|
||||
if (head_sequence_ + capacity_ < highest_sequence_) {
|
||||
srs_verbose("try collect packet becuase seq out of range");
|
||||
collect_packet();
|
||||
}
|
||||
while (head_sequence_ + capacity_ < highest_sequence_) {
|
||||
srs_trace("seqs out of range, head seq=%u, hightest seq=%u", head_sequence_, highest_sequence_);
|
||||
remove(head_sequence_);
|
||||
uint16_t s = head_sequence_ + 1;
|
||||
for ( ; s != highest_sequence_; ++s) {
|
||||
SrsRtpSharedPacket*& pkt = queue_[s % capacity_];
|
||||
// Choose the new head sequence. Must be the first packet of frame.
|
||||
if (pkt && pkt->rtp_payload_header->is_first_packet_of_frame) {
|
||||
srs_trace("find except, update head seq from %u to %u when seqs out of range", head_sequence_, s);
|
||||
head_sequence_ = s;
|
||||
break;
|
||||
}
|
||||
|
||||
// Drop the seq.
|
||||
nack_.remove(s);
|
||||
srs_verbose("seqs out of range, drop seq=%u", s);
|
||||
if (pkt && pkt->rtp_header.get_sequence() == s) {
|
||||
delete pkt;
|
||||
pkt = NULL;
|
||||
}
|
||||
}
|
||||
srs_trace("force update, update head seq from %u to %u when seqs out of range", head_sequence_, s);
|
||||
head_sequence_ = s;
|
||||
}
|
||||
|
||||
SrsRtpSharedPacket* old_pkt = queue_[seq % capacity_];
|
||||
if (old_pkt) {
|
||||
delete old_pkt;
|
||||
}
|
||||
|
||||
queue_[seq % capacity_] = rtp_pkt->copy();
|
||||
|
||||
// Marker bit means the last packet of frame received.
|
||||
if (rtp_pkt->rtp_header.get_marker() || (highest_sequence_ - head_sequence_ >= capacity_ / 2) || one_packet_per_frame_) {
|
||||
collect_packet();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtpQueue::remove(uint16_t seq)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRtpSharedPacket*& pkt = queue_[seq % capacity_];
|
||||
if (pkt && pkt->rtp_header.get_sequence() == seq) {
|
||||
delete pkt;
|
||||
pkt = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsRtpQueue::get_and_clean_collected_frames(std::vector<std::vector<SrsRtpSharedPacket*> >& frames)
|
||||
{
|
||||
frames.swap(frames_);
|
||||
}
|
||||
|
||||
void SrsRtpQueue::notify_drop_seq(uint16_t seq)
|
||||
{
|
||||
uint16_t s = seq + 1;
|
||||
for ( ; s != highest_sequence_; ++s) {
|
||||
SrsRtpSharedPacket* pkt = queue_[s % capacity_];
|
||||
if (pkt && pkt->rtp_payload_header->is_first_packet_of_frame) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
srs_verbose("drop seq=%u, highest seq=%u, update head seq %u to %u", seq, highest_sequence_, head_sequence_, s);
|
||||
head_sequence_ = s;
|
||||
}
|
||||
|
||||
uint32_t SrsRtpQueue::get_extended_highest_sequence()
|
||||
{
|
||||
return cycle_ * 65536 + highest_sequence_;
|
||||
}
|
||||
|
||||
uint8_t SrsRtpQueue::get_fraction_lost()
|
||||
{
|
||||
int64_t total = (number_of_packet_lossed_ - pre_number_of_packet_lossed_ + num_of_packet_received_ - pre_number_of_packet_received_);
|
||||
uint8_t loss = 0;
|
||||
if (total > 0) {
|
||||
loss = (number_of_packet_lossed_ - pre_number_of_packet_lossed_) * 256 / total;
|
||||
}
|
||||
|
||||
pre_number_of_packet_lossed_ = number_of_packet_lossed_;
|
||||
pre_number_of_packet_received_ = num_of_packet_received_;
|
||||
|
||||
return loss;
|
||||
}
|
||||
|
||||
uint32_t SrsRtpQueue::get_cumulative_number_of_packets_lost()
|
||||
{
|
||||
return number_of_packet_lossed_;
|
||||
}
|
||||
|
||||
uint32_t SrsRtpQueue::get_interarrival_jitter()
|
||||
{
|
||||
return static_cast<uint32_t>(jitter_);
|
||||
}
|
||||
|
||||
void SrsRtpQueue::update_rtt(int rtt)
|
||||
{
|
||||
nack_.update_rtt(rtt);
|
||||
}
|
||||
|
||||
void SrsRtpQueue::insert_into_nack_list(uint16_t seq_start, uint16_t seq_end)
|
||||
{
|
||||
for (uint16_t s = seq_start; s != seq_end; ++s) {
|
||||
srs_verbose("loss seq=%u, insert into nack list", s);
|
||||
nack_.insert(s);
|
||||
++number_of_packet_lossed_;
|
||||
}
|
||||
|
||||
// FIXME: Record key frame sequence.
|
||||
// FIXME: When nack list too long, clear and send PLI.
|
||||
}
|
||||
|
||||
void SrsRtpQueue::collect_packet()
|
||||
{
|
||||
vector<SrsRtpSharedPacket*> frame;
|
||||
for (uint16_t s = head_sequence_; s != highest_sequence_; ++s) {
|
||||
SrsRtpSharedPacket* pkt = queue_[s % capacity_];
|
||||
|
||||
nack_.dump();
|
||||
|
||||
if (nack_.find(s) != NULL) {
|
||||
srs_verbose("seq=%u, found in nack list when collect frame", s);
|
||||
break;
|
||||
}
|
||||
|
||||
// We must collect frame from first packet to last packet.
|
||||
if (s == head_sequence_ && pkt->rtp_payload_size() != 0 && ! pkt->rtp_payload_header->is_first_packet_of_frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
frame.push_back(pkt->copy());
|
||||
if (pkt->rtp_header.get_marker() || one_packet_per_frame_) {
|
||||
if (! start_collected_) {
|
||||
start_collected_ = true;
|
||||
}
|
||||
frames_.push_back(frame);
|
||||
frame.clear();
|
||||
|
||||
srs_verbose("head seq=%u, update to %u because collect one full farme", head_sequence_, s + 1);
|
||||
head_sequence_ = s + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// remove the tmp buffer
|
||||
for (size_t i = 0; i < frame.size(); ++i) {
|
||||
srs_freep(frame[i]);
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2020 John
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SRS_APP_RTP_QUEUE_HPP
|
||||
#define SRS_APP_RTP_QUEUE_HPP
|
||||
|
||||
#include <srs_core.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class SrsRtpSharedPacket;
|
||||
class SrsRtpQueue;
|
||||
|
||||
struct SrsNackOption
|
||||
{
|
||||
SrsNackOption()
|
||||
{
|
||||
// Default nack option.
|
||||
max_count = 5;
|
||||
max_alive_time = 2 * SRS_UTIME_SECONDS;
|
||||
first_nack_interval = 10 * SRS_UTIME_MILLISECONDS;
|
||||
nack_interval = 400 * SRS_UTIME_MILLISECONDS;
|
||||
}
|
||||
int max_count;
|
||||
srs_utime_t max_alive_time;
|
||||
int64_t first_nack_interval;
|
||||
int64_t nack_interval;
|
||||
};
|
||||
|
||||
struct SrsRtpNackInfo
|
||||
{
|
||||
SrsRtpNackInfo();
|
||||
|
||||
// Use to control the time of first nack req and the life of seq.
|
||||
srs_utime_t generate_time_;
|
||||
// Use to control nack interval.
|
||||
srs_utime_t pre_req_nack_time_;
|
||||
// Use to control nack times.
|
||||
int req_nack_count_;
|
||||
};
|
||||
|
||||
inline bool seq_cmp(const uint16_t& l, const uint16_t& r)
|
||||
{
|
||||
return ((int16_t)(r - l)) > 0;
|
||||
}
|
||||
|
||||
struct SeqComp
|
||||
{
|
||||
bool operator()(const uint16_t& l, const uint16_t& r) const
|
||||
{
|
||||
return seq_cmp(l, r);
|
||||
}
|
||||
};
|
||||
|
||||
class SrsRtpNackList
|
||||
{
|
||||
private:
|
||||
// Nack queue, seq order, oldest to newest.
|
||||
std::map<uint16_t, SrsRtpNackInfo, SeqComp> queue_;
|
||||
SrsRtpQueue* rtp_queue_;
|
||||
SrsNackOption opts_;
|
||||
private:
|
||||
srs_utime_t pre_check_time_;
|
||||
private:
|
||||
int rtt_;
|
||||
public:
|
||||
SrsRtpNackList(SrsRtpQueue* rtp_queue);
|
||||
virtual ~SrsRtpNackList();
|
||||
public:
|
||||
void insert(uint16_t seq);
|
||||
void remove(uint16_t seq);
|
||||
SrsRtpNackInfo* find(uint16_t seq);
|
||||
public:
|
||||
void dump();
|
||||
public:
|
||||
void get_nack_seqs(std::vector<uint16_t>& seqs);
|
||||
public:
|
||||
void update_rtt(int rtt);
|
||||
};
|
||||
|
||||
class SrsRtpQueue
|
||||
{
|
||||
private:
|
||||
/*
|
||||
*[seq1|seq2|seq3|seq4|seq5 ... seq10|seq11(loss)|seq12(loss)|seq13]
|
||||
* \___(head_sequence_) \ \___(highest_sequence_)
|
||||
* \___(no received, in nack list)
|
||||
*/
|
||||
// Capacity of the ring-buffer.
|
||||
size_t capacity_;
|
||||
// Thei highest sequence we have receive.
|
||||
uint16_t highest_sequence_;
|
||||
// The sequence waitting to read.
|
||||
uint16_t head_sequence_;
|
||||
bool initialized_;
|
||||
bool start_collected_;
|
||||
// Ring bufer.
|
||||
SrsRtpSharedPacket** queue_;
|
||||
private:
|
||||
uint64_t cycle_;
|
||||
double jitter_;
|
||||
int64_t last_trans_time_;
|
||||
uint64_t pre_number_of_packet_received_;
|
||||
uint64_t pre_number_of_packet_lossed_;
|
||||
uint64_t num_of_packet_received_;
|
||||
uint64_t number_of_packet_lossed_;
|
||||
private:
|
||||
bool one_packet_per_frame_;
|
||||
public:
|
||||
SrsRtpNackList nack_;
|
||||
private:
|
||||
std::vector<std::vector<SrsRtpSharedPacket*> > frames_;
|
||||
public:
|
||||
SrsRtpQueue(size_t capacity = 1024, bool one_packet_per_frame = false);
|
||||
virtual ~SrsRtpQueue();
|
||||
public:
|
||||
srs_error_t insert(SrsRtpSharedPacket* rtp_pkt);
|
||||
srs_error_t remove(uint16_t seq);
|
||||
public:
|
||||
void get_and_clean_collected_frames(std::vector<std::vector<SrsRtpSharedPacket*> >& frames);
|
||||
void notify_drop_seq(uint16_t seq);
|
||||
public:
|
||||
uint32_t get_extended_highest_sequence();
|
||||
uint8_t get_fraction_lost();
|
||||
uint32_t get_cumulative_number_of_packets_lost();
|
||||
uint32_t get_interarrival_jitter();
|
||||
public:
|
||||
void update_rtt(int rtt);
|
||||
private:
|
||||
void insert_into_nack_list(uint16_t seq_start, uint16_t seq_end);
|
||||
private:
|
||||
void collect_packet();
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue