/* SPDX-License-Identifier: BSD-2-Clause */ /* * h264_nal_synth.c — encode SPS / PPS NAL units from the V4L2 * stateless control structs. See header for design rationale. * * Spec references are to ITU-T H.264 (08/2021) section 7.3.2. * The RBSP encodings here cover the common profiles libva-v4l2- * request drives (Constrained Baseline, Main, High up to Hi10). * VUI parameters and seq_scaling_list payloads are NOT emitted — * we set the corresponding present flags to 0, which produces a * valid SPS / PPS that libavcodec accepts (it just uses default * scaling matrices and no VUI-derived timing). That matches the * V4L2 stateless control surface: the kernel controls don't carry * VUI fields either, so synthesising them would require fabrication. */ #include "h264_nal_synth.h" #include "bitstream_writer.h" #include #define NAL_SPS 7 #define NAL_PPS 8 #define NAL_REF_IDC_HIGHEST 3 /* * Profiles that carry the "chroma_format_idc and friends" extension * block per H.264 7.3.2.1.1. Any profile_idc not in this list skips * the chroma_format_idc/bit_depth/scaling/transform_bypass fields. */ static bool sps_has_chroma_format_block(uint8_t profile_idc) { switch (profile_idc) { case 100: case 110: case 122: case 244: case 44: case 83: case 86: case 118: case 128: case 138: case 139: case 134: case 135: return true; default: return false; } } /* * Insert emulation prevention bytes into @rbsp[0..len) and copy the * result into @out. Returns the number of bytes written to @out. * If the result would exceed @out_cap, returns 0. * * Rule (H.264 7.4.1.1): in the byte stream, any subsequence * 0x00 0x00 0x00, 0x00 0x00 0x01, 0x00 0x00 0x02, or 0x00 0x00 0x03 * inside the EBSP must be expanded to 0x00 0x00 0x03 . * Practically: scan the RBSP, after every "0x00 0x00" output the * 0x03 escape if the next byte is <= 0x03. */ static size_t emulation_prevent(const uint8_t *rbsp, size_t len, uint8_t *out, size_t out_cap) { size_t i, w = 0; int zeros = 0; for (i = 0; i < len; i++) { uint8_t b = rbsp[i]; if (zeros >= 2 && b <= 0x03) { if (w >= out_cap) return 0; out[w++] = 0x03; zeros = 0; } if (w >= out_cap) return 0; out[w++] = b; if (b == 0x00) zeros++; else zeros = 0; } return w; } /* * Emit AnnexB start code + nal_unit_header + EBSP into @out. * @rbsp/@rbsp_len is the raw RBSP (already byte-aligned). * Returns total bytes written, or 0 on overflow. */ static size_t wrap_nal_annexb(uint8_t nal_unit_type, uint8_t nal_ref_idc, const uint8_t *rbsp, size_t rbsp_len, uint8_t *out, size_t out_cap) { uint8_t header; size_t w = 0, ebsp_len; if (out_cap < 5) return 0; /* start code: 0x00 0x00 0x00 0x01 (4-byte form — safe for any * concatenation with other NALs since 3-byte form would risk * confusion when the preceding NAL ends in 0x00). */ out[w++] = 0x00; out[w++] = 0x00; out[w++] = 0x00; out[w++] = 0x01; header = (uint8_t) (((nal_ref_idc & 0x3) << 5) | (nal_unit_type & 0x1f)); out[w++] = header; ebsp_len = emulation_prevent(rbsp, rbsp_len, out + w, out_cap - w); if (ebsp_len == 0 && rbsp_len > 0) return 0; w += ebsp_len; return w; } size_t h264_synth_sps(const struct v4l2_ctrl_h264_sps *sps, uint8_t *out, size_t out_cap) { uint8_t rbsp[512]; struct bs_writer bs; uint32_t flags = sps->flags; bool has_chroma_block = sps_has_chroma_format_block(sps->profile_idc); bool frame_mbs_only = !!(flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY); bsw_init(&bs, rbsp, sizeof(rbsp)); bsw_put_u(&bs, sps->profile_idc, 8); bsw_put_u(&bs, sps->constraint_set_flags, 8); bsw_put_u(&bs, sps->level_idc, 8); bsw_put_ue(&bs, sps->seq_parameter_set_id); if (has_chroma_block) { bsw_put_ue(&bs, sps->chroma_format_idc); if (sps->chroma_format_idc == 3) { bsw_put_u(&bs, (flags & V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE) ? 1 : 0, 1); } bsw_put_ue(&bs, sps->bit_depth_luma_minus8); bsw_put_ue(&bs, sps->bit_depth_chroma_minus8); bsw_put_u(&bs, (flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS) ? 1 : 0, 1); /* seq_scaling_matrix_present_flag = 0 — let libavcodec * use default scaling matrices. V4L2 ships scaling * matrices via a separate control which we don't fold * into SPS here (the libavcodec decoder ignores them * for default-flat content anyway). */ bsw_put_u(&bs, 0u, 1); } bsw_put_ue(&bs, sps->log2_max_frame_num_minus4); bsw_put_ue(&bs, sps->pic_order_cnt_type); if (sps->pic_order_cnt_type == 0) { bsw_put_ue(&bs, sps->log2_max_pic_order_cnt_lsb_minus4); } else if (sps->pic_order_cnt_type == 1) { uint32_t n = sps->num_ref_frames_in_pic_order_cnt_cycle; uint32_t i; bsw_put_u(&bs, (flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO) ? 1 : 0, 1); bsw_put_se(&bs, sps->offset_for_non_ref_pic); bsw_put_se(&bs, sps->offset_for_top_to_bottom_field); bsw_put_ue(&bs, n); if (n > 255) return 0; for (i = 0; i < n; i++) bsw_put_se(&bs, sps->offset_for_ref_frame[i]); } bsw_put_ue(&bs, sps->max_num_ref_frames); bsw_put_u(&bs, (flags & V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED) ? 1 : 0, 1); bsw_put_ue(&bs, sps->pic_width_in_mbs_minus1); bsw_put_ue(&bs, sps->pic_height_in_map_units_minus1); bsw_put_u(&bs, frame_mbs_only ? 1u : 0u, 1); if (!frame_mbs_only) { bsw_put_u(&bs, (flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) ? 1 : 0, 1); } bsw_put_u(&bs, (flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) ? 1 : 0, 1); /* frame_cropping_flag = 0 — V4L2 SPS doesn't carry crop offsets. * libva/ffmpeg uses the surface dimensions from * VAPictureParameterBufferH264 directly so the SPS crop is * informational for libavcodec output sizing only; absent crop * means the daemon's output equals the encoded size, which * matches our wire protocol's capture_width/height. */ bsw_put_u(&bs, 0u, 1); /* vui_parameters_present_flag = 0 */ bsw_put_u(&bs, 0u, 1); bsw_align_rbsp(&bs); if (bsw_overflowed(&bs)) return 0; return wrap_nal_annexb(NAL_SPS, NAL_REF_IDC_HIGHEST, rbsp, bsw_bytes(&bs), out, out_cap); } size_t h264_synth_pps(const struct v4l2_ctrl_h264_pps *pps, uint8_t *out, size_t out_cap) { uint8_t rbsp[128]; struct bs_writer bs; uint16_t flags = pps->flags; bool transform_8x8 = !!(flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE); bsw_init(&bs, rbsp, sizeof(rbsp)); bsw_put_ue(&bs, pps->pic_parameter_set_id); bsw_put_ue(&bs, pps->seq_parameter_set_id); bsw_put_u(&bs, (flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) ? 1 : 0, 1); bsw_put_u(&bs, (flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT) ? 1 : 0, 1); bsw_put_ue(&bs, pps->num_slice_groups_minus1); /* Slice-group map types only meaningful when num_slice_groups_minus1 > 0; * V4L2 stateless decode path doesn't surface slice group maps, so we * assume single-slice-group (0) — this is the overwhelming common case. */ bsw_put_ue(&bs, pps->num_ref_idx_l0_default_active_minus1); bsw_put_ue(&bs, pps->num_ref_idx_l1_default_active_minus1); bsw_put_u(&bs, (flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) ? 1 : 0, 1); bsw_put_u(&bs, pps->weighted_bipred_idc, 2); bsw_put_se(&bs, pps->pic_init_qp_minus26); bsw_put_se(&bs, pps->pic_init_qs_minus26); bsw_put_se(&bs, pps->chroma_qp_index_offset); bsw_put_u(&bs, (flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) ? 1 : 0, 1); bsw_put_u(&bs, (flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) ? 1 : 0, 1); bsw_put_u(&bs, (flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) ? 1 : 0, 1); /* The "more_rbsp_data()" section: only emit when we actually have * something to say. If transform_8x8 is set OR the second chroma * offset differs from the first, write the extended trailer; * otherwise stop here and let rbsp_trailing_bits close out. This * matches what ffmpeg expects — too-short PPS with default values * is fine. */ if (transform_8x8 || pps->second_chroma_qp_index_offset != pps->chroma_qp_index_offset) { bsw_put_u(&bs, transform_8x8 ? 1u : 0u, 1); /* pic_scaling_matrix_present_flag = 0 — let libavcodec * use defaults; we'd need full scaling list serialisation * to do better and it rarely matters for stateless decode. */ bsw_put_u(&bs, 0u, 1); bsw_put_se(&bs, pps->second_chroma_qp_index_offset); } bsw_align_rbsp(&bs); if (bsw_overflowed(&bs)) return 0; return wrap_nal_annexb(NAL_PPS, NAL_REF_IDC_HIGHEST, rbsp, bsw_bytes(&bs), out, out_cap); }