/* * 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; }