Написание пользовательского push-фильтра DirectShow RTSP/RTP Source - отметка времени данных, поступающих из живых источников
Я пишу собственный исходный push-фильтр DirectShow, который должен получать данные RTP с видеосервера и передавать их в средство визуализации. Я написал класс CVideoPushPin, который наследуется от классов CSourceStream и CVideoReceiverThread, который является оболочкой для потока, получающего RTP-пакеты от видеосервера. Поток получателя по существу делает три вещи:
- получает необработанные 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();
Проблема в том, что я не уверен, как мне установить временные метки для каждого MediaSample в методе FillBuffer. Я пробовал несколько способов, но воспроизведение либо останавливается, либо идет слишком медленно. В настоящее время метод 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 не может извлекать данные достаточно быстро), и в результате увеличивается задержка при воспроизведении видео. Кто-нибудь есть идеи, как я должен делать отметки времени при получении данных из живых источников?
1 ответ
Рендерер будет рисовать кадры, когда время потока графика достигнет отметки времени на объекте-образце. Если я правильно прочитал ваш код, вы помечаете его временем потока в момент прибытия, поэтому они всегда будут задерживаться при рендеринге. Это несколько сбивает с толку аудио-рендерера: если аудио-рендеринг предоставляет часы графика, то он сообщит, что текущее время потока будет соответствовать сэмплу, который он воспроизводит в данный момент, и это вызовет некоторое нежелательное временное поведение.
Вы хотите установить время в будущем, чтобы учесть задержку на графике и любую буферизацию в вашем фильтре. Попробуйте установить время, возможно, 300 мс в будущем (время потока сейчас + 300 мс).
Вы хотите быть согласованными между кадрами, поэтому не ставьте временные метки в зависимости от времени прибытия каждого кадра. Используйте метку времени RTP для каждого кадра и установите базовую линию для первого в 300 мс в будущем; последующие кадры тогда (rtp - rtp_at_baseline) + dshow baseline (с соответствующими преобразованиями единиц измерения).
Вы должны пометить временные метки аудио и видео потоков одинаково, используя одну и ту же базовую линию. Однако, если я помню, временные метки RTP имеют различную базовую линию в каждом потоке, поэтому вам нужно использовать пакеты RTCP для преобразования временных меток RTP в (абсолютное) время NTP, а затем преобразовать NTP в DirectShow, используя исходную базовую линию (базовый уровень NTP = dshow). время потока сейчас + 300 мс).
г