替换sdp解析逻辑
This commit is contained in:
parent
a69d7d0f71
commit
6fe90fe4ba
|
|
@ -66,11 +66,11 @@ Sdp::Ptr Factory::getSdpByTrack(const Track::Ptr &track) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Track::Ptr Factory::getTrackBySdp(const string &sdp) {
|
Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) {
|
||||||
if (strcasestr(sdp.data(), "mpeg4-generic") != nullptr) {
|
if (strcasestr(track->_codec.data(), "mpeg4-generic") != nullptr) {
|
||||||
string aac_cfg_str = FindField(sdp.c_str(), "config=", "\r\n");
|
string aac_cfg_str = FindField(track->_fmtp.c_str(), "config=", "\r\n");
|
||||||
if (aac_cfg_str.size() != 4) {
|
if (aac_cfg_str.size() != 4) {
|
||||||
aac_cfg_str = FindField(sdp.c_str(), "config=", ";");
|
aac_cfg_str = FindField(track->_fmtp.c_str(), "config=", ";");
|
||||||
}
|
}
|
||||||
if (aac_cfg_str.size() != 4) {
|
if (aac_cfg_str.size() != 4) {
|
||||||
//延后获取adts头
|
//延后获取adts头
|
||||||
|
|
@ -91,8 +91,8 @@ Track::Ptr Factory::getTrackBySdp(const string &sdp) {
|
||||||
return std::make_shared<AACTrack>(aac_cfg);
|
return std::make_shared<AACTrack>(aac_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcasestr(sdp.data(), "h264") != nullptr) {
|
if (strcasestr(track->_codec.data(), "h264") != nullptr) {
|
||||||
string sps_pps = FindField(sdp.c_str(), "sprop-parameter-sets=", "\r\n");
|
string sps_pps = FindField(track->_fmtp.c_str(), "sprop-parameter-sets=", "\r\n");
|
||||||
if(sps_pps.empty()){
|
if(sps_pps.empty()){
|
||||||
return std::make_shared<H264Track>();
|
return std::make_shared<H264Track>();
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +107,7 @@ Track::Ptr Factory::getTrackBySdp(const string &sdp) {
|
||||||
return std::make_shared<H264Track>(sps,pps,0,0);
|
return std::make_shared<H264Track>(sps,pps,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
WarnL << "暂不支持该sdp:" << sdp;
|
WarnL << "暂不支持该sdp:" << track->_codec << " " << track->_fmtp;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* 根据sdp生成Track对象
|
* 根据sdp生成Track对象
|
||||||
*/
|
*/
|
||||||
static Track::Ptr getTrackBySdp(const string &sdp);
|
static Track::Ptr getTrackBySdp(const SdpTrack::Ptr &track);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据Track生成SDP对象
|
* 根据Track生成SDP对象
|
||||||
|
|
|
||||||
|
|
@ -110,13 +110,13 @@ void SdpAttr::load(const string &sdp) {
|
||||||
for (auto &pr : _track_map) {
|
for (auto &pr : _track_map) {
|
||||||
auto &track = *pr.second;
|
auto &track = *pr.second;
|
||||||
if (pr.first == "") {
|
if (pr.first == "") {
|
||||||
track._type = TrackTitle;
|
track.type = TrackTitle;
|
||||||
} else if (pr.first == "video") {
|
} else if (pr.first == "video") {
|
||||||
track._type = TrackVideo;
|
track.type = TrackVideo;
|
||||||
} else if (pr.first == "audio") {
|
} else if (pr.first == "audio") {
|
||||||
track._type = TrackAudio;
|
track.type = TrackAudio;
|
||||||
} else {
|
} else {
|
||||||
track._type = TrackInvalid;
|
track.type = TrackInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = track._attr.find("range");
|
auto it = track._attr.find("range");
|
||||||
|
|
@ -153,6 +153,8 @@ void SdpAttr::load(const string &sdp) {
|
||||||
it = track._attr.find("control");
|
it = track._attr.find("control");
|
||||||
if(it != track._attr.end()) {
|
if(it != track._attr.end()) {
|
||||||
track._control = it->second;
|
track._control = it->second;
|
||||||
|
auto surffix = string("/") + track._control;
|
||||||
|
track._control_surffix = surffix.substr(1 + surffix.rfind('/'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -163,64 +165,25 @@ bool SdpAttr::available() const {
|
||||||
|
|
||||||
SdpTrack::Ptr SdpAttr::getTrack(TrackType type) const {
|
SdpTrack::Ptr SdpAttr::getTrack(TrackType type) const {
|
||||||
for (auto &pr : _track_map){
|
for (auto &pr : _track_map){
|
||||||
if(pr.second->_type == type){
|
if(pr.second->type == type){
|
||||||
return pr.second;
|
return pr.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parserSDP(const string& sdp, RtspTrack Track[2]) {
|
vector<SdpTrack::Ptr> SdpAttr::getAvailableTrack() const {
|
||||||
int track_cnt = 0;
|
vector<SdpTrack::Ptr> ret;
|
||||||
string::size_type pos_head = 0;
|
auto video = getTrack(TrackVideo);
|
||||||
while ((pos_head = sdp.find("m=",pos_head)) != string::npos ) {
|
if(video){
|
||||||
auto pos_end = sdp.find("m=", pos_head + 2);
|
ret.emplace_back(video);
|
||||||
if (pos_end == string::npos) {
|
|
||||||
pos_end = sdp.size();
|
|
||||||
}
|
|
||||||
auto sdp_mid = sdp.substr(pos_head, pos_end - pos_head);
|
|
||||||
pos_head = pos_end;
|
|
||||||
Track[track_cnt].trackSdp = sdp_mid;
|
|
||||||
Track[track_cnt].inited = false;
|
|
||||||
Track[track_cnt].PT = atoi(FindField(sdp_mid.c_str(), "a=rtpmap:", " ").c_str());
|
|
||||||
auto control = string("/") + trim(FindField(sdp_mid.c_str(), "a=control:", "\n"));
|
|
||||||
Track[track_cnt].controlSuffix = control.substr(1 + control.rfind('/'));
|
|
||||||
|
|
||||||
if (sdp_mid.find("m=video") != string::npos) {
|
|
||||||
//视频通道
|
|
||||||
Track[track_cnt].type = TrackVideo;
|
|
||||||
} else if (sdp_mid.find("m=audio") != string::npos) {
|
|
||||||
//音频通道
|
|
||||||
Track[track_cnt].type = TrackAudio;
|
|
||||||
} else {
|
|
||||||
//不识别的track
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
track_cnt++;
|
|
||||||
}
|
}
|
||||||
return track_cnt;
|
auto audio = getTrack(TrackAudio);
|
||||||
|
if(audio){
|
||||||
|
ret.emplace_back(audio);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
static onceToken s_token([](){
|
|
||||||
string str = "v=0\n"
|
|
||||||
"o=- 1001 1 IN IP4 192.168.0.22\n"
|
|
||||||
"s=VCP IPC Realtime stream\n"
|
|
||||||
"a=range:npt=0-\n"
|
|
||||||
"m=video 0 RTP/AVP 105\n"
|
|
||||||
"c=IN IP4 192.168.0.22\n"
|
|
||||||
"a=control:rtsp://192.168.0.22/media/video1/video\n"
|
|
||||||
"a=rtpmap:105 H264/90000\n"
|
|
||||||
"a=fmtp:105 profile-level-id=64001f; packetization-mode=1; sprop-parameter-sets=Z2QAH6wrUCgC3QgAAB9AAAYahCAA,aO4xsg==\n"
|
|
||||||
"a=recvonly\n"
|
|
||||||
"m=application 0 RTP/AVP 107\n"
|
|
||||||
"c=IN IP4 192.168.0.22\n"
|
|
||||||
"a=control:rtsp://192.168.0.22/media/video1/metadata\n"
|
|
||||||
"a=rtpmap:107 vnd.onvif.metadata/90000\n"
|
|
||||||
"a=fmtp:107 DecoderTag=h3c-v3 RTCP=0\n"
|
|
||||||
"a=recvonly";
|
|
||||||
RtspTrack track[2];
|
|
||||||
parserSDP(str,track);
|
|
||||||
SdpAttr attr(str);
|
|
||||||
track[0].inited=true;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,34 +62,31 @@ public:
|
||||||
int _samplerate;
|
int _samplerate;
|
||||||
string _fmtp;
|
string _fmtp;
|
||||||
string _control;
|
string _control;
|
||||||
TrackType _type;
|
string _control_surffix;
|
||||||
|
TrackType type;
|
||||||
|
public:
|
||||||
|
uint8_t interleaved = 0;
|
||||||
|
bool inited = false;
|
||||||
|
uint32_t ssrc = 0;
|
||||||
|
uint16_t seq = 0;
|
||||||
|
uint32_t timeStamp = 0;
|
||||||
};
|
};
|
||||||
class SdpAttr {
|
class SdpAttr {
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<SdpAttr> Ptr;
|
typedef std::shared_ptr<SdpAttr> Ptr;
|
||||||
SdpAttr(const string &sdp){load(sdp);};
|
SdpAttr(){}
|
||||||
|
SdpAttr(const string &sdp){load(sdp);}
|
||||||
~SdpAttr(){}
|
~SdpAttr(){}
|
||||||
|
|
||||||
void load(const string &sdp);
|
void load(const string &sdp);
|
||||||
SdpTrack::Ptr getTrack(TrackType type) const;
|
bool available() const ;
|
||||||
bool available() const ;
|
|
||||||
|
SdpTrack::Ptr getTrack(TrackType type) const;
|
||||||
|
vector<SdpTrack::Ptr> getAvailableTrack() const;
|
||||||
private:
|
private:
|
||||||
map<string,SdpTrack::Ptr> _track_map;
|
map<string,SdpTrack::Ptr> _track_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RtspTrack{
|
|
||||||
public:
|
|
||||||
uint8_t PT;
|
|
||||||
uint8_t interleaved;
|
|
||||||
TrackType type = TrackInvalid;
|
|
||||||
string trackSdp;
|
|
||||||
string controlSuffix;
|
|
||||||
bool inited;
|
|
||||||
uint32_t ssrc = 0;
|
|
||||||
uint16_t seq;
|
|
||||||
uint32_t timeStamp;
|
|
||||||
uint32_t sampleRate;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RtcpCounter {
|
class RtcpCounter {
|
||||||
public:
|
public:
|
||||||
|
|
@ -99,7 +96,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
string FindField(const char* buf, const char* start, const char *end,int bufSize = 0 );
|
string FindField(const char* buf, const char* start, const char *end,int bufSize = 0 );
|
||||||
int parserSDP(const string& sdp, RtspTrack Track[2]);
|
|
||||||
|
|
||||||
struct StrCaseCompare
|
struct StrCaseCompare
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ public:
|
||||||
_pRing->write(rtppt,keyPos);
|
_pRing->write(rtppt,keyPos);
|
||||||
}
|
}
|
||||||
protected:
|
protected:
|
||||||
unordered_map<int, RtspTrack> _mapTracks;
|
unordered_map<int, SdpTrack> _mapTracks;
|
||||||
string _strSdp; //媒体描述信息
|
string _strSdp; //媒体描述信息
|
||||||
RingType::Ptr _pRing; //rtp环形缓冲
|
RingType::Ptr _pRing; //rtp环形缓冲
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ void RtspPlayer::teardown(){
|
||||||
|
|
||||||
erase(kRtspMd5Nonce);
|
erase(kRtspMd5Nonce);
|
||||||
erase(kRtspRealm);
|
erase(kRtspRealm);
|
||||||
_uiTrackCnt = 0;
|
_aTrackInfo.clear();
|
||||||
_onHandshake = nullptr;
|
_onHandshake = nullptr;
|
||||||
_uiRtpBufLen = 0;
|
_uiRtpBufLen = 0;
|
||||||
_strSession.clear();
|
_strSession.clear();
|
||||||
|
|
@ -276,17 +276,21 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//解析sdp
|
//解析sdp
|
||||||
_uiTrackCnt = parserSDP(strSdp, _aTrackInfo);
|
_sdpAttr.load(strSdp);
|
||||||
for (unsigned int i=0; i<_uiTrackCnt; i++) {
|
_aTrackInfo = _sdpAttr.getAvailableTrack();
|
||||||
_aTrackInfo[i].ssrc=0;
|
|
||||||
_aui32SsrcErrorCnt[i]=0;
|
if (_aTrackInfo.empty()) {
|
||||||
}
|
|
||||||
if (!_uiTrackCnt) {
|
|
||||||
throw std::runtime_error("解析SDP失败");
|
throw std::runtime_error("解析SDP失败");
|
||||||
}
|
}
|
||||||
if (!onCheckSDP(strSdp, _aTrackInfo, _uiTrackCnt)) {
|
if (!onCheckSDP(strSdp, _sdpAttr)) {
|
||||||
throw std::runtime_error("onCheckSDP faied");
|
throw std::runtime_error("onCheckSDP faied");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLEAR_ARR(_aui32SsrcErrorCnt)
|
||||||
|
for (auto &track : _aTrackInfo) {
|
||||||
|
track->ssrc=0;
|
||||||
|
}
|
||||||
|
|
||||||
sendSetup(0);
|
sendSetup(0);
|
||||||
}
|
}
|
||||||
//发送SETUP命令
|
//发送SETUP命令
|
||||||
|
|
@ -294,11 +298,11 @@ bool RtspPlayer::sendSetup(unsigned int trackIndex) {
|
||||||
_onHandshake = std::bind(&RtspPlayer::handleResSETUP,this, placeholders::_1,trackIndex);
|
_onHandshake = std::bind(&RtspPlayer::handleResSETUP,this, placeholders::_1,trackIndex);
|
||||||
|
|
||||||
auto &track = _aTrackInfo[trackIndex];
|
auto &track = _aTrackInfo[trackIndex];
|
||||||
auto baseUrl = _strContentBase + "/" + track.controlSuffix;
|
auto baseUrl = _strContentBase + "/" + track->_control_surffix;
|
||||||
switch (_eType) {
|
switch (_eType) {
|
||||||
case RTP_TCP: {
|
case RTP_TCP: {
|
||||||
StrCaseMap header;
|
StrCaseMap header;
|
||||||
header["Transport"] = StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track.type * 2 << "-" << track.type * 2 + 1;
|
header["Transport"] = StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->type * 2 << "-" << track->type * 2 + 1;
|
||||||
return sendRtspRequest("SETUP",baseUrl,header);
|
return sendRtspRequest("SETUP",baseUrl,header);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -348,7 +352,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex)
|
||||||
|
|
||||||
if(_eType == RTP_TCP) {
|
if(_eType == RTP_TCP) {
|
||||||
string interleaved = FindField( FindField((strTransport + ";").c_str(), "interleaved=", ";").c_str(), NULL, "-");
|
string interleaved = FindField( FindField((strTransport + ";").c_str(), "interleaved=", ";").c_str(), NULL, "-");
|
||||||
_aTrackInfo[uiTrackIndex].interleaved = atoi(interleaved.c_str());
|
_aTrackInfo[uiTrackIndex]->interleaved = atoi(interleaved.c_str());
|
||||||
}else{
|
}else{
|
||||||
const char *strPos = (_eType == RTP_MULTICAST ? "port=" : "server_port=") ;
|
const char *strPos = (_eType == RTP_MULTICAST ? "port=" : "server_port=") ;
|
||||||
auto port_str = FindField((strTransport + ";").c_str(), strPos, ";");
|
auto port_str = FindField((strTransport + ";").c_str(), strPos, ";");
|
||||||
|
|
@ -377,13 +381,13 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiTrackIndex < _uiTrackCnt - 1) {
|
if (uiTrackIndex < _aTrackInfo.size() - 1) {
|
||||||
//需要继续发送SETUP命令
|
//需要继续发送SETUP命令
|
||||||
sendSetup(uiTrackIndex + 1);
|
sendSetup(uiTrackIndex + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt && _eType != RTP_TCP; i++) {
|
for (unsigned int i = 0; i < _aTrackInfo.size() && _eType != RTP_TCP; i++) {
|
||||||
auto &pUdpSockRef = _apUdpSock[i];
|
auto &pUdpSockRef = _apUdpSock[i];
|
||||||
if(!pUdpSockRef){
|
if(!pUdpSockRef){
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -436,13 +440,8 @@ bool RtspPlayer::sendPause(bool bPause,float fTime){
|
||||||
_aNowStampTicker[0].resetTime();
|
_aNowStampTicker[0].resetTime();
|
||||||
_aNowStampTicker[1].resetTime();
|
_aNowStampTicker[1].resetTime();
|
||||||
float iTimeInc = fTime - getProgressTime();
|
float iTimeInc = fTime - getProgressTime();
|
||||||
for(unsigned int i = 0 ;i < _uiTrackCnt ;i++){
|
for(unsigned int i = 0 ;i < _aTrackInfo.size() ;i++){
|
||||||
if (_aTrackInfo[i].type == TrackVideo) {
|
_adFistStamp[i] = _adNowStamp[i] + iTimeInc * _aTrackInfo[i]->_samplerate;
|
||||||
_adFistStamp[i] = _adNowStamp[i] + iTimeInc * 90000.0;
|
|
||||||
}else if (_aTrackInfo[i].type == TrackAudio){
|
|
||||||
//todo(xzl) 修复此处
|
|
||||||
// _adFistStamp[i] = _adNowStamp[i] + iTimeInc * getAudioSampleRate();
|
|
||||||
}
|
|
||||||
_adNowStamp[i] = _adFistStamp[i];
|
_adNowStamp[i] = _adFistStamp[i];
|
||||||
}
|
}
|
||||||
_fSeekTo = fTime;
|
_fSeekTo = fTime;
|
||||||
|
|
@ -590,7 +589,7 @@ bool RtspPlayer::handleOneRtp(int iTrackidx, unsigned char *pucData, unsigned in
|
||||||
auto &track = _aTrackInfo[iTrackidx];
|
auto &track = _aTrackInfo[iTrackidx];
|
||||||
auto pt_ptr=_pktPool.obtain();
|
auto pt_ptr=_pktPool.obtain();
|
||||||
auto &rtppt=*pt_ptr;
|
auto &rtppt=*pt_ptr;
|
||||||
rtppt.interleaved = track.interleaved;
|
rtppt.interleaved = track->interleaved;
|
||||||
rtppt.length = uiLen + 4;
|
rtppt.length = uiLen + 4;
|
||||||
|
|
||||||
rtppt.mark = pucData[1] >> 7;
|
rtppt.mark = pucData[1] >> 7;
|
||||||
|
|
@ -604,15 +603,15 @@ bool RtspPlayer::handleOneRtp(int iTrackidx, unsigned char *pucData, unsigned in
|
||||||
//ssrc
|
//ssrc
|
||||||
memcpy(&rtppt.ssrc,pucData+8,4);//内存对齐
|
memcpy(&rtppt.ssrc,pucData+8,4);//内存对齐
|
||||||
rtppt.ssrc = ntohl(rtppt.ssrc);
|
rtppt.ssrc = ntohl(rtppt.ssrc);
|
||||||
rtppt.type = track.type;
|
rtppt.type = track->type;
|
||||||
if (track.ssrc == 0) {
|
if (track->ssrc == 0) {
|
||||||
track.ssrc = rtppt.ssrc;
|
track->ssrc = rtppt.ssrc;
|
||||||
//保存SSRC
|
//保存SSRC
|
||||||
} else if (track.ssrc != rtppt.ssrc) {
|
} else if (track->ssrc != rtppt.ssrc) {
|
||||||
//ssrc错误
|
//ssrc错误
|
||||||
WarnL << "ssrc错误";
|
WarnL << "ssrc错误";
|
||||||
if (_aui32SsrcErrorCnt[iTrackidx]++ > 10) {
|
if (_aui32SsrcErrorCnt[iTrackidx]++ > 10) {
|
||||||
track.ssrc = rtppt.ssrc;
|
track->ssrc = rtppt.ssrc;
|
||||||
WarnL << "ssrc更换!";
|
WarnL << "ssrc更换!";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -694,7 +693,7 @@ float RtspPlayer::getRtpLossRate(int iTrackType) const{
|
||||||
if(iTrackIdx == -1){
|
if(iTrackIdx == -1){
|
||||||
uint64_t totalRecv = 0;
|
uint64_t totalRecv = 0;
|
||||||
uint64_t totalSend = 0;
|
uint64_t totalSend = 0;
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||||
totalRecv += _aui64RtpRecv[i];
|
totalRecv += _aui64RtpRecv[i];
|
||||||
totalSend += (_aui16NowSeq[i] - _aui16FirstSeq[i] + 1);
|
totalSend += (_aui16NowSeq[i] - _aui16FirstSeq[i] + 1);
|
||||||
}
|
}
|
||||||
|
|
@ -713,15 +712,8 @@ float RtspPlayer::getRtpLossRate(int iTrackType) const{
|
||||||
|
|
||||||
float RtspPlayer::getProgressTime() const{
|
float RtspPlayer::getProgressTime() const{
|
||||||
double iTime[2] = {0,0};
|
double iTime[2] = {0,0};
|
||||||
for(unsigned int i = 0 ;i < _uiTrackCnt ;i++){
|
for(unsigned int i = 0 ;i < _aTrackInfo.size() ;i++){
|
||||||
if (_aTrackInfo[i].type == TrackVideo) {
|
iTime[i] = (_adNowStamp[i] - _adFistStamp[i]) / _aTrackInfo[i]->_samplerate;
|
||||||
iTime[i] = (_adNowStamp[i] - _adFistStamp[i]) / 90000.0;
|
|
||||||
}else if (_aTrackInfo[i].type == TrackAudio){
|
|
||||||
//todo(xzl) 修复此处
|
|
||||||
#if 0
|
|
||||||
iTime[i] = (_adNowStamp[i] - _adFistStamp[i]) / getAudioSampleRate();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return _fSeekTo + MAX(iTime[0],iTime[1]);
|
return _fSeekTo + MAX(iTime[0],iTime[1]);
|
||||||
}
|
}
|
||||||
|
|
@ -785,7 +777,7 @@ void RtspPlayer::onShutdown_l(const SockException &ex) {
|
||||||
_pBeatTimer.reset();
|
_pBeatTimer.reset();
|
||||||
onShutdown(ex);
|
onShutdown(ex);
|
||||||
}
|
}
|
||||||
void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const RtspTrack &track) {
|
void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track) {
|
||||||
_rtpTicker.resetTime();
|
_rtpTicker.resetTime();
|
||||||
onRecvRTP(pRtppt,track);
|
onRecvRTP(pRtppt,track);
|
||||||
}
|
}
|
||||||
|
|
@ -814,16 +806,16 @@ void RtspPlayer::onPlayResult_l(const SockException &ex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtspPlayer::getTrackIndexByControlSuffix(const string &controlSuffix) const{
|
int RtspPlayer::getTrackIndexByControlSuffix(const string &controlSuffix) const{
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||||
if (_aTrackInfo[i].controlSuffix == controlSuffix) {
|
if (_aTrackInfo[i]->_control_surffix == controlSuffix) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const{
|
int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const{
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||||
if (_aTrackInfo[i].interleaved == interleaved) {
|
if (_aTrackInfo[i]->interleaved == interleaved) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -831,8 +823,8 @@ int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const{
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtspPlayer::getTrackIndexByTrackType(TrackType trackType) const {
|
int RtspPlayer::getTrackIndexByTrackType(TrackType trackType) const {
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||||
if (_aTrackInfo[i].type == trackType) {
|
if (_aTrackInfo[i]->type == trackType) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,14 +58,14 @@ public:
|
||||||
float getRtpLossRate(int iTrackType) const override;
|
float getRtpLossRate(int iTrackType) const override;
|
||||||
protected:
|
protected:
|
||||||
//派生类回调函数
|
//派生类回调函数
|
||||||
virtual bool onCheckSDP(const string &strSdp, const RtspTrack *pTrack, int iTrackCnt) = 0;
|
virtual bool onCheckSDP(const string &strSdp, const SdpAttr &sdpAttr) = 0;
|
||||||
virtual void onRecvRTP(const RtpPacket::Ptr &pRtppt, const RtspTrack &track) = 0;
|
virtual void onRecvRTP(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track) = 0;
|
||||||
float getProgressTime() const;
|
float getProgressTime() const;
|
||||||
void seekToTime(float fTime);
|
void seekToTime(float fTime);
|
||||||
private:
|
private:
|
||||||
void onShutdown_l(const SockException &ex);
|
void onShutdown_l(const SockException &ex);
|
||||||
void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, int iTrackidx);
|
void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, int iTrackidx);
|
||||||
void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const RtspTrack &track);
|
void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track);
|
||||||
void onPlayResult_l(const SockException &ex);
|
void onPlayResult_l(const SockException &ex);
|
||||||
|
|
||||||
int getTrackIndexByControlSuffix(const string &controlSuffix) const;
|
int getTrackIndexByControlSuffix(const string &controlSuffix) const;
|
||||||
|
|
@ -97,8 +97,9 @@ private:
|
||||||
bool sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap());
|
bool sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap());
|
||||||
private:
|
private:
|
||||||
string _strUrl;
|
string _strUrl;
|
||||||
unsigned int _uiTrackCnt = 0;
|
|
||||||
RtspTrack _aTrackInfo[2];
|
SdpAttr _sdpAttr;
|
||||||
|
vector<SdpTrack::Ptr> _aTrackInfo;
|
||||||
|
|
||||||
function<void(const Parser&)> _onHandshake;
|
function<void(const Parser&)> _onHandshake;
|
||||||
RtspMediaSource::PoolType _pktPool;
|
RtspMediaSource::PoolType _pktPool;
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ public:
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
//派生类回调函数
|
//派生类回调函数
|
||||||
bool onCheckSDP(const string &sdp, const RtspTrack *track, int trackCnt) override {
|
bool onCheckSDP(const string &sdp, const SdpAttr &sdpAttr) override {
|
||||||
_pRtspMediaSrc = dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc);
|
_pRtspMediaSrc = dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc);
|
||||||
if(_pRtspMediaSrc){
|
if(_pRtspMediaSrc){
|
||||||
_pRtspMediaSrc->onGetSDP(sdp);
|
_pRtspMediaSrc->onGetSDP(sdp);
|
||||||
|
|
@ -70,7 +70,7 @@ private:
|
||||||
_parser.reset(new RtspDemuxer(sdp));
|
_parser.reset(new RtspDemuxer(sdp));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void onRecvRTP(const RtpPacket::Ptr &rtppt, const RtspTrack &track) override {
|
void onRecvRTP(const RtpPacket::Ptr &rtppt, const SdpTrack::Ptr &track) override {
|
||||||
if(_pRtspMediaSrc){
|
if(_pRtspMediaSrc){
|
||||||
_pRtspMediaSrc->onWrite(rtppt,true);
|
_pRtspMediaSrc->onWrite(rtppt,true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -511,12 +511,12 @@ bool RtspSession::handleReq_Setup() {
|
||||||
//未找到相应track
|
//未找到相应track
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
RtspTrack &trackRef = _aTrackInfo[trackIdx];
|
SdpTrack::Ptr &trackRef = _aTrackInfo[trackIdx];
|
||||||
if (trackRef.inited) {
|
if (trackRef->inited) {
|
||||||
//已经初始化过该Track
|
//已经初始化过该Track
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
trackRef.inited = true; //现在初始化
|
trackRef->inited = true; //现在初始化
|
||||||
|
|
||||||
auto strongRing = _pWeakRing.lock();
|
auto strongRing = _pWeakRing.lock();
|
||||||
if (!strongRing) {
|
if (!strongRing) {
|
||||||
|
|
@ -562,9 +562,9 @@ bool RtspSession::handleReq_Setup() {
|
||||||
"x-Dynamic-Rate: 1\r\n\r\n",
|
"x-Dynamic-Rate: 1\r\n\r\n",
|
||||||
_iCseq, SERVER_NAME,
|
_iCseq, SERVER_NAME,
|
||||||
RTSP_VERSION, RTSP_BUILDTIME,
|
RTSP_VERSION, RTSP_BUILDTIME,
|
||||||
dateHeader().data(), trackRef.type * 2,
|
dateHeader().data(), trackRef->type * 2,
|
||||||
trackRef.type * 2 + 1,
|
trackRef->type * 2 + 1,
|
||||||
printSSRC(trackRef.ssrc).data(),
|
printSSRC(trackRef->ssrc).data(),
|
||||||
_strSession.data());
|
_strSession.data());
|
||||||
SocketHelper::send(_pcBuf, iLen);
|
SocketHelper::send(_pcBuf, iLen);
|
||||||
}
|
}
|
||||||
|
|
@ -609,7 +609,7 @@ bool RtspSession::handleReq_Setup() {
|
||||||
RTSP_VERSION, RTSP_BUILDTIME,
|
RTSP_VERSION, RTSP_BUILDTIME,
|
||||||
dateHeader().data(), strClientPort.data(),
|
dateHeader().data(), strClientPort.data(),
|
||||||
pSockRtp->get_local_port(), pSockRtcp->get_local_port(),
|
pSockRtp->get_local_port(), pSockRtcp->get_local_port(),
|
||||||
printSSRC(trackRef.ssrc).data(),
|
printSSRC(trackRef->ssrc).data(),
|
||||||
_strSession.data());
|
_strSession.data());
|
||||||
SocketHelper::send(_pcBuf, n);
|
SocketHelper::send(_pcBuf, n);
|
||||||
}
|
}
|
||||||
|
|
@ -630,7 +630,7 @@ bool RtspSession::handleReq_Setup() {
|
||||||
strongSelf->safeShutdown();
|
strongSelf->safeShutdown();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
int iSrvPort = _pBrdcaster->getPort(trackRef.type);
|
int iSrvPort = _pBrdcaster->getPort(trackRef->type);
|
||||||
//我们用trackIdx区分rtp和rtcp包
|
//我们用trackIdx区分rtp和rtcp包
|
||||||
auto pSockRtcp = UDPServer::Instance().getSock(get_local_ip().data(),2*trackIdx + 1,iSrvPort + 1);
|
auto pSockRtcp = UDPServer::Instance().getSock(get_local_ip().data(),2*trackIdx + 1,iSrvPort + 1);
|
||||||
if (!pSockRtcp) {
|
if (!pSockRtcp) {
|
||||||
|
|
@ -652,7 +652,7 @@ bool RtspSession::handleReq_Setup() {
|
||||||
RTSP_VERSION, RTSP_BUILDTIME,
|
RTSP_VERSION, RTSP_BUILDTIME,
|
||||||
dateHeader().data(), _pBrdcaster->getIP().data(),
|
dateHeader().data(), _pBrdcaster->getIP().data(),
|
||||||
get_local_ip().data(), iSrvPort, pSockRtcp->get_local_port(),
|
get_local_ip().data(), iSrvPort, pSockRtcp->get_local_port(),
|
||||||
udpTTL,printSSRC(trackRef.ssrc).data(),
|
udpTTL,printSSRC(trackRef->ssrc).data(),
|
||||||
_strSession.data());
|
_strSession.data());
|
||||||
SocketHelper::send(_pcBuf, n);
|
SocketHelper::send(_pcBuf, n);
|
||||||
}
|
}
|
||||||
|
|
@ -733,9 +733,9 @@ bool RtspSession::handleReq_Play() {
|
||||||
}
|
}
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
||||||
auto &track = _aTrackInfo[i];
|
auto &track = _aTrackInfo[i];
|
||||||
track.ssrc = pMediaSrc->getSsrc(track.type);
|
track->ssrc = pMediaSrc->getSsrc(track->type);
|
||||||
track.seq = pMediaSrc->getSeqence(track.type);
|
track->seq = pMediaSrc->getSeqence(track->type);
|
||||||
track.timeStamp = pMediaSrc->getTimestamp(track.type);
|
track->timeStamp = pMediaSrc->getTimestamp(track->type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_bFirstPlay = false;
|
_bFirstPlay = false;
|
||||||
|
|
@ -750,13 +750,13 @@ bool RtspSession::handleReq_Play() {
|
||||||
|
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
||||||
auto &track = _aTrackInfo[i];
|
auto &track = _aTrackInfo[i];
|
||||||
if (track.inited == false) {
|
if (track->inited == false) {
|
||||||
//还有track没有setup
|
//还有track没有setup
|
||||||
shutdown();
|
shutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
iLen += sprintf(_pcBuf + iLen, "url=%s/%s;seq=%d;rtptime=%u,",
|
iLen += sprintf(_pcBuf + iLen, "url=%s/%s;seq=%d;rtptime=%u,",
|
||||||
_strUrl.data(), track.controlSuffix.data(), track.seq,track.timeStamp);
|
_strUrl.data(), track->_control_surffix.data(), track->seq,track->timeStamp);
|
||||||
}
|
}
|
||||||
iLen -= 1;
|
iLen -= 1;
|
||||||
(_pcBuf)[iLen] = '\0';
|
(_pcBuf)[iLen] = '\0';
|
||||||
|
|
@ -895,7 +895,10 @@ inline bool RtspSession::findStream() {
|
||||||
_strSdp = pMediaSrc->getSdp();
|
_strSdp = pMediaSrc->getSdp();
|
||||||
_pWeakRing = pMediaSrc->getRing();
|
_pWeakRing = pMediaSrc->getRing();
|
||||||
|
|
||||||
_uiTrackCnt = parserSDP(_strSdp, _aTrackInfo);
|
_sdpAttr.load(_strSdp);
|
||||||
|
_aTrackInfo = _sdpAttr.getAvailableTrack();
|
||||||
|
_uiTrackCnt = _aTrackInfo.size();
|
||||||
|
|
||||||
if (_uiTrackCnt == 0 || _uiTrackCnt > 2) {
|
if (_uiTrackCnt == 0 || _uiTrackCnt > 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -904,9 +907,9 @@ inline bool RtspSession::findStream() {
|
||||||
|
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
||||||
auto &track = _aTrackInfo[i];
|
auto &track = _aTrackInfo[i];
|
||||||
track.ssrc = pMediaSrc->getSsrc(track.type);
|
track->ssrc = pMediaSrc->getSsrc(track->type);
|
||||||
track.seq = pMediaSrc->getSeqence(track.type);
|
track->seq = pMediaSrc->getSeqence(track->type);
|
||||||
track.timeStamp = pMediaSrc->getTimestamp(track.type);
|
track->timeStamp = pMediaSrc->getTimestamp(track->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ private:
|
||||||
}
|
}
|
||||||
inline int getTrackIndexByTrackType(TrackType type) {
|
inline int getTrackIndexByTrackType(TrackType type) {
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
||||||
if (type == _aTrackInfo[i].type) {
|
if (type == _aTrackInfo[i]->type) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +126,7 @@ private:
|
||||||
}
|
}
|
||||||
inline int getTrackIndexByControlSuffix(const string &controlSuffix) {
|
inline int getTrackIndexByControlSuffix(const string &controlSuffix) {
|
||||||
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
for (unsigned int i = 0; i < _uiTrackCnt; i++) {
|
||||||
if (controlSuffix == _aTrackInfo[i].controlSuffix) {
|
if (controlSuffix == _aTrackInfo[i]->_control_surffix) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -161,8 +161,11 @@ private:
|
||||||
PlayerBase::eRtpType _rtpType = PlayerBase::RTP_UDP;
|
PlayerBase::eRtpType _rtpType = PlayerBase::RTP_UDP;
|
||||||
bool _bSetUped = false;
|
bool _bSetUped = false;
|
||||||
int _iCseq = 0;
|
int _iCseq = 0;
|
||||||
|
|
||||||
|
SdpAttr _sdpAttr;
|
||||||
unsigned int _uiTrackCnt = 0; //媒体track个数
|
unsigned int _uiTrackCnt = 0; //媒体track个数
|
||||||
RtspTrack _aTrackInfo[2]; //媒体track信息,trackid idx 为数组下标
|
vector<SdpTrack::Ptr> _aTrackInfo;
|
||||||
|
|
||||||
bool _bGotAllPeerUdp = false;
|
bool _bGotAllPeerUdp = false;
|
||||||
|
|
||||||
#ifdef RTSP_SEND_RTCP
|
#ifdef RTSP_SEND_RTCP
|
||||||
|
|
|
||||||
|
|
@ -51,25 +51,34 @@ static int getTimeInSDP(const string &sdp) {
|
||||||
return atof(strEnd.data()) - atof(strStart.data());
|
return atof(strEnd.data()) - atof(strStart.data());
|
||||||
}
|
}
|
||||||
RtspDemuxer::RtspDemuxer(const string& sdp) {
|
RtspDemuxer::RtspDemuxer(const string& sdp) {
|
||||||
RtspTrack tmp[2];
|
loadSdp(SdpAttr(sdp));
|
||||||
int cnt = parserSDP(sdp, tmp);
|
|
||||||
for (int i = 0; i < cnt; i++) {
|
|
||||||
switch (tmp[i].type) {
|
|
||||||
case TrackVideo: {
|
|
||||||
makeVideoTrack(tmp[i]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TrackAudio: {
|
|
||||||
makeAudioTrack(tmp[i]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_fDuration = getTimeInSDP(sdp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RtspDemuxer::RtspDemuxer(const SdpAttr &attr) {
|
||||||
|
loadSdp(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RtspDemuxer::loadSdp(const SdpAttr &attr) {
|
||||||
|
auto tracks = attr.getAvailableTrack();
|
||||||
|
for (auto &track : tracks){
|
||||||
|
switch (track->type) {
|
||||||
|
case TrackVideo: {
|
||||||
|
makeVideoTrack(track);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TrackAudio: {
|
||||||
|
makeAudioTrack(track);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto titleTrack = attr.getTrack(TrackTitle);
|
||||||
|
if(titleTrack){
|
||||||
|
_fDuration = titleTrack->_duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
bool RtspDemuxer::inputRtp(const RtpPacket::Ptr & rtp) {
|
bool RtspDemuxer::inputRtp(const RtpPacket::Ptr & rtp) {
|
||||||
switch (rtp->type) {
|
switch (rtp->type) {
|
||||||
case TrackVideo:{
|
case TrackVideo:{
|
||||||
|
|
@ -91,9 +100,9 @@ bool RtspDemuxer::inputRtp(const RtpPacket::Ptr & rtp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RtspDemuxer::makeAudioTrack(const RtspTrack &audio) {
|
void RtspDemuxer::makeAudioTrack(const SdpTrack::Ptr &audio) {
|
||||||
//生成Track对象
|
//生成Track对象
|
||||||
_audioTrack = dynamic_pointer_cast<AudioTrack>(Factory::getTrackBySdp(audio.trackSdp));
|
_audioTrack = dynamic_pointer_cast<AudioTrack>(Factory::getTrackBySdp(audio));
|
||||||
if(_audioTrack){
|
if(_audioTrack){
|
||||||
//生成RtpCodec对象以便解码rtp
|
//生成RtpCodec对象以便解码rtp
|
||||||
_audioRtpDecoder = Factory::getRtpDecoderById(_audioTrack->getCodecId(),_audioTrack->getAudioSampleRate());
|
_audioRtpDecoder = Factory::getRtpDecoderById(_audioTrack->getCodecId(),_audioTrack->getAudioSampleRate());
|
||||||
|
|
@ -107,9 +116,9 @@ void RtspDemuxer::makeAudioTrack(const RtspTrack &audio) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspDemuxer::makeVideoTrack(const RtspTrack &video) {
|
void RtspDemuxer::makeVideoTrack(const SdpTrack::Ptr &video) {
|
||||||
//生成Track对象
|
//生成Track对象
|
||||||
_videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getTrackBySdp(video.trackSdp));
|
_videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getTrackBySdp(video));
|
||||||
if(_videoTrack){
|
if(_videoTrack){
|
||||||
//生成RtpCodec对象以便解码rtp
|
//生成RtpCodec对象以便解码rtp
|
||||||
_videoRtpDecoder = Factory::getRtpDecoderById(_videoTrack->getCodecId(),90000);
|
_videoRtpDecoder = Factory::getRtpDecoderById(_videoTrack->getCodecId(),90000);
|
||||||
|
|
@ -152,5 +161,4 @@ float RtspDemuxer::getDuration() const {
|
||||||
return _fDuration;
|
return _fDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} /* namespace mediakit */
|
} /* namespace mediakit */
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ class RtspDemuxer : public PlayerBase{
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<RtspDemuxer> Ptr;
|
typedef std::shared_ptr<RtspDemuxer> Ptr;
|
||||||
RtspDemuxer(const string &sdp);
|
RtspDemuxer(const string &sdp);
|
||||||
|
RtspDemuxer(const SdpAttr &attr);
|
||||||
virtual ~RtspDemuxer(){};
|
virtual ~RtspDemuxer(){};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -72,8 +73,9 @@ public:
|
||||||
*/
|
*/
|
||||||
vector<Track::Ptr> getTracks() const override;
|
vector<Track::Ptr> getTracks() const override;
|
||||||
private:
|
private:
|
||||||
void makeAudioTrack(const RtspTrack &audio);
|
void makeAudioTrack(const SdpTrack::Ptr &audio);
|
||||||
void makeVideoTrack(const RtspTrack &video);
|
void makeVideoTrack(const SdpTrack::Ptr &video);
|
||||||
|
void loadSdp(const SdpAttr &attr);
|
||||||
private:
|
private:
|
||||||
float _fDuration = 0;
|
float _fDuration = 0;
|
||||||
AudioTrack::Ptr _audioTrack;
|
AudioTrack::Ptr _audioTrack;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue