From: James Almer <jamrial@gmail.com> To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 04/11] avformat/hevc: don't write NALUs with nuh_layer_id > 0 in hvcC boxes Date: Wed, 3 Jul 2024 18:26:39 -0300 Message-ID: <20240703212648.48483-4-jamrial@gmail.com> (raw) In-Reply-To: <20240703212648.48483-1-jamrial@gmail.com> hvcC should only contain nuh_layer_id == 0 NALUs. Support for a box meant to contain higher layer NALUs will be added in a following patch. To achieve this, all sources are parsed and filtered, including hvcC source that until now were propagated untouched. This is reflected in how the affected reference files now have ps_array_completeness set to 0 instead of 1 for some non-PS NALUs. Signed-off-by: James Almer <jamrial@gmail.com> --- libavformat/hevc.c | 140 ++++++++++++++++----- libavformat/hevc.h | 3 +- tests/ref/fate/enhanced-flv-hevc | 4 +- tests/ref/fate/matroska-dovi-write-config8 | 4 +- tests/ref/lavf-fate/hevc.flv | 2 +- 5 files changed, 118 insertions(+), 35 deletions(-) diff --git a/libavformat/hevc.c b/libavformat/hevc.c index d6b9d233d9..651c3b4b1d 100644 --- a/libavformat/hevc.c +++ b/libavformat/hevc.c @@ -40,12 +40,15 @@ enum { NB_ARRAYS }; +#define FLAG_ARRAY_COMPLETENESS (1 << 0) +#define FLAG_IS_NALFF (1 << 1) + typedef struct HVCCNALUnitArray { uint8_t array_completeness; uint8_t NAL_unit_type; uint16_t numNalus; uint16_t *nalUnitLength; - uint8_t **nalUnit; + const uint8_t **nalUnit; } HVCCNALUnitArray; typedef struct HEVCDecoderConfigurationRecord { @@ -654,24 +657,26 @@ static int hvcc_parse_pps(GetBitContext *gb, return 0; } -static void nal_unit_parse_header(GetBitContext *gb, uint8_t *nal_type) +static void nal_unit_parse_header(GetBitContext *gb, uint8_t *nal_type, + uint8_t *nuh_layer_id) { skip_bits1(gb); // forbidden_zero_bit *nal_type = get_bits(gb, 6); + *nuh_layer_id = get_bits(gb, 6); /* - * nuh_layer_id u(6) * nuh_temporal_id_plus1 u(3) */ - skip_bits(gb, 9); + skip_bits(gb, 3); } -static int hvcc_array_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size, - uint8_t nal_type, int ps_array_completeness, +static int hvcc_array_add_nal_unit(const uint8_t *nal_buf, uint32_t nal_size, + uint8_t nal_type, int flags, HVCCNALUnitArray *array) { int ret; + int ps_array_completeness = !!(flags & FLAG_ARRAY_COMPLETENESS); uint16_t numNalus = array->numNalus; ret = av_reallocp_array(&array->nalUnit, numNalus + 1, sizeof(uint8_t*)); @@ -699,14 +704,14 @@ static int hvcc_array_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size, return 0; } -static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size, - int ps_array_completeness, +static int hvcc_add_nal_unit(const uint8_t *nal_buf, uint32_t nal_size, HEVCDecoderConfigurationRecord *hvcc, - unsigned array_idx) + int flags, unsigned array_idx) { int ret = 0; + int is_nalff = !!(flags & FLAG_IS_NALFF); GetBitContext gbc; - uint8_t nal_type; + uint8_t nal_type, nuh_layer_id; uint8_t *rbsp_buf; uint32_t rbsp_size; @@ -720,7 +725,9 @@ static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size, if (ret < 0) goto end; - nal_unit_parse_header(&gbc, &nal_type); + nal_unit_parse_header(&gbc, &nal_type, &nuh_layer_id); + if (nuh_layer_id > 0) + goto end; /* * Note: only 'declarative' SEI messages are allowed in @@ -728,12 +735,17 @@ static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size, * and non-declarative SEI messages discarded? */ ret = hvcc_array_add_nal_unit(nal_buf, nal_size, nal_type, - ps_array_completeness, + flags, &hvcc->arrays[array_idx]); if (ret < 0) goto end; if (hvcc->arrays[array_idx].numNalus == 1) hvcc->numOfArrays++; + + /* Don't parse parameter sets. We already have the needed information*/ + if (is_nalff) + goto end; + if (nal_type == HEVC_NAL_VPS) ret = hvcc_parse_vps(&gbc, hvcc); else if (nal_type == HEVC_NAL_SPS) @@ -1041,20 +1053,100 @@ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out, return 0; } +static int hvcc_parse_nal_unit(const uint8_t *buf, uint32_t len, int type, + HEVCDecoderConfigurationRecord *hvcc, + int flags) +{ + for (unsigned i = 0; i < FF_ARRAY_ELEMS(hvcc->arrays); i++) { + static const uint8_t array_idx_to_type[] = + { HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS, + HEVC_NAL_SEI_PREFIX, HEVC_NAL_SEI_SUFFIX }; + + if (type == array_idx_to_type[i]) { + int ret = hvcc_add_nal_unit(buf, len, hvcc, flags, i); + if (ret < 0) + return ret; + break; + } + } + + return 0; +} + int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data, int size, int ps_array_completeness) { HEVCDecoderConfigurationRecord hvcc; - uint8_t *buf, *end, *start; + uint8_t *buf, *end, *start = NULL; + int flags = !!ps_array_completeness * FLAG_ARRAY_COMPLETENESS; int ret; if (size < 6) { /* We can't write a valid hvcC from the provided data */ return AVERROR_INVALIDDATA; } else if (*data == 1) { - /* Data is already hvcC-formatted */ - avio_write(pb, data, size); - return 0; + /* Data is already hvcC-formatted. Parse the arrays to skip any NALU + with nuh_layer_id > 0 */ + GetBitContext gbc; + int num_arrays; + + if (size < 23) + return AVERROR_INVALIDDATA; + + ret = init_get_bits8(&gbc, data, size); + if (ret < 0) + return ret; + + hvcc_init(&hvcc); + skip_bits(&gbc, 8); // hvcc.configurationVersion + hvcc.general_profile_space = get_bits(&gbc, 2); + hvcc.general_tier_flag = get_bits1(&gbc); + hvcc.general_profile_idc = get_bits(&gbc, 5); + hvcc.general_profile_compatibility_flags = get_bits_long(&gbc, 32); + hvcc.general_constraint_indicator_flags = get_bits64(&gbc, 48); + hvcc.general_level_idc = get_bits(&gbc, 8); + skip_bits(&gbc, 4); // reserved + hvcc.min_spatial_segmentation_idc = get_bits(&gbc, 12); + skip_bits(&gbc, 6); // reserved + hvcc.parallelismType = get_bits(&gbc, 2); + skip_bits(&gbc, 6); // reserved + hvcc.chromaFormat = get_bits(&gbc, 2); + skip_bits(&gbc, 5); // reserved + hvcc.bitDepthLumaMinus8 = get_bits(&gbc, 3); + skip_bits(&gbc, 5); // reserved + hvcc.bitDepthChromaMinus8 = get_bits(&gbc, 3); + hvcc.avgFrameRate = get_bits(&gbc, 16); + hvcc.constantFrameRate = get_bits(&gbc, 2); + hvcc.numTemporalLayers = get_bits(&gbc, 3); + hvcc.temporalIdNested = get_bits1(&gbc); + hvcc.lengthSizeMinusOne = get_bits(&gbc, 2); + + flags |= FLAG_IS_NALFF; + + num_arrays = get_bits(&gbc, 8); + for (int i = 0; i < num_arrays; i++) { + int type, num_nalus; + + skip_bits(&gbc, 2); + type = get_bits(&gbc, 6); + num_nalus = get_bits(&gbc, 16); + for (int j = 0; j < num_nalus; j++) { + int len = get_bits(&gbc, 16); + + if (len > (get_bits_left(&gbc) / 8)) + goto end; + + ret = hvcc_parse_nal_unit(data + get_bits_count(&gbc) / 8, + len, type, &hvcc, flags); + if (ret < 0) + goto end; + + skip_bits_long(&gbc, len * 8); + } + } + + ret = hvcc_write(pb, &hvcc); + goto end; } else if (!(AV_RB24(data) == 1 || AV_RB32(data) == 1)) { /* Not a valid Annex B start code prefix */ return AVERROR_INVALIDDATA; @@ -1075,19 +1167,9 @@ int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data, buf += 4; - for (unsigned i = 0; i < FF_ARRAY_ELEMS(hvcc.arrays); i++) { - static const uint8_t array_idx_to_type[] = - { HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS, - HEVC_NAL_SEI_PREFIX, HEVC_NAL_SEI_SUFFIX }; - - if (type == array_idx_to_type[i]) { - ret = hvcc_add_nal_unit(buf, len, ps_array_completeness, - &hvcc, i); - if (ret < 0) - goto end; - break; - } - } + ret = hvcc_parse_nal_unit(buf, len, type, &hvcc, flags); + if (ret < 0) + goto end; buf += len; } diff --git a/libavformat/hevc.h b/libavformat/hevc.h index 0f56325c1c..cb66ac66ac 100644 --- a/libavformat/hevc.h +++ b/libavformat/hevc.h @@ -79,7 +79,8 @@ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out, int *size, int filter_ps, int *ps_count); /** - * Writes HEVC extradata (parameter sets, declarative SEI NAL units) to the + * Writes HEVC extradata (parameter sets and declarative SEI NAL units with + * nuh_layer_id == 0, as a HEVCDecoderConfigurationRecord) to the * provided AVIOContext. * * If the extradata is Annex B format, it gets converted to hvcC format before diff --git a/tests/ref/fate/enhanced-flv-hevc b/tests/ref/fate/enhanced-flv-hevc index f011d38a30..f04905d06b 100644 --- a/tests/ref/fate/enhanced-flv-hevc +++ b/tests/ref/fate/enhanced-flv-hevc @@ -1,6 +1,6 @@ -0da54607064548fa1aae5695751f189c *tests/data/fate/enhanced-flv-hevc.flv +565cf155790db391137f81f619448477 *tests/data/fate/enhanced-flv-hevc.flv 3603038 tests/data/fate/enhanced-flv-hevc.flv -#extradata 0: 551, 0xa18acf66 +#extradata 0: 551, 0xb1ddcd66 #extradata 1: 2, 0x00340022 #tb 0: 1/1000 #media_type 0: video diff --git a/tests/ref/fate/matroska-dovi-write-config8 b/tests/ref/fate/matroska-dovi-write-config8 index 472cbed708..44ca015e0e 100644 --- a/tests/ref/fate/matroska-dovi-write-config8 +++ b/tests/ref/fate/matroska-dovi-write-config8 @@ -1,6 +1,6 @@ -0730145aa317d800cb4bde0e3a38bb8d *tests/data/fate/matroska-dovi-write-config8.matroska +3bd4b07d5af6153516e4c0e66a71c8c9 *tests/data/fate/matroska-dovi-write-config8.matroska 3600607 tests/data/fate/matroska-dovi-write-config8.matroska -#extradata 0: 551, 0xa18acf66 +#extradata 0: 551, 0xb1ddcd66 #extradata 1: 2, 0x00340022 #tb 0: 1/1000 #media_type 0: video diff --git a/tests/ref/lavf-fate/hevc.flv b/tests/ref/lavf-fate/hevc.flv index 1105d8eddb..e3962e0938 100644 --- a/tests/ref/lavf-fate/hevc.flv +++ b/tests/ref/lavf-fate/hevc.flv @@ -1,3 +1,3 @@ -39cf3df5fc3a9c50ab71a294f45663fe *tests/data/lavf-fate/lavf.hevc.flv +c9e8b5df15135d21bd2781558f32f269 *tests/data/lavf-fate/lavf.hevc.flv 11819 tests/data/lavf-fate/lavf.hevc.flv tests/data/lavf-fate/lavf.hevc.flv CRC=0xd29da885 -- 2.45.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next prev parent reply other threads:[~2024-07-03 21:27 UTC|newest] Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top 2024-07-03 21:26 [FFmpeg-devel] [PATCH 01/11] avformat/mov: add support for lhvC box parsing James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 02/11] avformat: Add a new stream disposition for multilayer video James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 03/11] avformat/mov: Mark streams with a layered HEVC box as multilayer James Almer 2024-07-03 21:26 ` James Almer [this message] 2024-07-07 15:46 ` [FFmpeg-devel] [PATCH 04/11] avformat/hevc: don't write NALUs with nuh_layer_id > 0 in hvcC boxes Andreas Rheinhardt 2024-07-07 16:09 ` James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 05/11] avformat/hevc: don't write the same array values per nal addition James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 06/11] avformat/hevc: use a single array for per-PS NALUs James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 07/11] avformat/hevc: store parameter set and layer IDs in HVCCNALUnit James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 08/11] avformat/hevc: add a function to write a lhvC box James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 09/11] avformat/movenc: add support for writing lhvC boxes James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 10/11] avformat/movenc: add support for writting vexu boxes James Almer 2024-07-03 21:26 ` [FFmpeg-devel] [PATCH 11/11] avformat/movenc: add support for writting hfov boxes James Almer 2024-07-07 15:21 ` [FFmpeg-devel] [PATCH 01/11] avformat/mov: add support for lhvC box parsing James Almer 2024-07-07 15:43 ` Andreas Rheinhardt 2024-07-07 16:18 ` James Almer
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20240703212648.48483-4-jamrial@gmail.com \ --to=jamrial@gmail.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git