[xiph-commits] r16304 - in branches/theora-thusnelda: examples lib/enc
xiphmont at svn.xiph.org
xiphmont at svn.xiph.org
Sat Jul 18 13:53:13 PDT 2009
Author: xiphmont
Date: 2009-07-18 13:53:13 -0700 (Sat, 18 Jul 2009)
New Revision: 16304
Modified:
branches/theora-thusnelda/examples/Makefile.am
branches/theora-thusnelda/lib/enc/analyze.c
branches/theora-thusnelda/lib/enc/encint.h
branches/theora-thusnelda/lib/enc/encode.c
branches/theora-thusnelda/lib/enc/mcenc.c
branches/theora-thusnelda/lib/enc/rate.c
Log:
Commit drop-frame support for rate control
Adjust MV handling to predict across dropped frames
Correct minor MV bugs that had nearly-negligable bitrate/psnr effects
eliminate accel prediction weighting in MV candidate calc; it
had only been needed to begin with because GOLD accel was being
calculated incorrectly
Minor Makefile.am fix in examples; don't explicitly set dependencies
(not setting the lib depndencies means nothing gets rebuilt on lib
changes, bad in static builds)
Modified: branches/theora-thusnelda/examples/Makefile.am
===================================================================
--- branches/theora-thusnelda/examples/Makefile.am 2009-07-18 20:33:54 UTC (rev 16303)
+++ branches/theora-thusnelda/examples/Makefile.am 2009-07-18 20:53:13 UTC (rev 16304)
@@ -15,12 +15,10 @@
dump_video_SOURCES = dump_video.c
EXTRA_dump_video_SOURCES = getopt.c getopt1.c getopt.h
dump_video_LDADD = $(GETOPT_OBJS) $(LDADDDEC)
-dump_video_DEPENDENCIES = $(GETOPT_OBJS)
dump_psnr_SOURCES = dump_psnr.c
EXTRA_dump_psnr_SOURCES = getopt.c getopt1.c getopt.h
dump_psnr_LDADD = $(GETOPT_OBJS) $(LDADDDEC) -lm
-dump_psnr_DEPENDENCIES = $(GETOPT_OBJS)
player_example_SOURCES = player_example.c
player_example_CFLAGS = $(SDL_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS)
@@ -30,12 +28,10 @@
EXTRA_encoder_example_SOURCES = getopt.c getopt1.c getopt.h
encoder_example_CFLAGS = $(OGG_CFLAGS) $(VORBIS_CFLAGS)
encoder_example_LDADD = $(GETOPT_OBJS) $(LDADDENC) $(VORBIS_LIBS) $(VORBISENC_LIBS)
-encoder_example_DEPENDENCIES = $(GETOPT_OBJS)
png2theora_SOURCES = png2theora.c
png2theora_CFLAGS = $(OGG_CFLAGS) $(PNG_CFLAGS)
png2theora_LDADD = $(GETOPT_OBJS) $(LDADD) $(PNG_LIBS)
-png2theora_DEPENDENCIES = $(GETOPT_OBJS)
debug:
$(MAKE) all CFLAGS="@DEBUG@"
Modified: branches/theora-thusnelda/lib/enc/analyze.c
===================================================================
--- branches/theora-thusnelda/lib/enc/analyze.c 2009-07-18 20:33:54 UTC (rev 16303)
+++ branches/theora-thusnelda/lib/enc/analyze.c 2009-07-18 20:53:13 UTC (rev 16304)
@@ -1197,7 +1197,6 @@
int pli;
set_chroma_mvs=OC_SET_CHROMA_MVS_TABLE[_enc->state.info.pixel_fmt];
_enc->state.frame_type=_frame_type;
- if(!_recode&&_enc->state.curframe_num>0)oc_mcenc_start(_enc,&mcenc);
oc_mode_scheme_chooser_reset(&_enc->chooser);
oc_enc_tokenize_start(_enc);
oc_enc_pipeline_init(_enc,&pipe);
@@ -1257,13 +1256,44 @@
/*Motion estimation:
We always do a basic 1MV search for all macroblocks, coded or not,
keyframe or not.*/
+ int accumP[2]={0,0};
+ int accumG[2]={0,0};
+ if(_enc->prevframe_dropped){
+ accumP[0] = embs[mbi].analysis_mv[0][OC_FRAME_PREV][0];
+ accumP[1] = embs[mbi].analysis_mv[0][OC_FRAME_PREV][1];
+ }
+ accumG[0]=embs[mbi].analysis_mv[2][OC_FRAME_GOLD][0];
+ accumG[1]=embs[mbi].analysis_mv[2][OC_FRAME_GOLD][1];
+
+ embs[mbi].analysis_mv[0][OC_FRAME_PREV][0] -= embs[mbi].analysis_mv[2][OC_FRAME_PREV][0];
+ embs[mbi].analysis_mv[0][OC_FRAME_PREV][1] -= embs[mbi].analysis_mv[2][OC_FRAME_PREV][1];
+
/*Move the motion vector predictors back a frame.*/
memmove(embs[mbi].analysis_mv+1,
- embs[mbi].analysis_mv,2*sizeof(embs[mbi].analysis_mv[0]));
+ embs[mbi].analysis_mv,2*sizeof(embs[mbi].analysis_mv[0]));
+
/*Search the last frame.*/
- oc_mcenc_search(_enc,&mcenc,mbi,OC_FRAME_PREV);
+ oc_mcenc_search(_enc,&mcenc,accumP,mbi,OC_FRAME_PREV);
+ embs[mbi].analysis_mv[2][OC_FRAME_PREV][0]=accumP[0];
+ embs[mbi].analysis_mv[2][OC_FRAME_PREV][1]=accumP[1];
+
+ /* GOLDEN mvs are different from PREV mvs in that they're
+ each absolute offsets from some frame in the past rather
+ than relative offsets from the frame before. For
+ predictor calculation to make sense, we need them to be
+ in the same form as PREV mvs */
+ embs[mbi].analysis_mv[1][OC_FRAME_GOLD][0]-=embs[mbi].analysis_mv[2][OC_FRAME_GOLD][0];
+ embs[mbi].analysis_mv[1][OC_FRAME_GOLD][1]-=embs[mbi].analysis_mv[2][OC_FRAME_GOLD][1];
+ embs[mbi].analysis_mv[2][OC_FRAME_GOLD][0]-=accumG[0];
+ embs[mbi].analysis_mv[2][OC_FRAME_GOLD][1]-=accumG[1];
/*Search the golden frame.*/
- oc_mcenc_search(_enc,&mcenc,mbi,OC_FRAME_GOLD);
+ oc_mcenc_search(_enc,&mcenc,accumG,mbi,OC_FRAME_GOLD);
+ /*Put GOLDEN mvs back into absolute offset form. Newest MV is already an absolute offset*/
+ embs[mbi].analysis_mv[2][OC_FRAME_GOLD][0]+=accumG[0];
+ embs[mbi].analysis_mv[2][OC_FRAME_GOLD][1]+=accumG[1];
+ embs[mbi].analysis_mv[1][OC_FRAME_GOLD][0]+=embs[mbi].analysis_mv[2][OC_FRAME_GOLD][0];
+ embs[mbi].analysis_mv[1][OC_FRAME_GOLD][1]+=embs[mbi].analysis_mv[2][OC_FRAME_GOLD][1];
+
}
dx=dy=0;
if(_enc->state.frame_type==OC_INTRA_FRAME){
Modified: branches/theora-thusnelda/lib/enc/encint.h
===================================================================
--- branches/theora-thusnelda/lib/enc/encint.h 2009-07-18 20:33:54 UTC (rev 16303)
+++ branches/theora-thusnelda/lib/enc/encint.h 2009-07-18 20:53:13 UTC (rev 16304)
@@ -193,8 +193,8 @@
void oc_enc_calc_lambda(oc_enc_ctx *_enc,int _frame_type);
void oc_rc_state_init(oc_rc_state *_rc,const oc_enc_ctx *_enc);
-void oc_enc_update_rc_state(oc_enc_ctx *_enc,
- long _bits,int _qti,int _qi,int _trial);
+int oc_enc_update_rc_state(oc_enc_ctx *_enc,
+ long _bits,int _qti,int _qi,int _trial,int _droppable);
int oc_enc_select_qi(oc_enc_ctx *_enc,int _qti,int _clamp);
@@ -230,6 +230,8 @@
unsigned char vp3_compatible;
/*Whether or not any INTER frames have been coded.*/
unsigned char coded_inter_frame;
+ /*Whether or not previous frame was dropped.*/
+ unsigned char prevframe_dropped;
/*Stores most recently chosen Huffman tables for each frame type, DC and AC
coefficients, and luma and chroma tokens.
The actual Huffman table used for a given coefficient depends not only on
@@ -295,17 +297,10 @@
int setb0;
/*The total number of candidates.*/
int ncandidates;
- /*Accelerated predictor weights for each frame type.*/
- ogg_int32_t mvapw1[2];
- ogg_int32_t mvapw2[2];
};
-
-/*Prep the motion search for the next frame.*/
-void oc_mcenc_start(oc_enc_ctx *_enc,oc_mcenc_ctx *_mcenc);
-
/*Search for a single MB MV (and with OC_FRAME_PREV, block MVs) in one frame.*/
-void oc_mcenc_search(oc_enc_ctx *_enc,oc_mcenc_ctx *_mcenc,int _mbi,int _frame);
+void oc_mcenc_search(oc_enc_ctx *_enc,oc_mcenc_ctx *_mcenc,int acc[2],int _mbi,int _frame);
/*Refine a MB MV for one frame.*/
void oc_mcenc_refine1mv(oc_enc_ctx *_enc,int _mbi,int _frame);
/*Refine the block MVs.*/
Modified: branches/theora-thusnelda/lib/enc/encode.c
===================================================================
--- branches/theora-thusnelda/lib/enc/encode.c 2009-07-18 20:33:54 UTC (rev 16303)
+++ branches/theora-thusnelda/lib/enc/encode.c 2009-07-18 20:53:13 UTC (rev 16304)
@@ -936,6 +936,15 @@
oc_state_clear(&_enc->state);
}
+static void oc_enc_drop_frame(th_enc_ctx *_enc){
+ /* use the previous frame's reconstruction */
+ _enc->state.ref_frame_idx[OC_FRAME_SELF]=
+ _enc->state.ref_frame_idx[OC_FRAME_PREV];
+ /* flag motion vector analysis about the frame drop */
+ _enc->prevframe_dropped=1;
+ /* zero the packet */
+ oggpackB_reset(&_enc->opb);
+}
static void oc_enc_compress_keyframe(oc_enc_ctx *_enc,int _recode){
if(_enc->state.info.target_bitrate>0){
@@ -951,7 +960,7 @@
if(!_recode&&_enc->state.curframe_num==0){
if(_enc->state.info.target_bitrate>0){
oc_enc_update_rc_state(_enc,oggpackB_bytes(&_enc->opb)<<3,
- OC_INTRA_FRAME,_enc->state.qis[0],1);
+ OC_INTRA_FRAME,_enc->state.qis[0],1,0);
}
oc_enc_compress_keyframe(_enc,1);
}
@@ -974,8 +983,9 @@
prime feed-forward statistics.*/
_enc->coded_inter_frame=1;
if(_enc->state.info.target_bitrate>0){
+ /* rate control also needs to prime */
oc_enc_update_rc_state(_enc,oggpackB_bytes(&_enc->opb)<<3,
- OC_INTER_FRAME,_enc->state.qis[0],1);
+ OC_INTER_FRAME,_enc->state.qis[0],1,0);
}
oc_enc_compress_frame(_enc,1);
}
@@ -1206,6 +1216,7 @@
}
}
+#include<stdio.h>
int th_encode_ycbcr_in(th_enc_ctx *_enc,th_ycbcr_buffer _img){
th_ycbcr_buffer img;
int cframe_width;
@@ -1218,6 +1229,7 @@
int vdec;
int pli;
int refi;
+ int drop=0;
/*Step 1: validate parameters.*/
if(_enc==NULL||_img==NULL)return TH_EFAULT;
if(_enc->packet_state==OC_PACKET_DONE)return TH_EINVAL;
@@ -1276,16 +1288,28 @@
_enc->state.curframe_num-_enc->state.keyframe_num+_enc->dup_count>=
_enc->keyframe_frequency_force){
oc_enc_compress_keyframe(_enc,0);
+ }else{
+ oc_enc_compress_frame(_enc,0);
+ drop=1;
}
- /*Compress the frame.*/
- else oc_enc_compress_frame(_enc,0);
oc_restore_fpu(&_enc->state);
- /*Update state variables.*/
+
+ /* drop is currently indicating if the frame is droppable.*/
+ if(_enc->state.info.target_bitrate>0)
+ drop=oc_enc_update_rc_state(_enc,oggpackB_bytes(&_enc->opb)<<3,
+ _enc->state.frame_type,_enc->state.qis[0],0,drop);
+ else
+ drop=0;
+ /* drop now indicates if the frame was dropped */
+
+ if(drop){
+ oc_enc_drop_frame(_enc);
+ _enc->prevframe_dropped=1;
+ }else{
+ _enc->prevframe_dropped=0;
+ }
+
_enc->packet_state=OC_PACKET_READY;
- if(_enc->state.info.target_bitrate>0){
- oc_enc_update_rc_state(_enc,oggpackB_bytes(&_enc->opb)<<3,
- _enc->state.frame_type,_enc->state.qis[0],0);
- }
_enc->prev_dup_count=_enc->nqueued_dups=_enc->dup_count;
_enc->dup_count=0;
#if defined(OC_DUMP_IMAGES)
Modified: branches/theora-thusnelda/lib/enc/mcenc.c
===================================================================
--- branches/theora-thusnelda/lib/enc/mcenc.c 2009-07-18 20:33:54 UTC (rev 16303)
+++ branches/theora-thusnelda/lib/enc/mcenc.c 2009-07-18 20:53:13 UTC (rev 16304)
@@ -69,25 +69,9 @@
{0,1,3}
};
-
-void oc_mcenc_start(oc_enc_ctx *_enc,oc_mcenc_ctx *_mcenc){
- ogg_int64_t nframes;
- /*Set up the accelerated MV weights for previous frame prediction.*/
- _mcenc->mvapw1[OC_FRAME_PREV]=(ogg_int32_t)1<<17;
- _mcenc->mvapw2[OC_FRAME_PREV]=(ogg_int32_t)1<<16;
- /*Set up the accelerated MV weights for golden frame prediction.*/
- nframes=_enc->state.curframe_num-_enc->state.keyframe_num;
- _mcenc->mvapw1[OC_FRAME_GOLD]=(ogg_int32_t)(
- nframes!=1?(nframes<<17)/(nframes-1):0);
- _mcenc->mvapw2[OC_FRAME_GOLD]=(ogg_int32_t)(
- nframes!=2?(nframes<<16)/(nframes-2):0);
-}
-
static void oc_mcenc_find_candidates(oc_enc_ctx *_enc,oc_mcenc_ctx *_mcenc,
- int _mbi,int _frame){
+ int _accum[2], int _mbi,int _frame){
oc_mb_enc_info *embs;
- ogg_int32_t mvapw1;
- ogg_int32_t mvapw2;
int a[3][2];
int ncandidates;
unsigned nmbi;
@@ -106,11 +90,11 @@
}
/*Add a few additional vectors to set A: the vector used in the
previous frame and the (0,0) vector.*/
- _mcenc->candidates[ncandidates][0]=embs[_mbi].analysis_mv[1][_frame][0];
- _mcenc->candidates[ncandidates][1]=embs[_mbi].analysis_mv[1][_frame][1];
+ _mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31,embs[_mbi].analysis_mv[1][_frame][0]+_accum[0],31);
+ _mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31,embs[_mbi].analysis_mv[1][_frame][1]+_accum[1],31);
ncandidates++;
- _mcenc->candidates[ncandidates][0]=0;
- _mcenc->candidates[ncandidates][1]=0;
+ _mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31,_accum[0],31);
+ _mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31,_accum[1],31);
ncandidates++;
/*Use the first three vectors of set A to find our best predictor: their
median.*/
@@ -128,30 +112,30 @@
/*The upper-left most macro block has no neighbors at all
We just use 0,0 as the median predictor and its previous motion vector
for set A.*/
- _mcenc->candidates[0][0]=0;
- _mcenc->candidates[0][1]=1;
- _mcenc->candidates[1][0]=embs[_mbi].analysis_mv[1][_frame][0];
- _mcenc->candidates[1][1]=embs[_mbi].analysis_mv[1][_frame][1];
+ _mcenc->candidates[0][0]=OC_CLAMPI(-31,_accum[0],31);
+ _mcenc->candidates[0][1]=OC_CLAMPI(-31,_accum[1],31);
+ _mcenc->candidates[1][0]=OC_CLAMPI(-31,embs[_mbi].analysis_mv[1][_frame][0]+_accum[0],31);
+ _mcenc->candidates[1][1]=OC_CLAMPI(-31,embs[_mbi].analysis_mv[1][_frame][1]+_accum[1],31);
ncandidates=2;
}
+
/*Fill in set B: accelerated predictors for this and adjacent macro
- blocks.*/
+ blocks.*/
_mcenc->setb0=ncandidates;
- mvapw1=_mcenc->mvapw1[_frame];
- mvapw2=_mcenc->mvapw2[_frame];
/*The first time through the loop use the current macro block.*/
nmbi=_mbi;
for(i=0;;i++){
- _mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31,
- OC_DIV_POW2_RE(embs[nmbi].analysis_mv[1][_frame][0]*mvapw1
- -embs[nmbi].analysis_mv[2][_frame][0]*mvapw2,16),31);
- _mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31,
- OC_DIV_POW2_RE(embs[nmbi].analysis_mv[1][_frame][1]*mvapw1
- -embs[nmbi].analysis_mv[2][_frame][1]*mvapw2,16),31);
+ _mcenc->candidates[ncandidates][0]=
+ OC_CLAMPI(-31, 2*embs[_mbi].analysis_mv[1][_frame][0]-
+ embs[_mbi].analysis_mv[2][_frame][0]+_accum[0], 31);
+ _mcenc->candidates[ncandidates][1]=
+ OC_CLAMPI(-31, 2*embs[_mbi].analysis_mv[1][_frame][1]-
+ embs[_mbi].analysis_mv[2][_frame][1]+_accum[1], 31);
ncandidates++;
if(i>=embs[_mbi].npneighbors)break;
nmbi=embs[_mbi].pneighbors[i];
}
+
/*Truncate to full-pel positions.*/
for(i=0;i<ncandidates;i++){
_mcenc->candidates[i][0]=OC_DIV2(_mcenc->candidates[i][0]);
@@ -246,9 +230,10 @@
The actual motion vector is stored in the appropriate place in the
oc_mb_enc_info structure.
_mcenc: The motion compensation context.
+ _accum: drop frame/golden mv accumulators
_mbi: The macro block index.
_frame: The frame to search, either OC_FRAME_PREV or OC_FRAME_GOLD.*/
-void oc_mcenc_search(oc_enc_ctx *_enc,oc_mcenc_ctx *_mcenc,int _mbi,int _frame){
+void oc_mcenc_search(oc_enc_ctx *_enc,oc_mcenc_ctx *_mcenc,int _accum[2],int _mbi,int _frame){
/*Note: Traditionally this search is done using a rate-distortion objective
function of the form D+lambda*R.
However, xiphmont tested this and found it produced a small degredation,
@@ -282,7 +267,7 @@
int bi;
embs=_enc->mb_info;
/*Find some candidate motion vectors.*/
- oc_mcenc_find_candidates(_enc,_mcenc,_mbi,_frame);
+ oc_mcenc_find_candidates(_enc,_mcenc,_accum,_mbi,_frame);
/*Clear the cache of locations we've examined.*/
memset(hit_cache,0,sizeof(hit_cache));
/*Start with the median predictor.*/
Modified: branches/theora-thusnelda/lib/enc/rate.c
===================================================================
--- branches/theora-thusnelda/lib/enc/rate.c 2009-07-18 20:33:54 UTC (rev 16303)
+++ branches/theora-thusnelda/lib/enc/rate.c 2009-07-18 20:53:13 UTC (rev 16304)
@@ -129,15 +129,26 @@
_rc->log_drop_scale=OC_Q57(0);
}
-void oc_enc_update_rc_state(oc_enc_ctx *_enc,
- long _bits,int _qti,int _qi,int _trial){
+int oc_enc_update_rc_state(oc_enc_ctx *_enc,
+ long _bits,int _qti,int _qi,int _trial,int _droppable){
+
/*Note, setting OC_SCALE_SMOOTHING[1] to 0x80 (0.5), which one might expect
- to be a reasonable value, actually causes a feedback loop with, e.g., 12
- fps content encoded at 24 fps; use values near 0 or near 1 for now.
+ to be a reasonable value, actually causes a feedback loop with, e.g., 12
+ fps content encoded at 24 fps; use values near 0 or near 1 for now.
TODO: Should probably revisit using an exponential moving average in the
- first place at some point; dup tracking should help as well.*/
+ first place at some point; dup tracking should help as well.*/
static const unsigned OC_SCALE_SMOOTHING[2]={0x13,0x00};
- if(_bits>0){
+ int dropped=0;
+
+ if(_bits<=0){
+ /* Update the buffering stats as if this dropped frame was a dup
+ of the previous frame. */
+ _enc->rc.prev_drop_count+=1+_enc->dup_count;
+ /*If this was the first frame of this type, lower the expected scale, but
+ don't set it to zero outright.*/
+ if(_trial)_enc->rc.log_scale[_qti]>>=1;
+ _bits=0;
+ }else{
ogg_int64_t log_scale;
ogg_int64_t log_bits;
ogg_int64_t log_qexp;
@@ -150,29 +161,33 @@
if(_trial)_enc->rc.log_scale[_qti]=log_scale;
else{
/*Otherwise update an exponential moving average.*/
+ /*log scale is updated regardless of dropping*/
_enc->rc.log_scale[_qti]=log_scale
- +(_enc->rc.log_scale[_qti]-log_scale+128>>8)*OC_SCALE_SMOOTHING[_qti];
- /*And update a simple exponential moving average to estimate the "real"
- frame rate taking drops and duplicates into account.*/
- _enc->rc.log_drop_scale=_enc->rc.log_drop_scale
- +oc_blog64(_enc->rc.prev_drop_count+1)>>1;
- _enc->rc.prev_drop_count=_enc->dup_count;
+ +(_enc->rc.log_scale[_qti]-log_scale+128>>8)*OC_SCALE_SMOOTHING[_qti];
+ /* If this frame busts our budget, it must be dropped.*/
+ if(_droppable && _enc->rc.fullness+_enc->rc.bits_per_frame*
+ (1+_enc->dup_count)<_bits){
+ _enc->rc.prev_drop_count+=1+_enc->dup_count;
+ _bits=0;
+ dropped=1;
+ }else{
+ /*log_drop_scale is only updated if the frame is coded as it
+ needs final previous counts*/
+ /*update a simple exponential moving average to estimate the "real"
+ frame rate taking drops and duplicates into account.*/
+ _enc->rc.log_drop_scale=_enc->rc.log_drop_scale
+ +oc_blog64(_enc->rc.prev_drop_count+1)>>1;
+ _enc->rc.prev_drop_count=_enc->dup_count;
+ }
}
}
- else{
- /*We dropped this frame.*/
- /*Add it to the previous frame's dup count.*/
- _enc->rc.prev_drop_count+=1+_enc->dup_count;
- /*If this was the first frame of this type, lower the expected scale, but
- don't set it to zero outright.*/
- if(_trial)_enc->rc.log_scale[_qti]>>=1;
- }
if(!_trial){
/*And update the buffer fullness level.*/
_enc->rc.fullness+=_enc->rc.bits_per_frame*(1+_enc->dup_count)-_bits;
/*If we're too quick filling the buffer, that rate is lost forever.*/
if(_enc->rc.fullness>_enc->rc.max)_enc->rc.fullness=_enc->rc.max;
}
+ return dropped;
}
int oc_enc_select_qi(oc_enc_ctx *_enc,int _qti,int _clamp){
@@ -283,6 +298,7 @@
if(log_scale0-log_qexp>log_hard_limit){
log_qexp=log_scale0-log_hard_limit;
log_qtarget=((log_qexp+(exp0>>1))/exp0<<6)+OC_Q57(2);
+ log_qtarget=OC_MINI(log_qtarget,OC_QUANT_MAX_LOG);
}
}
qi=oc_enc_find_qi_for_target(_enc,_qti,old_qi,
More information about the commits
mailing list