On 07/25/2007 07:50 AM, Stefan Gehrer wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Hi,
Michael Niedermayer wrote:
Michael, regarding your question on cvslog (sorry for crossposting, I wasn't subscribed to cvslog at the time):
Author: stefang Date: Sat Jul 7 09:14:58 2007 New Revision: 9518
Log: move dequantization into it's own inline function why inline? same question for the other inline functions is there some speed gain from duplicating them in the object files between encoder and decoder? To be honest I did not think much about object size, it just seemed to be a convenient way to share functions this way instead of having to create ff_cavs_foobar names and without creating function call overhead that was not there before. If you think object size would be a problem I can of course move some functions back from cavs.h to cavs.c.
yes please do unless theres a real speed loss from doing so
done
[...]
+/** + * eliminate residual blocks that only have insignificant coefficients, + * inspired from x264 and JVT-B118 + */ +static inline int decimate_block(uint8_t *run, DCTELEM *level, int count) { + static const uint8_t run_score[30] = { + 0,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 }; + int i; + int score = 0; + + if(count>4) + return 9; + for(i=0;i<count;i++) { + int abslevel = FFABS(level[i]); + if(abslevel> 1) + return 9; + score += run_score[FFMIN(run[i],29)]; + } + return score; +}
how much psnr/bitrate is gained by this? if none then please drop it
I tested on the popular foreman (300 CIF frames). When encoding at around 800kbps, roughly 0.1dB are gained by this. When encoding at around 200kbps, this increases to a gain of around 0.15dB. So I would like to keep it.
[...]
+ av_reduce(&frame_rate.den,&frame_rate.num, + s->avctx->time_base.num, s->avctx->time_base.den, 60000);
if the exact one isnt support then the code should failm its the user apps job to choose a supported one
removed
+ for(i=0;i<15;i++) + if((ff_frame_rate_tab[i].den == frame_rate.den)&& + (ff_frame_rate_tab[i].num == frame_rate.num)) + frame_rate_code = i; + if(frame_rate_code< 0) { + av_log(h->s.avctx, AV_LOG_ERROR, "unsupported framerate %d/%d\n", + frame_rate.num, frame_rate.den); + return -1; + }
[...]
+ put_bits(&s->pb,16,0); + put_bits(&s->pb,16,CAVS_START_CODE);
add a put_bits_long() to bitstrea.c, document the issue with 32bits and fix vorbis_enc.c :)
I looked a bit into this and I think a better way would be to fix put_bits() to support up to 32 bits instead of up to 31 bits. I am not so sure, but after a bit of checking in vorbis_enc.c it seems there is never any writing with more than 32 bits even though the local put_bits() function takes a 64bit argument.
Attached a new encoder patch and a proposed fix for put_bits().
Git-friendly patches attached so patchwork will catch it up. -Vitor
From stefan.gehreratgmx.de Sun Jan 23 16:19:30 2011 From: stefan.gehreratgmx.de (Stefan Gehrer) Date: Sun, 23 Jan 2011 16:19:30 +0100 Subject: [PATCH 1/2] Make put_bits() support 32 bits instead of 31 Message-ID: <mailman.461.1295796257.1307.ffmpeg-devel@mplayerhq.hu>
--- libavcodec/bitstream.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/libavcodec/bitstream.h b/libavcodec/bitstream.h index fd1b4a9..04942a0 100644 --- a/libavcodec/bitstream.h +++ b/libavcodec/bitstream.h @@ -226,7 +226,7 @@ static inline void put_bits(PutBitContext *s, int n, unsigned int value) bit_buf = (bit_buf<<n) | value; bit_left-=n; } else { - bit_buf<<=bit_left; + bit_buf = (uint64_t)bit_buf << bit_left; bit_buf |= value >> (n - bit_left); #ifdef UNALIGNED_STORES_ARE_BAD if (3 & (intptr_t) s->buf_ptr) { -- 1.7.1 --------------010605070306040906030502 Content-Type: text/x-patch; name="0002-Chinese-AVS-encoder.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0002-Chinese-AVS-encoder.patch"
From stefan.gehreratgmx.de Sun Jan 23 16:21:20 2011 From: stefan.gehreratgmx.de (Stefan Gehrer) Date: Sun, 23 Jan 2011 16:21:20 +0100 Subject: [PATCH 2/2] Chinese AVS encoder Message-ID: <mailman.462.1295796257.1307.ffmpeg-devel@mplayerhq.hu>
--- Changelog | 1 + doc/ffmpeg-doc.texi | 1 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 2 +- libavcodec/allcodecs.h | 1 + libavcodec/cavs.c | 4 +- libavcodec/cavs.h | 27 ++- libavcodec/cavsdsp.c | 99 +++++++- libavcodec/cavsenc.c | 687 ++++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/dsputil.h | 3 + 10 files changed, 821 insertions(+), 5 deletions(-) create mode 100644 libavcodec/cavsenc.c diff --git a/Changelog b/Changelog index 6ecbdb7..d8d177b 100644 --- a/Changelog +++ b/Changelog @@ -90,6 +90,7 @@ version <next> - RoQ video encoder - QTRLE encoder - OS/2 support removed +- Chinese AVS encoder version 0.4.9-pre1: diff --git a/doc/ffmpeg-doc.texi b/doc/ffmpeg-doc.texi index fca90cc..0e99c29 100644 --- a/doc/ffmpeg-doc.texi +++ b/doc/ffmpeg-doc.texi @@ -1072,6 +1072,7 @@ following image formats are supported: @item THP @tab @tab X @tab Used on the Nintendo GameCube. @item Bethsoft VID @tab @tab X @tab Used in some games from Bethesda Softworks. @item Renderware TXD @tab @tab X @tab Texture dictionaries used by the Renderware Engine. + at item Chinese AVS @tab X @tab X @tab JiZhun Profile @end multitable @code{X} means that encoding (resp. decoding) is supported. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index e1685fe..47ffdae 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -45,6 +45,7 @@ OBJS-$(CONFIG_BMP_DECODER) += bmp.o OBJS-$(CONFIG_BMP_ENCODER) += bmpenc.o OBJS-$(CONFIG_C93_DECODER) += c93.o OBJS-$(CONFIG_CAVS_DECODER) += cavs.o cavsdec.o cavsdsp.o golomb.o +OBJS-$(CONFIG_CAVS_ENCODER) += cavs.o cavsenc.o cavsdsp.o golomb.o OBJS-$(CONFIG_CINEPAK_DECODER) += cinepak.o OBJS-$(CONFIG_CLJR_DECODER) += cljr.o OBJS-$(CONFIG_CLJR_ENCODER) += cljr.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 8153e41..5a8f69b 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -63,7 +63,7 @@ void avcodec_register_all(void) REGISTER_DECODER(BETHSOFTVID, bethsoftvid); REGISTER_ENCDEC (BMP, bmp); REGISTER_DECODER(C93, c93); - REGISTER_DECODER(CAVS, cavs); + REGISTER_ENCDEC (CAVS, cavs); REGISTER_DECODER(CINEPAK, cinepak); REGISTER_DECODER(CLJR, cljr); REGISTER_DECODER(CSCD, cscd); diff --git a/libavcodec/allcodecs.h b/libavcodec/allcodecs.h index 3e18ca0..3d32945 100644 --- a/libavcodec/allcodecs.h +++ b/libavcodec/allcodecs.h @@ -27,6 +27,7 @@ extern AVCodec ac3_encoder; extern AVCodec asv1_encoder; extern AVCodec asv2_encoder; extern AVCodec bmp_encoder; +extern AVCodec cavs_encoder; extern AVCodec dvvideo_encoder; extern AVCodec ffv1_encoder; extern AVCodec ffvhuff_encoder; diff --git a/libavcodec/cavs.c b/libavcodec/cavs.c index 31b7e58..323989b 100644 --- a/libavcodec/cavs.c +++ b/libavcodec/cavs.c @@ -1,5 +1,5 @@ /* - * Chinese AVS video (AVS1-P2, JiZhun profile) decoder. + * Chinese AVS video (AVS1-P2, JiZhun profile) codec * Copyright (c) 2006 Stefan Gehrer <stefan.gehrer at gmx.de> * * This file is part of FFmpeg. @@ -21,7 +21,7 @@ /** * @file cavs.c - * Chinese AVS video (AVS1-P2, JiZhun profile) decoder + * Chinese AVS video (AVS1-P2, JiZhun profile) codec * @author Stefan Gehrer <stefan.gehrer at gmx.de> */ diff --git a/libavcodec/cavs.h b/libavcodec/cavs.h index adc9c24..fad084a 100644 --- a/libavcodec/cavs.h +++ b/libavcodec/cavs.h @@ -55,6 +55,13 @@ #define MV_BWD_OFFS 12 #define MV_STRIDE 4 +#define CAVS_MAX_RUN 25 +#define CAVS_MAX_LEVEL 26 +#define INTRA_BIAS 38<<8 +#define INTER_BIAS 28<<8 +#define ME_THRES 77000 +#define ME_ITER 20 + enum mb_t { I_8X8 = 0, P_SKIP, @@ -104,7 +111,8 @@ enum mv_pred_t { MV_PRED_TOP, MV_PRED_TOPRIGHT, MV_PRED_PSKIP, - MV_PRED_BSKIP + MV_PRED_BSKIP, + MV_PRED_ENC }; enum block_t { @@ -153,9 +161,16 @@ typedef struct dec_2dvlc_t { } dec_2dvlc_t; typedef struct { + int8_t rlcode[CAVS_MAX_RUN+1][CAVS_MAX_LEVEL+1]; + int end_code; + const dec_2dvlc_t *dec; +} enc_2dvlc_t; + +typedef struct { MpegEncContext s; Picture picture; ///< currently decoded frame Picture DPB[2]; ///< reference frames + Picture input_picture; ///< encoder input int dist[2]; ///< temporal distances from current frame to ref frames int profile, level; int aspect_ratio; @@ -171,6 +186,7 @@ typedef struct { int flags; ///< availability flags of neighbouring macroblocks int stc; ///< last start code uint8_t *cy, *cu, *cv; ///< current MB sample pointers + uint8_t *ey, *eu, *ev; ///< encoded MB sample pointers int left_qp; uint8_t *top_qp; @@ -222,6 +238,15 @@ typedef struct { int got_keyframe; DCTELEM *block; + + /* encoder only */ + DCTELEM *levels[6]; + uint8_t *runs[6]; + int total_coeff[6]; + int lambda; + int mrefs; ///< flag: one reference frame (0) or two (1) + int skip_count; + int poc; ///< picture order count } AVSContext; extern const uint8_t ff_cavs_dequant_shift[64]; diff --git a/libavcodec/cavsdsp.c b/libavcodec/cavsdsp.c index fd744cc..5db9abd 100644 --- a/libavcodec/cavsdsp.c +++ b/libavcodec/cavsdsp.c @@ -1,5 +1,5 @@ /* - * Chinese AVS video (AVS1-P2, JiZhun profile) decoder. + * Chinese AVS video (AVS1-P2, JiZhun profile) codec * * DSP functions * @@ -177,6 +177,101 @@ static void cavs_filter_ch_c(uint8_t *d, int stride, int alpha, int beta, int tc /***************************************************************************** * + * quantization + * + ****************************************************************************/ + +static void cavs_quant_c(DCTELEM *block, const uint16_t *norm, int mul, + int bias) { + int i; + + for(i=0;i<64;i++) { + if(block[i] > 0) + block[i] = ((((1<<18) + block[i]*norm[i])>>19)*mul+bias)>>15; + else + block[i] = -(((((1<<18) - block[i]*norm[i])>>19)*mul+bias)>>15); + } +} + +/***************************************************************************** + * + * forward transform + * + ****************************************************************************/ + +static void cavs_sub_dct8_c(uint8_t *src1, uint8_t *src2, DCTELEM *block, int stride1, int stride2) { + int i,j; + DCTELEM (*dst)[8] = (DCTELEM(*)[8])block; + + for(j=0;j<8;j++) { + for(i=0;i<8;i++) + dst[j][i] = src1[i] - src2[i]; + src1 += stride1; + src2 += stride2; + } + + for(i = 0; i < 8; i++) { + const int a0 = dst[i][0] + dst[i][7]; + const int a1 = dst[i][1] + dst[i][6]; + const int a2 = dst[i][2] + dst[i][5]; + const int a3 = dst[i][3] + dst[i][4]; + const int a4 = dst[i][0] - dst[i][7]; + const int a5 = dst[i][1] - dst[i][6]; + const int a6 = dst[i][2] - dst[i][5]; + const int a7 = dst[i][3] - dst[i][4]; + + const int b0 = a0 + a3; + const int b1 = a1 + a2; + const int b2 = a0 - a3; + const int b3 = a1 - a2; + + const int b4 = ((a4 - a7)<<1) + a4; + const int b5 = ((a6 + a5)<<1) + a5; + const int b6 = ((a6 - a5)<<1) + a6; + const int b7 = ((a4 + a7)<<1) + a7; + + dst[i][0] = (b0 + b1)<<3; + dst[i][1] = ((b4 + b5 + b7)<<1) + b5; + dst[i][2] = b2*10 + b3*4; + dst[i][3] = ((b4 - b5 - b6)<<1) + b4; + dst[i][4] = (b0 - b1)<<3; + dst[i][5] = ((b7 - b5 + b6)<<1) + b7; + dst[i][6] = b2*4 - b3*10; + dst[i][7] = ((b4 + b6 - b7)<<1) + b6; + } + for(i = 0; i < 8; i++) { + const int a0 = dst[0][i] + dst[7][i]; + const int a1 = dst[1][i] + dst[6][i]; + const int a2 = dst[2][i] + dst[5][i]; + const int a3 = dst[3][i] + dst[4][i]; + const int a4 = dst[0][i] - dst[7][i]; + const int a5 = dst[1][i] - dst[6][i]; + const int a6 = dst[2][i] - dst[5][i]; + const int a7 = dst[3][i] - dst[4][i]; + + const int b0 = a0 + a3 + 2; + const int b1 = a1 + a2; + const int b2 = a0 - a3; + const int b3 = a1 - a2; + + const int b4 = ((a4 - a7)<<1) + a4; + const int b5 = ((a6 + a5)<<1) + a5; + const int b6 = ((a6 - a5)<<1) + a6; + const int b7 = ((a4 + a7)<<1) + a7; + + dst[0][i] = (((b0 + b1)<<3) + 0)>>5; + dst[1][i] = ((((b4 + b5 + b7)<<1) + b5)+16)>>5; + dst[2][i] = ((b2*10 + b3*4) +16)>>5; + dst[3][i] = ((((b4 - b5 - b6)<<1) + b4)+16)>>5; + dst[4][i] = (((b0 - b1)<<3) + 0)>>5; + dst[5][i] = ((((b7 - b5 + b6)<<1) + b7)+16)>>5; + dst[6][i] = ((b2*4 - b3*10) +16)>>5; + dst[7][i] = ((((b4 + b6 - b7)<<1) + b6)+16)>>5; + } +} + +/***************************************************************************** + * * inverse transform * ****************************************************************************/ @@ -543,4 +638,6 @@ void ff_cavsdsp_init(DSPContext* c, AVCodecContext *avctx) { c->cavs_filter_cv = cavs_filter_cv_c; c->cavs_filter_ch = cavs_filter_ch_c; c->cavs_idct8_add = cavs_idct8_add_c; + c->cavs_sub_dct8 = cavs_sub_dct8_c; + c->cavs_quant = cavs_quant_c; } diff --git a/libavcodec/cavsenc.c b/libavcodec/cavsenc.c new file mode 100644 index 0000000..d2d2743 --- /dev/null +++ b/libavcodec/cavsenc.c @@ -0,0 +1,687 @@ +/* + * Chinese AVS video (AVS1-P2, JiZhun profile) encoder + * Copyright (c) 2006 Stefan Gehrer <stefan.gehrer at gmx.de> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file cavsenc.c + * Chinese AVS video (AVS1-P2, JiZhun profile) encoder + * @author Stefan Gehrer <stefan.gehrer at gmx.de> + */ + +#include "avcodec.h" +#include "golomb.h" +#include "cavs.h" + +static const uint8_t cbp_enc_tab[64][2] = { + { 4, 0},{16,19},{17,16},{19,15},{14,18},{ 9,11},{22,31},{ 8,13}, + {11,17},{21,30},{10,12},{ 7, 9},{12,10},{ 6, 7},{ 5, 8},{ 1, 1}, + {35, 4},{47,42},{48,38},{38,27},{46,39},{36,33},{50,59},{26,26}, + {45,40},{52,58},{41,35},{28,25},{37,29},{23,24},{31,28},{ 2, 3}, + {43, 5},{51,51},{56,52},{39,37},{55,50},{33,43},{62,63},{27,44}, + {54,53},{60,62},{40,48},{32,47},{42,34},{24,45},{29,49},{ 3, 6}, + {49,14},{53,55},{57,56},{25,36},{58,54},{30,41},{59,60},{15,21}, + {61,57},{63,61},{44,46},{18,22},{34,32},{13,20},{20,23},{ 0, 2} +}; + +/* + * taken from AVS paper by Lu Yu/Feng Yi/Jie Dong/Cixun Zhang + * this is used to post-normalise the rounding errors of the + * forward transform in the encoder as well as pre-normalise the + * rounding errors of the inverse transform in the decoder, + * both due to using non-orthogonal integer matrices + */ +static const uint16_t quant_norm[64] = { + 32768, 37958, 36158, 37958, 32768, 37958, 36158, 37958, + 37958, 43969, 41884, 43969, 37958, 43969, 41884, 43969, + 36158, 41884, 39898, 41884, 36158, 41884, 39898, 41884, + 37958, 43969, 41884, 43969, 37958, 43969, 41884, 43969, + 32768, 37958, 36158, 37958, 32768, 37958, 36158, 37958, + 37958, 43969, 41884, 43969, 37958, 43969, 41884, 43969, + 36158, 41884, 39898, 41884, 36158, 41884, 39898, 41884, + 37958, 43969, 41884, 43969, 37958, 43969, 41884, 43969, +}; + +static const uint16_t quant_mul[64] = { + 32768,29775,27554,25268,23170,21247,19369,17770, + 16302,15024,13777,12634,11626,10624, 9742, 8958, + 8192, 7512, 6889, 6305, 5793, 5303, 4878, 4467, + 4091, 3756, 3444, 3161, 2894, 2654, 2435, 2235, + 2048, 1878, 1722, 1579, 1449, 1329, 1218, 1117, + 1024, 939, 861, 790, 724, 664, 609, 558, + 512, 470, 430, 395, 362, 332, 304, 279, + 256, 235, 215, 197, 181, 166, 152, 140 +}; + +static enc_2dvlc_t intra_enc[7]; +static enc_2dvlc_t inter_enc[7]; +static enc_2dvlc_t chroma_enc[5]; + +/***************************************************************************** + * + * residual data encoding + * + ****************************************************************************/ + +/** kth-order exponential golomb code */ +static inline void put_ue_code(PutBitContext *pb, int order, int value) { + set_ue_golomb(pb, value>>order); + put_bits(pb, order, value & ((1<<order)-1)); +} + +/** + * entropy coding of one residual 8x8 block + * @param enc pointer to 2D VLC encoding table + * @param esc_golomb_order escape codes are k-golomb with this order k + * @param qp quantizer + * @param block block number [0..5] + */ +static void encode_residual_block(AVSContext *h, PutBitContext *pb, + const enc_2dvlc_t *enc, int esc_golomb_order, + int qp, int block) { + int run = 0; + int coeff_num, level_code; + DCTELEM *level_buf = h->levels[block]; + uint8_t *run_buf = h->runs[block]; + const dec_2dvlc_t *dec = enc->dec; + + for(coeff_num=0;coeff_num<h->total_coeff[block];coeff_num++) { + int level = abs(level_buf[coeff_num])-1; + int sign = (level_buf[coeff_num]>>31)&1; + run = run_buf[coeff_num] - 1; + if((level > CAVS_MAX_LEVEL)||(run > CAVS_MAX_RUN) + ||((level_code = enc->rlcode[run][level])<0)) { + put_ue_code(pb,dec->golomb_order,run*2+ESCAPE_CODE+1-sign); + if(run >= dec->max_run) + put_ue_code(pb,esc_golomb_order, level); + else + put_ue_code(pb,esc_golomb_order, level + 1 - + dec->level_add[run+1]); + while(level >= dec->inc_limit) { + dec++; + enc++; + } + } else { + put_ue_code(pb,dec->golomb_order,level_code+sign); + enc += dec->rltab[level_code][2]; + dec += dec->rltab[level_code][2]; + } + } + put_ue_code(pb,dec->golomb_order,enc->end_code); +} + +/** + * entropy coding of one macroblock + * @param intra flag inter=0 intra=1 + */ +static void encode_residual(AVSContext *h, int intra) { + PutBitContext *pb = &h->s.pb; + int block; + + for(block=0;block<4;block++) + if(h->cbp & (1<<block)) { + if(intra) + encode_residual_block(h,pb,intra_enc,1,h->qp,block); + else + encode_residual_block(h,pb,inter_enc,0,h->qp,block); + } + if(h->cbp & (1<<4)) + encode_residual_block(h,pb,chroma_enc,0,ff_cavs_chroma_qp[h->qp], 4); + if(h->cbp & (1<<5)) + encode_residual_block(h,pb,chroma_enc,0,ff_cavs_chroma_qp[h->qp], 5); +} + +/** + * eliminate residual blocks that only have insignificant coefficients, + * inspired from x264 and JVT-B118 + */ +static inline int decimate_block(uint8_t *run, DCTELEM *level, int count) { + static const uint8_t run_score[30] = { + 0,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 }; + int i; + int score = 0; + + if(count>4) + return 9; + for(i=0;i<count;i++) { + int abslevel = FFABS(level[i]); + if(abslevel > 1) + return 9; + score += run_score[FFMIN(run[i],29)]; + } + return score; +} + +/***************************************************************************** + * + * reconstruction as the decoder sees it + * + ****************************************************************************/ + +static int recon_block(AVSContext *h, uint8_t *src1, uint8_t *src2, int stride, + int block, int qp, int intra) { + int i,pos; + int score = 9; + int run = 0; + const uint8_t *scantab = ff_zigzag_direct; + + h->s.dsp.cavs_sub_dct8(src1, src2, h->block, stride, stride); + h->s.dsp.cavs_quant(h->block, quant_norm, quant_mul[qp], + intra ? INTRA_BIAS : INTER_BIAS); + /* forward scan */ + for(pos=63,i=0;pos>=0;pos--) { + if(h->block[scantab[pos]]) { + if(i) + h->runs[block][i-1] = run+1; + h->levels[block][i++] = h->block[scantab[pos]]; + run = 0; + } else + run++; + } + /* eliminate empty or insignificant blocks */ + if(!i) + return 0; + h->runs[block][i-1] = run+1; + if(!intra) { + score = decimate_block(h->runs[block],h->levels[block],i); + h->total_coeff[block] = 0; + if(score < 4) + return score; + } + h->cbp |= (1 << block); + h->total_coeff[block] = i; + /* dequantise and restore blocks */ + dequant(h , h->levels[block], h->runs[block], h->block, + ff_cavs_dequant_mul[qp], ff_cavs_dequant_shift[qp], i); + h->s.dsp.cavs_idct8_add(src2, h->block, stride); + return score; +} + +static inline void recon_mb(AVSContext *h, int *lscore, int *cscore) { + int block; + + for(block=0;block<4;block++) + *lscore += recon_block(h, h->ey + h->luma_scan[block], + h->cy + h->luma_scan[block], + h->l_stride, block, h->qp, 0); + *cscore += recon_block(h, h->eu, h->cu, h->c_stride, 4, + ff_cavs_chroma_qp[h->qp], 0); + *cscore += recon_block(h, h->ev, h->cv, h->c_stride, 5, + ff_cavs_chroma_qp[h->qp], 0); +} + +/***************************************************************************** + * + * intra encoding + * + ****************************************************************************/ + +static inline int modify(int flags, int block, int mode) { + if(!(flags & A_AVAIL) && !(block & 1)) + mode = ff_left_modifier_l[mode]; + if(!(flags & B_AVAIL) && !(block & 2) && (mode >= 0)) + mode = ff_top_modifier_l[mode]; + return mode; +} + +static void encode_mb_i(AVSContext *h) { + PutBitContext *pb = &h->s.pb; + int block, i, pred_mode_uv, min_cost, mode, best_mode, diff; + int coded_mode[5]; + uint8_t top[18]; + uint8_t *left = NULL; + uint8_t *d; + uint8_t *s; + + assert(h->mbx || (!(h->flags & A_AVAIL))); + h->lambda = h->qp/3; + h->cbp = pred_mode_uv = best_mode = 0; + for(block=0;block<4;block++) { + int pos = ff_cavs_scan3x3[block]; + int predpred = FFMIN(h->pred_mode_Y[pos-1], h->pred_mode_Y[pos-3]); + + min_cost = INT_MAX; + s = h->ey + h->luma_scan[block]; + d = h->cy + h->luma_scan[block]; + ff_cavs_load_intra_pred_luma(h, top, &left, block); + if(predpred == NOT_AVAIL) // if either is not available + predpred = INTRA_L_LP; + + /* try all luma intra prediction modes */ + for(i=0;i<5;i++) { + const int bitcost[5] = {3,3,3,3,1}; + + mode = (i == 4) ? predpred : i + (i >= predpred); + mode = modify(h->flags, block, mode); + if(mode >= 0) { + h->intra_pred_l[mode](d, top, left, h->l_stride); + diff = h->s.dsp.sse[1](NULL, s, d, h->l_stride, 8); + if((bitcost[i]*h->lambda + diff) < min_cost) { + min_cost = bitcost[i]*h->lambda + diff; + coded_mode[block] = i; + best_mode = mode; + h->pred_mode_Y[pos] = (i == 4) ? predpred : i + (i >= predpred); + } + } + } + /* reconstruct block for next one */ + h->intra_pred_l[best_mode](d, top, left, h->l_stride); + recon_block(h, s, d, h->l_stride, block, h->qp, 1); + } + + /* try all chroma intra prediction modes */ + ff_cavs_load_intra_pred_chroma(h); + min_cost = INT_MAX; + for(i=0;i<4;i++) { + const int bitcost[4] = {1,3,3,5}; + + mode = i; + if(!(h->flags & A_AVAIL)) + mode = ff_left_modifier_c[mode]; + if(!(h->flags & B_AVAIL) && (mode >=0)) + mode = ff_top_modifier_c[mode]; + if(mode >= 0) { + h->intra_pred_c[mode](h->cu, &h->top_border_u[h->mbx*10], + h->left_border_u, h->c_stride); + diff = h->s.dsp.sse[1](NULL, h->eu, h->cu, h->c_stride, 8); + h->intra_pred_c[mode](h->cv, &h->top_border_v[h->mbx*10], + h->left_border_v, h->c_stride); + diff += h->s.dsp.sse[1](NULL, h->ev, h->cv, h->c_stride, 8); + if((bitcost[i]*h->lambda + diff) < min_cost) { + min_cost = bitcost[i]*h->lambda + diff; + pred_mode_uv = mode; + coded_mode[4] = i; + } + } + } + + /* reconstruct blocks */ + h->intra_pred_c[pred_mode_uv](h->cu, &h->top_border_u[h->mbx*10], + h->left_border_u, h->c_stride); + recon_block(h, h->eu, h->cu, h->c_stride, 4, ff_cavs_chroma_qp[h->qp], 1); + h->intra_pred_c[pred_mode_uv](h->cv, &h->top_border_v[h->mbx*10], + h->left_border_v, h->c_stride); + recon_block(h, h->ev, h->cv, h->c_stride, 5, ff_cavs_chroma_qp[h->qp], 1); + + ff_cavs_modify_mb_i(h,&pred_mode_uv); + + if(h->pic_type != FF_I_TYPE) + set_ue_golomb(pb, cbp_enc_tab[h->cbp][0] + 4); + for(block=0;block<4;block++) + if(coded_mode[block] == 4) + put_bits(pb,1,1); + else + put_bits(pb,3,coded_mode[block]); + set_ue_golomb(pb,coded_mode[4]); + if(h->pic_type == FF_I_TYPE) + set_ue_golomb(pb, cbp_enc_tab[h->cbp][0]); + encode_residual(h, 1); + ff_cavs_filter(h,I_8X8); + set_mv_intra(h); +} + +/***************************************************************************** + * + * inter P encoding + * + ****************************************************************************/ + +static inline int mv_cost(vector_t *mv, vector_t *pmv) { + int x = FFABS(mv->x - pmv->x); + int y = FFABS(mv->y - pmv->y); + return 2*(av_log2(x*2)+av_log2(y*2))+2; +} + +static inline int check_mv(AVSContext *h, int x, int y, vector_t *pmv, + vector_t *mvmin, int *min_cost) { + int diff; + + h->mv[MV_FWD_X0].x = x; + h->mv[MV_FWD_X0].y = y; + ff_cavs_inter(h, P_16X16); + diff = h->s.dsp.sse[0](NULL, h->ey, h->cy, h->l_stride, 16); + if((mv_cost(&h->mv[MV_FWD_X0],pmv)*h->lambda + diff) < *min_cost) { + *mvmin = h->mv[MV_FWD_X0]; + *min_cost = mv_cost(&h->mv[MV_FWD_X0],pmv)*h->lambda + diff; + return 1; + } + return 0; +} + +static void encode_mb_p(AVSContext *h) { + PutBitContext *pb = &h->s.pb; + int lscore = 0; + int cscore = 0; + + h->lambda = h->qp/8; + + /* test if we can skip */ + ff_cavs_mv(h, MV_FWD_X0, MV_FWD_C2, MV_PRED_PSKIP, BLK_16X16, 0); + ff_cavs_inter(h, P_SKIP); + recon_mb(h, &lscore, &cscore); + if(lscore < 6 && cscore < 7) { + h->skip_count++; + if(h->cbp) //there can be an undecimated block + ff_cavs_inter(h, P_SKIP); + set_intra_mode_default(h); + ff_cavs_filter(h,P_SKIP); + } else { + int hex[6][2] = {{-4,-8},{4,-8},{8,0},{4,8},{-4,8},{-8,0}}; + vector_t pmv[2], mv[2], mvmin; + int i, sub, x, y, min_cost[2], ref, count; + int newmv = 1; + + set_ue_golomb(pb,h->skip_count); + h->skip_count = h->cbp = 0; + + for(ref=0;ref<=h->mrefs;ref++) { + /* get predicted mv */ + h->mv[MV_FWD_X0].ref=mv[ref].ref=pmv[ref].ref=mvmin.ref = ref; + ff_cavs_mv(h, MV_FWD_X0, MV_FWD_C2, MV_PRED_ENC, BLK_16X16, ref); + pmv[ref] = h->mv[MV_FWD_X0]; + + /* predicted full-pel mv as reference */ + h->mv[MV_FWD_X0].x = mv[ref].x = mvmin.x = (pmv[ref].x+2) & ~3; + h->mv[MV_FWD_X0].y = mv[ref].y = mvmin.y = (pmv[ref].y+2) & ~3; + ff_cavs_inter(h, P_16X16); + min_cost[ref] = h->s.dsp.sse[0](NULL,h->ey,h->cy,h->l_stride,16) + + mv_cost(&mv[ref],&pmv[ref])*h->lambda; + + /* iterative hex search */ + count = ME_ITER; + while(newmv && count--) { + newmv = 0; + for(i=0;i<6;i++) + if(check_mv(h, mv[ref].x+hex[i][0], mv[ref].y+hex[i][1], + &pmv[ref], &mvmin, &min_cost[ref])) + newmv = 1; + mv[ref] = mvmin; + } + + /* refinement (full,half,quarter) */ + for(sub=4;sub>0;sub>>=1) { + for(x=-sub;x<=sub;x+=sub) + for(y=-sub;y<=sub;y+=sub) + if(x|y) + check_mv(h, mv[ref].x+x, mv[ref].y+y, + &pmv[ref], &mvmin, &min_cost[ref]); + mv[ref] = mvmin; + } + } + ref = (h->mrefs && (min_cost[1] < min_cost[0])); + if(min_cost[ref] < ME_THRES) { + h->mv[MV_FWD_X0] = mv[ref]; + set_mvs(&h->mv[MV_FWD_X0],BLK_16X16); + ff_cavs_inter(h, P_16X16); + recon_mb(h,&lscore,&cscore); + set_intra_mode_default(h); + set_ue_golomb(pb,0); //mb_type is P_16X16 + if(h->mrefs) + put_bits(pb,1,ref); + set_se_golomb(pb,mv[ref].x - pmv[ref].x); //mvd + set_se_golomb(pb,mv[ref].y - pmv[ref].y); + set_ue_golomb(pb, cbp_enc_tab[h->cbp][1]); + encode_residual(h, 0); + ff_cavs_filter(h,P_16X16); + } else { + encode_mb_i(h); + } + } +} + +/***************************************************************************** + * + * header encoding + * + ****************************************************************************/ + +static int encode_pic(AVSContext *h, Picture *p) { + MpegEncContext *s = &h->s; + int deblock = 1; + + if (!s->context_initialized) { + s->avctx->idct_algo = FF_IDCT_CAVS; + if (MPV_common_init(s) < 0) + return -1; + ff_init_scantable(s->dsp.idct_permutation,&h->scantable,ff_zigzag_direct); + } + put_bits(&s->pb,16,0xdeaf); //TODO correct bbv_dwlay + if(h->pic_type == FF_I_TYPE) + put_bits(&s->pb,1,0); //time_code not present + else + put_bits(&s->pb,2,1); //picture type P + s->avctx->get_buffer(s->avctx, (AVFrame *)&h->picture); + ff_cavs_init_pic(h); + put_bits(&s->pb,8,h->poc); + h->picture.poc = h->poc*2; + h->dist[0] = (h->picture.poc - h->DPB[0].poc + 512) % 512; + h->dist[1] = (h->picture.poc - h->DPB[1].poc + 512) % 512; + h->scale_den[0] = h->dist[0] ? 512/h->dist[0] : 0; + h->scale_den[1] = h->dist[1] ? 512/h->dist[1] : 0; + h->poc = (h->poc + 1) & 0x7F; + + if(s->low_delay) + set_ue_golomb(&s->pb,0); //TODO correct bbv_check_times? + put_bits(&s->pb,1,1); //set progressive + + put_bits(&s->pb,1,1); //top_field_first + put_bits(&s->pb,1,0); //repeat_first_field + put_bits(&s->pb,1,1); //set qp_fixed + put_bits(&s->pb,6,h->qp); + if(h->pic_type == FF_I_TYPE) { + put_bits(&s->pb,4,0); //reserved bits + } else { + h->mrefs = !!h->DPB[1].data[0] && (s->avctx->refs > 1); + if(!(h->pic_type == FF_B_TYPE)) + put_bits(&s->pb,1,!h->mrefs); //multiple references + put_bits(&s->pb,4,0); //reserved bits + put_bits(&s->pb,1,1); //skip_mode_flag + } + put_bits(&s->pb,1,!deblock); + if(deblock) { + if(h->s.avctx->deblockalpha || h->s.avctx->deblockbeta) { + put_bits(&s->pb,1,1); //deblocking params coded + set_se_golomb(&s->pb, h->s.avctx->deblockalpha); + set_se_golomb(&s->pb, h->s.avctx->deblockbeta); + } else { + put_bits(&s->pb,1,0); //deblocking params not coded + } + } + h->skip_count = 0; + do { + h->ey = p->data[0] + h->mby*16*h->l_stride + h->mbx*16; + h->eu = p->data[1] + h->mby*8*h->c_stride + h->mbx*8; + h->ev = p->data[2] + h->mby*8*h->c_stride + h->mbx*8; + ff_cavs_init_mb(h); + if(h->pic_type == FF_I_TYPE) + encode_mb_i(h); + else + encode_mb_p(h); + } while(ff_cavs_next_mb(h)); + if(h->DPB[1].data[0]) + s->avctx->release_buffer(s->avctx, (AVFrame *)&h->DPB[1]); + memcpy(&h->DPB[1], &h->DPB[0], sizeof(Picture)); + memcpy(&h->DPB[0], &h->picture, sizeof(Picture)); + return 0; +} + +static int encode_seq_header(AVSContext *h) { + MpegEncContext *s = &h->s; + int frame_rate_code = -1; + int i; + + s->width = h->s.avctx->width; + s->height = h->s.avctx->height; + put_bits(&s->pb,8,h->profile); + put_bits(&s->pb,8,h->level); + put_bits(&s->pb,1,1); //progressive sequence only + put_bits(&s->pb,14,s->width); + put_bits(&s->pb,14,s->height); + put_bits(&s->pb,2,1); // 1 = YUV 4:2:0, 2 = YUV 4:2:2 + put_bits(&s->pb,3,1); //sample_precision 8bits + put_bits(&s->pb,4,h->aspect_ratio); + for(i=0;i<15;i++) + if((ff_frame_rate_tab[i].den == s->avctx->time_base.num) && + (ff_frame_rate_tab[i].num == s->avctx->time_base.den)) + frame_rate_code = i; + if(frame_rate_code < 0) { + av_log(h->s.avctx, AV_LOG_ERROR, "unsupported framerate %d/%d\n", + s->avctx->time_base.den, s->avctx->time_base.num); + return -1; + } + put_bits(&s->pb,4,frame_rate_code & 0xF); + put_bits(&s->pb,18,0); //bit_rate_lower + put_bits(&s->pb,1,1); //marker_bit + put_bits(&s->pb,12,1); //bit_rate_upper + put_bits(&s->pb,1,s->low_delay); + put_bits(&s->pb,1,1); //marker_bit + put_bits(&s->pb,18,225); //bbv_buffer_size, enough for level 6.2 + put_bits(&s->pb,3,0); //reserved_bits + + h->mb_width = (s->width + 15) >> 4; + h->mb_height = (s->height + 15) >> 4; + if(!h->top_qp) { + ff_cavs_init_top_lines(h); + h->levels[0] = av_mallocz(6*64*sizeof(DCTELEM)); + h->runs[0] = av_mallocz(6*64); + for(i=1;i<6;i++) { + h->levels[i] = h->levels[i-1] + 64; + h->runs[i] = h->runs[i-1] + 64; + } + } + return 0; +} + +static int cavs_encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data){ + AVSContext *h = avctx->priv_data; + MpegEncContext * const s = &h->s; + AVFrame *pict = data; + const int width= s->avctx->width; + const int height= s->avctx->height; + int size, i, y; + + for(i=0; i<3; i++){ + int shift= !!i; + for(y=0; y<(height>>shift); y++) + memcpy(&h->input_picture.data[i][y * h->input_picture.linesize[i]], + &pict->data[i][y * pict->linesize[i]], + width>>shift); + } + if(avctx->gop_size == 0 || h->poc % avctx->gop_size == 0) { + h->input_picture.pict_type= I_TYPE; + h->pic_type= FF_I_TYPE; + h->input_picture.key_frame= 1; + } else { + h->input_picture.pict_type= P_TYPE; + h->pic_type= FF_P_TYPE; + } + + init_put_bits(&s->pb, buf, buf_size); + if(h->pic_type == FF_I_TYPE) { + if(h->DPB[0].data[0]) + avctx->release_buffer(avctx, (AVFrame *)&h->DPB[0]); + if(h->DPB[1].data[0]) + avctx->release_buffer(avctx, (AVFrame *)&h->DPB[1]); + put_bits(&s->pb,16,0); + put_bits(&s->pb,16,CAVS_START_CODE); + if(encode_seq_header(h) < 0) + return -1; + align_put_bits(&s->pb); + put_bits(&s->pb,16,0); + put_bits(&s->pb,16,PIC_I_START_CODE); + } else { + put_bits(&s->pb,16,0); + put_bits(&s->pb,16,PIC_PB_START_CODE); + } + encode_pic(h, &h->input_picture); + align_put_bits(&s->pb); + flush_put_bits(&s->pb); + size = put_bits_count(&s->pb)/8; + if(avctx->flags&CODEC_FLAG_PSNR) { + int i,x,y; + for(i=0;i<3;i++) { + int wi = s->width >> (!!i); + int he = s->height >> (!!i); + int64_t error = 0; + for(y=0;y<he;y++) + for(x=0;x<wi;x++) { + int d = h->picture.data[i][y*h->picture.linesize[i] + x] + - pict->data[i][y* pict->linesize[i] + x]; + error += d*d; + } + s->avctx->error[i] += error; + h->picture.error[i] = error; + } + } + memset(&h->picture,0,sizeof(Picture)); + return size; +} + +static void init_enc_vlc(enc_2dvlc_t *enc, const dec_2dvlc_t *dec, int count) { + int i,j; + + for(j=0;j<count;j++) { + memset(enc, -1, sizeof(enc_2dvlc_t)); + for(i=0;i<ESCAPE_CODE;i++) { + if(dec->rltab[i][0]) { + enc->rlcode[dec->rltab[i][1]-1][dec->rltab[i][0]-1] = i; + i++; //skip negative level + } else + enc->end_code = i; + } + enc->dec = dec; + enc++; + dec++; + } +} + +static int cavs_encode_init(AVCodecContext * avctx) { + AVSContext *h = avctx->priv_data; + MpegEncContext * const s = &h->s; + + if(avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL){ + av_log(avctx, AV_LOG_ERROR, "Chinese AVS encoder has not been tested for standard compliance.\n" + "use vstrict=-2 / -strict -2 to use it anyway\n"); + return -1; + } + if(avctx->cqp > -1) + h->qp = avctx->cqp; + else { + av_log(avctx, AV_LOG_ERROR, "fixed qp encoding only, use -cqp\n"); + return -1; + } + ff_cavs_init(avctx); + init_enc_vlc(intra_enc, ff_cavs_intra_dec, 7); + init_enc_vlc(inter_enc, ff_cavs_inter_dec, 7); + init_enc_vlc(chroma_enc, ff_cavs_chroma_dec, 5); + s->avctx = avctx; + s->avctx->get_buffer(s->avctx, (AVFrame *)&h->input_picture); + h->poc = 0; + return 0; +} + +AVCodec cavs_encoder = { + "cavs", + CODEC_TYPE_VIDEO, + CODEC_ID_CAVS, + sizeof(AVSContext), + cavs_encode_init, + cavs_encode_frame, + ff_cavs_end, +}; diff --git a/libavcodec/dsputil.h b/libavcodec/dsputil.h index 2d312e5..d5ede49 100644 --- a/libavcodec/dsputil.h +++ b/libavcodec/dsputil.h @@ -297,6 +297,9 @@ typedef struct DSPContext { void (*cavs_filter_cv)(uint8_t *pix, int stride, int alpha, int beta, int tc, int bs1, int bs2); void (*cavs_filter_ch)(uint8_t *pix, int stride, int alpha, int beta, int tc, int bs1, int bs2); void (*cavs_idct8_add)(uint8_t *dst, DCTELEM *block, int stride); + void (*cavs_sub_dct8)(uint8_t *src1, uint8_t *src2, DCTELEM *block, + int stride1, int stride2); + void (*cavs_quant)(DCTELEM *block, const uint16_t *norm,int mul, int bias); me_cmp_func pix_abs[2][4]; -- 1.7.1 --------------010605070306040906030502--