我正在编写自定义DirectShow源推送过滤器,它应该从视频服务器接收RTP数据并将它们推送到渲染器。我写了一个CVideoPushPin类,它继承自CSourceStream和CVideoReceiverThread类,它是从视频服务器接收RTP数据包的线程的包装器。接收者线程基本上做了三件事:
汇总帧,将它们复制到缓冲区并将有关它们的信息存储到256 元素队列,定义如下:
struct queue_elem {
char *start; // Pointer to a frame in a buffer
int length; // Lenght of data
REFERENCE_TIME recvTime; // Timestamp when the frame was received (stream time)
};
struct data {
struct queue_elem queue[QUEUE_LENGTH];
int qWrIdx;
int qRdIdx;
HANDLE mutex;
};
每个收到的帧都带有当前流时间的时间戳
p->StreamTime(refTime);
REFERENCE_TIME rt = refTime.GetUnits();
问题在于我不知道如何在FillBuffer方法中为每个MediaSample设置时间戳。我尝试了几种方法,但播放要么停止,要么太慢。 目前,FillBuffer方法如下所示:
REFERENCE_TIME thisFrameStartTime, thisFrameEndTime;
// Make sure if there are at least 4 frames in the buffer
if(noOfFrames >= 4)
{
currentQe = m_myData.queue[m_myData.qRdIdx++]; //Take current frame description
if(m_myData.qRdIdx >= QUEUE_LENGTH)
{
m_myData.qRdIdx = 0;
}
nextQe = m_myData.queue[m_myData.qRdIdx]; //Take next frame description
if(currentQe.length > 0)
{
memcpy(pData, currentQe.start, currentQe.length);
pSample->SetActualDataLength(currentQe.length);
CRefTime refTime;
m_pFilter->StreamTime(refTime);
REFERENCE_TIME rt;
rt = refTime.GetUnits();
pSample->GetTime(&thisFrameStartTime, &thisFrameEndTime);
thisFrameEndTime = thisFrameStartTime + (nextQe.recvTime - currentQe.recvTime);
pSample->SetTime(&thisFrameStartTime, &thisFrameEndTime);
}
}
else
{
pSample->SetActualDataLength(0);
}
在这种情况下,我注意到队列中的项目数量增加得非常快(由于某种原因,FillBuffer方法无法足够快地提取数据),结果是播放视频时延迟增加。有没有人知道在从实时源接收数据时我应该如何进行时间戳?
答案 0 :(得分:6)
当图形的流时间到达样本对象的时间戳时,渲染器将绘制帧。如果我正确地读取了您的代码,那么您将使用到达时的流时间为它们加上时间戳,因此它们将始终在渲染时间较晚。这在音频渲染器中有些混淆:如果音频渲染器提供图形的时钟,那么它将报告当前流时间为当前正在播放的任何样本,这将导致一些不期望的时间行为。
您希望将来设置一个时间,以允许图表中的延迟和过滤器中的任何缓冲。尝试将未来的时间设置为300毫秒(流时间现在为+ 300毫秒)。
您希望帧之间保持一致,因此请不要根据每帧的到达时间对它们加时间戳。对每个帧使用RTP时间戳,并将第一个的基线设置为将来300ms;然后是后续帧(rtp - rtp_at_baseline)+ dshow基线(具有适当的单位转换。
您需要使用相同的基线以相同的方式为音频和视频流添加时间戳。但是,如果我记得,RTP时间戳在每个流中都有不同的基线,那么您需要使用RTCP数据包将RTP时间戳转换为(绝对)NTP时间,然后使用您的初始基线将NTP转换为directshow(基线NTP = dshow流时间现在+ 300毫秒)。