FFmpeg/libavcodec/h264_redundant_pps_bsf.c
Andreas Rheinhardt d1c6e09d09 h264_redundant_pps: Make it reference-compatible
Since c6a63e1109, the parameter sets
modified as content of PPS units were references shared with the
CodedBitstreamH264Context, so modifying them alters the parsing process
of future access units which meant that frames often got discarded
because invalid values were parsed. This patch makes h264_redundant_pps
compatible with the reality of reference-counted parameter sets.

Fixes #7807.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
Signed-off-by: Mark Thompson <sw@jkqxz.net>
2020-09-02 00:00:57 +01:00

207 lines
5.9 KiB
C

/*
* 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 <string.h>
#include "libavutil/common.h"
#include "libavutil/mem.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_h264.h"
#include "h264.h"
typedef struct H264RedundantPPSContext {
CodedBitstreamContext *input;
CodedBitstreamContext *output;
CodedBitstreamFragment access_unit;
int global_pic_init_qp;
int current_pic_init_qp;
int extradata_pic_init_qp;
} H264RedundantPPSContext;
static int h264_redundant_pps_fixup_pps(H264RedundantPPSContext *ctx,
CodedBitstreamUnit *unit)
{
H264RawPPS *pps;
int err;
// The changes we are about to perform affect the parsing process,
// so we must make sure that the PPS is writable, otherwise the
// parsing of future slices will be incorrect and even raise errors.
err = ff_cbs_make_unit_writable(ctx->input, unit);
if (err < 0)
return err;
pps = unit->content;
// Record the current value of pic_init_qp in order to fix up
// following slices, then overwrite with the global value.
ctx->current_pic_init_qp = pps->pic_init_qp_minus26 + 26;
pps->pic_init_qp_minus26 = ctx->global_pic_init_qp - 26;
// Some PPSs have this set, so it must be set in all of them.
// (Slices which do not use such a PPS on input will still have
// *_weight_l*flag as zero and therefore write equivalently.)
pps->weighted_pred_flag = 1;
return 0;
}
static int h264_redundant_pps_fixup_slice(H264RedundantPPSContext *ctx,
H264RawSliceHeader *slice)
{
int qp;
qp = ctx->current_pic_init_qp + slice->slice_qp_delta;
slice->slice_qp_delta = qp - ctx->global_pic_init_qp;
return 0;
}
static int h264_redundant_pps_filter(AVBSFContext *bsf, AVPacket *pkt)
{
H264RedundantPPSContext *ctx = bsf->priv_data;
CodedBitstreamFragment *au = &ctx->access_unit;
int au_has_sps;
int err, i;
err = ff_bsf_get_packet_ref(bsf, pkt);
if (err < 0)
return err;
err = ff_cbs_read_packet(ctx->input, au, pkt);
if (err < 0)
goto fail;
au_has_sps = 0;
for (i = 0; i < au->nb_units; i++) {
CodedBitstreamUnit *nal = &au->units[i];
if (nal->type == H264_NAL_SPS)
au_has_sps = 1;
if (nal->type == H264_NAL_PPS) {
err = h264_redundant_pps_fixup_pps(ctx, nal);
if (err < 0)
goto fail;
if (!au_has_sps) {
av_log(bsf, AV_LOG_VERBOSE, "Deleting redundant PPS "
"at %"PRId64".\n", pkt->pts);
ff_cbs_delete_unit(au, i);
i--;
continue;
}
}
if (nal->type == H264_NAL_SLICE ||
nal->type == H264_NAL_IDR_SLICE) {
H264RawSlice *slice = nal->content;
h264_redundant_pps_fixup_slice(ctx, &slice->header);
}
}
err = ff_cbs_write_packet(ctx->output, pkt, au);
if (err < 0)
goto fail;
err = 0;
fail:
ff_cbs_fragment_reset(au);
if (err < 0)
av_packet_unref(pkt);
return err;
}
static int h264_redundant_pps_init(AVBSFContext *bsf)
{
H264RedundantPPSContext *ctx = bsf->priv_data;
CodedBitstreamFragment *au = &ctx->access_unit;
int err, i;
err = ff_cbs_init(&ctx->input, AV_CODEC_ID_H264, bsf);
if (err < 0)
return err;
err = ff_cbs_init(&ctx->output, AV_CODEC_ID_H264, bsf);
if (err < 0)
return err;
ctx->global_pic_init_qp = 26;
if (bsf->par_in->extradata) {
err = ff_cbs_read_extradata(ctx->input, au, bsf->par_in);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n");
goto fail;
}
for (i = 0; i < au->nb_units; i++) {
if (au->units[i].type == H264_NAL_PPS) {
err = h264_redundant_pps_fixup_pps(ctx, &au->units[i]);
if (err < 0)
goto fail;
}
}
ctx->extradata_pic_init_qp = ctx->current_pic_init_qp;
err = ff_cbs_write_extradata(ctx->output, bsf->par_out, au);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n");
goto fail;
}
}
err = 0;
fail:
ff_cbs_fragment_reset(au);
return err;
}
static void h264_redundant_pps_flush(AVBSFContext *bsf)
{
H264RedundantPPSContext *ctx = bsf->priv_data;
ctx->current_pic_init_qp = ctx->extradata_pic_init_qp;
}
static void h264_redundant_pps_close(AVBSFContext *bsf)
{
H264RedundantPPSContext *ctx = bsf->priv_data;
ff_cbs_fragment_free(&ctx->access_unit);
ff_cbs_close(&ctx->input);
ff_cbs_close(&ctx->output);
}
static const enum AVCodecID h264_redundant_pps_codec_ids[] = {
AV_CODEC_ID_H264, AV_CODEC_ID_NONE,
};
const AVBitStreamFilter ff_h264_redundant_pps_bsf = {
.name = "h264_redundant_pps",
.priv_data_size = sizeof(H264RedundantPPSContext),
.init = &h264_redundant_pps_init,
.flush = &h264_redundant_pps_flush,
.close = &h264_redundant_pps_close,
.filter = &h264_redundant_pps_filter,
.codec_ids = h264_redundant_pps_codec_ids,
};