diff --git a/include/h264/decode.h b/include/h264/decode.h new file mode 100644 index 0000000..eefb313 --- /dev/null +++ b/include/h264/decode.h @@ -0,0 +1,134 @@ +#pragma once +#include "enum.h" +#include "stream.h" + +/** + * \defgroup h264_decode H264 Decode + * \ingroup h264 + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Calculate the amount of memory required for the specified parameters. + */ +H264Error +H264DECMemoryRequirement(int32_t profile, + int32_t level, + int32_t maxWidth, + int32_t maxHeight, + uint32_t *outMemoryRequirement); + + +/** + * Initialise a H264 decoder in the given memory. + */ +H264Error +H264DECInitParam(int32_t memorySize, + void *memory); + + +/** + * Set H264 decoder parameter. + */ +H264Error +H264DECSetParam(void *memory, + H264Parameter parameter, + void *value); + + +/** + * Set the callback which is called when a frame is output from the decoder. + */ +H264Error +H264DECSetParam_FPTR_OUTPUT(void *memory, + H264DECFptrOutputFn value); + + +/** + * Set whether the decoder should internally buffer frames or call the callback + * immediately as soon as a frame is emitted. + */ +H264Error +H264DECSetParam_OUTPUT_PER_FRAME(void *memory, + uint32_t value); + + +/** + * Set a user memory pointer which is passed to the frame output callback. + */ +H264Error +H264DECSetParam_USER_MEMORY(void *memory, + void *value); + + +/** + * Check if the provided memory can be used for decoding. + */ +H264Error +H264DECCheckMemSegmentation(void *memory, + uint32_t size); + + +/** + * Open a H264 decoder. + */ +H264Error +H264DECOpen(void *memory); + + +/** + * Prepare for decoding. + */ +H264Error +H264DECBegin(void *memory); + + +/** + * Set the bit stream to be read for decoding. + */ +H264Error +H264DECSetBitstream(void *memory, + uint8_t *buffer, + uint32_t bufferLength, + double timestamp); + + +/** + * Perform decoding of the bitstream and put the output frame into frameBuffer. + */ +H264Error +H264DECExecute(void *memory, + void *frameBuffer); + + +/** + * Flush any internally buffered frames. + */ +H264Error +H264DECFlush(void *memory); + + +/** + * End decoding of the current stream. + */ +H264Error +H264DECEnd(void *memory); + + +/** + * Cleanup the decoder. + */ +H264Error +H264DECClose(void *memory); + + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/include/h264/enum.h b/include/h264/enum.h new file mode 100644 index 0000000..46e8811 --- /dev/null +++ b/include/h264/enum.h @@ -0,0 +1,40 @@ +#pragma once +#include + +/** + * \defgroup h264_enum Enums + * \ingroup h26 + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum H264Error +{ + H264_ERROR_OK = 0, + H264_ERROR_INVALID_PPS = 24, + H264_ERROR_INVALID_SPS = 26, + H264_ERROR_INVALID_SLICEHEADER = 61, + H264_ERROR_GENERIC = 0x1000000, + H264_ERROR_INVALID_PARAMETER = 0x1010000, + H264_ERROR_OUT_OF_MEMORY = 0x1020000, + H264_ERROR_INVALID_PROFILE = 0x1080000, +} H264Error; + +typedef enum H264Parameter +{ + H264_PARAMETER_FRAME_POINTER_OUTPUT = 1, + H264_PARAMETER_OUTPUT_PER_FRAME = 0x20000002, + H264_PARAMETER_UNKNOWN_20000010 = 0x20000010, + H264_PARAMETER_UNKNOWN_20000030 = 0x20000030, + H264_PARAMETER_UNKNOWN_20000040 = 0x20000040, + H264_PARAMETER_USER_MEMORY = 0x70000001, +} H264Parameter; + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/include/h264/stream.h b/include/h264/stream.h new file mode 100644 index 0000000..ea34300 --- /dev/null +++ b/include/h264/stream.h @@ -0,0 +1,212 @@ +#pragma once +#include "enum.h" + +/** + * \defgroup h264_stream H264 Stream + * \ingroup h264 + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct H264DecodedVuiParameters H264DecodedVuiParameters; +typedef struct H264DecodeResult H264DecodeResult; +typedef struct H264DecodeOutput H264DecodeOutput; + +typedef void (*H264DECFptrOutputFn)(H264DecodeOutput *output); + +struct WUT_PACKED H264DecodedVuiParameters +{ + uint8_t aspect_ratio_info_present_flag; + uint8_t aspect_ratio_idc; + int16_t sar_width; + int16_t sar_height; + uint8_t overscan_info_present_flag; + uint8_t overscan_appropriate_flag; + uint8_t video_signal_type_present_flag; + uint8_t video_format; + uint8_t video_full_range_flag; + uint8_t colour_description_present_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + uint8_t chroma_loc_info_present_flag; + uint8_t chroma_sample_loc_type_top_field; + uint8_t chroma_sample_loc_type_bottom_field; + uint8_t timing_info_present_flag; + WUT_PADDING_BYTES(1); + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t fixed_frame_rate_flag; + uint8_t nal_hrd_parameters_present_flag; + uint8_t vcl_hrd_parameters_present_flag; + uint8_t low_delay_hrd_flag; + uint8_t pic_struct_present_flag; + uint8_t bitstream_restriction_flag; + uint8_t motion_vectors_over_pic_boundaries_flag; + WUT_PADDING_BYTES(1); + int16_t max_bytes_per_pic_denom; + int16_t max_bits_per_mb_denom; + int16_t log2_max_mv_length_horizontal; + int16_t log2_max_mv_length_vertical; + int16_t num_reorder_frames; + int16_t max_dec_frame_buffering; +}; +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x00, aspect_ratio_info_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x01, aspect_ratio_idc); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x02, sar_width); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x04, sar_height); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x06, overscan_info_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x07, overscan_appropriate_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x08, video_signal_type_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x09, video_format); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x0A, video_full_range_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x0B, colour_description_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x0C, colour_primaries); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x0D, transfer_characteristics); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x0E, matrix_coefficients); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x0F, chroma_loc_info_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x10, chroma_sample_loc_type_top_field); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x11, chroma_sample_loc_type_bottom_field); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x12, timing_info_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x14, num_units_in_tick); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x18, time_scale); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x1C, fixed_frame_rate_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x1D, nal_hrd_parameters_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x1E, vcl_hrd_parameters_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x1F, low_delay_hrd_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x20, pic_struct_present_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x21, bitstream_restriction_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x22, motion_vectors_over_pic_boundaries_flag); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x24, max_bytes_per_pic_denom); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x26, max_bits_per_mb_denom); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x28, log2_max_mv_length_horizontal); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x2A, log2_max_mv_length_vertical); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x2C, num_reorder_frames); +WUT_CHECK_OFFSET(H264DecodedVuiParameters, 0x2E, max_dec_frame_buffering); +WUT_CHECK_SIZE(H264DecodedVuiParameters, 0x30); + +struct WUT_PACKED H264DecodeResult +{ + int32_t status; + WUT_PADDING_BYTES(4); + double timestamp; + int32_t width; + int32_t height; + int32_t nextLine; + uint8_t cropEnableFlag; + WUT_PADDING_BYTES(3); + int32_t cropTop; + int32_t cropBottom; + int32_t cropLeft; + int32_t cropRight; + uint8_t panScanEnableFlag; + WUT_PADDING_BYTES(3); + int32_t panScanTop; + int32_t panScanBottom; + int32_t panScanLeft; + int32_t panScanRight; + void *framebuffer; + uint8_t vui_parameters_present_flag; + WUT_PADDING_BYTES(3); + H264DecodedVuiParameters *vui_parameters; + WUT_UNKNOWN_BYTES(40); +}; +WUT_CHECK_OFFSET(H264DecodeResult, 0x00, status); +WUT_CHECK_OFFSET(H264DecodeResult, 0x08, timestamp); +WUT_CHECK_OFFSET(H264DecodeResult, 0x10, width); +WUT_CHECK_OFFSET(H264DecodeResult, 0x14, height); +WUT_CHECK_OFFSET(H264DecodeResult, 0x18, nextLine); +WUT_CHECK_OFFSET(H264DecodeResult, 0x1C, cropEnableFlag); +WUT_CHECK_OFFSET(H264DecodeResult, 0x20, cropTop); +WUT_CHECK_OFFSET(H264DecodeResult, 0x24, cropBottom); +WUT_CHECK_OFFSET(H264DecodeResult, 0x28, cropLeft); +WUT_CHECK_OFFSET(H264DecodeResult, 0x2C, cropRight); +WUT_CHECK_OFFSET(H264DecodeResult, 0x30, panScanEnableFlag); +WUT_CHECK_OFFSET(H264DecodeResult, 0x34, panScanTop); +WUT_CHECK_OFFSET(H264DecodeResult, 0x38, panScanBottom); +WUT_CHECK_OFFSET(H264DecodeResult, 0x3C, panScanLeft); +WUT_CHECK_OFFSET(H264DecodeResult, 0x40, panScanRight); +WUT_CHECK_OFFSET(H264DecodeResult, 0x44, framebuffer); +WUT_CHECK_OFFSET(H264DecodeResult, 0x48, vui_parameters_present_flag); +WUT_CHECK_OFFSET(H264DecodeResult, 0x4C, vui_parameters); +WUT_CHECK_SIZE(H264DecodeResult, 0x78); + +struct WUT_PACKED H264DecodeOutput +{ + //! Number of frames output + int32_t frameCount; + + //! Frames + H264DecodeResult **decodeResults; + + //! User memory pointer passed into SetParam + void *userMemory; +}; +WUT_CHECK_OFFSET(H264DecodeOutput, 0x00, frameCount); +WUT_CHECK_OFFSET(H264DecodeOutput, 0x04, decodeResults); +WUT_CHECK_OFFSET(H264DecodeOutput, 0x08, userMemory); +WUT_CHECK_SIZE(H264DecodeOutput, 0x0C); + + +/** + * Check that the stream contains sufficient data to decode an entire frame. + */ +H264Error +H264DECCheckDecunitLength(void *memory, + const uint8_t *buffer, + int32_t bufferLength, + int32_t offset, + int32_t *outLength); + + +/** + * Check if the next NALU can be skipped without breaking decoding. + */ +H264Error +H264DECCheckSkipableFrame(const uint8_t *buffer, + int32_t bufferLength, + BOOL *outSkippable); + + +/** + * Find the first SPS in the stream. + */ +H264Error +H264DECFindDecstartpoint(const uint8_t *buffer, + int32_t bufferLength, + int32_t *outOffset); + + +/** + * Find the first "IDR point" in the stream. + * + * An IDR point is either: + * - If an SPS or PPS header is found before the IDR and there are no non-IDR + * inbetween the SPS/PPS and IDR then return the first of the SPS/PPS. + * - The first found IDR. + */ +int32_t +H264DECFindIdrpoint(const uint8_t *buffer, + int32_t bufferLength, + int32_t *outOffset); + + +/** + * Parse the H264 stream and read the width & height from the first found SPS. + */ +H264Error +H264DECGetImageSize(const uint8_t *buffer, + int32_t bufferLength, + int32_t offset, + int32_t *outWidth, + int32_t *outHeight); + + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/tests/test_compile_headers_common/test_compile_headers_list.h b/tests/test_compile_headers_common/test_compile_headers_list.h index 814efcf..f47fa5e 100644 --- a/tests/test_compile_headers_common/test_compile_headers_list.h +++ b/tests/test_compile_headers_common/test_compile_headers_list.h @@ -72,6 +72,9 @@ #include #include #include +#include +#include +#include #include #include #include