Commit 07c5341f by ziyue

兼容一些奇怪的rtsp流:#1031

parent 8b1d1d6e
...@@ -26,80 +26,70 @@ ...@@ -26,80 +26,70 @@
namespace mediakit{ namespace mediakit{
Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) {
if (strcasecmp(track->_codec.data(), "mpeg4-generic") == 0) { auto codec = getCodecId(track->_codec);
string aac_cfg_str = FindField(track->_fmtp.data(), "config=", ";"); if (codec == CodecInvalid) {
if (aac_cfg_str.empty()) { //根据传统的payload type 获取编码类型以及采样率等信息
aac_cfg_str = FindField(track->_fmtp.data(), "config=", nullptr); codec = RtpPayload::getCodecId(track->_pt);
}
if (aac_cfg_str.empty()) {
//如果sdp中获取不到aac config信息,那么在rtp也无法获取,那么忽略该Track
return nullptr;
}
string aac_cfg;
for(size_t i = 0 ; i < aac_cfg_str.size() / 2 ; ++i ){
unsigned int cfg;
sscanf(aac_cfg_str.substr(i * 2, 2).data(), "%02X", &cfg);
cfg &= 0x00FF;
aac_cfg.push_back((char)cfg);
}
return std::make_shared<AACTrack>(aac_cfg);
} }
switch (codec) {
case CodecG711A:
case CodecG711U: return std::make_shared<G711Track>(codec, track->_samplerate, track->_channel, 16);
case CodecL16: return std::make_shared<L16Track>(track->_samplerate, track->_channel);
case CodecOpus : return std::make_shared<OpusTrack>();
if (strcasecmp(track->_codec.data(), "opus") == 0) { case CodecAAC : {
return std::make_shared<OpusTrack>(); string aac_cfg_str = FindField(track->_fmtp.data(), "config=", ";");
} if (aac_cfg_str.empty()) {
aac_cfg_str = FindField(track->_fmtp.data(), "config=", nullptr);
if (strcasecmp(track->_codec.data(), "PCMA") == 0) { }
return std::make_shared<G711Track>(CodecG711A, track->_samplerate, track->_channel, 16); if (aac_cfg_str.empty()) {
} //如果sdp中获取不到aac config信息,那么在rtp也无法获取,那么忽略该Track
return nullptr;
if (strcasecmp(track->_codec.data(), "PCMU") == 0) { }
return std::make_shared<G711Track>(CodecG711U, track->_samplerate, track->_channel, 16); string aac_cfg;
} for (size_t i = 0; i < aac_cfg_str.size() / 2; ++i) {
unsigned int cfg;
if (strcasecmp(track->_codec.data(), "L16") == 0) { sscanf(aac_cfg_str.substr(i * 2, 2).data(), "%02X", &cfg);
return std::make_shared<L16Track>(track->_samplerate, track->_channel); cfg &= 0x00FF;
} aac_cfg.push_back((char) cfg);
}
if (strcasecmp(track->_codec.data(), "h264") == 0) { return std::make_shared<AACTrack>(aac_cfg);
//a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH9oBQBboQAAAAwBAAAAPI8YMqA==,aM48gA==
auto map = Parser::parseArgs(FindField(track->_fmtp.data()," ", nullptr),";","=");
auto sps_pps = map["sprop-parameter-sets"];
string base64_SPS = FindField(sps_pps.data(), NULL, ",");
string base64_PPS = FindField(sps_pps.data(), ",", NULL);
auto sps = decodeBase64(base64_SPS);
auto pps = decodeBase64(base64_PPS);
if(sps.empty() || pps.empty()){
//如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps
return std::make_shared<H264Track>();
} }
return std::make_shared<H264Track>(sps,pps,0,0); case CodecH264 : {
} //a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH9oBQBboQAAAAwBAAAAPI8YMqA==,aM48gA==
auto map = Parser::parseArgs(track->_fmtp, ";", "=");
auto sps_pps = map["sprop-parameter-sets"];
string base64_SPS = FindField(sps_pps.data(), NULL, ",");
string base64_PPS = FindField(sps_pps.data(), ",", NULL);
auto sps = decodeBase64(base64_SPS);
auto pps = decodeBase64(base64_PPS);
if (sps.empty() || pps.empty()) {
//如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps
return std::make_shared<H264Track>();
}
return std::make_shared<H264Track>(sps, pps, 0, 0);
}
if (strcasecmp(track->_codec.data(), "h265") == 0) { case CodecH265: {
//a=fmtp:96 sprop-sps=QgEBAWAAAAMAsAAAAwAAAwBdoAKAgC0WNrkky/AIAAADAAgAAAMBlQg=; sprop-pps=RAHA8vA8kAA= //a=fmtp:96 sprop-sps=QgEBAWAAAAMAsAAAAwAAAwBdoAKAgC0WNrkky/AIAAADAAgAAAMBlQg=; sprop-pps=RAHA8vA8kAA=
auto map = Parser::parseArgs(FindField(track->_fmtp.data()," ", nullptr),";","="); auto map = Parser::parseArgs(track->_fmtp, ";", "=");
auto vps = decodeBase64(map["sprop-vps"]); auto vps = decodeBase64(map["sprop-vps"]);
auto sps = decodeBase64(map["sprop-sps"]); auto sps = decodeBase64(map["sprop-sps"]);
auto pps = decodeBase64(map["sprop-pps"]); auto pps = decodeBase64(map["sprop-pps"]);
if(sps.empty() || pps.empty()){ if (sps.empty() || pps.empty()) {
//如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps //如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps
return std::make_shared<H265Track>(); return std::make_shared<H265Track>();
}
return std::make_shared<H265Track>(vps, sps, pps, 0, 0, 0);
} }
return std::make_shared<H265Track>(vps,sps,pps,0,0,0);
}
//可以根据传统的payload type 获取编码类型以及采样率等信息 default: {
CodecId codec_id = RtpPayload::getCodecId(track->_pt); //其他codec不支持
switch (codec_id){ WarnL << "暂不支持该rtsp编码类型:" << track->getName();
case CodecG711A : return nullptr;
case CodecG711U : return std::make_shared<G711Track>(codec_id, track->_samplerate, track->_channel, 16); }
default : break;
} }
WarnL << "暂不支持该sdp:" << track->getName();
return nullptr;
} }
RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) { RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) {
......
...@@ -12,41 +12,45 @@ ...@@ -12,41 +12,45 @@
#include "Rtsp.h" #include "Rtsp.h"
#include "Common/Parser.h" #include "Common/Parser.h"
namespace mediakit{ namespace mediakit {
int RtpPayload::getClockRate(int pt){ int RtpPayload::getClockRate(int pt) {
switch (pt){ switch (pt) {
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return clock_rate; #define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return clock_rate;
RTP_PT_MAP(SWITCH_CASE) RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE #undef SWITCH_CASE
default: return 90000; default:
return 90000;
} }
} }
TrackType RtpPayload::getTrackType(int pt){ TrackType RtpPayload::getTrackType(int pt) {
switch (pt){ switch (pt) {
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return type; #define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return type;
RTP_PT_MAP(SWITCH_CASE) RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE #undef SWITCH_CASE
default: return TrackInvalid; default:
return TrackInvalid;
} }
} }
int RtpPayload::getAudioChannel(int pt){ int RtpPayload::getAudioChannel(int pt) {
switch (pt){ switch (pt) {
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return channel; #define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return channel;
RTP_PT_MAP(SWITCH_CASE) RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE #undef SWITCH_CASE
default: return 1; default:
return 1;
} }
} }
const char * RtpPayload::getName(int pt){ const char *RtpPayload::getName(int pt) {
switch (pt){ switch (pt) {
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return #name; #define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return #name;
RTP_PT_MAP(SWITCH_CASE) RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE #undef SWITCH_CASE
default: return "unknown payload type"; default:
return "unknown payload type";
} }
} }
...@@ -55,75 +59,77 @@ CodecId RtpPayload::getCodecId(int pt) { ...@@ -55,75 +59,77 @@ CodecId RtpPayload::getCodecId(int pt) {
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return codec_id; #define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return codec_id;
RTP_PT_MAP(SWITCH_CASE) RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE #undef SWITCH_CASE
default : return CodecInvalid; default :
return CodecInvalid;
} }
} }
static void getAttrSdp(const map<string, string> &attr, _StrPrinter &printer){ static void getAttrSdp(const multimap<string, string> &attr, _StrPrinter &printer) {
const map<string, string>::value_type *ptr = nullptr; const map<string, string>::value_type *ptr = nullptr;
for(auto &pr : attr){ for (auto &pr : attr) {
if(pr.first == "control"){ if (pr.first == "control") {
ptr = &pr; ptr = &pr;
continue; continue;
} }
if(pr.second.empty()){ if (pr.second.empty()) {
printer << "a=" << pr.first << "\r\n"; printer << "a=" << pr.first << "\r\n";
}else{ } else {
printer << "a=" << pr.first << ":" << pr.second << "\r\n"; printer << "a=" << pr.first << ":" << pr.second << "\r\n";
} }
} }
if(ptr){ if (ptr) {
printer << "a=" << ptr->first << ":" << ptr->second << "\r\n"; printer << "a=" << ptr->first << ":" << ptr->second << "\r\n";
} }
} }
string SdpTrack::getName() const{ string SdpTrack::getName() const {
switch (_pt){ switch (_pt) {
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return #name; #define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value : return #name;
RTP_PT_MAP(SWITCH_CASE) RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE #undef SWITCH_CASE
default: return _codec; default:
return _codec;
} }
} }
string SdpTrack::getControlUrl(const string &base_url) const{ string SdpTrack::getControlUrl(const string &base_url) const {
if (_control.find("://") != string::npos) { if (_control.find("://") != string::npos) {
//以rtsp://开头 //以rtsp://开头
return _control; return _control;
} }
return base_url +"/" + _control; return base_url + "/" + _control;
} }
string SdpTrack::toString() const { string SdpTrack::toString() const {
_StrPrinter _printer; _StrPrinter _printer;
switch (_type){ switch (_type) {
case TrackTitle:{ case TrackTitle: {
_printer << "v=" << 0 << "\r\n"; _printer << "v=" << 0 << "\r\n";
if(!_o.empty()){ if (!_o.empty()) {
_printer << "o="<< _o << "\r\n"; _printer << "o=" << _o << "\r\n";
} }
if(!_c.empty()){ if (!_c.empty()) {
_printer << "c=" << _c << "\r\n"; _printer << "c=" << _c << "\r\n";
} }
if(!_t.empty()){ if (!_t.empty()) {
_printer << "t=" << _t << "\r\n"; _printer << "t=" << _t << "\r\n";
} }
_printer << "s=Streamed by " << SERVER_NAME << "\r\n"; _printer << "s=Streamed by " << SERVER_NAME << "\r\n";
getAttrSdp(_attr,_printer); getAttrSdp(_attr, _printer);
} }
break; break;
case TrackAudio: case TrackAudio:
case TrackVideo:{ case TrackVideo: {
if(_type == TrackAudio){ if (_type == TrackAudio) {
_printer << "m=audio 0 RTP/AVP " << _pt << "\r\n"; _printer << "m=audio 0 RTP/AVP " << _pt << "\r\n";
}else{ } else {
_printer << "m=video 0 RTP/AVP " << _pt << "\r\n"; _printer << "m=video 0 RTP/AVP " << _pt << "\r\n";
} }
if(!_b.empty()){ if (!_b.empty()) {
_printer << "b=" <<_b << "\r\n"; _printer << "b=" << _b << "\r\n";
} }
getAttrSdp(_attr,_printer); getAttrSdp(_attr, _printer);
} }
break; break;
default: default:
...@@ -200,9 +206,9 @@ void SdpParser::load(const string &sdp) { ...@@ -200,9 +206,9 @@ void SdpParser::load(const string &sdp) {
case 'a': { case 'a': {
string attr = FindField(opt_val.data(), nullptr, ":"); string attr = FindField(opt_val.data(), nullptr, ":");
if (attr.empty()) { if (attr.empty()) {
track->_attr[opt_val] = ""; track->_attr.emplace(opt_val, "");
} else { } else {
track->_attr[attr] = FindField(opt_val.data(), ":", nullptr); track->_attr.emplace(attr, FindField(opt_val.data(), ":", nullptr));
} }
} }
break; break;
...@@ -229,13 +235,18 @@ void SdpParser::load(const string &sdp) { ...@@ -229,13 +235,18 @@ void SdpParser::load(const string &sdp) {
} }
} }
it = track._attr.find("rtpmap"); for (it = track._attr.find("rtpmap"); it != track._attr.end();) {
if (it != track._attr.end()) { auto &rtpmap = it->second;
auto rtpmap = it->second;
int pt, samplerate, channel; int pt, samplerate, channel;
char codec[16] = {0}; char codec[16] = {0};
sscanf(rtpmap.data(), "%d", &pt);
if (track._pt != pt) {
//pt不匹配
it = track._attr.erase(it);
continue;
}
if (4 == sscanf(rtpmap.data(), "%d %15[^/]/%d/%d", &pt, codec, &samplerate, &channel)) { if (4 == sscanf(rtpmap.data(), "%d %15[^/]/%d/%d", &pt, codec, &samplerate, &channel)) {
track._pt = pt;
track._codec = codec; track._codec = codec;
track._samplerate = samplerate; track._samplerate = samplerate;
track._channel = channel; track._channel = channel;
...@@ -248,11 +259,20 @@ void SdpParser::load(const string &sdp) { ...@@ -248,11 +259,20 @@ void SdpParser::load(const string &sdp) {
//未设置视频采样率时,赋值为90000 //未设置视频采样率时,赋值为90000
track._samplerate = 90000; track._samplerate = 90000;
} }
++it;
} }
it = track._attr.find("fmtp"); for (it = track._attr.find("fmtp"); it != track._attr.end(); ) {
if (it != track._attr.end()) { auto &fmtp = it->second;
track._fmtp = it->second; int pt;
sscanf(fmtp.data(), "%d", &pt);
if (track._pt != pt) {
//pt不匹配
it = track._attr.erase(it);
continue;
}
track._fmtp = FindField(fmtp.data(), " ", nullptr);
++it;
} }
it = track._attr.find("control"); it = track._attr.find("control");
...@@ -267,8 +287,8 @@ bool SdpParser::available() const { ...@@ -267,8 +287,8 @@ bool SdpParser::available() const {
} }
SdpTrack::Ptr SdpParser::getTrack(TrackType type) const { SdpTrack::Ptr SdpParser::getTrack(TrackType type) const {
for (auto &track : _track_vec){ for (auto &track : _track_vec) {
if(track->_type == type){ if (track->_type == type) {
return track; return track;
} }
} }
...@@ -279,17 +299,17 @@ vector<SdpTrack::Ptr> SdpParser::getAvailableTrack() const { ...@@ -279,17 +299,17 @@ vector<SdpTrack::Ptr> SdpParser::getAvailableTrack() const {
vector<SdpTrack::Ptr> ret; vector<SdpTrack::Ptr> ret;
bool audio_added = false; bool audio_added = false;
bool video_added = false; bool video_added = false;
for (auto &track : _track_vec){ for (auto &track : _track_vec) {
if(track->_type == TrackAudio ){ if (track->_type == TrackAudio) {
if(!audio_added){ if (!audio_added) {
ret.emplace_back(track); ret.emplace_back(track);
audio_added = true; audio_added = true;
} }
continue; continue;
} }
if(track->_type == TrackVideo ){ if (track->_type == TrackVideo) {
if(!video_added){ if (!video_added) {
ret.emplace_back(track); ret.emplace_back(track);
video_added = true; video_added = true;
} }
...@@ -300,18 +320,18 @@ vector<SdpTrack::Ptr> SdpParser::getAvailableTrack() const { ...@@ -300,18 +320,18 @@ vector<SdpTrack::Ptr> SdpParser::getAvailableTrack() const {
} }
string SdpParser::toString() const { string SdpParser::toString() const {
string title,audio,video; string title, audio, video;
for(auto &track : _track_vec){ for (auto &track : _track_vec) {
switch (track->_type){ switch (track->_type) {
case TrackTitle:{ case TrackTitle: {
title = track->toString(); title = track->toString();
} }
break; break;
case TrackVideo:{ case TrackVideo: {
video = track->toString(); video = track->toString();
} }
break; break;
case TrackAudio:{ case TrackAudio: {
audio = track->toString(); audio = track->toString();
} }
break; break;
...@@ -375,7 +395,7 @@ bool RtspUrl::setup(bool isSSL, const string &strUrl, const string &strUser, con ...@@ -375,7 +395,7 @@ bool RtspUrl::setup(bool isSSL, const string &strUrl, const string &strUser, con
return true; return true;
} }
static void makeSockPair_l(std::pair<Socket::Ptr, Socket::Ptr> &pair, const string &local_ip){ static void makeSockPair_l(std::pair<Socket::Ptr, Socket::Ptr> &pair, const string &local_ip) {
auto &pSockRtp = pair.first; auto &pSockRtp = pair.first;
auto &pSockRtcp = pair.second; auto &pSockRtcp = pair.second;
...@@ -399,23 +419,23 @@ static void makeSockPair_l(std::pair<Socket::Ptr, Socket::Ptr> &pair, const stri ...@@ -399,23 +419,23 @@ static void makeSockPair_l(std::pair<Socket::Ptr, Socket::Ptr> &pair, const stri
} }
} }
void makeSockPair(std::pair<Socket::Ptr, Socket::Ptr> &pair, const string &local_ip){ void makeSockPair(std::pair<Socket::Ptr, Socket::Ptr> &pair, const string &local_ip) {
int try_count = 0; int try_count = 0;
while (true) { while (true) {
try { try {
makeSockPair_l(pair, local_ip); makeSockPair_l(pair, local_ip);
break; break;
} catch (...) { } catch (...) {
if (++try_count == 3) { if (++try_count == 3) {
throw; throw;
} }
WarnL << "open udp socket failed, retry: " << try_count; WarnL << "open udp socket failed, retry: " << try_count;
} }
} }
} }
string printSSRC(uint32_t ui32Ssrc) { string printSSRC(uint32_t ui32Ssrc) {
char tmp[9] = { 0 }; char tmp[9] = {0};
ui32Ssrc = htonl(ui32Ssrc); ui32Ssrc = htonl(ui32Ssrc);
uint8_t *pSsrc = (uint8_t *) &ui32Ssrc; uint8_t *pSsrc = (uint8_t *) &ui32Ssrc;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
...@@ -424,7 +444,7 @@ string printSSRC(uint32_t ui32Ssrc) { ...@@ -424,7 +444,7 @@ string printSSRC(uint32_t ui32Ssrc) {
return tmp; return tmp;
} }
Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved){ Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved) {
auto rtp_tcp = BufferRaw::create(); auto rtp_tcp = BufferRaw::create();
rtp_tcp->setCapacity(RtpPacket::kRtpTcpHeaderSize); rtp_tcp->setCapacity(RtpPacket::kRtpTcpHeaderSize);
rtp_tcp->setSize(RtpPacket::kRtpTcpHeaderSize); rtp_tcp->setSize(RtpPacket::kRtpTcpHeaderSize);
...@@ -463,7 +483,7 @@ size_t RtpHeader::getExtSize() const { ...@@ -463,7 +483,7 @@ size_t RtpHeader::getExtSize() const {
return AV_RB16(ext_ptr + 2) << 2; return AV_RB16(ext_ptr + 2) << 2;
} }
uint16_t RtpHeader::getExtReserved() const{ uint16_t RtpHeader::getExtReserved() const {
//rtp有ext //rtp有ext
if (!ext) { if (!ext) {
return 0; return 0;
...@@ -498,7 +518,7 @@ size_t RtpHeader::getPaddingSize(size_t rtp_size) const { ...@@ -498,7 +518,7 @@ size_t RtpHeader::getPaddingSize(size_t rtp_size) const {
return *end; return *end;
} }
size_t RtpHeader::getPayloadSize(size_t rtp_size) const{ size_t RtpHeader::getPayloadSize(size_t rtp_size) const {
auto invalid_size = getPayloadOffset() + getPaddingSize(rtp_size); auto invalid_size = getPayloadOffset() + getPaddingSize(rtp_size);
if (invalid_size + RtpPacket::kRtpHeaderSize >= rtp_size) { if (invalid_size + RtpPacket::kRtpHeaderSize >= rtp_size) {
return 0; return 0;
...@@ -506,14 +526,14 @@ size_t RtpHeader::getPayloadSize(size_t rtp_size) const{ ...@@ -506,14 +526,14 @@ size_t RtpHeader::getPayloadSize(size_t rtp_size) const{
return rtp_size - invalid_size - RtpPacket::kRtpHeaderSize; return rtp_size - invalid_size - RtpPacket::kRtpHeaderSize;
} }
string RtpHeader::dumpString(size_t rtp_size) const{ string RtpHeader::dumpString(size_t rtp_size) const {
_StrPrinter printer; _StrPrinter printer;
printer << "version:" << (int)version << "\r\n"; printer << "version:" << (int) version << "\r\n";
printer << "padding:" << getPaddingSize(rtp_size) << "\r\n"; printer << "padding:" << getPaddingSize(rtp_size) << "\r\n";
printer << "ext:" << getExtSize() << "\r\n"; printer << "ext:" << getExtSize() << "\r\n";
printer << "csrc:" << getCsrcSize() << "\r\n"; printer << "csrc:" << getCsrcSize() << "\r\n";
printer << "mark:" << (int)mark << "\r\n"; printer << "mark:" << (int) mark << "\r\n";
printer << "pt:" << (int)pt << "\r\n"; printer << "pt:" << (int) pt << "\r\n";
printer << "seq:" << ntohs(seq) << "\r\n"; printer << "seq:" << ntohs(seq) << "\r\n";
printer << "stamp:" << ntohl(stamp) << "\r\n"; printer << "stamp:" << ntohl(stamp) << "\r\n";
printer << "ssrc:" << ntohl(ssrc) << "\r\n"; printer << "ssrc:" << ntohl(ssrc) << "\r\n";
...@@ -563,7 +583,7 @@ size_t RtpPacket::getPayloadSize() const { ...@@ -563,7 +583,7 @@ size_t RtpPacket::getPayloadSize() const {
return getHeader()->getPayloadSize(size() - kRtpTcpHeaderSize); return getHeader()->getPayloadSize(size() - kRtpTcpHeaderSize);
} }
RtpPacket::Ptr RtpPacket::create(){ RtpPacket::Ptr RtpPacket::create() {
#if 0 #if 0
static ResourcePool<RtpPacket> packet_pool; static ResourcePool<RtpPacket> packet_pool;
static onceToken token([]() { static onceToken token([]() {
...@@ -580,5 +600,5 @@ RtpPacket::Ptr RtpPacket::create(){ ...@@ -580,5 +600,5 @@ RtpPacket::Ptr RtpPacket::create(){
}//namespace mediakit }//namespace mediakit
namespace toolkit { namespace toolkit {
StatisticImp(mediakit::RtpPacket); StatisticImp(mediakit::RtpPacket);
} }
\ No newline at end of file
...@@ -220,7 +220,7 @@ public: ...@@ -220,7 +220,7 @@ public:
float _end = 0; float _end = 0;
map<char, string> _other; map<char, string> _other;
map<string, string> _attr; multimap<string, string> _attr;
string toString() const; string toString() const;
string getName() const; string getName() const;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论