454 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * V4L2 H264 helpers.4 *5 * Copyright (C) 2019 Collabora, Ltd.6 *7 * Author: Boris Brezillon <boris.brezillon@collabora.com>8 */9 10#include <linux/module.h>11#include <linux/sort.h>12 13#include <media/v4l2-h264.h>14 15/*16 * Size of the tempory buffer allocated when printing reference lists. The17 * output will be truncated if the size is too small.18 */19static const int tmp_str_size = 1024;20 21/**22 * v4l2_h264_init_reflist_builder() - Initialize a P/B0/B1 reference list23 * builder24 *25 * @b: the builder context to initialize26 * @dec_params: decode parameters control27 * @sps: SPS control28 * @dpb: DPB to use when creating the reference list29 */30void31v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b,32 const struct v4l2_ctrl_h264_decode_params *dec_params,33 const struct v4l2_ctrl_h264_sps *sps,34 const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES])35{36 int cur_frame_num, max_frame_num;37 unsigned int i;38 39 max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4);40 cur_frame_num = dec_params->frame_num;41 42 memset(b, 0, sizeof(*b));43 if (!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) {44 b->cur_pic_order_count = min(dec_params->bottom_field_order_cnt,45 dec_params->top_field_order_cnt);46 b->cur_pic_fields = V4L2_H264_FRAME_REF;47 } else if (dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) {48 b->cur_pic_order_count = dec_params->bottom_field_order_cnt;49 b->cur_pic_fields = V4L2_H264_BOTTOM_FIELD_REF;50 } else {51 b->cur_pic_order_count = dec_params->top_field_order_cnt;52 b->cur_pic_fields = V4L2_H264_TOP_FIELD_REF;53 }54 55 for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {56 if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))57 continue;58 59 if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)60 b->refs[i].longterm = true;61 62 /*63 * Handle frame_num wraparound as described in section64 * '8.2.4.1 Decoding process for picture numbers' of the spec.65 * For long term references, frame_num is set to66 * long_term_frame_idx which requires no wrapping.67 */68 if (!b->refs[i].longterm && dpb[i].frame_num > cur_frame_num)69 b->refs[i].frame_num = (int)dpb[i].frame_num -70 max_frame_num;71 else72 b->refs[i].frame_num = dpb[i].frame_num;73 74 b->refs[i].top_field_order_cnt = dpb[i].top_field_order_cnt;75 b->refs[i].bottom_field_order_cnt = dpb[i].bottom_field_order_cnt;76 77 if (b->cur_pic_fields == V4L2_H264_FRAME_REF) {78 u8 fields = V4L2_H264_FRAME_REF;79 80 b->unordered_reflist[b->num_valid].index = i;81 b->unordered_reflist[b->num_valid].fields = fields;82 b->num_valid++;83 continue;84 }85 86 if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) {87 u8 fields = V4L2_H264_TOP_FIELD_REF;88 89 b->unordered_reflist[b->num_valid].index = i;90 b->unordered_reflist[b->num_valid].fields = fields;91 b->num_valid++;92 }93 94 if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) {95 u8 fields = V4L2_H264_BOTTOM_FIELD_REF;96 97 b->unordered_reflist[b->num_valid].index = i;98 b->unordered_reflist[b->num_valid].fields = fields;99 b->num_valid++;100 }101 }102 103 for (i = b->num_valid; i < ARRAY_SIZE(b->unordered_reflist); i++)104 b->unordered_reflist[i].index = i;105}106EXPORT_SYMBOL_GPL(v4l2_h264_init_reflist_builder);107 108static s32 v4l2_h264_get_poc(const struct v4l2_h264_reflist_builder *b,109 const struct v4l2_h264_reference *ref)110{111 switch (ref->fields) {112 case V4L2_H264_FRAME_REF:113 return min(b->refs[ref->index].top_field_order_cnt,114 b->refs[ref->index].bottom_field_order_cnt);115 case V4L2_H264_TOP_FIELD_REF:116 return b->refs[ref->index].top_field_order_cnt;117 case V4L2_H264_BOTTOM_FIELD_REF:118 return b->refs[ref->index].bottom_field_order_cnt;119 }120 121 /* not reached */122 return 0;123}124 125static int v4l2_h264_p_ref_list_cmp(const void *ptra, const void *ptrb,126 const void *data)127{128 const struct v4l2_h264_reflist_builder *builder = data;129 u8 idxa, idxb;130 131 idxa = ((struct v4l2_h264_reference *)ptra)->index;132 idxb = ((struct v4l2_h264_reference *)ptrb)->index;133 134 if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||135 idxb >= V4L2_H264_NUM_DPB_ENTRIES))136 return 1;137 138 if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) {139 /* Short term pics first. */140 if (!builder->refs[idxa].longterm)141 return -1;142 else143 return 1;144 }145 146 /*147 * For frames, short term pics are in descending pic num order and long148 * term ones in ascending order. For fields, the same direction is used149 * but with frame_num (wrapped). For frames, the value of pic_num and150 * frame_num are the same (see formula (8-28) and (8-29)). For this151 * reason we can use frame_num only and share this function between152 * frames and fields reflist.153 */154 if (!builder->refs[idxa].longterm)155 return builder->refs[idxb].frame_num <156 builder->refs[idxa].frame_num ?157 -1 : 1;158 159 return builder->refs[idxa].frame_num < builder->refs[idxb].frame_num ?160 -1 : 1;161}162 163static int v4l2_h264_b0_ref_list_cmp(const void *ptra, const void *ptrb,164 const void *data)165{166 const struct v4l2_h264_reflist_builder *builder = data;167 s32 poca, pocb;168 u8 idxa, idxb;169 170 idxa = ((struct v4l2_h264_reference *)ptra)->index;171 idxb = ((struct v4l2_h264_reference *)ptrb)->index;172 173 if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||174 idxb >= V4L2_H264_NUM_DPB_ENTRIES))175 return 1;176 177 if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) {178 /* Short term pics first. */179 if (!builder->refs[idxa].longterm)180 return -1;181 else182 return 1;183 }184 185 /* Long term pics in ascending frame num order. */186 if (builder->refs[idxa].longterm)187 return builder->refs[idxa].frame_num <188 builder->refs[idxb].frame_num ?189 -1 : 1;190 191 poca = v4l2_h264_get_poc(builder, ptra);192 pocb = v4l2_h264_get_poc(builder, ptrb);193 194 /*195 * Short term pics with POC < cur POC first in POC descending order196 * followed by short term pics with POC > cur POC in POC ascending197 * order.198 */199 if ((poca < builder->cur_pic_order_count) !=200 (pocb < builder->cur_pic_order_count))201 return poca < pocb ? -1 : 1;202 else if (poca < builder->cur_pic_order_count)203 return pocb < poca ? -1 : 1;204 205 return poca < pocb ? -1 : 1;206}207 208static int v4l2_h264_b1_ref_list_cmp(const void *ptra, const void *ptrb,209 const void *data)210{211 const struct v4l2_h264_reflist_builder *builder = data;212 s32 poca, pocb;213 u8 idxa, idxb;214 215 idxa = ((struct v4l2_h264_reference *)ptra)->index;216 idxb = ((struct v4l2_h264_reference *)ptrb)->index;217 218 if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||219 idxb >= V4L2_H264_NUM_DPB_ENTRIES))220 return 1;221 222 if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) {223 /* Short term pics first. */224 if (!builder->refs[idxa].longterm)225 return -1;226 else227 return 1;228 }229 230 /* Long term pics in ascending frame num order. */231 if (builder->refs[idxa].longterm)232 return builder->refs[idxa].frame_num <233 builder->refs[idxb].frame_num ?234 -1 : 1;235 236 poca = v4l2_h264_get_poc(builder, ptra);237 pocb = v4l2_h264_get_poc(builder, ptrb);238 239 /*240 * Short term pics with POC > cur POC first in POC ascending order241 * followed by short term pics with POC < cur POC in POC descending242 * order.243 */244 if ((poca < builder->cur_pic_order_count) !=245 (pocb < builder->cur_pic_order_count))246 return pocb < poca ? -1 : 1;247 else if (poca < builder->cur_pic_order_count)248 return pocb < poca ? -1 : 1;249 250 return poca < pocb ? -1 : 1;251}252 253/*254 * The references need to be reordered so that references are alternating255 * between top and bottom field references starting with the current picture256 * parity. This has to be done for short term and long term references257 * separately.258 */259static void reorder_field_reflist(const struct v4l2_h264_reflist_builder *b,260 struct v4l2_h264_reference *reflist)261{262 struct v4l2_h264_reference tmplist[V4L2_H264_REF_LIST_LEN];263 u8 lt, i = 0, j = 0, k = 0;264 265 memcpy(tmplist, reflist, sizeof(tmplist[0]) * b->num_valid);266 267 for (lt = 0; lt <= 1; lt++) {268 do {269 for (; i < b->num_valid && b->refs[tmplist[i].index].longterm == lt; i++) {270 if (tmplist[i].fields == b->cur_pic_fields) {271 reflist[k++] = tmplist[i++];272 break;273 }274 }275 276 for (; j < b->num_valid && b->refs[tmplist[j].index].longterm == lt; j++) {277 if (tmplist[j].fields != b->cur_pic_fields) {278 reflist[k++] = tmplist[j++];279 break;280 }281 }282 } while ((i < b->num_valid && b->refs[tmplist[i].index].longterm == lt) ||283 (j < b->num_valid && b->refs[tmplist[j].index].longterm == lt));284 }285}286 287static char ref_type_to_char(u8 ref_type)288{289 switch (ref_type) {290 case V4L2_H264_FRAME_REF:291 return 'f';292 case V4L2_H264_TOP_FIELD_REF:293 return 't';294 case V4L2_H264_BOTTOM_FIELD_REF:295 return 'b';296 }297 298 return '?';299}300 301static const char *format_ref_list_p(const struct v4l2_h264_reflist_builder *builder,302 struct v4l2_h264_reference *reflist,303 char **out_str)304{305 int n = 0, i;306 307 *out_str = kmalloc(tmp_str_size, GFP_KERNEL);308 if (!(*out_str))309 return NULL;310 311 n += snprintf(*out_str + n, tmp_str_size - n, "|");312 313 for (i = 0; i < builder->num_valid; i++) {314 /* this is pic_num for frame and frame_num (wrapped) for field,315 * but for frame pic_num is equal to frame_num (wrapped).316 */317 int frame_num = builder->refs[reflist[i].index].frame_num;318 bool longterm = builder->refs[reflist[i].index].longterm;319 320 n += scnprintf(*out_str + n, tmp_str_size - n, "%i%c%c|",321 frame_num, longterm ? 'l' : 's',322 ref_type_to_char(reflist[i].fields));323 }324 325 return *out_str;326}327 328static void print_ref_list_p(const struct v4l2_h264_reflist_builder *builder,329 struct v4l2_h264_reference *reflist)330{331 char *buf = NULL;332 333 pr_debug("ref_pic_list_p (cur_poc %u%c) %s\n",334 builder->cur_pic_order_count,335 ref_type_to_char(builder->cur_pic_fields),336 format_ref_list_p(builder, reflist, &buf));337 338 kfree(buf);339}340 341static const char *format_ref_list_b(const struct v4l2_h264_reflist_builder *builder,342 struct v4l2_h264_reference *reflist,343 char **out_str)344{345 int n = 0, i;346 347 *out_str = kmalloc(tmp_str_size, GFP_KERNEL);348 if (!(*out_str))349 return NULL;350 351 n += snprintf(*out_str + n, tmp_str_size - n, "|");352 353 for (i = 0; i < builder->num_valid; i++) {354 int frame_num = builder->refs[reflist[i].index].frame_num;355 u32 poc = v4l2_h264_get_poc(builder, reflist + i);356 bool longterm = builder->refs[reflist[i].index].longterm;357 358 n += scnprintf(*out_str + n, tmp_str_size - n, "%i%c%c|",359 longterm ? frame_num : poc,360 longterm ? 'l' : 's',361 ref_type_to_char(reflist[i].fields));362 }363 364 return *out_str;365}366 367static void print_ref_list_b(const struct v4l2_h264_reflist_builder *builder,368 struct v4l2_h264_reference *reflist, u8 list_num)369{370 char *buf = NULL;371 372 pr_debug("ref_pic_list_b%u (cur_poc %u%c) %s",373 list_num, builder->cur_pic_order_count,374 ref_type_to_char(builder->cur_pic_fields),375 format_ref_list_b(builder, reflist, &buf));376 377 kfree(buf);378}379 380/**381 * v4l2_h264_build_p_ref_list() - Build the P reference list382 *383 * @builder: reference list builder context384 * @reflist: 32 sized array used to store the P reference list. Each entry385 * is a v4l2_h264_reference structure386 *387 * This functions builds the P reference lists. This procedure is describe in388 * section '8.2.4 Decoding process for reference picture lists construction'389 * of the H264 spec. This function can be used by H264 decoder drivers that390 * need to pass a P reference list to the hardware.391 */392void393v4l2_h264_build_p_ref_list(const struct v4l2_h264_reflist_builder *builder,394 struct v4l2_h264_reference *reflist)395{396 memcpy(reflist, builder->unordered_reflist,397 sizeof(builder->unordered_reflist[0]) * builder->num_valid);398 sort_r(reflist, builder->num_valid, sizeof(*reflist),399 v4l2_h264_p_ref_list_cmp, NULL, builder);400 401 if (builder->cur_pic_fields != V4L2_H264_FRAME_REF)402 reorder_field_reflist(builder, reflist);403 404 print_ref_list_p(builder, reflist);405}406EXPORT_SYMBOL_GPL(v4l2_h264_build_p_ref_list);407 408/**409 * v4l2_h264_build_b_ref_lists() - Build the B0/B1 reference lists410 *411 * @builder: reference list builder context412 * @b0_reflist: 32 sized array used to store the B0 reference list. Each entry413 * is a v4l2_h264_reference structure414 * @b1_reflist: 32 sized array used to store the B1 reference list. Each entry415 * is a v4l2_h264_reference structure416 *417 * This functions builds the B0/B1 reference lists. This procedure is described418 * in section '8.2.4 Decoding process for reference picture lists construction'419 * of the H264 spec. This function can be used by H264 decoder drivers that420 * need to pass B0/B1 reference lists to the hardware.421 */422void423v4l2_h264_build_b_ref_lists(const struct v4l2_h264_reflist_builder *builder,424 struct v4l2_h264_reference *b0_reflist,425 struct v4l2_h264_reference *b1_reflist)426{427 memcpy(b0_reflist, builder->unordered_reflist,428 sizeof(builder->unordered_reflist[0]) * builder->num_valid);429 sort_r(b0_reflist, builder->num_valid, sizeof(*b0_reflist),430 v4l2_h264_b0_ref_list_cmp, NULL, builder);431 432 memcpy(b1_reflist, builder->unordered_reflist,433 sizeof(builder->unordered_reflist[0]) * builder->num_valid);434 sort_r(b1_reflist, builder->num_valid, sizeof(*b1_reflist),435 v4l2_h264_b1_ref_list_cmp, NULL, builder);436 437 if (builder->cur_pic_fields != V4L2_H264_FRAME_REF) {438 reorder_field_reflist(builder, b0_reflist);439 reorder_field_reflist(builder, b1_reflist);440 }441 442 if (builder->num_valid > 1 &&443 !memcmp(b1_reflist, b0_reflist, builder->num_valid))444 swap(b1_reflist[0], b1_reflist[1]);445 446 print_ref_list_b(builder, b0_reflist, 0);447 print_ref_list_b(builder, b1_reflist, 1);448}449EXPORT_SYMBOL_GPL(v4l2_h264_build_b_ref_lists);450 451MODULE_LICENSE("GPL");452MODULE_DESCRIPTION("V4L2 H264 Helpers");453MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>");454