RTP Buffer Побочные эффекты - PullRequest
0 голосов
/ 03 ноября 2011

В предыдущей версии моего RTSP Source Filter я немедленно упаковывал пакеты RTP, чтобы получить их.Не было буфера, сортировка по порядковому номеру, удаление кадров с отсутствующими фрагментами и т. Д., Но результат был в порядке, так как я проводил тесты в локальной сети.Я решил добавить буфер RTP, механизм сортировки и т. Д. На самом деле, основной код был успешным.Но теперь у меня есть некоторые проблемы с отправкой кадров с правильной синхронизацией.

У меня есть механизм отладки.Я могу хранить все кадры в отдельных файлах, таких как frame0.bin, frame1.bin и т. Д., И у меня есть инструмент, который может читать эти файлы и отправлять их в декодер h.264.Когда я воспроизводю эти кадры, результат идеален.Я считаю, что это доказывает, что мой RTP и буферы кадров работают нормально.

Когда я пытаюсь передать кадр в функцию FillBuffer, как только создается кадр (фактически, когда моя очередь кадров имеет более 1 кадра),результат действительно дрянной (я получаю изображение, но с задержками и поврежденными кадрами).Это, вероятно, вызвано передачей кадров в FillBuffer функцию немедленно.Моя функция DoBufferProcessingLoop выглядит следующим образом (я удалил большинство проверок ошибок из кода);

HRESULT RtspSourceFilterOutputPin::DoBufferProcessingLoop() {

    Command com;
    REFERENCE_TIME rtNow = 0L;
    REFERENCE_TIME rtAdvise = 0L;

    OnThreadStartPlay();

    do {
        while (!CheckRequest(&com)) {
            if(streamReader->frames.size() > 1) {
                IMediaSample *pSample;
                GetDeliveryBuffer(&pSample,NULL,NULL,FALSE);

                hr = FillBuffer(pSample);

                if (hr == S_OK) {
                    HRESULT result = Deliver(pSample);
                } else if (hr == S_FALSE) {
                    pSample->Release();
                    DeliverEndOfStream();
                    return S_OK;
                } else {
                    //error
                }
                pSample->Release();
            }
        }

        if (com == CMD_RUN || com == CMD_PAUSE) {
            com = GetRequest();
        } else if (com != CMD_STOP) {
            //error
        }
    } while (com != CMD_STOP);

    return S_OK;
}

Поскольку потоковая передача выполняется в реальном времени, я удалил все метки времени (SetTime, SetMediaTime и т. Д.)из FillBuffer функции.В FillBuffer нет ничего особенного.Он просто извлекает кадр из очереди кадров и передает его декодеру.И это работает, если удалить мой буферный механизм.

Наконец, вопрос ...

Должен ли я подождать некоторое время, например, "получено время + время буфера", прежде чем отправлять кадр вместопередать его FillBuffer, как только он будет создан?Или я должен использовать SetTime в FillBuffer функции?Я уже попробовал и потерпел неудачу.Я также пытался дать значения времени начала / остановки с добавленным временем буфера, но не работал лучше.

Почему результат дрянной, когда я передаю кадр в декодер, но результат в порядке, если я сохраняю эти кадрыв двоичные файлы и воспроизведение из этих файлов?Что мне здесь не хватает?

1 Ответ

2 голосов
/ 03 ноября 2011

Тот факт, что вы буферизуете, не должен иметь никакого значения, если ваши выборки имеют правильную отметку времени. Если вы не отметили время (например, вызовите SetTime с NULL, как в

pSample->SetTime(NULL, NULL);

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

Итак, чтобы ответить на ваш вопрос, да, вам нужно установить временные метки на образцах: в режиме реального времени вы не можете удалить временные метки. Дрожание в сети приведет к тому, что время поступления образцов будет отличаться, и отметки времени должны быть установлены правильно, если вы хотите просматривать поток (ы) правильно. Вам не нужно ждать какое-то время, прежде чем передавать сэмплы в FillBuffer, если установлена ​​метка времени, средство визуализации позаботится о правильном воспроизведении сэмплов.

Мой подход, который работал довольно хорошо, заключается в следующем: В исходном фильтре RTSP выполните требуемую буферизацию. Затем, после заполнения буфера, рассчитайте смещение, которое будет использоваться для каждого образца. Необходимо учитывать временную метку RTP первого полученного синхронизированного с RTCP сэмпла (ts_0) и DirectShow StreamTime (st_0) в тот момент времени, когда вы начинаете воспроизведение сэмплов. К тому времени, когда вы начнете передавать выборки по конвейеру, время потока, скорее всего, больше не будет равно нулю ....

Временная метка DirectShow ts_new для каждого семпла будет затем рассчитана как

ts_new_x = ts_x - ts_0 + st_0 + 50ms

Эти 50 мс полезны, чтобы установить временную отметку образца в будущем. Причина этого заключается в том, чтобы избежать опоздания образцов, поступающих к рендереру. Время окончания сэмпла может быть установлено как ts_new_x + 1. IIRC в любом случае это игнорируется, но не указывайте меня на этом.

Я бы порекомендовал прочитать все разделы в http://msdn.microsoft.com/en-us/library/windows/desktop/dd407202(v=vs.85).aspx пару раз, если вы этого еще не сделали. Я обнаружил, что это очень полезно во время реализации.

Кроме того, не стесняйтесь взглянуть на версию исходного кода фильтра RTSP с открытым исходным кодом Я написал. Его главная цель состояла в том, чтобы осветить некоторые аспекты написания живого исходного фильтра RTSP DirectShow и поддерживает PCM, AMR и MP3. Я добавил небольшую поддержку H.264, но так и не смог завершить эту часть (IIRC смог воспроизвести видеопотоки live555).

...