예제 #1
0
////////////////////////////////////////////////////////////////////////////
///
/// De-packetize incoming data and pass it to the elementary stream handler.
///
/// \return Collator status code, CollatorNoError indicates success.
///
CollatorStatus_t Collator_PesAudio_c::Input(PlayerInputDescriptor_t *Input,
					    unsigned int DataLength,
					    void *Data,
					    bool NonBlocking,
					    unsigned int *DataLengthRemaining)
{
	CollatorStatus_t Status;
//
	st_relayfs_write(ST_RELAY_TYPE_PES_AUDIO_BUFFER, ST_RELAY_SOURCE_AUDIO_COLLATOR, (unsigned char *)Data, DataLength, 0);
	COLLATOR_ASSERT(!NonBlocking);
	AssertComponentState("Collator_PesAudio_c::Input", ComponentRunning);
	InputEntry(Input, DataLength, Data, NonBlocking);
	ActOnInputDescriptor(Input);
	//
	// Copy our arguments to class members so the utility functions can
	// act upon it.
	//
	RemainingData = (unsigned char *)Data;
	RemainingLength = DataLength;
	//
	// Continually handle units of input until all input is exhausted (or an error occurs).
	//
	// Be aware that our helper functions may, during their execution, cause state changes
	// that result in a different branch being taken next time round the loop.
	//
	Status = CollatorNoError;
	while (Status == CollatorNoError && RemainingLength != 0)
	{
		//COLLATOR_DEBUG("De-PESing loop has %d bytes remaining\n", RemainingLength);
		//report_dump_hex( severity_note, RemainingData, min(RemainingLength, 188), 32, 0);
		if (SeekingPesHeader)
		{
			//
			// Try to lock to incoming PES headers
			//
			Status = SearchForPesHeader();
		}
		else if (GotPartialPesHeader)
		{
			//
			// Read in the remains of the PES header
			//
			Status = ReadPartialPesHeader();
		}
		else
		{
			//
			// Send the PES packet for frame level analysis
			//
			Status = ReadPesPacket();
		}
	}
	if (Status != CollatorNoError)
	{
		// if anything when wrong then we need to resynchronize
		COLLATOR_DEBUG("General failure; seeking new PES header\n");
		DiscardAccumulatedData();
		SeekingPesHeader = true;
	}
	InputExit();
	return Status;
}
예제 #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;
}
예제 #3
0
////////////////////////////////////////////////////////////////////////////
///
/// Handle a block of data that is not frame aligned.
///
/// There may be the end of a frame, whole frames, the start of a frame or even just
/// the middle of the frame.
///
/// If we have incomplete blocks we build up a complete one in the saved data,
/// in order to process we need to acquire a frame plus the next header (for sync check)
/// we can end up with the save buffer having a frame + part of a header, and a secondary
/// save with just part of a header.
///
/// \return Collator status code, CollatorNoError indicates success.
///
CollatorStatus_t Collator_PesAudio_c::HandleElementaryStream(unsigned int Length, unsigned char *Data)
{
	CollatorStatus_t Status;
	//
	if (DiscardPesPacket)
	{
		COLLATOR_DEBUG("Discarding %d bytes of elementary stream\n", Length);
		return CodecNoError;
	}
	//
	// Copy our arguments to class members so the utility functions can
	// act upon it.
	//
	RemainingElementaryOrigin = Data;
	RemainingElementaryData = Data;
	RemainingElementaryLength = Length;
	//
	// Continually handle units of input until all input is exhausted (or an error occurs).
	//
	// Be aware that our helper functions may, during their execution, cause state changes
	// that result in a different branch being taken next time round the loop.
	//
	Status = CollatorNoError;
	while (Status == CollatorNoError && RemainingElementaryLength != 0)
	{
		COLLATOR_DEBUG("ES loop has %d bytes remaining\n", RemainingElementaryLength);
		//report_dump_hex( severity_note, RemainingElementaryData, min(RemainingElementaryLength, 188), 32, 0);
		switch (CollatorState)
		{
			case SeekingSyncWord:
				//
				// Try to lock to incoming frame headers
				//
				Status = SearchForSyncWord();
				break;
			case GotSynchronized:
			case SeekingFrameEnd:
				//
				// Read in the remains of the frame header
				//
				Status = ReadPartialFrameHeader();
				break;
			case ReadSubFrame:
			case SkipSubFrame:
				//
				// Squirrel away the frame
				//
				Status = ReadFrame();
				break;
			case GotCompleteFrame:
				//
				// Pass the accumulated subframes to the frame parser
				//
				InternalFrameFlush();
				CollatorState = ReadSubFrame;
				break;
			default:
				// should not occur...
				COLLATOR_DEBUG("General failure; wrong collator state");
				Status = CollatorError;
				break;
		}
	}
	if (Status != CollatorNoError)
	{
		// if anything when wrong then we need to resynchronize
		COLLATOR_DEBUG("General failure; seeking new synchronization sequence\n");
		DiscardAccumulatedData();
		CollatorState = SeekingSyncWord;
	}
	return Status;
}
예제 #4
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;
}
예제 #5
0
//{{{ Input
////////////////////////////////////////////////////////////////////////////
///
/// Extract Frame length from Pes Private Data area if present.
///
/// \return Collator status code, CollatorNoError indicates success.
///
CollatorStatus_t Collator_PesFrame_c::Input(PlayerInputDescriptor_t *Input,
					    unsigned int DataLength,
					    void *Data,
					    bool NonBlocking,
					    unsigned int *DataLengthRemaining)
{
	CollatorStatus_t Status = CollatorNoError;
	bool PrivateDataPresent;
	unsigned char *DataBlock = (unsigned char *)Data;
	unsigned char *PesHeader;
	unsigned char *PayloadStart;
	unsigned int PayloadLength;
	unsigned int PesLength;
	unsigned int Offset;
	COLLATOR_ASSERT(!NonBlocking);
	AssertComponentState("Collator_PesFrame_c::Input", ComponentRunning);
	InputEntry(Input, DataLength, Data, NonBlocking);
	Offset = 0;
	while (Offset < DataLength)
	{
		// Read the length of the payload
		PrivateDataPresent = false;
		PesHeader = DataBlock + Offset;
		PesLength = (PesHeader[4] << 8) + PesHeader[5];
		if (PesLength != 0)
			PayloadLength = PesLength - PesHeader[8] - 3;
		else
			PayloadLength = 0;
		COLLATOR_DEBUG("DataLength %d, PesLength %d; PayloadLength %d, Offset %d\n", DataLength, PesLength, PayloadLength, Offset);
		Offset += PesLength + 6; // PES packet is PesLength + 6 bytes long
		Bits.SetPointer(PesHeader + 9); // Set bits pointer ready to process optional fields
		if ((PesHeader[7] & 0x80) == 0x80) // PTS present?
			//{{{ read PTS
		{
			Bits.FlushUnseen(4);
			PlaybackTime = (unsigned long long)(Bits.Get(3)) << 30;
			Bits.FlushUnseen(1);
			PlaybackTime |= Bits.Get(15) << 15;
			Bits.FlushUnseen(1);
			PlaybackTime |= Bits.Get(15);
			Bits.FlushUnseen(1);
			PlaybackTimeValid = true;
			COLLATOR_DEBUG("PTS %llu.\n", PlaybackTime);
		}
		//}}}
		if ((PesHeader[7] & 0xC0) == 0xC0) // DTS present?
			//{{{ read DTS
		{
			Bits.FlushUnseen(4);
			DecodeTime = (unsigned long long)(Bits.Get(3)) << 30;
			Bits.FlushUnseen(1);
			DecodeTime |= Bits.Get(15) << 15;
			Bits.FlushUnseen(1);
			DecodeTime |= Bits.Get(15);
			Bits.FlushUnseen(1);
			DecodeTimeValid = true;
		}
		//}}}
		else if ((PesHeader[7] & 0xC0) == 0x40)
		{
			COLLATOR_ERROR("Malformed pes header contains DTS without PTS.\n");
			DiscardAccumulatedData(); // throw away previous frame as incomplete
			InputExit();
			return CollatorError;
		}
		//}}}
		//{{{ walk down optional bits
		if ((PesHeader[7] & 0x20) == 0x20) // ESCR present
			Bits.FlushUnseen(48); // Size of ESCR
		if ((PesHeader[7] & 0x10) == 0x10) // ES Rate present
			Bits.FlushUnseen(24); // Size of ES Rate
		if ((PesHeader[7] & 0x08) == 0x08) // Trick mode control present
			Bits.FlushUnseen(8); // Size of Trick mode control
		if ((PesHeader[7] & 0x04) == 0x04) // Additional copy info present
			Bits.FlushUnseen(8); // Size of additional copy info
		if ((PesHeader[7] & 0x02) == 0x02) // PES CRC present
			Bits.FlushUnseen(16); // Size of previous packet CRC
		if ((PesHeader[7] & 0x01) == 0x01) // PES Extension flag
		{
			PrivateDataPresent = Bits.Get(1);
			Bits.FlushUnseen(7); // Size of Pes extension data
		}
		//}}}
		if (PrivateDataPresent)
		{
			if (RemainingDataLength != 0)
			{
				COLLATOR_ERROR("%s: Warning new frame indicated but %d bytes missing\n", __FUNCTION__, RemainingDataLength);
				DiscardAccumulatedData(); // throw away previous frame as incomplete
			}
			FrameSize = Bits.Get(8) + (Bits.Get(8) << 8) + (Bits.Get(8) << 16);
			RemainingDataLength = FrameSize;
			COLLATOR_DEBUG("%s: PlaybackTimeValid %d, PlaybackTime %llx, FrameSize %d\n", __FUNCTION__, PlaybackTimeValid, PlaybackTime, FrameSize);
		}
		if ((int)PayloadLength > RemainingDataLength) // Too much data - have some packets been lost?
		{
			if (RemainingDataLength != 0)
			{
				COLLATOR_ERROR("%s: Warning packet contains more bytes than needed %d bytes missing?\n", __FUNCTION__, RemainingDataLength);
				DiscardAccumulatedData(); // throw away previous frame as incomplete
				//RemainingDataLength = 0;
				//InputExit();
				//return CollatorError;
			}
			RemainingDataLength = PayloadLength; // assume new packet is stand alone frame
		}
		AccumulateStartCode(PackStartCode(AccumulatedDataSize, 0x42));
		PayloadStart = PesHeader + (PesLength + 6 - PayloadLength);
		Status = AccumulateData(PayloadLength, (unsigned char *)PayloadStart);
		if (Status != CollatorNoError)
		{
			InputExit();
			return Status;
		}
		RemainingDataLength -= PayloadLength;
		if (RemainingDataLength <= 0)
		{
			Status = InternalFrameFlush(); // flush out collected frame
			if (Status != CollatorNoError)
			{
				InputExit();
				return Status;
			}
		}
		COLLATOR_DEBUG("%s PrivateDataPresent %d, RemainingDataLength %d, PayloadLength %d\n",
			       __FUNCTION__, PrivateDataPresent, RemainingDataLength, PayloadLength);
	}
	InputExit();
	return CollatorNoError;
}
//}}}
//{{{ Input
////////////////////////////////////////////////////////////////////////////
///
/// Extract Frame length from Pes Private Data area if present.
///
/// \return Collator status code, CollatorNoError indicates success.
///
CollatorStatus_t Collator_PesVideoMjpeg_c::Input(PlayerInputDescriptor_t *Input,
												 unsigned int DataLength,
												 void *Data,
												 bool NonBlocking,
												 unsigned int *DataLengthRemaining)
{
	CollatorStatus_t Status = CollatorNoError;
	unsigned char *DataBlock = (unsigned char *)Data;
	unsigned char *PesHeader;
	unsigned int PesLength;
	unsigned int PayloadLength;
	unsigned int Offset;
	unsigned int CodeOffset;
	unsigned int Code;
	AssertComponentState("Collator_PacketPes_c::Input", ComponentRunning);
	COLLATOR_ASSERT(!NonBlocking);
	InputEntry(Input, DataLength, Data, NonBlocking);
	Offset = 0;
	RemainingData = (unsigned char *)Data;
	RemainingLength = DataLength;
	TerminationFlagIsSet = false;
	Offset = 0;
	while (Offset < DataLength)
	{
		// Read the length of the payload
		PesHeader = DataBlock + Offset;
		PesLength = (PesHeader[4] << 8) + PesHeader[5];
		if (PesLength != 0)
			PayloadLength = PesLength - PesHeader[8] - 3;
		else
			PayloadLength = 0;
		COLLATOR_DEBUG("DataLength %d, PesLength %d; PayloadLength %d, Offset %d\n", DataLength, PesLength, PayloadLength, Offset);
		Offset += PesLength + 6; // PES packet is PesLength + 6 bytes long
		Bits.SetPointer(PesHeader + 9); // Set bits pointer ready to process optional fields
		if ((PesHeader[7] & 0x80) == 0x80) // PTS present?
			//{{{ read PTS
		{
			Bits.FlushUnseen(4);
			PlaybackTime = (unsigned long long)(Bits.Get(3)) << 30;
			Bits.FlushUnseen(1);
			PlaybackTime |= Bits.Get(15) << 15;
			Bits.FlushUnseen(1);
			PlaybackTime |= Bits.Get(15);
			Bits.FlushUnseen(1);
			PlaybackTimeValid = true;
			COLLATOR_DEBUG("PTS %llu.\n", PlaybackTime);
		}
		//}}}
		if ((PesHeader[7] & 0xC0) == 0xC0) // DTS present?
			//{{{ read DTS
		{
			Bits.FlushUnseen(4);
			DecodeTime = (unsigned long long)(Bits.Get(3)) << 30;
			Bits.FlushUnseen(1);
			DecodeTime |= Bits.Get(15) << 15;
			Bits.FlushUnseen(1);
			DecodeTime |= Bits.Get(15);
			Bits.FlushUnseen(1);
			DecodeTimeValid = true;
		}
		//}}}
		else if ((PesHeader[7] & 0xC0) == 0x40)
		{
			COLLATOR_ERROR("Malformed pes header contains DTS without PTS.\n");
			DiscardAccumulatedData(); // throw away previous frame as incomplete
			InputExit();
			return CollatorError;
		}
		RemainingData = PesHeader + (PesLength + 6 - PayloadLength);
		RemainingLength = PayloadLength;
		while (RemainingLength > 0)
		{
			Status = FindNextStartCode(&CodeOffset);
			if (Status != CollatorNoError) // Error indicates no start code found
			{
				Status = AccumulateData(RemainingLength, RemainingData);
				if (Status != CollatorNoError)
					DiscardAccumulatedData();
				RemainingLength = 0;
				break;
			}
			// Got one accumulate up to and including it
			Status = AccumulateData(CodeOffset + 2, RemainingData);
			if (Status != CollatorNoError)
			{
				DiscardAccumulatedData();
				break;
			}
			Code = RemainingData[CodeOffset + 1];
			RemainingLength -= CodeOffset + 2;
			RemainingData += CodeOffset + 2;
			// Is it a block terminate code
			if ((Code == Configuration.BlockTerminateCode) && (AccumulatedDataSize > 2))
			{
				AccumulatedDataSize -= 2;
				Status = InternalFrameFlush((Configuration.StreamTerminateFlushesFrame && (Code == Configuration.StreamTerminationCode)));
				if (Status != CollatorNoError)
					break;
				BufferBase[0] = 0xff;
				BufferBase[1] = Code;
				AccumulatedDataSize = 2;
			}
			// Accumulate the start code
			Status = AccumulateStartCode(PackStartCode(AccumulatedDataSize - 2, Code));
			if (Status != CollatorNoError)
			{
				DiscardAccumulatedData();
				InputExit();
				return Status;
			}
		}
	}
	InputExit();
	return CollatorNoError;
}