rtp音视频同步问题解决方法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

rtp音视频同步问题解决方法
rtp同步方法的思考
由于音视频流是以两条独立的数据流在网络上传输的,如果网络质量相当差,那么在接收端收到的音视频数据流就有可能不是同步的了,为了克服这种不同步的现象,需要添加同步机制。

的同步机制是使用开源库jrtplib3.7.1来实现的,严格遵守rtp协议标准。

解决的方案如下:
当有数据需要发送时,往数据中加入时间戳,在接收端,读取时间戳,进行比较,如果相同或相差很近,就提交播放,如果其中一个时间戳更大,就等待。

如果网络质量很差,那么存在两种不同步的情况:
1. 对于单条数据流来说,如果网络质量很差,可能出现数据流的接收不流畅,如果没有做流畅处理,那么就可能出现抖动现象,这需要使用rtp中的时间戳解决。

2. 对于多条数据流来说,如果网络质量很差,可能出现本应该同时播放的数据帧没有在同一时间到达,需要做同步处理。

解决第1个问题的方法是向每个发送的数据包加上时间戳,在rtp库中,时间戳表示在打包数据段中第一个采样所对应的时间,时间戳的启始值是随机的,后续的时间戳是在前一个时间戳上的增量,在SendPacket中的时间戳参数表示的是时间戳增量,所以数据流的同步需要计算出时间戳增量。

对于音频数据,由于音频数据的采样率是8000HZ,所以每采样一次需要时间是1/8000s,由于是每20ms封包一次,所以时间戳的增量是(20*10**-3)*8000=160。

对于视频数据,由于视频数据的采样率是90000Hz,所以每采样一次需要时间是1/90000s,如果帧率是25帧/s,所以时间戳增量是90000/25=3600。

在发送端,每发送一个数据包,都打上该数据包对于的时间戳值,只需要向SendPacket的最后个参数传递时间戳增量,rtp库会自动算出时间戳,并加到发送的rtp数据包首部里边。

在接收端,当收到一个数据包时,获取该rtp数据包的时间戳值,计算出与前一个数据包的时间戳值的差值,乘以该媒体流的时间戳单位,就得出了当前数据包与前一个数据包之间的间隔的打包时间T。

所以只要保证在与前一个数据包被提交过后T时间后再提交当前接收到的数据包,那么在rtp层就解决了上边提出的第一个问题。

解决第2个问题的方法是使用rtcp发送者报告数据包中的时间信息,发送者报告被发送的间隔时间是不固定的,它的大小与参与到会话中的同步源数量成正比。

每个发送者报告中都有个ntp时间和rtp时间,该ntp时间表示在发送时间戳为该rtp时间的rtp包时的系统绝对时间,就是在发送这个rtp包时的系统时间,所以这两个时间值有对应关系,由于对于一个同步源,时间戳单位是固定的,所以可以由后
续的某个数据包的rtp时间戳timestamp1计算出这个数据包所对应的绝对时间,这个绝对时间就是在发送这个数据包时,发送端的系统时间
这个思路就是:
已知:ntp0,rtp0,rtp1,时间戳单位u,且ntp0与rtp0是表示同一时间值,只是测量标尺不同。

计算:ntp1,该ntp1与rtp1表示同一时间值
计算的公式:
ntp1 = ntp0 + (rtp1- rtp0)*u
使用这种方法就可以计算出每个收到的rtp数据包在发送端的系统时间,这个系统时间在发送端是单调递增的,所以可以通过这个值来同步多条数据流。

