summaryrefslogtreecommitdiff
path: root/thirdparty/libtheora/decode.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/libtheora/decode.c')
-rw-r--r--thirdparty/libtheora/decode.c2943
1 files changed, 2943 insertions, 0 deletions
diff --git a/thirdparty/libtheora/decode.c b/thirdparty/libtheora/decode.c
new file mode 100644
index 0000000000..7be66463d8
--- /dev/null
+++ b/thirdparty/libtheora/decode.c
@@ -0,0 +1,2943 @@
+/********************************************************************
+ * *
+ * 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-2009 *
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function:
+ last mod: $Id: decode.c 16581 2009-09-25 22:56:16Z gmaxwell $
+
+ ********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+#include "decint.h"
+#if defined(OC_DUMP_IMAGES)
+# include <stdio.h>
+# include "png.h"
+#endif
+#if defined(HAVE_CAIRO)
+# include <cairo.h>
+#endif
+
+
+/*No post-processing.*/
+#define OC_PP_LEVEL_DISABLED (0)
+/*Keep track of DC qi for each block only.*/
+#define OC_PP_LEVEL_TRACKDCQI (1)
+/*Deblock the luma plane.*/
+#define OC_PP_LEVEL_DEBLOCKY (2)
+/*Dering the luma plane.*/
+#define OC_PP_LEVEL_DERINGY (3)
+/*Stronger luma plane deringing.*/
+#define OC_PP_LEVEL_SDERINGY (4)
+/*Deblock the chroma planes.*/
+#define OC_PP_LEVEL_DEBLOCKC (5)
+/*Dering the chroma planes.*/
+#define OC_PP_LEVEL_DERINGC (6)
+/*Stronger chroma plane deringing.*/
+#define OC_PP_LEVEL_SDERINGC (7)
+/*Maximum valid post-processing level.*/
+#define OC_PP_LEVEL_MAX (7)
+
+
+
+/*The mode alphabets for the various mode coding schemes.
+ Scheme 0 uses a custom alphabet, which is not stored in this table.*/
+static const unsigned char OC_MODE_ALPHABETS[7][OC_NMODES]={
+ /*Last MV dominates */
+ {
+ OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_MV,
+ OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV,
+ OC_MODE_INTER_MV_FOUR
+ },
+ {
+ OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_NOMV,
+ OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV,
+ OC_MODE_INTER_MV_FOUR
+ },
+ {
+ OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV,OC_MODE_INTER_MV_LAST2,
+ OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV,
+ OC_MODE_INTER_MV_FOUR
+ },
+ {
+ OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV,OC_MODE_INTER_NOMV,
+ OC_MODE_INTER_MV_LAST2,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,
+ OC_MODE_GOLDEN_MV,OC_MODE_INTER_MV_FOUR
+ },
+ /*No MV dominates.*/
+ {
+ OC_MODE_INTER_NOMV,OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,
+ OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV,
+ OC_MODE_INTER_MV_FOUR
+ },
+ {
+ OC_MODE_INTER_NOMV,OC_MODE_GOLDEN_NOMV,OC_MODE_INTER_MV_LAST,
+ OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_MV,
+ OC_MODE_INTER_MV_FOUR
+ },
+ /*Default ordering.*/
+ {
+ OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_INTER_MV,OC_MODE_INTER_MV_LAST,
+ OC_MODE_INTER_MV_LAST2,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV,
+ OC_MODE_INTER_MV_FOUR
+ }
+};
+
+
+/*The original DCT tokens are extended and reordered during the construction of
+ the Huffman tables.
+ The extension means more bits can be read with fewer calls to the bitpacker
+ during the Huffman decoding process (at the cost of larger Huffman tables),
+ and fewer tokens require additional extra bits (reducing the average storage
+ per decoded token).
+ The revised ordering reveals essential information in the token value
+ itself; specifically, whether or not there are additional extra bits to read
+ and the parameter to which those extra bits are applied.
+ The token is used to fetch a code word from the OC_DCT_CODE_WORD table below.
+ The extra bits are added into code word at the bit position inferred from the
+ token value, giving the final code word from which all required parameters
+ are derived.
+ The number of EOBs and the leading zero run length can be extracted directly.
+ The coefficient magnitude is optionally negated before extraction, according
+ to a 'flip' bit.*/
+
+/*The number of additional extra bits that are decoded with each of the
+ internal DCT tokens.*/
+static const unsigned char OC_INTERNAL_DCT_TOKEN_EXTRA_BITS[15]={
+ 12,4,3,3,4,4,5,5,8,8,8,8,3,3,6
+};
+
+/*Whether or not an internal token needs any additional extra bits.*/
+#define OC_DCT_TOKEN_NEEDS_MORE(token) \
+ (token<(sizeof(OC_INTERNAL_DCT_TOKEN_EXTRA_BITS)/ \
+ sizeof(*OC_INTERNAL_DCT_TOKEN_EXTRA_BITS)))
+
+/*This token (OC_DCT_REPEAT_RUN3_TOKEN) requires more than 8 extra bits.*/
+#define OC_DCT_TOKEN_FAT_EOB (0)
+
+/*The number of EOBs to use for an end-of-frame token.
+ Note: We want to set eobs to PTRDIFF_MAX here, but that requires C99, which
+ is not yet available everywhere; this should be equivalent.*/
+#define OC_DCT_EOB_FINISH (~(size_t)0>>1)
+
+/*The location of the (6) run legth bits in the code word.
+ These are placed at index 0 and given 8 bits (even though 6 would suffice)
+ because it may be faster to extract the lower byte on some platforms.*/
+#define OC_DCT_CW_RLEN_SHIFT (0)
+/*The location of the (12) EOB bits in the code word.*/
+#define OC_DCT_CW_EOB_SHIFT (8)
+/*The location of the (1) flip bit in the code word.
+ This must be right under the magnitude bits.*/
+#define OC_DCT_CW_FLIP_BIT (20)
+/*The location of the (11) token magnitude bits in the code word.
+ These must be last, and rely on a sign-extending right shift.*/
+#define OC_DCT_CW_MAG_SHIFT (21)
+
+/*Pack the given fields into a code word.*/
+#define OC_DCT_CW_PACK(_eobs,_rlen,_mag,_flip) \
+ ((_eobs)<<OC_DCT_CW_EOB_SHIFT| \
+ (_rlen)<<OC_DCT_CW_RLEN_SHIFT| \
+ (_flip)<<OC_DCT_CW_FLIP_BIT| \
+ (_mag)-(_flip)<<OC_DCT_CW_MAG_SHIFT)
+
+/*A special code word value that signals the end of the frame (a long EOB run
+ of zero).*/
+#define OC_DCT_CW_FINISH (0)
+
+/*The position at which to insert the extra bits in the code word.
+ We use this formulation because Intel has no useful cmov.
+ A real architecture would probably do better with two of those.
+ This translates to 11 instructions(!), and is _still_ faster than either a
+ table lookup (just barely) or the naive double-ternary implementation (which
+ gcc translates to a jump and a cmov).
+ This assumes OC_DCT_CW_RLEN_SHIFT is zero, but could easily be reworked if
+ you want to make one of the other shifts zero.*/
+#define OC_DCT_TOKEN_EB_POS(_token) \
+ ((OC_DCT_CW_EOB_SHIFT-OC_DCT_CW_MAG_SHIFT&-((_token)<2)) \
+ +(OC_DCT_CW_MAG_SHIFT&-((_token)<12)))
+
+/*The code words for each internal token.
+ See the notes at OC_DCT_TOKEN_MAP for the reasons why things are out of
+ order.*/
+static const ogg_int32_t OC_DCT_CODE_WORD[92]={
+ /*These tokens require additional extra bits for the EOB count.*/
+ /*OC_DCT_REPEAT_RUN3_TOKEN (12 extra bits)*/
+ OC_DCT_CW_FINISH,
+ /*OC_DCT_REPEAT_RUN2_TOKEN (4 extra bits)*/
+ OC_DCT_CW_PACK(16, 0, 0,0),
+ /*These tokens require additional extra bits for the magnitude.*/
+ /*OC_DCT_VAL_CAT5 (4 extra bits-1 already read)*/
+ OC_DCT_CW_PACK( 0, 0, 13,0),
+ OC_DCT_CW_PACK( 0, 0, 13,1),
+ /*OC_DCT_VAL_CAT6 (5 extra bits-1 already read)*/
+ OC_DCT_CW_PACK( 0, 0, 21,0),
+ OC_DCT_CW_PACK( 0, 0, 21,1),
+ /*OC_DCT_VAL_CAT7 (6 extra bits-1 already read)*/
+ OC_DCT_CW_PACK( 0, 0, 37,0),
+ OC_DCT_CW_PACK( 0, 0, 37,1),
+ /*OC_DCT_VAL_CAT8 (10 extra bits-2 already read)*/
+ OC_DCT_CW_PACK( 0, 0, 69,0),
+ OC_DCT_CW_PACK( 0, 0,325,0),
+ OC_DCT_CW_PACK( 0, 0, 69,1),
+ OC_DCT_CW_PACK( 0, 0,325,1),
+ /*These tokens require additional extra bits for the run length.*/
+ /*OC_DCT_RUN_CAT1C (4 extra bits-1 already read)*/
+ OC_DCT_CW_PACK( 0,10, +1,0),
+ OC_DCT_CW_PACK( 0,10, -1,0),
+ /*OC_DCT_ZRL_TOKEN (6 extra bits)
+ Flip is set to distinguish this from OC_DCT_CW_FINISH.*/
+ OC_DCT_CW_PACK( 0, 0, 0,1),
+ /*The remaining tokens require no additional extra bits.*/
+ /*OC_DCT_EOB1_TOKEN (0 extra bits)*/
+ OC_DCT_CW_PACK( 1, 0, 0,0),
+ /*OC_DCT_EOB2_TOKEN (0 extra bits)*/
+ OC_DCT_CW_PACK( 2, 0, 0,0),
+ /*OC_DCT_EOB3_TOKEN (0 extra bits)*/
+ OC_DCT_CW_PACK( 3, 0, 0,0),
+ /*OC_DCT_RUN_CAT1A (1 extra bit-1 already read)x5*/
+ OC_DCT_CW_PACK( 0, 1, +1,0),
+ OC_DCT_CW_PACK( 0, 1, -1,0),
+ OC_DCT_CW_PACK( 0, 2, +1,0),
+ OC_DCT_CW_PACK( 0, 2, -1,0),
+ OC_DCT_CW_PACK( 0, 3, +1,0),
+ OC_DCT_CW_PACK( 0, 3, -1,0),
+ OC_DCT_CW_PACK( 0, 4, +1,0),
+ OC_DCT_CW_PACK( 0, 4, -1,0),
+ OC_DCT_CW_PACK( 0, 5, +1,0),
+ OC_DCT_CW_PACK( 0, 5, -1,0),
+ /*OC_DCT_RUN_CAT2A (2 extra bits-2 already read)*/
+ OC_DCT_CW_PACK( 0, 1, +2,0),
+ OC_DCT_CW_PACK( 0, 1, +3,0),
+ OC_DCT_CW_PACK( 0, 1, -2,0),
+ OC_DCT_CW_PACK( 0, 1, -3,0),
+ /*OC_DCT_RUN_CAT1B (3 extra bits-3 already read)*/
+ OC_DCT_CW_PACK( 0, 6, +1,0),
+ OC_DCT_CW_PACK( 0, 7, +1,0),
+ OC_DCT_CW_PACK( 0, 8, +1,0),
+ OC_DCT_CW_PACK( 0, 9, +1,0),
+ OC_DCT_CW_PACK( 0, 6, -1,0),
+ OC_DCT_CW_PACK( 0, 7, -1,0),
+ OC_DCT_CW_PACK( 0, 8, -1,0),
+ OC_DCT_CW_PACK( 0, 9, -1,0),
+ /*OC_DCT_RUN_CAT2B (3 extra bits-3 already read)*/
+ OC_DCT_CW_PACK( 0, 2, +2,0),
+ OC_DCT_CW_PACK( 0, 3, +2,0),
+ OC_DCT_CW_PACK( 0, 2, +3,0),
+ OC_DCT_CW_PACK( 0, 3, +3,0),
+ OC_DCT_CW_PACK( 0, 2, -2,0),
+ OC_DCT_CW_PACK( 0, 3, -2,0),
+ OC_DCT_CW_PACK( 0, 2, -3,0),
+ OC_DCT_CW_PACK( 0, 3, -3,0),
+ /*OC_DCT_SHORT_ZRL_TOKEN (3 extra bits-3 already read)
+ Flip is set on the first one to distinguish it from OC_DCT_CW_FINISH.*/
+ OC_DCT_CW_PACK( 0, 0, 0,1),
+ OC_DCT_CW_PACK( 0, 1, 0,0),
+ OC_DCT_CW_PACK( 0, 2, 0,0),
+ OC_DCT_CW_PACK( 0, 3, 0,0),
+ OC_DCT_CW_PACK( 0, 4, 0,0),
+ OC_DCT_CW_PACK( 0, 5, 0,0),
+ OC_DCT_CW_PACK( 0, 6, 0,0),
+ OC_DCT_CW_PACK( 0, 7, 0,0),
+ /*OC_ONE_TOKEN (0 extra bits)*/
+ OC_DCT_CW_PACK( 0, 0, +1,0),
+ /*OC_MINUS_ONE_TOKEN (0 extra bits)*/
+ OC_DCT_CW_PACK( 0, 0, -1,0),
+ /*OC_TWO_TOKEN (0 extra bits)*/
+ OC_DCT_CW_PACK( 0, 0, +2,0),
+ /*OC_MINUS_TWO_TOKEN (0 extra bits)*/
+ OC_DCT_CW_PACK( 0, 0, -2,0),
+ /*OC_DCT_VAL_CAT2 (1 extra bit-1 already read)x4*/
+ OC_DCT_CW_PACK( 0, 0, +3,0),
+ OC_DCT_CW_PACK( 0, 0, -3,0),
+ OC_DCT_CW_PACK( 0, 0, +4,0),
+ OC_DCT_CW_PACK( 0, 0, -4,0),
+ OC_DCT_CW_PACK( 0, 0, +5,0),
+ OC_DCT_CW_PACK( 0, 0, -5,0),
+ OC_DCT_CW_PACK( 0, 0, +6,0),
+ OC_DCT_CW_PACK( 0, 0, -6,0),
+ /*OC_DCT_VAL_CAT3 (2 extra bits-2 already read)*/
+ OC_DCT_CW_PACK( 0, 0, +7,0),
+ OC_DCT_CW_PACK( 0, 0, +8,0),
+ OC_DCT_CW_PACK( 0, 0, -7,0),
+ OC_DCT_CW_PACK( 0, 0, -8,0),
+ /*OC_DCT_VAL_CAT4 (3 extra bits-3 already read)*/
+ OC_DCT_CW_PACK( 0, 0, +9,0),
+ OC_DCT_CW_PACK( 0, 0,+10,0),
+ OC_DCT_CW_PACK( 0, 0,+11,0),
+ OC_DCT_CW_PACK( 0, 0,+12,0),
+ OC_DCT_CW_PACK( 0, 0, -9,0),
+ OC_DCT_CW_PACK( 0, 0,-10,0),
+ OC_DCT_CW_PACK( 0, 0,-11,0),
+ OC_DCT_CW_PACK( 0, 0,-12,0),
+ /*OC_DCT_REPEAT_RUN1_TOKEN (3 extra bits-3 already read)*/
+ OC_DCT_CW_PACK( 8, 0, 0,0),
+ OC_DCT_CW_PACK( 9, 0, 0,0),
+ OC_DCT_CW_PACK(10, 0, 0,0),
+ OC_DCT_CW_PACK(11, 0, 0,0),
+ OC_DCT_CW_PACK(12, 0, 0,0),
+ OC_DCT_CW_PACK(13, 0, 0,0),
+ OC_DCT_CW_PACK(14, 0, 0,0),
+ OC_DCT_CW_PACK(15, 0, 0,0),
+ /*OC_DCT_REPEAT_RUN0_TOKEN (2 extra bits-2 already read)*/
+ OC_DCT_CW_PACK( 4, 0, 0,0),
+ OC_DCT_CW_PACK( 5, 0, 0,0),
+ OC_DCT_CW_PACK( 6, 0, 0,0),
+ OC_DCT_CW_PACK( 7, 0, 0,0),
+};
+
+
+
+static int oc_sb_run_unpack(oc_pack_buf *_opb){
+ long bits;
+ int ret;
+ /*Coding scheme:
+ Codeword Run Length
+ 0 1
+ 10x 2-3
+ 110x 4-5
+ 1110xx 6-9
+ 11110xxx 10-17
+ 111110xxxx 18-33
+ 111111xxxxxxxxxxxx 34-4129*/
+ bits=oc_pack_read1(_opb);
+ if(bits==0)return 1;
+ bits=oc_pack_read(_opb,2);
+ if((bits&2)==0)return 2+(int)bits;
+ else if((bits&1)==0){
+ bits=oc_pack_read1(_opb);
+ return 4+(int)bits;
+ }
+ bits=oc_pack_read(_opb,3);
+ if((bits&4)==0)return 6+(int)bits;
+ else if((bits&2)==0){
+ ret=10+((bits&1)<<2);
+ bits=oc_pack_read(_opb,2);
+ return ret+(int)bits;
+ }
+ else if((bits&1)==0){
+ bits=oc_pack_read(_opb,4);
+ return 18+(int)bits;
+ }
+ bits=oc_pack_read(_opb,12);
+ return 34+(int)bits;
+}
+
+static int oc_block_run_unpack(oc_pack_buf *_opb){
+ long bits;
+ long bits2;
+ /*Coding scheme:
+ Codeword Run Length
+ 0x 1-2
+ 10x 3-4
+ 110x 5-6
+ 1110xx 7-10
+ 11110xx 11-14
+ 11111xxxx 15-30*/
+ bits=oc_pack_read(_opb,2);
+ if((bits&2)==0)return 1+(int)bits;
+ else if((bits&1)==0){
+ bits=oc_pack_read1(_opb);
+ return 3+(int)bits;
+ }
+ bits=oc_pack_read(_opb,2);
+ if((bits&2)==0)return 5+(int)bits;
+ else if((bits&1)==0){
+ bits=oc_pack_read(_opb,2);
+ return 7+(int)bits;
+ }
+ bits=oc_pack_read(_opb,3);
+ if((bits&4)==0)return 11+bits;
+ bits2=oc_pack_read(_opb,2);
+ return 15+((bits&3)<<2)+bits2;
+}
+
+
+
+static int oc_dec_init(oc_dec_ctx *_dec,const th_info *_info,
+ const th_setup_info *_setup){
+ int qti;
+ int pli;
+ int qi;
+ int ret;
+ ret=oc_state_init(&_dec->state,_info,3);
+ if(ret<0)return ret;
+ ret=oc_huff_trees_copy(_dec->huff_tables,
+ (const oc_huff_node *const *)_setup->huff_tables);
+ if(ret<0){
+ oc_state_clear(&_dec->state);
+ return ret;
+ }
+ /*For each fragment, allocate one byte for every DCT coefficient token, plus
+ one byte for extra-bits for each token, plus one more byte for the long
+ EOB run, just in case it's the very last token and has a run length of
+ one.*/
+ _dec->dct_tokens=(unsigned char *)_ogg_malloc((64+64+1)*
+ _dec->state.nfrags*sizeof(_dec->dct_tokens[0]));
+ if(_dec->dct_tokens==NULL){
+ oc_huff_trees_clear(_dec->huff_tables);
+ oc_state_clear(&_dec->state);
+ return TH_EFAULT;
+ }
+ for(qi=0;qi<64;qi++)for(pli=0;pli<3;pli++)for(qti=0;qti<2;qti++){
+ _dec->state.dequant_tables[qi][pli][qti]=
+ _dec->state.dequant_table_data[qi][pli][qti];
+ }
+ oc_dequant_tables_init(_dec->state.dequant_tables,_dec->pp_dc_scale,
+ &_setup->qinfo);
+ for(qi=0;qi<64;qi++){
+ int qsum;
+ qsum=0;
+ for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){
+ qsum+=_dec->state.dequant_tables[qti][pli][qi][12]+
+ _dec->state.dequant_tables[qti][pli][qi][17]+
+ _dec->state.dequant_tables[qti][pli][qi][18]+
+ _dec->state.dequant_tables[qti][pli][qi][24]<<(pli==0);
+ }
+ _dec->pp_sharp_mod[qi]=-(qsum>>11);
+ }
+ memcpy(_dec->state.loop_filter_limits,_setup->qinfo.loop_filter_limits,
+ sizeof(_dec->state.loop_filter_limits));
+ _dec->pp_level=OC_PP_LEVEL_DISABLED;
+ _dec->dc_qis=NULL;
+ _dec->variances=NULL;
+ _dec->pp_frame_data=NULL;
+ _dec->stripe_cb.ctx=NULL;
+ _dec->stripe_cb.stripe_decoded=NULL;
+#if defined(HAVE_CAIRO)
+ _dec->telemetry=0;
+ _dec->telemetry_bits=0;
+ _dec->telemetry_qi=0;
+ _dec->telemetry_mbmode=0;
+ _dec->telemetry_mv=0;
+ _dec->telemetry_frame_data=NULL;
+#endif
+ return 0;
+}
+
+static void oc_dec_clear(oc_dec_ctx *_dec){
+#if defined(HAVE_CAIRO)
+ _ogg_free(_dec->telemetry_frame_data);
+#endif
+ _ogg_free(_dec->pp_frame_data);
+ _ogg_free(_dec->variances);
+ _ogg_free(_dec->dc_qis);
+ _ogg_free(_dec->dct_tokens);
+ oc_huff_trees_clear(_dec->huff_tables);
+ oc_state_clear(&_dec->state);
+}
+
+
+static int oc_dec_frame_header_unpack(oc_dec_ctx *_dec){
+ long val;
+ /*Check to make sure this is a data packet.*/
+ val=oc_pack_read1(&_dec->opb);
+ if(val!=0)return TH_EBADPACKET;
+ /*Read in the frame type (I or P).*/
+ val=oc_pack_read1(&_dec->opb);
+ _dec->state.frame_type=(int)val;
+ /*Read in the qi list.*/
+ val=oc_pack_read(&_dec->opb,6);
+ _dec->state.qis[0]=(unsigned char)val;
+ val=oc_pack_read1(&_dec->opb);
+ if(!val)_dec->state.nqis=1;
+ else{
+ val=oc_pack_read(&_dec->opb,6);
+ _dec->state.qis[1]=(unsigned char)val;
+ val=oc_pack_read1(&_dec->opb);
+ if(!val)_dec->state.nqis=2;
+ else{
+ val=oc_pack_read(&_dec->opb,6);
+ _dec->state.qis[2]=(unsigned char)val;
+ _dec->state.nqis=3;
+ }
+ }
+ if(_dec->state.frame_type==OC_INTRA_FRAME){
+ /*Keyframes have 3 unused configuration bits, holdovers from VP3 days.
+ Most of the other unused bits in the VP3 headers were eliminated.
+ I don't know why these remain.*/
+ /*I wanted to eliminate wasted bits, but not all config wiggle room
+ --Monty.*/
+ val=oc_pack_read(&_dec->opb,3);
+ if(val!=0)return TH_EIMPL;
+ }
+ return 0;
+}
+
+/*Mark all fragments as coded and in OC_MODE_INTRA.
+ This also builds up the coded fragment list (in coded order), and clears the
+ uncoded fragment list.
+ It does not update the coded macro block list nor the super block flags, as
+ those are not used when decoding INTRA frames.*/
+static void oc_dec_mark_all_intra(oc_dec_ctx *_dec){
+ const oc_sb_map *sb_maps;
+ const oc_sb_flags *sb_flags;
+ oc_fragment *frags;
+ ptrdiff_t *coded_fragis;
+ ptrdiff_t ncoded_fragis;
+ ptrdiff_t prev_ncoded_fragis;
+ unsigned nsbs;
+ unsigned sbi;
+ int pli;
+ coded_fragis=_dec->state.coded_fragis;
+ prev_ncoded_fragis=ncoded_fragis=0;
+ sb_maps=(const oc_sb_map *)_dec->state.sb_maps;
+ sb_flags=_dec->state.sb_flags;
+ frags=_dec->state.frags;
+ sbi=nsbs=0;
+ for(pli=0;pli<3;pli++){
+ nsbs+=_dec->state.fplanes[pli].nsbs;
+ for(;sbi<nsbs;sbi++){
+ int quadi;
+ for(quadi=0;quadi<4;quadi++)if(sb_flags[sbi].quad_valid&1<<quadi){
+ int bi;
+ for(bi=0;bi<4;bi++){
+ ptrdiff_t fragi;
+ fragi=sb_maps[sbi][quadi][bi];
+ if(fragi>=0){
+ frags[fragi].coded=1;
+ frags[fragi].mb_mode=OC_MODE_INTRA;
+ coded_fragis[ncoded_fragis++]=fragi;
+ }
+ }
+ }
+ }
+ _dec->state.ncoded_fragis[pli]=ncoded_fragis-prev_ncoded_fragis;
+ prev_ncoded_fragis=ncoded_fragis;
+ }
+ _dec->state.ntotal_coded_fragis=ncoded_fragis;
+}
+
+/*Decodes the bit flags indicating whether each super block is partially coded
+ or not.
+ Return: The number of partially coded super blocks.*/
+static unsigned oc_dec_partial_sb_flags_unpack(oc_dec_ctx *_dec){
+ oc_sb_flags *sb_flags;
+ unsigned nsbs;
+ unsigned sbi;
+ unsigned npartial;
+ unsigned run_count;
+ long val;
+ int flag;
+ val=oc_pack_read1(&_dec->opb);
+ flag=(int)val;
+ sb_flags=_dec->state.sb_flags;
+ nsbs=_dec->state.nsbs;
+ sbi=npartial=0;
+ while(sbi<nsbs){
+ int full_run;
+ run_count=oc_sb_run_unpack(&_dec->opb);
+ full_run=run_count>=4129;
+ do{
+ sb_flags[sbi].coded_partially=flag;
+ sb_flags[sbi].coded_fully=0;
+ npartial+=flag;
+ sbi++;
+ }
+ while(--run_count>0&&sbi<nsbs);
+ if(full_run&&sbi<nsbs){
+ val=oc_pack_read1(&_dec->opb);
+ flag=(int)val;
+ }
+ else flag=!flag;
+ }
+ /*TODO: run_count should be 0 here.
+ If it's not, we should issue a warning of some kind.*/
+ return npartial;
+}
+
+/*Decodes the bit flags for whether or not each non-partially-coded super
+ block is fully coded or not.
+ This function should only be called if there is at least one
+ non-partially-coded super block.
+ Return: The number of partially coded super blocks.*/
+static void oc_dec_coded_sb_flags_unpack(oc_dec_ctx *_dec){
+ oc_sb_flags *sb_flags;
+ unsigned nsbs;
+ unsigned sbi;
+ unsigned run_count;
+ long val;
+ int flag;
+ sb_flags=_dec->state.sb_flags;
+ nsbs=_dec->state.nsbs;
+ /*Skip partially coded super blocks.*/
+ for(sbi=0;sb_flags[sbi].coded_partially;sbi++);
+ val=oc_pack_read1(&_dec->opb);
+ flag=(int)val;
+ do{
+ int full_run;
+ run_count=oc_sb_run_unpack(&_dec->opb);
+ full_run=run_count>=4129;
+ for(;sbi<nsbs;sbi++){
+ if(sb_flags[sbi].coded_partially)continue;
+ if(run_count--<=0)break;
+ sb_flags[sbi].coded_fully=flag;
+ }
+ if(full_run&&sbi<nsbs){
+ val=oc_pack_read1(&_dec->opb);
+ flag=(int)val;
+ }
+ else flag=!flag;
+ }
+ while(sbi<nsbs);
+ /*TODO: run_count should be 0 here.
+ If it's not, we should issue a warning of some kind.*/
+}
+
+static void oc_dec_coded_flags_unpack(oc_dec_ctx *_dec){
+ const oc_sb_map *sb_maps;
+ const oc_sb_flags *sb_flags;
+ oc_fragment *frags;
+ unsigned nsbs;
+ unsigned sbi;
+ unsigned npartial;
+ long val;
+ int pli;
+ int flag;
+ int run_count;
+ ptrdiff_t *coded_fragis;
+ ptrdiff_t *uncoded_fragis;
+ ptrdiff_t ncoded_fragis;
+ ptrdiff_t nuncoded_fragis;
+ ptrdiff_t prev_ncoded_fragis;
+ npartial=oc_dec_partial_sb_flags_unpack(_dec);
+ if(npartial<_dec->state.nsbs)oc_dec_coded_sb_flags_unpack(_dec);
+ if(npartial>0){
+ val=oc_pack_read1(&_dec->opb);
+ flag=!(int)val;
+ }
+ else flag=0;
+ sb_maps=(const oc_sb_map *)_dec->state.sb_maps;
+ sb_flags=_dec->state.sb_flags;
+ frags=_dec->state.frags;
+ sbi=nsbs=run_count=0;
+ coded_fragis=_dec->state.coded_fragis;
+ uncoded_fragis=coded_fragis+_dec->state.nfrags;
+ prev_ncoded_fragis=ncoded_fragis=nuncoded_fragis=0;
+ for(pli=0;pli<3;pli++){
+ nsbs+=_dec->state.fplanes[pli].nsbs;
+ for(;sbi<nsbs;sbi++){
+ int quadi;
+ for(quadi=0;quadi<4;quadi++)if(sb_flags[sbi].quad_valid&1<<quadi){
+ int bi;
+ for(bi=0;bi<4;bi++){
+ ptrdiff_t fragi;
+ fragi=sb_maps[sbi][quadi][bi];
+ if(fragi>=0){
+ int coded;
+ if(sb_flags[sbi].coded_fully)coded=1;
+ else if(!sb_flags[sbi].coded_partially)coded=0;
+ else{
+ if(run_count<=0){
+ run_count=oc_block_run_unpack(&_dec->opb);
+ flag=!flag;
+ }
+ run_count--;
+ coded=flag;
+ }
+ if(coded)coded_fragis[ncoded_fragis++]=fragi;
+ else *(uncoded_fragis-++nuncoded_fragis)=fragi;
+ frags[fragi].coded=coded;
+ }
+ }
+ }
+ }
+ _dec->state.ncoded_fragis[pli]=ncoded_fragis-prev_ncoded_fragis;
+ prev_ncoded_fragis=ncoded_fragis;
+ }
+ _dec->state.ntotal_coded_fragis=ncoded_fragis;
+ /*TODO: run_count should be 0 here.
+ If it's not, we should issue a warning of some kind.*/
+}
+
+
+
+typedef int (*oc_mode_unpack_func)(oc_pack_buf *_opb);
+
+static int oc_vlc_mode_unpack(oc_pack_buf *_opb){
+ long val;
+ int i;
+ for(i=0;i<7;i++){
+ val=oc_pack_read1(_opb);
+ if(!val)break;
+ }
+ return i;
+}
+
+static int oc_clc_mode_unpack(oc_pack_buf *_opb){
+ long val;
+ val=oc_pack_read(_opb,3);
+ return (int)val;
+}
+
+/*Unpacks the list of macro block modes for INTER frames.*/
+static void oc_dec_mb_modes_unpack(oc_dec_ctx *_dec){
+ const oc_mb_map *mb_maps;
+ signed char *mb_modes;
+ const oc_fragment *frags;
+ const unsigned char *alphabet;
+ unsigned char scheme0_alphabet[8];
+ oc_mode_unpack_func mode_unpack;
+ size_t nmbs;
+ size_t mbi;
+ long val;
+ int mode_scheme;
+ val=oc_pack_read(&_dec->opb,3);
+ mode_scheme=(int)val;
+ if(mode_scheme==0){
+ int mi;
+ /*Just in case, initialize the modes to something.
+ If the bitstream doesn't contain each index exactly once, it's likely
+ corrupt and the rest of the packet is garbage anyway, but this way we
+ won't crash, and we'll decode SOMETHING.*/
+ /*LOOP VECTORIZES*/
+ for(mi=0;mi<OC_NMODES;mi++)scheme0_alphabet[mi]=OC_MODE_INTER_NOMV;
+ for(mi=0;mi<OC_NMODES;mi++){
+ val=oc_pack_read(&_dec->opb,3);
+ scheme0_alphabet[val]=OC_MODE_ALPHABETS[6][mi];
+ }
+ alphabet=scheme0_alphabet;
+ }
+ else alphabet=OC_MODE_ALPHABETS[mode_scheme-1];
+ if(mode_scheme==7)mode_unpack=oc_clc_mode_unpack;
+ else mode_unpack=oc_vlc_mode_unpack;
+ mb_modes=_dec->state.mb_modes;
+ mb_maps=(const oc_mb_map *)_dec->state.mb_maps;
+ nmbs=_dec->state.nmbs;
+ frags=_dec->state.frags;
+ for(mbi=0;mbi<nmbs;mbi++){
+ if(mb_modes[mbi]!=OC_MODE_INVALID){
+ int bi;
+ /*Check for a coded luma block in this macro block.*/
+ for(bi=0;bi<4&&!frags[mb_maps[mbi][0][bi]].coded;bi++);
+ /*We found one, decode a mode.*/
+ if(bi<4)mb_modes[mbi]=alphabet[(*mode_unpack)(&_dec->opb)];
+ /*There were none: INTER_NOMV is forced.*/
+ else mb_modes[mbi]=OC_MODE_INTER_NOMV;
+ }
+ }
+}
+
+
+
+typedef int (*oc_mv_comp_unpack_func)(oc_pack_buf *_opb);
+
+static int oc_vlc_mv_comp_unpack(oc_pack_buf *_opb){
+ long bits;
+ int mask;
+ int mv;
+ bits=oc_pack_read(_opb,3);
+ switch(bits){
+ case 0:return 0;
+ case 1:return 1;
+ case 2:return -1;
+ case 3:
+ case 4:{
+ mv=(int)(bits-1);
+ bits=oc_pack_read1(_opb);
+ }break;
+ /*case 5:
+ case 6:
+ case 7:*/
+ default:{
+ mv=1<<bits-3;
+ bits=oc_pack_read(_opb,bits-2);
+ mv+=(int)(bits>>1);
+ bits&=1;
+ }break;
+ }
+ mask=-(int)bits;
+ return mv+mask^mask;
+}
+
+static int oc_clc_mv_comp_unpack(oc_pack_buf *_opb){
+ long bits;
+ int mask;
+ int mv;
+ bits=oc_pack_read(_opb,6);
+ mv=(int)bits>>1;
+ mask=-((int)bits&1);
+ return mv+mask^mask;
+}
+
+/*Unpacks the list of motion vectors for INTER frames, and propagtes the macro
+ block modes and motion vectors to the individual fragments.*/
+static void oc_dec_mv_unpack_and_frag_modes_fill(oc_dec_ctx *_dec){
+ const oc_mb_map *mb_maps;
+ const signed char *mb_modes;
+ oc_set_chroma_mvs_func set_chroma_mvs;
+ oc_mv_comp_unpack_func mv_comp_unpack;
+ oc_fragment *frags;
+ oc_mv *frag_mvs;
+ const unsigned char *map_idxs;
+ int map_nidxs;
+ oc_mv last_mv[2];
+ oc_mv cbmvs[4];
+ size_t nmbs;
+ size_t mbi;
+ long val;
+ set_chroma_mvs=OC_SET_CHROMA_MVS_TABLE[_dec->state.info.pixel_fmt];
+ val=oc_pack_read1(&_dec->opb);
+ mv_comp_unpack=val?oc_clc_mv_comp_unpack:oc_vlc_mv_comp_unpack;
+ map_idxs=OC_MB_MAP_IDXS[_dec->state.info.pixel_fmt];
+ map_nidxs=OC_MB_MAP_NIDXS[_dec->state.info.pixel_fmt];
+ memset(last_mv,0,sizeof(last_mv));
+ frags=_dec->state.frags;
+ frag_mvs=_dec->state.frag_mvs;
+ mb_maps=(const oc_mb_map *)_dec->state.mb_maps;
+ mb_modes=_dec->state.mb_modes;
+ nmbs=_dec->state.nmbs;
+ for(mbi=0;mbi<nmbs;mbi++){
+ int mb_mode;
+ mb_mode=mb_modes[mbi];
+ if(mb_mode!=OC_MODE_INVALID){
+ oc_mv mbmv;
+ ptrdiff_t fragi;
+ int coded[13];
+ int codedi;
+ int ncoded;
+ int mapi;
+ int mapii;
+ /*Search for at least one coded fragment.*/
+ ncoded=mapii=0;
+ do{
+ mapi=map_idxs[mapii];
+ fragi=mb_maps[mbi][mapi>>2][mapi&3];
+ if(frags[fragi].coded)coded[ncoded++]=mapi;
+ }
+ while(++mapii<map_nidxs);
+ if(ncoded<=0)continue;
+ switch(mb_mode){
+ case OC_MODE_INTER_MV_FOUR:{
+ oc_mv lbmvs[4];
+ int bi;
+ /*Mark the tail of the list, so we don't accidentally go past it.*/
+ coded[ncoded]=-1;
+ for(bi=codedi=0;bi<4;bi++){
+ if(coded[codedi]==bi){
+ codedi++;
+ fragi=mb_maps[mbi][0][bi];
+ frags[fragi].mb_mode=mb_mode;
+ lbmvs[bi][0]=(signed char)(*mv_comp_unpack)(&_dec->opb);
+ lbmvs[bi][1]=(signed char)(*mv_comp_unpack)(&_dec->opb);
+ memcpy(frag_mvs[fragi],lbmvs[bi],sizeof(lbmvs[bi]));
+ }
+ else lbmvs[bi][0]=lbmvs[bi][1]=0;
+ }
+ if(codedi>0){
+ memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1]));
+ memcpy(last_mv[0],lbmvs[coded[codedi-1]],sizeof(last_mv[0]));
+ }
+ if(codedi<ncoded){
+ (*set_chroma_mvs)(cbmvs,(const oc_mv *)lbmvs);
+ for(;codedi<ncoded;codedi++){
+ mapi=coded[codedi];
+ bi=mapi&3;
+ fragi=mb_maps[mbi][mapi>>2][bi];
+ frags[fragi].mb_mode=mb_mode;
+ memcpy(frag_mvs[fragi],cbmvs[bi],sizeof(cbmvs[bi]));
+ }
+ }
+ }break;
+ case OC_MODE_INTER_MV:{
+ memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1]));
+ mbmv[0]=last_mv[0][0]=(signed char)(*mv_comp_unpack)(&_dec->opb);
+ mbmv[1]=last_mv[0][1]=(signed char)(*mv_comp_unpack)(&_dec->opb);
+ }break;
+ case OC_MODE_INTER_MV_LAST:memcpy(mbmv,last_mv[0],sizeof(mbmv));break;
+ case OC_MODE_INTER_MV_LAST2:{
+ memcpy(mbmv,last_mv[1],sizeof(mbmv));
+ memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1]));
+ memcpy(last_mv[0],mbmv,sizeof(last_mv[0]));
+ }break;
+ case OC_MODE_GOLDEN_MV:{
+ mbmv[0]=(signed char)(*mv_comp_unpack)(&_dec->opb);
+ mbmv[1]=(signed char)(*mv_comp_unpack)(&_dec->opb);
+ }break;
+ default:memset(mbmv,0,sizeof(mbmv));break;
+ }
+ /*4MV mode fills in the fragments itself.
+ For all other modes we can use this common code.*/
+ if(mb_mode!=OC_MODE_INTER_MV_FOUR){
+ for(codedi=0;codedi<ncoded;codedi++){
+ mapi=coded[codedi];
+ fragi=mb_maps[mbi][mapi>>2][mapi&3];
+ frags[fragi].mb_mode=mb_mode;
+ memcpy(frag_mvs[fragi],mbmv,sizeof(mbmv));
+ }
+ }
+ }
+ }
+}
+
+static void oc_dec_block_qis_unpack(oc_dec_ctx *_dec){
+ oc_fragment *frags;
+ const ptrdiff_t *coded_fragis;
+ ptrdiff_t ncoded_fragis;
+ ptrdiff_t fragii;
+ ptrdiff_t fragi;
+ ncoded_fragis=_dec->state.ntotal_coded_fragis;
+ if(ncoded_fragis<=0)return;
+ frags=_dec->state.frags;
+ coded_fragis=_dec->state.coded_fragis;
+ if(_dec->state.nqis==1){
+ /*If this frame has only a single qi value, then just use it for all coded
+ fragments.*/
+ for(fragii=0;fragii<ncoded_fragis;fragii++){
+ frags[coded_fragis[fragii]].qii=0;
+ }
+ }
+ else{
+ long val;
+ int flag;
+ int nqi1;
+ int run_count;
+ /*Otherwise, we decode a qi index for each fragment, using two passes of
+ the same binary RLE scheme used for super-block coded bits.
+ The first pass marks each fragment as having a qii of 0 or greater than
+ 0, and the second pass (if necessary), distinguishes between a qii of
+ 1 and 2.
+ At first we just store the qii in the fragment.
+ After all the qii's are decoded, we make a final pass to replace them
+ with the corresponding qi's for this frame.*/
+ val=oc_pack_read1(&_dec->opb);
+ flag=(int)val;
+ nqi1=0;
+ fragii=0;
+ while(fragii<ncoded_fragis){
+ int full_run;
+ run_count=oc_sb_run_unpack(&_dec->opb);
+ full_run=run_count>=4129;
+ do{
+ frags[coded_fragis[fragii++]].qii=flag;
+ nqi1+=flag;
+ }
+ while(--run_count>0&&fragii<ncoded_fragis);
+ if(full_run&&fragii<ncoded_fragis){
+ val=oc_pack_read1(&_dec->opb);
+ flag=(int)val;
+ }
+ else flag=!flag;
+ }
+ /*TODO: run_count should be 0 here.
+ If it's not, we should issue a warning of some kind.*/
+ /*If we have 3 different qi's for this frame, and there was at least one
+ fragment with a non-zero qi, make the second pass.*/
+ if(_dec->state.nqis==3&&nqi1>0){
+ /*Skip qii==0 fragments.*/
+ for(fragii=0;frags[coded_fragis[fragii]].qii==0;fragii++);
+ val=oc_pack_read1(&_dec->opb);
+ flag=(int)val;
+ do{
+ int full_run;
+ run_count=oc_sb_run_unpack(&_dec->opb);
+ full_run=run_count>=4129;
+ for(;fragii<ncoded_fragis;fragii++){
+ fragi=coded_fragis[fragii];
+ if(frags[fragi].qii==0)continue;
+ if(run_count--<=0)break;
+ frags[fragi].qii+=flag;
+ }
+ if(full_run&&fragii<ncoded_fragis){
+ val=oc_pack_read1(&_dec->opb);
+ flag=(int)val;
+ }
+ else flag=!flag;
+ }
+ while(fragii<ncoded_fragis);
+ /*TODO: run_count should be 0 here.
+ If it's not, we should issue a warning of some kind.*/
+ }
+ }
+}
+
+
+
+/*Unpacks the DC coefficient tokens.
+ Unlike when unpacking the AC coefficient tokens, we actually need to decode
+ the DC coefficient values now so that we can do DC prediction.
+ _huff_idx: The index of the Huffman table to use for each color plane.
+ _ntoks_left: The number of tokens left to be decoded in each color plane for
+ each coefficient.
+ This is updated as EOB tokens and zero run tokens are decoded.
+ Return: The length of any outstanding EOB run.*/
+static ptrdiff_t oc_dec_dc_coeff_unpack(oc_dec_ctx *_dec,int _huff_idxs[2],
+ ptrdiff_t _ntoks_left[3][64]){
+ unsigned char *dct_tokens;
+ oc_fragment *frags;
+ const ptrdiff_t *coded_fragis;
+ ptrdiff_t ncoded_fragis;
+ ptrdiff_t fragii;
+ ptrdiff_t eobs;
+ ptrdiff_t ti;
+ int pli;
+ dct_tokens=_dec->dct_tokens;
+ frags=_dec->state.frags;
+ coded_fragis=_dec->state.coded_fragis;
+ ncoded_fragis=fragii=eobs=ti=0;
+ for(pli=0;pli<3;pli++){
+ ptrdiff_t run_counts[64];
+ ptrdiff_t eob_count;
+ ptrdiff_t eobi;
+ int rli;
+ ncoded_fragis+=_dec->state.ncoded_fragis[pli];
+ memset(run_counts,0,sizeof(run_counts));
+ _dec->eob_runs[pli][0]=eobs;
+ _dec->ti0[pli][0]=ti;
+ /*Continue any previous EOB run, if there was one.*/
+ eobi=eobs;
+ if(ncoded_fragis-fragii<eobi)eobi=ncoded_fragis-fragii;
+ eob_count=eobi;
+ eobs-=eobi;
+ while(eobi-->0)frags[coded_fragis[fragii++]].dc=0;
+ while(fragii<ncoded_fragis){
+ int token;
+ int cw;
+ int eb;
+ int skip;
+ token=oc_huff_token_decode(&_dec->opb,
+ _dec->huff_tables[_huff_idxs[pli+1>>1]]);
+ dct_tokens[ti++]=(unsigned char)token;
+ if(OC_DCT_TOKEN_NEEDS_MORE(token)){
+ eb=(int)oc_pack_read(&_dec->opb,
+ OC_INTERNAL_DCT_TOKEN_EXTRA_BITS[token]);
+ dct_tokens[ti++]=(unsigned char)eb;
+ if(token==OC_DCT_TOKEN_FAT_EOB)dct_tokens[ti++]=(unsigned char)(eb>>8);
+ eb<<=OC_DCT_TOKEN_EB_POS(token);
+ }
+ else eb=0;
+ cw=OC_DCT_CODE_WORD[token]+eb;
+ eobs=cw>>OC_DCT_CW_EOB_SHIFT&0xFFF;
+ if(cw==OC_DCT_CW_FINISH)eobs=OC_DCT_EOB_FINISH;
+ if(eobs){
+ eobi=OC_MINI(eobs,ncoded_fragis-fragii);
+ eob_count+=eobi;
+ eobs-=eobi;
+ while(eobi-->0)frags[coded_fragis[fragii++]].dc=0;
+ }
+ else{
+ int coeff;
+ skip=(unsigned char)(cw>>OC_DCT_CW_RLEN_SHIFT);
+ cw^=-(cw&1<<OC_DCT_CW_FLIP_BIT);
+ coeff=cw>>OC_DCT_CW_MAG_SHIFT;
+ if(skip)coeff=0;
+ run_counts[skip]++;
+ frags[coded_fragis[fragii++]].dc=coeff;
+ }
+ }
+ /*Add the total EOB count to the longest run length.*/
+ run_counts[63]+=eob_count;
+ /*And convert the run_counts array to a moment table.*/
+ for(rli=63;rli-->0;)run_counts[rli]+=run_counts[rli+1];
+ /*Finally, subtract off the number of coefficients that have been
+ accounted for by runs started in this coefficient.*/
+ for(rli=64;rli-->0;)_ntoks_left[pli][rli]-=run_counts[rli];
+ }
+ _dec->dct_tokens_count=ti;
+ return eobs;
+}
+
+/*Unpacks the AC coefficient tokens.
+ This can completely discard coefficient values while unpacking, and so is
+ somewhat simpler than unpacking the DC coefficient tokens.
+ _huff_idx: The index of the Huffman table to use for each color plane.
+ _ntoks_left: The number of tokens left to be decoded in each color plane for
+ each coefficient.
+ This is updated as EOB tokens and zero run tokens are decoded.
+ _eobs: The length of any outstanding EOB run from previous
+ coefficients.
+ Return: The length of any outstanding EOB run.*/
+static int oc_dec_ac_coeff_unpack(oc_dec_ctx *_dec,int _zzi,int _huff_idxs[2],
+ ptrdiff_t _ntoks_left[3][64],ptrdiff_t _eobs){
+ unsigned char *dct_tokens;
+ ptrdiff_t ti;
+ int pli;
+ dct_tokens=_dec->dct_tokens;
+ ti=_dec->dct_tokens_count;
+ for(pli=0;pli<3;pli++){
+ ptrdiff_t run_counts[64];
+ ptrdiff_t eob_count;
+ size_t ntoks_left;
+ size_t ntoks;
+ int rli;
+ _dec->eob_runs[pli][_zzi]=_eobs;
+ _dec->ti0[pli][_zzi]=ti;
+ ntoks_left=_ntoks_left[pli][_zzi];
+ memset(run_counts,0,sizeof(run_counts));
+ eob_count=0;
+ ntoks=0;
+ while(ntoks+_eobs<ntoks_left){
+ int token;
+ int cw;
+ int eb;
+ int skip;
+ ntoks+=_eobs;
+ eob_count+=_eobs;
+ token=oc_huff_token_decode(&_dec->opb,
+ _dec->huff_tables[_huff_idxs[pli+1>>1]]);
+ dct_tokens[ti++]=(unsigned char)token;
+ if(OC_DCT_TOKEN_NEEDS_MORE(token)){
+ eb=(int)oc_pack_read(&_dec->opb,
+ OC_INTERNAL_DCT_TOKEN_EXTRA_BITS[token]);
+ dct_tokens[ti++]=(unsigned char)eb;
+ if(token==OC_DCT_TOKEN_FAT_EOB)dct_tokens[ti++]=(unsigned char)(eb>>8);
+ eb<<=OC_DCT_TOKEN_EB_POS(token);
+ }
+ else eb=0;
+ cw=OC_DCT_CODE_WORD[token]+eb;
+ skip=(unsigned char)(cw>>OC_DCT_CW_RLEN_SHIFT);
+ _eobs=cw>>OC_DCT_CW_EOB_SHIFT&0xFFF;
+ if(cw==OC_DCT_CW_FINISH)_eobs=OC_DCT_EOB_FINISH;
+ if(_eobs==0){
+ run_counts[skip]++;
+ ntoks++;
+ }
+ }
+ /*Add the portion of the last EOB run actually used by this coefficient.*/
+ eob_count+=ntoks_left-ntoks;
+ /*And remove it from the remaining EOB count.*/
+ _eobs-=ntoks_left-ntoks;
+ /*Add the total EOB count to the longest run length.*/
+ run_counts[63]+=eob_count;
+ /*And convert the run_counts array to a moment table.*/
+ for(rli=63;rli-->0;)run_counts[rli]+=run_counts[rli+1];
+ /*Finally, subtract off the number of coefficients that have been
+ accounted for by runs started in this coefficient.*/
+ for(rli=64-_zzi;rli-->0;)_ntoks_left[pli][_zzi+rli]-=run_counts[rli];
+ }
+ _dec->dct_tokens_count=ti;
+ return _eobs;
+}
+
+/*Tokens describing the DCT coefficients that belong to each fragment are
+ stored in the bitstream grouped by coefficient, not by fragment.
+
+ This means that we either decode all the tokens in order, building up a
+ separate coefficient list for each fragment as we go, and then go back and
+ do the iDCT on each fragment, or we have to create separate lists of tokens
+ for each coefficient, so that we can pull the next token required off the
+ head of the appropriate list when decoding a specific fragment.
+
+ The former was VP3's choice, and it meant 2*w*h extra storage for all the
+ decoded coefficient values.
+
+ We take the second option, which lets us store just one to three bytes per
+ token (generally far fewer than the number of coefficients, due to EOB
+ tokens and zero runs), and which requires us to only maintain a counter for
+ each of the 64 coefficients, instead of a counter for every fragment to
+ determine where the next token goes.
+
+ We actually use 3 counters per coefficient, one for each color plane, so we
+ can decode all color planes simultaneously.
+ This lets color conversion, etc., be done as soon as a full MCU (one or
+ two super block rows) is decoded, while the image data is still in cache.*/
+
+static void oc_dec_residual_tokens_unpack(oc_dec_ctx *_dec){
+ static const unsigned char OC_HUFF_LIST_MAX[5]={1,6,15,28,64};
+ ptrdiff_t ntoks_left[3][64];
+ int huff_idxs[2];
+ ptrdiff_t eobs;
+ long val;
+ int pli;
+ int zzi;
+ int hgi;
+ for(pli=0;pli<3;pli++)for(zzi=0;zzi<64;zzi++){
+ ntoks_left[pli][zzi]=_dec->state.ncoded_fragis[pli];
+ }
+ val=oc_pack_read(&_dec->opb,4);
+ huff_idxs[0]=(int)val;
+ val=oc_pack_read(&_dec->opb,4);
+ huff_idxs[1]=(int)val;
+ _dec->eob_runs[0][0]=0;
+ eobs=oc_dec_dc_coeff_unpack(_dec,huff_idxs,ntoks_left);
+#if defined(HAVE_CAIRO)
+ _dec->telemetry_dc_bytes=oc_pack_bytes_left(&_dec->opb);
+#endif
+ val=oc_pack_read(&_dec->opb,4);
+ huff_idxs[0]=(int)val;
+ val=oc_pack_read(&_dec->opb,4);
+ huff_idxs[1]=(int)val;
+ zzi=1;
+ for(hgi=1;hgi<5;hgi++){
+ huff_idxs[0]+=16;
+ huff_idxs[1]+=16;
+ for(;zzi<OC_HUFF_LIST_MAX[hgi];zzi++){
+ eobs=oc_dec_ac_coeff_unpack(_dec,zzi,huff_idxs,ntoks_left,eobs);
+ }
+ }
+ /*TODO: eobs should be exactly zero, or 4096 or greater.
+ The second case occurs when an EOB run of size zero is encountered, which
+ gets treated as an infinite EOB run (where infinity is PTRDIFF_MAX).
+ If neither of these conditions holds, then a warning should be issued.*/
+}
+
+
+static int oc_dec_postprocess_init(oc_dec_ctx *_dec){
+ /*pp_level 0: disabled; free any memory used and return*/
+ if(_dec->pp_level<=OC_PP_LEVEL_DISABLED){
+ if(_dec->dc_qis!=NULL){
+ _ogg_free(_dec->dc_qis);
+ _dec->dc_qis=NULL;
+ _ogg_free(_dec->variances);
+ _dec->variances=NULL;
+ _ogg_free(_dec->pp_frame_data);
+ _dec->pp_frame_data=NULL;
+ }
+ return 1;
+ }
+ if(_dec->dc_qis==NULL){
+ /*If we haven't been tracking DC quantization indices, there's no point in
+ starting now.*/
+ if(_dec->state.frame_type!=OC_INTRA_FRAME)return 1;
+ _dec->dc_qis=(unsigned char *)_ogg_malloc(
+ _dec->state.nfrags*sizeof(_dec->dc_qis[0]));
+ if(_dec->dc_qis==NULL)return 1;
+ memset(_dec->dc_qis,_dec->state.qis[0],_dec->state.nfrags);
+ }
+ else{
+ unsigned char *dc_qis;
+ const ptrdiff_t *coded_fragis;
+ ptrdiff_t ncoded_fragis;
+ ptrdiff_t fragii;
+ unsigned char qi0;
+ /*Update the DC quantization index of each coded block.*/
+ dc_qis=_dec->dc_qis;
+ coded_fragis=_dec->state.coded_fragis;
+ ncoded_fragis=_dec->state.ncoded_fragis[0]+
+ _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2];
+ qi0=(unsigned char)_dec->state.qis[0];
+ for(fragii=0;fragii<ncoded_fragis;fragii++){
+ dc_qis[coded_fragis[fragii]]=qi0;
+ }
+ }
+ /*pp_level 1: Stop after updating DC quantization indices.*/
+ if(_dec->pp_level<=OC_PP_LEVEL_TRACKDCQI){
+ if(_dec->variances!=NULL){
+ _ogg_free(_dec->variances);
+ _dec->variances=NULL;
+ _ogg_free(_dec->pp_frame_data);
+ _dec->pp_frame_data=NULL;
+ }
+ return 1;
+ }
+ if(_dec->variances==NULL){
+ size_t frame_sz;
+ size_t c_sz;
+ int c_w;
+ int c_h;
+ frame_sz=_dec->state.info.frame_width*(size_t)_dec->state.info.frame_height;
+ c_w=_dec->state.info.frame_width>>!(_dec->state.info.pixel_fmt&1);
+ c_h=_dec->state.info.frame_height>>!(_dec->state.info.pixel_fmt&2);
+ c_sz=c_w*(size_t)c_h;
+ /*Allocate space for the chroma planes, even if we're not going to use
+ them; this simplifies allocation state management, though it may waste
+ memory on the few systems that don't overcommit pages.*/
+ frame_sz+=c_sz<<1;
+ _dec->pp_frame_data=(unsigned char *)_ogg_malloc(
+ frame_sz*sizeof(_dec->pp_frame_data[0]));
+ _dec->variances=(int *)_ogg_malloc(
+ _dec->state.nfrags*sizeof(_dec->variances[0]));
+ if(_dec->variances==NULL||_dec->pp_frame_data==NULL){
+ _ogg_free(_dec->pp_frame_data);
+ _dec->pp_frame_data=NULL;
+ _ogg_free(_dec->variances);
+ _dec->variances=NULL;
+ return 1;
+ }
+ /*Force an update of the PP buffer pointers.*/
+ _dec->pp_frame_state=0;
+ }
+ /*Update the PP buffer pointers if necessary.*/
+ if(_dec->pp_frame_state!=1+(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC)){
+ if(_dec->pp_level<OC_PP_LEVEL_DEBLOCKC){
+ /*If chroma processing is disabled, just use the PP luma plane.*/
+ _dec->pp_frame_buf[0].width=_dec->state.info.frame_width;
+ _dec->pp_frame_buf[0].height=_dec->state.info.frame_height;
+ _dec->pp_frame_buf[0].stride=-_dec->pp_frame_buf[0].width;
+ _dec->pp_frame_buf[0].data=_dec->pp_frame_data+
+ (1-_dec->pp_frame_buf[0].height)*(ptrdiff_t)_dec->pp_frame_buf[0].stride;
+ }
+ else{
+ size_t y_sz;
+ size_t c_sz;
+ int c_w;
+ int c_h;
+ /*Otherwise, set up pointers to all three PP planes.*/
+ y_sz=_dec->state.info.frame_width*(size_t)_dec->state.info.frame_height;
+ c_w=_dec->state.info.frame_width>>!(_dec->state.info.pixel_fmt&1);
+ c_h=_dec->state.info.frame_height>>!(_dec->state.info.pixel_fmt&2);
+ c_sz=c_w*(size_t)c_h;
+ _dec->pp_frame_buf[0].width=_dec->state.info.frame_width;
+ _dec->pp_frame_buf[0].height=_dec->state.info.frame_height;
+ _dec->pp_frame_buf[0].stride=_dec->pp_frame_buf[0].width;
+ _dec->pp_frame_buf[0].data=_dec->pp_frame_data;
+ _dec->pp_frame_buf[1].width=c_w;
+ _dec->pp_frame_buf[1].height=c_h;
+ _dec->pp_frame_buf[1].stride=_dec->pp_frame_buf[1].width;
+ _dec->pp_frame_buf[1].data=_dec->pp_frame_buf[0].data+y_sz;
+ _dec->pp_frame_buf[2].width=c_w;
+ _dec->pp_frame_buf[2].height=c_h;
+ _dec->pp_frame_buf[2].stride=_dec->pp_frame_buf[2].width;
+ _dec->pp_frame_buf[2].data=_dec->pp_frame_buf[1].data+c_sz;
+ oc_ycbcr_buffer_flip(_dec->pp_frame_buf,_dec->pp_frame_buf);
+ }
+ _dec->pp_frame_state=1+(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC);
+ }
+ /*If we're not processing chroma, copy the reference frame's chroma planes.*/
+ if(_dec->pp_level<OC_PP_LEVEL_DEBLOCKC){
+ memcpy(_dec->pp_frame_buf+1,
+ _dec->state.ref_frame_bufs[_dec->state.ref_frame_idx[OC_FRAME_SELF]]+1,
+ sizeof(_dec->pp_frame_buf[1])*2);
+ }
+ return 0;
+}
+
+
+
+typedef struct{
+ int bounding_values[256];
+ ptrdiff_t ti[3][64];
+ ptrdiff_t eob_runs[3][64];
+ const ptrdiff_t *coded_fragis[3];
+ const ptrdiff_t *uncoded_fragis[3];
+ ptrdiff_t ncoded_fragis[3];
+ ptrdiff_t nuncoded_fragis[3];
+ const ogg_uint16_t *dequant[3][3][2];
+ int fragy0[3];
+ int fragy_end[3];
+ int pred_last[3][3];
+ int mcu_nvfrags;
+ int loop_filter;
+ int pp_level;
+}oc_dec_pipeline_state;
+
+
+
+/*Initialize the main decoding pipeline.*/
+static void oc_dec_pipeline_init(oc_dec_ctx *_dec,
+ oc_dec_pipeline_state *_pipe){
+ const ptrdiff_t *coded_fragis;
+ const ptrdiff_t *uncoded_fragis;
+ int pli;
+ int qii;
+ int qti;
+ /*If chroma is sub-sampled in the vertical direction, we have to decode two
+ super block rows of Y' for each super block row of Cb and Cr.*/
+ _pipe->mcu_nvfrags=4<<!(_dec->state.info.pixel_fmt&2);
+ /*Initialize the token and extra bits indices for each plane and
+ coefficient.*/
+ memcpy(_pipe->ti,_dec->ti0,sizeof(_pipe->ti));
+ /*Also copy over the initial the EOB run counts.*/
+ memcpy(_pipe->eob_runs,_dec->eob_runs,sizeof(_pipe->eob_runs));
+ /*Set up per-plane pointers to the coded and uncoded fragments lists.*/
+ coded_fragis=_dec->state.coded_fragis;
+ uncoded_fragis=coded_fragis+_dec->state.nfrags;
+ for(pli=0;pli<3;pli++){
+ ptrdiff_t ncoded_fragis;
+ _pipe->coded_fragis[pli]=coded_fragis;
+ _pipe->uncoded_fragis[pli]=uncoded_fragis;
+ ncoded_fragis=_dec->state.ncoded_fragis[pli];
+ coded_fragis+=ncoded_fragis;
+ uncoded_fragis+=ncoded_fragis-_dec->state.fplanes[pli].nfrags;
+ }
+ /*Set up condensed quantizer tables.*/
+ for(pli=0;pli<3;pli++){
+ for(qii=0;qii<_dec->state.nqis;qii++){
+ for(qti=0;qti<2;qti++){
+ _pipe->dequant[pli][qii][qti]=
+ _dec->state.dequant_tables[_dec->state.qis[qii]][pli][qti];
+ }
+ }
+ }
+ /*Set the previous DC predictor to 0 for all color planes and frame types.*/
+ memset(_pipe->pred_last,0,sizeof(_pipe->pred_last));
+ /*Initialize the bounding value array for the loop filter.*/
+ _pipe->loop_filter=!oc_state_loop_filter_init(&_dec->state,
+ _pipe->bounding_values);
+ /*Initialize any buffers needed for post-processing.
+ We also save the current post-processing level, to guard against the user
+ changing it from a callback.*/
+ if(!oc_dec_postprocess_init(_dec))_pipe->pp_level=_dec->pp_level;
+ /*If we don't have enough information to post-process, disable it, regardless
+ of the user-requested level.*/
+ else{
+ _pipe->pp_level=OC_PP_LEVEL_DISABLED;
+ memcpy(_dec->pp_frame_buf,
+ _dec->state.ref_frame_bufs[_dec->state.ref_frame_idx[OC_FRAME_SELF]],
+ sizeof(_dec->pp_frame_buf[0])*3);
+ }
+}
+
+/*Undo the DC prediction in a single plane of an MCU (one or two super block
+ rows).
+ As a side effect, the number of coded and uncoded fragments in this plane of
+ the MCU is also computed.*/
+static void oc_dec_dc_unpredict_mcu_plane(oc_dec_ctx *_dec,
+ oc_dec_pipeline_state *_pipe,int _pli){
+ const oc_fragment_plane *fplane;
+ oc_fragment *frags;
+ int *pred_last;
+ ptrdiff_t ncoded_fragis;
+ ptrdiff_t fragi;
+ int fragx;
+ int fragy;
+ int fragy0;
+ int fragy_end;
+ int nhfrags;
+ /*Compute the first and last fragment row of the current MCU for this
+ plane.*/
+ fplane=_dec->state.fplanes+_pli;
+ fragy0=_pipe->fragy0[_pli];
+ fragy_end=_pipe->fragy_end[_pli];
+ nhfrags=fplane->nhfrags;
+ pred_last=_pipe->pred_last[_pli];
+ frags=_dec->state.frags;
+ ncoded_fragis=0;
+ fragi=fplane->froffset+fragy0*(ptrdiff_t)nhfrags;
+ for(fragy=fragy0;fragy<fragy_end;fragy++){
+ if(fragy==0){
+ /*For the first row, all of the cases reduce to just using the previous
+ predictor for the same reference frame.*/
+ for(fragx=0;fragx<nhfrags;fragx++,fragi++){
+ if(frags[fragi].coded){
+ int ref;
+ ref=OC_FRAME_FOR_MODE(frags[fragi].mb_mode);
+ pred_last[ref]=frags[fragi].dc+=pred_last[ref];
+ ncoded_fragis++;
+ }
+ }
+ }
+ else{
+ oc_fragment *u_frags;
+ int l_ref;
+ int ul_ref;
+ int u_ref;
+ u_frags=frags-nhfrags;
+ l_ref=-1;
+ ul_ref=-1;
+ u_ref=u_frags[fragi].coded?OC_FRAME_FOR_MODE(u_frags[fragi].mb_mode):-1;
+ for(fragx=0;fragx<nhfrags;fragx++,fragi++){
+ int ur_ref;
+ if(fragx+1>=nhfrags)ur_ref=-1;
+ else{
+ ur_ref=u_frags[fragi+1].coded?
+ OC_FRAME_FOR_MODE(u_frags[fragi+1].mb_mode):-1;
+ }
+ if(frags[fragi].coded){
+ int pred;
+ int ref;
+ ref=OC_FRAME_FOR_MODE(frags[fragi].mb_mode);
+ /*We break out a separate case based on which of our neighbors use
+ the same reference frames.
+ This is somewhat faster than trying to make a generic case which
+ handles all of them, since it reduces lots of poorly predicted
+ jumps to one switch statement, and also lets a number of the
+ multiplications be optimized out by strength reduction.*/
+ switch((l_ref==ref)|(ul_ref==ref)<<1|
+ (u_ref==ref)<<2|(ur_ref==ref)<<3){
+ default:pred=pred_last[ref];break;
+ case 1:
+ case 3:pred=frags[fragi-1].dc;break;
+ case 2:pred=u_frags[fragi-1].dc;break;
+ case 4:
+ case 6:
+ case 12:pred=u_frags[fragi].dc;break;
+ case 5:pred=(frags[fragi-1].dc+u_frags[fragi].dc)/2;break;
+ case 8:pred=u_frags[fragi+1].dc;break;
+ case 9:
+ case 11:
+ case 13:{
+ pred=(75*frags[fragi-1].dc+53*u_frags[fragi+1].dc)/128;
+ }break;
+ case 10:pred=(u_frags[fragi-1].dc+u_frags[fragi+1].dc)/2;break;
+ case 14:{
+ pred=(3*(u_frags[fragi-1].dc+u_frags[fragi+1].dc)
+ +10*u_frags[fragi].dc)/16;
+ }break;
+ case 7:
+ case 15:{
+ int p0;
+ int p1;
+ int p2;
+ p0=frags[fragi-1].dc;
+ p1=u_frags[fragi-1].dc;
+ p2=u_frags[fragi].dc;
+ pred=(29*(p0+p2)-26*p1)/32;
+ if(abs(pred-p2)>128)pred=p2;
+ else if(abs(pred-p0)>128)pred=p0;
+ else if(abs(pred-p1)>128)pred=p1;
+ }break;
+ }
+ pred_last[ref]=frags[fragi].dc+=pred;
+ ncoded_fragis++;
+ l_ref=ref;
+ }
+ else l_ref=-1;
+ ul_ref=u_ref;
+ u_ref=ur_ref;
+ }
+ }
+ }
+ _pipe->ncoded_fragis[_pli]=ncoded_fragis;
+ /*Also save the number of uncoded fragments so we know how many to copy.*/
+ _pipe->nuncoded_fragis[_pli]=
+ (fragy_end-fragy0)*(ptrdiff_t)nhfrags-ncoded_fragis;
+}
+
+/*Reconstructs all coded fragments in a single MCU (one or two super block
+ rows).
+ This requires that each coded fragment have a proper macro block mode and
+ motion vector (if not in INTRA mode), and have it's DC value decoded, with
+ the DC prediction process reversed, and the number of coded and uncoded
+ fragments in this plane of the MCU be counted.
+ The token lists for each color plane and coefficient should also be filled
+ in, along with initial token offsets, extra bits offsets, and EOB run
+ counts.*/
+static void oc_dec_frags_recon_mcu_plane(oc_dec_ctx *_dec,
+ oc_dec_pipeline_state *_pipe,int _pli){
+ unsigned char *dct_tokens;
+ const unsigned char *dct_fzig_zag;
+ ogg_uint16_t dc_quant[2];
+ const oc_fragment *frags;
+ const ptrdiff_t *coded_fragis;
+ ptrdiff_t ncoded_fragis;
+ ptrdiff_t fragii;
+ ptrdiff_t *ti;
+ ptrdiff_t *eob_runs;
+ int qti;
+ dct_tokens=_dec->dct_tokens;
+ dct_fzig_zag=_dec->state.opt_data.dct_fzig_zag;
+ frags=_dec->state.frags;
+ coded_fragis=_pipe->coded_fragis[_pli];
+ ncoded_fragis=_pipe->ncoded_fragis[_pli];
+ ti=_pipe->ti[_pli];
+ eob_runs=_pipe->eob_runs[_pli];
+ for(qti=0;qti<2;qti++)dc_quant[qti]=_pipe->dequant[_pli][0][qti][0];
+ for(fragii=0;fragii<ncoded_fragis;fragii++){
+ /*This array is made one element larger because the zig-zag index array
+ uses the final element as a dumping ground for out-of-range indices
+ to protect us from buffer overflow.*/
+ OC_ALIGN8(ogg_int16_t dct_coeffs[65]);
+ const ogg_uint16_t *ac_quant;
+ ptrdiff_t fragi;
+ int last_zzi;
+ int zzi;
+ fragi=coded_fragis[fragii];
+ for(zzi=0;zzi<64;zzi++)dct_coeffs[zzi]=0;
+ qti=frags[fragi].mb_mode!=OC_MODE_INTRA;
+ ac_quant=_pipe->dequant[_pli][frags[fragi].qii][qti];
+ /*Decode the AC coefficients.*/
+ for(zzi=0;zzi<64;){
+ int token;
+ last_zzi=zzi;
+ if(eob_runs[zzi]){
+ eob_runs[zzi]--;
+ break;
+ }
+ else{
+ ptrdiff_t eob;
+ int cw;
+ int rlen;
+ int coeff;
+ int lti;
+ lti=ti[zzi];
+ token=dct_tokens[lti++];
+ cw=OC_DCT_CODE_WORD[token];
+ /*These parts could be done branchless, but the branches are fairly
+ predictable and the C code translates into more than a few
+ instructions, so it's worth it to avoid them.*/
+ if(OC_DCT_TOKEN_NEEDS_MORE(token)){
+ cw+=dct_tokens[lti++]<<OC_DCT_TOKEN_EB_POS(token);
+ }
+ eob=cw>>OC_DCT_CW_EOB_SHIFT&0xFFF;
+ if(token==OC_DCT_TOKEN_FAT_EOB){
+ eob+=dct_tokens[lti++]<<8;
+ if(eob==0)eob=OC_DCT_EOB_FINISH;
+ }
+ rlen=(unsigned char)(cw>>OC_DCT_CW_RLEN_SHIFT);
+ cw^=-(cw&1<<OC_DCT_CW_FLIP_BIT);
+ coeff=cw>>OC_DCT_CW_MAG_SHIFT;
+ eob_runs[zzi]=eob;
+ ti[zzi]=lti;
+ zzi+=rlen;
+ dct_coeffs[dct_fzig_zag[zzi]]=(ogg_int16_t)(coeff*(int)ac_quant[zzi]);
+ zzi+=!eob;
+ }
+ }
+ /*TODO: zzi should be exactly 64 here.
+ If it's not, we should report some kind of warning.*/
+ zzi=OC_MINI(zzi,64);
+ dct_coeffs[0]=(ogg_int16_t)frags[fragi].dc;
+ /*last_zzi is always initialized.
+ If your compiler thinks otherwise, it is dumb.*/
+ oc_state_frag_recon(&_dec->state,fragi,_pli,
+ dct_coeffs,last_zzi,dc_quant[qti]);
+ }
+ _pipe->coded_fragis[_pli]+=ncoded_fragis;
+ /*Right now the reconstructed MCU has only the coded blocks in it.*/
+ /*TODO: We make the decision here to always copy the uncoded blocks into it
+ from the reference frame.
+ We could also copy the coded blocks back over the reference frame, if we
+ wait for an additional MCU to be decoded, which might be faster if only a
+ small number of blocks are coded.
+ However, this introduces more latency, creating a larger cache footprint.
+ It's unknown which decision is better, but this one results in simpler
+ code, and the hard case (high bitrate, high resolution) is handled
+ correctly.*/
+ /*Copy the uncoded blocks from the previous reference frame.*/
+ _pipe->uncoded_fragis[_pli]-=_pipe->nuncoded_fragis[_pli];
+ oc_state_frag_copy_list(&_dec->state,_pipe->uncoded_fragis[_pli],
+ _pipe->nuncoded_fragis[_pli],OC_FRAME_SELF,OC_FRAME_PREV,_pli);
+}
+
+/*Filter a horizontal block edge.*/
+static void oc_filter_hedge(unsigned char *_dst,int _dst_ystride,
+ const unsigned char *_src,int _src_ystride,int _qstep,int _flimit,
+ int *_variance0,int *_variance1){
+ unsigned char *rdst;
+ const unsigned char *rsrc;
+ unsigned char *cdst;
+ const unsigned char *csrc;
+ int r[10];
+ int sum0;
+ int sum1;
+ int bx;
+ int by;
+ rdst=_dst;
+ rsrc=_src;
+ for(bx=0;bx<8;bx++){
+ cdst=rdst;
+ csrc=rsrc;
+ for(by=0;by<10;by++){
+ r[by]=*csrc;
+ csrc+=_src_ystride;
+ }
+ sum0=sum1=0;
+ for(by=0;by<4;by++){
+ sum0+=abs(r[by+1]-r[by]);
+ sum1+=abs(r[by+5]-r[by+6]);
+ }
+ *_variance0+=OC_MINI(255,sum0);
+ *_variance1+=OC_MINI(255,sum1);
+ if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){
+ *cdst=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3);
+ cdst+=_dst_ystride;
+ *cdst=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3);
+ cdst+=_dst_ystride;
+ for(by=0;by<4;by++){
+ *cdst=(unsigned char)(r[by]+r[by+1]+r[by+2]+r[by+3]*2+
+ r[by+4]+r[by+5]+r[by+6]+4>>3);
+ cdst+=_dst_ystride;
+ }
+ *cdst=(unsigned char)(r[4]+r[5]+r[6]+r[7]*2+r[8]+r[9]*2+4>>3);
+ cdst+=_dst_ystride;
+ *cdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3);
+ }
+ else{
+ for(by=1;by<=8;by++){
+ *cdst=(unsigned char)r[by];
+ cdst+=_dst_ystride;
+ }
+ }
+ rdst++;
+ rsrc++;
+ }
+}
+
+/*Filter a vertical block edge.*/
+static void oc_filter_vedge(unsigned char *_dst,int _dst_ystride,
+ int _qstep,int _flimit,int *_variances){
+ unsigned char *rdst;
+ const unsigned char *rsrc;
+ unsigned char *cdst;
+ int r[10];
+ int sum0;
+ int sum1;
+ int bx;
+ int by;
+ cdst=_dst;
+ for(by=0;by<8;by++){
+ rsrc=cdst-1;
+ rdst=cdst;
+ for(bx=0;bx<10;bx++)r[bx]=*rsrc++;
+ sum0=sum1=0;
+ for(bx=0;bx<4;bx++){
+ sum0+=abs(r[bx+1]-r[bx]);
+ sum1+=abs(r[bx+5]-r[bx+6]);
+ }
+ _variances[0]+=OC_MINI(255,sum0);
+ _variances[1]+=OC_MINI(255,sum1);
+ if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){
+ *rdst++=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3);
+ *rdst++=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3);
+ for(bx=0;bx<4;bx++){
+ *rdst++=(unsigned char)(r[bx]+r[bx+1]+r[bx+2]+r[bx+3]*2+
+ r[bx+4]+r[bx+5]+r[bx+6]+4>>3);
+ }
+ *rdst++=(unsigned char)(r[4]+r[5]+r[6]+r[7]*2+r[8]+r[9]*2+4>>3);
+ *rdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3);
+ }
+ cdst+=_dst_ystride;
+ }
+}
+
+static void oc_dec_deblock_frag_rows(oc_dec_ctx *_dec,
+ th_img_plane *_dst,th_img_plane *_src,int _pli,int _fragy0,
+ int _fragy_end){
+ oc_fragment_plane *fplane;
+ int *variance;
+ unsigned char *dc_qi;
+ unsigned char *dst;
+ const unsigned char *src;
+ ptrdiff_t froffset;
+ int dst_ystride;
+ int src_ystride;
+ int nhfrags;
+ int width;
+ int notstart;
+ int notdone;
+ int flimit;
+ int qstep;
+ int y_end;
+ int y;
+ int x;
+ _dst+=_pli;
+ _src+=_pli;
+ fplane=_dec->state.fplanes+_pli;
+ nhfrags=fplane->nhfrags;
+ froffset=fplane->froffset+_fragy0*(ptrdiff_t)nhfrags;
+ variance=_dec->variances+froffset;
+ dc_qi=_dec->dc_qis+froffset;
+ notstart=_fragy0>0;
+ notdone=_fragy_end<fplane->nvfrags;
+ /*We want to clear an extra row of variances, except at the end.*/
+ memset(variance+(nhfrags&-notstart),0,
+ (_fragy_end+notdone-_fragy0-notstart)*(nhfrags*sizeof(variance[0])));
+ /*Except for the first time, we want to point to the middle of the row.*/
+ y=(_fragy0<<3)+(notstart<<2);
+ dst_ystride=_dst->stride;
+ src_ystride=_src->stride;
+ dst=_dst->data+y*(ptrdiff_t)dst_ystride;
+ src=_src->data+y*(ptrdiff_t)src_ystride;
+ width=_dst->width;
+ for(;y<4;y++){
+ memcpy(dst,src,width*sizeof(dst[0]));
+ dst+=dst_ystride;
+ src+=src_ystride;
+ }
+ /*We also want to skip the last row in the frame for this loop.*/
+ y_end=_fragy_end-!notdone<<3;
+ for(;y<y_end;y+=8){
+ qstep=_dec->pp_dc_scale[*dc_qi];
+ flimit=(qstep*3)>>2;
+ oc_filter_hedge(dst,dst_ystride,src-src_ystride,src_ystride,
+ qstep,flimit,variance,variance+nhfrags);
+ variance++;
+ dc_qi++;
+ for(x=8;x<width;x+=8){
+ qstep=_dec->pp_dc_scale[*dc_qi];
+ flimit=(qstep*3)>>2;
+ oc_filter_hedge(dst+x,dst_ystride,src+x-src_ystride,src_ystride,
+ qstep,flimit,variance,variance+nhfrags);
+ oc_filter_vedge(dst+x-(dst_ystride<<2)-4,dst_ystride,
+ qstep,flimit,variance-1);
+ variance++;
+ dc_qi++;
+ }
+ dst+=dst_ystride<<3;
+ src+=src_ystride<<3;
+ }
+ /*And finally, handle the last row in the frame, if it's in the range.*/
+ if(!notdone){
+ int height;
+ height=_dst->height;
+ for(;y<height;y++){
+ memcpy(dst,src,width*sizeof(dst[0]));
+ dst+=dst_ystride;
+ src+=src_ystride;
+ }
+ /*Filter the last row of vertical block edges.*/
+ dc_qi++;
+ for(x=8;x<width;x+=8){
+ qstep=_dec->pp_dc_scale[*dc_qi++];
+ flimit=(qstep*3)>>2;
+ oc_filter_vedge(dst+x-(dst_ystride<<3)-4,dst_ystride,
+ qstep,flimit,variance++);
+ }
+ }
+}
+
+static void oc_dering_block(unsigned char *_idata,int _ystride,int _b,
+ int _dc_scale,int _sharp_mod,int _strong){
+ static const unsigned char OC_MOD_MAX[2]={24,32};
+ static const unsigned char OC_MOD_SHIFT[2]={1,0};
+ const unsigned char *psrc;
+ const unsigned char *src;
+ const unsigned char *nsrc;
+ unsigned char *dst;
+ int vmod[72];
+ int hmod[72];
+ int mod_hi;
+ int by;
+ int bx;
+ mod_hi=OC_MINI(3*_dc_scale,OC_MOD_MAX[_strong]);
+ dst=_idata;
+ src=dst;
+ psrc=src-(_ystride&-!(_b&4));
+ for(by=0;by<9;by++){
+ for(bx=0;bx<8;bx++){
+ int mod;
+ mod=32+_dc_scale-(abs(src[bx]-psrc[bx])<<OC_MOD_SHIFT[_strong]);
+ vmod[(by<<3)+bx]=mod<-64?_sharp_mod:OC_CLAMPI(0,mod,mod_hi);
+ }
+ psrc=src;
+ src+=_ystride&-(!(_b&8)|by<7);
+ }
+ nsrc=dst;
+ psrc=dst-!(_b&1);
+ for(bx=0;bx<9;bx++){
+ src=nsrc;
+ for(by=0;by<8;by++){
+ int mod;
+ mod=32+_dc_scale-(abs(*src-*psrc)<<OC_MOD_SHIFT[_strong]);
+ hmod[(bx<<3)+by]=mod<-64?_sharp_mod:OC_CLAMPI(0,mod,mod_hi);
+ psrc+=_ystride;
+ src+=_ystride;
+ }
+ psrc=nsrc;
+ nsrc+=!(_b&2)|bx<7;
+ }
+ src=dst;
+ psrc=src-(_ystride&-!(_b&4));
+ nsrc=src+_ystride;
+ for(by=0;by<8;by++){
+ int a;
+ int b;
+ int w;
+ a=128;
+ b=64;
+ w=hmod[by];
+ a-=w;
+ b+=w**(src-!(_b&1));
+ w=vmod[by<<3];
+ a-=w;
+ b+=w*psrc[0];
+ w=vmod[by+1<<3];
+ a-=w;
+ b+=w*nsrc[0];
+ w=hmod[(1<<3)+by];
+ a-=w;
+ b+=w*src[1];
+ dst[0]=OC_CLAMP255(a*src[0]+b>>7);
+ for(bx=1;bx<7;bx++){
+ a=128;
+ b=64;
+ w=hmod[(bx<<3)+by];
+ a-=w;
+ b+=w*src[bx-1];
+ w=vmod[(by<<3)+bx];
+ a-=w;
+ b+=w*psrc[bx];
+ w=vmod[(by+1<<3)+bx];
+ a-=w;
+ b+=w*nsrc[bx];
+ w=hmod[(bx+1<<3)+by];
+ a-=w;
+ b+=w*src[bx+1];
+ dst[bx]=OC_CLAMP255(a*src[bx]+b>>7);
+ }
+ a=128;
+ b=64;
+ w=hmod[(7<<3)+by];
+ a-=w;
+ b+=w*src[6];
+ w=vmod[(by<<3)+7];
+ a-=w;
+ b+=w*psrc[7];
+ w=vmod[(by+1<<3)+7];
+ a-=w;
+ b+=w*nsrc[7];
+ w=hmod[(8<<3)+by];
+ a-=w;
+ b+=w*src[7+!(_b&2)];
+ dst[7]=OC_CLAMP255(a*src[7]+b>>7);
+ dst+=_ystride;
+ psrc=src;
+ src=nsrc;
+ nsrc+=_ystride&-(!(_b&8)|by<6);
+ }
+}
+
+#define OC_DERING_THRESH1 (384)
+#define OC_DERING_THRESH2 (4*OC_DERING_THRESH1)
+#define OC_DERING_THRESH3 (5*OC_DERING_THRESH1)
+#define OC_DERING_THRESH4 (10*OC_DERING_THRESH1)
+
+static void oc_dec_dering_frag_rows(oc_dec_ctx *_dec,th_img_plane *_img,
+ int _pli,int _fragy0,int _fragy_end){
+ th_img_plane *iplane;
+ oc_fragment_plane *fplane;
+ oc_fragment *frag;
+ int *variance;
+ unsigned char *idata;
+ ptrdiff_t froffset;
+ int ystride;
+ int nhfrags;
+ int sthresh;
+ int strong;
+ int y_end;
+ int width;
+ int height;
+ int y;
+ int x;
+ iplane=_img+_pli;
+ fplane=_dec->state.fplanes+_pli;
+ nhfrags=fplane->nhfrags;
+ froffset=fplane->froffset+_fragy0*(ptrdiff_t)nhfrags;
+ variance=_dec->variances+froffset;
+ frag=_dec->state.frags+froffset;
+ strong=_dec->pp_level>=(_pli?OC_PP_LEVEL_SDERINGC:OC_PP_LEVEL_SDERINGY);
+ sthresh=_pli?OC_DERING_THRESH4:OC_DERING_THRESH3;
+ y=_fragy0<<3;
+ ystride=iplane->stride;
+ idata=iplane->data+y*(ptrdiff_t)ystride;
+ y_end=_fragy_end<<3;
+ width=iplane->width;
+ height=iplane->height;
+ for(;y<y_end;y+=8){
+ for(x=0;x<width;x+=8){
+ int b;
+ int qi;
+ int var;
+ qi=_dec->state.qis[frag->qii];
+ var=*variance;
+ b=(x<=0)|(x+8>=width)<<1|(y<=0)<<2|(y+8>=height)<<3;
+ if(strong&&var>sthresh){
+ oc_dering_block(idata+x,ystride,b,
+ _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1);
+ if(_pli||!(b&1)&&*(variance-1)>OC_DERING_THRESH4||
+ !(b&2)&&variance[1]>OC_DERING_THRESH4||
+ !(b&4)&&*(variance-nhfrags)>OC_DERING_THRESH4||
+ !(b&8)&&variance[nhfrags]>OC_DERING_THRESH4){
+ oc_dering_block(idata+x,ystride,b,
+ _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1);
+ oc_dering_block(idata+x,ystride,b,
+ _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1);
+ }
+ }
+ else if(var>OC_DERING_THRESH2){
+ oc_dering_block(idata+x,ystride,b,
+ _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1);
+ }
+ else if(var>OC_DERING_THRESH1){
+ oc_dering_block(idata+x,ystride,b,
+ _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],0);
+ }
+ frag++;
+ variance++;
+ }
+ idata+=ystride<<3;
+ }
+}
+
+
+
+th_dec_ctx *th_decode_alloc(const th_info *_info,const th_setup_info *_setup){
+ oc_dec_ctx *dec;
+ if(_info==NULL||_setup==NULL)return NULL;
+ dec=_ogg_malloc(sizeof(*dec));
+ if(dec==NULL||oc_dec_init(dec,_info,_setup)<0){
+ _ogg_free(dec);
+ return NULL;
+ }
+ dec->state.curframe_num=0;
+ return dec;
+}
+
+void th_decode_free(th_dec_ctx *_dec){
+ if(_dec!=NULL){
+ oc_dec_clear(_dec);
+ _ogg_free(_dec);
+ }
+}
+
+int th_decode_ctl(th_dec_ctx *_dec,int _req,void *_buf,
+ size_t _buf_sz){
+ switch(_req){
+ case TH_DECCTL_GET_PPLEVEL_MAX:{
+ if(_dec==NULL||_buf==NULL)return TH_EFAULT;
+ if(_buf_sz!=sizeof(int))return TH_EINVAL;
+ (*(int *)_buf)=OC_PP_LEVEL_MAX;
+ return 0;
+ }break;
+ case TH_DECCTL_SET_PPLEVEL:{
+ int pp_level;
+ if(_dec==NULL||_buf==NULL)return TH_EFAULT;
+ if(_buf_sz!=sizeof(int))return TH_EINVAL;
+ pp_level=*(int *)_buf;
+ if(pp_level<0||pp_level>OC_PP_LEVEL_MAX)return TH_EINVAL;
+ _dec->pp_level=pp_level;
+ return 0;
+ }break;
+ case TH_DECCTL_SET_GRANPOS:{
+ ogg_int64_t granpos;
+ if(_dec==NULL||_buf==NULL)return TH_EFAULT;
+ if(_buf_sz!=sizeof(ogg_int64_t))return TH_EINVAL;
+ granpos=*(ogg_int64_t *)_buf;
+ if(granpos<0)return TH_EINVAL;
+ _dec->state.granpos=granpos;
+ _dec->state.keyframe_num=(granpos>>_dec->state.info.keyframe_granule_shift)
+ -_dec->state.granpos_bias;
+ _dec->state.curframe_num=_dec->state.keyframe_num
+ +(granpos&(1<<_dec->state.info.keyframe_granule_shift)-1);
+ return 0;
+ }break;
+ case TH_DECCTL_SET_STRIPE_CB:{
+ th_stripe_callback *cb;
+ if(_dec==NULL||_buf==NULL)return TH_EFAULT;
+ if(_buf_sz!=sizeof(th_stripe_callback))return TH_EINVAL;
+ cb=(th_stripe_callback *)_buf;
+ _dec->stripe_cb.ctx=cb->ctx;
+ _dec->stripe_cb.stripe_decoded=cb->stripe_decoded;
+ return 0;
+ }break;
+#ifdef HAVE_CAIRO
+ case TH_DECCTL_SET_TELEMETRY_MBMODE:{
+ if(_dec==NULL||_buf==NULL)return TH_EFAULT;
+ if(_buf_sz!=sizeof(int))return TH_EINVAL;
+ _dec->telemetry=1;
+ _dec->telemetry_mbmode=*(int *)_buf;
+ return 0;
+ }break;
+ case TH_DECCTL_SET_TELEMETRY_MV:{
+ if(_dec==NULL||_buf==NULL)return TH_EFAULT;
+ if(_buf_sz!=sizeof(int))return TH_EINVAL;
+ _dec->telemetry=1;
+ _dec->telemetry_mv=*(int *)_buf;
+ return 0;
+ }break;
+ case TH_DECCTL_SET_TELEMETRY_QI:{
+ if(_dec==NULL||_buf==NULL)return TH_EFAULT;
+ if(_buf_sz!=sizeof(int))return TH_EINVAL;
+ _dec->telemetry=1;
+ _dec->telemetry_qi=*(int *)_buf;
+ return 0;
+ }break;
+ case TH_DECCTL_SET_TELEMETRY_BITS:{
+ if(_dec==NULL||_buf==NULL)return TH_EFAULT;
+ if(_buf_sz!=sizeof(int))return TH_EINVAL;
+ _dec->telemetry=1;
+ _dec->telemetry_bits=*(int *)_buf;
+ return 0;
+ }break;
+#endif
+ default:return TH_EIMPL;
+ }
+}
+
+/*We're decoding an INTER frame, but have no initialized reference
+ buffers (i.e., decoding did not start on a key frame).
+ We initialize them to a solid gray here.*/
+static void oc_dec_init_dummy_frame(th_dec_ctx *_dec){
+ th_info *info;
+ size_t yplane_sz;
+ size_t cplane_sz;
+ int yhstride;
+ int yheight;
+ int chstride;
+ int cheight;
+ _dec->state.ref_frame_idx[OC_FRAME_GOLD]=0;
+ _dec->state.ref_frame_idx[OC_FRAME_PREV]=0;
+ _dec->state.ref_frame_idx[OC_FRAME_SELF]=1;
+ info=&_dec->state.info;
+ yhstride=info->frame_width+2*OC_UMV_PADDING;
+ yheight=info->frame_height+2*OC_UMV_PADDING;
+ chstride=yhstride>>!(info->pixel_fmt&1);
+ cheight=yheight>>!(info->pixel_fmt&2);
+ yplane_sz=yhstride*(size_t)yheight;
+ cplane_sz=chstride*(size_t)cheight;
+ memset(_dec->state.ref_frame_data[0],0x80,yplane_sz+2*cplane_sz);
+}
+
+int th_decode_packetin(th_dec_ctx *_dec,const ogg_packet *_op,
+ ogg_int64_t *_granpos){
+ int ret;
+ if(_dec==NULL||_op==NULL)return TH_EFAULT;
+ /*A completely empty packet indicates a dropped frame and is treated exactly
+ like an inter frame with no coded blocks.
+ Only proceed if we have a non-empty packet.*/
+ if(_op->bytes!=0){
+ oc_dec_pipeline_state pipe;
+ th_ycbcr_buffer stripe_buf;
+ int stripe_fragy;
+ int refi;
+ int pli;
+ int notstart;
+ int notdone;
+ oc_pack_readinit(&_dec->opb,_op->packet,_op->bytes);
+#if defined(HAVE_CAIRO)
+ _dec->telemetry_frame_bytes=_op->bytes;
+#endif
+ ret=oc_dec_frame_header_unpack(_dec);
+ if(ret<0)return ret;
+ /*Select a free buffer to use for the reconstructed version of this
+ frame.*/
+ if(_dec->state.frame_type!=OC_INTRA_FRAME&&
+ (_dec->state.ref_frame_idx[OC_FRAME_GOLD]<0||
+ _dec->state.ref_frame_idx[OC_FRAME_PREV]<0)){
+ /*No reference frames yet!*/
+ oc_dec_init_dummy_frame(_dec);
+ refi=_dec->state.ref_frame_idx[OC_FRAME_SELF];
+ }
+ else{
+ for(refi=0;refi==_dec->state.ref_frame_idx[OC_FRAME_GOLD]||
+ refi==_dec->state.ref_frame_idx[OC_FRAME_PREV];refi++);
+ _dec->state.ref_frame_idx[OC_FRAME_SELF]=refi;
+ }
+ if(_dec->state.frame_type==OC_INTRA_FRAME){
+ oc_dec_mark_all_intra(_dec);
+ _dec->state.keyframe_num=_dec->state.curframe_num;
+#if defined(HAVE_CAIRO)
+ _dec->telemetry_coding_bytes=
+ _dec->telemetry_mode_bytes=
+ _dec->telemetry_mv_bytes=oc_pack_bytes_left(&_dec->opb);
+#endif
+ }
+ else{
+ oc_dec_coded_flags_unpack(_dec);
+#if defined(HAVE_CAIRO)
+ _dec->telemetry_coding_bytes=oc_pack_bytes_left(&_dec->opb);
+#endif
+ oc_dec_mb_modes_unpack(_dec);
+#if defined(HAVE_CAIRO)
+ _dec->telemetry_mode_bytes=oc_pack_bytes_left(&_dec->opb);
+#endif
+ oc_dec_mv_unpack_and_frag_modes_fill(_dec);
+#if defined(HAVE_CAIRO)
+ _dec->telemetry_mv_bytes=oc_pack_bytes_left(&_dec->opb);
+#endif
+ }
+ oc_dec_block_qis_unpack(_dec);
+#if defined(HAVE_CAIRO)
+ _dec->telemetry_qi_bytes=oc_pack_bytes_left(&_dec->opb);
+#endif
+ oc_dec_residual_tokens_unpack(_dec);
+ /*Update granule position.
+ This must be done before the striped decode callbacks so that the
+ application knows what to do with the frame data.*/
+ _dec->state.granpos=(_dec->state.keyframe_num+_dec->state.granpos_bias<<
+ _dec->state.info.keyframe_granule_shift)
+ +(_dec->state.curframe_num-_dec->state.keyframe_num);
+ _dec->state.curframe_num++;
+ if(_granpos!=NULL)*_granpos=_dec->state.granpos;
+ /*All of the rest of the operations -- DC prediction reversal,
+ reconstructing coded fragments, copying uncoded fragments, loop
+ filtering, extending borders, and out-of-loop post-processing -- should
+ be pipelined.
+ I.e., DC prediction reversal, reconstruction, and uncoded fragment
+ copying are done for one or two super block rows, then loop filtering is
+ run as far as it can, then bordering copying, then post-processing.
+ For 4:2:0 video a Minimum Codable Unit or MCU contains two luma super
+ block rows, and one chroma.
+ Otherwise, an MCU consists of one super block row from each plane.
+ Inside each MCU, we perform all of the steps on one color plane before
+ moving on to the next.
+ After reconstruction, the additional filtering stages introduce a delay
+ since they need some pixels from the next fragment row.
+ Thus the actual number of decoded rows available is slightly smaller for
+ the first MCU, and slightly larger for the last.
+
+ This entire process allows us to operate on the data while it is still in
+ cache, resulting in big performance improvements.
+ An application callback allows further application processing (blitting
+ to video memory, color conversion, etc.) to also use the data while it's
+ in cache.*/
+ oc_dec_pipeline_init(_dec,&pipe);
+ oc_ycbcr_buffer_flip(stripe_buf,_dec->pp_frame_buf);
+ notstart=0;
+ notdone=1;
+ for(stripe_fragy=0;notdone;stripe_fragy+=pipe.mcu_nvfrags){
+ int avail_fragy0;
+ int avail_fragy_end;
+ avail_fragy0=avail_fragy_end=_dec->state.fplanes[0].nvfrags;
+ notdone=stripe_fragy+pipe.mcu_nvfrags<avail_fragy_end;
+ for(pli=0;pli<3;pli++){
+ oc_fragment_plane *fplane;
+ int frag_shift;
+ int pp_offset;
+ int sdelay;
+ int edelay;
+ fplane=_dec->state.fplanes+pli;
+ /*Compute the first and last fragment row of the current MCU for this
+ plane.*/
+ frag_shift=pli!=0&&!(_dec->state.info.pixel_fmt&2);
+ pipe.fragy0[pli]=stripe_fragy>>frag_shift;
+ pipe.fragy_end[pli]=OC_MINI(fplane->nvfrags,
+ pipe.fragy0[pli]+(pipe.mcu_nvfrags>>frag_shift));
+ oc_dec_dc_unpredict_mcu_plane(_dec,&pipe,pli);
+ oc_dec_frags_recon_mcu_plane(_dec,&pipe,pli);
+ sdelay=edelay=0;
+ if(pipe.loop_filter){
+ sdelay+=notstart;
+ edelay+=notdone;
+ oc_state_loop_filter_frag_rows(&_dec->state,pipe.bounding_values,
+ refi,pli,pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay);
+ }
+ /*To fill the borders, we have an additional two pixel delay, since a
+ fragment in the next row could filter its top edge, using two pixels
+ from a fragment in this row.
+ But there's no reason to delay a full fragment between the two.*/
+ oc_state_borders_fill_rows(&_dec->state,refi,pli,
+ (pipe.fragy0[pli]-sdelay<<3)-(sdelay<<1),
+ (pipe.fragy_end[pli]-edelay<<3)-(edelay<<1));
+ /*Out-of-loop post-processing.*/
+ pp_offset=3*(pli!=0);
+ if(pipe.pp_level>=OC_PP_LEVEL_DEBLOCKY+pp_offset){
+ /*Perform de-blocking in one plane.*/
+ sdelay+=notstart;
+ edelay+=notdone;
+ oc_dec_deblock_frag_rows(_dec,_dec->pp_frame_buf,
+ _dec->state.ref_frame_bufs[refi],pli,
+ pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay);
+ if(pipe.pp_level>=OC_PP_LEVEL_DERINGY+pp_offset){
+ /*Perform de-ringing in one plane.*/
+ sdelay+=notstart;
+ edelay+=notdone;
+ oc_dec_dering_frag_rows(_dec,_dec->pp_frame_buf,pli,
+ pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay);
+ }
+ }
+ /*If no post-processing is done, we still need to delay a row for the
+ loop filter, thanks to the strange filtering order VP3 chose.*/
+ else if(pipe.loop_filter){
+ sdelay+=notstart;
+ edelay+=notdone;
+ }
+ /*Compute the intersection of the available rows in all planes.
+ If chroma is sub-sampled, the effect of each of its delays is
+ doubled, but luma might have more post-processing filters enabled
+ than chroma, so we don't know up front which one is the limiting
+ factor.*/
+ avail_fragy0=OC_MINI(avail_fragy0,pipe.fragy0[pli]-sdelay<<frag_shift);
+ avail_fragy_end=OC_MINI(avail_fragy_end,
+ pipe.fragy_end[pli]-edelay<<frag_shift);
+ }
+ if(_dec->stripe_cb.stripe_decoded!=NULL){
+ /*The callback might want to use the FPU, so let's make sure they can.
+ We violate all kinds of ABI restrictions by not doing this until
+ now, but none of them actually matter since we don't use floating
+ point ourselves.*/
+ oc_restore_fpu(&_dec->state);
+ /*Make the callback, ensuring we flip the sense of the "start" and
+ "end" of the available region upside down.*/
+ (*_dec->stripe_cb.stripe_decoded)(_dec->stripe_cb.ctx,stripe_buf,
+ _dec->state.fplanes[0].nvfrags-avail_fragy_end,
+ _dec->state.fplanes[0].nvfrags-avail_fragy0);
+ }
+ notstart=1;
+ }
+ /*Finish filling in the reference frame borders.*/
+ for(pli=0;pli<3;pli++)oc_state_borders_fill_caps(&_dec->state,refi,pli);
+ /*Update the reference frame indices.*/
+ if(_dec->state.frame_type==OC_INTRA_FRAME){
+ /*The new frame becomes both the previous and gold reference frames.*/
+ _dec->state.ref_frame_idx[OC_FRAME_GOLD]=
+ _dec->state.ref_frame_idx[OC_FRAME_PREV]=
+ _dec->state.ref_frame_idx[OC_FRAME_SELF];
+ }
+ else{
+ /*Otherwise, just replace the previous reference frame.*/
+ _dec->state.ref_frame_idx[OC_FRAME_PREV]=
+ _dec->state.ref_frame_idx[OC_FRAME_SELF];
+ }
+ /*Restore the FPU before dump_frame, since that _does_ use the FPU (for PNG
+ gamma values, if nothing else).*/
+ oc_restore_fpu(&_dec->state);
+#if defined(OC_DUMP_IMAGES)
+ /*Don't dump images for dropped frames.*/
+ oc_state_dump_frame(&_dec->state,OC_FRAME_SELF,"dec");
+#endif
+ return 0;
+ }
+ else{
+ if(_dec->state.ref_frame_idx[OC_FRAME_GOLD]<0||
+ _dec->state.ref_frame_idx[OC_FRAME_PREV]<0){
+ int refi;
+ /*No reference frames yet!*/
+ oc_dec_init_dummy_frame(_dec);
+ refi=_dec->state.ref_frame_idx[OC_FRAME_PREV];
+ _dec->state.ref_frame_idx[OC_FRAME_SELF]=refi;
+ memcpy(_dec->pp_frame_buf,_dec->state.ref_frame_bufs[refi],
+ sizeof(_dec->pp_frame_buf[0])*3);
+ }
+ /*Just update the granule position and return.*/
+ _dec->state.granpos=(_dec->state.keyframe_num+_dec->state.granpos_bias<<
+ _dec->state.info.keyframe_granule_shift)
+ +(_dec->state.curframe_num-_dec->state.keyframe_num);
+ _dec->state.curframe_num++;
+ if(_granpos!=NULL)*_granpos=_dec->state.granpos;
+ return TH_DUPFRAME;
+ }
+}
+
+int th_decode_ycbcr_out(th_dec_ctx *_dec,th_ycbcr_buffer _ycbcr){
+ if(_dec==NULL||_ycbcr==NULL)return TH_EFAULT;
+ oc_ycbcr_buffer_flip(_ycbcr,_dec->pp_frame_buf);
+#if defined(HAVE_CAIRO)
+ /*If telemetry ioctls are active, we need to draw to the output buffer.
+ Stuff the plane into cairo.*/
+ if(_dec->telemetry){
+ cairo_surface_t *cs;
+ unsigned char *data;
+ unsigned char *y_row;
+ unsigned char *u_row;
+ unsigned char *v_row;
+ unsigned char *rgb_row;
+ int cstride;
+ int w;
+ int h;
+ int x;
+ int y;
+ int hdec;
+ int vdec;
+ w=_ycbcr[0].width;
+ h=_ycbcr[0].height;
+ hdec=!(_dec->state.info.pixel_fmt&1);
+ vdec=!(_dec->state.info.pixel_fmt&2);
+ /*Lazy data buffer init.
+ We could try to re-use the post-processing buffer, which would save
+ memory, but complicate the allocation logic there.
+ I don't think anyone cares about memory usage when using telemetry; it is
+ not meant for embedded devices.*/
+ if(_dec->telemetry_frame_data==NULL){
+ _dec->telemetry_frame_data=_ogg_malloc(
+ (w*h+2*(w>>hdec)*(h>>vdec))*sizeof(*_dec->telemetry_frame_data));
+ if(_dec->telemetry_frame_data==NULL)return 0;
+ }
+ cs=cairo_image_surface_create(CAIRO_FORMAT_RGB24,w,h);
+ /*Sadly, no YUV support in Cairo (yet); convert into the RGB buffer.*/
+ data=cairo_image_surface_get_data(cs);
+ if(data==NULL){
+ cairo_surface_destroy(cs);
+ return 0;
+ }
+ cstride=cairo_image_surface_get_stride(cs);
+ y_row=_ycbcr[0].data;
+ u_row=_ycbcr[1].data;
+ v_row=_ycbcr[2].data;
+ rgb_row=data;
+ for(y=0;y<h;y++){
+ for(x=0;x<w;x++){
+ int r;
+ int g;
+ int b;
+ r=(1904000*y_row[x]+2609823*v_row[x>>hdec]-363703744)/1635200;
+ g=(3827562*y_row[x]-1287801*u_row[x>>hdec]
+ -2672387*v_row[x>>hdec]+447306710)/3287200;
+ b=(952000*y_row[x]+1649289*u_row[x>>hdec]-225932192)/817600;
+ rgb_row[4*x+0]=OC_CLAMP255(b);
+ rgb_row[4*x+1]=OC_CLAMP255(g);
+ rgb_row[4*x+2]=OC_CLAMP255(r);
+ }
+ y_row+=_ycbcr[0].stride;
+ u_row+=_ycbcr[1].stride&-((y&1)|!vdec);
+ v_row+=_ycbcr[2].stride&-((y&1)|!vdec);
+ rgb_row+=cstride;
+ }
+ /*Draw coded identifier for each macroblock (stored in Hilbert order).*/
+ {
+ cairo_t *c;
+ const oc_fragment *frags;
+ oc_mv *frag_mvs;
+ const signed char *mb_modes;
+ oc_mb_map *mb_maps;
+ size_t nmbs;
+ size_t mbi;
+ int row2;
+ int col2;
+ int qim[3]={0,0,0};
+ if(_dec->state.nqis==2){
+ int bqi;
+ bqi=_dec->state.qis[0];
+ if(_dec->state.qis[1]>bqi)qim[1]=1;
+ if(_dec->state.qis[1]<bqi)qim[1]=-1;
+ }
+ if(_dec->state.nqis==3){
+ int bqi;
+ int cqi;
+ int dqi;
+ bqi=_dec->state.qis[0];
+ cqi=_dec->state.qis[1];
+ dqi=_dec->state.qis[2];
+ if(cqi>bqi&&dqi>bqi){
+ if(dqi>cqi){
+ qim[1]=1;
+ qim[2]=2;
+ }
+ else{
+ qim[1]=2;
+ qim[2]=1;
+ }
+ }
+ else if(cqi<bqi&&dqi<bqi){
+ if(dqi<cqi){
+ qim[1]=-1;
+ qim[2]=-2;
+ }
+ else{
+ qim[1]=-2;
+ qim[2]=-1;
+ }
+ }
+ else{
+ if(cqi<bqi)qim[1]=-1;
+ else qim[1]=1;
+ if(dqi<bqi)qim[2]=-1;
+ else qim[2]=1;
+ }
+ }
+ c=cairo_create(cs);
+ frags=_dec->state.frags;
+ frag_mvs=_dec->state.frag_mvs;
+ mb_modes=_dec->state.mb_modes;
+ mb_maps=_dec->state.mb_maps;
+ nmbs=_dec->state.nmbs;
+ row2=0;
+ col2=0;
+ for(mbi=0;mbi<nmbs;mbi++){
+ float x;
+ float y;
+ int bi;
+ y=h-(row2+((col2+1>>1)&1))*16-16;
+ x=(col2>>1)*16;
+ cairo_set_line_width(c,1.);
+ /*Keyframe (all intra) red box.*/
+ if(_dec->state.frame_type==OC_INTRA_FRAME){
+ if(_dec->telemetry_mbmode&0x02){
+ cairo_set_source_rgba(c,1.,0,0,.5);
+ cairo_rectangle(c,x+2.5,y+2.5,11,11);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,1.,0,0,.25);
+ cairo_fill(c);
+ }
+ }
+ else{
+ const signed char *frag_mv;
+ ptrdiff_t fragi;
+ for(bi=0;bi<4;bi++){
+ fragi=mb_maps[mbi][0][bi];
+ if(fragi>=0&&frags[fragi].coded){
+ frag_mv=frag_mvs[fragi];
+ break;
+ }
+ }
+ if(bi<4){
+ switch(mb_modes[mbi]){
+ case OC_MODE_INTRA:{
+ if(_dec->telemetry_mbmode&0x02){
+ cairo_set_source_rgba(c,1.,0,0,.5);
+ cairo_rectangle(c,x+2.5,y+2.5,11,11);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,1.,0,0,.25);
+ cairo_fill(c);
+ }
+ }break;
+ case OC_MODE_INTER_NOMV:{
+ if(_dec->telemetry_mbmode&0x01){
+ cairo_set_source_rgba(c,0,0,1.,.5);
+ cairo_rectangle(c,x+2.5,y+2.5,11,11);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,0,0,1.,.25);
+ cairo_fill(c);
+ }
+ }break;
+ case OC_MODE_INTER_MV:{
+ if(_dec->telemetry_mbmode&0x04){
+ cairo_rectangle(c,x+2.5,y+2.5,11,11);
+ cairo_set_source_rgba(c,0,1.,0,.5);
+ cairo_stroke(c);
+ }
+ if(_dec->telemetry_mv&0x04){
+ cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]);
+ cairo_set_source_rgba(c,1.,1.,1.,.9);
+ cairo_set_line_width(c,3.);
+ cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,2.);
+ cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,1.);
+ cairo_line_to(c,x+8,y+8);
+ cairo_stroke(c);
+ }
+ }break;
+ case OC_MODE_INTER_MV_LAST:{
+ if(_dec->telemetry_mbmode&0x08){
+ cairo_rectangle(c,x+2.5,y+2.5,11,11);
+ cairo_set_source_rgba(c,0,1.,0,.5);
+ cairo_move_to(c,x+13.5,y+2.5);
+ cairo_line_to(c,x+2.5,y+8);
+ cairo_line_to(c,x+13.5,y+13.5);
+ cairo_stroke(c);
+ }
+ if(_dec->telemetry_mv&0x08){
+ cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]);
+ cairo_set_source_rgba(c,1.,1.,1.,.9);
+ cairo_set_line_width(c,3.);
+ cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,2.);
+ cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,1.);
+ cairo_line_to(c,x+8,y+8);
+ cairo_stroke(c);
+ }
+ }break;
+ case OC_MODE_INTER_MV_LAST2:{
+ if(_dec->telemetry_mbmode&0x10){
+ cairo_rectangle(c,x+2.5,y+2.5,11,11);
+ cairo_set_source_rgba(c,0,1.,0,.5);
+ cairo_move_to(c,x+8,y+2.5);
+ cairo_line_to(c,x+2.5,y+8);
+ cairo_line_to(c,x+8,y+13.5);
+ cairo_move_to(c,x+13.5,y+2.5);
+ cairo_line_to(c,x+8,y+8);
+ cairo_line_to(c,x+13.5,y+13.5);
+ cairo_stroke(c);
+ }
+ if(_dec->telemetry_mv&0x10){
+ cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]);
+ cairo_set_source_rgba(c,1.,1.,1.,.9);
+ cairo_set_line_width(c,3.);
+ cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,2.);
+ cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,1.);
+ cairo_line_to(c,x+8,y+8);
+ cairo_stroke(c);
+ }
+ }break;
+ case OC_MODE_GOLDEN_NOMV:{
+ if(_dec->telemetry_mbmode&0x20){
+ cairo_set_source_rgba(c,1.,1.,0,.5);
+ cairo_rectangle(c,x+2.5,y+2.5,11,11);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,1.,1.,0,.25);
+ cairo_fill(c);
+ }
+ }break;
+ case OC_MODE_GOLDEN_MV:{
+ if(_dec->telemetry_mbmode&0x40){
+ cairo_rectangle(c,x+2.5,y+2.5,11,11);
+ cairo_set_source_rgba(c,1.,1.,0,.5);
+ cairo_stroke(c);
+ }
+ if(_dec->telemetry_mv&0x40){
+ cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]);
+ cairo_set_source_rgba(c,1.,1.,1.,.9);
+ cairo_set_line_width(c,3.);
+ cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,2.);
+ cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,1.);
+ cairo_line_to(c,x+8,y+8);
+ cairo_stroke(c);
+ }
+ }break;
+ case OC_MODE_INTER_MV_FOUR:{
+ if(_dec->telemetry_mbmode&0x80){
+ cairo_rectangle(c,x+2.5,y+2.5,4,4);
+ cairo_rectangle(c,x+9.5,y+2.5,4,4);
+ cairo_rectangle(c,x+2.5,y+9.5,4,4);
+ cairo_rectangle(c,x+9.5,y+9.5,4,4);
+ cairo_set_source_rgba(c,0,1.,0,.5);
+ cairo_stroke(c);
+ }
+ /*4mv is odd, coded in raster order.*/
+ fragi=mb_maps[mbi][0][0];
+ if(frags[fragi].coded&&_dec->telemetry_mv&0x80){
+ frag_mv=frag_mvs[fragi];
+ cairo_move_to(c,x+4+frag_mv[0],y+12-frag_mv[1]);
+ cairo_set_source_rgba(c,1.,1.,1.,.9);
+ cairo_set_line_width(c,3.);
+ cairo_line_to(c,x+4+frag_mv[0]*.66,y+12-frag_mv[1]*.66);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,2.);
+ cairo_line_to(c,x+4+frag_mv[0]*.33,y+12-frag_mv[1]*.33);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,1.);
+ cairo_line_to(c,x+4,y+12);
+ cairo_stroke(c);
+ }
+ fragi=mb_maps[mbi][0][1];
+ if(frags[fragi].coded&&_dec->telemetry_mv&0x80){
+ frag_mv=frag_mvs[fragi];
+ cairo_move_to(c,x+12+frag_mv[0],y+12-frag_mv[1]);
+ cairo_set_source_rgba(c,1.,1.,1.,.9);
+ cairo_set_line_width(c,3.);
+ cairo_line_to(c,x+12+frag_mv[0]*.66,y+12-frag_mv[1]*.66);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,2.);
+ cairo_line_to(c,x+12+frag_mv[0]*.33,y+12-frag_mv[1]*.33);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,1.);
+ cairo_line_to(c,x+12,y+12);
+ cairo_stroke(c);
+ }
+ fragi=mb_maps[mbi][0][2];
+ if(frags[fragi].coded&&_dec->telemetry_mv&0x80){
+ frag_mv=frag_mvs[fragi];
+ cairo_move_to(c,x+4+frag_mv[0],y+4-frag_mv[1]);
+ cairo_set_source_rgba(c,1.,1.,1.,.9);
+ cairo_set_line_width(c,3.);
+ cairo_line_to(c,x+4+frag_mv[0]*.66,y+4-frag_mv[1]*.66);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,2.);
+ cairo_line_to(c,x+4+frag_mv[0]*.33,y+4-frag_mv[1]*.33);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,1.);
+ cairo_line_to(c,x+4,y+4);
+ cairo_stroke(c);
+ }
+ fragi=mb_maps[mbi][0][3];
+ if(frags[fragi].coded&&_dec->telemetry_mv&0x80){
+ frag_mv=frag_mvs[fragi];
+ cairo_move_to(c,x+12+frag_mv[0],y+4-frag_mv[1]);
+ cairo_set_source_rgba(c,1.,1.,1.,.9);
+ cairo_set_line_width(c,3.);
+ cairo_line_to(c,x+12+frag_mv[0]*.66,y+4-frag_mv[1]*.66);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,2.);
+ cairo_line_to(c,x+12+frag_mv[0]*.33,y+4-frag_mv[1]*.33);
+ cairo_stroke_preserve(c);
+ cairo_set_line_width(c,1.);
+ cairo_line_to(c,x+12,y+4);
+ cairo_stroke(c);
+ }
+ }break;
+ }
+ }
+ }
+ /*qii illustration.*/
+ if(_dec->telemetry_qi&0x2){
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_SQUARE);
+ for(bi=0;bi<4;bi++){
+ ptrdiff_t fragi;
+ int qiv;
+ int xp;
+ int yp;
+ xp=x+(bi&1)*8;
+ yp=y+8-(bi&2)*4;
+ fragi=mb_maps[mbi][0][bi];
+ if(fragi>=0&&frags[fragi].coded){
+ qiv=qim[frags[fragi].qii];
+ cairo_set_line_width(c,3.);
+ cairo_set_source_rgba(c,0.,0.,0.,.5);
+ switch(qiv){
+ /*Double plus:*/
+ case 2:{
+ if((bi&1)^((bi&2)>>1)){
+ cairo_move_to(c,xp+2.5,yp+1.5);
+ cairo_line_to(c,xp+2.5,yp+3.5);
+ cairo_move_to(c,xp+1.5,yp+2.5);
+ cairo_line_to(c,xp+3.5,yp+2.5);
+ cairo_move_to(c,xp+5.5,yp+4.5);
+ cairo_line_to(c,xp+5.5,yp+6.5);
+ cairo_move_to(c,xp+4.5,yp+5.5);
+ cairo_line_to(c,xp+6.5,yp+5.5);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,0.,1.,1.,1.);
+ }
+ else{
+ cairo_move_to(c,xp+5.5,yp+1.5);
+ cairo_line_to(c,xp+5.5,yp+3.5);
+ cairo_move_to(c,xp+4.5,yp+2.5);
+ cairo_line_to(c,xp+6.5,yp+2.5);
+ cairo_move_to(c,xp+2.5,yp+4.5);
+ cairo_line_to(c,xp+2.5,yp+6.5);
+ cairo_move_to(c,xp+1.5,yp+5.5);
+ cairo_line_to(c,xp+3.5,yp+5.5);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,0.,1.,1.,1.);
+ }
+ }break;
+ /*Double minus:*/
+ case -2:{
+ cairo_move_to(c,xp+2.5,yp+2.5);
+ cairo_line_to(c,xp+5.5,yp+2.5);
+ cairo_move_to(c,xp+2.5,yp+5.5);
+ cairo_line_to(c,xp+5.5,yp+5.5);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,1.,1.,1.,1.);
+ }break;
+ /*Plus:*/
+ case 1:{
+ if(bi&2==0)yp-=2;
+ if(bi&1==0)xp-=2;
+ cairo_move_to(c,xp+4.5,yp+2.5);
+ cairo_line_to(c,xp+4.5,yp+6.5);
+ cairo_move_to(c,xp+2.5,yp+4.5);
+ cairo_line_to(c,xp+6.5,yp+4.5);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,.1,1.,.3,1.);
+ break;
+ }
+ /*Fall through.*/
+ /*Minus:*/
+ case -1:{
+ cairo_move_to(c,xp+2.5,yp+4.5);
+ cairo_line_to(c,xp+6.5,yp+4.5);
+ cairo_stroke_preserve(c);
+ cairo_set_source_rgba(c,1.,.3,.1,1.);
+ }break;
+ default:continue;
+ }
+ cairo_set_line_width(c,1.);
+ cairo_stroke(c);
+ }
+ }
+ }
+ col2++;
+ if((col2>>1)>=_dec->state.nhmbs){
+ col2=0;
+ row2+=2;
+ }
+ }
+ /*Bit usage indicator[s]:*/
+ if(_dec->telemetry_bits){
+ int widths[6];
+ int fpsn;
+ int fpsd;
+ int mult;
+ int fullw;
+ int padw;
+ int i;
+ fpsn=_dec->state.info.fps_numerator;
+ fpsd=_dec->state.info.fps_denominator;
+ mult=(_dec->telemetry_bits>=0xFF?1:_dec->telemetry_bits);
+ fullw=250.f*h*fpsd*mult/fpsn;
+ padw=w-24;
+ /*Header and coded block bits.*/
+ if(_dec->telemetry_frame_bytes<0||
+ _dec->telemetry_frame_bytes==OC_LOTS_OF_BITS){
+ _dec->telemetry_frame_bytes=0;
+ }
+ if(_dec->telemetry_coding_bytes<0||
+ _dec->telemetry_coding_bytes>_dec->telemetry_frame_bytes){
+ _dec->telemetry_coding_bytes=0;
+ }
+ if(_dec->telemetry_mode_bytes<0||
+ _dec->telemetry_mode_bytes>_dec->telemetry_frame_bytes){
+ _dec->telemetry_mode_bytes=0;
+ }
+ if(_dec->telemetry_mv_bytes<0||
+ _dec->telemetry_mv_bytes>_dec->telemetry_frame_bytes){
+ _dec->telemetry_mv_bytes=0;
+ }
+ if(_dec->telemetry_qi_bytes<0||
+ _dec->telemetry_qi_bytes>_dec->telemetry_frame_bytes){
+ _dec->telemetry_qi_bytes=0;
+ }
+ if(_dec->telemetry_dc_bytes<0||
+ _dec->telemetry_dc_bytes>_dec->telemetry_frame_bytes){
+ _dec->telemetry_dc_bytes=0;
+ }
+ widths[0]=padw*(_dec->telemetry_frame_bytes-_dec->telemetry_coding_bytes)/fullw;
+ widths[1]=padw*(_dec->telemetry_coding_bytes-_dec->telemetry_mode_bytes)/fullw;
+ widths[2]=padw*(_dec->telemetry_mode_bytes-_dec->telemetry_mv_bytes)/fullw;
+ widths[3]=padw*(_dec->telemetry_mv_bytes-_dec->telemetry_qi_bytes)/fullw;
+ widths[4]=padw*(_dec->telemetry_qi_bytes-_dec->telemetry_dc_bytes)/fullw;
+ widths[5]=padw*(_dec->telemetry_dc_bytes)/fullw;
+ for(i=0;i<6;i++)if(widths[i]>w)widths[i]=w;
+ cairo_set_source_rgba(c,.0,.0,.0,.6);
+ cairo_rectangle(c,10,h-33,widths[0]+1,5);
+ cairo_rectangle(c,10,h-29,widths[1]+1,5);
+ cairo_rectangle(c,10,h-25,widths[2]+1,5);
+ cairo_rectangle(c,10,h-21,widths[3]+1,5);
+ cairo_rectangle(c,10,h-17,widths[4]+1,5);
+ cairo_rectangle(c,10,h-13,widths[5]+1,5);
+ cairo_fill(c);
+ cairo_set_source_rgb(c,1,0,0);
+ cairo_rectangle(c,10.5,h-32.5,widths[0],4);
+ cairo_fill(c);
+ cairo_set_source_rgb(c,0,1,0);
+ cairo_rectangle(c,10.5,h-28.5,widths[1],4);
+ cairo_fill(c);
+ cairo_set_source_rgb(c,0,0,1);
+ cairo_rectangle(c,10.5,h-24.5,widths[2],4);
+ cairo_fill(c);
+ cairo_set_source_rgb(c,.6,.4,.0);
+ cairo_rectangle(c,10.5,h-20.5,widths[3],4);
+ cairo_fill(c);
+ cairo_set_source_rgb(c,.3,.3,.3);
+ cairo_rectangle(c,10.5,h-16.5,widths[4],4);
+ cairo_fill(c);
+ cairo_set_source_rgb(c,.5,.5,.8);
+ cairo_rectangle(c,10.5,h-12.5,widths[5],4);
+ cairo_fill(c);
+ }
+ /*Master qi indicator[s]:*/
+ if(_dec->telemetry_qi&0x1){
+ cairo_text_extents_t extents;
+ char buffer[10];
+ int p;
+ int y;
+ p=0;
+ y=h-7.5;
+ if(_dec->state.qis[0]>=10)buffer[p++]=48+_dec->state.qis[0]/10;
+ buffer[p++]=48+_dec->state.qis[0]%10;
+ if(_dec->state.nqis>=2){
+ buffer[p++]=' ';
+ if(_dec->state.qis[1]>=10)buffer[p++]=48+_dec->state.qis[1]/10;
+ buffer[p++]=48+_dec->state.qis[1]%10;
+ }
+ if(_dec->state.nqis==3){
+ buffer[p++]=' ';
+ if(_dec->state.qis[2]>=10)buffer[p++]=48+_dec->state.qis[2]/10;
+ buffer[p++]=48+_dec->state.qis[2]%10;
+ }
+ buffer[p++]='\0';
+ cairo_select_font_face(c,"sans",
+ CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size(c,18);
+ cairo_text_extents(c,buffer,&extents);
+ cairo_set_source_rgb(c,1,1,1);
+ cairo_move_to(c,w-extents.x_advance-10,y);
+ cairo_show_text(c,buffer);
+ cairo_set_source_rgb(c,0,0,0);
+ cairo_move_to(c,w-extents.x_advance-10,y);
+ cairo_text_path(c,buffer);
+ cairo_set_line_width(c,.8);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_stroke(c);
+ }
+ cairo_destroy(c);
+ }
+ /*Out of the Cairo plane into the telemetry YUV buffer.*/
+ _ycbcr[0].data=_dec->telemetry_frame_data;
+ _ycbcr[0].stride=_ycbcr[0].width;
+ _ycbcr[1].data=_ycbcr[0].data+h*_ycbcr[0].stride;
+ _ycbcr[1].stride=_ycbcr[1].width;
+ _ycbcr[2].data=_ycbcr[1].data+(h>>vdec)*_ycbcr[1].stride;
+ _ycbcr[2].stride=_ycbcr[2].width;
+ y_row=_ycbcr[0].data;
+ u_row=_ycbcr[1].data;
+ v_row=_ycbcr[2].data;
+ rgb_row=data;
+ /*This is one of the few places it's worth handling chroma on a
+ case-by-case basis.*/
+ switch(_dec->state.info.pixel_fmt){
+ case TH_PF_420:{
+ for(y=0;y<h;y+=2){
+ unsigned char *y_row2;
+ unsigned char *rgb_row2;
+ y_row2=y_row+_ycbcr[0].stride;
+ rgb_row2=rgb_row+cstride;
+ for(x=0;x<w;x+=2){
+ int y;
+ int u;
+ int v;
+ y=(65481*rgb_row[4*x+2]+128553*rgb_row[4*x+1]
+ +24966*rgb_row[4*x+0]+4207500)/255000;
+ y_row[x]=OC_CLAMP255(y);
+ y=(65481*rgb_row[4*x+6]+128553*rgb_row[4*x+5]
+ +24966*rgb_row[4*x+4]+4207500)/255000;
+ y_row[x+1]=OC_CLAMP255(y);
+ y=(65481*rgb_row2[4*x+2]+128553*rgb_row2[4*x+1]
+ +24966*rgb_row2[4*x+0]+4207500)/255000;
+ y_row2[x]=OC_CLAMP255(y);
+ y=(65481*rgb_row2[4*x+6]+128553*rgb_row2[4*x+5]
+ +24966*rgb_row2[4*x+4]+4207500)/255000;
+ y_row2[x+1]=OC_CLAMP255(y);
+ u=(-8372*(rgb_row[4*x+2]+rgb_row[4*x+6]
+ +rgb_row2[4*x+2]+rgb_row2[4*x+6])
+ -16436*(rgb_row[4*x+1]+rgb_row[4*x+5]
+ +rgb_row2[4*x+1]+rgb_row2[4*x+5])
+ +24808*(rgb_row[4*x+0]+rgb_row[4*x+4]
+ +rgb_row2[4*x+0]+rgb_row2[4*x+4])+29032005)/225930;
+ v=(39256*(rgb_row[4*x+2]+rgb_row[4*x+6]
+ +rgb_row2[4*x+2]+rgb_row2[4*x+6])
+ -32872*(rgb_row[4*x+1]+rgb_row[4*x+5]
+ +rgb_row2[4*x+1]+rgb_row2[4*x+5])
+ -6384*(rgb_row[4*x+0]+rgb_row[4*x+4]
+ +rgb_row2[4*x+0]+rgb_row2[4*x+4])+45940035)/357510;
+ u_row[x>>1]=OC_CLAMP255(u);
+ v_row[x>>1]=OC_CLAMP255(v);
+ }
+ y_row+=_ycbcr[0].stride<<1;
+ u_row+=_ycbcr[1].stride;
+ v_row+=_ycbcr[2].stride;
+ rgb_row+=cstride<<1;
+ }
+ }break;
+ case TH_PF_422:{
+ for(y=0;y<h;y++){
+ for(x=0;x<w;x+=2){
+ int y;
+ int u;
+ int v;
+ y=(65481*rgb_row[4*x+2]+128553*rgb_row[4*x+1]
+ +24966*rgb_row[4*x+0]+4207500)/255000;
+ y_row[x]=OC_CLAMP255(y);
+ y=(65481*rgb_row[4*x+6]+128553*rgb_row[4*x+5]
+ +24966*rgb_row[4*x+4]+4207500)/255000;
+ y_row[x+1]=OC_CLAMP255(y);
+ u=(-16744*(rgb_row[4*x+2]+rgb_row[4*x+6])
+ -32872*(rgb_row[4*x+1]+rgb_row[4*x+5])
+ +49616*(rgb_row[4*x+0]+rgb_row[4*x+4])+29032005)/225930;
+ v=(78512*(rgb_row[4*x+2]+rgb_row[4*x+6])
+ -65744*(rgb_row[4*x+1]+rgb_row[4*x+5])
+ -12768*(rgb_row[4*x+0]+rgb_row[4*x+4])+45940035)/357510;
+ u_row[x>>1]=OC_CLAMP255(u);
+ v_row[x>>1]=OC_CLAMP255(v);
+ }
+ y_row+=_ycbcr[0].stride;
+ u_row+=_ycbcr[1].stride;
+ v_row+=_ycbcr[2].stride;
+ rgb_row+=cstride;
+ }
+ }break;
+ /*case TH_PF_444:*/
+ default:{
+ for(y=0;y<h;y++){
+ for(x=0;x<w;x++){
+ int y;
+ int u;
+ int v;
+ y=(65481*rgb_row[4*x+2]+128553*rgb_row[4*x+1]
+ +24966*rgb_row[4*x+0]+4207500)/255000;
+ u=(-33488*rgb_row[4*x+2]-65744*rgb_row[4*x+1]
+ +99232*rgb_row[4*x+0]+29032005)/225930;
+ v=(157024*rgb_row[4*x+2]-131488*rgb_row[4*x+1]
+ -25536*rgb_row[4*x+0]+45940035)/357510;
+ y_row[x]=OC_CLAMP255(y);
+ u_row[x]=OC_CLAMP255(u);
+ v_row[x]=OC_CLAMP255(v);
+ }
+ y_row+=_ycbcr[0].stride;
+ u_row+=_ycbcr[1].stride;
+ v_row+=_ycbcr[2].stride;
+ rgb_row+=cstride;
+ }
+ }break;
+ }
+ /*Finished.
+ Destroy the surface.*/
+ cairo_surface_destroy(cs);
+ }
+#endif
+ return 0;
+}