mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2026-04-18 00:20:21 +00:00
ff_lcvec_parse_config_record() returns success before this patch when no IDR or NON_IDR NAL units are found. Signed-off-by: Zhao Zhili <zhilizhao@tencent.com>
339 lines
10 KiB
C
339 lines
10 KiB
C
/*
|
|
* 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;
|
|
}
|