H. Analysis of FPGA source code of 264 video codec intra prediction 1


The previous article is linked here~~
H. FPGA source code analysis of 264 video codec (I) input data analysis

Principle of intra prediction algorithm

General introduction based on the paper

  1. Objective: to improve the performance of I-frame compression
  2. Core idea: reduce the coding rate by using the correlation between adjacent pixels in the image, especially in the image smoothing part, that is, reduce the spatial redundancy to achieve compression
  3. The prediction value of the current macroblock is calculated by using the macroblock above and on the left of the currently encoded macroblock, and then the prediction value is subtracted from the actual pixel value to get the prediction residual. When encoding, only the prediction residual is encoded (a bit like the idea of residual neural network hehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehehe
  4. Four intra prediction modes: 4 × 4 brightness block prediction (intra 4 × 4), 16 × 16 brightness block prediction (intra 16 × 16), 8 × 8 chroma block prediction (intra chrome), PCM prediction (I PCM)

Hardware implementation

Division of luminance block and chroma block

Let's review the previous one Article We have analyzed that the signal MC u u cur UU and mcuu cur UU are connected with the 8 × 8 chroma block and the intra prediction module, and the connection mode is as follows:

mb_cb mc_cur_u
mb_cr mc_cur_v

Then let's see where the data went

genvar j; 
  for(j=0;j<64; j=j+1) begin:j_n
  	always @( * ) begin
  		cur_u[j] = mb_cb[(j+1)*8-1:j*8];
  		cur_v[j] = mb_cr[(j+1)*8-1:j*8];  

Here, cur u u and cur UU are register arrays, which are defined as follows:

reg [`BIT_DEPTH-1:0] 		cur_u[0:63];
reg [`BIT_DEPTH-1:0] 		cur_v[0:63];

And then this data~~

always @(*) begin
	case (chroma_num)
	3'b000:ori_uv={cur_u[0],  cur_u[1],  cur_u[2],  cur_u[3],
				   cur_u[8],  cur_u[9],  cur_u[10], cur_u[11],
				   cur_u[16], cur_u[17], cur_u[18], cur_u[19],
				   cur_u[24], cur_u[25], cur_u[26], cur_u[27] };
	3'b001:ori_uv={cur_u[4],  cur_u[5],  cur_u[6],  cur_u[7],
				   cur_u[12], cur_u[13], cur_u[14], cur_u[15],
				   cur_u[20], cur_u[21], cur_u[22], cur_u[23],
				   cur_u[28], cur_u[29], cur_u[30], cur_u[31] };
	3'b010:ori_uv={cur_u[32], cur_u[33], cur_u[34], cur_u[35],
				   cur_u[40], cur_u[41], cur_u[42], cur_u[43],
				   cur_u[48], cur_u[49], cur_u[50], cur_u[51],
				   cur_u[56], cur_u[57], cur_u[58], cur_u[59] };
	3'b011:ori_uv={cur_u[36], cur_u[37], cur_u[38], cur_u[39],
				   cur_u[44], cur_u[45], cur_u[46], cur_u[47],
				   cur_u[52], cur_u[53], cur_u[54], cur_u[55],
				   cur_u[60], cur_u[61], cur_u[62], cur_u[63] };	
	3'b100:ori_uv={cur_v[0],  cur_v[1],  cur_v[2],  cur_v[3],
				   cur_v[8],  cur_v[9],  cur_v[10], cur_v[11],
				   cur_v[16], cur_v[17], cur_v[18], cur_v[19],
				   cur_v[24], cur_v[25], cur_v[26], cur_v[27] };
	3'b101:ori_uv={cur_v[4],  cur_v[5],  cur_v[6],  cur_v[7],
				   cur_v[12], cur_v[13], cur_v[14], cur_v[15],
				   cur_v[20], cur_v[21], cur_v[22], cur_v[23],
				   cur_v[28], cur_v[29], cur_v[30], cur_v[31] };
	3'b110:ori_uv={cur_v[32], cur_v[33], cur_v[34], cur_v[35],
				   cur_v[40], cur_v[41], cur_v[42], cur_v[43],
				   cur_v[48], cur_v[49], cur_v[50], cur_v[51],
				   cur_v[56], cur_v[57], cur_v[58], cur_v[59] };
	3'b111:ori_uv={cur_v[36], cur_v[37], cur_v[38], cur_v[39],
				   cur_v[44], cur_v[45], cur_v[46], cur_v[47],
				   cur_v[52], cur_v[53], cur_v[54], cur_v[55],
				   cur_v[60], cur_v[61], cur_v[62], cur_v[63] };

