2020-04-04 01:13:41 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* V4L2 H264 helpers .
*
* Copyright ( C ) 2019 Collabora , Ltd .
*
* Author : Boris Brezillon < boris . brezillon @ collabora . com >
*/
# include <linux/module.h>
# include <linux/sort.h>
# include <media/v4l2-h264.h>
/**
* v4l2_h264_init_reflist_builder ( ) - Initialize a P / B0 / B1 reference list
* builder
*
* @ b : the builder context to initialize
* @ dec_params : decode parameters control
* @ sps : SPS control
* @ dpb : DPB to use when creating the reference list
*/
void
v4l2_h264_init_reflist_builder ( struct v4l2_h264_reflist_builder * b ,
const struct v4l2_ctrl_h264_decode_params * dec_params ,
const struct v4l2_ctrl_h264_sps * sps ,
const struct v4l2_h264_dpb_entry dpb [ V4L2_H264_NUM_DPB_ENTRIES ] )
{
int cur_frame_num , max_frame_num ;
unsigned int i ;
max_frame_num = 1 < < ( sps - > log2_max_frame_num_minus4 + 4 ) ;
2020-08-25 06:52:36 +03:00
cur_frame_num = dec_params - > frame_num ;
2020-04-04 01:13:41 +03:00
memset ( b , 0 , sizeof ( * b ) ) ;
2020-08-25 06:52:36 +03:00
if ( ! ( dec_params - > flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC ) )
2020-04-04 01:13:41 +03:00
b - > cur_pic_order_count = min ( dec_params - > bottom_field_order_cnt ,
dec_params - > top_field_order_cnt ) ;
2020-08-25 06:52:36 +03:00
else if ( dec_params - > flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD )
2020-04-04 01:13:41 +03:00
b - > cur_pic_order_count = dec_params - > bottom_field_order_cnt ;
else
b - > cur_pic_order_count = dec_params - > top_field_order_cnt ;
for ( i = 0 ; i < V4L2_H264_NUM_DPB_ENTRIES ; i + + ) {
u32 pic_order_count ;
if ( ! ( dpb [ i ] . flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE ) )
continue ;
b - > refs [ i ] . pic_num = dpb [ i ] . pic_num ;
if ( dpb [ i ] . flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM )
b - > refs [ i ] . longterm = true ;
/*
* Handle frame_num wraparound as described in section
* ' 8.2 .4 .1 Decoding process for picture numbers ' of the spec .
* TODO : This logic will have to be adjusted when we start
* supporting interlaced content .
*/
if ( dpb [ i ] . frame_num > cur_frame_num )
b - > refs [ i ] . frame_num = ( int ) dpb [ i ] . frame_num -
max_frame_num ;
else
b - > refs [ i ] . frame_num = dpb [ i ] . frame_num ;
2020-08-25 06:52:32 +03:00
if ( dpb [ i ] . fields = = V4L2_H264_FRAME_REF )
2020-04-04 01:13:41 +03:00
pic_order_count = min ( dpb [ i ] . top_field_order_cnt ,
dpb [ i ] . bottom_field_order_cnt ) ;
2020-08-25 06:52:32 +03:00
else if ( dpb [ i ] . fields & V4L2_H264_BOTTOM_FIELD_REF )
2020-04-04 01:13:41 +03:00
pic_order_count = dpb [ i ] . bottom_field_order_cnt ;
else
pic_order_count = dpb [ i ] . top_field_order_cnt ;
b - > refs [ i ] . pic_order_count = pic_order_count ;
b - > unordered_reflist [ b - > num_valid ] = i ;
b - > num_valid + + ;
}
for ( i = b - > num_valid ; i < ARRAY_SIZE ( b - > unordered_reflist ) ; i + + )
b - > unordered_reflist [ i ] = i ;
}
EXPORT_SYMBOL_GPL ( v4l2_h264_init_reflist_builder ) ;
static int v4l2_h264_p_ref_list_cmp ( const void * ptra , const void * ptrb ,
const void * data )
{
const struct v4l2_h264_reflist_builder * builder = data ;
u8 idxa , idxb ;
idxa = * ( ( u8 * ) ptra ) ;
idxb = * ( ( u8 * ) ptrb ) ;
if ( WARN_ON ( idxa > = V4L2_H264_NUM_DPB_ENTRIES | |
idxb > = V4L2_H264_NUM_DPB_ENTRIES ) )
return 1 ;
if ( builder - > refs [ idxa ] . longterm ! = builder - > refs [ idxb ] . longterm ) {
/* Short term pics first. */
if ( ! builder - > refs [ idxa ] . longterm )
return - 1 ;
else
return 1 ;
}
/*
* Short term pics in descending pic num order , long term ones in
* ascending order .
*/
if ( ! builder - > refs [ idxa ] . longterm )
return builder - > refs [ idxb ] . frame_num <
builder - > refs [ idxa ] . frame_num ?
- 1 : 1 ;
return builder - > refs [ idxa ] . pic_num < builder - > refs [ idxb ] . pic_num ?
- 1 : 1 ;
}
static int v4l2_h264_b0_ref_list_cmp ( const void * ptra , const void * ptrb ,
const void * data )
{
const struct v4l2_h264_reflist_builder * builder = data ;
s32 poca , pocb ;
u8 idxa , idxb ;
idxa = * ( ( u8 * ) ptra ) ;
idxb = * ( ( u8 * ) ptrb ) ;
if ( WARN_ON ( idxa > = V4L2_H264_NUM_DPB_ENTRIES | |
idxb > = V4L2_H264_NUM_DPB_ENTRIES ) )
return 1 ;
if ( builder - > refs [ idxa ] . longterm ! = builder - > refs [ idxb ] . longterm ) {
/* Short term pics first. */
if ( ! builder - > refs [ idxa ] . longterm )
return - 1 ;
else
return 1 ;
}
/* Long term pics in ascending pic num order. */
if ( builder - > refs [ idxa ] . longterm )
return builder - > refs [ idxa ] . pic_num <
builder - > refs [ idxb ] . pic_num ?
- 1 : 1 ;
poca = builder - > refs [ idxa ] . pic_order_count ;
pocb = builder - > refs [ idxb ] . pic_order_count ;
/*
* Short term pics with POC < cur POC first in POC descending order
* followed by short term pics with POC > cur POC in POC ascending
* order .
*/
if ( ( poca < builder - > cur_pic_order_count ) ! =
( pocb < builder - > cur_pic_order_count ) )
return poca < pocb ? - 1 : 1 ;
else if ( poca < builder - > cur_pic_order_count )
return pocb < poca ? - 1 : 1 ;
return poca < pocb ? - 1 : 1 ;
}
static int v4l2_h264_b1_ref_list_cmp ( const void * ptra , const void * ptrb ,
const void * data )
{
const struct v4l2_h264_reflist_builder * builder = data ;
s32 poca , pocb ;
u8 idxa , idxb ;
idxa = * ( ( u8 * ) ptra ) ;
idxb = * ( ( u8 * ) ptrb ) ;
if ( WARN_ON ( idxa > = V4L2_H264_NUM_DPB_ENTRIES | |
idxb > = V4L2_H264_NUM_DPB_ENTRIES ) )
return 1 ;
if ( builder - > refs [ idxa ] . longterm ! = builder - > refs [ idxb ] . longterm ) {
/* Short term pics first. */
if ( ! builder - > refs [ idxa ] . longterm )
return - 1 ;
else
return 1 ;
}
/* Long term pics in ascending pic num order. */
if ( builder - > refs [ idxa ] . longterm )
return builder - > refs [ idxa ] . pic_num <
builder - > refs [ idxb ] . pic_num ?
- 1 : 1 ;
poca = builder - > refs [ idxa ] . pic_order_count ;
pocb = builder - > refs [ idxb ] . pic_order_count ;
/*
* Short term pics with POC > cur POC first in POC ascending order
* followed by short term pics with POC < cur POC in POC descending
* order .
*/
if ( ( poca < builder - > cur_pic_order_count ) ! =
( pocb < builder - > cur_pic_order_count ) )
return pocb < poca ? - 1 : 1 ;
else if ( poca < builder - > cur_pic_order_count )
return pocb < poca ? - 1 : 1 ;
return poca < pocb ? - 1 : 1 ;
}
/**
* v4l2_h264_build_p_ref_list ( ) - Build the P reference list
*
* @ builder : reference list builder context
* @ reflist : 16 - bytes array used to store the P reference list . Each entry
* is an index in the DPB
*
* This functions builds the P reference lists . This procedure is describe in
* section ' 8.2 .4 Decoding process for reference picture lists construction '
* of the H264 spec . This function can be used by H264 decoder drivers that
* need to pass a P reference list to the hardware .
*/
void
v4l2_h264_build_p_ref_list ( const struct v4l2_h264_reflist_builder * builder ,
u8 * reflist )
{
memcpy ( reflist , builder - > unordered_reflist ,
sizeof ( builder - > unordered_reflist [ 0 ] ) * builder - > num_valid ) ;
sort_r ( reflist , builder - > num_valid , sizeof ( * reflist ) ,
v4l2_h264_p_ref_list_cmp , NULL , builder ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_h264_build_p_ref_list ) ;
/**
* v4l2_h264_build_b_ref_lists ( ) - Build the B0 / B1 reference lists
*
* @ builder : reference list builder context
* @ b0_reflist : 16 - bytes array used to store the B0 reference list . Each entry
* is an index in the DPB
* @ b1_reflist : 16 - bytes array used to store the B1 reference list . Each entry
* is an index in the DPB
*
* This functions builds the B0 / B1 reference lists . This procedure is described
* in section ' 8.2 .4 Decoding process for reference picture lists construction '
* of the H264 spec . This function can be used by H264 decoder drivers that
* need to pass B0 / B1 reference lists to the hardware .
*/
void
v4l2_h264_build_b_ref_lists ( const struct v4l2_h264_reflist_builder * builder ,
u8 * b0_reflist , u8 * b1_reflist )
{
memcpy ( b0_reflist , builder - > unordered_reflist ,
sizeof ( builder - > unordered_reflist [ 0 ] ) * builder - > num_valid ) ;
sort_r ( b0_reflist , builder - > num_valid , sizeof ( * b0_reflist ) ,
v4l2_h264_b0_ref_list_cmp , NULL , builder ) ;
memcpy ( b1_reflist , builder - > unordered_reflist ,
sizeof ( builder - > unordered_reflist [ 0 ] ) * builder - > num_valid ) ;
sort_r ( b1_reflist , builder - > num_valid , sizeof ( * b1_reflist ) ,
v4l2_h264_b1_ref_list_cmp , NULL , builder ) ;
if ( builder - > num_valid > 1 & &
! memcmp ( b1_reflist , b0_reflist , builder - > num_valid ) )
swap ( b1_reflist [ 0 ] , b1_reflist [ 1 ] ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_h264_build_b_ref_lists ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " V4L2 H264 Helpers " ) ;
MODULE_AUTHOR ( " Boris Brezillon <boris.brezillon@collabora.com> " ) ;