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); } }