发送端(需要修改):
if(mediatype == 0)
{
if(first_flag)
{
//usleep(125*266*30);
presampletime = RTPTime::CurrentTime();
status = rtpsession->SendPacket((void *)buf, len, payloadtype, false, 0);
first_flag = false;
}else
{
tmpcurtime = curtime = RTPTime::CurrentTime();
curtime-=presampletime;
interval = (uint32_t)(curtime.GetDouble() * 8000.0);
status = rtpsession->SendPacket((void *)buf, len, payloadtype, false, 160/*interval*/);
presampletime = tmpcurtime;
}
}else
{
GwRTPRecv::g_start_send = 1;
if(GwRTPRecv::g_skip_frame==1)
{
char buf123[]="#1234#";
status = rtpsession->SendPacket((void *)buf123, strlen(buf123), payloadtype,true,0);
GwRTPRecv::g_skip_frame = 2;
}
if(GwMpeg4Codec::g_key_frame)
{
if(buf[10] != 'I')
{
return false;
}else if(GwMpeg4Codec::g_key_frame==2)
{
GwMpeg4Codec::g_key_frame = 0;
first_flag = true;
}
}
if(first_flag)
{
presampletime = RTPTime::CurrentTime();
status = rtpsession->SendPacket((void *)buf, len, payloadtype,
false, 0);
first_flag = false;
}else
{
tmpcurtime = curtime = RTPTime::CurrentTime();
curtime-=presampletime;
interval = (uint32_t)(curtime.GetDouble() * 90000.0);
status = rtpsession->SendPacket((void *)buf, len, payloadtype, false, interval);
presampletime = tmpcurtime;
}
}
接收端(需要修改):
bool recved = false;
RTPPacket *pack;
uint8_t *data ;
RTPTime curtime(0,0);
uint32_t timestamp;
RTPTime delay_time(0, 0);
uint32_t rtcp_timestamp;
RTPNTPTime rtcp_ntptime(0,0);
double interval_time;
double interval_time_abs;
RTPSourceData* psourcedata;
char need_sync_flag = false;
if(mediatype == 0)
{
rtpsession->BeginDataAccess();
if (rtpsession->GotoFirstSourceWithData())
{
do
{
while ((pack = rtpsession->GetNextPacket()) != NULL) {
if(pack->GetPayloadType() != payloadtype)
{
rtpsession->DeletePacket(pack);
continue;
}
recvlen = pack->GetPayloadLength();
if(recvlen > pack_size|| recvlen <= 0 )
{
rtpsession->DeletePacket(pack);
continue;
}
timestamp = pack->GetTimestamp();
data = pack->GetPayloadData();
*len = recvlen;
memcpy(buf, data, recvlen);
recved = true;
psourcedata =
rtpsession->GetCurrentSourceInfo();
if(psourcedata->SR_HasInfo())
{
rtcp_timestamp = psourcedata->SR_GetRTPTimestamp();
rtcp_ntptime = psourcedata->SR_GetNTPTimestamp();
recvd_rtcp = true;
}
if(recvd_rtcp)
{
RTPTime rtcpntptime(rtcp_ntptime);
uint32_t timestamp_bound = timestamp > rtcp_timestamp?(timestamp-rtcp_timestamp):(rtcp_timestamp-timesta mp);
if(timestamp_bound > 0xffff0000)
{
interval_time_abs = (0xffffffff - timestamp_bound)/8000.0;
interval_time = timestamp < rtcp_timestamp?(interval_time_abs):(-interval_time_abs);
}else
{
interval_time_abs = timestamp_bound/8000.0;
interval_time = timestamp < rtcp_timestamp?(-interval_time_abs):(interval_time_abs);
}
RTPTime rtptime_interval(interval_time_abs);
if(interval_time > 0)
{
rtcpntptime+=rtptime_interval;
}else
{
rtcpntptime-=rtptime_interval;
}
audio_time_sem.lockSem();
audio_curtime = rtcpntptime;
audio_time_sem.unlockSem();
}
rtpsession->DeletePacket(pack);
goto aurecv_end;
}
} while (rtpsession->GotoNextSourceWithData());
}
aurecv_end:
rtpsession->EndDataAccess();
}else
{
rtpsession->BeginDataAccess();
if (rtpsession->GotoFirstSourceWithData())
{
do
{
while ((pack = rtpsession->GetNextPacket()) != NULL) {
if(pack->GetPayloadType() != payloadtype)
{
rtpsession->DeletePacket(pack);
continue;
}
recvlen = pack->GetPayloadLength();
if(recvlen > pack_size|| recvlen <= 0 )
{
rtpsession->DeletePacket(pack);
continue;
}
timestamp = pack->GetTimestamp();
data = pack->GetPayloadData();
*len = recvlen;
memcpy(buf, data, recvlen);
recved = true;
if(pack->HasMarker() && strncmp((char*)data, "#1234#", 6) == 0)
{
rtpsession->DeletePacket(pack);
goto verecv_end;
}
if(buffer[0]=='#' && buffer[3]=='*' && buffer[2] == 'I')
{
first_flag = true;
}
psourcedata = rtpsession->GetCurrentSourceInfo();
if(psourcedata->SR_HasInfo())
{
rtcp_timestamp = psourcedata->SR_GetRTPTimestamp();
rtcp_ntptime = psourcedata->SR_GetNTPTimestamp();
recvd_rtcp = true;
}
if(recvd_rtcp)
{
RTPTime rtcpntptime(rtcp_ntptime);
uint32_t timestamp_bound = timestamp > rtcp_timestamp?(timestamp-rtcp_timestamp):(rtcp_timestamp-timesta mp);
if(timestamp_bound > 0xffff0000)
{
interval_time_abs = (0xffffffff - timestamp_bound)/90000.0;
interval_time = timestamp < rtcp_timestamp?(interval_time_abs):(-interval_time_abs);
}else
{
interval_time_abs = timestamp_bound/90000.0;
interval_time = timestamp < rtcp_timestamp?(-interva
l_time_abs):(interval_time_abs);
}
RTPTime rtptime_interval(interval_time_abs);
if(interval_time > 0)
{
rtcpntptime+=rtptime_interval;
}else
{
rtcpntptime-=rtptime_interval;
}
audio_time_sem.lockSem();
RTPTime audio_curtime1 = audio_curtime;
audio_time_sem.unlockSem();
if((audio_curtime1.GetSeconds() != 0 ||audio_curtime1.GetMicroSeconds() != 0))
{
if(rtcpntptime < audio_curtime1)
{
RTPTime tmp = audio_curtime1;
if((tmp-=rtcpntptime) > RTPTime(0, 500000))
{
first_flag = true; /*make vedio stream receive faster*/
*len = 0;
recved = false; /*discard packet becase it is too late*/
goto verecv_end;
}
}else
{
if(first_sync_flag)
{
presynctime = RTPTime::CurrentTime();
first_sync_flag = false;
need_sync_flag = true;
}else
{
curtime = RTPTime::CurrentTime();
RTPTime tmp = curtime;
tmp -= presynctime;
if(tmp >= RTPTime(5,0))
{
need_sync_flag = true;
presynctime = curtime;
}
}
if(need_sync_flag)
{
rtcpntptime-=RTPTime(0, 1000);
while ( audio_recving && rtcpntptime > audio_curtime1 ) /*vedio advance*/
{
audio_time_sem.lockSem();
audio_curtime1 = audio_curtime;
audio_time_sem.unlockSem();
usleep(5);
}
first_flag = true;
}
}
}
}
if(first_flag)
{
preplaytime = RTPTime::CurrentTime(); pretimestamp = timestamp;
first_flag = false;
}else
{
double delay;
if(timestamp < pretimestamp)
{
delay = (0xffffffff - pretimestamp + timestamp)/90000.0; }else
{
delay = (timestamp - pretimestamp)/90000.0;
}
RTPTime delay_time(delay);
curtime = RTPTime::CurrentTime();
curtime-=preplaytime;
if(delay_time > curtime)
{
delay_time -= curtime;
}
preplaytime = RTPTime::CurrentTime();
preplaytime+=delay_time;
pretimestamp = timestamp;
}
rtpsession->DeletePacket(pack);
goto verecv_end;
}
}while (rtpsession->GotoNextSourceWithData());
}
verecv_end:
rtpsession->EndDataAccess();
if(delay_time.GetSeconds() != 0 || delay_time.GetMicroSeconds() !=0)
{
RTPTime::Wait(delay_time);
}
}
return recved;。

相关文档
最新文档