重写rtcp框架

This commit is contained in:
xiongziliang 2021-01-31 19:18:17 +08:00
parent 629c39685b
commit 5c6560f55d
16 changed files with 1617 additions and 337 deletions

41
src/Common/macros.h Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_MACROS_H
#define ZLMEDIAKIT_MACROS_H
#if defined(__MACH__)
#include <arpa/inet.h>
#include <machine/endian.h>
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#elif defined(__linux__)
#include <endian.h>
#include <arpa/inet.h>
#elif defined(_WIN32)
#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0
#define BYTE_ORDER LITTLE_ENDIAN
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#endif
#ifndef PACKED
#if !defined(_WIN32)
#define PACKED __attribute__((packed))
#else
#define PACKED
#endif //!defined(_WIN32)
#endif
#endif //ZLMEDIAKIT_MACROS_H

405
src/Rtcp/Rtcp.cpp Normal file
View File

@ -0,0 +1,405 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <stddef.h>
#include "Rtcp.h"
#include "Util/logger.h"
namespace mediakit {
const char *rtcpTypeToStr(RtcpType type){
switch (type){
#define SWITCH_CASE(key, value) case RtcpType::key : return #value "(" #key ")";
RTCP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE
default: return "unknown rtcp pt";
}
}
const char *sdesTypeToStr(SdesType type){
switch (type){
#define SWITCH_CASE(key, value) case SdesType::key : return #value "(" #key ")";
SDES_TYPE_MAP(SWITCH_CASE)
#undef SWITCH_CASE
default: return "unknown source description type";
}
}
static size_t alignSize(size_t bytes) {
return 4 * (size_t)((bytes + 3) / 4);
}
static void setupHeader(RtcpHeader *rtcp, RtcpType type, size_t report_count, size_t total_bytes) {
rtcp->version = 2;
rtcp->padding = 0;
if (report_count > 0x1F) {
throw std::invalid_argument(StrPrinter << "rtcp report_count最大赋值为31,当前为:" << report_count);
}
//items总个数
rtcp->report_count = report_count;
rtcp->pt = (uint8_t) type;
//不包含rtcp头的长度
rtcp->length = htons((uint16_t)((total_bytes / 4) - 1));
}
/////////////////////////////////////////////////////////////////////////////
void RtcpHeader::net2Host() {
length = ntohs(length);
}
string RtcpHeader::dumpHeader() const{
_StrPrinter printer;
printer << "version:" << version << "\r\n";
printer << "padding:" << padding << "\r\n";
printer << "report_count:" << report_count << "\r\n";
printer << "pt:" << rtcpTypeToStr((RtcpType)pt) << "\r\n";
printer << "length:" << length << "\r\n";
printer << "--------\r\n";
return printer;
}
string RtcpHeader::dumpString() const {
switch ((RtcpType)pt) {
case RtcpType::RTCP_SR: {
RtcpSR *rtcp = (RtcpSR *)this;
return rtcp->dumpString();
}
case RtcpType::RTCP_RR: {
RtcpRR *rtcp = (RtcpRR *)this;
return rtcp->dumpString();
}
case RtcpType::RTCP_SDES: {
RtcpSdes *rtcp = (RtcpSdes *)this;
return rtcp->dumpString();
}
default: return StrPrinter << dumpHeader() << hexdump((char *)this + sizeof(*this), length * 4);
}
}
void RtcpHeader::net2Host(size_t len){
switch ((RtcpType)pt) {
case RtcpType::RTCP_SR: {
RtcpSR *sr = (RtcpSR *)this;
sr->net2Host(len);
break;
}
case RtcpType::RTCP_RR: {
RtcpRR *rr = (RtcpRR *)this;
rr->net2Host(len);
break;
}
case RtcpType::RTCP_SDES: {
RtcpSdes *sdes = (RtcpSdes *)this;
sdes->net2Host(len);
break;
}
default: throw std::runtime_error(StrPrinter << "未处理的rtcp包:" << rtcpTypeToStr((RtcpType) this->pt));
}
}
vector<RtcpHeader *> RtcpHeader::loadFromBytes(char *data, size_t len){
vector<RtcpHeader *> ret;
ssize_t remain = len;
char *ptr = data;
while (remain > (ssize_t) sizeof(RtcpHeader)) {
RtcpHeader *rtcp = (RtcpHeader *) ptr;
auto rtcp_len = 4 * (1 + ntohs(rtcp->length));
try {
rtcp->net2Host(rtcp_len);
ret.emplace_back(rtcp);
} catch (std::exception &ex) {
//不能处理的rtcp包或者无法解析的rtcp包忽略掉
WarnL << ex.what();
}
ptr += rtcp_len;
remain -= rtcp_len;
}
return ret;
}
class BufferRtcp : public Buffer {
public:
BufferRtcp(std::shared_ptr<RtcpHeader> rtcp) {
_rtcp = std::move(rtcp);
_size = (htons(_rtcp->length) + 1) * 4;
}
~BufferRtcp() override {}
char *data() const override {
return (char *) _rtcp.get();
}
size_t size() const override {
return _size;
}
private:
std::size_t _size;
std::shared_ptr<RtcpHeader> _rtcp;
};
Buffer::Ptr RtcpHeader::toBuffer(std::shared_ptr<RtcpHeader> rtcp) {
return std::make_shared<BufferRtcp>(std::move(rtcp));
}
/////////////////////////////////////////////////////////////////////////////
std::shared_ptr<RtcpSR> RtcpSR::create(size_t item_count) {
auto bytes = alignSize(sizeof(RtcpSR) - sizeof(ReportItem) + item_count * sizeof(ReportItem));
auto ptr = (RtcpSR *) new char[bytes];
setupHeader(ptr, RtcpType::RTCP_SR, item_count, bytes);
return std::shared_ptr<RtcpSR>(ptr, [](RtcpSR *ptr) {
delete[] (char *) ptr;
});
}
string RtcpSR::getNtpStamp() const{
struct timeval tv;
tv.tv_sec = ntpmsw - 0x83AA7E80;
tv.tv_usec = (decltype(tv.tv_usec))(ntplsw / ((double) (((uint64_t) 1) << 32) * 1.0e-6));
return LogChannel::printTime(tv);
}
void RtcpSR::setNtpStamp(struct timeval tv) {
ntpmsw = htonl(tv.tv_sec + 0x83AA7E80); /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */
ntplsw = htonl((uint32_t) ((double) tv.tv_usec * (double) (((uint64_t) 1) << 32) * 1.0e-6));
}
string RtcpSR::dumpString() const{
_StrPrinter printer;
printer << RtcpHeader::dumpHeader();
printer << "ssrc:" << ssrc << "\r\n";
printer << "ntpmsw:" << ntpmsw << "\r\n";
printer << "ntplsw:" << ntplsw << "\r\n";
printer << "ntp time:" << getNtpStamp() << "\r\n";
printer << "rtpts:" << rtpts << "\r\n";
printer << "packet_count:" << packet_count << "\r\n";
printer << "octet_count:" << octet_count << "\r\n";
auto items = ((RtcpSR *)this)->getItemList();
auto i = 0;
for (auto &item : items) {
printer << "---- item:" << i++ << " ----\r\n";
printer << item->dumpString();
}
return printer;
}
#define CHECK_MIN_SIZE(size, kMinSize) \
if (size < kMinSize) { \
throw std::out_of_range(StrPrinter << rtcpTypeToStr((RtcpType)pt) << " 长度不足:" << size << " < " << kMinSize); \
}
#define CHECK_LENGTH(size, item_count) \
/*修正个数防止getItemList时内存越界*/ \
if (report_count != item_count) { \
WarnL << rtcpTypeToStr((RtcpType)pt) << " report_count 字段不正确,已修正为:" << (int)report_count << " -> " << item_count; \
report_count = item_count; \
} \
if ((size_t) (length + 1) * 4 != size) { \
WarnL << rtcpTypeToStr((RtcpType)pt) << " length字段不正确:" << (size_t) (length + 1) * 4 << " != " << size; \
}
void RtcpSR::net2Host(size_t size) {
static const size_t kMinSize = sizeof(RtcpSR) - sizeof(items);
CHECK_MIN_SIZE(size, kMinSize);
RtcpHeader::net2Host();
ssrc = ntohl(ssrc);
ntpmsw = ntohl(ntpmsw);
ntplsw = ntohl(ntplsw);
rtpts = ntohl(rtpts);
packet_count = ntohl(packet_count);
octet_count = ntohl(octet_count);
ReportItem *ptr = &items;
int item_count = 0;
for(int i = 0; i < (int)report_count && (char *)(ptr) + sizeof(ReportItem) <= (char *)(this) + size; ++i){
ptr->net2Host();
++ptr;
++item_count;
}
CHECK_LENGTH(size, item_count);
}
vector<ReportItem*> RtcpSR::getItemList(){
vector<ReportItem *> ret;
ReportItem *ptr = &items;
for (int i = 0; i < (int) report_count; ++i) {
ret.emplace_back(ptr);
++ptr;
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////
string ReportItem::dumpString() const{
_StrPrinter printer;
printer << "ssrc:" << ssrc << "\r\n";
printer << "fraction:" << fraction << "\r\n";
printer << "cumulative:" << cumulative << "\r\n";
printer << "seq_cycles:" << seq_cycles << "\r\n";
printer << "seq_max:" << seq_max << "\r\n";
printer << "jitter:" << jitter << "\r\n";
printer << "last_sr_stamp:" << last_sr_stamp << "\r\n";
printer << "delay_since_last_sr:" << delay_since_last_sr << "\r\n";
return printer;
}
void ReportItem::net2Host() {
ssrc = ntohl(ssrc);
cumulative = ntohl(cumulative ) >> 8;
seq_cycles = ntohs(seq_cycles);
seq_max = ntohs(seq_max);
jitter = ntohl(jitter);
last_sr_stamp = ntohl(last_sr_stamp);
delay_since_last_sr = ntohl(delay_since_last_sr);
}
/////////////////////////////////////////////////////////////////////////////
std::shared_ptr<RtcpRR> RtcpRR::create(size_t item_count) {
auto bytes = alignSize(sizeof(RtcpRR) - sizeof(ReportItem) + item_count * sizeof(ReportItem));
auto ptr = (RtcpRR *) new char[bytes];
setupHeader(ptr, RtcpType::RTCP_RR, item_count, bytes);
return std::shared_ptr<RtcpRR>(ptr, [](RtcpRR *ptr) {
delete[] (char *) ptr;
});
}
string RtcpRR::dumpString() const{
_StrPrinter printer;
printer << RtcpHeader::dumpHeader();
printer << "ssrc:" << ssrc << "\r\n";
auto items = ((RtcpRR *)this)->getItemList();
auto i = 0;
for (auto &item : items) {
printer << "---- item:" << i++ << " ----\r\n";
printer << item->dumpString();
}
return printer;
}
void RtcpRR::net2Host(size_t size) {
static const size_t kMinSize = sizeof(RtcpRR) - sizeof(items);
CHECK_MIN_SIZE(size, kMinSize);
RtcpHeader::net2Host();
ssrc = ntohl(ssrc);
ReportItem *ptr = &items;
int item_count = 0;
for(int i = 0; i < (int)report_count && (char *)(ptr) + sizeof(ReportItem) <= (char *)(this) + size; ++i){
ptr->net2Host();
++ptr;
++item_count;
}
CHECK_LENGTH(size, item_count);
}
vector<ReportItem*> RtcpRR::getItemList() {
vector<ReportItem *> ret;
ReportItem *ptr = &items;
for (int i = 0; i < (int) report_count; ++i) {
ret.emplace_back(ptr);
++ptr;
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////
void SdesItem::net2Host() {
ssrc = ntohl(ssrc);
}
size_t SdesItem::totalBytes() const{
return alignSize(minSize() + length);
}
size_t SdesItem::minSize() {
return sizeof(SdesItem) - sizeof(text);
}
string SdesItem::dumpString() const{
_StrPrinter printer;
printer << "ssrc:" << ssrc << "\r\n";
printer << "type:" << sdesTypeToStr((SdesType) type) << "\r\n";
printer << "length:" << (int) length << "\r\n";
printer << "text:" << (length ? string(&text, length) : "") << "\r\n";
return printer;
}
/////////////////////////////////////////////////////////////////////////////
std::shared_ptr<RtcpSdes> RtcpSdes::create(const std::initializer_list<string> &item_text) {
size_t item_total_size = 0;
for (auto &text : item_text) {
//统计所有SdesItem对象占用的空间
item_total_size += alignSize(SdesItem::minSize() + (0xFF & text.size()));
}
auto bytes = alignSize(sizeof(RtcpSdes) - sizeof(SdesItem) + item_total_size);
auto ptr = (RtcpSdes *) new char[bytes];
auto item_ptr = &ptr->items;
for (auto &text : item_text) {
item_ptr->length = (0xFF & text.size());
//确保赋值\0为RTCP_SDES_END
memcpy(&(item_ptr->text), text.data(), item_ptr->length + 1);
item_ptr = (SdesItem *) ((char *) item_ptr + item_ptr->totalBytes());
}
setupHeader(ptr, RtcpType::RTCP_SDES, item_text.size(), bytes);
return std::shared_ptr<RtcpSdes>(ptr, [](RtcpSdes *ptr) {
delete [] (char *) ptr;
});
}
string RtcpSdes::dumpString() const {
_StrPrinter printer;
printer << RtcpHeader::dumpHeader();
auto items = ((RtcpSdes *)this)->getItemList();
auto i = 0;
for (auto &item : items) {
printer << "---- item:" << i++ << " ----\r\n";
printer << item->dumpString();
}
return printer;
}
void RtcpSdes::net2Host(size_t size) {
static const size_t kMinSize = sizeof(RtcpSdes) - sizeof(items);
CHECK_MIN_SIZE(size, kMinSize);
RtcpHeader::net2Host();
SdesItem *ptr = &items;
int item_count = 0;
for(int i = 0; i < (int)report_count && (char *)(ptr) + SdesItem::minSize() <= (char *)(this) + size; ++i){
ptr->net2Host();
ptr = (SdesItem *) ((char *) ptr + ptr->totalBytes());
++item_count;
}
CHECK_LENGTH(size, item_count);
}
vector<SdesItem *> RtcpSdes::getItemList() {
vector<SdesItem *> ret;
SdesItem *ptr = &items;
for (int i = 0; i < (int) report_count; ++i) {
ret.emplace_back(ptr);
ptr = (SdesItem *) ((char *) ptr + ptr->totalBytes());
}
return ret;
}
}//namespace mediakit

464
src/Rtcp/Rtcp.h Normal file
View File

@ -0,0 +1,464 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_RTCP_H
#define ZLMEDIAKIT_RTCP_H
#include <stdint.h>
#include <vector>
#include "Util/util.h"
#include "Network/Buffer.h"
#include "Common/macros.h"
using namespace std;
using namespace toolkit;
namespace mediakit {
#if defined(_WIN32)
#pragma pack(push, 1)
#endif // defined(_WIN32)
//https://datatracker.ietf.org/doc/rfc3550
#define RTCP_PT_MAP(XX) \
XX(RTCP_FIR, 192) \
XX(RTCP_NACK, 193) \
XX(RTCP_SMPTETC, 194) \
XX(RTCP_IJ, 195) \
XX(RTCP_SR, 200) \
XX(RTCP_RR, 201) \
XX(RTCP_SDES, 202) \
XX(RTCP_BYE, 203) \
XX(RTCP_APP, 204) \
XX(RTCP_RTPFB, 205) \
XX(RTCP_PSFB, 206) \
XX(RTCP_XR, 207) \
XX(RTCP_AVB, 208) \
XX(RTCP_RSI, 209) \
XX(RTCP_TOKEN, 210)
#define SDES_TYPE_MAP(XX) \
XX(RTCP_SDES_END, 0) \
XX(RTCP_SDES_CNAME, 1) \
XX(RTCP_SDES_NAME, 2) \
XX(RTCP_SDES_EMAIL, 3) \
XX(RTCP_SDES_PHONE, 4) \
XX(RTCP_SDES_LOC, 5) \
XX(RTCP_SDES_TOOL, 6) \
XX(RTCP_SDES_NOTE, 7) \
XX(RTCP_SDES_PRIVATE, 8)
//rtcp类型枚举
enum class RtcpType : uint8_t {
#define XX(key, value) key = value,
RTCP_PT_MAP(XX)
#undef XX
};
//sdes类型枚举
enum class SdesType : uint8_t {
#define XX(key, value) key = value,
SDES_TYPE_MAP(XX)
#undef XX
};
/**
* RtcpType转描述字符串
*/
const char *rtcpTypeToStr(RtcpType type);
/**
* SdesType枚举转描述字符串
*/
const char *sdesTypeToStr(SdesType type);
class RtcpHeader {
public:
#if __BYTE_ORDER == __BIG_ENDIAN
//版本号固定为2
uint32_t version: 2;
//padding固定为0
uint32_t padding: 1;
//reception report count
uint32_t report_count: 5;
#else
//reception report count
uint32_t report_count: 5;
//padding固定为0
uint32_t padding: 1;
//版本号固定为2
uint32_t version: 2;
#endif
//rtcp类型,RtcpType
uint32_t pt: 8;
//长度
uint32_t length: 16;
public:
/**
* rtcp并转换网络字节序为主机字节序RtcpHeader派生类列表
* @param data
* @param size
* @return rtcp对象列表free
*/
static vector<RtcpHeader *> loadFromBytes(char *data, size_t size);
/**
* rtcp包转Buffer对象
* @param rtcp rtcp包对象智能指针
* @return Buffer对象
*/
static Buffer::Ptr toBuffer(std::shared_ptr<RtcpHeader> rtcp);
/**
* rtcp相关字段详情(dumpString函数)
*
* 使net2Host转换成主机字节序后才可使用此函数
*/
string dumpString() const;
protected:
/**
*
*/
void net2Host();
/**
*
* 使net2Host转换成主机字节序后才可使用此函数
*/
string dumpHeader() const;
private:
/**
* net2Host函数
* @param size rtcp字符长度
*/
void net2Host(size_t size);
} PACKED;
/////////////////////////////////////////////////////////////////////////////
//ReportBlock
class ReportItem {
public:
friend class RtcpSR;
friend class RtcpRR;
uint32_t ssrc;
//Fraction lost
uint32_t fraction: 8;
//Cumulative number of packets lost
uint32_t cumulative: 24;
//Sequence number cycles count
uint16_t seq_cycles;
//Highest sequence number received
uint16_t seq_max;
//Interarrival jitter
uint32_t jitter;
//Last SR timestamp, NTP timestamp,(ntpmsw & 0xFFFF) << 16 | (ntplsw >> 16) & 0xFFFF)
uint32_t last_sr_stamp;
//Delay since last SR timestamp,expressed in units of 1/65536 seconds
uint32_t delay_since_last_sr;
private:
/**
*
* 使net2Host转换成主机字节序后才可使用此函数
*/
string dumpString() const;
/**
*
*/
void net2Host();
}PACKED;
/*
* 6.4.1 SR: Sender Report RTCP Packet
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P| RC | PT=SR=200 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of sender |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
sender | NTP timestamp, most significant word |
info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NTP timestamp, least significant word |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sender's packet count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sender's octet count |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report | SSRC_1 (SSRC of first source) |
block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1 | fraction lost | cumulative number of packets lost |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| extended highest sequence number received |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| interarrival jitter |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| last SR (LSR) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| delay since last SR (DLSR) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report | SSRC_2 (SSRC of second source) |
block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2 : ... :
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| profile-specific extensions |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
// sender report
class RtcpSR : public RtcpHeader {
public:
friend class RtcpHeader;
uint32_t ssrc;
// ntp timestamp MSW(in second)
uint32_t ntpmsw;
// ntp timestamp LSW(in picosecond)
uint32_t ntplsw;
// rtp timestamp
uint32_t rtpts;
// sender packet count
uint32_t packet_count;
// sender octet count
uint32_t octet_count;
//可能有很多个
ReportItem items;
public:
/**
* SR包RtcpHeader部分()
* @param item_count ReportItem对象个数
* @return SR包
*/
static std::shared_ptr<RtcpSR> create(size_t item_count);
/**
* ntpmsw与ntplsw字段为网络字节序
* @param tv
*/
void setNtpStamp(struct timeval tv);
/**
* ntp时间的字符串
* 使net2Host转换成主机字节序后才可使用此函数
*/
string getNtpStamp() const;
/**
* ReportItem对象指针列表
* 使net2Host转换成主机字节序后才可使用此函数
*/
vector<ReportItem*> getItemList();
private:
/**
*
* 使net2Host转换成主机字节序后才可使用此函数
*/
string dumpString() const;
/**
*
* @param size
*/
void net2Host(size_t size);
} PACKED;
/////////////////////////////////////////////////////////////////////////////
/*
* 6.4.2 RR: Receiver Report RTCP Packet
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P| RC | PT=RR=201 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of packet sender |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report | SSRC_1 (SSRC of first source) |
block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1 | fraction lost | cumulative number of packets lost |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| extended highest sequence number received |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| interarrival jitter |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| last SR (LSR) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| delay since last SR (DLSR) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report | SSRC_2 (SSRC of second source) |
block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2 : ... :
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| profile-specific extensions |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
//Receiver Report
class RtcpRR : public RtcpHeader {
public:
friend class RtcpHeader;
uint32_t ssrc;
//可能有很多个
ReportItem items;
public:
/**
* RR包RtcpHeader部分
* @param item_count ReportItem对象个数
* @return RR包
*/
static std::shared_ptr<RtcpRR> create(size_t item_count);
/**
* ReportItem对象指针列表
* 使net2Host转换成主机字节序后才可使用此函数
*/
vector<ReportItem*> getItemList();
private:
/**
*
* @param size
*/
void net2Host(size_t size);
/**
*
* 使net2Host转换成主机字节序后才可使用此函数
*/
string dumpString() const;
} PACKED;
/////////////////////////////////////////////////////////////////////////////
/*
* 6.5 SDES: Source Description RTCP Packet
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P| SC | PT=SDES=202 | length |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
chunk | SSRC/CSRC_1 |
1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SDES items |
| ... |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
chunk | SSRC/CSRC_2 |
2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SDES items |
| ... |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*/
/*
SDES items
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SdesType | length | user and domain name ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
//Source description item
class SdesItem {
public:
friend class RtcpSdes;
uint32_t ssrc;
//SdesType
uint8_t type;
//text长度股可以为0
uint8_t length;
//不定长
char text;
//最后以RTCP_SDES_END结尾
//只字段为占位字段,不代表真实位置
uint8_t end;
public:
/**
*
*/
size_t totalBytes() const;
/**
*
*/
static size_t minSize();
private:
/**
*
* 使net2Host转换成主机字节序后才可使用此函数
*/
string dumpString() const;
/**
*
*/
void net2Host();
} PACKED;
//Source description
class RtcpSdes : public RtcpHeader {
public:
friend class RtcpHeader;
//可能有很多个
SdesItem items;
public:
/**
* SDES包RtcpHeader以及SdesItem对象的length和text部分
* @param item_text SdesItem列表length和text部分
* @return SDES包
*/
static std::shared_ptr<RtcpSdes> create(const std::initializer_list<string> &item_text);
/**
* SdesItem对象指针列表
* 使net2Host转换成主机字节序后才可使用此函数
*/
vector<SdesItem*> getItemList();
private:
/**
*
* 使net2Host转换成主机字节序后才可使用此函数
*/
string dumpString() const;
/**
*
* @param size
*/
void net2Host(size_t size);
} PACKED;
#if defined(_WIN32)
#pragma pack(pop)
#endif // defined(_WIN32)
} //namespace mediakit
#endif //ZLMEDIAKIT_RTCP_H

147
src/Rtcp/RtcpContext.cpp Normal file
View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include "RtcpContext.h"
using namespace toolkit;
namespace mediakit {
void RtcpContext::clear(){
memset(this, 0, sizeof(RtcpContext));
}
RtcpContext::RtcpContext(uint32_t sample_rate){
_sample_rate = sample_rate;
}
void RtcpContext::onRtp(uint16_t seq, uint32_t stamp, size_t bytes) {
_bytes += bytes;
++_packets;
auto sys_stamp = getCurrentMillisecond();
if (_last_rtp_sys_stamp) {
//计算时间戳抖动值
double diff = double(int64_t(sys_stamp) - int64_t(_last_rtp_sys_stamp) - int64_t(stamp) + int64_t(_last_rtp_stamp));
if (diff < 0) {
diff = -diff;
}
//抖动单位为采样次数
diff *= (_sample_rate / 1000.0);
_jitter += (diff - _jitter) / 16.0;
} else {
_jitter = 0;
}
if (_last_rtp_seq > 0xFF00 && seq < 0xFF && (!_seq_cycles || _packets - _last_cycle_packets > 0x1FFF)) {
//上次seq大于0xFF00且本次seq小于0xFF
//且未发生回环或者距离上次回环间隔超过0x1FFF个包则认为回环
++_seq_cycles;
_last_cycle_packets = _packets;
_seq_max = seq;
} else if (seq > _seq_max) {
//本次回环前最大seq
_seq_max = seq;
}
if (!_seq_base) {
//记录第一个rtp的seq
_seq_base = seq;
} else if (!_seq_cycles && seq < _seq_base) {
//未发生回环那么取最新的seq为基准seq
_seq_base = seq;
}
_last_rtp_stamp = stamp;
_last_rtp_sys_stamp = sys_stamp;
_last_rtp_seq = seq;
}
void RtcpContext::onRtcp(RtcpHeader *rtcp){
if ((RtcpType) rtcp->pt != RtcpType::RTCP_SR) {
return;
}
auto rtcp_sr = (RtcpSR *)(rtcp);
/**
last SR timestamp (LSR): 32 bits
The middle 32 bits out of 64 in the NTP timestamp (as explained in
Section 4) received as part of the most recent RTCP sender report
(SR) packet from source SSRC_n. If no SR has been received yet,
the field is set to zero.
*/
_last_sr_lsr = ((rtcp_sr->ntpmsw & 0xFFFF) << 16) | ((rtcp_sr->ntplsw >> 16) & 0xFFFF);
_last_sr_ntp_sys = getCurrentMillisecond();
}
size_t RtcpContext::getExpectedPackets() const{
return (_seq_cycles << 16) + _seq_max - _seq_base + 1;
}
size_t RtcpContext::getExpectedPacketsInterval(){
auto expected = getExpectedPackets();
auto ret = expected - _last_expected;
_last_expected = expected;
return ret;
}
size_t RtcpContext::getLost(){
return getExpectedPackets() - _packets;
}
size_t RtcpContext::geLostInterval(){
auto lost = getLost();
auto ret = lost - _last_lost;
_last_lost = lost;
return ret;
}
Buffer::Ptr RtcpContext::createRtcpSR(uint32_t rtcp_ssrc){
auto rtcp = RtcpSR::create(0);
rtcp->ssrc = htonl(rtcp_ssrc);
struct timeval tv;
gettimeofday(&tv, NULL);
rtcp->setNtpStamp(tv);
//转换成rtp时间戳
rtcp->rtpts = htonl(uint32_t(_last_rtp_stamp * (_sample_rate / 1000.0)));
rtcp->packet_count = htonl((uint32_t)_packets);
rtcp->octet_count = htonl((uint32_t)_bytes);
return RtcpHeader::toBuffer(std::move(rtcp));
}
Buffer::Ptr RtcpContext::createRtcpRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc){
auto rtcp = RtcpRR::create(1);
rtcp->ssrc = htonl(rtcp_ssrc);
ReportItem *item = (ReportItem *) &rtcp->items;
item->ssrc = htonl(rtp_ssrc);
uint8_t fraction = 0;
auto expected_interval = getExpectedPacketsInterval();
if (expected_interval) {
fraction = uint8_t(geLostInterval() << 8 / expected_interval);
}
item->fraction = fraction;
item->cumulative = htonl(uint32_t(getLost())) >> 8;
item->seq_cycles = htons(_seq_cycles);
item->seq_max = htons(_seq_max);
item->jitter = htonl(uint32_t(_jitter));
item->last_sr_stamp = htonl(_last_sr_lsr);
// now - Last SR time,单位毫秒
auto delay = getCurrentMillisecond() - _last_sr_ntp_sys;
// in units of 1/65536 seconds
auto dlsr = (uint32_t)(delay / 1000.0f * 65536);
item->delay_since_last_sr = htonl(_last_sr_lsr ? dlsr : 0);
return RtcpHeader::toBuffer(rtcp);
}
}//namespace mediakit

