void H264VideoStreamDiscreteFramer
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
                     struct timeval presentationTime,
                     unsigned durationInMicroseconds) {
  // Get the "nal_unit_type", to see if this NAL unit is one that we want to save a copy of:
  u_int8_t nal_unit_type = frameSize == 0 ? 0xFF : fTo[0]&0x1F;

  // Check for a (likely) common error: NAL units that (erroneously) begin with a 0x00000001 or 0x000001 'start code'
  //     (Those start codes should only be in byte-stream data; *not* data that consists of discrete NAL units.)
  //     Once again, to be clear: The NAL units that you feed to a "H264VideoStreamDiscreteFramer" MUST NOT include start codes.
  if (nal_unit_type == 0) {
    if (frameSize >= 4 && fTo[0] == 0 && fTo[1] == 0 && ((fTo[2] == 0 && fTo[3] == 1) || fTo[2] == 1)) {
      envir() << "H264VideoStreamDiscreteFramer error: MPEG 'start code' seen in the input\n";
    } else {
      envir() << "Warning: Invalid 'nal_unit_type': 0\n";
    }
  } else if (nal_unit_type == 7) { // Sequence parameter set (SPS)
    saveCopyOfSPS(fTo, frameSize);
  } else if (nal_unit_type == 8) { // Picture parameter set (PPS)
    saveCopyOfPPS(fTo, frameSize);
  }

  // Next, check whether this NAL unit ends the current 'access unit' (basically, a video frame).  Unfortunately, we can't do this
  // reliably, because we don't yet know anything about the *next* NAL unit that we'll see.  So, we guess this as best as we can,
  // by assuming that if this NAL unit is a VCL NAL unit, then it ends the current 'access unit'.
  Boolean const isVCL = nal_unit_type <= 5 && nal_unit_type > 0; // Would need to include type 20 for SVC and MVC #####
  if (isVCL) fPictureEndMarker = True;

  // Finally, complete delivery to the client:
  fFrameSize = frameSize;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}
void H264VideoStreamFramer::setSPSandPPS(char const* sPropParameterSetsStr) {
  unsigned numSPropRecords;
  SPropRecord* sPropRecords = parseSPropParameterSets(sPropParameterSetsStr, numSPropRecords);
  for (unsigned i = 0; i < numSPropRecords; ++i) {
    if (sPropRecords[i].sPropLength == 0) continue; // bad data
    u_int8_t nal_unit_type = (sPropRecords[i].sPropBytes[0])&0x1F;
    if (nal_unit_type == 7/*SPS*/) {
      saveCopyOfSPS(sPropRecords[i].sPropBytes, sPropRecords[i].sPropLength);
    } else if (nal_unit_type == 8/*PPS*/) {
      saveCopyOfPPS(sPropRecords[i].sPropBytes, sPropRecords[i].sPropLength);
    }
  }
  delete[] sPropRecords;
}
void H264RealTimeStreamFramer::doGetNextFrame() {
    struct timespec TimeSpec = {0, 0};

    if (fNeedNextFrame) {
        if (fFirstFrame) {
            nextTask() = envir().taskScheduler().scheduleDelayedTask(0, (TaskFunc *)&tryGetNextFrame, this);
            fFirstFrame = False;
            PRINT_LOG(ASSERT, "First H264 frame");
            return;
        }

        MediaFrame * nextFrame = fFrameCapture->GetNextFrame(fCurrentFrame);
        if (NULL == nextFrame) {
            nextTask() = envir().taskScheduler().scheduleDelayedTask(10000, (TaskFunc *)&tryGetNextFrame, this);
            return;
        }

        // ASSERT(nextFrame->IsH264Frame(), "nextFrame MUST be H264Frame");

        PRINT_LOG(ASSERT, "Get %c frame, size = %u", nextFrame->IsKeyFrame() ? 'I' : 'P', nextFrame->Length());

        clock_gettime(CLOCK_MONOTONIC, &TimeSpec);
        PRINT_LOG(VERBOSE, "Time to get frame :  %12ld.%9ld", TimeSpec.tv_sec, TimeSpec.tv_nsec);

        fCurrentFrame = dynamic_cast<H264Frame *>(nextFrame);
        fNeedNextFrame = False;
        fCurrentNaluUnitIndex = 0;
        fOffset = 1;

        // ASSERT(fCurrentFrame->NaluCount() > 0, "H264 frame MUST have at least 1 nalu unit");
    }

    const H264Frame::NaluUnit & Nalu     = fCurrentFrame->GetNaluUnit(fCurrentNaluUnitIndex);
    const uint8_t             * addr     = Nalu.s_Addr;
    uint32_t                    size     = Nalu.s_Length;
    uint8_t                     naluType = (addr[0] & 0x1F);

    if (naluType == NALU_TYPE_SPS && fNeedSPS) {
        // Save SPS(Sequence Parameter Set)
        saveCopyOfSPS((u_int8_t *)addr, size);
        fNeedSPS = False;
    } else if (naluType == NALU_TYPE_PPS && fNeedPPS) {
        // Save PPS(Picture Parameter Set)
        saveCopyOfPPS((u_int8_t *)addr, size);
        fNeedPPS = False;
    }

#ifdef USE_H264_VIDEO_RTP_SINK

    fFrameSize = (size > fMaxSize) ? fMaxSize : size;
    fNumTruncatedBytes = size - fFrameSize;

    memmove(fTo, addr, fFrameSize);

    fPresentationTime = fCurrentFrame->TimeStamp();

    fCurrentNaluUnitIndex ++;

#else // USE_H264_VIDEO_RTP_SINK

    // Make sure max size of the data MUST NOT be greater then (MAX_BYTES_PER_UDP_PACKET - RTP_HEADER_SIZE)
    if (fMaxSize > MAX_BYTES_PER_UDP_PACKET - RTP_HEADER_SIZE) {
        fMaxSize = MAX_BYTES_PER_UDP_PACKET - RTP_HEADER_SIZE;
    }

    if (size > fMaxSize) {
        fFrameSize = size - fOffset + 2;
        fFrameSize = (fFrameSize > fMaxSize) ? fMaxSize : fFrameSize;
        memmove(fTo + 2, addr + fOffset, fFrameSize - 2);

        fTo[0] = (addr[0] & 0xE0) | 0x1C;
        if (fOffset == 1) {
            fTo[1] = (addr[0] & 0x1F) | 0x80;
        } else if (fOffset + fFrameSize - 2 >= size) {
            fTo[1] = (addr[0] & 0x1F) | 0x40;
            fCurrentNaluUnitIndex ++;
            fOffset = 1;
        } else {
            fTo[1] = (addr[0] & 0x1F);
        }

        fOffset += fFrameSize - 2;

    } else {
        fFrameSize = size;
        memmove(fTo, addr, fFrameSize);
        fCurrentNaluUnitIndex ++;
    }

    fPresentationTime = fCurrentFrame->TimeStamp();
    fNumTruncatedBytes = 0;

#endif // USE_H264_VIDEO_RTP_SINK

    if (fCurrentNaluUnitIndex >= fCurrentFrame->NaluCount()) {
        fPictureEndMarker = True;
        fNeedNextFrame = True;
        fDurationInMicroseconds = fCurrentFrame->Duration();
    } else {
        fPictureEndMarker = False;
        fDurationInMicroseconds = 0;
    }

    afterGetting(this);

    if (fNeedNextFrame)
    {
        clock_gettime(CLOCK_MONOTONIC, &TimeSpec);
        PRINT_LOG(VERBOSE, "Time to sent frame : %12ld.%9ld", TimeSpec.tv_sec, TimeSpec.tv_nsec);
    }
}