📄 encoder_toplevel.c
字号:
/********************************************************************
* *
* THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2003 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function:
********************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "toplevel_lookup.h"
#include "toplevel.h"
#define A_TABLE_SIZE 29
#define DF_CANDIDATE_WINDOW 5
static void EClearFragmentInfo(CP_INSTANCE * cpi){
if(cpi->extra_fragments)
_ogg_free(cpi->extra_fragments);
if(cpi->FragmentLastQ)
_ogg_free(cpi->FragmentLastQ);
if(cpi->FragTokens)
_ogg_free(cpi->FragTokens);
if(cpi->FragTokenCounts)
_ogg_free(cpi->FragTokenCounts);
if(cpi->RunHuffIndices)
_ogg_free(cpi->RunHuffIndices);
if(cpi->LastCodedErrorScore)
_ogg_free(cpi->LastCodedErrorScore);
if(cpi->ModeList)
_ogg_free(cpi->ModeList);
if(cpi->MVList)
_ogg_free(cpi->MVList);
if(cpi->DCT_codes )
_ogg_free( cpi->DCT_codes );
if(cpi->DCTDataBuffer )
_ogg_free( cpi->DCTDataBuffer);
if(cpi->quantized_list)
_ogg_free( cpi->quantized_list);
if(cpi->OriginalDC)
_ogg_free( cpi->OriginalDC);
if(cpi->PartiallyCodedFlags)
_ogg_free(cpi->PartiallyCodedFlags);
if(cpi->PartiallyCodedMbPatterns)
_ogg_free(cpi->PartiallyCodedMbPatterns);
if(cpi->UncodedMbFlags)
_ogg_free(cpi->UncodedMbFlags);
if(cpi->BlockCodedFlags)
_ogg_free(cpi->BlockCodedFlags);
cpi->extra_fragments = 0;
cpi->FragmentLastQ = 0;
cpi->FragTokens = 0;
cpi->FragTokenCounts = 0;
cpi->RunHuffIndices = 0;
cpi->LastCodedErrorScore = 0;
cpi->ModeList = 0;
cpi->MVList = 0;
cpi->DCT_codes = 0;
cpi->DCTDataBuffer = 0;
cpi->quantized_list = 0;
cpi->OriginalDC = 0;
cpi->BlockCodedFlags = 0;
}
static void EInitFragmentInfo(CP_INSTANCE * cpi){
/* clear any existing info */
EClearFragmentInfo(cpi);
/* Perform Fragment Allocations */
cpi->extra_fragments =
_ogg_malloc(cpi->pb.UnitFragments*sizeof(unsigned char));
/* A note to people reading and wondering why malloc returns aren't
checked:
lines like the following that implement a general strategy of
'check the return of malloc; a zero pointer means we're out of
memory!'...:
if(!cpi->extra_fragments) { EDeleteFragmentInfo(cpi); return FALSE; }
...are not useful. It's true that many platforms follow this
malloc behavior, but many do not. The more modern malloc
strategy is only to allocate virtual pages, which are not mapped
until the memory on that page is touched. At *that* point, if
the machine is out of heap, the page fails to be mapped and a
SEGV is generated.
That means that if we want to deal with out of memory conditions,
we *must* be prepared to process a SEGV. If we implement the
SEGV handler, there's no reason to to check malloc return; it is
a waste of code. */
cpi->FragmentLastQ =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(*cpi->FragmentLastQ));
cpi->FragTokens =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(*cpi->FragTokens));
cpi->OriginalDC =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(*cpi->OriginalDC));
cpi->FragTokenCounts =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(*cpi->FragTokenCounts));
cpi->RunHuffIndices =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(*cpi->RunHuffIndices));
cpi->LastCodedErrorScore =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(*cpi->LastCodedErrorScore));
cpi->BlockCodedFlags =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(*cpi->BlockCodedFlags));
cpi->ModeList =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(*cpi->ModeList));
cpi->MVList =
_ogg_malloc(cpi->pb.UnitFragments*
sizeof(cpi->MVList));
cpi->DCT_codes =
_ogg_malloc(64*
sizeof(*cpi->DCT_codes));
cpi->DCTDataBuffer =
_ogg_malloc(64*
sizeof(*cpi->DCTDataBuffer));
cpi->quantized_list =
_ogg_malloc(64*
sizeof(*cpi->quantized_list));
cpi->PartiallyCodedFlags =
_ogg_malloc(cpi->pb.MacroBlocks*
sizeof(*cpi->PartiallyCodedFlags));
cpi->PartiallyCodedMbPatterns =
_ogg_malloc(cpi->pb.MacroBlocks*
sizeof(*cpi->PartiallyCodedMbPatterns));
cpi->UncodedMbFlags =
_ogg_malloc(cpi->pb.MacroBlocks*
sizeof(*cpi->UncodedMbFlags));
}
static void EClearFrameInfo(CP_INSTANCE * cpi) {
if(cpi->ConvDestBuffer )
_ogg_free(cpi->ConvDestBuffer );
cpi->ConvDestBuffer = 0;
if(cpi->yuv0ptr)
_ogg_free(cpi->yuv0ptr);
cpi->yuv0ptr = 0;
if(cpi->yuv1ptr)
_ogg_free(cpi->yuv1ptr);
cpi->yuv1ptr = 0;
if(cpi->OptimisedTokenListEb )
_ogg_free(cpi->OptimisedTokenListEb);
cpi->OptimisedTokenListEb = 0;
if(cpi->OptimisedTokenList )
_ogg_free(cpi->OptimisedTokenList);
cpi->OptimisedTokenList = 0;
if(cpi->OptimisedTokenListHi )
_ogg_free(cpi->OptimisedTokenListHi);
cpi->OptimisedTokenListHi = 0;
if(cpi->OptimisedTokenListPl )
_ogg_free(cpi->OptimisedTokenListPl);
cpi->OptimisedTokenListPl = 0;
}
static void EInitFrameInfo(CP_INSTANCE * cpi){
int FrameSize = cpi->pb.ReconYPlaneSize + 2 * cpi->pb.ReconUVPlaneSize;
/* clear any existing info */
EClearFrameInfo(cpi);
/* allocate frames */
cpi->ConvDestBuffer =
_ogg_malloc(FrameSize*
sizeof(*cpi->ConvDestBuffer));
cpi->yuv0ptr =
_ogg_malloc(FrameSize*
sizeof(*cpi->yuv0ptr));
cpi->yuv1ptr =
_ogg_malloc(FrameSize*
sizeof(*cpi->yuv1ptr));
cpi->OptimisedTokenListEb =
_ogg_malloc(FrameSize*
sizeof(*cpi->OptimisedTokenListEb));
cpi->OptimisedTokenList =
_ogg_malloc(FrameSize*
sizeof(*cpi->OptimisedTokenList));
cpi->OptimisedTokenListHi =
_ogg_malloc(FrameSize*
sizeof(*cpi->OptimisedTokenListHi));
cpi->OptimisedTokenListPl =
_ogg_malloc(FrameSize*
sizeof(*cpi->OptimisedTokenListPl));
}
static void SetupKeyFrame(CP_INSTANCE *cpi) {
/* Make sure the "last frame" buffer contains the first frame data
as well. */
memcpy ( cpi->yuv0ptr, cpi->yuv1ptr,
cpi->pb.ReconYPlaneSize + 2 * cpi->pb.ReconUVPlaneSize );
/* Initialise the cpi->pb.display_fragments and other fragment
structures for the first frame. */
memset( cpi->pb.display_fragments, 1, cpi->pb.UnitFragments );
memset( cpi->extra_fragments, 1, cpi->pb.UnitFragments );
/* Set up for a KEY FRAME */
SetFrameType( &cpi->pb,KEY_FRAME );
}
static void AdjustKeyFrameContext(CP_INSTANCE *cpi) {
ogg_uint32_t i;
ogg_uint32_t AvKeyFrameFrequency =
(ogg_uint32_t) (cpi->CurrentFrame / cpi->KeyFrameCount);
ogg_uint32_t AvKeyFrameBytes =
(ogg_uint32_t) (cpi->TotKeyFrameBytes / cpi->KeyFrameCount);
ogg_uint32_t TotalWeight=0;
ogg_int32_t AvKeyFramesPerSecond;
ogg_int32_t MinFrameTargetRate;
/* Update the frame carry over. */
cpi->TotKeyFrameBytes += oggpackB_bytes(cpi->oggbuffer);
/* reset keyframe context and calculate weighted average of last
KEY_FRAME_CONTEXT keyframes */
for( i = 0 ; i < KEY_FRAME_CONTEXT ; i ++ ) {
if ( i < KEY_FRAME_CONTEXT -1) {
cpi->PriorKeyFrameSize[i] = cpi->PriorKeyFrameSize[i+1];
cpi->PriorKeyFrameDistance[i] = cpi->PriorKeyFrameDistance[i+1];
} else {
cpi->PriorKeyFrameSize[KEY_FRAME_CONTEXT - 1] =
oggpackB_bytes(cpi->oggbuffer);
cpi->PriorKeyFrameDistance[KEY_FRAME_CONTEXT - 1] =
cpi->LastKeyFrame;
}
AvKeyFrameBytes += PriorKeyFrameWeight[i] *
cpi->PriorKeyFrameSize[i];
AvKeyFrameFrequency += PriorKeyFrameWeight[i] *
cpi->PriorKeyFrameDistance[i];
TotalWeight += PriorKeyFrameWeight[i];
}
AvKeyFrameBytes /= TotalWeight;
AvKeyFrameFrequency /= TotalWeight;
AvKeyFramesPerSecond = 100 * cpi->Configuration.OutputFrameRate /
AvKeyFrameFrequency ;
/* Calculate a new target rate per frame allowing for average key
frame frequency over newest frames . */
if ( 100 * cpi->Configuration.TargetBandwidth >
AvKeyFrameBytes * AvKeyFramesPerSecond &&
(100 * cpi->Configuration.OutputFrameRate - AvKeyFramesPerSecond )){
cpi->frame_target_rate =
(ogg_int32_t)(100* cpi->Configuration.TargetBandwidth -
AvKeyFrameBytes * AvKeyFramesPerSecond ) /
( (100 * cpi->Configuration.OutputFrameRate - AvKeyFramesPerSecond ) );
} else {
/* don't let this number get too small!!! */
cpi->frame_target_rate = 1;
}
/* minimum allowable frame_target_rate */
MinFrameTargetRate = (cpi->Configuration.TargetBandwidth /
cpi->Configuration.OutputFrameRate) / 3;
if(cpi->frame_target_rate < MinFrameTargetRate ) {
cpi->frame_target_rate = MinFrameTargetRate;
}
cpi->LastKeyFrame = 1;
cpi->LastKeyFrameSize=oggpackB_bytes(cpi->oggbuffer);
}
static void UpdateFrame(CP_INSTANCE *cpi){
double CorrectionFactor;
/* Reset the DC predictors. */
cpi->pb.LastIntraDC = 0;
cpi->pb.InvLastIntraDC = 0;
cpi->pb.LastInterDC = 0;
cpi->pb.InvLastInterDC = 0;
/* Initialise bit packing mechanism. */
#ifndef LIBOGG2
oggpackB_reset(cpi->oggbuffer);
#else
oggpackB_writeinit(cpi->oggbuffer, cpi->oggbufferstate);
#endif
/* mark as video frame */
oggpackB_write(cpi->oggbuffer,0,1);
/* Write out the frame header information including size. */
WriteFrameHeader(cpi);
/* Copy back any extra frags that are to be updated by the codec
as part of the background cleanup task */
CopyBackExtraFrags(cpi);
/* Encode the data. */
EncodeData(cpi);
/* Adjust drop frame trigger. */
if ( GetFrameType(&cpi->pb) != KEY_FRAME ) {
/* Apply decay factor then add in the last frame size. */
cpi->DropFrameTriggerBytes =
((cpi->DropFrameTriggerBytes * (DF_CANDIDATE_WINDOW-1)) /
DF_CANDIDATE_WINDOW) + oggpackB_bytes(cpi->oggbuffer);
}else{
/* Increase cpi->DropFrameTriggerBytes a little. Just after a key
frame may actually be a good time to drop a frame. */
cpi->DropFrameTriggerBytes =
(cpi->DropFrameTriggerBytes * DF_CANDIDATE_WINDOW) /
(DF_CANDIDATE_WINDOW-1);
}
/* Test for overshoot which may require a dropped frame next time
around. If we are already in a drop frame condition but the
previous frame was not dropped then the threshold for continuing
to allow dropped frames is reduced. */
if ( cpi->DropFrameCandidate ) {
if ( cpi->DropFrameTriggerBytes >
(cpi->frame_target_rate * (DF_CANDIDATE_WINDOW+1)) )
cpi->DropFrameCandidate = 1;
else
cpi->DropFrameCandidate = 0;
} else {
if ( cpi->DropFrameTriggerBytes >
(cpi->frame_target_rate * ((DF_CANDIDATE_WINDOW*2)-2)) )
cpi->DropFrameCandidate = 1;
else
cpi->DropFrameCandidate = 0;
}
/* Update the BpbCorrectionFactor variable according to whether or
not we were close enough with our selection of DCT quantiser. */
if ( GetFrameType(&cpi->pb) != KEY_FRAME ) {
/* Work out a size correction factor. */
CorrectionFactor = (double)oggpackB_bytes(cpi->oggbuffer) /
(double)cpi->ThisFrameTargetBytes;
if ( (CorrectionFactor > 1.05) &&
(cpi->pb.ThisFrameQualityValue <
cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]) ) {
CorrectionFactor = 1.0 + ((CorrectionFactor - 1.0)/2);
if ( CorrectionFactor > 1.5 )
cpi->BpbCorrectionFactor *= 1.5;
else
cpi->BpbCorrectionFactor *= CorrectionFactor;
/* Keep BpbCorrectionFactor within limits */
if ( cpi->BpbCorrectionFactor > MAX_BPB_FACTOR )
cpi->BpbCorrectionFactor = MAX_BPB_FACTOR;
} else if ( (CorrectionFactor < 0.95) &&
(cpi->pb.ThisFrameQualityValue > VERY_BEST_Q) ){
CorrectionFactor = 1.0 - ((1.0 - CorrectionFactor)/2);
if ( CorrectionFactor < 0.75 )
cpi->BpbCorrectionFactor *= 0.75;
else
cpi->BpbCorrectionFactor *= CorrectionFactor;
/* Keep BpbCorrectionFactor within limits */
if ( cpi->BpbCorrectionFactor < MIN_BPB_FACTOR )
cpi->BpbCorrectionFactor = MIN_BPB_FACTOR;
}
}
/* Adjust carry over and or key frame context. */
if ( GetFrameType(&cpi->pb) == KEY_FRAME ) {
/* Adjust the key frame context unless the key frame was very small */
AdjustKeyFrameContext(cpi);
} else {
/* Update the frame carry over */
cpi->CarryOver += ((ogg_int32_t)cpi->frame_target_rate -
(ogg_int32_t)oggpackB_bytes(cpi->oggbuffer));
}
cpi->TotalByteCount += oggpackB_bytes(cpi->oggbuffer);
}
static void CompressFirstFrame(CP_INSTANCE *cpi) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -