blob: 85ea96d22e468e375cc72dbc7d213c8981da4e7f [file]
/*
* 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;
}