d3d12: Support writing H264 temporal scalability prefix slice NAL on demand

Reviewed-By: Pohsiang (John) Hsu <pohhsu@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31268>
This commit is contained in:
Sil Vilerino
2024-02-13 12:43:54 -05:00
committed by Marge Bot
parent 06787d947d
commit e0b84d4af1
10 changed files with 341 additions and 8 deletions
+159 -3
View File
@@ -297,6 +297,9 @@ d3d12_video_encoder_destroy(struct pipe_video_codec *codec)
d3d12_video_encoder_sync_completion(codec, pD3D12Enc->m_spFence.Get(), curBatchFence, OS_TIMEOUT_INFINITE);
}
if (pD3D12Enc->m_nalPrefixTmpBuffer)
pD3D12Enc->m_screen->resource_destroy(pD3D12Enc->m_screen, pD3D12Enc->m_nalPrefixTmpBuffer);
// Call d3d12_video_encoder dtor to make ComPtr and other member's destructors work
delete pD3D12Enc;
}
@@ -1611,6 +1614,32 @@ d3d12_video_encoder_create_command_objects(struct d3d12_video_encoder *pD3D12Enc
return true;
}
static inline void
d3d12_video_encoder_reallocate_prefix_nal_buffer(struct d3d12_video_encoder *pD3D12Enc,
pipe_resource** out_buffer,
uint32_t byte_size)
{
if (*out_buffer != NULL)
{
debug_printf("[d3d12_video_encoder_reallocate_prefix_nal_buffer] Destroying existing buffer at address: 0x%p byte size: %d\n", *out_buffer, (*out_buffer)->width0);
pD3D12Enc->m_screen->resource_destroy(pD3D12Enc->m_screen, *out_buffer);
*out_buffer = NULL;
}
struct pipe_resource templ = { };
memset(&templ, 0, sizeof(templ));
templ.target = PIPE_BUFFER;
templ.usage = PIPE_USAGE_DEFAULT;
templ.format = PIPE_FORMAT_R8_UINT;
templ.width0 = byte_size;
templ.height0 = 1;
templ.depth0 = 1;
templ.array_size = 1;
*out_buffer = pD3D12Enc->m_screen->resource_create(pD3D12Enc->m_screen, &templ);
assert(*out_buffer);
debug_printf("[d3d12_video_encoder_reallocate_prefix_nal_buffer] Created new buffer at address: 0x%p byte size: %d\n", *out_buffer, (*out_buffer)->width0);
}
struct pipe_video_codec *
d3d12_video_encoder_create_encoder(struct pipe_context *context, const struct pipe_video_codec *codec)
{
@@ -2014,6 +2043,9 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec,
assert(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize == pD3D12Enc->m_BitstreamHeadersBuffer.size());
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersBytePadding = 0;
// Save the pipe destination buffer the headers need to be written to in get_feedback if post encode headers needed or H264 SVC NAL prefixes, etc
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination = &pOutputBitstreamBuffer->base.b;
// Only upload headers now and leave prefix offset space gap in compressed bitstream if the codec builds headers before execution.
if (!pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].postEncodeHeadersNeeded)
{
@@ -2075,9 +2107,6 @@ d3d12_video_encoder_encode_bitstream(struct pipe_video_codec * codec,
// Headers are written after execution, have EncodeFrame write into a staging buffer
// and then get_feedback will pack the finalized bitstream and copy into comp_bit_destination
pOutputBufferD3D12Res = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].spStagingBitstream.Get();
// Save the pipe destination buffer the headers need to be written to in get_feedback
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination = &pOutputBitstreamBuffer->base.b;
}
memset(&pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_FenceData,
@@ -2462,6 +2491,133 @@ d3d12_video_encoder_get_feedback(struct pipe_video_codec *codec,
}
else
{
#if VIDEO_CODEC_H264ENC
// Add H264 temporal layers slice nal prefixes if necessary
if ((D3D12_VIDEO_ENCODER_CODEC_H264 == pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig.m_encoderCodecDesc)
&& (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig.m_ConfigDirtyFlags & d3d12_video_encoder_config_dirty_flag_svcprefix_slice_header)
&& (pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig.m_encoderCodecSpecificSequenceStateDescH264.num_temporal_layers > 1))
{
if (!pD3D12Enc->m_nalPrefixTmpBuffer)
d3d12_video_encoder_reallocate_prefix_nal_buffer(pD3D12Enc,
&pD3D12Enc->m_nalPrefixTmpBuffer,
D3D12_DEFAULT_COMPBIT_STAGING_SIZE);
assert (pD3D12Enc->m_nalPrefixTmpBuffer);
//
// Copy slices from driver comp_bit_destination into m_nalPrefixTmpBuffer with collated slices NAL prefixes
//
// Skip SPS, PPS, etc first preEncodeGeneratedHeadersByteSize bytes in src_driver_buffer_read_bytes
uint32_t src_driver_buffer_read_bytes = pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize;
uint32_t dst_tmp_buffer_written_bytes = 0;
uint32_t num_slices = pSubregionsMetadata.size();
std::vector<std::vector<uint8_t> > prefix_nal_bufs;
prefix_nal_bufs.resize(num_slices);
size_t written_prefix_nal_bytes = 0;
for (uint32_t cur_slice_idx = 0; cur_slice_idx < num_slices; cur_slice_idx++)
{
// At the end of the loop we're inserting at pSubregionsMetadata.insert(std::next(pSubregionsMetadata.begin()
// so we have to 2x the loop current index to index the current slice, skipping the prefix NALs of the previously processed slices
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA cur_subregion_metadata = pSubregionsMetadata[2 * cur_slice_idx];
d3d12_video_encoder_build_slice_svc_prefix_nalu_h264(pD3D12Enc,
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot],
prefix_nal_bufs[cur_slice_idx],
prefix_nal_bufs[cur_slice_idx].begin(),
written_prefix_nal_bytes);
// Upload nal prefix to m_nalPrefixTmpBuffer
pD3D12Enc->base.context->buffer_subdata(pD3D12Enc->base.context, // context
pD3D12Enc->m_nalPrefixTmpBuffer, // dst buffer
PIPE_MAP_WRITE, // dst usage PIPE_MAP_x
dst_tmp_buffer_written_bytes, // dst offset
written_prefix_nal_bytes, // src size
prefix_nal_bufs[cur_slice_idx].data()); // src void* buffer
dst_tmp_buffer_written_bytes += written_prefix_nal_bytes;
// Copy slice (padded as-is) cur_subregion_metadata.bSize at src_driver_buffer_read_bytes into m_nalPrefixTmpBuffer AFTER the prefix nal (written_prefix_nal_bytes) to m_nalPrefixTmpBuffer
struct pipe_box src_box = {};
u_box_3d(src_driver_buffer_read_bytes, // x
0, // y
0, // z
static_cast<int>(cur_subregion_metadata.bSize), // width
1, // height
1, // depth
&src_box
);
pD3D12Enc->base.context->resource_copy_region(pD3D12Enc->base.context, // ctx
pD3D12Enc->m_nalPrefixTmpBuffer, // dst
0, // dst_level
dst_tmp_buffer_written_bytes, // dstX - Skip the other headers in the final bitstream (e.g SPS, PPS, etc)
0, // dstY
0, // dstZ
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination, // src
0, // src level
&src_box);
src_driver_buffer_read_bytes += cur_subregion_metadata.bSize;
dst_tmp_buffer_written_bytes += cur_subregion_metadata.bSize;
// Insert prefix NAL before current slice in cur_subregion_metadata so it's treated as just another NAL below for reporting metadata
pSubregionsMetadata.insert(std::next(pSubregionsMetadata.begin(), 2 * cur_slice_idx), { /* bSize */ written_prefix_nal_bytes, /* bStartOffset */ 0, /* bHeaderSize */ 0 });
}
//
// Copy from m_nalPrefixTmpBuffer with prefixes and slices back into comp_bit_destination
//
// Make sure we have enough space in destination buffer
if (dst_tmp_buffer_written_bytes >
(pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize + pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination->width0))
{
opt_metadata.encode_result = PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED;
debug_printf("[d3d12_video_encoder] Insufficient compressed buffer size passed from frontend while building the H264 SVC NAL prefixes.\n");
assert(false);
if(pMetadata)
*pMetadata = opt_metadata;
return;
}
// Do the copy
struct pipe_box src_box = {};
u_box_3d(0, // x
0, // y
0, // z
static_cast<int>(dst_tmp_buffer_written_bytes), // width
1, // height
1, // depth
&src_box
);
pD3D12Enc->base.context->resource_copy_region(pD3D12Enc->base.context, // ctx
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].comp_bit_destination, // dst
0, // dst_level
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].preEncodeGeneratedHeadersByteSize,// dstX - Skip the other headers in the final bitstream (e.g SPS, PPS, etc)
0, // dstY
0, // dstZ
pD3D12Enc->m_nalPrefixTmpBuffer, // src
0, // src level
&src_box);
//
// Flush copies in batch and wait on this CPU thread for GPU work completion
//
{
struct pipe_fence_handle *pUploadGPUCompletionFence = NULL;
pD3D12Enc->base.context->flush(pD3D12Enc->base.context,
&pUploadGPUCompletionFence,
PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH);
assert(pUploadGPUCompletionFence);
pD3D12Enc->m_pD3D12Screen->base.fence_finish(&pD3D12Enc->m_pD3D12Screen->base,
NULL,
pUploadGPUCompletionFence,
OS_TIMEOUT_INFINITE);
pD3D12Enc->m_pD3D12Screen->base.fence_reference(&pD3D12Enc->m_pD3D12Screen->base,
&pUploadGPUCompletionFence,
NULL);
}
}
#endif // VIDEO_CODEC_H264ENC
*output_buffer_size = 0;
for (uint32_t i = 0; i < pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes.size() ; i++) {
unpadded_frame_size += pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].pWrittenCodecUnitsSizes[i];
@@ -137,6 +137,7 @@ enum d3d12_video_encoder_config_dirty_flags
d3d12_video_encoder_config_dirty_flag_picture_header = 0x2000,
d3d12_video_encoder_config_dirty_flag_aud_header = 0x4000,
d3d12_video_encoder_config_dirty_flag_sei_header = 0x8000,
d3d12_video_encoder_config_dirty_flag_svcprefix_slice_header = 0x10000,
};
DEFINE_ENUM_FLAG_OPERATORS(d3d12_video_encoder_config_dirty_flags);
@@ -287,6 +288,8 @@ struct D3D12EncodeConfiguration
struct pipe_h265_enc_seq_param m_encoderCodecSpecificSequenceStateDescH265;
struct pipe_h265_enc_vid_param m_encoderCodecSpecificVideoStateDescH265;
struct pipe_h265_enc_pic_param m_encoderCodecSpecificPictureStateDescH265;
bool m_bUsedAsReference; // Set if frame will be used as reference frame
};
struct EncodedBitstreamResolvedMetadata
@@ -408,6 +411,7 @@ struct d3d12_video_encoder
std::shared_ptr<d3d12_video_dpb_storage_manager_interface> m_upDPBStorageManager;
std::unique_ptr<d3d12_video_bitstream_builder_interface> m_upBitstreamBuilder;
pipe_resource* m_nalPrefixTmpBuffer = NULL;
std::vector<uint8_t> m_BitstreamHeadersBuffer;
std::vector<uint8_t> m_StagingHeadersBuffer;
std::vector<EncodedBitstreamResolvedMetadata> m_spEncodedFrameMetadata;
@@ -1218,7 +1218,8 @@ d3d12_video_encoder_update_current_frame_pic_params_info_av1(struct d3d12_video_
struct pipe_av1_enc_picture_desc *pAV1Pic = (struct pipe_av1_enc_picture_desc *) picture;
// Output param bUsedAsReference
bUsedAsReference = (pAV1Pic->refresh_frame_flags != 0);
pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference = (pAV1Pic->refresh_frame_flags != 0);
bUsedAsReference = pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference;
// D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAGS Flags;
picParams.pAV1PicData->Flags = D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_NONE;
@@ -356,7 +356,8 @@ d3d12_video_encoder_update_current_frame_pic_params_info_h264(struct d3d12_video
static_cast<d3d12_video_bitstream_builder_h264 *>(pD3D12Enc->m_upBitstreamBuilder.get());
assert(pH264BitstreamBuilder != nullptr);
bUsedAsReference = !h264Pic->not_referenced;
pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference = !h264Pic->not_referenced;
bUsedAsReference = pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference;
if (pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_H264CodecCaps.SupportFlags &
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE_FLAG_SLICE_SUPPORT)
@@ -391,6 +392,13 @@ d3d12_video_encoder_update_current_frame_pic_params_info_h264(struct d3d12_video
picParams.pH264PicData->pRateControlQPMap = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[h264Pic->pic_ctrl.temporal_id].m_pRateControlQPMap8Bit.data();
picParams.pH264PicData->QPMapValuesCount = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[h264Pic->pic_ctrl.temporal_id].m_pRateControlQPMap8Bit.size();
}
// Save state snapshot from record time to resolve headers at get_feedback time
uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeCapabilities =
pD3D12Enc->m_currentEncodeCapabilities;
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig =
pD3D12Enc->m_currentEncodeConfig;
}
///
@@ -933,6 +941,8 @@ d3d12_video_encoder_update_current_encoder_config_state_h264(struct d3d12_video_
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_aud_header;
else if (header->type == NAL_TYPE_SEI)
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_sei_header;
else if (header->type == NAL_TYPE_PREFIX)
pD3D12Enc->m_currentEncodeConfig.m_ConfigDirtyFlags |= d3d12_video_encoder_config_dirty_flag_svcprefix_slice_header;
}
// Set input format
@@ -1262,3 +1272,36 @@ d3d12_video_encoder_build_codec_headers_h264(struct d3d12_video_encoder *pD3D12E
static_cast<uint64_t>(pD3D12Enc->m_BitstreamHeadersBuffer.size()));
return pD3D12Enc->m_BitstreamHeadersBuffer.size();
}
uint32_t
d3d12_video_encoder_build_slice_svc_prefix_nalu_h264(struct d3d12_video_encoder * pD3D12Enc,
EncodedBitstreamResolvedMetadata& associatedMetadata,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenSVCPrefixNalBytes)
{
d3d12_video_bitstream_builder_h264 *pH264BitstreamBuilder =
static_cast<d3d12_video_bitstream_builder_h264 *>(pD3D12Enc->m_upBitstreamBuilder.get());
assert(pH264BitstreamBuilder);
H264_SLICE_PREFIX_SVC nal_svc_prefix = {};
memset(&nal_svc_prefix, 0, sizeof(nal_svc_prefix));
nal_svc_prefix.idr_flag = (associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_H264PicData.FrameType == D3D12_VIDEO_ENCODER_FRAME_TYPE_H264_IDR_FRAME) ? 1 : 0;
nal_svc_prefix.no_inter_layer_pred_flag = 1;
nal_svc_prefix.output_flag = 1;
nal_svc_prefix.discardable_flag = 1;
nal_svc_prefix.temporal_id = associatedMetadata.m_associatedEncodeConfig.m_encoderPicParamsDesc.m_H264PicData.TemporalLayerIndex;
nal_svc_prefix.priority_id = nal_svc_prefix.temporal_id;
nal_svc_prefix.nal_ref_idc = associatedMetadata.m_associatedEncodeConfig.m_bUsedAsReference ? NAL_REFIDC_REF : NAL_REFIDC_NONREF;
pH264BitstreamBuilder->write_slice_svc_prefix(nal_svc_prefix,
headerBitstream,
placingPositionStart,
writtenSVCPrefixNalBytes);
// Shrink buffer to fit the headers
if (headerBitstream.size() > (writtenSVCPrefixNalBytes)) {
headerBitstream.resize(writtenSVCPrefixNalBytes);
}
return headerBitstream.size();
}
@@ -64,4 +64,11 @@ d3d12_video_encoder_compare_slice_config_h264_hevc(
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE otherMode,
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_SLICES otherConfig);
uint32_t
d3d12_video_encoder_build_slice_svc_prefix_nalu_h264(struct d3d12_video_encoder * pD3D12Enc,
EncodedBitstreamResolvedMetadata& associatedMetadata,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenSVCPrefixNalBytes);
#endif
@@ -354,7 +354,8 @@ d3d12_video_encoder_update_current_frame_pic_params_info_hevc(struct d3d12_video
static_cast<d3d12_video_bitstream_builder_hevc *>(pD3D12Enc->m_upBitstreamBuilder.get());
assert(pHEVCBitstreamBuilder != nullptr);
bUsedAsReference = !hevcPic->not_referenced;
pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference = !hevcPic->not_referenced;
bUsedAsReference = pD3D12Enc->m_currentEncodeConfig.m_bUsedAsReference;
if (pD3D12Enc->m_currentEncodeCapabilities.m_encoderCodecSpecificConfigCaps.m_HEVCCodecCaps.SupportFlags &
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE_FLAG_SLICE_SUPPORT)
@@ -492,6 +493,13 @@ d3d12_video_encoder_update_current_frame_pic_params_info_hevc(struct d3d12_video
picParams.pHEVCPicData->pRateControlQPMap = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[hevcPic->pic.temporal_id].m_pRateControlQPMap8Bit.data();
picParams.pHEVCPicData->QPMapValuesCount = pD3D12Enc->m_currentEncodeConfig.m_encoderRateControlDesc[hevcPic->pic.temporal_id].m_pRateControlQPMap8Bit.size();
}
// Save state snapshot from record time to resolve headers at get_feedback time
uint64_t current_metadata_slot = (pD3D12Enc->m_fenceValue % D3D12_VIDEO_ENC_METADATA_BUFFERS_COUNT);
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeCapabilities =
pD3D12Enc->m_currentEncodeCapabilities;
pD3D12Enc->m_spEncodedFrameMetadata[current_metadata_slot].m_associatedEncodeConfig =
pD3D12Enc->m_currentEncodeConfig;
}
///
@@ -209,6 +209,15 @@ d3d12_video_bitstream_builder_h264::write_sei_messages(const std::vector<H264_SE
}
}
void
d3d12_video_bitstream_builder_h264::write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
m_h264Encoder.write_slice_svc_prefix(nal_svc_prefix, headerBitstream, placingPositionStart, writtenBytes);
}
H264_PPS
d3d12_video_bitstream_builder_h264::build_pps(const enum pipe_video_profile & profile,
const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264 & codecConfig,
@@ -72,6 +72,10 @@ class d3d12_video_bitstream_builder_h264 : public d3d12_video_bitstream_builder_
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void print_pps(const H264_PPS &pps);
void print_sps(const H264_SPS &sps);
@@ -268,7 +268,8 @@ uint32_t
d3d12_video_nalu_writer_h264::wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU,
d3d12_video_encoder_bitstream *pRBSP,
uint32_t iNaluIdc,
uint32_t iNaluType)
uint32_t iNaluType,
const H264_SLICE_PREFIX_SVC* pSvcExtendedHeader)
{
bool isAligned = pRBSP->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
assert(isAligned);
@@ -285,6 +286,24 @@ d3d12_video_nalu_writer_h264::wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream
pNALU->put_bits(1, 0);
pNALU->put_bits(2, iNaluIdc);
pNALU->put_bits(5, iNaluType);
if (iNaluType == NAL_TYPE_PREFIX)
{
assert(pSvcExtendedHeader);
pNALU->put_bits(1, 1); // svc_extension_flag u(1)
// nal_unit_header_svc_extension( )
pNALU->put_bits(1, pSvcExtendedHeader->idr_flag);
pNALU->put_bits(6, pSvcExtendedHeader->priority_id);
pNALU->put_bits(1, pSvcExtendedHeader->no_inter_layer_pred_flag);
pNALU->put_bits(3, pSvcExtendedHeader->dependency_id);
pNALU->put_bits(4, pSvcExtendedHeader->quality_id);
pNALU->put_bits(3, pSvcExtendedHeader->temporal_id);
pNALU->put_bits(1, pSvcExtendedHeader->use_ref_base_pic_flag);
pNALU->put_bits(1, pSvcExtendedHeader->discardable_flag);
pNALU->put_bits(1, pSvcExtendedHeader->output_flag);
pNALU->put_bits(2, 3 /* reserved_three_2bits */);
}
pNALU->flush();
// NAL body
@@ -648,3 +667,57 @@ d3d12_video_nalu_writer_h264::write_sei_nalu(H264_SEI_MESSAGE sei_
writtenBytes = naluByteSize;
}
void
d3d12_video_nalu_writer_h264::write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes)
{
d3d12_video_encoder_bitstream rbsp, nalu;
if (!rbsp.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
debug_printf("rbsp.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
assert(false);
}
if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
assert(false);
}
rbsp.set_start_code_prevention(true);
// prefix_nal_unit_svc ( )
if (nal_svc_prefix.nal_ref_idc == NAL_REFIDC_REF)
{
rbsp.put_bits(1, nal_svc_prefix.store_ref_base_pic_flag);
rbsp.put_bits(1, 0 /* additional_prefix_nal_unit_extension_flag */);
}
else
{
// No more_rbsp_data( ) so we don't need to code anything else
}
rbsp_trailing(&rbsp);
rbsp.flush();
if (wrap_rbsp_into_nalu(&nalu, &rbsp, nal_svc_prefix.nal_ref_idc, NAL_TYPE_PREFIX, &nal_svc_prefix) <= 0u) {
debug_printf(
"wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_ACCESS_UNIT_DELIMITER) didn't write any bytes.\n");
assert(false);
}
// Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
// memory.
uint8_t *naluBytes = nalu.get_bitstream_buffer();
size_t naluByteSize = nalu.get_byte_count();
auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
headerBitstream.resize(startDstIndex + naluByteSize);
}
std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
writtenBytes = naluByteSize;
}
@@ -55,6 +55,28 @@ enum H264_NALU_TYPE
/* 24...31 UNSPECIFIED */
};
typedef struct
{
// nal header
H264_NALREF_IDC nal_ref_idc;
// nal_unit_header_svc_extension( )
uint32_t idr_flag; // u(1)
uint32_t priority_id; // u(6)
uint32_t no_inter_layer_pred_flag; // u(1)
uint32_t dependency_id; // u(3)
uint32_t quality_id; // u(4)
uint32_t temporal_id; // u(3)
uint32_t use_ref_base_pic_flag; // u(1)
uint32_t discardable_flag; // u(1)
uint32_t output_flag; // u(1)
// uint32_t reserved_three_2bits; // u(2)
// prefix_nal_unit_svc ( )
uint32_t store_ref_base_pic_flag; // u(1)
// uint32_t additional_prefix_nal_unit_extension_flag; // u(1)
} H264_SLICE_PREFIX_SVC;
enum H264_SEI_TYPE
{
H264_SEI_SCALABILITY_INFO = 24,
@@ -215,6 +237,11 @@ class d3d12_video_nalu_writer_h264
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
void write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix,
std::vector<uint8_t> & headerBitstream,
std::vector<uint8_t>::iterator placingPositionStart,
size_t & writtenBytes);
private:
// Writes from structure into bitstream with RBSP trailing but WITHOUT NAL unit wrap (eg. nal_idc_type, etc)
uint32_t write_sps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_SPS *pSPS);
@@ -231,7 +258,8 @@ class d3d12_video_nalu_writer_h264
uint32_t wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU,
d3d12_video_encoder_bitstream *pRBSP,
uint32_t iNaluIdc,
uint32_t iNaluType);
uint32_t iNaluType,
const H264_SLICE_PREFIX_SVC* pSvcExtendedHeader = NULL);
};
#endif