Написание пользовательского push-фильтра DirectShow RTSP / RTP Source - отметка времени данных, поступающих из живых источников - PullRequest
5 голосов
/ 10 февраля 2010

Я пишу собственный исходный 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 Ответ

6 голосов
/ 11 февраля 2010

Средство визуализации будет рисовать кадры, когда время потока графика достигает отметки времени на объекте образца. Если я правильно прочитал ваш код, вы помечаете его временем потока в момент прибытия, поэтому они всегда будут задерживаться при рендеринге. Это несколько сбивает с толку аудио-рендерера: если аудио-рендеринг предоставляет часы графика, то он сообщит, что текущее время потока будет соответствовать сэмплу, который он воспроизводит в данный момент, и это приведет к нежелательному поведению во времени.

  1. Вы хотите установить время в будущем, чтобы учесть задержку на графике и любую буферизацию в вашем фильтре. Попробуйте установить время, возможно, 300 мс в будущем (текущее время + 300 мс).

  2. Вы хотите быть согласованными между кадрами, поэтому не ставьте временные метки в зависимости от времени прибытия каждого кадра. Используйте метку времени RTP для каждого кадра и установите базовую линию для первого в 300 мс в будущем; последующие кадры тогда (rtp - rtp_at_baseline) + dshow baseline (с соответствующими преобразованиями единиц.

  3. Вам необходимо пометить временные метки аудио и видео потоков одинаковым образом, используя одну и ту же базовую линию. Однако, если я помню, временные метки RTP имеют различную базовую линию в каждом потоке, поэтому вам нужно использовать пакеты RTCP для преобразования временных меток RTP в (абсолютное) время NTP, а затем преобразовать NTP в DirectShow с использованием вашей исходной базовой линии (базовый NTP = dshow). время потока сейчас + 300 мс).

G

...