void RegulateQ( CP_INSTANCE *cpi, ogg_int32_t UpdateScore ) { double PredUnitScoreBytes; ogg_uint32_t QIndex = Q_TABLE_SIZE - 1; ogg_uint32_t i; if ( UpdateScore > 0 ) { double TargetUnitScoreBytes = (double)cpi->ThisFrameTargetBytes / (double)UpdateScore; double LastBitError = 10000.0; /* Silly high number */ /* Search for the best Q for the target bitrate. */ for ( i = 0; i < Q_TABLE_SIZE; i++ ) { PredUnitScoreBytes = GetEstimatedBpb( cpi, cpi->pb.QThreshTable[i] ); if ( PredUnitScoreBytes > TargetUnitScoreBytes ) { if ( (PredUnitScoreBytes - TargetUnitScoreBytes) <= LastBitError ) { QIndex = i; } else { QIndex = i - 1; } break; } else { LastBitError = TargetUnitScoreBytes - PredUnitScoreBytes; } } } /* QIndex should now indicate the optimal Q. */ cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[QIndex]; /* Apply range restrictions for key frames. */ if ( cpi->pb.FrameType == KEY_FRAME ) { if ( cpi->pb.ThisFrameQualityValue > cpi->pb.QThreshTable[20] ) cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[20]; else if ( cpi->pb.ThisFrameQualityValue < cpi->pb.QThreshTable[50] ) cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[50]; } /* Limit the Q value to the maximum available value */ if (cpi->pb.ThisFrameQualityValue > cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]) { cpi->pb.ThisFrameQualityValue = (ogg_uint32_t)cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]; } if(cpi->FixedQ) { if ( cpi->pb.FrameType == KEY_FRAME ) { cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[43]; cpi->pb.ThisFrameQualityValue = cpi->FixedQ; } else { cpi->pb.ThisFrameQualityValue = cpi->FixedQ; } } /* If the quantizer value has changed then re-initialise it */ if ( cpi->pb.ThisFrameQualityValue != cpi->pb.LastFrameQualityValue ) { /* Initialise quality tables. */ UpdateQC( cpi, cpi->pb.ThisFrameQualityValue ); cpi->pb.LastFrameQualityValue = cpi->pb.ThisFrameQualityValue; } }
static void CompressFrame( CP_INSTANCE *cpi) { ogg_int32_t min_blocks_per_frame; ogg_uint32_t i; int DropFrame = 0; ogg_uint32_t ResidueBlocksAdded=0; ogg_uint32_t KFIndicator = 0; double QModStep; double QModifier = 1.0; /* Clear down the macro block level mode and MV arrays. */ for ( i = 0; i < cpi->pb.UnitFragments; i++ ) { cpi->pb.FragCodingMethod[i] = CODE_INTER_NO_MV; /* Default coding mode */ cpi->pb.FragMVect[i].x = 0; cpi->pb.FragMVect[i].y = 0; } /* Default to delta frames. */ SetFrameType( &cpi->pb, DELTA_FRAME ); /* Clear down the difference arrays for the current frame. */ memset( cpi->pb.display_fragments, 0, cpi->pb.UnitFragments ); memset( cpi->extra_fragments, 0, cpi->pb.UnitFragments ); /* Calculate the target bytes for this frame. */ cpi->ThisFrameTargetBytes = cpi->frame_target_rate; /* Correct target to try and compensate for any overall rate error that is developing */ /* Set the max allowed Q for this frame based upon carry over history. First set baseline worst Q for this frame */ cpi->Configuration.ActiveMaxQ = cpi->Configuration.MaxQ + 10; if ( cpi->Configuration.ActiveMaxQ >= Q_TABLE_SIZE ) cpi->Configuration.ActiveMaxQ = Q_TABLE_SIZE - 1; /* Make a further adjustment based upon the carry over and recent history.. cpi->Configuration.ActiveMaxQ reduced by 1 for each 1/2 seconds worth of -ve carry over up to a limit of 6. Also cpi->Configuration.ActiveMaxQ reduced if frame is a "DropFrameCandidate". Remember that if we are behind the bit target carry over is -ve. */ if ( cpi->CarryOver < 0 ) { if ( cpi->DropFrameCandidate ) { cpi->Configuration.ActiveMaxQ -= 4; } if ( cpi->CarryOver < -((ogg_int32_t)cpi->Configuration.TargetBandwidth*3) ) cpi->Configuration.ActiveMaxQ -= 6; else cpi->Configuration.ActiveMaxQ += (ogg_int32_t) ((cpi->CarryOver*2) / (ogg_int32_t)cpi->Configuration.TargetBandwidth); /* Check that we have not dropped quality too far */ if ( cpi->Configuration.ActiveMaxQ < cpi->Configuration.MaxQ ) cpi->Configuration.ActiveMaxQ = cpi->Configuration.MaxQ; } /* Calculate the Q Modifier step size required to cause a step down from full target bandwidth to 40% of target between max Q and best Q */ QModStep = 0.5 / (double)((Q_TABLE_SIZE - 1) - cpi->Configuration.ActiveMaxQ); /* Set up the cpi->QTargetModifier[] table. */ for ( i = 0; i < cpi->Configuration.ActiveMaxQ; i++ ) { cpi->QTargetModifier[i] = QModifier; } for ( i = cpi->Configuration.ActiveMaxQ; i < Q_TABLE_SIZE; i++ ) { cpi->QTargetModifier[i] = QModifier; QModifier -= QModStep; } /* if we are allowed to drop frames and are falling behind (eg more than x frames worth of bandwidth) */ if ( cpi->pb.info.dropframes_p && ( cpi->DropCount < cpi->MaxConsDroppedFrames) && ( cpi->CarryOver < -((ogg_int32_t)cpi->Configuration.TargetBandwidth)) && ( cpi->DropFrameCandidate) ) { /* (we didn't do this frame so we should have some left over for the next frame) */ cpi->CarryOver += cpi->frame_target_rate; DropFrame = 1; cpi->DropCount ++; /* Adjust DropFrameTriggerBytes to account for the saving achieved. */ cpi->DropFrameTriggerBytes = (cpi->DropFrameTriggerBytes * (DF_CANDIDATE_WINDOW-1))/DF_CANDIDATE_WINDOW; /* Even if we drop a frame we should account for it when considering key frame seperation. */ cpi->LastKeyFrame++; } else if ( cpi->CarryOver < -((ogg_int32_t)cpi->Configuration.TargetBandwidth * 2) ) { /* Reduce frame bit target by 1.75% for each 1/10th of a seconds worth of -ve carry over down to a minimum of 65% of its un-modified value. */ cpi->ThisFrameTargetBytes = (ogg_uint32_t)(cpi->ThisFrameTargetBytes * 0.65); } else if ( cpi->CarryOver < 0 ) { /* Note that cpi->CarryOver is a -ve here hence 1.0 "+" ... */ cpi->ThisFrameTargetBytes = (ogg_uint32_t)(cpi->ThisFrameTargetBytes * (1.0 + ( ((cpi->CarryOver * 10)/ ((ogg_int32_t)cpi-> Configuration.TargetBandwidth)) * 0.0175) )); } if ( !DropFrame ) { /* pick all the macroblock modes and motion vectors */ ogg_uint32_t InterError; ogg_uint32_t IntraError; /* Set Baseline filter level. */ ConfigurePP( &cpi->pp, cpi->pb.info.noise_sensitivity); /* Score / analyses the fragments. */ cpi->MotionScore = YUVAnalyseFrame(&cpi->pp, &KFIndicator ); /* Get the baseline Q value */ RegulateQ( cpi, cpi->MotionScore ); /* Recode blocks if the error score in last frame was high. */ ResidueBlocksAdded = 0; for ( i = 0; i < cpi->pb.UnitFragments; i++ ){ if ( !cpi->pb.display_fragments[i] ){ if ( cpi->LastCodedErrorScore[i] >= ResidueErrorThresh[cpi->pb.FrameQIndex] ) { cpi->pb.display_fragments[i] = 1; /* Force block update */ cpi->extra_fragments[i] = 1; /* Insures up to date pixel data is used. */ ResidueBlocksAdded ++; } } } /* Adjust the motion score to allow for residue blocks added. These are assumed to have below average impact on bitrate (Hence ResidueBlockFactor). */ cpi->MotionScore = cpi->MotionScore + (ResidueBlocksAdded / ResidueBlockFactor[cpi->pb.FrameQIndex]); /* Estimate the min number of blocks at best Q */ min_blocks_per_frame = (ogg_int32_t)(cpi->ThisFrameTargetBytes / GetEstimatedBpb( cpi, VERY_BEST_Q )); if ( min_blocks_per_frame == 0 ) min_blocks_per_frame = 1; /* If we have less than this number then consider adding in some extra blocks */ if ( cpi->MotionScore < min_blocks_per_frame ) { min_blocks_per_frame = cpi->MotionScore + (ogg_int32_t)(((min_blocks_per_frame - cpi->MotionScore) * 4) / 3 ); UpRegulateDataStream( cpi, VERY_BEST_Q, min_blocks_per_frame ); }else{ /* Reset control variable for best quality final pass. */ cpi->FinalPassLastPos = 0; } /* Get the modified Q prediction taking into account extra blocks added. */ RegulateQ( cpi, cpi->MotionScore ); /* Unless we are already well ahead (4 seconds of data) of the projected bitrate */ if ( cpi->CarryOver < (ogg_int32_t)(cpi->Configuration.TargetBandwidth * 4) ){ /* Look at the predicted Q (pbi->FrameQIndex). Adjust the target bits for this frame based upon projected Q and re-calculate. The idea is that if the Q is better than a given (good enough) level then we will try and save some bits for use in more difficult segments. */ cpi->ThisFrameTargetBytes = (ogg_int32_t) (cpi->ThisFrameTargetBytes * cpi->QTargetModifier[cpi->pb.FrameQIndex]); /* Recalculate Q again */ RegulateQ( cpi, cpi->MotionScore ); } /* Select modes and motion vectors for each of the blocks : return an error score for inter and intra */ PickModes( cpi, cpi->pb.YSBRows, cpi->pb.YSBCols, cpi->pb.info.width, &InterError, &IntraError ); /* decide whether we really should have made this frame a key frame */ /* forcing out a keyframe if the max interval is up is done at a higher level */ if( cpi->pb.info.keyframe_auto_p){ if( ( 2* IntraError < 5 * InterError ) && ( KFIndicator >= (ogg_uint32_t) cpi->pb.info.keyframe_auto_threshold) && ( cpi->LastKeyFrame > cpi->pb.info.keyframe_mindistance) ){ CompressKeyFrame(cpi); /* Code a key frame */ return; } } /* Increment the frames since last key frame count */ cpi->LastKeyFrame++; /* Proceed with the frame update. */ UpdateFrame(cpi); cpi->DropCount = 0; if ( cpi->MotionScore > 0 ){ /* Note the Quantizer used for each block coded. */ for ( i = 0; i < cpi->pb.UnitFragments; i++ ){ if ( cpi->pb.display_fragments[i] ){ cpi->FragmentLastQ[i] = cpi->pb.ThisFrameQualityValue; } } } }else{ /* even if we 'drop' a frame, a placeholder must be written as we currently assume fixed frame rate timebase as Ogg mapping invariant */ UpdateFrame(cpi); } }
/**************************************************************************** * * ROUTINE : RegulateQ * * INPUTS : INT32 BlocksToUpdate * * OUTPUTS : None. * * RETURNS : None. * * FUNCTION : If appropriate this function regulates the DCT * coefficients to match the stream size to the * available bandwidth (within defined limits). * * SPECIAL NOTES : None. * * * ERRORS : None. * ****************************************************************************/ void RegulateQ( CP_INSTANCE *cpi, INT32 UpdateScore ) { double TargetUnitScoreBytes = (double)cpi->ThisFrameTargetBytes / (double)UpdateScore; double PredUnitScoreBytes; double LastBitError = 10000.0; // Silly high number UINT32 QIndex = Q_TABLE_SIZE - 1; UINT32 i; // Search for the best Q for the target bitrate. for ( i = 0; i < Q_TABLE_SIZE; i++ ) { PredUnitScoreBytes = GetEstimatedBpb( cpi, cpi->pb.QThreshTable[i] ); if ( PredUnitScoreBytes > TargetUnitScoreBytes ) { if ( (PredUnitScoreBytes - TargetUnitScoreBytes) <= LastBitError ) { QIndex = i; } else { QIndex = i - 1; } break; } else { LastBitError = TargetUnitScoreBytes - PredUnitScoreBytes; } } // QIndex should now indicate the optimal Q. cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[QIndex]; // Apply range restrictions for key frames. if ( GetFrameType(&cpi->pb) == BASE_FRAME ) { if ( cpi->pb.ThisFrameQualityValue > cpi->pb.QThreshTable[20] ) cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[20]; else if ( cpi->pb.ThisFrameQualityValue < cpi->pb.QThreshTable[50] ) cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[50]; } // Limit the Q value to the maximum available value if (cpi->pb.ThisFrameQualityValue > cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]) //if (cpi->pb.ThisFrameQualityValue > QThreshTable[cpi->Configuration.ActiveMaxQ]) { cpi->pb.ThisFrameQualityValue = (UINT32)cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]; } if(cpi->FixedQ) { if ( GetFrameType(&cpi->pb) == BASE_FRAME ) { cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[43]; cpi->pb.ThisFrameQualityValue = cpi->FixedQ; } else { cpi->pb.ThisFrameQualityValue = cpi->FixedQ; } } // If th quantiser value has changed then re-initialise it if ( cpi->pb.ThisFrameQualityValue != cpi->pb.LastFrameQualityValue ) { /* Initialise quality tables. */ UpdateQC( cpi, cpi->pb.ThisFrameQualityValue ); cpi->pb.LastFrameQualityValue = cpi->pb.ThisFrameQualityValue; } }