1 /*
2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3 * All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * - Neither the name of the copyright owner, nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "oapv_rc.h"
33
oapve_rc_get_tile_cost(oapve_ctx_t * ctx,oapve_core_t * core,oapve_tile_t * tile)34 int oapve_rc_get_tile_cost(oapve_ctx_t* ctx, oapve_core_t* core, oapve_tile_t* tile)
35 {
36 int sum = 0;
37 tile->rc.number_pixel = 0;
38 for (int c = Y_C; c < ctx->num_comp; c++)
39 {
40 int step_w = 8 << ctx->comp_sft[c][0];
41 int step_h = 8 << ctx->comp_sft[c][1];
42 for (int y = 0; y < tile->h; y += step_h)
43 {
44 for (int x = 0; x < tile->w; x += step_w)
45 {
46 int tx = tile->x + x;
47 int ty = tile->y + y;
48
49 ctx->fn_imgb_to_block_rc(ctx->imgb, c, tx, ty, 8, 8, core->coef);
50 sum += ctx->fn_had8x8(core->coef, 8);
51 tile->rc.number_pixel += 64;
52 }
53 }
54 }
55
56 tile->rc.cost = sum;
57
58 return OAPV_OK;
59 }
60
get_tile_cost_thread(void * arg)61 int get_tile_cost_thread(void* arg)
62 {
63 oapve_core_t* core = (oapve_core_t*)arg;
64 oapve_ctx_t* ctx = core->ctx;
65 oapve_tile_t* tile = ctx->tile;
66 int tidx = 0, ret = OAPV_OK, i;
67
68 while (1) {
69 // find not processed tile
70 oapv_tpool_enter_cs(ctx->sync_obj);
71 for (i = 0; i < ctx->num_tiles; i++)
72 {
73 if (tile[i].stat == ENC_TILE_STAT_NOT_ENCODED)
74 {
75 tile[i].stat = ENC_TILE_STAT_ON_ENCODING;
76 tidx = i;
77 break;
78 }
79 }
80 oapv_tpool_leave_cs(ctx->sync_obj);
81 if (i == ctx->num_tiles)
82 {
83 break;
84 }
85
86 ret = oapve_rc_get_tile_cost(ctx, core, &tile[tidx]);
87 oapv_assert_g(OAPV_SUCCEEDED(ret), ERR);
88
89 oapv_tpool_enter_cs(ctx->sync_obj);
90 tile[tidx].stat = ENC_TILE_STAT_ENCODED;
91 oapv_tpool_leave_cs(ctx->sync_obj);
92 }
93 ERR:
94 return ret;
95 }
96
oapve_rc_get_tile_cost_thread(oapve_ctx_t * ctx,u64 * sum)97 int oapve_rc_get_tile_cost_thread(oapve_ctx_t* ctx, u64* sum)
98 {
99 for (int i = 0; i < ctx->num_tiles; i++) {
100 ctx->tile[i].stat = ENC_TILE_STAT_NOT_ENCODED;
101 }
102
103 oapv_tpool_t* tpool = ctx->tpool;
104 int parallel_task = (ctx->cdesc.threads > ctx->num_tiles) ? ctx->num_tiles : ctx->cdesc.threads;
105
106 // run new threads
107 int tidx = 0;
108 for (tidx = 0; tidx < (parallel_task - 1); tidx++) {
109 tpool->run(ctx->thread_id[tidx], get_tile_cost_thread, (void*)ctx->core[tidx]);
110 }
111 // use main thread
112 int ret = get_tile_cost_thread((void*)ctx->core[tidx]);
113 oapv_assert_rv(OAPV_SUCCEEDED(ret), ret);
114
115 for (int thread_num1 = 0; thread_num1 < parallel_task - 1; thread_num1++) {
116 int res = tpool->join(ctx->thread_id[thread_num1], &ret);
117 oapv_assert_rv(res == TPOOL_SUCCESS, ret);
118 oapv_assert_rv(OAPV_SUCCEEDED(ret), ret);
119 }
120
121 *sum = 0;
122 for (int i = 0; i < ctx->num_tiles; i++)
123 {
124 *sum += ctx->tile[i].rc.cost;
125 ctx->tile[i].stat = ENC_TILE_STAT_NOT_ENCODED;
126 }
127
128 return ret;
129 }
130
rc_calculate_lambda(double alpha,double beta,double cost_pixel,double bits_pixel)131 static double rc_calculate_lambda(double alpha, double beta, double cost_pixel, double bits_pixel)
132 {
133 return ((alpha / 256.0) * pow(cost_pixel / bits_pixel, beta));
134 }
135
oapve_rc_estimate_pic_lambda(oapve_ctx_t * ctx,double cost)136 double oapve_rc_estimate_pic_lambda(oapve_ctx_t* ctx, double cost)
137 {
138 int num_pixel = ctx->w * ctx->h;
139 for (int c = 1; c < ctx->num_comp; c++)
140 {
141 num_pixel += (ctx->w * ctx->h) >> (ctx->comp_sft[c][0] + ctx->comp_sft[c][1]);
142 }
143
144 double alpha = ctx->rc_param.alpha;
145 double beta = ctx->rc_param.beta;
146 double bpp = ((double)ctx->param->bitrate * 1000) / ((double)num_pixel * ((double)ctx->param->fps_num / ctx->param->fps_den));
147
148 double est_lambda = rc_calculate_lambda(alpha, beta, pow(cost / (double)num_pixel, OAPV_RC_BETA), bpp);
149 est_lambda = oapv_clip3(0.1, 10000.0, est_lambda);
150 const int lambda_prec = 1000000;
151
152 est_lambda = (double)((s64)(est_lambda * (double)lambda_prec + 0.5)) / (double)lambda_prec;
153
154 return est_lambda;
155 }
156
oapve_rc_estimate_pic_qp(double lambda)157 int oapve_rc_estimate_pic_qp(double lambda)
158 {
159 int qp = (int)(4.2005 * log(lambda) + 13.7122 + 0.5) + OAPV_RC_QP_OFFSET;
160 qp = oapv_clip3(MIN_QUANT, MAX_QUANT, qp);
161 return qp;
162 }
163
oapve_rc_get_qp(oapve_ctx_t * ctx,oapve_tile_t * tile,int frame_qp,int * qp)164 void oapve_rc_get_qp(oapve_ctx_t* ctx, oapve_tile_t* tile, int frame_qp, int* qp)
165 {
166 double alpha = ctx->rc_param.alpha;
167 double beta = ctx->rc_param.beta;
168
169 double cost_pixel = tile->rc.cost / (double)tile->rc.number_pixel;
170 cost_pixel = pow(cost_pixel, OAPV_RC_BETA);
171
172 double bit_pixel = (double)tile->rc.target_bits / (double)tile->rc.number_pixel;
173 double est_lambda = rc_calculate_lambda(alpha, beta, cost_pixel, bit_pixel);
174
175 int min_qp = frame_qp - 2 - OAPV_RC_QP_OFFSET;
176 int max_qp = frame_qp + 2 - OAPV_RC_QP_OFFSET;
177
178 double max_lambda = exp(((double)(max_qp + 0.49) - 13.7122) / 4.2005);
179 double min_lambda = exp(((double)(min_qp - 0.49) - 13.7122) / 4.2005);
180
181 const int LAMBDA_PREC = 1000000;
182 est_lambda = oapv_clip3(min_lambda, max_lambda, est_lambda);
183 est_lambda = (double)((s64)(est_lambda * (double)LAMBDA_PREC + 0.5)) / (double)LAMBDA_PREC;
184 *qp = (int)(4.2005 * log(est_lambda) + 13.7122 + 0.5);
185 *qp = oapv_clip3(min_qp, max_qp, *qp);
186 *qp += OAPV_RC_QP_OFFSET;
187
188 }
189
oapve_rc_update_after_pic(oapve_ctx_t * ctx,double cost)190 void oapve_rc_update_after_pic(oapve_ctx_t* ctx, double cost)
191 {
192 int num_pixel = ctx->w * ctx->h;
193 for (int c = 1; c < ctx->num_comp; c++)
194 {
195 num_pixel += (ctx->w * ctx->h) >> (ctx->comp_sft[c][0] + ctx->comp_sft[c][1]);
196 }
197
198 int total_bits = 0;
199 for (int i = 0; i < ctx->num_tiles; i++)
200 {
201 total_bits += ctx->fh.tile_size[i] * 8;
202 }
203
204 double ln_bpp = log(pow(cost / (double)num_pixel, OAPV_RC_BETA));
205 double diff_lambda = (ctx->rc_param.beta) * (log((double)total_bits) - log(((double)ctx->param->bitrate * 1000 / ((double)ctx->param->fps_num / ctx->param->fps_den))));
206
207 diff_lambda = oapv_clip3(-0.125, 0.125, 0.25 * diff_lambda);
208 ctx->rc_param.alpha = (ctx->rc_param.alpha) * exp(diff_lambda);
209 ctx->rc_param.beta = (ctx->rc_param.beta) + diff_lambda / ln_bpp;
210 }
211