DirectShow Audio / Video PTS Расчет синхронизации - PullRequest
2 голосов
/ 21 ноября 2010

Greetings,

Я написал фильтр источника DirectShow, который принимает видеокадры AVC / AAC / блоки доступа AAC из трансляции ATSC-153, записанные на видеопроцессоре WinCE / ARM.Выходные контакты (2 из них, один для видео, один для аудио) подключены к соответствующим декодерам и средствам визуализации.В настоящее время я беру PTS из соответствующих заголовков RTP и передаю их исходному фильтру, а затем выполняю вычисление с помощью часов прямого показа.Видео PTS имеет частоту 90 кГц, частота звука PTS варьируется, мой текущий тестовый поток имеет тиканье звука на частоте 55,2 кГц.

Ниже приведены процедуры convert_to_dshow_timestamp () и FillBuffer ().Когда я распечатываю конвертированные метки времени, когда видео / аудио извлекаются фильтром, времена находятся в пределах 100-200 мс.Это не было бы плохо, что-то для работы.Тем не менее, видео отстает от аудио на 2-3 секунды.

/ * Процедура преобразования тактовой частоты в тактовую частоту DirectShow * / static unsigned long long convert_to_dshow_timestamp (unsigned long long ts, unsigned long rate) {longдвойной гц;длинный двойной мульти;long double tmp;

if (rate == 0)
{
    return 0;
}

hz = (long double) 1.0 / rate;
multi = hz / 1e-7;

tmp = ((long double) ts * multi) + 0.5;
return (unsigned long long) tmp;

}

/ * Исходный фильтр FillBuffer () рутина * / HRESULT OutputPin :: FillBuffer (IMediaSample * pSamp) {BYTE * pData;DWORD dataSize;pipeStream stream;BOOL retVal;DWORD returnBytes;HRESULT hr;DWORD отключить;REFERENCE_TIME ts;REFERENCE_TIME df;без знака длинные длинные отличия;unsigned long long difTimeRef;

pSamp->GetPointer(&pData);
dataSize = pSamp->GetSize();

ZeroMemory(pData, dataSize);

stream.lBuf = pData;
stream.dataSize = dataSize;

/* Pin type 1 is H.264 AVC video frames */
if (m_iPinType == 1)
{
    retVal = DeviceIoControl(
                                ghMHTune,
                                IOCTL_MHTUNE_RVIDEO_STREAM,
                                NULL,
                                0,
                                &stream,
                                sizeof(pipeStream),
                                &returnBytes,
                                NULL
                            );
    if (retVal == TRUE)
    {
        /* Get the data */
        /* Check for the first of the stream, if so, set the start time */
        pSamp->SetActualDataLength(returnBytes);
        hr = S_OK;
        if (returnBytes > 0)
        {
            /* The discontinuety is set in upper layers, when an RTP
             * sequence number has been lost.
             */
            discont = stream.discont;

            /* Check for another break in stream time */
            if (
                m_PrevTimeRef &&
                ((m_PrevTimeRef > (stream.timeRef + 90000 * 10)) ||
                ((m_PrevTimeRef + 90000 * 10) < stream.timeRef))
               )
            {
                dbg_log(TEXT("MY:DISC HERE\n"));
                 if (m_StartStream > 0)
                {
                    discont = 1;
                }
            }

            /* If the stream has not started yet, or there is a
             * discontinuety then reset the stream time.
             */
            if ((m_StartStream == 0) || (discont != 0))
            {
                sys_time = timeGetTime() - m_ClockStartTime;
                m_OtherSide->sys_time = sys_time;

                /* For Video, the clockRate is 90Khz */
                m_RefGap = (sys_time * (stream.clockRate / 1000)) +
                                                    (stream.clockRate / 2);

                /* timeRef is the PTS for the frame from the RTP header */
                m_TimeGap = stream.timeRef;
                m_StartStream = 1;
                difTimeRef = 1;
                m_PrevPTS = 0;
                m_PrevSysTime = timeGetTime();
                dbg_log(
                        TEXT("MY:StartStream %lld: %lld: %lld\n"),
                        sys_time,
                        m_RefGap,
                        m_TimeGap
                       );
            }
            else
            {
                m_StartStream++;
            }

            difTimeRef = stream.timeRef - m_PrevTimeRef;
            m_PrevTimeRef = stream.timeRef;

            /* Difference in 90 Khz clocking */
            ts = stream.timeRef - m_TimeGap + m_RefGap;
            ts = convert_to_dshow_timestamp(ts, stream.clockRate);

            if (discont != 0)
            {
                dbg_log(TEXT("MY:VDISC TRUE\n"));
                pSamp->SetDiscontinuity(TRUE);
            }
            else
            {
                pSamp->SetDiscontinuity(FALSE);
                pSamp->SetSyncPoint(TRUE);
            }

            difPts = ts - m_PrevPTS;

            df = ts + 1;
            m_PrevPTS = ts;
            dbg_log(
                    TEXT("MY:T %lld: %lld = %lld: %d: %lld\n"),
                    ts,
                    m_OtherSide->m_PrevPTS,
                    stream.timeRef,
                    (timeGetTime() - m_PrevSysTime),
                    difPts
                   );

            pSamp->SetTime(&ts, &df);
            m_PrevSysTime = timeGetTime();
        }
        else
        {
            Sleep(10);
        }
    }
    else
    {
        dbg_log(TEXT("MY:  Fill FAIL\n"));
        hr = E_FAIL;
    }
}
else if (m_iPinType == 2)
{
    /* Pin Type 2 is audio AAC Access units, with ADTS headers */
    retVal = DeviceIoControl(
                                ghMHTune,
                                IOCTL_MHTUNE_RAUDIO_STREAM,
                                NULL,
                                0,
                                &stream,
                                sizeof(pipeStream),
                                &returnBytes,
                                NULL
                            );

    if (retVal == TRUE)
    {
        /* Get the data */
        /* Check for the first of the stream, if so, set the start time */
        hr = S_OK;
        if (returnBytes > 0)
        {
            discont = stream.discont;
            if ((m_StartStream == 0) || (discont != 0))
            {
                sys_time = timeGetTime() - m_ClockStartTime;
                m_RefGap = (sys_time * (stream.clockRate / 1000)) +
                                                    (stream.clockRate / 2);

                /* Mark the first PTS from stream.  This PTS is from the
                 * RTP header, and is usually clocked differently than the
                 * video clock.
                 */
                m_TimeGap = stream.timeRef;
                m_StartStream = 1;
                difTimeRef = 1;
                m_PrevPTS = 0;
                m_PrevSysTime = timeGetTime();
                dbg_log(
                        TEXT("MY:AStartStream %lld: %lld: %lld\n"),
                        sys_time,
                        m_RefGap,
                        m_TimeGap
                       );
            }

            /* Let the video side stream in first before letting audio
             * start to flow.
             */
            if (m_OtherSide->m_StartStream < 32)
            {
                pSamp->SetActualDataLength(0);
                Sleep(10);
                return hr;
            }
            else
            {
                pSamp->SetActualDataLength(returnBytes);
            }

            difTimeRef = stream.timeRef - m_PrevTimeRef;
            m_PrevTimeRef = stream.timeRef;

            if (discont != 0)
            {
                dbg_log(TEXT("MY:ADISC TRUE\n"));
                pSamp->SetDiscontinuity(TRUE);
            }
            else
            {
                pSamp->SetDiscontinuity(FALSE);
                pSamp->SetSyncPoint(TRUE);
            }

            /* Difference in Audio PTS clock, TESTING AT 55.2 Khz */
            ts = stream.timeRef - m_TimeGap + m_RefGap;
            ts = convert_to_dshow_timestamp(ts, stream.clockRate);

            difPts = ts - m_PrevPTS;

            df = ts + 1;
            m_PrevPTS = ts;
            dbg_log(
                    TEXT("MY:AT %lld = %lld: %d: %lld\n"),
                    ts,
                    stream.timeRef,
                    (timeGetTime() - m_PrevSysTime),
                    difPts
                   );

            pSamp->SetTime(&ts, &df);
            m_PrevSysTime = timeGetTime();
        }
        else
        {
            pSamp->SetActualDataLength(0);
            Sleep(10);
        }
    }
}
return hr;

} / * Конец кода * /

