//}}}
//{{{  SetSpeed
HavanaStatus_t HavanaPlayback_c::SetSpeed   (int        PlaySpeed)
{
    PlayerStatus_t      Status;
    PlayDirection_t     Direction;
    Rational_t          Speed;

    if ((unsigned int)PlaySpeed == PLAY_SPEED_REVERSE_STOPPED)
    {
        Direction       = PlayBackward;
        PlaySpeed       = PLAY_SPEED_STOPPED;
    }
    else if (PlaySpeed >= 0)
        Direction       = PlayForward;
    else
    {
        Direction       = PlayBackward;
        PlaySpeed       = -PlaySpeed;
    }

    Speed               = Rational_t(PlaySpeed, PLAY_SPEED_NORMAL_PLAY);

    PLAYBACK_DEBUG("Setting speed to %d.%06d\n", Speed.IntegerPart(), Speed.RemainderDecimal());
    Status      = Player->SetPlaybackSpeed     (PlayerPlayback, Speed, Direction);
    if (Status != PlayerNoError)
    {
        PLAYBACK_ERROR("Failed to set speed - Status = %x\n", Status);
        return HavanaError;
    }

    return HavanaNoError;
}
OutputTimerStatus_t   OutputTimer_Video_c::FrameDuration(       void                      *ParsedAudioVideoDataParameters,
								unsigned long long        *Duration )
{
ParsedVideoParameters_t  *ParsedVideoParameters = (ParsedVideoParameters_t *)ParsedAudioVideoDataParameters;
Rational_t                TimePerFrame;
Rational_t                TotalTime;

    //
    // Calculated as :-
    //          Time per frame 1000000/frame rate
    //          Number of frames = count 0 + count 1 divided by 2 if not a progressive sequence
    //

    TimePerFrame        = (1000000 / ParsedVideoParameters->Content.FrameRate);
    TotalTime           = TimePerFrame * (ParsedVideoParameters->DisplayCount[0] + ParsedVideoParameters->DisplayCount[1]);

    if( !ParsedVideoParameters->Content.Progressive )
	TotalTime       = TotalTime / 2;

    *Duration            = TotalTime.RoundedLongLongIntegerPart();

//

    return OutputTimerNoError;
}
//}}}
//{{{  GetSpeed
HavanaStatus_t HavanaPlayback_c::GetSpeed(int*       PlaySpeed)
{
	PlayerStatus_t      Status;
	PlayDirection_t     Direction;
	Rational_t          Speed;
	Status      = Player->GetPlaybackSpeed(PlayerPlayback, &Speed, &Direction);
	if (Status != PlayerNoError)
	{
		PLAYBACK_ERROR("Failed to get speed - Status = %x\n", Status);
		return HavanaError;
	}
	PLAYBACK_DEBUG("Getting speed of %d.%06d\n", Speed.IntegerPart(), Speed.RemainderDecimal());
	*PlaySpeed          = (int)IntegerPart(Speed * PLAY_SPEED_NORMAL_PLAY);
	if (((*PlaySpeed) == PLAY_SPEED_STOPPED) && (Direction != PlayForward))
		*PlaySpeed      = PLAY_SPEED_REVERSE_STOPPED;
	else if (Direction != PlayForward)
		*PlaySpeed      = -*PlaySpeed;
	return HavanaNoError;
}
OutputTimerStatus_t   OutputTimer_Video_c::FillOutFrameTimingRecord(
							unsigned long long        SystemTime,
							void                     *ParsedAudioVideoDataParameters,
							void                     *AudioVideoDataOutputTiming )
{
unsigned int                      i;
ParsedVideoParameters_t          *ParsedVideoParameters = (ParsedVideoParameters_t *)ParsedAudioVideoDataParameters;
VideoOutputTiming_t              *VideoOutputTiming     = (VideoOutputTiming_t *)AudioVideoDataOutputTiming;
Rational_t                        FrameRate;
bool                              RemoveAnyThreeTwoPulldown;
Rational_t                        AccumulatedPanScanError;
Rational_t                        FrameCount;
Rational_t                        ThreeTwoJitter;
Rational_t                        SystemTimeError;
Rational_t                        Tmp;
Rational_t                        Speed;
PlayDirection_t                   Direction;
unsigned int                      Lose;
unsigned long long                NormalSpeedExpectedDurationTime;
bool				  InterlacedContentOnInterlacedDisplay;
bool				  PartialFrame;

    //
    // Get the playback speed and direction for reverse field inversion
    //

#if 0
report( severity_info, "NickOrd - PictureStructure = %d (%d %d %d - %d %d)\n", 
	ParsedVideoParameters->PictureStructure, 
	ParsedVideoParameters->Content.Progressive,
	ParsedVideoParameters->InterlacedFrame, ParsedVideoParameters->TopFieldFirst,
	ParsedVideoParameters->DisplayCount[0], ParsedVideoParameters->DisplayCount[1] );
#endif

    Player->GetPlaybackSpeed( Playback, &Speed, &Direction );

    //
    // Adjust data for a non-paired field
    //

    if( ParsedVideoParameters->PictureStructure == StructureEmpty )
    {
	report( severity_error, "OutputTimer_Video_c::FillOutFrameTimingRecord - Empty picture structure.\n" );
	VideoOutputTiming->DisplayCount[0]	= 0;
	VideoOutputTiming->DisplayCount[1]	= 0;
	return OutputTimerNoError;
    }

    PartialFrame	= ParsedVideoParameters->PictureStructure != StructureFrame;
    if( PartialFrame )
    {
	ParsedVideoParameters->Content.Progressive	= false;
	ParsedVideoParameters->InterlacedFrame		= true;
	ParsedVideoParameters->TopFieldFirst		= ParsedVideoParameters->PictureStructure == StructureTopField;

// Temporary set to 1 to pass test
	ParsedVideoParameters->DisplayCount[0]		= 1;	// Whole frame
	if( ParsedVideoParameters->PanScan.Count > 0 )
	{
	    ParsedVideoParameters->PanScan.Count		= 1;	// Whole frame
	    ParsedVideoParameters->PanScan.DisplayCount[0]	= 1;	// Whole frame
	}
    }

    //
    // Update the record of whether or not we have seen interlaced frames
    //

    InterlacedContentOnInterlacedDisplay		= ParsedVideoParameters->InterlacedFrame && 
							  !VideoOutputSurfaceDescriptor->Progressive;

    if( InterlacedContentOnInterlacedDisplay && !PartialFrame )
	SeenInterlacedContentOnInterlacedDisplay	= true;

    //
    // Validate the incoming frame rate, if it isn't reasonable 
    // then take the output rate as a reasonable stab at it.
    //

    FrameRate           = ParsedVideoParameters->Content.FrameRate;
    if( !inrange( FrameRate, 1, 120 ) )
    {
	report( severity_error, "OutputTimer_Video_c::FillOutFrameTimingRecord - Ridiculous frame rate %d.%06dfps.\n", 
				FrameRate.IntegerPart(), FrameRate.RemainderDecimal() );

	FrameRate       = VideoOutputSurfaceDescriptor->FrameRate;
	if( !ParsedVideoParameters->Content.Progressive )
	    FrameRate   = FrameRate / 2;
    }

    //
    // Check for and if appropriate remove 3:2 pulldown. 
    // We use a very simple 3:2 detection algorithm, 
    // if we ever see a repeated field, for a progressive frame,
    // but in a non-progressive sequence, we assume 3:2 pulldown 
    // is in operation.
    // This may need modifying later, but for now we go with it.
    //

    RemoveAnyThreeTwoPulldown           = inrange(FrameRate, 29, 31) &&
					  ((VideoOutputSurfaceDescriptor->FrameRate < 51) || (Speed != 1));

    if( !ThreeTwoPulldownDetected                       &&
	!ParsedVideoParameters->Content.Progressive     &&
	!ParsedVideoParameters->InterlacedFrame         &&
	(ParsedVideoParameters->DisplayCount[0] == 2) )
    {
	ThreeTwoPulldownDetected        = true;

	//
	// You are going to love this, because we are now entering 3:2 pulldown
	// the accumulated error value that we have is incorrect, on the previous 
	// frame (before entry) we will have added to the error a value of (Fo/Fi - 1)
	// where Fo and Fi are the output and input frame rates respectively. But 
	// we should have added (Fo/(4Fi/5) - 1), so now we need to correct the error
	// by adding 1/4(Fo/Fi).
	//
	// Here we fudge, in most cases we know that the error will currently equal 
	// the initial value (Fo - Fi)/Fi, in order to make the rational not expand 
	// the denominator, we multiply by 4/4 making 4(Fo-Fi)/4Fi when we add Fo/4Fi 
	// this means we have a common denominator
	//

	if( RemoveAnyThreeTwoPulldown )
	{
	    AccumulatedError    = AccumulatedError * Rational_t(4,4);
	    AccumulatedError    = AccumulatedError + (VideoOutputSurfaceDescriptor->FrameRate / ( 4 * FrameRate));
	}
    }
    else if( ParsedVideoParameters->DisplayCount[0] > 2 )
	ThreeTwoPulldownDetected        = false;

//

    if( ThreeTwoPulldownDetected && RemoveAnyThreeTwoPulldown )
    {
	FrameRate                       = (FrameRate * 4) / 5;                  // e.g. 29.970 -> 23.976
	if( ParsedVideoParameters->DisplayCount[0] == 2 )
	{
	    //
	    // Remove the extra field, and also adjust the system 
	    // time to remove the jitter added by 3:2 pulldown
	    //

	    ParsedVideoParameters->DisplayCount[0] = 1;

	    if( SystemTime != UNSPECIFIED_TIME ) 
	    {
		ThreeTwoJitter  = 200000 / FrameRate;
		SystemTime      = SystemTime + ThreeTwoJitter.RoundedLongLongIntegerPart();
	    }

	    //
	    // Do we also need to remove the 3:2 pulldown affects from the pan scan vectors
	    //

	    if( (ParsedVideoParameters->PanScan.Count == 3) && (ParsedVideoParameters->PanScan.DisplayCount[1] == 1) )
	    {
		ParsedVideoParameters->PanScan.DisplayCount[1]          = ParsedVideoParameters->PanScan.DisplayCount[2];
		ParsedVideoParameters->PanScan.HorizontalOffset[1]      = ParsedVideoParameters->PanScan.HorizontalOffset[2];
		ParsedVideoParameters->PanScan.VerticalOffset[1]        = ParsedVideoParameters->PanScan.VerticalOffset[2];
		ParsedVideoParameters->PanScan.Count                    = 2;
	    }
	    else if( ParsedVideoParameters->PanScan.Count != 0 )
		ParsedVideoParameters->PanScan.DisplayCount[0]--;
	}
    }

    //
    // Do we need to re-calculate the field/frame count multiplier
    //

    if( (AdjustedSpeedAfterFrameDrop != LastAdjustedSpeedAfterFrameDrop) ||
	(FrameRate != PreviousFrameRate) ||
	(VideoOutputSurfaceDescriptor->FrameRate != PreviousDisplayFrameRate) ||
	(ParsedVideoParameters->Content.Progressive != PreviousContentProgressive) )
    {
	//
	// Re-calculate frame rate conversion parameters,
	// NOTE we do not reset the accumulated error, we 
	// leave it accumulating, except when we transit to a mode 
	// where there is no error IE when the multiplier is 1.
	//

	CountMultiplier                 = VideoOutputSurfaceDescriptor->FrameRate / (AdjustedSpeedAfterFrameDrop * FrameRate );

	if( !ParsedVideoParameters->Content.Progressive )
	    CountMultiplier             = CountMultiplier / 2;

	if( CountMultiplier == 1 )
	    AccumulatedError            = 0;

//

	Tmp                             = 1000000 / VideoOutputSurfaceDescriptor->FrameRate;
	FrameDurationTime               = Tmp.LongLongIntegerPart();

	Tmp				= 1000000 / FrameRate;
	if( !ParsedVideoParameters->Content.Progressive )
	    Tmp				= Tmp / 2;
	SourceFrameDurationTime		= Tmp.LongLongIntegerPart();

	PreviousFrameRate               = FrameRate;
	PreviousDisplayFrameRate        = VideoOutputSurfaceDescriptor->FrameRate;
	PreviousContentProgressive	= ParsedVideoParameters->Content.Progressive;
	LastAdjustedSpeedAfterFrameDrop = AdjustedSpeedAfterFrameDrop;

#if 0
report( severity_info, "OutputTimer_Video_c::FillOutFrameTimingRecord - DisplayFrameRate %d.%06d, ContentFrameRate %d.%06d, CountMultiplier %d.%06d\n",
		PreviousDisplayFrameRate.IntegerPart(), PreviousDisplayFrameRate.RemainderDecimal(),
		PreviousFrameRate.IntegerPart(), PreviousFrameRate.RemainderDecimal(),
		CountMultiplier.IntegerPart(), CountMultiplier.RemainderDecimal() );
#endif
    }

    //
    // Jitter the output time by the accumulated error
    //

    if( SystemTime != UNSPECIFIED_TIME ) 
    {
	SystemTimeError                 = AccumulatedError * FrameDurationTime;
	if( InterlacedContentOnInterlacedDisplay && !PartialFrame )
	    SystemTimeError             = SystemTimeError * 2;

	SystemTime                      = SystemTime - SystemTimeError.LongLongIntegerPart();
    }

    //
    // Calculate the field counts
    //

    AccumulatedPanScanError             = AccumulatedError;

    FrameCount                          = (CountMultiplier * ParsedVideoParameters->DisplayCount[0]) + AccumulatedError;
    VideoOutputTiming->DisplayCount[0]  = FrameCount.IntegerPart();
    AccumulatedError                    = FrameCount.Remainder();

    if( PartialFrame )
    {
	VideoOutputTiming->DisplayCount[0]      = 1;
	VideoOutputTiming->DisplayCount[1]      = 0;
    }
    else if( InterlacedContentOnInterlacedDisplay )
    {
	if( ParsedVideoParameters->DisplayCount[0] != ParsedVideoParameters->DisplayCount[1] )
	    report( severity_error, "OutputTimer_Video_c::FillOutFrameTimingRecord - Interlaced content expected identical field counts (%d - %d)\n", ParsedVideoParameters->DisplayCount[0], ParsedVideoParameters->DisplayCount[1] );

	VideoOutputTiming->DisplayCount[1]      = VideoOutputTiming->DisplayCount[0];
    }
    else if( !ParsedVideoParameters->Content.Progressive )
    {
	FrameCount                              = (CountMultiplier * ParsedVideoParameters->DisplayCount[1]) + AccumulatedError;
	VideoOutputTiming->DisplayCount[1]      = FrameCount.IntegerPart();
	AccumulatedError                        = FrameCount.Remainder();
    }
    else
	VideoOutputTiming->DisplayCount[1]      = 0;

    //
    // Calculate the panscan field counts
    //

    memcpy( &VideoOutputTiming->PanScan, &ParsedVideoParameters->PanScan, sizeof(PanScan_t) );

    if( InterlacedContentOnInterlacedDisplay && (VideoOutputTiming->PanScan.Count != 0) )
    {
	if( VideoOutputTiming->PanScan.Count == 1 )
	{
	    VideoOutputTiming->PanScan.DisplayCount[0]  = VideoOutputTiming->DisplayCount[0] + VideoOutputTiming->DisplayCount[1];
	}
	else if( VideoOutputTiming->PanScan.Count == 2 )
	{
	    VideoOutputTiming->PanScan.DisplayCount[0]  = VideoOutputTiming->DisplayCount[0];
	    VideoOutputTiming->PanScan.DisplayCount[1]  = VideoOutputTiming->DisplayCount[1];
	}
	else
	{
	    report( severity_error, "OutputTimer_Video_c::FillOutFrameTimingRecord - Interlaced content 0,1 or 2 pan scan values (%d)\n", VideoOutputTiming->PanScan.Count );
	}
    }
    else
    {
	for( i=0; i<VideoOutputTiming->PanScan.Count; i++ )
	{
	    FrameCount                                  = (CountMultiplier * VideoOutputTiming->PanScan.DisplayCount[i]) + AccumulatedPanScanError;
	    VideoOutputTiming->PanScan.DisplayCount[i]  = FrameCount.IntegerPart();
	    AccumulatedPanScanError                     = FrameCount.Remainder();
	}
    }

//

{
// Some nick debug

unsigned int Count;

    if( VideoOutputTiming->PanScan.Count != 0 )
    {
	Count            = VideoOutputTiming->DisplayCount[0];
	if( !ParsedVideoParameters->Content.Progressive )
	    Count       += VideoOutputTiming->DisplayCount[1]; 

	for( i=0; i<VideoOutputTiming->PanScan.Count; i++ )
	    Count       -= VideoOutputTiming->PanScan.DisplayCount[i];

	if( Count != 0 )
	{
	    report( severity_error, "OutputTimer_Video_c::FillOutFrameTimingRecord - Pan scan counts and display counts do not match.\n" );
	    report( severity_info,  "                                                Display  %2d %2d    => %2d %2d\n", ParsedVideoParameters->DisplayCount[0], ParsedVideoParameters->DisplayCount[1], VideoOutputTiming->DisplayCount[0], VideoOutputTiming->DisplayCount[1] );
	    report( severity_info,  "                                                Pan Scan %2d %2d %2d => %2d %2d %2d\n", 
		ParsedVideoParameters->PanScan.DisplayCount[0], ParsedVideoParameters->PanScan.DisplayCount[1], ParsedVideoParameters->PanScan.DisplayCount[2],
		VideoOutputTiming->PanScan.DisplayCount[0], VideoOutputTiming->PanScan.DisplayCount[1], VideoOutputTiming->PanScan.DisplayCount[2] );
	}
    }
}

    //
    // Are we in the process of performing an avsync correction
    //

    if( (LoseFramesForSynchronization != 0) && ((VideoOutputTiming->DisplayCount[0] + VideoOutputTiming->DisplayCount[1]) != 0) )
    {
	if( InterlacedContentOnInterlacedDisplay && !PartialFrame )
	{
	    Lose        = min( LoseFramesForSynchronization/2, VideoOutputTiming->DisplayCount[0] );

	    LoseFramesForSynchronization        -= 2 * Lose;
	    VideoOutputTiming->DisplayCount[0]  -= Lose;
	    VideoOutputTiming->DisplayCount[1]  -= Lose;
	}
	else
	{
	    Lose        = min( LoseFramesForSynchronization, VideoOutputTiming->DisplayCount[0] );

	    LoseFramesForSynchronization        -= Lose;
	    VideoOutputTiming->DisplayCount[0]  -= Lose;

	    Lose        = min( LoseFramesForSynchronization, VideoOutputTiming->DisplayCount[1] );

	    LoseFramesForSynchronization        -= Lose;
	    VideoOutputTiming->DisplayCount[1]  -= Lose;
	}
    }

    //
    // After all of that wondeful calculation, we now 
    // override the lot of it if we are single stepping.
    //

    if( Speed == 0 )
    {
	VideoOutputTiming->DisplayCount[0]	= 1;
	if( !ParsedVideoParameters->Content.Progressive )
	    VideoOutputTiming->DisplayCount[1]	= 1;
    }

    //
    // Fill in the other timing parameters, adjusting the decode time (for decode window porch control).
    //

    VideoOutputTiming->SystemPlaybackTime       = SystemTime;
    VideoOutputTiming->ExpectedDurationTime     = FrameDurationTime * (VideoOutputTiming->DisplayCount[0] + VideoOutputTiming->DisplayCount[1]);
    VideoOutputTiming->ActualSystemPlaybackTime = INVALID_TIME;
    VideoOutputTiming->Interlaced               = ParsedVideoParameters->InterlacedFrame;
    VideoOutputTiming->TopFieldFirst            = ParsedVideoParameters->TopFieldFirst;

    //
    // Apply an update to the frame decode time, 
    // based on the native decode duration, 
    // rather than the adjusted for speed decode duration
    //

    NormalSpeedExpectedDurationTime             = SourceFrameDurationTime * (ParsedVideoParameters->DisplayCount[0] + ParsedVideoParameters->DisplayCount[1]);
    Configuration.FrameDecodeTime               = max( NormalSpeedExpectedDurationTime, Configuration.FrameDecodeTime);

    //
    // Apply an update to the next expected playback time, used to spot PTS jumps
    //

    PlaybackTimeIncrement			= NormalSpeedExpectedDurationTime;

    //
    // If we are running in reverse and an interlaced frame, then reverse the fields
    // This also involves reversing the display counts, and the pan scan arrays.
    //

    if( !ParsedVideoParameters->Content.Progressive &&
	Direction == PlayBackward )
    {
	unsigned int    Tmp;
	int             Tmpi;
	unsigned int    Cnt;

	VideoOutputTiming->TopFieldFirst        = !VideoOutputTiming->TopFieldFirst;

	Tmp                                     = VideoOutputTiming->DisplayCount[0];
	VideoOutputTiming->DisplayCount[0]      = VideoOutputTiming->DisplayCount[1];
	VideoOutputTiming->DisplayCount[1]      = Tmp;

	Cnt                                     = VideoOutputTiming->PanScan.Count;
	for( i=0; i<Cnt/2; i++ )
	{
	    Tmp                                                         = VideoOutputTiming->PanScan.DisplayCount[i];
	    VideoOutputTiming->PanScan.DisplayCount[i]                  = VideoOutputTiming->PanScan.DisplayCount[Cnt - 1 - i];
	    VideoOutputTiming->PanScan.DisplayCount[Cnt - 1 - i]        = Tmp;
	    Tmpi                                                        = VideoOutputTiming->PanScan.HorizontalOffset[i];
	    VideoOutputTiming->PanScan.HorizontalOffset[i]              = VideoOutputTiming->PanScan.HorizontalOffset[Cnt - 1 - i];
	    VideoOutputTiming->PanScan.HorizontalOffset[Cnt - 1 - i]    = Tmpi;
	    Tmpi                                                        = VideoOutputTiming->PanScan.VerticalOffset[i];
	    VideoOutputTiming->PanScan.VerticalOffset[i]                = VideoOutputTiming->PanScan.VerticalOffset[Cnt - 1 - i];
	    VideoOutputTiming->PanScan.VerticalOffset[Cnt - 1 - i]      = Tmpi;
	}
    }

    //
    // Finally, if we have a zero first field count we move the second field count up,
    // and invert the top field first flag. Also we shuffle up and zero pan scan field counts.
    // NOTE the shuffle uses while, it makes the code simple, and if it passes through twice, 
    // I will be a monkeys uncle.
    //

    if( VideoOutputTiming->DisplayCount[0] == 0 )
    {
	VideoOutputTiming->DisplayCount[0]      = VideoOutputTiming->DisplayCount[1];
	VideoOutputTiming->DisplayCount[1]      = 0;
	VideoOutputTiming->TopFieldFirst        = !VideoOutputTiming->TopFieldFirst;
    }

    while( (VideoOutputTiming->PanScan.Count != 0) && (VideoOutputTiming->PanScan.DisplayCount[0] == 0) )
    {
	VideoOutputTiming->PanScan.Count--;
	for( i=0; i<VideoOutputTiming->PanScan.Count; i++ )
	{
	    VideoOutputTiming->PanScan.DisplayCount[i]                  = VideoOutputTiming->PanScan.DisplayCount[i+1];
	    VideoOutputTiming->PanScan.HorizontalOffset[i]              = VideoOutputTiming->PanScan.HorizontalOffset[i+1];
	    VideoOutputTiming->PanScan.VerticalOffset[i]                = VideoOutputTiming->PanScan.VerticalOffset[i+1];
	}
    }

//

#if 0
{
static unsigned long long LastSystemTime        = INVALID_TIME;

    if( LastSystemTime == INVALID_TIME )
	LastSystemTime  = SystemTime;

    report( severity_info, "Timing Out  %12lld - Error was %12lld\n", SystemTime - LastSystemTime, SystemTimeError.LongLongIntegerPart() );
    report( severity_info, "Display     %d (3:2 %d %d) (%d %d) - %2d %2d    => %2d %2d\n", ParsedVideoParameters->PictureStructure, ThreeTwoPulldownDetected, RemoveAnyThreeTwoPulldown, ParsedVideoParameters->InterlacedFrame, ParsedVideoParameters->TopFieldFirst, ParsedVideoParameters->DisplayCount[0], ParsedVideoParameters->DisplayCount[1], VideoOutputTiming->DisplayCount[0], VideoOutputTiming->DisplayCount[1] );

    LastSystemTime  = SystemTime;
}
#endif
    return OutputTimerNoError;
}