PlayerStatus_t   Player_Generic_c::ProcessAccumulatedControlMessages(
						PlayerStream_t                    Stream,
						unsigned int                     *MessageCount,
						unsigned int                      MessageTableSize,
						PlayerBufferRecord_t             *MessageTable,
						unsigned long long                SequenceNumber,
						unsigned long long                Time )
{
unsigned int    i;
bool            SequenceCheck;
bool            ProcessNow;

    //
    // If we have no messages to scan return in a timely fashion
    //

    if( *MessageCount == 0 )
	return PlayerNoError;

    //
    // Perform the scan
    //

    for( i=0; i<MessageTableSize; i++ )
	if( MessageTable[i].Buffer != NULL )
	{
	    SequenceCheck       = (MessageTable[i].ControlStructure->SequenceType == SequenceTypeBeforeSequenceNumber) ||
				  (MessageTable[i].ControlStructure->SequenceType == SequenceTypeAfterSequenceNumber);

	    ProcessNow          = SequenceCheck ? ((SequenceNumber != INVALID_SEQUENCE_VALUE) && (MessageTable[i].ControlStructure->SequenceValue <= SequenceNumber)) :
						  ((Time           != INVALID_SEQUENCE_VALUE) && (MessageTable[i].ControlStructure->SequenceValue <= Time));

	    if( ProcessNow )
	    {
		ProcessControlMessage( Stream, MessageTable[i].Buffer, MessageTable[i].ControlStructure );

		MessageTable[i].Buffer                  = NULL;
		MessageTable[i].ControlStructure        = NULL;
		(*MessageCount)--;
	    }
	}

    return PlayerNoError;
}
/***********************
 * ParseData
 * 	Process incomming data
 **********************/
