1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
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. The |
17 | * output will be truncated if the size is too small. |
18 | */ |
19 | static const int tmp_str_size = 1024; |
20 | |
21 | /** |
22 | * v4l2_h264_init_reflist_builder() - Initialize a P/B0/B1 reference list |
23 | * builder |
24 | * |
25 | * @b: the builder context to initialize |
26 | * @dec_params: decode parameters control |
27 | * @sps: SPS control |
28 | * @dpb: DPB to use when creating the reference list |
29 | */ |
30 | void |
31 | v4l2_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 section |
64 | * '8.2.4.1 Decoding process for picture numbers' of the spec. |
65 | * For long term references, frame_num is set to |
66 | * 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 | else |
72 | 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 | } |
106 | EXPORT_SYMBOL_GPL(v4l2_h264_init_reflist_builder); |
107 | |
108 | static 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 | |
125 | static 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 | else |
143 | return 1; |
144 | } |
145 | |
146 | /* |
147 | * For frames, short term pics are in descending pic num order and long |
148 | * term ones in ascending order. For fields, the same direction is used |
149 | * but with frame_num (wrapped). For frames, the value of pic_num and |
150 | * frame_num are the same (see formula (8-28) and (8-29)). For this |
151 | * reason we can use frame_num only and share this function between |
152 | * 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 | |
163 | static 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 | else |
182 | 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(b: builder, ref: ptra); |
192 | pocb = v4l2_h264_get_poc(b: builder, ref: ptrb); |
193 | |
194 | /* |
195 | * Short term pics with POC < cur POC first in POC descending order |
196 | * followed by short term pics with POC > cur POC in POC ascending |
197 | * 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 | |
208 | static 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 | else |
227 | 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(b: builder, ref: ptra); |
237 | pocb = v4l2_h264_get_poc(b: builder, ref: ptrb); |
238 | |
239 | /* |
240 | * Short term pics with POC > cur POC first in POC ascending order |
241 | * followed by short term pics with POC < cur POC in POC descending |
242 | * 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 alternating |
255 | * between top and bottom field references starting with the current picture |
256 | * parity. This has to be done for short term and long term references |
257 | * separately. |
258 | */ |
259 | static 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 | |
287 | static 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 | |
301 | static 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(size: tmp_str_size, GFP_KERNEL); |
308 | if (!(*out_str)) |
309 | return NULL; |
310 | |
311 | n += snprintf(buf: *out_str + n, size: tmp_str_size - n, fmt: "|" ); |
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(buf: *out_str + n, size: tmp_str_size - n, fmt: "%i%c%c|" , |
321 | frame_num, longterm ? 'l' : 's', |
322 | ref_type_to_char(ref_type: reflist[i].fields)); |
323 | } |
324 | |
325 | return *out_str; |
326 | } |
327 | |
328 | static 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(objp: buf); |
339 | } |
340 | |
341 | static 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(size: tmp_str_size, GFP_KERNEL); |
348 | if (!(*out_str)) |
349 | return NULL; |
350 | |
351 | n += snprintf(buf: *out_str + n, size: tmp_str_size - n, fmt: "|" ); |
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(b: builder, ref: reflist + i); |
356 | bool longterm = builder->refs[reflist[i].index].longterm; |
357 | |
358 | n += scnprintf(buf: *out_str + n, size: tmp_str_size - n, fmt: "%i%c%c|" , |
359 | longterm ? frame_num : poc, |
360 | longterm ? 'l' : 's', |
361 | ref_type_to_char(ref_type: reflist[i].fields)); |
362 | } |
363 | |
364 | return *out_str; |
365 | } |
366 | |
367 | static 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(objp: buf); |
378 | } |
379 | |
380 | /** |
381 | * v4l2_h264_build_p_ref_list() - Build the P reference list |
382 | * |
383 | * @builder: reference list builder context |
384 | * @reflist: 32 sized array used to store the P reference list. Each entry |
385 | * is a v4l2_h264_reference structure |
386 | * |
387 | * This functions builds the P reference lists. This procedure is describe in |
388 | * 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 that |
390 | * need to pass a P reference list to the hardware. |
391 | */ |
392 | void |
393 | v4l2_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(base: reflist, num: builder->num_valid, size: sizeof(*reflist), |
399 | cmp_func: v4l2_h264_p_ref_list_cmp, NULL, priv: builder); |
400 | |
401 | if (builder->cur_pic_fields != V4L2_H264_FRAME_REF) |
402 | reorder_field_reflist(b: builder, reflist); |
403 | |
404 | print_ref_list_p(builder, reflist); |
405 | } |
406 | EXPORT_SYMBOL_GPL(v4l2_h264_build_p_ref_list); |
407 | |
408 | /** |
409 | * v4l2_h264_build_b_ref_lists() - Build the B0/B1 reference lists |
410 | * |
411 | * @builder: reference list builder context |
412 | * @b0_reflist: 32 sized array used to store the B0 reference list. Each entry |
413 | * is a v4l2_h264_reference structure |
414 | * @b1_reflist: 32 sized array used to store the B1 reference list. Each entry |
415 | * is a v4l2_h264_reference structure |
416 | * |
417 | * This functions builds the B0/B1 reference lists. This procedure is described |
418 | * 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 that |
420 | * need to pass B0/B1 reference lists to the hardware. |
421 | */ |
422 | void |
423 | v4l2_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(base: b0_reflist, num: builder->num_valid, size: sizeof(*b0_reflist), |
430 | cmp_func: v4l2_h264_b0_ref_list_cmp, NULL, priv: builder); |
431 | |
432 | memcpy(b1_reflist, builder->unordered_reflist, |
433 | sizeof(builder->unordered_reflist[0]) * builder->num_valid); |
434 | sort_r(base: b1_reflist, num: builder->num_valid, size: sizeof(*b1_reflist), |
435 | cmp_func: v4l2_h264_b1_ref_list_cmp, NULL, priv: builder); |
436 | |
437 | if (builder->cur_pic_fields != V4L2_H264_FRAME_REF) { |
438 | reorder_field_reflist(b: builder, reflist: b0_reflist); |
439 | reorder_field_reflist(b: builder, reflist: b1_reflist); |
440 | } |
441 | |
442 | if (builder->num_valid > 1 && |
443 | !memcmp(p: b1_reflist, q: b0_reflist, size: builder->num_valid)) |
444 | swap(b1_reflist[0], b1_reflist[1]); |
445 | |
446 | print_ref_list_b(builder, reflist: b0_reflist, list_num: 0); |
447 | print_ref_list_b(builder, reflist: b1_reflist, list_num: 1); |
448 | } |
449 | EXPORT_SYMBOL_GPL(v4l2_h264_build_b_ref_lists); |
450 | |
451 | MODULE_LICENSE("GPL" ); |
452 | MODULE_DESCRIPTION("V4L2 H264 Helpers" ); |
453 | MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>" ); |
454 | |