Я попытался настроить PTS видео, просто добавив (90000 * 10), чтобы увидеть, если видеобудет далеко впереди аудио, однако это не так.Видео по-прежнему отстает от звука на 2 секунды и более.Я действительно не понимаю, почему это не сработает.Каждый видеокадр должен показываться на 10 секунд вперед.Разве это не будет правильно?

Главный вопрос в том, являются ли алгоритмы правильными?Кажется, они работают нормально, независимо от того, работают ли видео / аудио.

Фильтр источника не является push-фильтром, я не уверен, что это изменит ситуацию.У меня нет проблем с тем, чтобы декодеры не синхронизировались со входом трансляции.

Большое спасибо.

1 Ответ

3 голосов
/ 01 декабря 2010

На самом деле я разобрался с проблемой, которой было две.

Первый - плохая работа с кадром SPS H.264. Когда декодер запускается, он будет отбрасывать каждый кадр, пока не найдет кадр SPS. Поток был закодирован со скоростью 15 кадров в секунду. Это приведет к потере времени, так как декодер будет потреблять до второй стоимости видео менее чем за 10 мс. Каждый кадр, который был представлен после этого, рассматривался с опозданием, и он пытался перемотать кадры вперед, чтобы наверстать упущенное. Будучи живым источником, у него снова не будет кадров. Обходной путь был помещен в код перед моим, чтобы убедиться, что буфер был не менее 32 кадров, что составляет около 2 секунд.

Вторая проблема действительно связана с корнем проблемы. Я использовал PTS из заголовка RTP в качестве эталона времени. Хотя это будет работать в отдельном случае аудио и / или видео, нет никакой гарантии, что PTS видео RTP будет соответствовать соответствующему PTS аудио RTP, и, как правило, не будет. Следовательно использование времени NTP RTCP в соответствии со следующей формулой согласно спецификации:

PTS = RTCP_SR_NTP_timestamp + (RTP_timestamp - RTCP_SR_RTP_timestamp) / media_clock_rate

Это позволяет мне сопоставить фактическое видео PTS с соответствующим аудио PTS.

...