This neat code doesn't look too good
You can see that the state variable of case is chroma num, which comes from module intra 16 x 16 chroma top

intra_16x16_chroma_top u_intra_16x16_chroma_top(
				.clk				( clk				),    
				.rst_n				( rst_n				),    
				.mb_x_total			( mb_x_total		),    
				.mb_x				( mb_x				),    
				.mb_y				( mb_y				),   
				.pred_start_i    	( i16x16_chroma_start ),
				.pred_done_o        ( i16x16_chroma_done  ),
				.i16x16_rec_start_i ( i16x16_rec_start  ),
				.i16x16_rec_done_o  ( i16x16_rec_done   ),
				.chroma_rec_start_i ( chroma_rec_start  ),
				.chroma_rec_done_o  ( chroma_rec_done   ),
				.luma_16x16_cost_o  ( i16x16_cost       ),
				.chroma_8x8_cost_o	( chroma_cost   	),			      
				.luma_16x16_sel_o   ( i16x16_num        ),
				.chroma_8x8_sel_o   ( chroma_num        ),

Limited by space, I only intercept a part of the code of module instantiation
That is to say, the module receives the indication signal from intra x16$chroma, and then assigns the value of ori ũ UV according to the value of the signal. The variable is defined as:

reg [`BIT_DEPTH*16-1:0] 	ori_uv;

It is a 8*16=128bit register variable. There are eight cases in this case. There is no default, but because all possible cases have been included, there will be no latch. Chroma? Num is mainly used to indicate which data to assign to ori? UV. But I didn't pay attention when I first saw it here. Actually, cur u u v and cur UU are different

{cur_u[{addr_uv, 2'b00}],cur_v[{addr_uv, 2'b00}],cur_u[{addr_uv, 2'b01}],cur_v[{addr_uv, 2'b01}],
cur_u[{addr_uv, 2'b10}],cur_v[{addr_uv, 2'b10}],cur_u[{addr_uv, 2'b11}],cur_v[{addr_uv, 2'b11}]} <= pdata_i;

That is to say, cur u u v and cur UU are assigned alternately.

I don't understand how to assign the value here. We can see the process of assignment directly through simulation. Take the code from pdata to ori UUV separately and simulate it. Try it! let’s go!

Start counting, pdata takes out 64bit at a time, the first number is 525956565d575355
Number of corresponding files

You can see two rows of data at a time

It can be seen that when pvalid? I becomes high, addr? P starts to count, then the next clock cycle, cur? Y starts to assign, and one cycle assigns 64bit data. At the same time, addr? Y (5bit) and addr? UV (4bit) start to count synchronously.

When the highest bit of addr ﹣ P (6bit) becomes 1, the next cycle cur ﹣ u starts to assign value, using pdata ﹣ I to assign one every other, only taking 32bit data.

One 16 × 16 brightness macroblock and two 8 × 8 chroma macroblocks
I can't see what it is...
So why is it so divided? This involves the coding format of YUV: Y in YUV represents luma, and CbCr represents chroma. There are three common formats: YUV444, YUV422, YUV420. The first number 4 represents four image pixels, each of which has a luminance value, the second number represents U, which means that of the four image pixels, Cb only occupies two pixels, and the third number represents V. For details, please refer to the figure below

So the assignment method here is similar to the first reciprocal or the third reciprocal?

4 × 4 brightness prediction module

It can be seen that the intra 4 × 4 module sends an indication signal i4x4 ﹣ num

intra_4x4_top u_intra_4x4_top(
				.clk				( clk				), 
				.rst_n				( rst_n				), 
				.mb_x_total			( mb_x_total		), 
				.mb_x				( mb_x				), 
				.mb_y				( mb_y				), 
				.start_i           	( i4x4_start        ),
				.done_o            	( i4x4_done         ),
				.lambda_i          	( lambda            ),
				.luma_4x4_cost_o   	( i4x4_cost         ),
				.i4x4_num_o        	( i4x4_num          ),

This signal is used to indicate the allocation of cur uy data

always @(*) begin
	case (i4x4_num)
		4'b0000:ori4x4 ={ cur_y[0],   cur_y[1],   cur_y[2],   cur_y[3],
						  cur_y[16],  cur_y[17],  cur_y[18],  cur_y[19],
						  cur_y[32],  cur_y[33],  cur_y[34],  cur_y[35],
						  cur_y[48],  cur_y[49],  cur_y[50],  cur_y[51] };
		4'b0001:ori4x4 ={ cur_y[4],   cur_y[5],   cur_y[6],   cur_y[7],
						  cur_y[20],  cur_y[21],  cur_y[22],  cur_y[23],
						  cur_y[36],  cur_y[37],  cur_y[38],  cur_y[39],
						  cur_y[52],  cur_y[53],  cur_y[54],  cur_y[55] };
		4'b0010:ori4x4 ={ cur_y[64],  cur_y[65],  cur_y[66],  cur_y[67],
						  cur_y[80],  cur_y[81],  cur_y[82],  cur_y[83],
						  cur_y[96],  cur_y[97],  cur_y[98],  cur_y[99],
						  cur_y[112], cur_y[113], cur_y[114], cur_y[115] };
		4'b0011:ori4x4 ={ cur_y[68],  cur_y[69],  cur_y[70],  cur_y[71],
						  cur_y[84],  cur_y[85],  cur_y[86],  cur_y[87],
						  cur_y[100], cur_y[101], cur_y[102], cur_y[103],
						  cur_y[116], cur_y[117], cur_y[118], cur_y[119] };
		4'b0100:ori4x4 ={ cur_y[8],   cur_y[9],   cur_y[10],  cur_y[11],
						  cur_y[24],  cur_y[25],  cur_y[26],  cur_y[27],
						  cur_y[40],  cur_y[41],  cur_y[42],  cur_y[43],
						  cur_y[56],  cur_y[57],  cur_y[58],  cur_y[59] };
		4'b0101:ori4x4 ={ cur_y[12],  cur_y[13],  cur_y[14],  cur_y[15],
						  cur_y[28],  cur_y[29],  cur_y[30],  cur_y[31],
						  cur_y[44],  cur_y[45],  cur_y[46],  cur_y[47],
						  cur_y[60],  cur_y[61],  cur_y[62],  cur_y[63] };
		4'b0110:ori4x4 ={ cur_y[72],  cur_y[73],  cur_y[74],  cur_y[75],
						  cur_y[88],  cur_y[89],  cur_y[90],  cur_y[91],
						  cur_y[104], cur_y[105], cur_y[106], cur_y[107],
						  cur_y[120], cur_y[121], cur_y[122], cur_y[123] };
		4'b0111:ori4x4 ={ cur_y[76],  cur_y[77],  cur_y[78],  cur_y[79],
						  cur_y[92],  cur_y[93],  cur_y[94],  cur_y[95],
						  cur_y[108], cur_y[109], cur_y[110], cur_y[111],
						  cur_y[124], cur_y[125], cur_y[126], cur_y[127] };
		4'b1000:ori4x4 ={ cur_y[128], cur_y[129], cur_y[130], cur_y[131],
						  cur_y[144], cur_y[145], cur_y[146], cur_y[147],
						  cur_y[160], cur_y[161], cur_y[162], cur_y[163],
						  cur_y[176], cur_y[177], cur_y[178], cur_y[179] };
		4'b1001:ori4x4 ={ cur_y[132], cur_y[133], cur_y[134], cur_y[135],
						  cur_y[148], cur_y[149], cur_y[150], cur_y[151],
						  cur_y[164], cur_y[165], cur_y[166], cur_y[167],
						  cur_y[180], cur_y[181], cur_y[182], cur_y[183] };
		4'b1010:ori4x4 ={ cur_y[192], cur_y[193], cur_y[194], cur_y[195],
						  cur_y[208], cur_y[209], cur_y[210], cur_y[211],
						  cur_y[224], cur_y[225], cur_y[226], cur_y[227],
						  cur_y[240], cur_y[241], cur_y[242], cur_y[243] };
		4'b1011:ori4x4 ={ cur_y[196], cur_y[197], cur_y[198], cur_y[199],
						  cur_y[212], cur_y[213], cur_y[214], cur_y[215],
						  cur_y[228], cur_y[229], cur_y[230], cur_y[231],
						  cur_y[244], cur_y[245], cur_y[246], cur_y[247] };									     
		4'b1100:ori4x4 ={ cur_y[136], cur_y[137], cur_y[138], cur_y[139],
						  cur_y[152], cur_y[153], cur_y[154], cur_y[155],
						  cur_y[168], cur_y[169], cur_y[170], cur_y[171],
						  cur_y[184], cur_y[185], cur_y[186], cur_y[187] };
		4'b1101:ori4x4 ={ cur_y[140], cur_y[141], cur_y[142], cur_y[143],
						  cur_y[156], cur_y[157], cur_y[158], cur_y[159],
						  cur_y[172], cur_y[173], cur_y[174], cur_y[175],
						  cur_y[188], cur_y[189], cur_y[190], cur_y[191] };
		4'b1110:ori4x4 ={ cur_y[200], cur_y[201], cur_y[202], cur_y[203],
						  cur_y[216], cur_y[217], cur_y[218], cur_y[219],
						  cur_y[232], cur_y[233], cur_y[234], cur_y[235],
						  cur_y[248], cur_y[249], cur_y[250], cur_y[251] };
		4'b1111:ori4x4 ={ cur_y[204], cur_y[205], cur_y[206], cur_y[207],
						  cur_y[220], cur_y[221], cur_y[222], cur_y[223],
						  cur_y[236], cur_y[237], cur_y[238], cur_y[239],
						  cur_y[252], cur_y[253], cur_y[254], cur_y[255] };

16 cases in all

assign ori00_4x4 = ori4x4[127:120];
assign ori01_4x4 = ori4x4[119:112];
assign ori02_4x4 = ori4x4[111:104];
assign ori03_4x4 = ori4x4[103: 96];
assign ori10_4x4 = ori4x4[ 95: 88];
assign ori11_4x4 = ori4x4[ 87: 80];
assign ori12_4x4 = ori4x4[ 79: 72];
assign ori13_4x4 = ori4x4[ 71: 64];
assign ori20_4x4 = ori4x4[ 63: 56];
assign ori21_4x4 = ori4x4[ 55: 48];
assign ori22_4x4 = ori4x4[ 47: 40];
assign ori23_4x4 = ori4x4[ 39: 32];
assign ori30_4x4 = ori4x4[ 31: 24];
assign ori31_4x4 = ori4x4[ 23: 16];
assign ori32_4x4 = ori4x4[ 15:  8];
assign ori33_4x4 = ori4x4[  7:  0];
intra_4x4_top u_intra_4x4_top(
				.clk				( clk				), 
				.rst_n				( rst_n				), 
				.mb_x_total			( mb_x_total		), 
				.mb_x				( mb_x				), 
				.mb_y				( mb_y				), 
				.start_i           	( i4x4_start        ),
				.done_o            	( i4x4_done         ),
				.lambda_i          	( lambda            ),
				.luma_4x4_cost_o   	( i4x4_cost         ),
				.i4x4_num_o        	( i4x4_num          ),
				.i4x4_end_o        	( i4x4_end          ),
				.i4x4_pred_mode_i  	( i4x4_pred_mode    ),
				.i4x4_min_mode_o   	( i4x4_min_mode     ),
				.i4x4_min_val_o    	( i4x4_min_val      ),
				.i4x4_min_num_o	  	( i4x4_min_num	    ),
				.tq_en_o           	( tq_i4x4_en_o      ),
				.tq_mod_o          	( tq_i4x4_mod_o     ),
				.tq_num_o          	( tq_i4x4_num_o     ),
				.tq_end_o          	( tq_i4x4_end_o     ),
				.tq_min_o          	( tq_i4x4_min_o     ),				
				.ori00 ( ori00_4x4 ), .ori01 ( ori01_4x4 ), .ori02 ( ori02_4x4 ), .ori03 ( ori03_4x4 ),
				.ori10 ( ori10_4x4 ), .ori11 ( ori11_4x4 ), .ori12 ( ori12_4x4 ), .ori13 ( ori13_4x4 ),
				.ori20 ( ori20_4x4 ), .ori21 ( ori21_4x4 ), .ori22 ( ori22_4x4 ), .ori23 ( ori23_4x4 ),
				.ori30 ( ori30_4x4 ), .ori31 ( ori31_4x4 ), .ori32 ( ori32_4x4 ), .ori33 ( ori33_4x4 ),

Next, we jump to the intra 4 × 4 module
Corresponding definition

// Original Pixel Input
input  [`BIT_DEPTH-1:0]		ori00, ori01, ori02, ori03,
                            ori10, ori11, ori12, ori13,
                            ori20, ori21, ori22, ori23,
                            ori30, ori31, ori32, ori33;
// Reference Pixel Input
input  [`BIT_DEPTH-1:0]		ref00tl,                            
							ref00t,  ref01t,  ref02t,  ref03t,  
							ref00l,  ref01l,  ref02l,  ref03l,  
							ref00tr, ref01tr, ref02tr, ref03tr;
// Predicted Pixel Output
output  [`BIT_DEPTH-1:0]	pre00, pre01, pre02, pre03,
							pre10, pre11, pre12, pre13,
							pre20, pre21, pre22, pre23,
							pre30, pre31, pre32, pre33;
// Residual Data Output
output  [`BIT_DEPTH:0]		res00, res01, res02, res03,
							res10, res11, res12, res13,
							res20, res21, res22, res23,
							res30, res31, res32, res33;

OK, let's analyze it.
Cur_yis a register array with 8 bit width and 256 depth, but in the 4 × 4 intra prediction module, it is implemented with a 44 sub block as the unit. So it is divided into 44 sub blocks, in which the rows are continuous, but the data between the rows is discontinuous. Then it is transformed into serial data and transferred to 4 × 4 intra prediction module for processing.
There is also a reference signal involved here. Let's see what this signal is.


// Ref Pixels for Intra_4x4
wire [`BIT_DEPTH-1:0] ref4x4_00tl,                                      
					  ref4x4_00t,  ref4x4_01t,  ref4x4_02t,  ref4x4_03t,
					  ref4x4_00l,  ref4x4_01l,  ref4x4_02l,  ref4x4_03l,
					  ref4x4_00tr, ref4x4_01tr, ref4x4_02tr, ref4x4_03tr;

As you can see, it is generated from module intra Ref. [here we will introduce next time]

How to generate prediction pixels and residual pixels?

Enter this module:

intra_4x4_pe u_intra_4x4_pe (
				.clk  		( clk   			),
				.rst_n		( rst_n 			),
				.curr_mode	( i4x4_curr_mode  	),
				.blk_avail	( blk_avail 		),
				.pixel_ori00( ori00 ), .pixel_ori01( ori01 ), .pixel_ori02( ori02 ), .pixel_ori03( ori03 ),
				.pixel_ori10( ori10 ), .pixel_ori11( ori11 ), .pixel_ori12( ori12 ), .pixel_ori13( ori13 ),
				.pixel_ori20( ori20 ), .pixel_ori21( ori21 ), .pixel_ori22( ori22 ), .pixel_ori23( ori23 ),
				.pixel_ori30( ori30 ), .pixel_ori31( ori31 ), .pixel_ori32( ori32 ), .pixel_ori33( ori33 ),

The output residual signal is pixel ﹣ re00 ﹣ o, which is the assignment after one beat. The assignment relationship before one beat is:

assign pixel_res00 = {1'b0,pixel_ori00} - {1'b0,pixel_pred00[`BIT_DEPTH-1:0]};
assign pixel_res01 = {1'b0,pixel_ori01} - {1'b0,pixel_pred01[`BIT_DEPTH-1:0]};
assign pixel_res02 = {1'b0,pixel_ori02} - {1'b0,pixel_pred02[`BIT_DEPTH-1:0]};
assign pixel_res03 = {1'b0,pixel_ori03} - {1'b0,pixel_pred03[`BIT_DEPTH-1:0]};
assign pixel_res10 = {1'b0,pixel_ori10} - {1'b0,pixel_pred10[`BIT_DEPTH-1:0]};
assign pixel_res11 = {1'b0,pixel_ori11} - {1'b0,pixel_pred11[`BIT_DEPTH-1:0]};
assign pixel_res12 = {1'b0,pixel_ori12} - {1'b0,pixel_pred12[`BIT_DEPTH-1:0]};
assign pixel_res13 = {1'b0,pixel_ori13} - {1'b0,pixel_pred13[`BIT_DEPTH-1:0]};
assign pixel_res20 = {1'b0,pixel_ori20} - {1'b0,pixel_pred20[`BIT_DEPTH-1:0]};
assign pixel_res21 = {1'b0,pixel_ori21} - {1'b0,pixel_pred21[`BIT_DEPTH-1:0]};
assign pixel_res22 = {1'b0,pixel_ori22} - {1'b0,pixel_pred22[`BIT_DEPTH-1:0]};
assign pixel_res23 = {1'b0,pixel_ori23} - {1'b0,pixel_pred23[`BIT_DEPTH-1:0]};
assign pixel_res30 = {1'b0,pixel_ori30} - {1'b0,pixel_pred30[`BIT_DEPTH-1:0]};
assign pixel_res31 = {1'b0,pixel_ori31} - {1'b0,pixel_pred31[`BIT_DEPTH-1:0]};
assign pixel_res32 = {1'b0,pixel_ori32} - {1'b0,pixel_pred32[`BIT_DEPTH-1:0]};
assign pixel_res33 = {1'b0,pixel_ori33} - {1'b0,pixel_pred33[`BIT_DEPTH-1:0]};

Where pixel_ori00 is the original input signal, what is pixel_pred00?
As you can see, this variable is assigned through a case:

always @(*)begin
		INTRA4x4_V   :begin
			pixel_pred00=ref00_t; pixel_pred01=ref01_t; pixel_pred02=ref02_t; pixel_pred03=ref03_t;
			pixel_pred10=ref00_t; pixel_pred11=ref01_t; pixel_pred12=ref02_t; pixel_pred13=ref03_t;
			pixel_pred20=ref00_t; pixel_pred21=ref01_t; pixel_pred22=ref02_t; pixel_pred23=ref03_t;
			pixel_pred30=ref00_t; pixel_pred31=ref01_t; pixel_pred32=ref02_t; pixel_pred33=ref03_t;

The variable to control the state is curr UU mode. There are 9 cases in total, plus a default case (all 0). This makes it clear that there are nine prediction models:

Vertical mode intra4x4 μ V

pixel_pred00=ref00_t; pixel_pred01=ref01_t; pixel_pred02=ref02_t; pixel_pred03=ref03_t;
pixel_pred10=ref00_t; pixel_pred11=ref01_t; pixel_pred12=ref02_t; pixel_pred13=ref03_t;
pixel_pred20=ref00_t; pixel_pred21=ref01_t; pixel_pred22=ref02_t; pixel_pred23=ref03_t;
pixel_pred30=ref00_t; pixel_pred31=ref01_t; pixel_pred32=ref02_t; pixel_pred33=ref03_t;

ABCD corresponds to ref00 T / Ref01 T / Ref02 T / Ref03 t respectively

Horizontal mode intra4x4

pixel_pred00=ref00_l; pixel_pred01=ref00_l; pixel_pred02=ref00_l; pixel_pred03=ref00_l;
pixel_pred10=ref01_l; pixel_pred11=ref01_l; pixel_pred12=ref01_l; pixel_pred13=ref01_l;
pixel_pred20=ref02_l; pixel_pred21=ref02_l; pixel_pred22=ref02_l; pixel_pred23=ref02_l;
pixel_pred30=ref03_l; pixel_pred31=ref03_l; pixel_pred32=ref03_l; pixel_pred33=ref03_l;

IJKL corresponds to ref00 / Ref01 / Ref02 / Ref03

DC mode intra4x4u DC

	else if(blk_avail[3])begin
	else if(blk_avail[1])begin
	else begin

It looks like a long time ha ha ha ha.

The indicator signal intra 4 Ctrl comes from the intra 4 Ctrl module. The official note is 4x4 block top, left, top left, top right available info. It is a combination of four signals

assign 	blk_avail 		= {blk_lf_avail,blk_tl_avail,blk_tp_avail,blk_tr_avail};

I think it can be understood that if the first two digits are 1, the average value in the horizontal and vertical directions will be taken; if only the highest digit is 1, the average value in the horizontal direction will be taken; if only the second highest digit is 1, the average value in the vertical direction will be taken.
Huhu ~ here comes the first code, and the rest is written in the following article
Refueling duck! ヾ(◍°∇°◍)ノ゙

Published 3 original articles, won praise 1, visited 18
Private letter follow

Tags: Lambda codec encoding network

Posted on Fri, 13 Mar 2020 04:15:02 -0700 by Rayn