void RTMPConnection::ParseData(BYTE *data,const DWORD size)
{
	RTMPChunkInputStreams::iterator it;
	int len = 0;
	int digesOffsetMethod = 0;

	//Get pointer and data size
	BYTE *buffer = data;
	DWORD bufferSize = size;
	DWORD digestPosServer = 0;

	//Increase current window
	curWindowSize += size;
	//And total size
	recvSize += size;
	
	//Check current window
	if (windowSize && curWindowSize>windowSize)
	{
		//Send
		SendControlMessage(RTMPMessage::Acknowledgement,RTMPAcknowledgement::Create(recvSize));
		//Reset window
		curWindowSize = 0;
	}

	//While there is data
	while(bufferSize>0)
	{
		//Check connection state
		switch(state)
		{
			case HEADER_C0_WAIT:
				//Parse c0
				len = c0.Parse(buffer,bufferSize);
				//Move
				buffer+=len;
				bufferSize-=len;
				//If it is parsed
				if (c0.IsParsed())
				{
					//Move to next state
					state = HEADER_C1_WAIT;
					//Debug
					Log("Received c0 version: %d\n",c0.GetRTMPVersion());
				}
				break;
			case HEADER_C1_WAIT:
				//Parse c1
				len = c1.Parse(buffer,bufferSize);
				//Move
				buffer+=len;
				bufferSize-=len;
				//If it is parsed
				if (c1.IsParsed())
				{
					Log("-Received C1 client version [%d,%d,%d,%d]\n",c1.GetVersion()[0],c1.GetVersion()[1],c1.GetVersion()[2],c1.GetVersion()[3]);
					//Set s0 data
					s01.SetRTMPVersion(3);
					//Set current timestamp
					s01.SetTime(getDifTime(&startTime)/1000);
					//Check which version are we using
					if (c1.GetVersion()[3])
					{
						//Verify client
						digesOffsetMethod = VerifyC1Data(c1.GetData(),c1.GetSize());
						//Set version
						s01.SetVersion(3,5,1,1);
						//Check if found diggest ofset
						digest = (digesOffsetMethod>0);
					} else {
						//Seet version to zero
						s01.SetVersion(0,0,0,0);
						//Do not calculate digest
						digest = false;
					}
					//Set random data from memory
					BYTE* random = s01.GetRandom();
					//Fill it
					for (int i=0;i<s01.GetRandomSize();i++)
						//With random
						random[i] = rand();
					//If we have to calculate digest
					if (digest)
						//calculate digest for s1 only, skipping s0
						digestPosServer = GenerateS1Data(digesOffsetMethod,s01.GetData()+1,s01.GetSize()-1);
					//Send S01 data
					WriteData(s01.GetData(),s01.GetSize());	
					//Move to next state
					state = HEADER_C2_WAIT;
					//Debug
					Log("Sending s0 and s1 with digest %s offset method %d\n",digest?"on":"off",digesOffsetMethod);
				}
				break;
			case HEADER_C2_WAIT:
				//Parse c2
				len = c2.Parse(buffer,bufferSize);
				//Move
				buffer+=len;
				bufferSize-=len;
				//If it is parsed
				if (c2.IsParsed())
				{
					//Set s2 data
					s2.SetTime(c1.GetTime());
					//Set current timestamp
					s2.SetTime2(getDifTime(&startTime)/1000);
					//Echo c1 data
					s2.SetRandom(c1.GetRandom(),c1.GetRandomSize());
					//If we have to calculate digest
					if (digest)
						//calculate digest for s1
						GenerateS2Data(digesOffsetMethod,s2.GetData(),s2.GetSize());
					//Send S2 data
					WriteData(s2.GetData(),s2.GetSize());	
					//Move to next state
					state = CHUNK_HEADER_WAIT;
					//Debug
					Log("Received c2, sending c2. CONNECTED.\n");
				}
				break;
			case CHUNK_HEADER_WAIT:
				//Parse c2
				len = header.Parse(buffer,bufferSize);
				//Move
				buffer+=len;
				bufferSize-=len;
				//If it is parsed
				if (header.IsParsed())
				{
					//Clean all buffers
					type0.Reset();
					type1.Reset();
					type2.Reset();
					extts.Reset();
					//Move to next state
					state = CHUNK_TYPE_WAIT;
					//Debug
					//Log("Received header [fmt:%d,stream:%d]\n",header.GetFmt(),header.GetStreamId());
					//header.Dump();
				}
				break;
			case CHUNK_TYPE_WAIT:
				//Get sream id
				chunkStreamId = header.GetStreamId();
				//Find chunk stream
				it = chunkInputStreams.find(chunkStreamId);
				//Check if we have a new chunk stream or already got one
				if (it==chunkInputStreams.end())
				{
					//Log
					//Log("Creating new chunk stream [id:%d]\n",chunkStreamId);
					//Create it
					chunkInputStream = new RTMPChunkInputStream();
					//Append it
					chunkInputStreams[chunkStreamId] = chunkInputStream;	
				} else 
					//Set the stream
					chunkInputStream = it->second;
				//Switch type
				switch(header.GetFmt())
				{
					case 0:
						//Check if the buffer type has been parsed
						len = type0.Parse(buffer,bufferSize);
						//Check if it is parsed
						if (type0.IsParsed())
						{
							//Debug
							//Debug("Got type 0 header [timestamp:%lu,messagelength:%d,type:%d,streamId:%d]\n",type0.GetTimestamp(),type0.GetMessageLength(),type0.GetMessageTypeId(),type0.GetMessageStreamId());
							//type0.Dump();
							//Set data for stream
							chunkInputStream->SetMessageLength	(type0.GetMessageLength());
							chunkInputStream->SetMessageTypeId	(type0.GetMessageTypeId());
							chunkInputStream->SetMessageStreamId	(type0.GetMessageStreamId());
							//Check if we have extended timestamp
							if (type0.GetTimestamp()!=0xFFFFFF)
							{
								//Set timesptamp
								chunkInputStream->SetTimestamp(type0.GetTimestamp());
								//No timestamp delta
								chunkInputStream->SetTimestampDelta(0);
								//Move to next state
								state = CHUNK_DATA_WAIT;
							} else
								//We have to read 4 more bytes
								state = CHUNK_EXT_TIMESTAMP_WAIT;
							//Start data reception
							chunkInputStream->StartChunkData();
							//Reset sent bytes in buffer
							chunkLen = 0;
						}	
						break;
					case 1:
						//Check if the buffer type has been parsed
						len = type1.Parse(buffer,bufferSize);
						//Check if it is parsed
						if (type1.IsParsed())
						{
							//Debug
							//Debug("Got type 1 header [timestampDelta:%u,messagelength:%d,type:%d]\n",type1.GetTimestampDelta(),type1.GetMessageLength(),type1.GetMessageTypeId());
							//type1.Dump();
							//Set data for stream
							chunkInputStream->SetMessageLength(type1.GetMessageLength());
							chunkInputStream->SetMessageTypeId(type1.GetMessageTypeId());
							//Check if we have extended timestam
							if (type1.GetTimestampDelta()!=0xFFFFFF)
							{
								//Set timestamp delta
								chunkInputStream->SetTimestampDelta(type1.GetTimestampDelta());
								//Set timestamp
								chunkInputStream->IncreaseTimestampWithDelta();
								//Move to next state
								state = CHUNK_DATA_WAIT;
							} else
								//We have to read 4 more bytes
								state = CHUNK_EXT_TIMESTAMP_WAIT;
							//Start data reception
							chunkInputStream->StartChunkData();
							//Reset sent bytes in buffer
							chunkLen = 0;
						}	
						break;
					case 2:
						//Check if the buffer type has been parsed
						len = type2.Parse(buffer,bufferSize);
						//Check if it is parsed
						if (type2.IsParsed())
						{
							//Debug
							//Debug("Got type 2 header [timestampDelta:%lu]\n",type2.GetTimestampDelta());
							//type2.Dump();
							//Check if we have extended timestam
							if (type2.GetTimestampDelta()!=0xFFFFFF)
							{
								//Set timestamp delta
								chunkInputStream->SetTimestampDelta(type2.GetTimestampDelta());
								//Increase timestamp
								chunkInputStream->IncreaseTimestampWithDelta();
								//Move to next state
								state = CHUNK_DATA_WAIT;
							} else
								//We have to read 4 more bytes
								state = CHUNK_EXT_TIMESTAMP_WAIT;
							//Start data reception
							chunkInputStream->StartChunkData();
							//Reset sent bytes in buffer
							chunkLen = 0;
						}	
						break;
					case 3:
						//Debug("Got type 3 header\n");
						//No header chunck
						len = 0;
						//If it is the first chunk
						if (chunkInputStream->IsFirstChunk())
							//Increase timestamp with previous delta
							chunkInputStream->IncreaseTimestampWithDelta();
						//Start data reception
						chunkInputStream->StartChunkData();
						//Move to next state
						state = CHUNK_DATA_WAIT;
						//Reset sent bytes in buffer
						chunkLen = 0;
						break;
				}
				//Move pointer
				buffer += len;
				bufferSize -= len;
				break;
			case CHUNK_EXT_TIMESTAMP_WAIT:
				//Parse extended timestamp
				len = extts.Parse(buffer,bufferSize);
				//Move
				buffer+=len;
				bufferSize-=len;
				//If it is parsed
				if (extts.IsParsed())
				{
					//Check header type
					if (header.GetFmt()==1)
					{
						//Set the timestamp
						chunkInputStream->SetTimestamp(extts.GetTimestamp());
						//No timestamp delta
						chunkInputStream->SetTimestampDelta(0);
					} else {
						//Set timestamp delta
						chunkInputStream->SetTimestampDelta(extts.GetTimestamp());
						//Increase timestamp
						chunkInputStream->IncreaseTimestampWithDelta();
					}
					//Move to next state
					state = CHUNK_DATA_WAIT;
				}
				break;
			case CHUNK_DATA_WAIT:
				//Check max buffer size
				if (maxChunkSize && chunkLen+bufferSize>maxChunkSize)
					//Parse only max chunk size
					len = maxChunkSize-chunkLen;
				else
					//parse all data
					len = bufferSize;
				//Check size
				if (!len)
				{
					//Debug
					Error("Chunk data of size zero  [maxChunkSize:%d,chunkLen:%d]\n");
					//Skip
					break;
				}
				//Parse data
				len = chunkInputStream->Parse(buffer,len);
				//Check if it has parsed a msg
				if (chunkInputStream->IsParsed())
				{
					//Log("Got message [timestamp:%lu]\n",chunkInputStream->GetTimestamp());
					//Get message
					RTMPMessage* msg = chunkInputStream->GetMessage();
					//Get message stream
					DWORD messageStreamId = msg->GetStreamId();
					//Check message type
					if (msg->IsControlProtocolMessage())
					{
						//Get message type
						BYTE type = msg->GetType();
						//Get control protocl message
						RTMPObject* ctrl = msg->GetControlProtocolMessage();
						//Procces msg
						ProcessControlMessage(messageStreamId,type,ctrl);
					} else if (msg->IsCommandMessage()) {
						//Get Command message
						RTMPCommandMessage* cmd = msg->GetCommandMessage();
						//Dump msg
						cmd->Dump();
						//Proccess msg
						ProcessCommandMessage(messageStreamId,cmd);
					} else if (msg->IsMedia()) {
						//Get media frame
						RTMPMediaFrame* frame = msg->GetMediaFrame();
						//Check if we have it
						if (frame)
							//Process message
							ProcessMediaData(messageStreamId,frame);
					} else if (msg->IsMetaData() || msg->IsSharedObject()) {
						//Get object
						RTMPMetaData *meta = msg->GetMetaData();
						//Debug it
						meta->Dump();
						//Process meta data
						ProcessMetaData(messageStreamId,meta);
					} else {
						//UUh??
						Error("Unknown rtmp message, should never happen\n");
					}
					//Delete msg
					delete(msg);
					//Move to next chunck
					state = CHUNK_HEADER_WAIT;	
					//Clean header
					header.Reset();
				}
				//Increase buffer length
				chunkLen += len;
				//Move pointer
				buffer += len;
				bufferSize -= len;
				//Check max chunk size
				if (maxChunkSize && chunkLen>=maxChunkSize)
				{
					//Wait for next buffer header
					state = CHUNK_HEADER_WAIT;
					//Clean header
					header.Reset();
				}

				break;
		}
	}
}
void   Player_Generic_c::ProcessPostManifest(	PlayerStream_t		  Stream )
{
PlayerStatus_t			  Status;
RingStatus_t			  RingStatus;
Buffer_t			  Buffer;
Buffer_t			  OriginalCodedFrameBuffer;
BufferType_t			  BufferType;
PlayerControlStructure_t	 *ControlStructure;
ParsedFrameParameters_t		 *ParsedFrameParameters;
PlayerSequenceNumber_t		 *SequenceNumberStructure;
unsigned long long		  LastEntryTime;
unsigned long long		  SequenceNumber;
unsigned long long                MaximumActualSequenceNumberSeen;
unsigned long long 		  Time;
unsigned int			  AccumulatedBeforeControlMessagesCount;
unsigned int			  AccumulatedAfterControlMessagesCount;
bool				  ProcessNow;
unsigned int			 *Count;
PlayerBufferRecord_t		 *Table;
VideoOutputTiming_t		 *OutputTiming;
unsigned long long		  Now;

//

    LastEntryTime				= OS_GetTimeInMicroSeconds();
    SequenceNumber				= INVALID_SEQUENCE_VALUE;
    MaximumActualSequenceNumberSeen             = 0;
    Time					= INVALID_TIME;
    AccumulatedBeforeControlMessagesCount	= 0;
    AccumulatedAfterControlMessagesCount	= 0;

    //
    // Signal we have started
    //

    OS_LockMutex( &Lock );

    Stream->ProcessRunningCount++;

    if( Stream->ProcessRunningCount == Stream->ExpectedProcessCount )
	OS_SetEvent( &Stream->StartStopEvent );

    OS_UnLockMutex( &Lock );

    //
    // Main Loop
    //

    while( !Stream->Terminating )
    {
	RingStatus	= Stream->ManifestedBufferRing->Extract( (unsigned int *)(&Buffer), PLAYER_MAX_EVENT_WAIT );

	Now	= OS_GetTimeInMicroSeconds();
	if( Stream->ReTimeQueuedFrames && ((Now - Stream->ReTimeStart) > PLAYER_MAX_TIME_IN_RETIMING) )
	    Stream->ReTimeQueuedFrames	= false;

	if( RingStatus == RingNothingToGet )
	    continue;

	Buffer->GetType( &BufferType );
	Buffer->TransferOwnership( IdentifierProcessPostManifest );

	//
	// Deal with a coded frame buffer 
	//

	if( BufferType == Stream->DecodeBufferType )
	{
	    Stream->FramesFromManifestorCount++;

#if 0  
{
	static unsigned long long         LastTime = 0;
	static unsigned long long         LastActualTime = 0;
	AudioOutputTiming_t              *OutputTiming;
	
	Buffer->ObtainMetaDataReference( MetaDataAudioOutputTimingType, (void **)&OutputTiming);
	
	report( severity_info, "Post Dn = %d, DS= %6lld, DAS = %6lld, S = %016llx,AS = %016llx\n",
	                OutputTiming->DisplayCount,
	                OutputTiming->SystemPlaybackTime - LastTime,
	                OutputTiming->ActualSystemPlaybackTime - LastActualTime,
	                OutputTiming->SystemPlaybackTime,
					OutputTiming->ActualSystemPlaybackTime );

    LastTime            = OutputTiming->SystemPlaybackTime;
    LastActualTime      = OutputTiming->ActualSystemPlaybackTime;
}
#endif
#if 0
{
static unsigned long long	  LastTime = 0;
static unsigned long long	  LastActualTime = 0;
VideoOutputTiming_t     	 *OutputTiming;

Buffer->ObtainMetaDataReference( MetaDataVideoOutputTimingType, (void **)&OutputTiming );

report( severity_info, "Post Dn = %d %d, I = %d, TFF = %d, DS= %6lld, DAS = %6lld, S = %016llx, AS = %016llx\n",
		OutputTiming->DisplayCount[0], OutputTiming->DisplayCount[1],
		OutputTiming->Interlaced, OutputTiming->TopFieldFirst,
		OutputTiming->SystemPlaybackTime - LastTime,
		OutputTiming->ActualSystemPlaybackTime - LastActualTime,
		OutputTiming->SystemPlaybackTime, OutputTiming->ActualSystemPlaybackTime );

    LastTime 		= OutputTiming->SystemPlaybackTime;
    LastActualTime 	= OutputTiming->ActualSystemPlaybackTime;
}
#endif

	    //
	    // Obtain a sequence number from the buffer
	    //

	    Status	= Buffer->ObtainAttachedBufferReference( Stream->CodedFrameBufferType, &OriginalCodedFrameBuffer );
	    if( Status != PlayerNoError )
	    {
	        report( severity_error, "Player_Generic_c::ProcessPostManifest - Unable to obtain the the original coded frame buffer - Implementation error\n" );
		Buffer->DecrementReferenceCount( IdentifierProcessPostManifest );
		continue;
	    }

	    Status	= OriginalCodedFrameBuffer->ObtainMetaDataReference( MetaDataSequenceNumberType, (void **)(&SequenceNumberStructure) );
	    if( Status != PlayerNoError )
	    {
	        report( severity_error, "Player_Generic_c::ProcessPostManifest - Unable to obtain the meta data \"SequenceNumber\" - Implementation error\n" );
		Buffer->DecrementReferenceCount( IdentifierProcessPostManifest );
		continue;
	    }

	    Status	= Buffer->ObtainMetaDataReference( MetaDataParsedFrameParametersReferenceType, (void **)(&ParsedFrameParameters) );
	    if( Status != PlayerNoError )
	    {
	        report( severity_error, "Player_Generic_c::ProcessPostManifest - Unable to obtain the meta data \"ParsedFrameParametersReference\" - Implementation error\n" );
		Buffer->DecrementReferenceCount( IdentifierProcessPostManifest );
		continue;
	    }

	    //
	    // Check for whether or not we are in re-timing
	    //

	    if( Stream->ReTimeQueuedFrames && !SequenceNumberStructure->MarkerFrame )
	    {
		Status	= Buffer->ObtainMetaDataReference( (Stream->StreamType == StreamTypeVideo ? MetaDataVideoOutputTimingType : MetaDataAudioOutputTimingType),
							   (void **)&OutputTiming );
		if( Status != PlayerNoError )
		{
	            report( severity_error, "Player_Generic_c::ProcessPostManifest - Unable to obtain the meta data \"%s\" - Implementation error\n",
				(Stream->StreamType == StreamTypeVideo ? "VideoOutputTiming" : "AudioOutputTiming") );
		    Buffer->DecrementReferenceCount( IdentifierProcessPostManifest );
		    continue;
		}

		if( ValidTime(OutputTiming->ActualSystemPlaybackTime) )
		{
		    Stream->ReTimeQueuedFrames	= false;
		}
		else
		{
		    Stream->OutputTimer->GenerateFrameTiming( Buffer );
		    Status  = Stream->OutputTimer->TestForFrameDrop( Buffer, OutputTimerBeforeManifestation );
		    if( !Stream->Terminating && (Status == OutputTimerNoError) )
		    {
			Stream->FramesToManifestorCount++;
			Stream->Manifestor->QueueDecodeBuffer( Buffer );
			continue;
		    }
		}
	    }

	    //
	    // Extract the sequence number, and write the timing statistics
	    //

//report( severity_info, "MQ Post Man %d - %d\n", Stream->StreamType, ParsedFrameParameters->DisplayFrameIndex );

	    SequenceNumberStructure->TimeEntryInProcess3	= OS_GetTimeInMicroSeconds();
	    SequenceNumberStructure->DeltaEntryInProcess3	= SequenceNumberStructure->TimeEntryInProcess3 - LastEntryTime;
	    LastEntryTime					= SequenceNumberStructure->TimeEntryInProcess3;
	    SequenceNumber					= SequenceNumberStructure->Value;
	    MaximumActualSequenceNumberSeen			= max(SequenceNumber, MaximumActualSequenceNumberSeen);
	    Time						= ParsedFrameParameters->NativePlaybackTime;

#ifndef __TDT__
	    ProcessStatistics( Stream, SequenceNumberStructure );
#endif

	    if( SequenceNumberStructure->MarkerFrame )
	    {
		Stream->DiscardingUntilMarkerFramePostM	= false;
		Time					= INVALID_TIME;
	    }

	    //
	    // Process any outstanding control messages to be applied before this buffer
	    //

	    ProcessAccumulatedControlMessages( 	Stream, 
						&AccumulatedBeforeControlMessagesCount,
						PLAYER_MAX_POSTM_MESSAGES,
						Stream->AccumulatedBeforePostMControlMessages, 
						SequenceNumber, Time );

	    //
	    // Pass buffer back into output timer
	    // and release the buffer.
	    //

	    if( !SequenceNumberStructure->MarkerFrame )
	    {
		Stream->OutputTimer->RecordActualFrameTiming( Buffer );
		Stream->Codec->ReleaseDecodeBuffer( Buffer );
	    }
	    else
		Buffer->DecrementReferenceCount( IdentifierProcessPostManifest );

	    //
	    // Process any outstanding control messages to be applied after this buffer
	    //

	    ProcessAccumulatedControlMessages( 	Stream,
						&AccumulatedAfterControlMessagesCount,
						PLAYER_MAX_POSTM_MESSAGES,
						Stream->AccumulatedAfterPostMControlMessages, 
						SequenceNumber, Time );
	}

	//
	// Deal with a player control structure
	//

	else if( BufferType == BufferPlayerControlStructureType )
	{
	    Buffer->ObtainDataReference( NULL, NULL, (void **)(&ControlStructure) );

	    ProcessNow	= (ControlStructure->SequenceType == SequenceTypeImmediate) ||
			  ((SequenceNumber != INVALID_SEQUENCE_VALUE) && (ControlStructure->SequenceValue <= MaximumActualSequenceNumberSeen));

	    if( ProcessNow )
		ProcessControlMessage( Stream, Buffer, ControlStructure );
	    else
	    {
		if( (ControlStructure->SequenceType == SequenceTypeBeforeSequenceNumber) ||
		    (ControlStructure->SequenceType == SequenceTypeBeforePlaybackTime) )
		{
		    Count	= &AccumulatedBeforeControlMessagesCount;
		    Table	= Stream->AccumulatedBeforePostMControlMessages;
		}
		else
		{
		    Count	= &AccumulatedAfterControlMessagesCount;
		    Table	= Stream->AccumulatedAfterPostMControlMessages;
		}

		AccumulateControlMessage( Buffer, ControlStructure, Count, PLAYER_MAX_POSTM_MESSAGES, Table );
	    }
	}
	else
	{
	    report( severity_error, "Player_Generic_c::ProcessPostManifest - Unknown buffer type received - Implementation error.\n" );
	    Buffer->DecrementReferenceCount();
	}
    }

    report( severity_info, "3333 Holding control strutures %d\n", AccumulatedBeforeControlMessagesCount + AccumulatedAfterControlMessagesCount );

    //
    // Make sur no one will wait for these
    //

    Stream->ReTimeQueuedFrames	= false;

    //
    // Signal we have terminated
    //

    OS_LockMutex( &Lock );

    Stream->ProcessRunningCount--;

    if( Stream->ProcessRunningCount == 0 )
	OS_SetEvent( &Stream->StartStopEvent );

    OS_UnLockMutex( &Lock );
}
void   Player_Generic_c::ProcessDecodeToManifest(       PlayerStream_t            Stream )
{
unsigned int                      i;
PlayerStatus_t                    Status;
RingStatus_t                      RingStatus;
unsigned int                      AccumulatedBufferTableOccupancy;
PlayerBufferRecord_t             *AccumulatedBufferTable;
Buffer_t                          Buffer = NULL;
Buffer_t                          OriginalCodedFrameBuffer;
BufferType_t                      BufferType;
PlayerControlStructure_t         *ControlStructure;
ParsedFrameParameters_t          *ParsedFrameParameters;
unsigned int                      LowestIndex;
unsigned int                      LowestDisplayFrameIndex;
unsigned int                      DesiredFrameIndex;
unsigned int			  PossibleDecodeBuffers;
unsigned int                      MaxDecodesOutOfOrder;
PlayerSequenceNumber_t           *SequenceNumberStructure;
unsigned long long                LastEntryTime;
unsigned long long                SequenceNumber;
unsigned long long                MinumumSequenceNumberAccumulated;
unsigned long long                MaximumActualSequenceNumberSeen;
unsigned long long                Time;
unsigned int                      AccumulatedBeforeControlMessagesCount;
unsigned int                      AccumulatedAfterControlMessagesCount;
bool                              SequenceCheck;
bool                              ProcessNow;
unsigned int                     *Count;
PlayerBufferRecord_t             *Table;
Buffer_t                          MarkerFrameBuffer;
bool                              FirstFrame;
bool                              DiscardBuffer;
bool				  LastPreManifestDiscardBuffer;
unsigned char                     SubmitInitialFrame;
Buffer_t                          InitialFrameBuffer;

    //
    // Set parameters
    //

    AccumulatedBufferTableOccupancy             = 0;
    AccumulatedBufferTable                      = Stream->AccumulatedDecodeBufferTable;

    LastEntryTime                               = OS_GetTimeInMicroSeconds();
    SequenceNumber                              = INVALID_SEQUENCE_VALUE;
    Time                                        = INVALID_TIME;
    AccumulatedBeforeControlMessagesCount       = 0;
    AccumulatedAfterControlMessagesCount        = 0;

    MinumumSequenceNumberAccumulated		= 0xffffffffffffffffULL;
    MaximumActualSequenceNumberSeen             = 0;
    DesiredFrameIndex                           = 0;
    FirstFrame                                  = true;

    MarkerFrameBuffer                           = NULL;
    InitialFrameBuffer				= NULL;

    LastPreManifestDiscardBuffer		= false;

    //
    // Signal we have started
    //

    OS_LockMutex( &Lock );

    Stream->ProcessRunningCount++;

    if( Stream->ProcessRunningCount == Stream->ExpectedProcessCount )
	OS_SetEvent( &Stream->StartStopEvent );

    OS_UnLockMutex( &Lock );

    //
    // Main Loop
    //

    while( !Stream->Terminating )
    {
	//
	// Buffer re-ordering loop
	//

	while( !Stream->Terminating )
	{
	    //
	    // Scan the list of accumulated buffers to see if the next display frame is available
	    // (whether because it was already accumulated, or because its display frame index has been updated)
	    //

	    MinumumSequenceNumberAccumulated    = 0xffffffffffffffffULL;
	    if( AccumulatedBufferTableOccupancy != 0 )
	    {
		LowestIndex                     = INVALID_INDEX;
		LowestDisplayFrameIndex         = INVALID_INDEX;
		for( i=0; i<Stream->NumberOfDecodeBuffers; i++ )
		    if( AccumulatedBufferTable[i].Buffer != NULL )
		    {
			MinumumSequenceNumberAccumulated        = min(MinumumSequenceNumberAccumulated, AccumulatedBufferTable[i].SequenceNumber);

			if( (AccumulatedBufferTable[i].ParsedFrameParameters->DisplayFrameIndex != INVALID_INDEX)       &&
			    ((LowestIndex == INVALID_INDEX) || (AccumulatedBufferTable[i].ParsedFrameParameters->DisplayFrameIndex < LowestDisplayFrameIndex)) )
			{
			    LowestDisplayFrameIndex     = AccumulatedBufferTable[i].ParsedFrameParameters->DisplayFrameIndex;
			    LowestIndex                 = i;
			}
		    }

		Stream->Manifestor->GetDecodeBufferCount( &PossibleDecodeBuffers );
		MaxDecodesOutOfOrder		= (Stream->Playback->Direction == PlayForward) ? (PossibleDecodeBuffers >> 1) : (3 * PossibleDecodeBuffers)/4;
		MaxDecodesOutOfOrder		= min( (PossibleDecodeBuffers - PLAYER_MINIMUM_NUMBER_OF_WORKING_DECODE_BUFFERS), MaxDecodesOutOfOrder );
		if( Stream->Playback->Direction == PlayForward )
		    MaxDecodesOutOfOrder	= min( PLAYER_LIMIT_ON_OUT_OF_ORDER_DECODES, MaxDecodesOutOfOrder );

		if( (LowestIndex != INVALID_INDEX) &&
		    (   (LowestDisplayFrameIndex == DesiredFrameIndex) || 
			(AccumulatedBufferTableOccupancy >= MaxDecodesOutOfOrder) ||
			(AccumulatedBufferTable[LowestIndex].ParsedFrameParameters->CollapseHolesInDisplayIndices) ||
			(MarkerFrameBuffer != NULL) ) )
		{
		    Buffer                                                      = AccumulatedBufferTable[LowestIndex].Buffer;
		    ParsedFrameParameters                                       = AccumulatedBufferTable[LowestIndex].ParsedFrameParameters;
		    SequenceNumber                                              = AccumulatedBufferTable[LowestIndex].SequenceNumber;
		    BufferType                                                  = Stream->DecodeBufferType;
		    AccumulatedBufferTable[LowestIndex].Buffer                  = NULL;
		    AccumulatedBufferTable[LowestIndex].ParsedFrameParameters   = NULL;
		    AccumulatedBufferTableOccupancy--;
		    break;
		}
	    }

	    //
	    // Skip any frame indices that were unused
	    //

	    while( CheckForNonDecodedFrame( Stream, DesiredFrameIndex ) )
		DesiredFrameIndex++;

	    //
	    // If we get here with a marker frame, then we have emptied our accumulated list
	    //

	    if( MarkerFrameBuffer != NULL )
	    {
		SequenceNumber                          = SequenceNumberStructure->Value;
		MaximumActualSequenceNumberSeen		= max(SequenceNumber, MaximumActualSequenceNumberSeen);
		ProcessAccumulatedControlMessages(  Stream, 
						    &AccumulatedBeforeControlMessagesCount,
						    PLAYER_MAX_DTOM_MESSAGES,
						    Stream->AccumulatedBeforeDtoMControlMessages, 
						    SequenceNumber, Time );

		ProcessAccumulatedControlMessages(  Stream,
						    &AccumulatedAfterControlMessagesCount,
						    PLAYER_MAX_DTOM_MESSAGES,
						    Stream->AccumulatedAfterDtoMControlMessages, 
						    SequenceNumber, Time );

		Stream->ManifestedBufferRing->Insert( (unsigned int)MarkerFrameBuffer );        // Pass on the marker
		MarkerFrameBuffer                       = NULL;
		Stream->DiscardingUntilMarkerFrameDtoM  = false;
		continue;
	    }

	    //
	    // Get a new buffer (continue will still perform the scan)
	    //

	    RingStatus  = Stream->DecodedFrameRing->Extract( (unsigned int *)(&Buffer), PLAYER_NEXT_FRAME_EVENT_WAIT );
	    if( (RingStatus == RingNothingToGet) || (Buffer == NULL) || Stream->Terminating )
		continue;

	    Buffer->TransferOwnership( IdentifierProcessDecodeToManifest );
	    Buffer->GetType( &BufferType );
	    if( BufferType != Stream->DecodeBufferType )
		break;

	    //
	    // Obtain a sequence number from the buffer
	    //

	    Status      = Buffer->ObtainAttachedBufferReference( Stream->CodedFrameBufferType, &OriginalCodedFrameBuffer );
	    if( Status != PlayerNoError )
	    {
		report( severity_error, "Player_Generic_c::ProcessDecodeToManifest - Unable to obtain the the original coded frame buffer - Implementation error\n" );
		Buffer->DecrementReferenceCount( IdentifierProcessDecodeToManifest );
		continue;
	    }

	    Status      = OriginalCodedFrameBuffer->ObtainMetaDataReference( MetaDataSequenceNumberType, (void **)(&SequenceNumberStructure) );
	    if( Status != PlayerNoError )
	    {
		report( severity_error, "Player_Generic_c::ProcessDecodeToManifest - Unable to obtain the meta data \"SequenceNumber\" - Implementation error\n" );
		Buffer->DecrementReferenceCount( IdentifierProcessDecodeToManifest );
		continue;
	    }

	    SequenceNumberStructure->TimeEntryInProcess2        = OS_GetTimeInMicroSeconds();
	    SequenceNumberStructure->DeltaEntryInProcess2       = SequenceNumberStructure->TimeEntryInProcess2 - LastEntryTime;
	    LastEntryTime                                       = SequenceNumberStructure->TimeEntryInProcess2;
	    SequenceNumber                                      = SequenceNumberStructure->Value;

	    //
	    // Check, is this a marker frame
	    //

	    if( SequenceNumberStructure->MarkerFrame )
	    {
		MarkerFrameBuffer       = Buffer;
		continue;                       // allow us to empty the accumulated buffer list
	    }

	    //
	    // If this is the first seen decode buffer do we wish to offer it up as an initial frame
	    //

	    if( FirstFrame )
	    {
		FirstFrame              = false;
		SubmitInitialFrame      = PolicyValue( Stream->Playback, Stream, PolicyManifestFirstFrameEarly );
		if( SubmitInitialFrame == PolicyValueApply )
		{
		    Status      	= Stream->Manifestor->InitialFrame( Buffer );
		    if( Status != ManifestorNoError )
			report( severity_error, "Player_Generic_c::ProcessDecodeToManifest - Failed to apply InitialFrame action.\n" );

		    if( InitialFrameBuffer != NULL )
			Stream->Codec->ReleaseDecodeBuffer( InitialFrameBuffer );

		    InitialFrameBuffer	= Buffer;
		    InitialFrameBuffer->IncrementReferenceCount();
		}
	    }

	    //
	    // Do we want to insert this in the table
	    //

	    Status      = Buffer->ObtainMetaDataReference( MetaDataParsedFrameParametersReferenceType, (void **)(&ParsedFrameParameters) );
	    if( Status != PlayerNoError )
	    {
		report( severity_error, "Player_Generic_c::ProcessDecodeToManifest - Unable to obtain the meta data \"ParsedFrameParametersReference\" - Implementation error\n" );
		Buffer->DecrementReferenceCount( IdentifierProcessDecodeToManifest );
		continue;
	    }

#if 0
{
unsigned int A, B;
	Stream->DecodeBufferPool->GetPoolUsage( &A, &B, NULL, NULL, NULL );

report( severity_info, "Got(%d) %3d (R = %d, K = %d) %d, %016llx - %d %d/%d\n", Stream->StreamType, ParsedFrameParameters->DecodeFrameIndex, ParsedFrameParameters->ReferenceFrame, ParsedFrameParameters->KeyFrame, ParsedFrameParameters->DisplayFrameIndex, ParsedFrameParameters->NormalizedPlaybackTime, AccumulatedBufferTableOccupancy, B, A );
}
#endif
	    if( ParsedFrameParameters->DisplayFrameIndex <= DesiredFrameIndex )
		break;

	    for( i=0; i<Stream->NumberOfDecodeBuffers; i++ )
		if( AccumulatedBufferTable[i].Buffer == NULL )
		{
		    AccumulatedBufferTable[i].Buffer                    = Buffer;
		    AccumulatedBufferTable[i].SequenceNumber            = SequenceNumber;
		    AccumulatedBufferTable[i].ParsedFrameParameters     = ParsedFrameParameters;
		    AccumulatedBufferTableOccupancy++;
		    break;
		}

	    if( i >= Stream->NumberOfDecodeBuffers )
	    {
		report( severity_error, "Player_Generic_c::ProcessDecodeToManifest - Unable to insert buffer in table - Implementation error.\n" );
		break;  // Assume it is immediate, as an implementation error this is pretty nasty
	    }
	}

	if( Stream->Terminating )
	    continue;

	// --------------------------------------------------------------------------------------------
	// We now have a buffer after frame re-ordering
	//
	// First calculate the sequence number that applies to this frame
	// this calculation may appear wierd, the idea is this, assume you
	// have a video stream IPBB, sequence numbers 0 1 2 3, frame reordering
	// will yield sequence numbers 0 2 3 1 IE any command to be executed at
	// the end of the stream will appear 1 frame early, the calculations 
	// below will re-wossname the sequence numbers to 0 1 1 3 causing the 
	// signal to occur at the correct point.
	//

	//
	// Deal with a coded frame buffer 
	//

	if( BufferType == Stream->DecodeBufferType )
	{
	    //
	    // Report any re-ordering problems
	    //

	    if( ParsedFrameParameters->CollapseHolesInDisplayIndices && (ParsedFrameParameters->DisplayFrameIndex > DesiredFrameIndex) )
		DesiredFrameIndex	= ParsedFrameParameters->DisplayFrameIndex;

	    if( ParsedFrameParameters->DisplayFrameIndex > DesiredFrameIndex )
		report( severity_error, "Player_Generic_c::ProcessDecodeToManifest - Hole in display frame indices (Got %d Expected %d).\n", ParsedFrameParameters->DisplayFrameIndex, DesiredFrameIndex );

	    if( ParsedFrameParameters->DisplayFrameIndex < DesiredFrameIndex )
		report( severity_error, "Player_Generic_c::ProcessDecodeToManifest - Frame re-ordering failure (Got %d Expected %d) - Implementation error.\n", ParsedFrameParameters->DisplayFrameIndex, DesiredFrameIndex );

	    //
	    // First calculate the sequence number that applies to this frame
	    // this calculation may appear wierd, the idea is this, assume you
	    // have a video stream IPBB, sequence numbers 0 1 2 3, frame reordering
	    // will yield sequence numbers 0 2 3 1 IE any command to be executed at
	    // the end of the stream will appear 1 frame early, the calculations 
	    // below will re-wossname the sequence numbers to 0 1 1 3 causing the 
	    // signal to occur at the correct point.
	    //

	    MaximumActualSequenceNumberSeen     = max(SequenceNumber, MaximumActualSequenceNumberSeen);
	    SequenceNumber                      = min(MaximumActualSequenceNumberSeen, MinumumSequenceNumberAccumulated );

	    Time                                = ParsedFrameParameters->NativePlaybackTime;

	    //
	    // Process any outstanding control messages to be applied before this buffer
	    //

	    ProcessAccumulatedControlMessages(  Stream, 
						&AccumulatedBeforeControlMessagesCount,
						PLAYER_MAX_DTOM_MESSAGES,
						Stream->AccumulatedBeforeDtoMControlMessages, 
						SequenceNumber, Time );

	    //
	    // If we are paused, then we loop waiting for something to happen
	    //

	    if( Stream->Playback->Speed == 0 )
	    {
		while( (Stream->Playback->Speed == 0) && !Stream->Step && !Stream->Terminating && !Stream->DiscardingUntilMarkerFrameDtoM )
		{
		    OS_WaitForEvent( &Stream->SingleStepMayHaveHappened, PLAYER_NEXT_FRAME_EVENT_WAIT );
		    OS_ResetEvent( &Stream->SingleStepMayHaveHappened );
		}

		Stream->Step    = false;
	    }

	    //
	    // If we are not discarding everything, then procede to process the buffer
	    //

	    DiscardBuffer       = Stream->DiscardingUntilMarkerFrameDtoM;

	    //
	    // Handle output timing functions, await entry into the decode window, 
	    // Then check for frame drop (whether due to trick mode, or because 
	    // we are running late). 
	    // NOTE1 Indicating we are not before decode, means 
	    // reference frames can be dropped, we will simply not display them
	    // NOTE2 We may block in these functions, so it is important to 
	    // recheck flags
	    //

	    if( !DiscardBuffer )
	    {
		Status  = Stream->OutputTimer->TestForFrameDrop( Buffer, OutputTimerBeforeOutputTiming );

		if( Status == OutputTimerNoError )
		{
		    //
		    // Note we loop here if we are engaged in re-timing the decoded frames
		    //

		    while( !Stream->Terminating && Stream->ReTimeQueuedFrames )
			OS_SleepMilliSeconds( PLAYER_RETIMING_WAIT );

		    Stream->OutputTimer->GenerateFrameTiming( Buffer );
		    Status  = Stream->OutputTimer->TestForFrameDrop( Buffer, OutputTimerBeforeManifestation );
		}

		if( Stream->DiscardingUntilMarkerFrameDtoM ||
		    Stream->Terminating ||
		    (Status != OutputTimerNoError) )
		    DiscardBuffer       = true;

		if( (DiscardBuffer != LastPreManifestDiscardBuffer) &&
		    (Status        == OutputTimerUntimedFrame) )
		{
		    report( severity_error, "Discarding untimed frames.\n" );
		}
		LastPreManifestDiscardBuffer	= DiscardBuffer;

#if 0
		// Nick debug data
		if( Status != OutputTimerNoError )
		    report( severity_info, "Timer Discard(%d) %3d (before Manifest) %08x\n", Stream->StreamType, ParsedFrameParameters->DecodeFrameIndex, Status );
#endif
	    }

	    //
	    // Pass the buffer to the manifestor for manifestation
	    // we do not release our hold on this buffer, buffers passed
	    // to the manifestor always re-appear on its output ring.
	    // NOTE calculate next desired frame index before we 
	    // give away the buffer, because ParsedFrameParameters
	    // can become invalid after either of the calls below.
	    //

	    DesiredFrameIndex   = ParsedFrameParameters->DisplayFrameIndex + 1;

	    if( !DiscardBuffer )
	    {
		SequenceNumberStructure->TimePassToManifestor   = OS_GetTimeInMicroSeconds();

#if 0
{
static unsigned long long         LastOutputTime = 0;
static unsigned long long         LastOutputTime1 = 0;
VideoOutputTiming_t              *OutputTiming;
unsigned int                      C0,C1,C2,C3;

Buffer->ObtainMetaDataReference( MetaDataVideoOutputTimingType, (void **)&OutputTiming );
Stream->CodedFrameBufferPool->GetPoolUsage( &C0, &C1, NULL, NULL, NULL );
Stream->DecodeBufferPool->GetPoolUsage( &C2, &C3, NULL, NULL, NULL );
report( severity_info, "Ord %3d (R = %d, K = %d) %d, %6lld %6lld %6lld %6lld (%d/%d %d/%d) (%d %d) %6lld %6lld\n",
	ParsedFrameParameters->DecodeFrameIndex, ParsedFrameParameters->ReferenceFrame, ParsedFrameParameters->KeyFrame, ParsedFrameParameters->DisplayFrameIndex,
	OutputTiming->SystemPlaybackTime - SequenceNumberStructure->TimePassToManifestor,
	SequenceNumberStructure->TimePassToManifestor - SequenceNumberStructure->TimeEntryInProcess2,
	SequenceNumberStructure->TimePassToManifestor - SequenceNumberStructure->TimeEntryInProcess1,
	SequenceNumberStructure->TimePassToManifestor - SequenceNumberStructure->TimeEntryInProcess0,
	C0, C1, C2, C3,
	Stream->FramesToManifestorCount, Stream->FramesFromManifestorCount,
	OutputTiming->SystemPlaybackTime - LastOutputTime, ParsedFrameParameters->NormalizedPlaybackTime - LastOutputTime1 );

//Buffer->TransferOwnership( IdentifierProcessDecodeToManifest, IdentifierManifestor );
//if( (OutputTiming->SystemPlaybackTime - SequenceNumberStructure->TimePassToManifestor) > 0xffffffffULL )
//    Stream->DecodeBufferPool->Dump( DumpAll );

    LastOutputTime = OutputTiming->SystemPlaybackTime;
    LastOutputTime1 = ParsedFrameParameters->NormalizedPlaybackTime;

}
if( Stream->FramesToManifestorCount >= 55 )
{
OS_SleepMilliSeconds( 1000 );
report( severity_info, "Ord(%d) %3d (R = %d, K = %d) %d, %016llx %016llx\n", Stream->StreamType, ParsedFrameParameters->DecodeFrameIndex, ParsedFrameParameters->ReferenceFrame, ParsedFrameParameters->KeyFrame, ParsedFrameParameters->DisplayFrameIndex, ParsedFrameParameters->NormalizedPlaybackTime, ParsedFrameParameters->NativePlaybackTime );
OS_SleepMilliSeconds( 4000 );
}
#endif
		Stream->FramesToManifestorCount++;
		Status	= Stream->Manifestor->QueueDecodeBuffer( Buffer );

		if( Status != ManifestorNoError )
		    DiscardBuffer	= true;

		if( InitialFrameBuffer != NULL )
		{
		    Stream->Codec->ReleaseDecodeBuffer( InitialFrameBuffer );
		    InitialFrameBuffer	= NULL;
		}
	    }

	    if( DiscardBuffer )
	    {
		Stream->Codec->ReleaseDecodeBuffer( Buffer );

		if( Stream->Playback->Speed == 0 )
		    Stream->Step	= true;
	    }

	    //
	    // Process any outstanding control messages to be applied after this buffer
	    //

	    ProcessAccumulatedControlMessages(  Stream,
						&AccumulatedAfterControlMessagesCount,
						PLAYER_MAX_DTOM_MESSAGES,
						Stream->AccumulatedAfterDtoMControlMessages, 
						SequenceNumber, Time );
	}

	//
	// Deal with a player control structure
	//

	else if( BufferType == BufferPlayerControlStructureType )
	{
	    Buffer->ObtainDataReference( NULL, NULL, (void **)(&ControlStructure) );

	    ProcessNow  = (ControlStructure->SequenceType == SequenceTypeImmediate);
	    if( !ProcessNow )
	    {
		SequenceCheck   = (ControlStructure->SequenceType == SequenceTypeBeforeSequenceNumber) ||
				  (ControlStructure->SequenceType == SequenceTypeAfterSequenceNumber);

		ProcessNow      = SequenceCheck ? ((SequenceNumber != INVALID_SEQUENCE_VALUE) && (ControlStructure->SequenceValue <= MaximumActualSequenceNumberSeen)) :
						  ((Time           != INVALID_SEQUENCE_VALUE) && (ControlStructure->SequenceValue <= Time));
	    }

	    if( ProcessNow )
		ProcessControlMessage( Stream, Buffer, ControlStructure );
	    else
	    {
		if( (ControlStructure->SequenceType == SequenceTypeBeforeSequenceNumber) ||
		    (ControlStructure->SequenceType == SequenceTypeBeforePlaybackTime) )
		{
		    Count       = &AccumulatedBeforeControlMessagesCount;
		    Table       = Stream->AccumulatedBeforeDtoMControlMessages;
		}
		else
		{
		    Count       = &AccumulatedAfterControlMessagesCount;
		    Table       = Stream->AccumulatedAfterDtoMControlMessages;
		}

		AccumulateControlMessage( Buffer, ControlStructure, Count, PLAYER_MAX_DTOM_MESSAGES, Table );
	    }
	}
	else
	{
	    report( severity_error, "Player_Generic_c::ProcessDecodeToManifest - Unknown buffer type received - Implementation error.\n" );
	    Buffer->DecrementReferenceCount();
	}
    }