////////////////////////////////////////////////////////////////////////////
///
/// Pass data to the elementary stream handler until the PES payload is consumed.
///
/// When all data has been consumed then switch back to the GotPartialPesHeader
/// state since (if the stream is correctly formed) the next three bytes will
/// contain the PES start code.
///
/// \return Collator status code, CollatorNoError indicates success.
///
CollatorStatus_t Collator_PesAudio_c::ReadPesPacket( void )
{
CollatorStatus_t Status;
unsigned int BytesToRead;

//
    
    BytesToRead = min( PesPayloadRemaining, RemainingLength );
    
    Status = HandleElementaryStream( BytesToRead, RemainingData );
    if( Status == CollatorNoError )
    {
	if (BytesToRead == PesPayloadRemaining)
	{
	    GotPartialPesHeader = true;
	    GotPartialPesHeaderBytes = 0;
	}
	
	RemainingData += BytesToRead;
	RemainingLength -= BytesToRead;
	PesPayloadRemaining -= BytesToRead;
    }
   
//
    
    return CollatorNoError;
}
Example #2
0
////////////////////////////////////////////////////////////////////////////
///
/// Accumulate incoming data until we have the full PES header.
///
/// Strictly speaking this method handles two sub-states. In the first state
/// we do not have sufficient data accumulated to determine how long the PES
/// header is. In the second we still don't have a complete PES packet but
/// at least we know how much more data we need.
///
/// This code assumes that PES packet uses >=9 bytes PES headers rather than
/// the 6 byte headers found in the program stream map, padding stream,
/// private stream 2, etc.
///
/// \return Collator status code, CollatorNoError indicates success.
///
CollatorStatus_t Collator_PesAudio_c::ReadPartialPesHeader(void)
{
	CollatorStatus_t Status;
	unsigned int PesHeaderBytes, BytesNeeded, BytesToRead;
	unsigned char PesPrivateData[MAX_PES_PRIVATE_DATA_LENGTH];
//
	if (GotPartialPesHeaderBytes < PES_INITIAL_HEADER_SIZE)
	{
		COLLATOR_DEBUG("Waiting for first part of PES header\n");
		StoredPesHeader = BufferBase + AccumulatedDataSize - GotPartialPesHeaderBytes;
		BytesToRead = min(RemainingLength, PES_INITIAL_HEADER_SIZE - GotPartialPesHeaderBytes);
		Status = AccumulateData(BytesToRead, RemainingData);
		if (Status == CollatorNoError)
		{
			GotPartialPesHeaderBytes += BytesToRead;
			RemainingData += BytesToRead;
			RemainingLength -= BytesToRead;
		}
		else
		{
			COLLATOR_DEBUG("Cannot accumulate data #6 (%d)\n", Status);
		}
		return Status;
	}
	//
	// We now have accumulated sufficient data to know how long the PES header actually is!
	//
	// pass the stream_id field to the collator sub-class (might update Configuration.ExtendedHeaderLength)
	SetPesPrivateDataLength(StoredPesHeader[3]);
	PesHeaderBytes = PES_INITIAL_HEADER_SIZE + StoredPesHeader[8] + Configuration.ExtendedHeaderLength;
	BytesNeeded = PesHeaderBytes - GotPartialPesHeaderBytes;
	BytesToRead = min(RemainingLength, BytesNeeded);
	Status = AccumulateData(BytesToRead, RemainingData);
	if (Status == CollatorNoError)
	{
		GotPartialPesHeaderBytes += BytesToRead;
		RemainingData += BytesToRead;
		RemainingLength -= BytesToRead;
		COLLATOR_DEBUG("BytesNeeded %d; BytesToRead %d\n", BytesNeeded, BytesToRead);
		if (BytesNeeded == BytesToRead)
		{
			//
			// Woo hoo! We've got the whole header, examine it and change state
			//
			COLLATOR_DEBUG("Got entire PES header\n");
			//report_dump_hex( severity_note, StoredPesHeader, PesHeaderBytes, 32, 0);
			Status = CollatorNoError; // strictly speaking this is a no-op but the code might change
			if (StoredPesHeader[0] != 0x00 || StoredPesHeader[1] != 0x00 || StoredPesHeader[2] != 0x01 ||
					CollatorNoError != (Status = ReadPesHeader()))
			{
				COLLATOR_DEBUG("%s; seeking new PES header",
					       (Status == CollatorNoError ? "Start code not where expected" :
						"Badly formed PES header"));
				SeekingPesHeader = true;
				DiscardAccumulatedData();
				// we have contained the error by changing states...
				return CollatorNoError;
			}
			//
			// Placeholder: Generic stream id based PES filtering (configured by sub-class) could be inserted
			// here (set DiscardPesPacket to true to discard).
			//
			if (Configuration.ExtendedHeaderLength)
			{
				// store a pointer to the PES private header. it is located just above the end of the
				// accumulated data and is will be safely accumulated providing the private header is
				// smaller than the rest of the PES packet. if a very large PES private header is
				// encountered we will need to introduce a temporary buffer to store the header in.
				if (Configuration.ExtendedHeaderLength <= MAX_PES_PRIVATE_DATA_LENGTH)
				{
					memcpy(PesPrivateData, BufferBase + AccumulatedDataSize - Configuration.ExtendedHeaderLength, Configuration.ExtendedHeaderLength);
				}
				else
				{
					COLLATOR_ERROR("Implementation error: Pes Private data area too big for temporay buffer\n");
				}
				Status = HandlePesPrivateData(PesPrivateData);
				if (Status != CollatorNoError)
				{
					COLLATOR_ERROR("Unhandled error when parsing PES private data\n");
					return (Status);
				}
			}
			// discard the actual PES packet from the accumulate buffer
			AccumulatedDataSize -= PesHeaderBytes;
			// record the number of bytes we need to ignore before we reach the next start code
			PesPayloadRemaining = PesPayloadLength;
			// switch states and absorb the packet
			COLLATOR_DEBUG("Discovered PES packet (header %d bytes, payload %d bytes)\n",
				       PesPacketLength - PesPayloadLength + 6, PesPayloadLength);
			GotPartialPesHeader = false;
			if (PassPesPrivateDataToElementaryStreamHandler && Configuration.ExtendedHeaderLength)
			{
				// update PesPacketLength (to ensure that GetOffsetIntoPacket gives the correct value)
				PesPayloadLength += Configuration.ExtendedHeaderLength;
				Status = HandleElementaryStream(Configuration.ExtendedHeaderLength, PesPrivateData);
				if (Status != CollatorNoError)
				{
					COLLATOR_ERROR("Failed not accumulate the PES private data area\n");
				}
			}
		}
	}
	else
	{
		COLLATOR_DEBUG("Cannot accumulate data #7 (%d)\n", Status);
	}
	return Status;
}
Example #3
0
////////////////////////////////////////////////////////////////////////////
///
/// Handle losing lock on the frame headers.
///
/// This function is called to handle the data that was spuriously accumulated
/// when the frame header was badly parsed.
///
/// In principle this function is quite simple. We allocate a new accumulation buffer and
/// use the currently accumulated data is the data source to run the elementary stream
/// state machine. There is however a little extra logic to get rid of recursion.
/// Specificially we change the error handling behaviour if this method is re-entered
/// so that there error is reported back to the already executing copy of the method.
///
/// \return Collator status code, CollatorNoError indicates success.
///
CollatorStatus_t Collator_PesAudio_c::HandleMissingNextFrameHeader(void)
{
	CollatorStatus_t Status;
	//
	// Mark the collator as having lost frame lock.
	// Yes! We really do want to do this before the re-entry checks.
	//
	CollatorState = SeekingSyncWord; // we really do want to do this before the re-entry checks
	AccumulatedFrameReady = false;
	//
	// Check for re-entry
	//
	if (AlreadyHandlingMissingNextFrameHeader)
	{
		COLLATOR_DEBUG("Re-entered the error recovery handler, initiating stack unwind\n");
		return CollatorUnwindStack;
	}
	//
	// Check whether the sub-class wants trivial or aggressive error recovery
	//
	if (!ReprocessAccumulatedDataDuringErrorRecovery)
	{
		DiscardAccumulatedData();
		return CollatorNoError;
	}
	//
	// Remember the original elementary stream pointers for when we return to 'normal' processing.
	//
	unsigned char *OldRemainingElementaryOrigin = RemainingElementaryOrigin;
	unsigned char *OldRemainingElementaryData = RemainingElementaryData;
	unsigned int OldRemainingElementaryLength = RemainingElementaryLength;
	//
	// Take ownership of the already accumulated data
	//
	Buffer_t ReprocessingDataBuffer = CodedFrameBuffer;
	unsigned char *ReprocessingData = BufferBase;
	unsigned int ReprocessingDataLength = AccumulatedDataSize;
	ReprocessingDataBuffer->SetUsedDataSize(ReprocessingDataLength);
	Status = ReprocessingDataBuffer->ShrinkBuffer(max(ReprocessingDataLength, 1));
	if (Status != BufferNoError)
	{
		COLLATOR_ERROR("Failed to shrink the reprocessing buffer to size (%08x).\n", Status);
		// not fatal - we're merely wasting memory
	}
	// At the time of writing GetNewBuffer() doesn't check for leaks. This is good because otherwise
	// we wouldn't have transfer the ownership of the ReprocessingDataBuffer by making this call.
	Status = GetNewBuffer();
	if (Status != CollatorNoError)
	{
		COLLATOR_ERROR("Cannot get new buffer during error recovery\n");
		return CollatorError;
	}
	//
	// Remember that we are re-processing the previously accumulated elementary stream
	//
	AlreadyHandlingMissingNextFrameHeader = true;
	//
	// WARNING: From this point on we own the ReprocessingDataBuffer, have set the recursion avoidance
	// marker and may have damaged the RemainingElementaryData pointer. There should be no
	// short-circuit exit paths used after this point otherwise we risk avoiding the clean up
	// at the bottom of the method.
	//
	while (ReprocessingDataLength > 1)
	{
		//
		// Remove the first byte from the recovery buffer (to avoid detecting again the same start code).
		//
		ReprocessingData += 1;
		ReprocessingDataLength -= 1;
		//
		// Search for a start code in the reprocessing data. This allows us to throw away data that we
		// know will never need reprocessing which makes the recursion avoidance code more efficient.
		//
		RemainingElementaryOrigin = ReprocessingData;
		RemainingElementaryData = ReprocessingData;
		RemainingElementaryLength = ReprocessingDataLength;
		int CodeOffset;
		PotentialFrameHeaderLength = 0; // ensure no (now voided) historic data is considered by sub-class
		Status = FindNextSyncWord(&CodeOffset);
		if (Status == CodecNoError)
		{
			COLLATOR_ASSERT(CodeOffset >= 0);
			COLLATOR_DEBUG("Found start code during error recovery (byte %d of %d)\n",
				       CodeOffset, ReprocessingDataLength);
			// We found a start code, snip off all preceding data
			ReprocessingData += CodeOffset;
			ReprocessingDataLength -= CodeOffset;
		}
		else
		{
			// We didn't find a start code, snip off everything except the last few bytes. This
			// final fragment may contain a partial start code so we want to pass if through the
			// elementary stream handler again.
			unsigned FinalBytes = min(ReprocessingDataLength, FrameHeaderLength - 1);
			COLLATOR_DEBUG("Found no start code during error recovery (processing final %d bytes of %d)\n",
				       ReprocessingDataLength, FinalBytes);
			ReprocessingData += ReprocessingDataLength;
			ReprocessingDataLength = FinalBytes;
			ReprocessingData -= ReprocessingDataLength;
		}
		//
		// Process the elementary stream
		//
		Status = HandleElementaryStream(ReprocessingDataLength, ReprocessingData);
		if (CollatorNoError == Status)
		{
			COLLATOR_DEBUG("Error recovery completed, returning to normal processing\n");
			// All data consumed and stored in the subsequent accumulation buffer
			break; // Success will propagate when we return Status
		}
		else if (CollatorUnwindStack == Status)
		{
			COLLATOR_DEBUG("Stack unwound successfully, re-trying error recovery\n");
			// We found a frame header but lost lock again... let's have another go
			AccumulatedDataSize = 0; // make sure no accumulated data is carried round the loop
			continue;
		}
		else
		{
			COLLATOR_ERROR("Error handling elementary stream during error recovery\n");
			break; // Failure will propagate when we return Status
		}
	}
	//
	// Free the buffer we just consumed and restore the original elementary stream pointers
	//
	RemainingElementaryOrigin = OldRemainingElementaryOrigin;
	RemainingElementaryData = OldRemainingElementaryData;
	RemainingElementaryLength = OldRemainingElementaryLength;
	(void) ReprocessingDataBuffer->DecrementReferenceCount(IdentifierCollator);
	AlreadyHandlingMissingNextFrameHeader = false;
	return Status;
}