| /* |
| * LCEVC helper functions for muxers |
| * |
| * This file is part of FFmpeg. |
| * |
| * FFmpeg is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * FFmpeg is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with FFmpeg; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "libavutil/error.h" |
| #include "libavutil/intreadwrite.h" |
| #include "libavutil/mem.h" |
| #include "libavcodec/bytestream.h" |
| #include "libavcodec/h2645_parse.h" |
| #include "libavcodec/lcevc.h" |
| #include "libavcodec/lcevctab.h" |
| #include "libavcodec/lcevc_parse.h" |
| #include "avio.h" |
| #include "avio_internal.h" |
| #include "lcevc.h" |
| |
| /** |
| * Rewrite the NALu stripping the unneeded blocks. |
| * Given that length fields coded inside the NALu are not aware of any emulation_3bytes |
| * present in the bitstream, we need to keep track of the raw buffer as we navigate |
| * the stripped buffer in order to write proper NALu sizes. |
| */ |
| static int write_nalu(LCEVCDecoderConfigurationRecord *lvcc, AVIOContext *pb, |
| const H2645NAL *nal) |
| { |
| GetByteContext gbc, raw_gbc; |
| int64_t start = avio_tell(pb), end; |
| int sc = 0, gc = 0; |
| int skipped_byte_pos = 0, nalu_length = 3; |
| |
| bytestream2_init(&gbc, nal->data, nal->size); |
| bytestream2_init(&raw_gbc, nal->raw_data, nal->raw_size); |
| avio_wb16(pb, 0); // size placeholder |
| avio_wb16(pb, bytestream2_get_be16(&gbc)); // nal_unit_header |
| bytestream2_skip(&raw_gbc, 2); |
| |
| while (bytestream2_get_bytes_left(&gbc) > 1 && (!sc || !gc)) { |
| GetBitContext gb; |
| uint64_t payload_size; |
| int payload_size_type, payload_type; |
| int block_size, raw_block_size, block_end; |
| |
| init_get_bits8(&gb, gbc.buffer, bytestream2_get_bytes_left(&gbc)); |
| |
| payload_size_type = get_bits(&gb, 3); |
| payload_type = get_bits(&gb, 5); |
| payload_size = payload_size_type; |
| if (payload_size_type == 6) |
| return AVERROR_PATCHWELCOME; |
| if (payload_size_type == 7) |
| payload_size = get_mb(&gb); |
| |
| if (payload_size > INT_MAX - (get_bits_count(&gb) >> 3)) |
| return AVERROR_INVALIDDATA; |
| |
| block_size = raw_block_size = payload_size + (get_bits_count(&gb) >> 3); |
| if (block_size >= bytestream2_get_bytes_left(&gbc)) |
| return AVERROR_INVALIDDATA; |
| |
| block_end = bytestream2_tell(&gbc) + block_size; |
| // Take into account removed emulation 3bytes, as payload_size in |
| // the bitstream is not aware of them. |
| for (; skipped_byte_pos < nal->skipped_bytes; skipped_byte_pos++) { |
| if (nal->skipped_bytes_pos[skipped_byte_pos] >= block_end) |
| break; |
| raw_block_size++; |
| } |
| |
| switch (payload_type) { |
| case 0: |
| if (sc) |
| break; |
| |
| lvcc->profile_idc = get_bits(&gb, 4); |
| lvcc->level_idc = get_bits(&gb, 4); |
| |
| avio_write(pb, raw_gbc.buffer, raw_block_size); |
| nalu_length += raw_block_size; |
| sc = 1; |
| break; |
| case 1: { |
| int resolution_type, bit_depth; |
| int processed_planes_type_flag; |
| |
| if (gc) |
| break; |
| |
| processed_planes_type_flag = get_bits1(&gb); |
| resolution_type = get_bits(&gb, 6); |
| |
| skip_bits1(&gb); |
| lvcc->chroma_format_idc = get_bits(&gb, 2); |
| |
| skip_bits(&gb, 2); |
| bit_depth = get_bits(&gb, 2) * 2; // enhancement_depth_type |
| lvcc->bit_depth_luma_minus8 = bit_depth; |
| lvcc->bit_depth_chroma_minus8 = bit_depth; |
| |
| if (resolution_type < 63) { |
| lvcc->pic_width_in_luma_samples = ff_lcevc_resolution_type[resolution_type].width; |
| lvcc->pic_height_in_luma_samples = ff_lcevc_resolution_type[resolution_type].height; |
| } else { |
| int upsample_type, tile_dimensions_type; |
| int temporal_step_width_modifier_signalled_flag, level1_filtering_signalled_flag; |
| // Skip syntax elements until we get to the custom dimension ones |
| temporal_step_width_modifier_signalled_flag = get_bits1(&gb); |
| skip_bits(&gb, 3); |
| upsample_type = get_bits(&gb, 3); |
| level1_filtering_signalled_flag = get_bits1(&gb); |
| skip_bits(&gb, 4); |
| tile_dimensions_type = get_bits(&gb, 2); |
| skip_bits(&gb, 4); |
| if (processed_planes_type_flag) |
| skip_bits(&gb, 4); |
| if (temporal_step_width_modifier_signalled_flag) |
| skip_bits(&gb, 8); |
| if (upsample_type) |
| skip_bits_long(&gb, 64); |
| if (level1_filtering_signalled_flag) |
| skip_bits(&gb, 8); |
| if (tile_dimensions_type) { |
| if (tile_dimensions_type == 3) |
| skip_bits_long(&gb, 32); |
| skip_bits(&gb, 8); |
| } |
| |
| lvcc->pic_width_in_luma_samples = get_bits(&gb, 16); |
| lvcc->pic_height_in_luma_samples = get_bits(&gb, 16); |
| } |
| |
| if (!lvcc->pic_width_in_luma_samples || !lvcc->pic_height_in_luma_samples) |
| break; |
| |
| avio_write(pb, raw_gbc.buffer, raw_block_size); |
| nalu_length += raw_block_size; |
| gc = 1; |
| break; |
| } |
| case 5: |
| avio_write(pb, raw_gbc.buffer, raw_block_size); |
| nalu_length += raw_block_size; |
| break; |
| default: |
| break; |
| } |
| |
| bytestream2_skip(&gbc, block_size); |
| bytestream2_skip(&raw_gbc, raw_block_size); |
| } |
| |
| if (!sc || !gc) |
| return AVERROR_INVALIDDATA; |
| |
| avio_w8(pb, 0x80); // rbsp_alignment bits |
| |
| end = avio_tell(pb); |
| avio_seek(pb, start, SEEK_SET); |
| avio_wb16(pb, nalu_length); |
| avio_seek(pb, end, SEEK_SET); |
| |
| return 0; |
| } |
| |
| int ff_lcvec_parse_config_record(LCEVCDecoderConfigurationRecord *lvcc, |
| const uint8_t *buf, int size) |
| { |
| H2645Packet h2645_pkt = { 0 }; |
| AVIOContext *pb; |
| int ret; |
| int found; |
| |
| if (size <= 0) |
| return AVERROR_INVALIDDATA; |
| |
| if (buf[0] == 1) { |
| GetBitContext gb; |
| |
| if (size < 13) |
| return AVERROR_INVALIDDATA; |
| |
| ret = init_get_bits8(&gb, buf, 13); |
| if (ret < 0) |
| return ret; |
| |
| memset(lvcc, 0, sizeof(*lvcc)); |
| |
| skip_bits(&gb, 8); |
| lvcc->profile_idc = get_bits(&gb, 8); |
| lvcc->level_idc = get_bits(&gb, 8); |
| lvcc->chroma_format_idc = get_bits(&gb, 2); |
| lvcc->bit_depth_luma_minus8 = get_bits(&gb, 3); |
| lvcc->bit_depth_chroma_minus8 = get_bits(&gb, 3); |
| skip_bits(&gb, 8); |
| lvcc->pic_width_in_luma_samples = get_bits_long(&gb, 32); |
| lvcc->pic_height_in_luma_samples = get_bits_long(&gb, 32); |
| |
| return 0; |
| } |
| |
| ret = ffio_open_null_buf(&pb); |
| if (ret < 0) |
| return ret; |
| |
| ret = ff_h2645_packet_split(&h2645_pkt, buf, size, NULL, 0, AV_CODEC_ID_LCEVC, 0); |
| if (ret < 0) |
| goto fail; |
| |
| /* look for IDR or NON_IDR */ |
| found = 0; |
| for (int i = 0; i < h2645_pkt.nb_nals; i++) { |
| const H2645NAL *nal = &h2645_pkt.nals[i]; |
| |
| if (nal->type == LCEVC_IDR_NUT || |
| nal->type == LCEVC_NON_IDR_NUT) { |
| ret = write_nalu(lvcc, pb, nal); |
| if (ret < 0) |
| goto fail; |
| found = 1; |
| } |
| } |
| |
| if (!found) { |
| ret = AVERROR_INVALIDDATA; |
| goto fail; |
| } |
| |
| ret = 0; |
| fail: |
| ffio_close_null_buf(pb); |
| ff_h2645_packet_uninit(&h2645_pkt); |
| |
| return ret; |
| } |
| |
| int ff_isom_write_lvcc(AVIOContext *pb, const uint8_t *data, int len) |
| { |
| LCEVCDecoderConfigurationRecord lvcc = { 0 }; |
| AVIOContext *idr_pb = NULL, *nidr_pb = NULL; |
| H2645Packet h2645_pkt = { 0 }; |
| uint8_t *idr, *nidr; |
| uint32_t idr_size = 0, nidr_size = 0; |
| int ret, nb_idr = 0, nb_nidr = 0; |
| |
| if (len <= 6) |
| return AVERROR_INVALIDDATA; |
| |
| /* check for start code */ |
| if (AV_RB32(data) != 0x00000001 && |
| AV_RB24(data) != 0x000001) { |
| avio_write(pb, data, len); |
| return 0; |
| } |
| |
| ret = ff_h2645_packet_split(&h2645_pkt, data, len, NULL, 0, AV_CODEC_ID_LCEVC, 0); |
| if (ret < 0) |
| return ret; |
| |
| ret = avio_open_dyn_buf(&idr_pb); |
| if (ret < 0) |
| goto fail; |
| ret = avio_open_dyn_buf(&nidr_pb); |
| if (ret < 0) |
| goto fail; |
| |
| /* look for IDR or NON_IDR */ |
| for (int i = 0; i < h2645_pkt.nb_nals; i++) { |
| const H2645NAL *nal = &h2645_pkt.nals[i]; |
| |
| if (nal->type == LCEVC_IDR_NUT) { |
| nb_idr++; |
| |
| ret = write_nalu(&lvcc, idr_pb, nal); |
| if (ret < 0) |
| goto fail; |
| } else if (nal->type == LCEVC_NON_IDR_NUT) { |
| nb_nidr++; |
| |
| ret = write_nalu(&lvcc, nidr_pb, nal); |
| if (ret < 0) |
| goto fail; |
| } |
| } |
| idr_size = avio_get_dyn_buf(idr_pb, &idr); |
| nidr_size = avio_get_dyn_buf(nidr_pb, &nidr); |
| |
| if (!idr_size && !nidr_size) { |
| ret = AVERROR_INVALIDDATA; |
| goto fail; |
| } |
| |
| avio_w8(pb, 1); /* version */ |
| avio_w8(pb, lvcc.profile_idc); |
| avio_w8(pb, lvcc.level_idc); |
| avio_w8(pb, (lvcc.chroma_format_idc << 6) | |
| (lvcc.bit_depth_luma_minus8 << 3) | |
| lvcc.bit_depth_chroma_minus8); |
| avio_w8(pb, 0xff); /* 2 bits nal size length - 1 (11) + 6 bits reserved (111111)*/ |
| avio_wb32(pb, lvcc.pic_width_in_luma_samples); |
| avio_wb32(pb, lvcc.pic_height_in_luma_samples); |
| avio_w8(pb, 0xff); |
| |
| int nb_arrays = !!nb_idr + !!nb_nidr; |
| avio_w8(pb, nb_arrays); |
| |
| if (nb_idr) { |
| avio_w8(pb, LCEVC_IDR_NUT); |
| avio_wb16(pb, nb_idr); |
| avio_write(pb, idr, idr_size); |
| } |
| if (nb_nidr) { |
| avio_w8(pb, LCEVC_NON_IDR_NUT); |
| avio_wb16(pb, nb_nidr); |
| avio_write(pb, nidr, nidr_size); |
| } |
| |
| ret = 0; |
| fail: |
| ffio_free_dyn_buf(&idr_pb); |
| ffio_free_dyn_buf(&nidr_pb); |
| ff_h2645_packet_uninit(&h2645_pkt); |
| |
| return ret; |
| } |