Good morning. Our  C++ CustomH264Sink derived classes' afterGettingFrame 
methods shown below are not recognizing the IFrame bytes supposed to be  
contained in a H264 packet generated from a file containing 5 minutes of 
Live555 H264 video data. Please tell me what we are doing incorrectly or 
forgetting to do.  

 The "testRTSPClient" demo application receives each (video and/or audio) frame 
into a memory buffer, but does not do anything with the frame data. You can, 
however, use this code as a model for a 'media player' application that decodes 
and renders these frames. Note, in particular, the "DummySink" class that the 
"testRTSPClient" demo application uses - and the (non-static) 
"DummySink::afterGettingFrame()" function. When this function is called, a 
complete 'frame' (for H.264 or H.265, this will be a "NAL unit") will have 
already been delivered into "fReceiveBuffer". Note that our "DummySink" 
implementation doesn't actually do anything with this data; that's why it's 
called a 'dummy' sink. 
 If you want to decode (or otherwise process) these frames, you would replace 
"DummySink" with your own "MediaSink" subclass. Its "afterGettingFrame()" 
function would pass the data (at "fReceiveBuffer", of length "frameSize") to a 
decoder
 If you are receiving H.264 video data, there is one more thing that you have 
to do before you start feeding frames to your decoder. H.264 streams have 
out-of-band configuration information ("SPS" and "PPS" NAL units) that you may 
need to feed to the decoder to initialize it. To get this information, call 
"MediaSubsession::fmtp_spropparametersets()" (on the video 'subsession' 
object). This will give you a (ASCII) character string. You can then pass this 
to "parseSPropParameterSets()" (defined in the file 
"include/H264VideoRTPSource.hh"), to generate binary NAL units for your 
decoder. 


 void CustomH264Sink::afterGettingFrame(void* clientData, unsigned frameSize, 
unsigned numTruncatedBytes, struct timeval presentationTime, unsigned 
durationInMicroseconds)
 {
       CustomH264Sink* sink = (CustomH264Sink*)clientData;
       /* HERE I DISCOVERED THAT sink byte buffer does not contain an IFRAME */
        sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, 
durationInMicroseconds);
 }

 void CustomH264Sink::afterGettingFrame(unsigned frameSize, unsigned 
numTruncatedBytes, struct timeval presentationTime, unsigned 
/*durationInMicroseconds*/)
 {
       // We've just received a frame of data.  (Optionally) print out 
information about it:

       if (fSubsession.rtpSource()->curPacketMarkerBit())
       {
             m_nFrameSize += frameSize;
             int nNALType = fReceiveBuffer[H264_NAL_HEADER_STARTCODE_LENGTH] & 
0x1F;

             bool bNalHeaderPresent = false;
             if ((nNALType == 0) && (m_nFrameSize >= 
H264_NAL_HEADER_STARTCODE_LENGTH + H264_NAL_HEADER_STARTCODE_LENGTH))
             {      // Most of the cameras sending the frames without nal 
header start code, however some cameras  send the frame with start code
                   // Since we initialized the buffer with start code, we need 
to skip that. 
                   // We are reinitilzing without nal header start code, we 
will execute this code only for the first frame
                   nNALType = fReceiveBuffer[H264_NAL_HEADER_STARTCODE_LENGTH + 
H264_NAL_HEADER_STARTCODE_LENGTH] & 0x1F;

                   // First make sure we have valid NAL Type
                   if (nNALType == NALTYPE::NALTYPE_IDRPicture || nNALType == 
NALTYPE::NALTYPE_SEI || nNALType == NALTYPE::NALTYPE_SequenceParameterSet
                         || nNALType == NALTYPE::NALTYPE_PictureParameterSet || 
nNALType == NALTYPE::NALTYPE_AccessUnitDelimiter || nNALType == 
NALTYPE::NALTYPE_SliceLayerWithoutPartitioning)
                   {
                         memmove(fReceiveBuffer, fReceiveBuffer + 
H264_NAL_HEADER_STARTCODE_LENGTH, m_nFrameSize - 
H264_NAL_HEADER_STARTCODE_LENGTH);
                         bNalHeaderPresent = true;
                   }
             }

             if (nNALType == NALTYPE::NALTYPE_IDRPicture)
             {
                   envir() << "I Frame      (" << m_nFrameWidth << "x" << 
m_nFrameHeight << ")         " << m_nFrameSize + frameSize << "\n";
                   m_nFrameCounter = 0;

                   if (m_nConfigLength)
                         
m_pCamera->Add2FrameQueue(CC_SAMPLETYPE_MPEG4AVC_CONFIG, m_pConfig, 
m_nConfigLength, m_nFrameWidth, m_nFrameHeight, m_nFrameCounter);
                   m_pCamera->Add2FrameQueue(CC_SAMPLETYPE_MPEG4AVC_IFRAME, 
fReceiveBuffer, m_nFrameSize, m_nFrameWidth, m_nFrameHeight, m_nFrameCounter);
                   DisplayDiagnosticsInfo(m_nFrameSize);
                   CalculateFrameRate();
                   CalculateIFrameInterval(true);
             }
             else if (nNALType == NALTYPE::NALTYPE_SEI)
             {
                   //envir() << "SEI        " << m_nFrameSize << "\n";
             }
             else if (nNALType == NALTYPE::NALTYPE_SequenceParameterSet)
             {
                   // Some Cameras send I frame with Config Data , process 
those cases
                   unsigned long nEndOfConfigData = m_nFrameSize + 1;
                   unsigned long nBytesToCheck = m_nFrameSize - 5;
                   BYTE nNALTypeSPS;
                   const unsigned char* pData = (const unsigned 
char*)fReceiveBuffer;

                   // Special Case 1
                   for (unsigned long n = 0; n < nBytesToCheck; ++n)
                   {
                         if (pData[0] == 0x00 && pData[1] == 0x00 && pData[2] 
== 0x00 && pData[3] == 0x01)
                         {
                               nNALTypeSPS = pData[4] & 0x1F;

                               if ((nNALTypeSPS == NALTYPE_IDRPicture) || 
(nNALTypeSPS == NALTYPE_SliceLayerWithoutPartitioning))
                               {      // Frame Data is started
                                     nEndOfConfigData = n;
                                     break;
                               }
                         }

                         ++pData;
                   }


             

                   if (nEndOfConfigData < m_nFrameSize)
                   {
                         m_nFrameCounter = 0;

                         if (m_nConfigLength)
                               
m_pCamera->Add2FrameQueue(CC_SAMPLETYPE_MPEG4AVC_CONFIG, fReceiveBuffer, 
nEndOfConfigData, m_nFrameWidth, m_nFrameHeight, m_nFrameCounter);
                         
m_pCamera->Add2FrameQueue(CC_SAMPLETYPE_MPEG4AVC_IFRAME, fReceiveBuffer + 
nEndOfConfigData, m_nFrameSize - nEndOfConfigData, m_nFrameWidth, 
m_nFrameHeight, m_nFrameCounter);
                         DisplayDiagnosticsInfo(m_nFrameSize);
                         CalculateFrameRate();
                         CalculateIFrameInterval(true);
                   }
                   else
                   {      // This is normal case, most of the cameras
                         if (m_pConfig == NULL && m_nFrameSize)
                         {
                               m_nConfigLength = m_nFrameSize;
                               m_pConfig = new uint8_t[m_nConfigLength];
                               memcpy(m_pConfig, fReceiveBuffer, 
m_nConfigLength);
                         }
                   }
                   // Diag
                   //envir() << "SPS        " << m_nFrameSize << "\n";
             }
             else if (nNALType == NALTYPE::NALTYPE_PictureParameterSet)
             {
                   
             }
             else if (nNALType == NALTYPE::NALTYPE_AccessUnitDelimiter)
             {
                   //envir() << "AUD        " << m_nFrameSize << "\n";
             }
             else if (nNALType == 
NALTYPE::NALTYPE_SliceLayerWithoutPartitioning)
             {
                   //envir() << "P Frame        " << m_nFrameSize << "\n";
                   m_nFrameCounter++;
                   m_pCamera->Add2FrameQueue(CC_SAMPLETYPE_MPEG4AVC_PFRAME, 
fReceiveBuffer, m_nFrameSize, m_nFrameWidth, m_nFrameHeight, m_nFrameCounter);
                   CalculateFrameRate();
                   CalculateIFrameInterval(false);
             }
             else
             {
                   envir() << "Unrecognizable DATA 
.............................................................    \n";
                   envir() << nNALType;
                   bNalHeaderPresent = false;
             }

             //envir() << m_nFrameSize << "\n";

             if (bNalHeaderPresent)
             {      // Reinitilize the offset, since this camera is sending nal 
header start code
                   m_nNalHeaderStartCodeOffset = 0;
             }

             m_nFrameSize = m_nNalHeaderStartCodeOffset;
       }
       else
       {
             m_nFrameSize += frameSize;

             // 08/19/2015 ODD Case, Major IP Camera Vendor's firmware sends 
SEI data without rtp market set, Needs to investigate further
             int nNALType = fReceiveBuffer[H264_NAL_HEADER_STARTCODE_LENGTH] & 
0x1F;

             if (nNALType == NALTYPE::NALTYPE_SEI)
             {
                   // Ignore the packet, we don't need SEI motion detection data
                   m_nFrameSize = H264_NAL_HEADER_STARTCODE_LENGTH;
                   envir() << "SEI without Marker       " << m_nFrameSize << "
             }

             //envir() << "Incomplete Data.......................    \n";
       }



       // Then continue, to request the next frame of data:
       continuePlaying();
 }

 Boolean CustomH264Sink::continuePlaying()
 {
       if (fSource == NULL) return False; // sanity check (should not happen)

       // Request the next frame of data from our input source.  
"afterGettingFrame()" will get called later, when it arrives:

 #if 1
       fSource->getNextFrame(fReceiveBuffer + m_nFrameSize, 
fReceiveBufferLength - m_nFrameSize,
             afterGettingFrame, this,
             onSourceClosure, this);
 #else
       fSource->getNextFrame(fReceiveBuffer, fReceiveBufferLength,
             afterGettingFrame, this,
             onSourceClosure, this);

 #endif


       return True;
 }

 Any help is greatly appreciated. 
_______________________________________________
live-devel mailing list
live-devel@lists.live555.com
http://lists.live555.com/mailman/listinfo/live-devel

Reply via email to