mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 19:00:01 +08:00
重写rtcp框架
This commit is contained in:
parent
629c39685b
commit
5c6560f55d
41
src/Common/macros.h
Normal file
41
src/Common/macros.h
Normal 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
405
src/Rtcp/Rtcp.cpp
Normal 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
464
src/Rtcp/Rtcp.h
Normal 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
147
src/Rtcp/RtcpContext.cpp
Normal 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
118
src/Rtcp/RtcpContext.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
input(buf->data(), buf->size());
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
return (float)(1.0f - (double) totalRecv / totalSend);
|
||||
}
|
||||
if (!expected) {
|
||||
return 0;
|
||||
}
|
||||
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) {
|
||||
//每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 (track_idx == 0) {
|
||||
//只需要发送一次心跳信令包
|
||||
sendKeepAlive();
|
||||
ticker.resetTime();
|
||||
_send_rtcp = true;
|
||||
}
|
||||
}
|
||||
if (ticker.elapsedTime() < 3 * 1000) {
|
||||
//时间未到
|
||||
return;
|
||||
}
|
||||
auto &rtcp_flag = _send_rtcp[track_idx];
|
||||
|
||||
//每3秒发送一次心跳,rtcp与rtsp信令轮流心跳,该特性用于兼容issue:642
|
||||
//有些rtsp服务器需要rtcp保活,有些需要发送信令保活
|
||||
|
||||
//发送信令保活
|
||||
if (!rtcp_flag) {
|
||||
if (track_idx == 0) {
|
||||
sendKeepAlive();
|
||||
}
|
||||
ticker.resetTime();
|
||||
//下次发送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) {
|
||||
|
@ -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 */
|
||||
|
@ -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 ¶ms_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;
|
||||
}
|
||||
|
@ -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 ¶ms_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 */
|
||||
|
@ -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){
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t interleaved = data[1];
|
||||
if(interleaved %2 == 0){
|
||||
if (interleaved % 2 == 0) {
|
||||
if (!_push_src) {
|
||||
return;
|
||||
}
|
||||
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];
|
||||
//send rtcp every 5 second
|
||||
if (ticker.elapsedTime() > 5 * 1000) {
|
||||
//send rtcp every 5 second
|
||||
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) {
|
||||
|
@ -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
131
tests/test_rtcp.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user