mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 19:00:01 +08:00
防止时间戳回滚
This commit is contained in:
parent
5efea2d0cc
commit
ec925a64e4
@ -28,51 +28,51 @@
|
|||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
void Stamp::revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_out) {
|
int64_t DeltaStamp::deltaStamp(int64_t stamp) {
|
||||||
if(!pts){
|
if(!_last_stamp){
|
||||||
//没有播放时间戳,使其赋值为解码时间戳
|
//第一次计算时间戳增量,时间戳增量为0
|
||||||
|
_last_stamp = stamp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ret = stamp - _last_stamp;
|
||||||
|
if(ret >= 0){
|
||||||
|
//时间戳增量为正,返回之
|
||||||
|
_last_stamp = stamp;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//时间戳增量为负,说明时间戳回环了或回退了
|
||||||
|
_last_stamp = stamp;
|
||||||
|
return _playback ? ret : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeltaStamp::setPlayBack(bool playback) {
|
||||||
|
_playback = playback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out) {
|
||||||
|
if(!dts && !pts){
|
||||||
|
//没有时间戳,我们生成时间戳
|
||||||
|
pts = dts = _ticker.elapsedTime();
|
||||||
|
}else if(!pts){
|
||||||
|
//只是没有播放时间戳,使其赋值为解码时间戳
|
||||||
pts = dts;
|
pts = dts;
|
||||||
}
|
}
|
||||||
|
|
||||||
//pts和dts的差值
|
//pts和dts的差值
|
||||||
int pts_dts_diff = pts - dts;
|
int pts_dts_diff = pts - dts;
|
||||||
|
|
||||||
if(_first){
|
|
||||||
//记录第一次时间戳,后面好计算时间戳增量
|
|
||||||
_start_dts = dts;
|
|
||||||
_first = false;
|
|
||||||
_ticker.resetTime();
|
|
||||||
}
|
|
||||||
if (!dts) {
|
|
||||||
//没有解码时间戳,我们生成解码时间戳
|
|
||||||
dts = _ticker.elapsedTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
//相对时间戳
|
//相对时间戳
|
||||||
dts_out = dts - _start_dts;
|
_relativeStamp += deltaStamp(dts);
|
||||||
if(!_playback){
|
dts_out = _relativeStamp;
|
||||||
if(dts_out < _dts_inc){
|
|
||||||
//本次相对时间戳竟然小于上次?
|
|
||||||
if(dts_out < 0 || _dts_inc - dts_out > 0xFFFF){
|
|
||||||
//时间戳回环,保证下次相对时间戳与本次相对合理增长
|
|
||||||
_start_dts = dts - _dts_inc;
|
|
||||||
//本次时间戳强制等于上次时间戳
|
|
||||||
dts_out = _dts_inc;
|
|
||||||
}else{
|
|
||||||
//时间戳变小了?,那么取上次时间戳
|
|
||||||
dts_out = _dts_inc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//保留这次相对时间戳,以便下次对比是否回环或乱序
|
|
||||||
_dts_inc = dts_out;
|
|
||||||
|
|
||||||
//////////////以下是播放时间戳的计算//////////////////
|
//////////////以下是播放时间戳的计算//////////////////
|
||||||
if(pts_dts_diff > 200 || pts_dts_diff < -200){
|
if(pts_dts_diff > 200 || pts_dts_diff < -200){
|
||||||
//如果差值大于200毫秒,则认为由于回环导致时间戳错乱了
|
//如果差值大于200毫秒,则认为由于回环导致时间戳错乱了
|
||||||
pts_dts_diff = 0;
|
pts_dts_diff = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pts_out = dts_out + pts_dts_diff;
|
pts_out = dts_out + pts_dts_diff;
|
||||||
if(pts_out < 0){
|
if(pts_out < 0){
|
||||||
//时间戳不能小于0
|
//时间戳不能小于0
|
||||||
@ -80,8 +80,13 @@ void Stamp::revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_ou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stamp::setPlayBack(bool playback) {
|
void Stamp::setRelativeStamp(int64_t relativeStamp) {
|
||||||
_playback = playback;
|
_relativeStamp = relativeStamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t Stamp::getRelativeStamp() const {
|
||||||
|
return _relativeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
@ -33,18 +33,33 @@ using namespace toolkit;
|
|||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
//该类解决时间戳回环、回退问题
|
class DeltaStamp{
|
||||||
//计算相对时间戳或者产生平滑时间戳
|
|
||||||
class Stamp {
|
|
||||||
public:
|
public:
|
||||||
Stamp() = default;
|
DeltaStamp() = default;
|
||||||
~Stamp() = default;
|
~DeltaStamp() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置回放模式,回放模式时间戳可以回退
|
* 计算时间戳增量
|
||||||
|
* @param stamp 绝对时间戳
|
||||||
|
* @return 时间戳增量
|
||||||
|
*/
|
||||||
|
int64_t deltaStamp(int64_t stamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否为回放模式,回放模式运行时间戳回退
|
||||||
* @param playback 是否为回放模式
|
* @param playback 是否为回放模式
|
||||||
*/
|
*/
|
||||||
void setPlayBack(bool playback = true);
|
void setPlayBack(bool playback = true);
|
||||||
|
private:
|
||||||
|
int64_t _last_stamp = 0;
|
||||||
|
bool _playback = false;
|
||||||
|
};
|
||||||
|
//该类解决时间戳回环、回退问题
|
||||||
|
//计算相对时间戳或者产生平滑时间戳
|
||||||
|
class Stamp : public DeltaStamp{
|
||||||
|
public:
|
||||||
|
Stamp() = default;
|
||||||
|
~Stamp() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修正时间戳
|
* 修正时间戳
|
||||||
@ -53,12 +68,21 @@ public:
|
|||||||
* @param dts_out 输出dts
|
* @param dts_out 输出dts
|
||||||
* @param pts_out 输出pts
|
* @param pts_out 输出pts
|
||||||
*/
|
*/
|
||||||
void revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_out);
|
void revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 再设置相对时间戳,用于seek用
|
||||||
|
* @param relativeStamp 相对时间戳
|
||||||
|
*/
|
||||||
|
void setRelativeStamp(int64_t relativeStamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前相对时间戳
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int64_t getRelativeStamp() const ;
|
||||||
private:
|
private:
|
||||||
bool _playback = false;
|
int64_t _relativeStamp = 0;
|
||||||
int64_t _start_dts = 0;
|
|
||||||
int64_t _dts_inc = 0;
|
|
||||||
bool _first = true;
|
|
||||||
SmoothTicker _ticker;
|
SmoothTicker _ticker;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,12 +68,9 @@ void RtspPlayer::teardown(){
|
|||||||
CLEAR_ARR(_aui64RtpRecv)
|
CLEAR_ARR(_aui64RtpRecv)
|
||||||
CLEAR_ARR(_aui64RtpRecv)
|
CLEAR_ARR(_aui64RtpRecv)
|
||||||
CLEAR_ARR(_aui16NowSeq)
|
CLEAR_ARR(_aui16NowSeq)
|
||||||
CLEAR_ARR(_aiFistStamp);
|
|
||||||
CLEAR_ARR(_aiNowStamp);
|
|
||||||
|
|
||||||
_pPlayTimer.reset();
|
_pPlayTimer.reset();
|
||||||
_pRtpTimer.reset();
|
_pRtpTimer.reset();
|
||||||
_iSeekTo = 0;
|
|
||||||
_uiCseq = 1;
|
_uiCseq = 1;
|
||||||
_onHandshake = nullptr;
|
_onHandshake = nullptr;
|
||||||
}
|
}
|
||||||
@ -222,6 +219,16 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
|
|||||||
SdpParser sdpParser(parser.Content());
|
SdpParser sdpParser(parser.Content());
|
||||||
//解析sdp
|
//解析sdp
|
||||||
_aTrackInfo = sdpParser.getAvailableTrack();
|
_aTrackInfo = sdpParser.getAvailableTrack();
|
||||||
|
auto title = sdpParser.getTrack(TrackTitle);
|
||||||
|
bool isPlayback = false;
|
||||||
|
if(title && title->_duration ){
|
||||||
|
isPlayback = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto &stamp : _stamp){
|
||||||
|
stamp.setPlayBack(isPlayback);
|
||||||
|
stamp.setRelativeStamp(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (_aTrackInfo.empty()) {
|
if (_aTrackInfo.empty()) {
|
||||||
throw std::runtime_error("无有效的Sdp Track");
|
throw std::runtime_error("无有效的Sdp Track");
|
||||||
@ -386,7 +393,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex)
|
|||||||
}
|
}
|
||||||
//所有setup命令发送完毕
|
//所有setup命令发送完毕
|
||||||
//发送play命令
|
//发送play命令
|
||||||
pause(false);
|
sendPause(false, 0,false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPlayer::sendOptions() {
|
void RtspPlayer::sendOptions() {
|
||||||
@ -403,25 +410,19 @@ void RtspPlayer::sendDescribe() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RtspPlayer::sendPause(bool bPause,uint32_t seekMS){
|
void RtspPlayer::sendPause(bool bPause,uint32_t seekMS,bool range){
|
||||||
if(!bPause){
|
|
||||||
//修改时间轴
|
|
||||||
int iTimeInc = seekMS - getProgressMilliSecond();
|
|
||||||
for(unsigned int i = 0 ;i < _aTrackInfo.size() ;i++){
|
|
||||||
_aiFistStamp[i] = _aiNowStamp[i] + iTimeInc;
|
|
||||||
_aiNowStamp[i] = _aiFistStamp[i];
|
|
||||||
}
|
|
||||||
_iSeekTo = seekMS;
|
|
||||||
}
|
|
||||||
|
|
||||||
//开启或暂停rtsp
|
//开启或暂停rtsp
|
||||||
_onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,bPause);
|
_onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,bPause);
|
||||||
sendRtspRequest(bPause ? "PAUSE" : "PLAY",
|
if(!bPause && range){
|
||||||
_strContentBase,
|
sendRtspRequest(bPause ? "PAUSE" : "PLAY", _strContentBase,
|
||||||
{"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"});
|
{"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"});
|
||||||
|
} else{
|
||||||
|
sendRtspRequest(bPause ? "PAUSE" : "PLAY", _strContentBase);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
void RtspPlayer::pause(bool bPause) {
|
void RtspPlayer::pause(bool bPause) {
|
||||||
sendPause(bPause, getProgressMilliSecond());
|
sendPause(bPause, getProgressMilliSecond(),false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPlayer::handleResPAUSE(const Parser& parser, bool bPause) {
|
void RtspPlayer::handleResPAUSE(const Parser& parser, bool bPause) {
|
||||||
@ -430,6 +431,7 @@ void RtspPlayer::handleResPAUSE(const Parser& parser, bool bPause) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!bPause) {
|
if (!bPause) {
|
||||||
|
uint32_t iSeekTo = 0;
|
||||||
//修正时间轴
|
//修正时间轴
|
||||||
auto strRange = parser["Range"];
|
auto strRange = parser["Range"];
|
||||||
if (strRange.size()) {
|
if (strRange.size()) {
|
||||||
@ -437,25 +439,12 @@ void RtspPlayer::handleResPAUSE(const Parser& parser, bool bPause) {
|
|||||||
if (strStart == "now") {
|
if (strStart == "now") {
|
||||||
strStart = "0";
|
strStart = "0";
|
||||||
}
|
}
|
||||||
_iSeekTo = 1000 * atof(strStart.data());
|
iSeekTo = 1000 * atof(strStart.data());
|
||||||
DebugL << "seekTo(ms):" << _iSeekTo ;
|
DebugL << "seekTo(ms):" << iSeekTo ;
|
||||||
}
|
|
||||||
auto strRtpInfo = parser["RTP-Info"];
|
|
||||||
if (strRtpInfo.size()) {
|
|
||||||
strRtpInfo.append(",");
|
|
||||||
vector<string> vec = split(strRtpInfo, ",");
|
|
||||||
for(auto &strTrack : vec){
|
|
||||||
strTrack.append(";");
|
|
||||||
auto strControlSuffix = strTrack.substr(1 + strTrack.rfind('/'),strTrack.find(';') - strTrack.rfind('/') - 1);
|
|
||||||
auto strRtpTime = FindField(strTrack.data(), "rtptime=", ";");
|
|
||||||
auto idx = getTrackIndexByControlSuffix(strControlSuffix);
|
|
||||||
if(idx != -1){
|
|
||||||
_aiFistStamp[idx] = _aTrackInfo[idx]->_samplerate>0?atoll(strRtpTime.data()) * 1000 / _aTrackInfo[idx]->_samplerate :1;
|
|
||||||
_aiNowStamp[idx] = _aiFistStamp[idx];
|
|
||||||
DebugL << "rtptime(ms):" << strControlSuffix <<" " << strRtpTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
//设置相对时间戳
|
||||||
|
_stamp[0].setRelativeStamp(iSeekTo);
|
||||||
|
_stamp[1].setRelativeStamp(iSeekTo);
|
||||||
onPlayResult_l(SockException(Err_success, "rtsp play success"));
|
onPlayResult_l(SockException(Err_success, "rtsp play success"));
|
||||||
} else {
|
} else {
|
||||||
_pRtpTimer.reset();
|
_pRtpTimer.reset();
|
||||||
@ -630,12 +619,11 @@ void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){
|
|||||||
}
|
}
|
||||||
_aui64RtpRecv[trackidx] ++;
|
_aui64RtpRecv[trackidx] ++;
|
||||||
_aui16NowSeq[trackidx] = rtppt->sequence;
|
_aui16NowSeq[trackidx] = rtppt->sequence;
|
||||||
_aiNowStamp[trackidx] = rtppt->timeStamp;
|
|
||||||
if( _aiFistStamp[trackidx] == 0){
|
|
||||||
_aiFistStamp[trackidx] = _aiNowStamp[trackidx];
|
|
||||||
}
|
|
||||||
|
|
||||||
rtppt->timeStamp -= _aiFistStamp[trackidx];
|
//计算相对时间戳
|
||||||
|
int64_t dts_out;
|
||||||
|
_stamp[trackidx].revise(rtppt->timeStamp,rtppt->timeStamp,dts_out,dts_out);
|
||||||
|
rtppt->timeStamp = dts_out;
|
||||||
onRecvRTP_l(rtppt,_aTrackInfo[trackidx]);
|
onRecvRTP_l(rtppt,_aTrackInfo[trackidx]);
|
||||||
}
|
}
|
||||||
float RtspPlayer::getPacketLossRate(TrackType type) const{
|
float RtspPlayer::getPacketLossRate(TrackType type) const{
|
||||||
@ -653,7 +641,6 @@ float RtspPlayer::getPacketLossRate(TrackType type) const{
|
|||||||
return 1.0 - (double)totalRecv / totalSend;
|
return 1.0 - (double)totalRecv / totalSend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1 == 0){
|
if(_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1 == 0){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -661,14 +648,10 @@ float RtspPlayer::getPacketLossRate(TrackType type) const{
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RtspPlayer::getProgressMilliSecond() const{
|
uint32_t RtspPlayer::getProgressMilliSecond() const{
|
||||||
uint32_t iTime[2] = {0,0};
|
return MAX(_stamp[0].getRelativeStamp(),_stamp[1].getRelativeStamp());
|
||||||
for(unsigned int i = 0 ;i < _aTrackInfo.size() ;i++){
|
|
||||||
iTime[i] = _aiNowStamp[i] - _aiFistStamp[i];
|
|
||||||
}
|
|
||||||
return _iSeekTo + MAX(iTime[0],iTime[1]);
|
|
||||||
}
|
}
|
||||||
void RtspPlayer::seekToMilliSecond(uint32_t ms) {
|
void RtspPlayer::seekToMilliSecond(uint32_t ms) {
|
||||||
sendPause(false,ms);
|
sendPause(false,ms, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const std::initializer_list<string> &header) {
|
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const std::initializer_list<string> &header) {
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "Network/TcpClient.h"
|
#include "Network/TcpClient.h"
|
||||||
#include "RtspSplitter.h"
|
#include "RtspSplitter.h"
|
||||||
#include "RtpReceiver.h"
|
#include "RtpReceiver.h"
|
||||||
|
#include "MediaFile/Stamp.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace toolkit;
|
using namespace toolkit;
|
||||||
@ -114,7 +115,7 @@ private:
|
|||||||
|
|
||||||
//发送SETUP命令
|
//发送SETUP命令
|
||||||
void sendSetup(unsigned int uiTrackIndex);
|
void sendSetup(unsigned int uiTrackIndex);
|
||||||
void sendPause(bool bPause,uint32_t ms);
|
void sendPause(bool bPause,uint32_t ms, bool range);
|
||||||
void sendOptions();
|
void sendOptions();
|
||||||
void sendDescribe();
|
void sendDescribe();
|
||||||
|
|
||||||
@ -148,12 +149,8 @@ private:
|
|||||||
std::shared_ptr<Timer> _pPlayTimer;
|
std::shared_ptr<Timer> _pPlayTimer;
|
||||||
std::shared_ptr<Timer> _pRtpTimer;
|
std::shared_ptr<Timer> _pRtpTimer;
|
||||||
|
|
||||||
//播放进度控制,单位毫秒
|
//时间戳
|
||||||
uint32_t _iSeekTo = 0;
|
Stamp _stamp[2];
|
||||||
|
|
||||||
//单位毫秒
|
|
||||||
uint32_t _aiFistStamp[2] = {0,0};
|
|
||||||
uint32_t _aiNowStamp[2] = {0,0};
|
|
||||||
|
|
||||||
//rtcp相关
|
//rtcp相关
|
||||||
RtcpCounter _aRtcpCnt[2]; //rtcp统计,trackid idx 为数组下标
|
RtcpCounter _aRtcpCnt[2]; //rtcp统计,trackid idx 为数组下标
|
||||||
|
Loading…
Reference in New Issue
Block a user