2010-11-21 38 views
2

Salutations,DirectShow Audio/Vidéo PTS Pointant Calcul

J'ai écrit un filtre source DirectShow qui prend les unités d'accès aux images vidéo AVC/AAC/AAC de l'émission ATSC-153, écrit sur le processeur vidéo WinCE/ARM. Les broches de sortie (2 d'entre elles, une pour la vidéo, une pour l'audio) sont connectées aux décodeurs et aux moteurs de rendu appropriés. Actuellement, je prends le PTS à partir des en-têtes RTP appropriés, et je les passe au filtre source et j'effectue le calcul sur l'horloge DirectShow. Video PTS est au taux de 90Khz, le taux de PTS audio varie, mon flux de test actuel a le ticking audio à 55,2Khz.

Ce qui suit est les routines convert_to_dshow_timestamp() et FillBuffer(). Lorsque j'imprime les horodatages convertis lorsque la vidéo/l'audio est récupérée par le filtre, les temps sont compris entre 100 et 200 ms. Ce ne serait pas mauvais, quelque chose avec lequel travailler. Cependant, la vidéo suit l'audio de 2 à 3 secondes.

/* routine pour convertir une fréquence d'horloge à DirectShow fréquence d'horloge */ statique unsigned long long convert_to_dshow_timestamp ( longs ts unsigned long, taux unsigned long ) { long double hz; long double multi; 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; 

}

/* Filtre source routine FillBuffer() */ HRESULT OutputPin :: FillBuffer (IMediaSample * pSamp) { BYTE * pData; DWORD dataSize; pipeStream stream; BOOL retVal; DWORD returnBytes; HRESULT hr; DWORD discont; REFERENCE_TIME ts; REFERENCE_TIME df; difPts longs non signés; 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; 

} /* Fin code */

J'ai essayé le réglage de la PTS de vidéo, en ajoutant simplement (90000 * 10), pour voir si la vidéo irait loin de l'audio, Cependant, ce n'est pas le cas. La vidéo suit toujours l'audio de 2 secondes ou plus. Je ne comprends vraiment pas pourquoi cela ne marcherait pas. Chaque image vidéo devrait présenter 10 secondes d'avance. Est-ce que ce ne serait pas correct?

Leur principale question est, fondamentalement, sont les algorithmes sonores? Ils semblent fonctionner correctement la vidéo/audio indépendamment.

Le filtre source n'est pas un filtre poussoir, je ne suis pas sûr si cela fera une différence. Je n'ai pas de problèmes avec les décodeurs qui ne sont pas synchronisés avec les données de l'émission.

Merci beaucoup.

+0

Est-ce que le suivi se produit au fil du temps ou commence-t-il immédiatement avec le délai de 2-3 secondes? En outre, reformatez le code pour qu'il soit plus lisible. – BeemerGuy

+0

Désolé pour le format du code. Je vais essayer de faire mieux la prochaine fois. – davroslyrad

Répondre

3

En fait, j'ai compris le problème, dont il y avait deux.

Le premier était un mauvais travail autour de la trame SPS H.264. Lorsque le décodeur a démarré, il abandonne toutes les images jusqu'à ce qu'il trouve la trame SPS. Le flux a été codé à 15 images par seconde. Cela découragerait le chronométrage, car le décodeur consommerait jusqu'à une seconde de vidéo en moins de 10 ms.Chaque image qui a été présentée après cela a été considérée en retard, et elle essayait d'avancer rapidement les images pour les rattraper. Être une source en direct, il serait à court d'images à nouveau. La solution de contournement a été placée dans le code en amont du mien, pour s'assurer qu'il y avait un tampon d'au moins 32 images, ce qui est environ 2 secondes.

Le deuxième problème tourne autour de la racine du problème. J'utilisais les PTS de l'en-tête RTP comme référence temporelle. Bien que cela fonctionne dans le cas individuel de l'audio et/ou de la vidéo, il n'y a aucune garantie que la vidéo RTP PTS corresponde au PTS audio RTP correspondant, ce qui n'est généralement pas le cas. D'où l'utilisation du temps NTP RTCP selon la formule suivante, selon les spécifications:

PTS = RTCP_SR_NTP_timestamp + (RTP_timestamp - RTCP_SR_RTP_timestamp)/media_clock_rate 

Cela me permet de faire correspondre les PTS vidéo réelle aux PTS audio correspondants.