118
src/Rtcp/RtcpContext.h Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_RTCPCONTEXT_H
#define ZLMEDIAKIT_RTCPCONTEXT_H
#include <stdint.h>
#include <stddef.h>
#include "Rtcp.h"
namespace mediakit {
class RtcpContext {
public:
using Ptr = std::shared_ptr<RtcpContext>;
/**
* rtcp上下文
* @param sample_rate 90000
*/
RtcpContext(uint32_t sample_rate);
/**
* rtp时调用
* @param seq rtp的seq
* @param stamp rtp的时间戳
* @param bytes rtp数据长度
*/
void onRtp(uint16_t seq, uint32_t stamp, size_t bytes);
/**
* sr rtcp包
* @param rtcp rtcp
*/
void onRtcp(RtcpHeader *rtcp);
/**
*
*/
size_t getLost();
/**
* rtp数
*/
size_t getExpectedPackets() const;
/**
* SR rtcp包
* @param rtcp_ssrc rtcp的ssrc
* @return rtcp包
*/
Buffer::Ptr createRtcpSR(uint32_t rtcp_ssrc);
/**
* RR rtcp包
* @param rtcp_ssrc rtcp的ssrc
* @param rtp_ssrc rtp的ssrc
* @return rtcp包
*/
Buffer::Ptr createRtcpRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc);
/**
*
*/
void clear();
private:
/**
*
*/
size_t getExpectedPacketsInterval();
/**
*
*/
size_t geLostInterval();
private:
//时间戳抖动值
double _jitter = 0;
//视频默认90000,音频为采样率
uint32_t _sample_rate;
//收到或发送的rtp的字节数
size_t _bytes = 0;
//收到或发送的rtp的个数
size_t _packets = 0;
//第一个seq的值
uint16_t _seq_base = 0;
//rtp最大seq
uint16_t _seq_max = 0;
//rtp回环次数
uint16_t _seq_cycles = 0;
//上次回环发生时记录的rtp包数
size_t _last_cycle_packets = 0;
//上次的seq
uint16_t _last_rtp_seq = 0;
//上次的rtp时间戳,毫秒
uint32_t _last_rtp_stamp = 0;
//上次的rtp的系统时间戳(毫秒)用于统计抖动
uint64_t _last_rtp_sys_stamp = 0;
//上次统计的丢包总数
size_t _last_lost = 0;
//上次统计应收rtp包总数
size_t _last_expected = 0;
//上次收到sr包时计算出的Last SR timestamp
uint32_t _last_sr_lsr = 0;
//上次收到sr时的系统时间戳,单位毫秒
uint64_t _last_sr_ntp_sys = 0;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_RTCPCONTEXT_H

View File

@ -30,7 +30,7 @@ RtpReceiver::RtpReceiver() {
}
RtpReceiver::~RtpReceiver() {}
bool RtpReceiver::handleOneRtp(int track_index, TrackType type, int samplerate, unsigned char *rtp_raw_ptr, size_t rtp_raw_len) {
bool RtpReceiver::handleOneRtp(int track_index, TrackType type, int samplerate, uint8_t *rtp_raw_ptr, size_t rtp_raw_len) {
if (rtp_raw_len < 12) {
WarnL << "rtp包太小:" << rtp_raw_len;
return false;
@ -136,6 +136,7 @@ bool RtpReceiver::handleOneRtp(int track_index, TrackType type, int samplerate,
memcpy(payload_ptr + 4, rtp_raw_ptr, rtp_raw_len);
//排序rtp
auto seq = rtp_ptr->sequence;
onBeforeRtpSorted(rtp_ptr, track_index);
_rtp_sortor[track_index].sortPacket(seq, std::move(rtp_ptr));
return true;
}

View File

@ -175,7 +175,7 @@ protected:
* @param rtp_raw_len rtp数据指针长度
* @return true
*/
bool handleOneRtp(int track_index, TrackType type, int samplerate, unsigned char *rtp_raw_ptr, size_t rtp_raw_len);
bool handleOneRtp(int track_index, TrackType type, int samplerate, uint8_t *rtp_raw_ptr, size_t rtp_raw_len);
/**
* rtp数据包排序后输出
@ -184,6 +184,13 @@ protected:
*/
virtual void onRtpSorted(const RtpPacket::Ptr &rtp, int track_index) {}
/**
* rtp但还未排序
* @param rtp rtp数据包
* @param track_index track索引
*/
virtual void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) {}
void clear();
void setPoolSize(size_t size);
size_t getJitterSize(int track_index) const;

View File

@ -414,4 +414,15 @@ string printSSRC(uint32_t ui32Ssrc) {
return tmp;
}
Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved){
auto rtp_tcp = std::make_shared<BufferRaw>(4);
auto ptr = rtp_tcp->data();
ptr[0] = '$';
ptr[1] = interleaved;
ptr[2] = (size >> 8) & 0xFF;
ptr[3] = size & 0xFF;
rtp_tcp->setSize(4);
return rtp_tcp;
}
}//namespace mediakit

View File

@ -82,6 +82,8 @@ public:
size_t offset;
};
Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved);
class RtpPayload{
public:
static int getClockRate(int pt);

View File

@ -15,9 +15,8 @@
#include "Common/config.h"
#include "RtspPlayer.h"
#include "Util/MD5.h"
#include "Util/util.h"
#include "Util/base64.h"
#include "Network/sockutil.h"
#include "Rtcp/Rtcp.h"
using namespace toolkit;
using namespace mediakit::Client;
@ -53,13 +52,10 @@ void RtspPlayer::teardown(){
_session_id.clear();
_content_base.clear();
RtpReceiver::clear();
_rtcp_context.clear();
CLEAR_ARR(_rtp_sock);
CLEAR_ARR(_rtcp_sock);
CLEAR_ARR(_rtp_seq_start)
CLEAR_ARR(_rtp_recv_count)
CLEAR_ARR(_rtp_recv_count)
CLEAR_ARR(_rtp_seq_now)
_play_check_timer.reset();
_rtp_check_timer.reset();
@ -119,7 +115,12 @@ void RtspPlayer::onRecv(const Buffer::Ptr& buf) {
_rtp_recv_ticker.resetTime();
return;
}
try {
input(buf->data(), buf->size());
} catch (exception &e) {
SockException ex(Err_other, e.what());
onPlayResult_l(ex, !_play_check_timer);
}
}
void RtspPlayer::onErr(const SockException &ex) {
@ -196,15 +197,15 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
SdpParser sdpParser(parser.Content());
//解析sdp
_sdp_track = sdpParser.getAvailableTrack();
auto title = sdpParser.getTrack(TrackTitle);
if (_sdp_track.empty()) {
throw std::runtime_error("无有效的Sdp Track");
}
if (!onCheckSDP(sdpParser.toString())) {
throw std::runtime_error("onCheckSDP faied");
}
for (auto &track : _sdp_track) {
_rtcp_context.emplace_back(std::make_shared<RtcpContext>(track->_samplerate));
}
sendSetup(0);
}
@ -331,7 +332,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) {
return;
}
strongSelf->handleOneRtp(track_idx, strongSelf->_sdp_track[track_idx]->_type,
strongSelf->_sdp_track[track_idx]->_samplerate, (unsigned char *) buf->data(), buf->size());
strongSelf->_sdp_track[track_idx]->_samplerate, (uint8_t *) buf->data(), buf->size());
});
if(pRtcpSockRef) {
@ -345,7 +346,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) {
WarnL << "收到其他地址的rtcp数据:" << SockUtil::inet_ntoa(((struct sockaddr_in *) addr)->sin_addr);
return;
}
strongSelf->onRtcpPacket(track_idx, strongSelf->_sdp_track[track_idx], (unsigned char *) buf->data(), buf->size());
strongSelf->onRtcpPacket(track_idx, strongSelf->_sdp_track[track_idx], (uint8_t *) buf->data(), buf->size());
});
}
}
@ -477,174 +478,44 @@ void RtspPlayer::onRtpPacket(const char *data, size_t len) {
uint8_t interleaved = data[1];
if(interleaved %2 == 0){
trackIdx = getTrackIndexByInterleaved(interleaved);
handleOneRtp(trackIdx, _sdp_track[trackIdx]->_type, _sdp_track[trackIdx]->_samplerate, (unsigned char *)data + 4, len - 4);
handleOneRtp(trackIdx, _sdp_track[trackIdx]->_type, _sdp_track[trackIdx]->_samplerate, (uint8_t *)data + 4, len - 4);
}else{
trackIdx = getTrackIndexByInterleaved(interleaved - 1);
onRtcpPacket(trackIdx, _sdp_track[trackIdx], (unsigned char *) data + 4, len - 4);
onRtcpPacket(trackIdx, _sdp_track[trackIdx], (uint8_t *) data + 4, len - 4);
}
}
//此处预留rtcp处理函数
void RtspPlayer::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, unsigned char *data, size_t len){}
#if 0
//改代码提取自FFmpeg参考之
// Receiver Report
avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */
avio_w8(pb, RTCP_RR);
avio_wb16(pb, 7); /* length in words - 1 */
// our own SSRC: we use the server's SSRC + 1 to avoid conflicts
avio_wb32(pb, s->ssrc + 1);
avio_wb32(pb, s->ssrc); // server SSRC
// some placeholders we should really fill...
// RFC 1889/p64
extended_max = stats->cycles + stats->max_seq;
expected = extended_max - stats->base_seq;
lost = expected - stats->received;
lost = FFMIN(lost, 0xffffff); // clamp it since it's only 24 bits...
expected_interval = expected - stats->expected_prior;
stats->expected_prior = expected;
received_interval = stats->received - stats->received_prior;
stats->received_prior = stats->received;
lost_interval = expected_interval - received_interval;
if (expected_interval == 0 || lost_interval <= 0)
fraction = 0;
else
fraction = (lost_interval << 8) / expected_interval;
fraction = (fraction << 24) | lost;
avio_wb32(pb, fraction); /* 8 bits of fraction, 24 bits of total packets lost */
avio_wb32(pb, extended_max); /* max sequence received */
avio_wb32(pb, stats->jitter >> 4); /* jitter */
if (s->last_rtcp_ntp_time == AV_NOPTS_VALUE) {
avio_wb32(pb, 0); /* last SR timestamp */
avio_wb32(pb, 0); /* delay since last SR */
} else {
uint32_t middle_32_bits = s->last_rtcp_ntp_time >> 16; // this is valid, right? do we need to handle 64 bit values special?
uint32_t delay_since_last = av_rescale(av_gettime_relative() - s->last_rtcp_reception_time,
65536, AV_TIME_BASE);
avio_wb32(pb, middle_32_bits); /* last SR timestamp */
avio_wb32(pb, delay_since_last); /* delay since last SR */
}
// CNAME
avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */
avio_w8(pb, RTCP_SDES);
len = strlen(s->hostname);
avio_wb16(pb, (7 + len + 3) / 4); /* length in words - 1 */
avio_wb32(pb, s->ssrc + 1);
avio_w8(pb, 0x01);
avio_w8(pb, len);
avio_write(pb, s->hostname, len);
avio_w8(pb, 0); /* END */
// padding
for (len = (7 + len) % 4; len % 4; len++)
avio_w8(pb, 0);
#endif
void RtspPlayer::sendReceiverReport(bool over_tcp, int track_idx){
static const char s_cname[] = "ZLMediaKitRtsp";
uint8_t aui8Rtcp[4 + 32 + 10 + sizeof(s_cname) + 1] = {0};
uint8_t *pui8Rtcp_RR = aui8Rtcp + 4, *pui8Rtcp_SDES = pui8Rtcp_RR + 32;
auto &track = _sdp_track[track_idx];
auto &counter = _rtcp_counter[track_idx];
aui8Rtcp[0] = '$';
aui8Rtcp[1] = track->_interleaved + 1;
aui8Rtcp[2] = (sizeof(aui8Rtcp) - 4) >> 8;
aui8Rtcp[3] = (sizeof(aui8Rtcp) - 4) & 0xFF;
pui8Rtcp_RR[0] = 0x81;/* 1 report block */
pui8Rtcp_RR[1] = 0xC9;//RTCP_RR
pui8Rtcp_RR[2] = 0x00;
pui8Rtcp_RR[3] = 0x07;/* length in words - 1 */
auto track_ssrc = track->_ssrc ? track->_ssrc : getSSRC(track_idx);
// our own SSRC: we use the server's SSRC + 1 to avoid conflicts
uint32_t ssrc = htonl(track_ssrc + 1);
memcpy(&pui8Rtcp_RR[4], &ssrc, 4);
// server SSRC
ssrc = htonl(track_ssrc);
memcpy(&pui8Rtcp_RR[8], &ssrc, 4);
//FIXME: 8 bits of fraction, 24 bits of total packets lost
pui8Rtcp_RR[12] = 0x00;
pui8Rtcp_RR[13] = 0x00;
pui8Rtcp_RR[14] = 0x00;
pui8Rtcp_RR[15] = 0x00;
//FIXME: max sequence received
uint16_t cycleCount = (uint16_t)getCycleCount(track_idx);
pui8Rtcp_RR[16] = (cycleCount >> 8) & 0xFF;
pui8Rtcp_RR[17] = cycleCount & 0xFF;
pui8Rtcp_RR[18] = counter.pktCnt >> 8;
pui8Rtcp_RR[19] = counter.pktCnt & 0xFF;
uint32_t jitter = htonl((uint32_t)getJitterSize(track_idx));
//FIXME: jitter
memcpy(pui8Rtcp_RR + 20, &jitter, 4);
/* last SR timestamp */
memcpy(pui8Rtcp_RR + 24, &counter.lastTimeStamp, 4);
uint32_t msInc = htonl(ntohl(counter.timeStamp) - ntohl(counter.lastTimeStamp));
/* delay since last SR */
memcpy(pui8Rtcp_RR + 28, &msInc, 4);
// CNAME
pui8Rtcp_SDES[0] = 0x81;
pui8Rtcp_SDES[1] = 0xCA;
pui8Rtcp_SDES[2] = 0x00;
pui8Rtcp_SDES[3] = 0x06;
memcpy(&pui8Rtcp_SDES[4], &ssrc, 4);
pui8Rtcp_SDES[8] = 0x01;
pui8Rtcp_SDES[9] = 0x0f;
memcpy(&pui8Rtcp_SDES[10], s_cname, sizeof(s_cname));
pui8Rtcp_SDES[10 + sizeof(s_cname)] = 0x00;
if (over_tcp) {
send(obtainBuffer((char *) aui8Rtcp, sizeof(aui8Rtcp)));
} else if (_rtcp_sock[track_idx]) {
_rtcp_sock[track_idx]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4);
void RtspPlayer::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len){
auto rtcp_arr = RtcpHeader::loadFromBytes((char *) data, len);
for (auto &rtcp : rtcp_arr) {
_rtcp_context[track_idx]->onRtcp(rtcp);
}
}
void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){
//统计丢包率
if (_rtp_seq_start[trackidx] == 0 || rtppt->sequence < _rtp_seq_start[trackidx]) {
_rtp_seq_start[trackidx] = rtppt->sequence;
_rtp_recv_count[trackidx] = 0;
}
_rtp_recv_count[trackidx] ++;
_rtp_seq_now[trackidx] = rtppt->sequence;
_stamp[trackidx] = rtppt->timeStamp;
//计算相对时间戳
onRecvRTP_l(rtppt, _sdp_track[trackidx]);
_rtp_recv_ticker.resetTime();
onRecvRTP(rtppt, _sdp_track[trackidx]);
}
float RtspPlayer::getPacketLossRate(TrackType type) const{
size_t lost = 0, expected = 0;
try {
auto track_idx = getTrackIndexByTrackType(type);
if (_rtp_seq_now[track_idx] - _rtp_seq_start[track_idx] + 1 == 0) {
return 0;
}
return (float)(1.0f - (double) _rtp_recv_count[track_idx] / (_rtp_seq_now[track_idx] - _rtp_seq_start[track_idx] + 1));
auto ctx = _rtcp_context[track_idx];
lost = ctx->getLost();
expected = ctx->getExpectedPackets();
} catch (...) {
uint64_t totalRecv = 0;
uint64_t totalSend = 0;
for (unsigned int i = 0; i < _sdp_track.size(); i++) {
totalRecv += _rtp_recv_count[i];
totalSend += (_rtp_seq_now[i] - _rtp_seq_start[i] + 1);
for (auto &ctx : _rtcp_context) {
lost += ctx->getLost();
expected += ctx->getExpectedPackets();
}
if (totalSend == 0) {
}
if (!expected) {
return 0;
}
return (float)(1.0f - (double) totalRecv / totalSend);
}
return (float) (double(lost) / double(expected));
}
uint32_t RtspPlayer::getProgressMilliSecond() const{
@ -720,35 +591,51 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrC
SockSender::send(std::move(printer));
}
void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) {
_rtp_recv_ticker.resetTime();
onRecvRTP(rtp, track);
void RtspPlayer::onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_idx){
auto &rtcp_ctx = _rtcp_context[track_idx];
rtcp_ctx->onRtp(rtp->sequence, rtp->timeStamp, rtp->size() - 4);
int track_idx = getTrackIndexByTrackType(rtp->type);
RtcpCounter &counter = _rtcp_counter[track_idx];
counter.pktCnt = rtp->sequence;
auto &ticker = _rtcp_send_ticker[track_idx];
if (ticker.elapsedTime() > 3 * 1000) {
if (ticker.elapsedTime() < 3 * 1000) {
//时间未到
return;
}
auto &rtcp_flag = _send_rtcp[track_idx];
//每3秒发送一次心跳rtcp与rtsp信令轮流心跳该特性用于兼容issue:642
if (_send_rtcp) {
counter.lastTimeStamp = counter.timeStamp;
//直接保存网络字节序
memcpy(&counter.timeStamp, rtp->data() + 8, 4);
if (counter.lastTimeStamp != 0) {
sendReceiverReport(_rtp_type == Rtsp::RTP_TCP, track_idx);
ticker.resetTime();
_send_rtcp = false;
}
} else {
//有些rtsp服务器需要rtcp保活有些需要发送信令保活
//发送信令保活
if (!rtcp_flag) {
if (track_idx == 0) {
//只需要发送一次心跳信令包
sendKeepAlive();
}
ticker.resetTime();
_send_rtcp = true;
}
//下次发送rtcp保活
rtcp_flag = true;
return;
}
//发送rtcp
static auto send_rtcp = [](RtspPlayer *thiz, int index, Buffer::Ptr ptr) {
if (thiz->_rtp_type == Rtsp::RTP_TCP) {
auto &track = thiz->_sdp_track[index];
thiz->send(makeRtpOverTcpPrefix((uint16_t) (ptr->size()), track->_interleaved + 1));
thiz->send(std::move(ptr));
} else {
thiz->_rtcp_sock[index]->send(std::move(ptr));
}
};
auto rtcp = rtcp_ctx->createRtcpRR(rtp->ssrc + 1, rtp->ssrc);
auto rtcp_sdes = RtcpSdes::create({SERVER_NAME});
rtcp_sdes->items.type = (uint8_t) SdesType::RTCP_SDES_CNAME;
rtcp_sdes->items.ssrc = htonl(rtp->ssrc);
send_rtcp(this, track_idx, std::move(rtcp));
send_rtcp(this, track_idx, RtcpHeader::toBuffer(rtcp_sdes));
ticker.resetTime();
//下次发送信令保活
rtcp_flag = false;
}
void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshake_done) {

View File

@ -25,6 +25,7 @@
#include "RtspSplitter.h"
#include "RtpReceiver.h"
#include "Common/Stamp.h"
#include "Rtcp/RtcpContext.h"
using namespace std;
using namespace toolkit;
@ -71,6 +72,13 @@ protected:
*/
void onRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) override;
/**
* rtp但还未排序
* @param rtp rtp数据包
* @param track_index track索引
*/
void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override;
/**
* RTCP包回调
* @param track_idx track索引
@ -78,7 +86,7 @@ protected:
* @param data rtcp内容
* @param len rtcp内容长度
*/
virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, unsigned char *data, size_t len);
virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len);
/////////////TcpClient override/////////////
void onConnect(const SockException &err) override;
@ -86,7 +94,6 @@ protected:
void onErr(const SockException &ex) override;
private:
void onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track);
void onPlayResult_l(const SockException &ex , bool handshake_done);
int getTrackIndexByInterleaved(int interleaved) const;
@ -106,14 +113,13 @@ private:
void sendKeepAlive();
void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap());
void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list<string> &header);
void sendReceiverReport(bool over_tcp, int track_idx);
void createUdpSockIfNecessary(int track_idx);
private:
//是否为性能测试模式
bool _benchmark_mode = false;
//轮流发送rtcp与GET_PARAMETER保活
bool _send_rtcp = true;
bool _send_rtcp[2] = {true, true};
string _play_url;
vector<SdpTrack::Ptr> _sdp_track;
@ -132,10 +138,6 @@ private:
string _content_base;
Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP;
/* 丢包率统计需要用到的参数 */
uint16_t _rtp_seq_start[2] = {0, 0};
uint16_t _rtp_seq_now[2] = {0, 0};
uint64_t _rtp_recv_count[2] = {0, 0};
//当前rtp时间戳
uint32_t _stamp[2] = {0, 0};
@ -143,14 +145,13 @@ private:
Ticker _rtp_recv_ticker;
std::shared_ptr<Timer> _play_check_timer;
std::shared_ptr<Timer> _rtp_check_timer;
//rtcp统计,trackid idx 为数组下标
RtcpCounter _rtcp_counter[2];
//rtcp发送时间,trackid idx 为数组下标
Ticker _rtcp_send_ticker[2];
//服务器支持的命令
set<string> _supported_cmd;
////////// rtcp ////////////////
//rtcp发送时间,trackid idx 为数组下标
Ticker _rtcp_send_ticker[2];
//统计rtp并发送rtcp
vector<RtcpContext::Ptr> _rtcp_context;
};
} /* namespace mediakit */

