//{{{ Constructor
////////////////////////////////////////////////////////////////////////////
///
/// Initialize the class by resetting it.
///
/// During a constructor calls to virtual methods resolve to the current class (because
/// the vtable is still for the class being constructed). This means we need to call
/// ::Reset again because the calls made by the sub-constructors will not have called
/// our reset method.
///
Collator_PesVideoMjpeg_c::Collator_PesVideoMjpeg_c(void)
{
	COLLATOR_TRACE("%s\n", __FUNCTION__);
	if (InitializationStatus != CollatorNoError)
		return;
	Collator_PesVideoMjpeg_c::Reset();
}
////////////////////////////////////////////////////////////////////////////
///
/// Record a prediction about the location of the next sync word.
///
void Collator_PesAudioDvd_c::MakeDvdSyncWordPrediction(int Prediction)
{
	// Every time we make a bad prediction then we decrement the mis-prediction counter.
	// When it reaches zero we force a wildcard prediction and enter DVD mode. This is a hack
	// to allow us to play DVD-style streams with broken first access unit pointers where the
	// pointers don't, in fact, point to the an access unit at all. Such streams have been
	// observed in the wild - see https://bugzilla.stlinux.com/show_bug.cgi?id=5051
	if (0 == RemainingMispredictionsBeforeAutomaticWildcard)
	{
		COLLATOR_TRACE("Deploying bad first access unit pointer workaround.\n");
		Prediction = WILDCARD_PREDICTION;
		PassPesPrivateDataToElementaryStreamHandler = false;
		RemainingMispredictionsBeforeAutomaticWildcard = MaxMispredictionsBeforeAutomaticWildcard;
	}
	COLLATOR_DEBUG("Making prediction of %d\n", Prediction);
	SyncWordPrediction = Prediction;
}
////////////////////////////////////////////////////////////////////////////
///
/// Check a previous prediction regarding the location of the sync word.
///
/// This method does not report the result of the prediction. Instead it
/// updates Collator_PesAudio_c::PassPesPrivateDataToElementaryStreamHandler
/// accordingly.
///
void Collator_PesAudioDvd_c::VerifyDvdSyncWordPrediction(int Offset)
{
	if (SyncWordPrediction != WILDCARD_PREDICTION)
	{
		// If SyncWordPrediction != Offset then we are *not* a DVD-style PES stream
		// and need to ensure the DVD PES private data *is* processed as elementary
		// stream.
		PassPesPrivateDataToElementaryStreamHandler = (SyncWordPrediction != Offset);
		// If we badly predicted the location of the sync word then record this. This
		// counter is used as part of a heuristic workaround in other parts of the code.
		if ((SyncWordPrediction != Offset) &&
				(SyncWordPrediction != INVALID_PREDICTION) &&
				(RemainingMispredictionsBeforeAutomaticWildcard > 0))
			RemainingMispredictionsBeforeAutomaticWildcard--;
		// Having consumed the prediction enter a wildcard mode. This causes
		// PassPesPrivateDataToElementaryStreamHandler to hold the same
		// value until we process a PES private data area whilst SeekingFrameHeader.
		// This is very important for DVD streams which can have sync words located
		// such that we never process a PES private data area in this mode. This this
		// case we would never regain lock on the stream.
		MakeDvdSyncWordPrediction(WILDCARD_PREDICTION);
	}
	else
	{
		// Apply a wildcard (this is not a 'match all'; it means 'do not change state').
		// However we can only apply a wildcard a limited number of times. After this
		// point we assume the current state (PassPesPrivateDataToElementaryStreamHandler) is
		// impeeding regain of sync and force a change of state.
		if (--RemainingWildcardsPermitted < 0)
		{
			COLLATOR_TRACE("Having trouble re-locking, %s DVD wildcard mode.\n",
						   (PassPesPrivateDataToElementaryStreamHandler ? "entering" : "leaving"));
			PassPesPrivateDataToElementaryStreamHandler = !PassPesPrivateDataToElementaryStreamHandler;
			// having switched modes we can reset our counter.
			RemainingWildcardsPermitted = MaxWildcardsPermitted;
		}
	}
}
//}}}
//{{{ Reset
////////////////////////////////////////////////////////////////////////////
///
/// Resets and configures according to the requirements of this stream content
///
/// \return void
///
CollatorStatus_t Collator_PesVideoMjpeg_c::Reset(void)
{
	CollatorStatus_t Status;
	COLLATOR_TRACE("%s\n", __FUNCTION__);
	Status = Collator_PesVideo_c::Reset();
	if (Status != CollatorNoError)
		return Status;
	Configuration.GenerateStartCodeList = true;
	Configuration.MaxStartCodes = 32;
	Configuration.StreamIdentifierMask = PES_START_CODE_MASK;
	Configuration.StreamIdentifierCode = PES_START_CODE_VIDEO;
	Configuration.BlockTerminateMask = 0xff;
	Configuration.BlockTerminateCode = MJPEG_SOI;
	Configuration.IgnoreCodesRangeStart = 0x00;
	Configuration.IgnoreCodesRangeEnd = MJPEG_SOF_0 - 1;
	Configuration.InsertFrameTerminateCode = false; // Force the mme decode to terminate after a picture
	Configuration.TerminalCode = 0;
	Configuration.ExtendedHeaderLength = 0;
	Configuration.DeferredTerminateFlag = false;
	Configuration.StreamTerminateFlushesFrame = false; // Use an end of sequence to force a frame flush
	Configuration.StreamTerminationCode = 0;
	return CollatorNoError;
}
////////////////////////////////////////////////////////////////////////////
///
/// Determine the new state of the collator according to the incoming sub frame
/// Also returns this sub frame length
///
/// \return Collator status code, CollatorNoError indicates success.
///
CollatorStatus_t Collator_PesAudioLpcm_c::DecideCollatorNextStateAndGetLength( unsigned int *FrameLength )
{
  CollatorStatus_t Status = CollatorNoError;

  //
  COLLATOR_DEBUG(">><<\n");
  
  if ( IsFirstPacket )
    {
      //case of very first packet, skip the packet portion before the FirstAccessUnitOffsetPointer
      CollatorState = SkipSubFrame;

      IsFirstPacket = false;

      // for the very first packet of dvd-audio, the stuffing bytes are not part of the PDA
      // since Configuration.ExtendedHeaderLength is not the same as PrivateHeaderLength,
      // so skip these bytes in this case
      
      *FrameLength = PesPrivateToSkip +
	             NextParsedFrameHeader.FirstAccessUnitPointer + 
	             FirstAccessUnitOffset[StreamType] -
	             NextParsedFrameHeader.PrivateHeaderLength;
      PesPrivateToSkip = 0;

      COLLATOR_TRACE("First packet: Skipping %d bytes\n", *FrameLength);

      return (Status);
    }

  // accumulate the private data area for the frame parser, 
  // if some major parameter inside have changed...

  if ( AccumulatePrivateDataArea )
    {
      // save the location of the private data area, to update it later...
      Status = AccumulateData( Configuration.ExtendedHeaderLength, &NewPesPrivateDataArea[0] );
      if ( Status != CollatorNoError )
	{
	  return (Status);
	}
      AccumulatePrivateDataArea = false;
      COLLATOR_DEBUG("Accumulate PDA of length %d\n", Configuration.ExtendedHeaderLength);
    }

  if ( !IsPesPrivateDataAreaValid )
    {
      //case of wrong packet passed to collator, skip the whole packet
      CollatorState = SkipSubFrame;
      *FrameLength = RemainingElementaryLength;
    }
  else if ( PesPrivateToSkip > 0 )
    {
      CollatorState = SkipSubFrame;
      *FrameLength = min(PesPrivateToSkip, RemainingElementaryLength);
      PesPrivateToSkip -= *FrameLength;
      COLLATOR_DEBUG("Skipping %d bytes of pda\n", *FrameLength);
    }
  else if ( RemainingDataLength > 0 )
    {
      CollatorState = ReadSubFrame;
      *FrameLength = min(RemainingDataLength, RemainingElementaryLength);
      RemainingDataLength -= *FrameLength;
      COLLATOR_DEBUG("Reading %d bytes (rest of frame)\n", *FrameLength);
    }
  else if ( (AccumulatedFrameNumber >= NbAudioFramesToGlob[StreamType][NextParsedFrameHeader.SamplingFrequency1]) ||
       IsPesPrivateDataAreaNew )
    {
      // flush the frame if we have already more than x accumulated frames
      // or if some pda key parameters are new
      CollatorState = GotCompleteFrame;
      COLLATOR_DEBUG("Flush after %d audio frames\n", AccumulatedFrameNumber);

      AccumulatedFrameNumber = 0;
      // prevent accumulating anything for this frame, the next thing we need to do
      // is accumlate the private data area for the frame parser but we can't do that
      // until we've cleared this frame from the accumulation buffer.
      *FrameLength = 0;
      // at next call of this function accumulate the pda
      AccumulatePrivateDataArea = true;
      // reset
      IsPesPrivateDataAreaNew = false;

      PlaybackTime += (GlobbedFramesOfNewPacket * LpcmPresentationTime[StreamType][NextParsedFrameHeader.SamplingFrequency1]);

      COLLATOR_DEBUG("PlaybackTime: %x\n", PlaybackTime);
    }
  else
    {
      // normal case: accumulate the audio frames
      CollatorState = ReadSubFrame;
      AccumulatedFrameNumber += 1;
      GlobbedFramesOfNewPacket += 1;
      
      COLLATOR_ASSERT(0 == RemainingDataLength);
      COLLATOR_ASSERT(0 == PesPrivateToSkip);

      // DVD-audio allows stuffing bytes to form part of the private data area (and these can be different
      // for each packet). We can't easily handle this in the PES layer since the length can't be predicted
      // and therefore we cannot set Configuration.ExtendedHeaderLength until it is too late. Instead
      // Collator_PesAudioLpcm_c::HandlePesPrivateData() records how many bytes we must skip
      // (PesPrivateToSkip) and leaves it to the state change logic to skip that data. This means that we
      // must ensure that this method will be called at the right point to skip data. We do this be making
      // sure we don't ever return a *FrameLength larger than the RemainingElementaryData .
      if ((RemainingElementaryLength < NextParsedFrameHeader.AudioFrameSize))
	{
	  RemainingDataLength = NextParsedFrameHeader.AudioFrameSize - RemainingElementaryLength;
	  *FrameLength        = RemainingElementaryLength;
	}
      else
	{
	  // read the whole frame!
	  *FrameLength        = NextParsedFrameHeader.AudioFrameSize;
	}

      COLLATOR_DEBUG("Read frame of size %d (total frames in this chunk: %d)\n", 
		     *FrameLength, 
		     AccumulatedFrameNumber);
    }

 return Status;
}