brintos

brintos / linux-shallow public Read only

0
0
Text · 13.5 KiB · c00197d Raw
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