//}}} //{{{ 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; }