View File

@ -38,7 +38,8 @@ void RtspPusher::sendTeardown(){
void RtspPusher::teardown() {
sendTeardown();
reset();
CLEAR_ARR(_udp_socks);
CLEAR_ARR(_rtp_sock);
CLEAR_ARR(_rtcp_sock);
_nonce.clear();
_realm.clear();
_track_vec.clear();
@ -148,6 +149,22 @@ void RtspPusher::onWholeRtspPacket(Parser &parser) {
parser.Clear();
}
void RtspPusher::onRtpPacket(const char *data, size_t len) {
int trackIdx = -1;
uint8_t interleaved = data[1];
if (interleaved % 2 != 0) {
trackIdx = getTrackIndexByInterleaved(interleaved - 1);
onRtcpPacket(trackIdx, _track_vec[trackIdx], (uint8_t *) data + 4, len - 4);
}
}
void RtspPusher::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len){
auto rtcp_arr = RtcpHeader::loadFromBytes((char *) data, len);
for (auto &rtcp : rtcp_arr) {
_rtcp_context[track_idx]->onRtcp(rtcp);
}
}
void RtspPusher::sendAnnounce() {
auto src = _push_src.lock();
if (!src) {
@ -156,11 +173,12 @@ void RtspPusher::sendAnnounce() {
//解析sdp
_sdp_parser.load(src->getSdp());
_track_vec = _sdp_parser.getAvailableTrack();
if (_track_vec.empty()) {
throw std::runtime_error("无有效的Sdp Track");
}
for (auto &track : _track_vec) {
_rtcp_context.emplace_back(std::make_shared<RtcpContext>(track->_samplerate));
}
_on_res_func = std::bind(&RtspPusher::handleResAnnounce, this, placeholders::_1);
sendRtspRequest("ANNOUNCE", _url, {}, src->getSdp());
}
@ -229,14 +247,13 @@ bool RtspPusher::handleAuthenticationFailure(const string &params_str) {
//有必要的情况下创建udp端口
void RtspPusher::createUdpSockIfNecessary(int track_idx){
auto &rtp_sock = _udp_socks[track_idx];
if (!rtp_sock) {
rtp_sock = createSocket();
//rtp随机端口
if (!rtp_sock->bindUdpSock(0, get_local_ip().data())) {
rtp_sock.reset();
throw std::runtime_error("open rtp sock failed");
}
auto &rtpSockRef = _rtp_sock[track_idx];
auto &rtcpSockRef = _rtcp_sock[track_idx];
if (!rtpSockRef || !rtcpSockRef) {
std::pair<Socket::Ptr, Socket::Ptr> pr = std::make_pair(createSocket(), createSocket());
makeSockPair(pr, get_local_ip());
rtpSockRef = pr.first;
rtcpSockRef = pr.second;
}
}
@ -256,7 +273,7 @@ void RtspPusher::sendSetup(unsigned int track_idx) {
break;
case Rtsp::RTP_UDP: {
createUdpSockIfNecessary(track_idx);
int port = _udp_socks[track_idx]->get_local_port();
int port = _rtp_sock[track_idx]->get_local_port();
sendRtspRequest("SETUP", base_url,
{"Transport", StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1});
}
@ -266,7 +283,6 @@ void RtspPusher::sendSetup(unsigned int track_idx) {
}
}
void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) {
if (parser.Url() != "200") {
throw std::runtime_error(StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
@ -289,12 +305,40 @@ void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) {
createUdpSockIfNecessary(track_idx);
const char *strPos = "server_port=";
auto port_str = FindField((transport + ";").data(), strPos, ";");
uint16_t port = atoi(FindField(port_str.data(), NULL, "-").data());
uint16_t rtp_port = atoi(FindField(port_str.data(), NULL, "-").data());
uint16_t rtcp_port = atoi(FindField(port_str.data(), "-", NULL).data());
auto &rtp_sock = _rtp_sock[track_idx];
auto &rtcp_sock = _rtcp_sock[track_idx];
struct sockaddr_in rtpto;
rtpto.sin_port = ntohs(port);
//设置rtp发送目标为后续发送rtp做准备
rtpto.sin_port = ntohs(rtp_port);
rtpto.sin_family = AF_INET;
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
_udp_socks[track_idx]->setSendPeerAddr((struct sockaddr *) &(rtpto));
rtp_sock->setSendPeerAddr((struct sockaddr *) &(rtpto));
//设置rtcp发送目标为后续发送rtcp做准备
rtpto.sin_port = ntohs(rtcp_port);
rtpto.sin_family = AF_INET;
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
rtcp_sock->setSendPeerAddr((struct sockaddr *)&(rtpto));
auto srcIP = inet_addr(get_peer_ip().data());
weak_ptr<RtspPusher> weakSelf = dynamic_pointer_cast<RtspPusher>(shared_from_this());
if(rtcp_sock) {
//设置rtcp over udp接收回调处理函数
rtcp_sock->setOnRead([srcIP, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
if (((struct sockaddr_in *) addr)->sin_addr.s_addr != srcIP) {
WarnL << "收到其他地址的rtcp数据:" << SockUtil::inet_ntoa(((struct sockaddr_in *) addr)->sin_addr);
return;
}
strongSelf->onRtcpPacket(track_idx, strongSelf->_track_vec[track_idx], (uint8_t *) buf->data(), buf->size());
});
}
}
RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP);
@ -313,13 +357,42 @@ void RtspPusher::sendOptions() {
sendRtspRequest("OPTIONS", _content_base);
}
inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
void RtspPusher::updateRtcpContext(const RtpPacket::Ptr &rtp){
int track_index = getTrackIndexByTrackType(rtp->type);
auto &ticker = _rtcp_send_ticker[track_index];
auto &rtcp_ctx = _rtcp_context[track_index];
rtcp_ctx->onRtp(rtp->sequence, rtp->timeStamp, rtp->size() - 4);
//send rtcp every 5 second
if (ticker.elapsedTime() > 5 * 1000) {
ticker.resetTime();
static auto send_rtcp = [](RtspPusher *thiz, int index, Buffer::Ptr ptr) {
if (thiz->_rtp_type == Rtsp::RTP_TCP) {
auto &track = thiz->_track_vec[index];
thiz->send(makeRtpOverTcpPrefix((uint16_t) (ptr->size()), track->_interleaved + 1));
thiz->send(std::move(ptr));
} else {
thiz->_rtcp_sock[index]->send(std::move(ptr));
}
};
auto rtcp = rtcp_ctx->createRtcpSR(rtp->ssrc + 1);
auto rtcp_sdes = RtcpSdes::create({SERVER_NAME});
rtcp_sdes->items.type = (uint8_t) SdesType::RTCP_SDES_CNAME;
rtcp_sdes->items.ssrc = htonl(rtp->ssrc);
send_rtcp(this, track_index, std::move(rtcp));
send_rtcp(this, track_index, RtcpHeader::toBuffer(rtcp_sdes));
}
}
void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
switch (_rtp_type) {
case Rtsp::RTP_TCP: {
size_t i = 0;
auto size = pkt->size();
setSendFlushFlag(false);
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
updateRtcpContext(rtp);
if (++i == size) {
setSendFlushFlag(true);
}
@ -333,8 +406,9 @@ inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt)
size_t i = 0;
auto size = pkt->size();
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
updateRtcpContext(rtp);
int iTrackIndex = getTrackIndexByTrackType(rtp->type);
auto &pSock = _udp_socks[iTrackIndex];
auto &pSock = _rtp_sock[iTrackIndex];
if (!pSock) {
shutdown(SockException(Err_shutdown, "udp sock not opened yet"));
return;
@ -349,8 +423,20 @@ inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt)
}
}
inline int RtspPusher::getTrackIndexByTrackType(TrackType type) {
for (unsigned int i = 0; i < _track_vec.size(); i++) {
int RtspPusher::getTrackIndexByInterleaved(int interleaved) const {
for (int i = 0; i < (int)_track_vec.size(); ++i) {
if (_track_vec[i]->_interleaved == interleaved) {
return i;
}
}
if (_track_vec.size() == 1) {
return 0;
}
throw SockException(Err_shutdown, StrPrinter << "no such track with interleaved:" << interleaved);
}
int RtspPusher::getTrackIndexByTrackType(TrackType type) const{
for (int i = 0; i < (int)_track_vec.size(); ++i) {
if (type == _track_vec[i]->_type) {
return i;
}

View File

@ -21,6 +21,7 @@
#include "Network/TcpClient.h"
#include "RtspSplitter.h"
#include "Pusher/PusherBase.h"
#include "Rtcp/RtcpContext.h"
using namespace std;
using namespace toolkit;
@ -51,7 +52,9 @@ protected:
//RtspSplitter override
void onWholeRtspPacket(Parser &parser) override ;
void onRtpPacket(const char *data,size_t len) override {};
void onRtpPacket(const char *data,size_t len) override;
virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len);
private:
void onPublishResult(const SockException &ex, bool handshake_done);
@ -66,7 +69,8 @@ private:
void handleResSetup(const Parser &parser, unsigned int track_idx);
bool handleAuthenticationFailure(const string &params_str);
inline int getTrackIndexByTrackType(TrackType type);
int getTrackIndexByInterleaved(int interleaved) const;
int getTrackIndexByTrackType(TrackType type) const;
void sendRtpPacket(const RtspMediaSource::RingDataType & pkt) ;
void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap(),const string &sdp = "" );
@ -74,6 +78,7 @@ private:
void createUdpSockIfNecessary(int track_idx);
void setSocketFlags();
void updateRtcpContext(const RtpPacket::Ptr &pkt);
private:
unsigned int _cseq = 1;
@ -87,7 +92,10 @@ private:
string _content_base;
SdpParser _sdp_parser;
vector<SdpTrack::Ptr> _track_vec;
Socket::Ptr _udp_socks[2];
//RTP端口,trackid idx 为数组下标
Socket::Ptr _rtp_sock[2];
//RTCP端口,trackid idx 为数组下标
Socket::Ptr _rtcp_sock[2];
//超时功能实现
std::shared_ptr<Timer> _publish_timer;
//心跳定时器
@ -98,6 +106,11 @@ private:
Event _on_shutdown;
Event _on_published;
function<void(const Parser&)> _on_res_func;
////////// rtcp ////////////////
//rtcp发送时间,trackid idx 为数组下标
Ticker _rtcp_send_ticker[2];
//统计rtp并发送rtcp
vector<RtcpContext::Ptr> _rtcp_context;
};
} /* namespace mediakit */

View File

@ -13,15 +13,9 @@
#include "Common/config.h"
#include "UDPServer.h"
#include "RtspSession.h"
#include "Util/mini.h"
#include "Util/MD5.h"
#include "Util/base64.h"
#include "Util/onceToken.h"
#include "Util/TimeTicker.h"
#include "Util/NoticeCenter.h"
#include "Network/sockutil.h"
#define RTSP_SERVER_SEND_RTCP 0
#include "Rtcp/Rtcp.h"
using namespace std;
using namespace toolkit;
@ -171,21 +165,25 @@ void RtspSession::onWholeRtspPacket(Parser &parser) {
}
void RtspSession::onRtpPacket(const char *data, size_t len) {
if(!_push_src){
uint8_t interleaved = data[1];
if (interleaved % 2 == 0) {
if (!_push_src) {
return;
}
uint8_t interleaved = data[1];
if(interleaved %2 == 0){
auto track_idx = getTrackIndexByInterleaved(interleaved);
handleOneRtp(track_idx, _sdp_track[track_idx]->_type, _sdp_track[track_idx]->_samplerate, (unsigned char *) data + 4, len - 4);
}else{
handleOneRtp(track_idx, _sdp_track[track_idx]->_type, _sdp_track[track_idx]->_samplerate, (uint8_t *) data + 4, len - 4);
} else {
auto track_idx = getTrackIndexByInterleaved(interleaved - 1);
onRtcpPacket(track_idx, _sdp_track[track_idx], data + 4, len - 4);
}
}
void RtspSession::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, const char *data, size_t len){}
void RtspSession::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, const char *data, size_t len){
auto rtcp_arr = RtcpHeader::loadFromBytes((char *) data, len);
for (auto &rtcp : rtcp_arr) {
_rtcp_context[track_idx]->onRtcp(rtcp);
}
}
ssize_t RtspSession::getContentLength(Parser &parser) {
if(parser.Method() == "POST"){
@ -224,18 +222,26 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) {
if(_media_info._app.empty() || _media_info._streamid.empty()){
//推流rtsp url必须最少两级(rtsp://host/app/stream_id)不允许莫名其妙的推流url
sendRtspResponse("403 Forbidden", {"Content-Type", "text/plain"}, "rtsp推流url非法,最少确保两级rtsp url");
throw SockException(Err_shutdown,StrPrinter << "rtsp推流url非法:" << full_url);
static constexpr auto err = "rtsp推流url非法,最少确保两级rtsp url";
sendRtspResponse("403 Forbidden", {"Content-Type", "text/plain"}, err);
throw SockException(Err_shutdown, StrPrinter << err << ":" << full_url);
}
SdpParser sdpParser(parser.Content());
_sessionid = makeRandStr(12);
_sdp_track = sdpParser.getAvailableTrack();
if (_sdp_track.empty()) {
//sdp无效
static constexpr auto err = "sdp中无有效track";
sendRtspResponse("403 Forbidden", {"Content-Type", "text/plain"}, err);
throw SockException(Err_shutdown,StrPrinter << err << ":" << full_url);
}
for (auto &track : _sdp_track) {
_rtcp_context.emplace_back(std::make_shared<RtcpContext>(track->_samplerate));
}
_push_src = std::make_shared<RtspMediaSourceImp>(_media_info._vhost, _media_info._app, _media_info._streamid);
_push_src->setListener(dynamic_pointer_cast<MediaSourceEvent>(shared_from_this()));
_push_src->setSdp(sdpParser.toString());
sendRtspResponse("200 OK",{"Content-Base", _content_base + "/"});
}
@ -395,11 +401,14 @@ void RtspSession::onAuthSuccess() {
strongSelf->_sdp_track = SdpParser(rtsp_src->getSdp()).getAvailableTrack();
if (strongSelf->_sdp_track.empty()) {
//该流无效
DebugL << "无trackInfo,该流无效";
WarnL << "sdp中有效track该流无效:" << rtsp_src->getSdp();
strongSelf->send_StreamNotFound();
strongSelf->shutdown(SockException(Err_shutdown,"can not find any available track in sdp"));
return;
}
for (auto &track : strongSelf->_sdp_track) {
strongSelf->_rtcp_context.emplace_back(std::make_shared<RtcpContext>(track->_samplerate));
}
strongSelf->_sessionid = makeRandStr(12);
strongSelf->_play_src = rtsp_src;
for(auto &track : strongSelf->_sdp_track){
@ -590,15 +599,15 @@ void RtspSession::onAuthUser(const string &realm,const string &authorization){
}
}
inline void RtspSession::send_StreamNotFound() {
void RtspSession::send_StreamNotFound() {
sendRtspResponse("404 Stream Not Found",{"Connection","Close"});
}
inline void RtspSession::send_UnsupportedTransport() {
void RtspSession::send_UnsupportedTransport() {
sendRtspResponse("461 Unsupported Transport",{"Connection","Close"});
}
inline void RtspSession::send_SessionNotFound() {
void RtspSession::send_SessionNotFound() {
sendRtspResponse("454 Session Not Found",{"Connection","Close"});
}
@ -899,7 +908,7 @@ void RtspSession::handleReq_SET_PARAMETER(const Parser &parser) {
sendRtspResponse("200 OK");
}
inline void RtspSession::send_NotAcceptable() {
void RtspSession::send_NotAcceptable() {
sendRtspResponse("406 Not Acceptable",{"Connection","Close"});
}
@ -913,7 +922,7 @@ void RtspSession::onRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) {
_push_src->onWrite(rtp, false);
}
inline void RtspSession::onRcvPeerUdpData(int interleaved, const Buffer::Ptr &buf, const struct sockaddr &addr) {
void RtspSession::onRcvPeerUdpData(int interleaved, const Buffer::Ptr &buf, const struct sockaddr &addr) {
//这是rtcp心跳包说明播放器还存活
_alive_ticker.resetTime();
@ -921,7 +930,7 @@ inline void RtspSession::onRcvPeerUdpData(int interleaved, const Buffer::Ptr &bu
if (_push_src) {
//这是rtsp推流上来的rtp包
auto &ref = _sdp_track[interleaved / 2];
handleOneRtp(interleaved / 2, ref->_type, ref->_samplerate, (unsigned char *) buf->data(), buf->size());
handleOneRtp(interleaved / 2, ref->_type, ref->_samplerate, (uint8_t *) buf->data(), buf->size());
} else if (!_udp_connected_flags.count(interleaved)) {
//这是rtsp播放器的rtp打洞包
_udp_connected_flags.emplace(interleaved);
@ -937,7 +946,7 @@ inline void RtspSession::onRcvPeerUdpData(int interleaved, const Buffer::Ptr &bu
}
}
inline void RtspSession::startListenPeerUdpData(int track_idx) {
void RtspSession::startListenPeerUdpData(int track_idx) {
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
auto srcIP = inet_addr(get_peer_ip().data());
auto onUdpData = [weakSelf,srcIP](const Buffer::Ptr &buf, struct sockaddr *peer_addr, int interleaved){
@ -1052,7 +1061,7 @@ bool RtspSession::sendRtspResponse(const string &res_code, const std::initialize
return sendRtspResponse(res_code,header_map,sdp,protocol);
}
inline int RtspSession::getTrackIndexByTrackType(TrackType type) {
int RtspSession::getTrackIndexByTrackType(TrackType type) {
for (unsigned int i = 0; i < _sdp_track.size(); i++) {
if (type == _sdp_track[i]->_type) {
return i;
@ -1064,7 +1073,7 @@ inline int RtspSession::getTrackIndexByTrackType(TrackType type) {
throw SockException(Err_shutdown, StrPrinter << "no such track with type:" << (int) type);
}
inline int RtspSession::getTrackIndexByControlSuffix(const string &controlSuffix) {
int RtspSession::getTrackIndexByControlSuffix(const string &controlSuffix) {
for (unsigned int i = 0; i < _sdp_track.size(); i++) {
if (controlSuffix == _sdp_track[i]->_control_surffix) {
return i;
@ -1076,7 +1085,7 @@ inline int RtspSession::getTrackIndexByControlSuffix(const string &controlSuffix
throw SockException(Err_shutdown, StrPrinter << "no such track with suffix:" << controlSuffix);
}
inline int RtspSession::getTrackIndexByInterleaved(int interleaved){
int RtspSession::getTrackIndexByInterleaved(int interleaved){
for (unsigned int i = 0; i < _sdp_track.size(); i++) {
if (_sdp_track[i]->_interleaved == interleaved) {
return i;
@ -1114,21 +1123,37 @@ std::shared_ptr<SockInfo> RtspSession::getOriginSock(MediaSource &sender) const
return const_cast<RtspSession *>(this)->shared_from_this();
}
inline void RtspSession::onSendRtpPacket(const RtpPacket::Ptr &pkt){
#if RTSP_SERVER_SEND_RTCP
int track_index = getTrackIndexByTrackType(pkt->type);
RtcpCounter &counter = _rtcp_counter[track_index];
counter.pktCnt += 1;
counter.octCount += (pkt->size() - pkt->offset);
void RtspSession::onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index){
updateRtcpContext(rtp);
}
void RtspSession::updateRtcpContext(const RtpPacket::Ptr &rtp){
int track_index = getTrackIndexByTrackType(rtp->type);
auto &rtcp_ctx = _rtcp_context[track_index];
rtcp_ctx->onRtp(rtp->sequence, rtp->timeStamp, rtp->size() - 4);
auto &ticker = _rtcp_send_tickers[track_index];
if (ticker.elapsedTime() > 5 * 1000) {
//send rtcp every 5 second
if (ticker.elapsedTime() > 5 * 1000) {
ticker.resetTime();
//直接保存网络字节序
memcpy(&counter.time_stamp, pkt->data() + 8, 4);
sendSenderReport(_rtp_type == Rtsp::RTP_TCP, track_index);
static auto send_rtcp = [](RtspSession *thiz, int index, Buffer::Ptr ptr) {
if (thiz->_rtp_type == Rtsp::RTP_TCP) {
auto &track = thiz->_sdp_track[index];
thiz->send(makeRtpOverTcpPrefix((uint16_t)(ptr->size()), track->_interleaved + 1));
thiz->send(std::move(ptr));
} else {
thiz->_rtcp_socks[index]->send(std::move(ptr));
}
};
auto rtcp = _push_src ? rtcp_ctx->createRtcpRR(rtp->ssrc + 1, rtp->ssrc) : rtcp_ctx->createRtcpSR(rtp->ssrc + 1);
auto rtcp_sdes = RtcpSdes::create({SERVER_NAME});
rtcp_sdes->items.type = (uint8_t)SdesType::RTCP_SDES_CNAME;
rtcp_sdes->items.ssrc = htonl(rtp->ssrc);
send_rtcp(this, track_index, std::move(rtcp));
send_rtcp(this, track_index, RtcpHeader::toBuffer(rtcp_sdes));
}
#endif
}
void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
@ -1138,7 +1163,7 @@ void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
auto size = pkt->size();
setSendFlushFlag(false);
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
onSendRtpPacket(rtp);
updateRtcpContext(rtp);
if (++i == size) {
setSendFlushFlag(true);
}
@ -1150,7 +1175,7 @@ void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
size_t i = 0;
auto size = pkt->size();
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
onSendRtpPacket(rtp);
updateRtcpContext(rtp);
int track_index = getTrackIndexByTrackType(rtp->type);
auto &pSock = _rtp_socks[track_index];
if (!pSock) {
@ -1168,66 +1193,6 @@ void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
}
}
void RtspSession::sendSenderReport(bool over_tcp, int track_index) {
static const char s_cname[] = "ZLMediaKitRtsp";
uint8_t rtcp_buf[4 + 28 + 10 + sizeof(s_cname) + 1] = {0};
uint8_t *rtcp_sr = rtcp_buf + 4, *rtcp_sdes = rtcp_sr + 28;
auto &track = _sdp_track[track_index];
auto &counter = _rtcp_counter[track_index];
rtcp_buf[0] = '$';
rtcp_buf[1] = track->_interleaved + 1;
rtcp_buf[2] = (sizeof(rtcp_buf) - 4) >> 8;
rtcp_buf[3] = (sizeof(rtcp_buf) - 4) & 0xFF;
rtcp_sr[0] = 0x80;
rtcp_sr[1] = 0xC8;
rtcp_sr[2] = 0x00;
rtcp_sr[3] = 0x06;
uint32_t ssrc = htonl(track->_ssrc);
memcpy(&rtcp_sr[4], &ssrc, 4);
uint32_t msw;
uint32_t lsw;
struct timeval tv;
gettimeofday(&tv, NULL);
msw = tv.tv_sec + 0x83AA7E80; /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */
lsw = (uint32_t) ((double) tv.tv_usec * (double) (((uint64_t) 1) << 32) * 1.0e-6);
msw = htonl(msw);
memcpy(&rtcp_sr[8], &msw, 4);
lsw = htonl(lsw);
memcpy(&rtcp_sr[12], &lsw, 4);
//直接使用网络字节序
memcpy(&rtcp_sr[16], &counter.timeStamp, 4);
uint32_t pktCnt = htonl(counter.pktCnt);
memcpy(&rtcp_sr[20], &pktCnt, 4);
uint32_t octCount = htonl(counter.octCount);
memcpy(&rtcp_sr[24], &octCount, 4);
rtcp_sdes[0] = 0x81;
rtcp_sdes[1] = 0xCA;
rtcp_sdes[2] = 0x00;
rtcp_sdes[3] = 0x06;
memcpy(&rtcp_sdes[4], &ssrc, 4);
rtcp_sdes[8] = 0x01;
rtcp_sdes[9] = 0x0f;
memcpy(&rtcp_sdes[10], s_cname, sizeof(s_cname));
rtcp_sdes[10 + sizeof(s_cname)] = 0x00;
if (over_tcp) {
send(obtainBuffer((char *) rtcp_buf, sizeof(rtcp_buf)));
} else {
_rtcp_socks[track_index]->send((char *) rtcp_buf + 4, sizeof(rtcp_buf) - 4);
}
}
void RtspSession::setSocketFlags(){
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
if(mergeWriteMS > 0) {

View File

@ -26,6 +26,7 @@
#include "RtpReceiver.h"
#include "RtspMediaSourceImp.h"
#include "Common/Stamp.h"
#include "Rtcp/RtcpContext.h"
using namespace std;
using namespace toolkit;
@ -79,6 +80,7 @@ protected:
////RtpReceiver override////
void onRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) override;
void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override;
///////MediaSourceEvent override///////
// 关闭
@ -152,12 +154,11 @@ private:
//发送rtp给客户端
void sendRtpPacket(const RtspMediaSource::RingDataType &pkt);
//触发rtcp发送
void onSendRtpPacket(const RtpPacket::Ptr &rtp);
void updateRtcpContext(const RtpPacket::Ptr &rtp);
//回复客户端
bool sendRtspResponse(const string &res_code, const std::initializer_list<string> &header, const string &sdp = "", const char *protocol = "RTSP/1.0");
bool sendRtspResponse(const string &res_code, const StrCaseMap &header = StrCaseMap(), const string &sdp = "", const char *protocol = "RTSP/1.0");
//服务器发送rtcp
void sendSenderReport(bool over_tcp, int track_idx);
//设置socket标志
void setSocketFlags();
@ -196,11 +197,6 @@ private:
//sdp里面有效的track,包含音频或视频
vector<SdpTrack::Ptr> _sdp_track;
//rtcp统计,trackid idx 为数组下标
RtcpCounter _rtcp_counter[2];
//rtcp发送时间,trackid idx 为数组下标
Ticker _rtcp_send_tickers[2];
////////RTP over udp////////
//RTP端口,trackid idx 为数组下标
Socket::Ptr _rtp_socks[2];
@ -216,6 +212,11 @@ private:
//一次发送 get 一次发送post需要通过x-sessioncookie关联起来
string _http_x_sessioncookie;
function<void(const Buffer::Ptr &)> _on_recv;
////////// rtcp ////////////////
//rtcp发送时间,trackid idx 为数组下标
Ticker _rtcp_send_tickers[2];
//统计rtp并发送rtcp
vector<RtcpContext::Ptr> _rtcp_context;
};
/**

131
tests/test_rtcp.cpp Normal file
View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include "Rtcp/Rtcp.h"
#include "Util/logger.h"
using namespace std;
using namespace toolkit;
using namespace mediakit;
void printRtcp(const std::shared_ptr<Buffer> &buffer){
auto rtcp_arr = RtcpHeader::loadFromBytes(buffer->data(), buffer->size());
//转换为主机字节序方可打印
InfoL << "\r\n" << rtcp_arr[0]->dumpString();
}
std::shared_ptr<Buffer> makeRtcpSR() {
auto rtcp = RtcpSR::create(3);
rtcp->ssrc = htonl(1);
struct timeval tv;
gettimeofday(&tv, NULL);
rtcp->setNtpStamp(tv);
rtcp->rtpts = htonl(2);
rtcp->packet_count = htonl(3);
rtcp->octet_count = htonl(4);
auto i = 5;
for (auto &ptr : rtcp->getItemList()) {
ReportItem *item = (ReportItem *) ptr;
item->ssrc = htonl(i++);
item->fraction = i++;
item->cumulative = htonl(i++) >> 8;
item->seq_cycles = htons(i++);
item->seq_max = htons(i++);
item->jitter = htonl(i++);
item->last_sr_stamp = htonl(i++);
item->delay_since_last_sr = htonl(i++);
}
//返回网络字节序
return RtcpHeader::toBuffer(rtcp);
}
std::shared_ptr<Buffer> makeRtcpRR() {
auto rtcp = RtcpRR::create(3);
rtcp->ssrc = htonl(1);
auto i = 5;
for (auto &ptr : rtcp->getItemList()) {
ReportItem *item = (ReportItem *) ptr;
item->ssrc = htonl(i++);
item->fraction = i++;
item->cumulative = htonl(i++) >> 8;
item->seq_cycles = htons(i++);
item->seq_max = htons(i++);
item->jitter = htonl(i++);
item->last_sr_stamp = htonl(i++);
item->delay_since_last_sr = htonl(i++);
}
//返回网络字节序
return RtcpHeader::toBuffer(rtcp);
}
std::shared_ptr<Buffer> makeRtcpSDES() {
auto rtcp = RtcpSdes::create({"zlmediakit", "", "https://github.com/xia-chu/ZLMediaKit", "1213642868@qq.com", "123456789012345678"});
auto i = 5;
auto items = rtcp->getItemList();
items[0]->type = (uint8_t)SdesType::RTCP_SDES_CNAME;
items[0]->ssrc = htonl(i++);
items[1]->type = (uint8_t)SdesType::RTCP_SDES_NOTE;
items[1]->ssrc = htonl(i++);
items[2]->type = (uint8_t)SdesType::RTCP_SDES_LOC;
items[2]->ssrc = htonl(i++);
items[3]->type = (uint8_t)SdesType::RTCP_SDES_EMAIL;
items[3]->ssrc = htonl(i++);
items[4]->type = (uint8_t)SdesType::RTCP_SDES_PHONE;
items[4]->ssrc = htonl(i++);
//返回网络字节序
return RtcpHeader::toBuffer(rtcp);
}
int main(int argc, char *argv[]){
Logger::Instance().add(std::make_shared<ConsoleChannel>());
{
static char rtcp_data[] = "\x81\xca\x00\x05\x70\xd8\xac\x1b\x01\x0b\x7a\x73\x68\x50\x43\x40"
"\x7a\x73\x68\x50\x43\x00\x00\x00"
"\x81\xc9\x00\x07\x70\xd8\xac\x1b\x55\x66\x77\x88\x00\x00\x00\x00"
"\x00\x00\x0d\x21\x00\x00\x00\x32\xdd\xf1\x00\x00\x00\x03\x4f\x67"
"\x80\xc8\x00\x06\x55\x66\x77\x88\xe3\x70\xdd\xf1\x00\x00\xc2\xb8"
"\x00\x21\xe4\x90\x00\x00\x0b\x81\x00\x2f\x6a\x60";
auto rtcp_arr = RtcpHeader::loadFromBytes(rtcp_data, sizeof(rtcp_data) - 1);
for (auto &rtcp : rtcp_arr) {
DebugL << "\r\n" << rtcp->dumpString();
}
}
{
printRtcp(makeRtcpSR());
printRtcp(makeRtcpRR());
printRtcp(makeRtcpSDES());
}
{
string str;
auto sr = makeRtcpSR();
auto rr = makeRtcpRR();
auto sdes = makeRtcpSDES();
str.append(sr->data(), sr->size());
str.append(rr->data(), rr->size());
str.append(sdes->data(), sdes->size());
//测试内存越界
char *data = new char[str.size()];
memcpy(data, str.data(), str.size());
auto rtcp_arr = RtcpHeader::loadFromBytes(data, str.size());
for (auto &rtcp : rtcp_arr) {
WarnL << "\r\n" << rtcp->dumpString();
}
delete [] data;
}
}