diff --git a/include/h264-ctrls.h b/include/h264-ctrls.h index e1404d7..e877bf1 100644 --- a/include/h264-ctrls.h +++ b/include/h264-ctrls.h @@ -14,7 +14,7 @@ #include /* Our pixel format isn't stable at the moment */ -#define V4L2_PIX_FMT_H264_SLICE_RAW v4l2_fourcc('S', '2', '6', '4') /* H264 parsed slices */ +#define V4L2_PIX_FMT_H264_SLICE v4l2_fourcc('S', '2', '6', '4') /* H264 parsed slices */ /* * This is put insanely high to avoid conflicting with controls that @@ -26,6 +26,8 @@ #define V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX (V4L2_CID_MPEG_BASE+1002) #define V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS (V4L2_CID_MPEG_BASE+1003) #define V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS (V4L2_CID_MPEG_BASE+1004) +#define V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE (V4L2_CID_MPEG_BASE+1005) +#define V4L2_CID_MPEG_VIDEO_H264_START_CODE (V4L2_CID_MPEG_BASE+1006) /* enum v4l2_ctrl_type type values */ #define V4L2_CTRL_TYPE_H264_SPS 0x0110 @@ -34,6 +36,16 @@ #define V4L2_CTRL_TYPE_H264_SLICE_PARAMS 0x0113 #define V4L2_CTRL_TYPE_H264_DECODE_PARAMS 0x0114 +enum v4l2_mpeg_video_h264_decode_mode { + V4L2_MPEG_VIDEO_H264_DECODE_MODE_SLICE_BASED, + V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED, +}; + +enum v4l2_mpeg_video_h264_start_code { + V4L2_MPEG_VIDEO_H264_START_CODE_NONE, + V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, +}; + #define V4L2_H264_SPS_CONSTRAINT_SET0_FLAG 0x01 #define V4L2_H264_SPS_CONSTRAINT_SET1_FLAG 0x02 #define V4L2_H264_SPS_CONSTRAINT_SET2_FLAG 0x04 @@ -125,6 +137,10 @@ struct v4l2_h264_pred_weight_table { struct v4l2_ctrl_h264_slice_params { /* Size in bytes, including header */ __u32 size; + + /* Offset in bytes to the start of slice in the OUTPUT buffer. */ + __u32 start_byte_offset; + /* Offset in bits to slice_data() from the beginning of this slice. */ __u32 header_bit_size; @@ -186,9 +202,6 @@ struct v4l2_ctrl_h264_decode_params { struct v4l2_h264_dpb_entry dpb[16]; __u16 num_slices; __u16 nal_ref_idc; - __u8 ref_pic_list_p0[32]; - __u8 ref_pic_list_b0[32]; - __u8 ref_pic_list_b1[32]; __s32 top_field_order_cnt; __s32 bottom_field_order_cnt; __u32 flags; /* V4L2_H264_DECODE_PARAM_FLAG_* */ diff --git a/src/config.c b/src/config.c index e396268..8c08148 100644 --- a/src/config.c +++ b/src/config.c @@ -128,7 +128,7 @@ VAStatus RequestQueryConfigProfiles(VADriverContextP context, found = v4l2_find_format(driver_data->video_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_PIX_FMT_H264_SLICE_RAW); + V4L2_PIX_FMT_H264_SLICE); if (found && index < (V4L2_REQUEST_MAX_CONFIG_ATTRIBUTES - 5)) { profiles[index++] = VAProfileH264Main; profiles[index++] = VAProfileH264High; diff --git a/src/context.c b/src/context.c index 04ba9a6..c32eacb 100644 --- a/src/context.c +++ b/src/context.c @@ -104,7 +104,9 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id, case VAProfileH264ConstrainedBaseline: case VAProfileH264MultiviewHigh: case VAProfileH264StereoHigh: - pixelformat = V4L2_PIX_FMT_H264_SLICE_RAW; + pixelformat = V4L2_PIX_FMT_H264_SLICE; + /* Query decode mode and start code */ + h264_get_controls(driver_data, context_object); break; case VAProfileHEVCMain: diff --git a/src/context.h b/src/context.h index cd0910a..8f4f70f 100644 --- a/src/context.h +++ b/src/context.h @@ -50,6 +50,7 @@ struct object_context { /* H264 only */ struct h264_dpb dpb; + bool h264_start_code; }; VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id, diff --git a/src/h264.c b/src/h264.c index 25bc8cb..1209c8f 100644 --- a/src/h264.c +++ b/src/h264.c @@ -197,6 +197,7 @@ static void h264_fill_dpb(struct request_data *data, } dpb->frame_num = entry->pic.frame_idx; + dpb->pic_num = entry->pic.picture_id; dpb->top_field_order_cnt = entry->pic.TopFieldOrderCnt; dpb->bottom_field_order_cnt = entry->pic.BottomFieldOrderCnt; @@ -218,9 +219,23 @@ static void h264_va_picture_to_v4l2(struct request_data *driver_data, struct v4l2_ctrl_h264_pps *pps, struct v4l2_ctrl_h264_sps *sps) { + unsigned char *b; + unsigned char nal_ref_idc; + unsigned char nal_unit_type; + + /* Extract missing nal_ref_idc and nal_unit_type */ + b = surface->source_data; + if (context->h264_start_code) + b += 3; + nal_ref_idc = (b[0] >> 5) & 0x3; + nal_unit_type = b[0] & 0x1f; + h264_fill_dpb(driver_data, context, decode); decode->num_slices = surface->slices_count; + decode->nal_ref_idc = nal_ref_idc; + if (nal_unit_type == 5) + decode->flags = V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC; decode->top_field_order_cnt = VAPicture->CurrPic.TopFieldOrderCnt; decode->bottom_field_order_cnt = VAPicture->CurrPic.BottomFieldOrderCnt; @@ -255,6 +270,7 @@ static void h264_va_picture_to_v4l2(struct request_data *driver_data, if (VAPicture->pic_fields.bits.redundant_pic_cnt_present_flag) pps->flags |= V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT; + sps->max_num_ref_frames = VAPicture->num_ref_frames; sps->chroma_format_idc = VAPicture->seq_fields.bits.chroma_format_idc; sps->bit_depth_luma_minus8 = VAPicture->bit_depth_luma_minus8; sps->bit_depth_chroma_minus8 = VAPicture->bit_depth_chroma_minus8; @@ -330,9 +346,12 @@ static void h264_va_slice_to_v4l2(struct request_data *driver_data, struct v4l2_ctrl_h264_slice_params *slice) { slice->size = VASlice->slice_data_size; + if (context->h264_start_code) + slice->size += 3; slice->header_bit_size = VASlice->slice_data_bit_offset; slice->first_mb_in_slice = VASlice->first_mb_in_slice; slice->slice_type = VASlice->slice_type; + slice->frame_num = VAPicture->frame_num; slice->cabac_init_idc = VASlice->cabac_init_idc; slice->slice_qp_delta = VASlice->slice_qp_delta; slice->disable_deblocking_filter_idc = @@ -405,8 +424,67 @@ static void h264_va_slice_to_v4l2(struct request_data *driver_data, VASlice->chroma_offset_l1); } +int h264_get_controls(struct request_data *driver_data, + struct object_context *context) +{ + struct v4l2_ext_control controls[2] = { + { + .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE, + }, { + .id = V4L2_CID_MPEG_VIDEO_H264_START_CODE, + } + }; + int rc; + + rc = v4l2_get_controls(driver_data->video_fd, -1, controls, 2); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + switch (controls[0].value) { + case V4L2_MPEG_VIDEO_H264_DECODE_MODE_SLICE_BASED: + break; + case V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED: + break; + default: + request_log("Unsupported decode mode\n"); + return VA_STATUS_ERROR_OPERATION_FAILED; + } + + switch (controls[1].value) { + case V4L2_MPEG_VIDEO_H264_START_CODE_NONE: + break; + case V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B: + context->h264_start_code = true; + break; + default: + request_log("Unsupported start code\n"); + return VA_STATUS_ERROR_OPERATION_FAILED; + } + + return VA_STATUS_SUCCESS; +} + +static inline __u8 h264_profile_to_idc(VAProfile profile) +{ + switch (profile) { + case VAProfileH264Main: + return 77; + case VAProfileH264High: + return 100; + case VAProfileH264ConstrainedBaseline: + return 66; + case VAProfileH264MultiviewHigh: + return 118; + case VAProfileH264StereoHigh: + return 128; + default: + return 0; + } +} + int h264_set_controls(struct request_data *driver_data, struct object_context *context, + VAProfile profile, struct object_surface *surface) { struct v4l2_ctrl_h264_scaling_matrix matrix = { 0 }; @@ -435,31 +513,34 @@ int h264_set_controls(struct request_data *driver_data, &surface->params.h264.slice, &surface->params.h264.picture, &slice); - rc = v4l2_set_control(driver_data->video_fd, surface->request_fd, - V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS, &decode, - sizeof(decode)); - if (rc < 0) - return VA_STATUS_ERROR_OPERATION_FAILED; - - rc = v4l2_set_control(driver_data->video_fd, surface->request_fd, - V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS, &slice, - sizeof(slice)); - if (rc < 0) - return VA_STATUS_ERROR_OPERATION_FAILED; - - rc = v4l2_set_control(driver_data->video_fd, surface->request_fd, - V4L2_CID_MPEG_VIDEO_H264_PPS, &pps, sizeof(pps)); - if (rc < 0) - return VA_STATUS_ERROR_OPERATION_FAILED; - - rc = v4l2_set_control(driver_data->video_fd, surface->request_fd, - V4L2_CID_MPEG_VIDEO_H264_SPS, &sps, sizeof(sps)); - if (rc < 0) - return VA_STATUS_ERROR_OPERATION_FAILED; + sps.profile_idc = h264_profile_to_idc(profile); + + struct v4l2_ext_control controls[5] = { + { + .id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .ptr = &sps, + .size = sizeof(sps), + }, { + .id = V4L2_CID_MPEG_VIDEO_H264_PPS, + .ptr = &pps, + .size = sizeof(pps), + }, { + .id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, + .ptr = &matrix, + .size = sizeof(matrix), + }, { + .id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS, + .ptr = &slice, + .size = sizeof(slice), + }, { + .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS, + .ptr = &decode, + .size = sizeof(decode), + } + }; - rc = v4l2_set_control(driver_data->video_fd, surface->request_fd, - V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, &matrix, - sizeof(matrix)); + rc = v4l2_set_controls(driver_data->video_fd, surface->request_fd, + controls, 5); if (rc < 0) return VA_STATUS_ERROR_OPERATION_FAILED; diff --git a/src/h264.h b/src/h264.h index 35ef31d..da0b87f 100644 --- a/src/h264.h +++ b/src/h264.h @@ -51,8 +51,11 @@ struct h264_dpb { unsigned int age; }; +int h264_get_controls(struct request_data *driver_data, + struct object_context *context); int h264_set_controls(struct request_data *data, struct object_context *context, + VAProfile profile, struct object_surface *surface); #endif diff --git a/src/picture.c b/src/picture.c index aa86265..a65dd7f 100644 --- a/src/picture.c +++ b/src/picture.c @@ -51,6 +51,7 @@ #include "autoconfig.h" static VAStatus codec_store_buffer(struct request_data *driver_data, + struct object_context *context, VAProfile profile, struct object_surface *surface_object, struct object_buffer *buffer_object) @@ -63,6 +64,14 @@ static VAStatus codec_store_buffer(struct request_data *driver_data, * RenderPicture), we can't use a V4L2 buffer directly * and have to copy from a regular buffer. */ + if (context->h264_start_code) { + static const char start_code[3] = { 0x00, 0x00, 0x01 }; + + memcpy(surface_object->source_data + + surface_object->slices_size, + start_code, sizeof(start_code)); + surface_object->slices_size += sizeof(start_code); + } memcpy(surface_object->source_data + surface_object->slices_size, buffer_object->data, @@ -184,7 +193,8 @@ static VAStatus codec_set_controls(struct request_data *driver_data, case VAProfileH264ConstrainedBaseline: case VAProfileH264MultiviewHigh: case VAProfileH264StereoHigh: - rc = h264_set_controls(driver_data, context, surface_object); + rc = h264_set_controls(driver_data, context, profile, + surface_object); if (rc < 0) return VA_STATUS_ERROR_OPERATION_FAILED; break; @@ -255,7 +265,8 @@ VAStatus RequestRenderPicture(VADriverContextP context, VAContextID context_id, if (buffer_object == NULL) return VA_STATUS_ERROR_INVALID_BUFFER; - rc = codec_store_buffer(driver_data, config_object->profile, + rc = codec_store_buffer(driver_data, context_object, + config_object->profile, surface_object, buffer_object); if (rc != VA_STATUS_SUCCESS) return rc; diff --git a/src/v4l2.c b/src/v4l2.c index d5000ac..3addb33 100644 --- a/src/v4l2.c +++ b/src/v4l2.c @@ -428,37 +428,71 @@ int v4l2_export_buffer(int video_fd, unsigned int type, unsigned int index, return 0; } -int v4l2_set_control(int video_fd, int request_fd, unsigned int id, void *data, - unsigned int size) +static int v4l2_ioctl_controls(int video_fd, int request_fd, unsigned long ioc, + struct v4l2_ext_control *control_array, + unsigned int num_controls) { - struct v4l2_ext_control control; struct v4l2_ext_controls controls; - int rc; - memset(&control, 0, sizeof(control)); memset(&controls, 0, sizeof(controls)); - control.id = id; - control.ptr = data; - control.size = size; - - controls.controls = &control; - controls.count = 1; + controls.controls = control_array; + controls.count = num_controls; if (request_fd >= 0) { controls.which = V4L2_CTRL_WHICH_REQUEST_VAL; controls.request_fd = request_fd; } - rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &controls); + return ioctl(video_fd, ioc, &controls); +} + +int v4l2_get_controls(int video_fd, int request_fd, + struct v4l2_ext_control *control_array, + unsigned int num_controls) +{ + int rc; + + rc = v4l2_ioctl_controls(video_fd, request_fd, VIDIOC_G_EXT_CTRLS, + control_array, num_controls); if (rc < 0) { - request_log("Unable to set control: %s\n", strerror(errno)); + request_log("Unable to get control(s): %s\n", strerror(errno)); return -1; } return 0; } +int v4l2_set_controls(int video_fd, int request_fd, + struct v4l2_ext_control *control_array, + unsigned int num_controls) +{ + int rc; + + rc = v4l2_ioctl_controls(video_fd, request_fd, VIDIOC_S_EXT_CTRLS, + control_array, num_controls); + if (rc < 0) { + request_log("Unable to set control(s): %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +int v4l2_set_control(int video_fd, int request_fd, unsigned int id, void *data, + unsigned int size) +{ + struct v4l2_ext_control control; + + memset(&control, 0, sizeof(control)); + + control.id = id; + control.ptr = data; + control.size = size; + + return v4l2_set_controls(video_fd, request_fd, &control, 1); +} + int v4l2_set_stream(int video_fd, unsigned int type, bool enable) { enum v4l2_buf_type buf_type = type; diff --git a/src/v4l2.h b/src/v4l2.h index 73e9a42..24c12a0 100644 --- a/src/v4l2.h +++ b/src/v4l2.h @@ -54,6 +54,12 @@ int v4l2_dequeue_buffer(int video_fd, int request_fd, unsigned int type, int v4l2_export_buffer(int video_fd, unsigned int type, unsigned int index, unsigned int flags, int *export_fds, unsigned int export_fds_count); +int v4l2_get_controls(int video_fd, int request_fd, + struct v4l2_ext_control *controls, + unsigned int num_controls); +int v4l2_set_controls(int video_fd, int request_fd, + struct v4l2_ext_control *controls, + unsigned int num_controls); int v4l2_set_control(int video_fd, int request_fd, unsigned int id, void *data, unsigned int size); int v4l2_set_stream(int video_fd, unsigned int type, bool enable);