diff mbox

[FFmpeg-devel,v5,1/3] hevc_mp4toannexb: Insert correct parameter sets before IRAP

Message ID 20190926181001.23609-2-andriy.gelman@gmail.com
State Superseded
Headers show

Commit Message

Andriy Gelman Sept. 26, 2019, 6:09 p.m. UTC
From: Andriy Gelman <andriy.gelman@gmail.com>

Fixes #7799

Currently, the mp4toannexb filter always inserts the same extradata at
the start of the first IRAP unit. As in ticket #7799, this can lead to
decoding errors if modified parameter sets are signalled in-band.

This commit keeps track of the VPS/SPS/PPS parameter sets during the
conversion. The correct combination is inserted at the start of the
first IRAP. SEIs from extradata are inserted before each IRAP.

This commit also makes an update to the hevc-bsf-mp4toannexb fate test
since the result before this patch contained duplicate parameter sets
in-band.
---
 libavcodec/hevc_mp4toannexb_bsf.c | 488 ++++++++++++++++++++++++++++--
 tests/fate/hevc.mak               |   2 +-
 2 files changed, 456 insertions(+), 34 deletions(-)

Comments

Andriy Gelman Oct. 2, 2019, 12:05 p.m. UTC | #1
On Thu, 26. Sep 14:09, Andriy Gelman wrote:
> From: Andriy Gelman <andriy.gelman@gmail.com>
> 
> Fixes #7799
> 
> Currently, the mp4toannexb filter always inserts the same extradata at
> the start of the first IRAP unit. As in ticket #7799, this can lead to
> decoding errors if modified parameter sets are signalled in-band.
> 
> This commit keeps track of the VPS/SPS/PPS parameter sets during the
> conversion. The correct combination is inserted at the start of the
> first IRAP. SEIs from extradata are inserted before each IRAP.
> 
> This commit also makes an update to the hevc-bsf-mp4toannexb fate test
> since the result before this patch contained duplicate parameter sets
> in-band.
> ---
>  libavcodec/hevc_mp4toannexb_bsf.c | 488 ++++++++++++++++++++++++++++--
>  tests/fate/hevc.mak               |   2 +-
>  2 files changed, 456 insertions(+), 34 deletions(-)
> 
> diff --git a/libavcodec/hevc_mp4toannexb_bsf.c b/libavcodec/hevc_mp4toannexb_bsf.c
> index 09bce5b34c..90a4d17d25 100644
> --- a/libavcodec/hevc_mp4toannexb_bsf.c
> +++ b/libavcodec/hevc_mp4toannexb_bsf.c
> @@ -23,19 +23,209 @@
>  
>  #include "libavutil/intreadwrite.h"
>  #include "libavutil/mem.h"
> +#include "libavutil/avassert.h"
>  
>  #include "avcodec.h"
>  #include "bsf.h"
>  #include "bytestream.h"
>  #include "hevc.h"
> +#include "h2645_parse.h"
> +#include "hevc_ps.h"
> +#include "golomb.h"
>  
>  #define MIN_HEVCC_LENGTH 23
> +#define PROFILE_WITHOUT_IDC_BITS    88
> +#define IS_IRAP(s)                  ((s)->type >= 16 && (s)->type <= 23)
> +#define IS_PARAMSET(s)              ((s)->type >= 32 && (s)->type <= 34)
> +
> +                                    /*reserved VCLs not included*/
> +#define IS_VCL(s)                   ((s)->type <= 9 || ((s)->type >= 16 && (s)->type <= 21))
> +#define HEVC_NAL_HEADER_BITS        16
> +
> +/*
> + *Copies data from input buffer to output buffer. Appends annexb startcode.
> + *out must be allocated at least out_len + in_len + 4.
> + */
> +#define WRITE_NAL(out, out_len, in, in_len) do {                        \
> +    AV_WB32((out) + (out_len), 1);                                      \
> +    (out_len) += 4;                                                     \
> +    memcpy((out) + (out_len), (in), (in_len));                          \
> +    (out_len) += (in_len);                                              \
> +} while (0)
> +
> +typedef struct Param {
> +    uint8_t *raw_data;          /*raw data to store param set payload*/
> +    int      raw_size;          /*size of raw_data*/
> +    size_t   allocated_size;    /*allocated size of raw_data*/
> +    int      ref;               /*stores the ref of the higher level parameter set*/
> +    int      is_signalled;      /*indicates whether this param has already been signalled in the cvs*/
> +} Param;
> +
> +/*modified version of HEVCParamSets to store bytestream and reference to previous level*/
> +typedef struct ParamSets {
> +    Param vps_list[HEVC_MAX_VPS_COUNT];
> +    Param sps_list[HEVC_MAX_SPS_COUNT];
> +    Param pps_list[HEVC_MAX_PPS_COUNT];
> +
> +    Param sei; /*cached SEIs from extradata in annexb format*/
> +} ParamSets;
>  
>  typedef struct HEVCBSFContext {
>      uint8_t  length_size;
>      int      extradata_parsed;
> +    ParamSets ps; /*cached VPS/SPS/PPS parameter sets + SEI from extradata*/
>  } HEVCBSFContext;
>  
> +static int update_cached_paramset(AVBSFContext *ctx, Param *cached_param,
> +                                  H2645NAL *nal, int ref)
> +{
> +    int ret;
> +
> +    if (cached_param->raw_data && cached_param->raw_size == nal->raw_size &&
> +        !memcmp(cached_param->raw_data, nal->raw_data, nal->raw_size)) {
> +        av_log(ctx, AV_LOG_DEBUG, "NAL unit: %d. Copy already exists in parameter set.\n", nal->type);
> +    } else {
> +        if (nal->raw_size > cached_param->allocated_size) {
> +            ret = av_reallocp(&cached_param->raw_data, nal->raw_size);
> +            if (ret < 0)
> +                return ret;
> +            cached_param->allocated_size = nal->raw_size;
> +        }
> +        memcpy(cached_param->raw_data, nal->raw_data, nal->raw_size);
> +        cached_param->raw_size     = nal->raw_size;
> +        cached_param->ref          = ref;
> +        cached_param->is_signalled = 0;
> +    }
> +    return 0;
> +}
> +
> +static int parse_vps(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    int vps_id, ret;
> +
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +
> +    GetBitContext *gb = &nal->gb;
> +    gb->index         = HEVC_NAL_HEADER_BITS;
> +
> +    vps_id = get_bits(gb, 4);
> +
> +    av_log(ctx, AV_LOG_TRACE, "Updating VPS id: %d\n", vps_id);
> +    ret = update_cached_paramset(ctx, &ps->vps_list[vps_id], nal, 0);
> +    return ret;
> +}
> +
> +static int parse_sps(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    int i, ret;
> +    int sps_id, vps_ref, max_sub_layers_minus1;
> +
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +
> +    uint8_t sub_layer_profile_present_flag[HEVC_MAX_SUB_LAYERS];
> +    uint8_t sub_layer_level_present_flag[HEVC_MAX_SUB_LAYERS];
> +
> +    GetBitContext *gb = &nal->gb;
> +    gb->index         = HEVC_NAL_HEADER_BITS;
> +
> +    vps_ref = get_bits(gb, 4);
> +
> +    max_sub_layers_minus1 = get_bits(gb, 3);
> +    skip_bits1(gb);                          /*sps_temporal_id_nesting_flag*/
> +    skip_bits(gb, PROFILE_WITHOUT_IDC_BITS); /*profile_tier_level*/
> +    skip_bits(gb, 8);                        /*general_level_idc*/
> +
> +    for (i = 0; i < max_sub_layers_minus1; i++) {
> +        sub_layer_profile_present_flag[i] = get_bits1(gb);
> +        sub_layer_level_present_flag[i]   = get_bits1(gb);
> +    }
> +
> +    if (max_sub_layers_minus1 > 0)
> +        for (i = max_sub_layers_minus1; i < 8; i++)
> +            skip_bits(gb, 2); /*reserved_zero_2bits[i]*/
> +
> +    for (i = 0; i < max_sub_layers_minus1; i++) {
> +        if (sub_layer_profile_present_flag[i])
> +            skip_bits(gb, PROFILE_WITHOUT_IDC_BITS); /*profile_tier_level*/
> +        if (sub_layer_level_present_flag[i])
> +            skip_bits(gb, 8);                        /*sub_layer_level_idc*/
> +    }
> +
> +    /*we only need the sps_id index*/
> +    sps_id = get_ue_golomb_long(gb);
> +    if (sps_id < 0 ||  sps_id >= HEVC_MAX_SPS_COUNT) {
> +        av_log(ctx, AV_LOG_ERROR, "SPS id out of range: %d\n", sps_id);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    av_log(ctx, AV_LOG_TRACE, "Updating SPS id: %d, VPS ref: %d\n", sps_id, vps_ref);
> +    ret = update_cached_paramset(ctx, &ps->sps_list[sps_id], nal, vps_ref);
> +    return ret;
> +}
> +
> +static int parse_pps(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    int ret;
> +    int pps_id, sps_ref;
> +
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +
> +    GetBitContext *gb = &nal->gb;
> +    gb->index         = HEVC_NAL_HEADER_BITS;
> +
> +    pps_id = get_ue_golomb_long(gb);
> +    if (pps_id < 0 || pps_id >= HEVC_MAX_PPS_COUNT) {
> +        av_log(ctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    sps_ref = get_ue_golomb_long(gb);
> +    if (sps_ref < 0 || sps_ref >= HEVC_MAX_SPS_COUNT) {
> +        av_log(ctx, AV_LOG_ERROR, "SPS id out of range: %d\n", sps_ref);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    av_log(ctx, AV_LOG_TRACE, "Updating PPS id: %d, SPS ref: %d\n", pps_id, sps_ref);
> +    ret = update_cached_paramset(ctx, &ps->pps_list[pps_id], nal, sps_ref);
> +    return ret;
> +}
> +
> +static int append_sei_annexb(Param *sei, H2645NAL *nal)
> +{
> +    int ret;
> +
> +    ret = av_reallocp(&sei->raw_data, sei->allocated_size + nal->raw_size + 4);
> +    if (ret < 0)
> +        return ret;
> +    sei->allocated_size += nal->raw_size + 4;
> +
> +    WRITE_NAL(sei->raw_data, sei->raw_size, nal->raw_data, nal->raw_size);
> +    av_assert1(sei->raw_size == sei->allocated_size);
> +    return 0;
> +}
> +
> +static int update_paramset(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    int ret;
> +    switch (nal->type) {
> +        case (HEVC_NAL_VPS):
> +            if ((ret = parse_vps(ctx, nal)) < 0)
> +                return ret;
> +            break;
> +        case (HEVC_NAL_SPS):
> +            if ((ret = parse_sps(ctx, nal)) < 0)
> +                return ret;
> +            break;
> +        case (HEVC_NAL_PPS):
> +            if ((ret = parse_pps(ctx, nal)) < 0)
> +                return ret;
> +    }
> +    return 0;
> +}
> +
>  static int hevc_extradata_to_annexb(AVBSFContext *ctx)
>  {
>      GetByteContext gb;
> @@ -97,84 +287,315 @@ fail:
>  static int hevc_mp4toannexb_init(AVBSFContext *ctx)
>  {
>      HEVCBSFContext *s = ctx->priv_data;
> -    int ret;
> +    H2645Packet pkt;
> +    int i, ret;
>  
>      if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH ||
>          AV_RB24(ctx->par_in->extradata) == 1           ||
>          AV_RB32(ctx->par_in->extradata) == 1) {
>          av_log(ctx, AV_LOG_VERBOSE,
>                 "The input looks like it is Annex B already\n");
> +        return 0;
>      } else {
>          ret = hevc_extradata_to_annexb(ctx);
>          if (ret < 0)
>              return ret;
>          s->length_size      = ret;
>          s->extradata_parsed = 1;
> +
> +        memset(&pkt, 0, sizeof(H2645Packet));
> +        ret = ff_h2645_packet_split(&pkt, ctx->par_out->extradata, ctx->par_out->extradata_size,
> +                                     ctx, 0, 0, AV_CODEC_ID_HEVC, 1, 0);
> +        if (ret < 0)
> +            goto done;
> +
> +        for (i = 0; i < pkt.nb_nals; ++i) {
> +            H2645NAL *nal = &pkt.nals[i];
> +
> +            /*current segmentation algorithm includes next 0x00 from next nal unit*/
> +            if (nal->raw_data[nal->raw_size - 1] == 0x00)
> +                nal->raw_size--;
> +
> +            if (IS_PARAMSET(nal)) {
> +                ret = update_paramset(ctx, nal);
> +                if (ret < 0)
> +                    goto done;
> +                continue;
> +            }
> +
> +            if (nal->type == HEVC_NAL_SEI_PREFIX || nal->type == HEVC_NAL_SEI_SUFFIX) {
> +                ret = append_sei_annexb(&s->ps.sei, nal);
> +                if (ret < 0)
> +                    goto done;
> +            }
> +        }
> +    }
> +done:
> +    ff_h2645_packet_uninit(&pkt);
> +    return ret;
> +}
> +
> +static void ps_uninit(ParamSets *ps)
> +{
> +    int i;
> +    for (i = 0; i < FF_ARRAY_ELEMS(ps->vps_list); i++) {
> +       av_freep(&ps->vps_list[i].raw_data);
> +       ps->vps_list[i].allocated_size = 0;
> +       ps->vps_list[i].raw_size       = 0;
> +    }
> +    for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) {
> +       av_freep(&ps->sps_list[i].raw_data);
> +       ps->sps_list[i].allocated_size = 0;
> +       ps->sps_list[i].raw_size       = 0;
> +    }
> +    for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) {
> +       av_freep(&ps->pps_list[i].raw_data);
> +       ps->pps_list[i].allocated_size = 0;
> +       ps->pps_list[i].raw_size       = 0;
> +    }
> +    av_freep(&ps->sei);
> +    ps->sei.allocated_size = 0;
> +    ps->sei.raw_size       = 0;
> +}
> +
> +static void hevc_mp4toannexb_close(AVBSFContext *ctx)
> +{
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ps_uninit(&s->ps);
> +}
> +
> +static int get_vcl_pps_id(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    GetBitContext *gb = &nal->gb;
> +    int pps_id;
> +
> +    gb->index = HEVC_NAL_HEADER_BITS;
> +
> +    skip_bits1(gb);        /*first_slice_segment_in_pic_flag*/
> +
> +    if (IS_IRAP(nal))
> +        skip_bits1(gb);    /*no_output_of_prior_pics_flag*/
> +
> +    pps_id = get_ue_golomb_long(gb);
> +    if (pps_id < 0 || pps_id >= HEVC_MAX_PPS_COUNT) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS id: %d\n", pps_id);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    return pps_id;
> +}
> +
> +static int write_vcl(AVBSFContext *ctx, AVPacket *pkt_out, int *prev_size,
> +                     H2645NAL *nal)
> +{
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +    int ret, pps_id;
> +    Param *pps;
> +
> +    /*get frame pps*/
> +    pps_id = get_vcl_pps_id(ctx, nal);
> +    if (pps_id < 0)
> +        return AVERROR_INVALIDDATA;
> +    av_log(ctx, AV_LOG_TRACE, "VCL PPS id: %d\n", pps_id);
> +
> +    /*write pps if not signalled*/
> +    pps = &ps->pps_list[pps_id];
> +    if (!pps->raw_data) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS: %d doesn't exist\n", pps_id);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    if (!pps->is_signalled) {
> +        av_log(ctx, AV_LOG_TRACE, "Referenced PPS: %d has not been signalled. Writing PPS to output stream\n", pps_id);
> +        ret = av_grow_packet(pkt_out, pps->raw_size + 4);
> +        if (ret < 0)
> +            return ret;
> +        WRITE_NAL(pkt_out->data, *prev_size, pps->raw_data, pps->raw_size);
> +        pps->is_signalled = 1;
> +    }
> +
> +    /*write actual packet*/
> +    ret = av_grow_packet(pkt_out, nal->raw_size + 4);
> +    if (ret < 0)
> +        return ret;
> +    WRITE_NAL(pkt_out->data, *prev_size, nal->raw_data, nal->raw_size);
> +
> +    return 0;
> +}
> +
> +static int write_extradata(AVBSFContext *ctx, AVPacket *pkt_out, int *prev_size,
> +                           H2645NAL *nal_irap)
> +{
> +    int ref, ret;
> +    int new_extradata_size = 0;
> +
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +    GetBitContext *gb = &nal_irap->gb;
> +
> +    /*active parameter sets for the new irap*/
> +    const Param *vps;
> +    const Param *sps;
> +    Param *pps; /*non-const because we have to update flag that pps_id is signalled in cvs*/
> +
> +    skip_bits1(gb); /*first_slice_ic_pic_flag*/
> +    skip_bits1(gb); /*no_output_of_prior_pics_flag*/
> +
> +    ref = get_ue_golomb_long(gb);
> +    if (ref < 0 || ref >= HEVC_MAX_PPS_COUNT || !ps->pps_list[ref].raw_data) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS: %d\n", ref);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    av_log(ctx, AV_LOG_TRACE, "Writing PPS id: %d\n", ref);
> +    pps                 = &ps->pps_list[ref];
> +    new_extradata_size += pps->raw_size + 4;
> +    ref                 = pps->ref;
> +
> +    if (ref >= HEVC_MAX_SPS_COUNT || !ps->sps_list[ref].raw_data) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid SPS: %d\n", ref);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    av_log(ctx, AV_LOG_TRACE, "Writing SPS id: %d\n", ref);
> +    sps                 = &ps->sps_list[ref];
> +    new_extradata_size += sps->raw_size + 4;
> +    ref                 = sps->ref;
> +
> +    if (ref >= HEVC_MAX_VPS_COUNT || !ps->vps_list[ref].raw_data) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid VPS: %d\n", ref);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    av_log(ctx, AV_LOG_TRACE, "Writing VPS id: %d\n", ref);
> +    vps                 = &ps->vps_list[ref];
> +    new_extradata_size += vps->raw_size + 4;
> +
> +    if (ps->sei.raw_data)
> +        new_extradata_size += ps->sei.raw_size; /*+4 not needed because it's already in annexb*/
> +
> +    ret = av_grow_packet(pkt_out, new_extradata_size);
> +    if (ret < 0)
> +        return ret;
> +
> +    WRITE_NAL(pkt_out->data, *prev_size, vps->raw_data, vps->raw_size);
> +    WRITE_NAL(pkt_out->data, *prev_size, sps->raw_data, sps->raw_size);
> +    WRITE_NAL(pkt_out->data, *prev_size, pps->raw_data, pps->raw_size);
> +    pps->is_signalled = 1;
> +
> +    if (ps->sei.raw_data) {
> +        memcpy(pkt_out->data + *prev_size, ps->sei.raw_data, ps->sei.raw_size);
> +        *prev_size += ps->sei.raw_size;
>      }
>  
> +    av_assert1(*prev_size == pkt_out->size);
>      return 0;
>  }
>  
> +/*
> + *Function converts mp4 access unit into annexb
> + *Output packet structure
> + *VPS, SPS, PPS, [SEI(from extradata)], [SEI_PREFIX(from access unit)], IRAP, [SEI_SUFFIX]
> + *or
> + *[SEI_PREFIX (from access unit)], [PPS (if not already signalled)], VCL(non-irap), [SEI_SUFFIX]
> + */
>  static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
>  {
>      HEVCBSFContext *s = ctx->priv_data;
>      AVPacket *in;
> -    GetByteContext gb;
> -
> -    int got_irap = 0;
> -    int i, ret = 0;
> +    H2645Packet pkt;
> +    int i, j, prev_size, ret;
> +    int got_irap;
>  
>      ret = ff_bsf_get_packet(ctx, &in);
>      if (ret < 0)
>          return ret;
>  
> +    /*output the annexb nalu if extradata is not parsed*/
>      if (!s->extradata_parsed) {
>          av_packet_move_ref(out, in);
>          av_packet_free(&in);
>          return 0;
>      }
>  
> -    bytestream2_init(&gb, in->data, in->size);
> +    memset(&pkt, 0, sizeof(H2645Packet));
> +    ret = ff_h2645_packet_split(&pkt, in->data, in->size, ctx, 1, s->length_size, AV_CODEC_ID_HEVC, 1, 0);
> +    if (ret < 0)
> +        goto done;
>  
> -    while (bytestream2_get_bytes_left(&gb)) {
> -        uint32_t nalu_size = 0;
> -        int      nalu_type;
> -        int is_irap, add_extradata, extra_size, prev_size;
> +    got_irap = 1; /*1 means that there is no irap or the irap has already been processed.*/
> +    for (i = 0; i < pkt.nb_nals; i++) {
> +        if (IS_IRAP(&pkt.nals[i])) {
> +            got_irap = 0;
> +            break;
> +        }
> +    }
>  
> -        for (i = 0; i < s->length_size; i++)
> -            nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
> +    prev_size = out->size; /*prev_size stores the current length of output packet */
> +    av_assert1(prev_size == 0);
>  
> -        nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
> +    for (i = 0; i < pkt.nb_nals; i++) {
> +        H2645NAL *nal = &pkt.nals[i];
>  
> -        /* prepend extradata to IRAP frames */
> -        is_irap       = nalu_type >= 16 && nalu_type <= 23;
> -        add_extradata = is_irap && !got_irap;
> -        extra_size    = add_extradata * ctx->par_out->extradata_size;
> -        got_irap     |= is_irap;
> +        if (IS_PARAMSET(nal)) {
> +            ret = update_paramset(ctx, nal);
> +            if (ret < 0)
> +                goto done;
> +            continue;
> +        }
>  
> -        if (SIZE_MAX - nalu_size < 4 ||
> -            SIZE_MAX - 4 - nalu_size < extra_size) {
> -            ret = AVERROR_INVALIDDATA;
> -            goto fail;
> +        if (!got_irap && IS_IRAP(nal)) { /*append extradata and sei before first irap*/
> +
> +            /*reset the pps signalled flag in this cvs*/
> +            for (j = 0; j < HEVC_MAX_PPS_COUNT; j++)
> +                s->ps.pps_list[j].is_signalled = 0;
> +
> +            ret = write_extradata(ctx, out, &prev_size, nal);
> +            if (ret < 0)
> +                goto done;
> +
> +            /*append any past SEI prefix nals*/
> +            for (j = 0; j < i; j++) {
> +                H2645NAL *nal_past = &pkt.nals[j];
> +                if (nal_past->type == HEVC_NAL_SEI_PREFIX) {
> +                    ret = av_grow_packet(out, nal_past->raw_size + 4);
> +                    if (ret < 0)
> +                        goto done;
> +                    WRITE_NAL(out->data, prev_size, nal_past->raw_data, nal_past->raw_size);
> +                }
> +            }
> +
> +            /*write irap nal unit*/
> +            ret = write_vcl(ctx, out, &prev_size, nal);
> +            if (ret < 0)
> +                goto done;
> +            got_irap = 1;
> +            continue;
>          }
>  
> -        prev_size = out->size;
> +        /*do not write any sei prefix nal units if irap exists but has not been
> +         * processed*/
> +        if (nal->type == HEVC_NAL_SEI_PREFIX && !got_irap)
> +            continue;
>  
> -        ret = av_grow_packet(out, 4 + nalu_size + extra_size);
> -        if (ret < 0)
> -            goto fail;
> +        if (IS_VCL(nal)) {
> +            ret = write_vcl(ctx, out, &prev_size, nal);
> +            if (ret < 0)
> +                goto done;
> +            continue;
> +        }
>  
> -        if (add_extradata)
> -            memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
> -        AV_WB32(out->data + prev_size + extra_size, 1);
> -        bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size);
> +        /*copy any other nal units to the output - i.e. SEI_SUFFIX*/
> +        ret = av_grow_packet(out, nal->raw_size + 4);
> +        if (ret < 0)
> +            goto done;
> +        WRITE_NAL(out->data, prev_size, nal->raw_data, nal->raw_size);
>      }
>  
> +    av_assert1(prev_size == out->size);
> +
>      ret = av_packet_copy_props(out, in);
> -    if (ret < 0)
> -        goto fail;
>  
> -fail:
> +done:
> +    ff_h2645_packet_uninit(&pkt);
> +
>      if (ret < 0)
>          av_packet_unref(out);
>      av_packet_free(&in);
> @@ -190,6 +611,7 @@ const AVBitStreamFilter ff_hevc_mp4toannexb_bsf = {
>      .name           = "hevc_mp4toannexb",
>      .priv_data_size = sizeof(HEVCBSFContext),
>      .init           = hevc_mp4toannexb_init,
> +    .close          = hevc_mp4toannexb_close,
>      .filter         = hevc_mp4toannexb_filter,
>      .codec_ids      = codec_ids,
>  };
> diff --git a/tests/fate/hevc.mak b/tests/fate/hevc.mak
> index 559c3898bc..4f812b0834 100644
> --- a/tests/fate/hevc.mak
> +++ b/tests/fate/hevc.mak
> @@ -238,7 +238,7 @@ FATE_HEVC-$(call ALLYES, HEVC_DEMUXER MOV_DEMUXER HEVC_MP4TOANNEXB_BSF MOV_MUXER
>  fate-hevc-bsf-mp4toannexb: tests/data/hevc-mp4.mov
>  fate-hevc-bsf-mp4toannexb: CMD = md5 -i $(TARGET_PATH)/tests/data/hevc-mp4.mov -c:v copy -fflags +bitexact -f hevc
>  fate-hevc-bsf-mp4toannexb: CMP = oneline
> -fate-hevc-bsf-mp4toannexb: REF = 1873662a3af1848c37e4eb25722c8df9
> +fate-hevc-bsf-mp4toannexb: REF = 3c9d998a3aa2b9e0fb1c1f434952bf8b
>  
>  fate-hevc-skiploopfilter: CMD = framemd5 -skip_loop_filter nokey -i $(TARGET_SAMPLES)/hevc-conformance/SAO_D_Samsung_5.bit -sws_flags bitexact
>  FATE_HEVC += fate-hevc-skiploopfilter
> -- 
> 2.23.0
> 

ping
Andriy Gelman Oct. 7, 2019, 1:44 p.m. UTC | #2
On Wed, 02. Oct 08:05, Andriy Gelman wrote:
> On Thu, 26. Sep 14:09, Andriy Gelman wrote:
> > From: Andriy Gelman <andriy.gelman@gmail.com>
> > 
> > Fixes #7799
> > 
> > Currently, the mp4toannexb filter always inserts the same extradata at
> > the start of the first IRAP unit. As in ticket #7799, this can lead to
> > decoding errors if modified parameter sets are signalled in-band.
> > 
> > This commit keeps track of the VPS/SPS/PPS parameter sets during the
> > conversion. The correct combination is inserted at the start of the
> > first IRAP. SEIs from extradata are inserted before each IRAP.
> > 
> > This commit also makes an update to the hevc-bsf-mp4toannexb fate test
> > since the result before this patch contained duplicate parameter sets
> > in-band.
> > ---
> >  libavcodec/hevc_mp4toannexb_bsf.c | 488 ++++++++++++++++++++++++++++--
> >  tests/fate/hevc.mak               |   2 +-
> >  2 files changed, 456 insertions(+), 34 deletions(-)
> > 
> > diff --git a/libavcodec/hevc_mp4toannexb_bsf.c b/libavcodec/hevc_mp4toannexb_bsf.c
> > index 09bce5b34c..90a4d17d25 100644
> > --- a/libavcodec/hevc_mp4toannexb_bsf.c
> > +++ b/libavcodec/hevc_mp4toannexb_bsf.c
> > @@ -23,19 +23,209 @@
> >  
> >  #include "libavutil/intreadwrite.h"
> >  #include "libavutil/mem.h"
> > +#include "libavutil/avassert.h"
> >  
> >  #include "avcodec.h"
> >  #include "bsf.h"
> >  #include "bytestream.h"
> >  #include "hevc.h"
> > +#include "h2645_parse.h"
> > +#include "hevc_ps.h"
> > +#include "golomb.h"
> >  
> >  #define MIN_HEVCC_LENGTH 23
> > +#define PROFILE_WITHOUT_IDC_BITS    88
> > +#define IS_IRAP(s)                  ((s)->type >= 16 && (s)->type <= 23)
> > +#define IS_PARAMSET(s)              ((s)->type >= 32 && (s)->type <= 34)
> > +
> > +                                    /*reserved VCLs not included*/
> > +#define IS_VCL(s)                   ((s)->type <= 9 || ((s)->type >= 16 && (s)->type <= 21))
> > +#define HEVC_NAL_HEADER_BITS        16
> > +
> > +/*
> > + *Copies data from input buffer to output buffer. Appends annexb startcode.
> > + *out must be allocated at least out_len + in_len + 4.
> > + */
> > +#define WRITE_NAL(out, out_len, in, in_len) do {                        \
> > +    AV_WB32((out) + (out_len), 1);                                      \
> > +    (out_len) += 4;                                                     \
> > +    memcpy((out) + (out_len), (in), (in_len));                          \
> > +    (out_len) += (in_len);                                              \
> > +} while (0)
> > +
> > +typedef struct Param {
> > +    uint8_t *raw_data;          /*raw data to store param set payload*/
> > +    int      raw_size;          /*size of raw_data*/
> > +    size_t   allocated_size;    /*allocated size of raw_data*/
> > +    int      ref;               /*stores the ref of the higher level parameter set*/
> > +    int      is_signalled;      /*indicates whether this param has already been signalled in the cvs*/
> > +} Param;
> > +
> > +/*modified version of HEVCParamSets to store bytestream and reference to previous level*/
> > +typedef struct ParamSets {
> > +    Param vps_list[HEVC_MAX_VPS_COUNT];
> > +    Param sps_list[HEVC_MAX_SPS_COUNT];
> > +    Param pps_list[HEVC_MAX_PPS_COUNT];
> > +
> > +    Param sei; /*cached SEIs from extradata in annexb format*/
> > +} ParamSets;
> >  
> >  typedef struct HEVCBSFContext {
> >      uint8_t  length_size;
> >      int      extradata_parsed;
> > +    ParamSets ps; /*cached VPS/SPS/PPS parameter sets + SEI from extradata*/
> >  } HEVCBSFContext;
> >  
> > +static int update_cached_paramset(AVBSFContext *ctx, Param *cached_param,
> > +                                  H2645NAL *nal, int ref)
> > +{
> > +    int ret;
> > +
> > +    if (cached_param->raw_data && cached_param->raw_size == nal->raw_size &&
> > +        !memcmp(cached_param->raw_data, nal->raw_data, nal->raw_size)) {
> > +        av_log(ctx, AV_LOG_DEBUG, "NAL unit: %d. Copy already exists in parameter set.\n", nal->type);
> > +    } else {
> > +        if (nal->raw_size > cached_param->allocated_size) {
> > +            ret = av_reallocp(&cached_param->raw_data, nal->raw_size);
> > +            if (ret < 0)
> > +                return ret;
> > +            cached_param->allocated_size = nal->raw_size;
> > +        }
> > +        memcpy(cached_param->raw_data, nal->raw_data, nal->raw_size);
> > +        cached_param->raw_size     = nal->raw_size;
> > +        cached_param->ref          = ref;
> > +        cached_param->is_signalled = 0;
> > +    }
> > +    return 0;
> > +}
> > +
> > +static int parse_vps(AVBSFContext *ctx, H2645NAL *nal)
> > +{
> > +    int vps_id, ret;
> > +
> > +    HEVCBSFContext *s = ctx->priv_data;
> > +    ParamSets *ps     = &s->ps;
> > +
> > +    GetBitContext *gb = &nal->gb;
> > +    gb->index         = HEVC_NAL_HEADER_BITS;
> > +
> > +    vps_id = get_bits(gb, 4);
> > +
> > +    av_log(ctx, AV_LOG_TRACE, "Updating VPS id: %d\n", vps_id);
> > +    ret = update_cached_paramset(ctx, &ps->vps_list[vps_id], nal, 0);
> > +    return ret;
> > +}
> > +
> > +static int parse_sps(AVBSFContext *ctx, H2645NAL *nal)
> > +{
> > +    int i, ret;
> > +    int sps_id, vps_ref, max_sub_layers_minus1;
> > +
> > +    HEVCBSFContext *s = ctx->priv_data;
> > +    ParamSets *ps     = &s->ps;
> > +
> > +    uint8_t sub_layer_profile_present_flag[HEVC_MAX_SUB_LAYERS];
> > +    uint8_t sub_layer_level_present_flag[HEVC_MAX_SUB_LAYERS];
> > +
> > +    GetBitContext *gb = &nal->gb;
> > +    gb->index         = HEVC_NAL_HEADER_BITS;
> > +
> > +    vps_ref = get_bits(gb, 4);
> > +
> > +    max_sub_layers_minus1 = get_bits(gb, 3);
> > +    skip_bits1(gb);                          /*sps_temporal_id_nesting_flag*/
> > +    skip_bits(gb, PROFILE_WITHOUT_IDC_BITS); /*profile_tier_level*/
> > +    skip_bits(gb, 8);                        /*general_level_idc*/
> > +
> > +    for (i = 0; i < max_sub_layers_minus1; i++) {
> > +        sub_layer_profile_present_flag[i] = get_bits1(gb);
> > +        sub_layer_level_present_flag[i]   = get_bits1(gb);
> > +    }
> > +
> > +    if (max_sub_layers_minus1 > 0)
> > +        for (i = max_sub_layers_minus1; i < 8; i++)
> > +            skip_bits(gb, 2); /*reserved_zero_2bits[i]*/
> > +
> > +    for (i = 0; i < max_sub_layers_minus1; i++) {
> > +        if (sub_layer_profile_present_flag[i])
> > +            skip_bits(gb, PROFILE_WITHOUT_IDC_BITS); /*profile_tier_level*/
> > +        if (sub_layer_level_present_flag[i])
> > +            skip_bits(gb, 8);                        /*sub_layer_level_idc*/
> > +    }
> > +
> > +    /*we only need the sps_id index*/
> > +    sps_id = get_ue_golomb_long(gb);
> > +    if (sps_id < 0 ||  sps_id >= HEVC_MAX_SPS_COUNT) {
> > +        av_log(ctx, AV_LOG_ERROR, "SPS id out of range: %d\n", sps_id);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    av_log(ctx, AV_LOG_TRACE, "Updating SPS id: %d, VPS ref: %d\n", sps_id, vps_ref);
> > +    ret = update_cached_paramset(ctx, &ps->sps_list[sps_id], nal, vps_ref);
> > +    return ret;
> > +}
> > +
> > +static int parse_pps(AVBSFContext *ctx, H2645NAL *nal)
> > +{
> > +    int ret;
> > +    int pps_id, sps_ref;
> > +
> > +    HEVCBSFContext *s = ctx->priv_data;
> > +    ParamSets *ps     = &s->ps;
> > +
> > +    GetBitContext *gb = &nal->gb;
> > +    gb->index         = HEVC_NAL_HEADER_BITS;
> > +
> > +    pps_id = get_ue_golomb_long(gb);
> > +    if (pps_id < 0 || pps_id >= HEVC_MAX_PPS_COUNT) {
> > +        av_log(ctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    sps_ref = get_ue_golomb_long(gb);
> > +    if (sps_ref < 0 || sps_ref >= HEVC_MAX_SPS_COUNT) {
> > +        av_log(ctx, AV_LOG_ERROR, "SPS id out of range: %d\n", sps_ref);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    av_log(ctx, AV_LOG_TRACE, "Updating PPS id: %d, SPS ref: %d\n", pps_id, sps_ref);
> > +    ret = update_cached_paramset(ctx, &ps->pps_list[pps_id], nal, sps_ref);
> > +    return ret;
> > +}
> > +
> > +static int append_sei_annexb(Param *sei, H2645NAL *nal)
> > +{
> > +    int ret;
> > +
> > +    ret = av_reallocp(&sei->raw_data, sei->allocated_size + nal->raw_size + 4);
> > +    if (ret < 0)
> > +        return ret;
> > +    sei->allocated_size += nal->raw_size + 4;
> > +
> > +    WRITE_NAL(sei->raw_data, sei->raw_size, nal->raw_data, nal->raw_size);
> > +    av_assert1(sei->raw_size == sei->allocated_size);
> > +    return 0;
> > +}
> > +
> > +static int update_paramset(AVBSFContext *ctx, H2645NAL *nal)
> > +{
> > +    int ret;
> > +    switch (nal->type) {
> > +        case (HEVC_NAL_VPS):
> > +            if ((ret = parse_vps(ctx, nal)) < 0)
> > +                return ret;
> > +            break;
> > +        case (HEVC_NAL_SPS):
> > +            if ((ret = parse_sps(ctx, nal)) < 0)
> > +                return ret;
> > +            break;
> > +        case (HEVC_NAL_PPS):
> > +            if ((ret = parse_pps(ctx, nal)) < 0)
> > +                return ret;
> > +    }
> > +    return 0;
> > +}
> > +
> >  static int hevc_extradata_to_annexb(AVBSFContext *ctx)
> >  {
> >      GetByteContext gb;
> > @@ -97,84 +287,315 @@ fail:
> >  static int hevc_mp4toannexb_init(AVBSFContext *ctx)
> >  {
> >      HEVCBSFContext *s = ctx->priv_data;
> > -    int ret;
> > +    H2645Packet pkt;
> > +    int i, ret;
> >  
> >      if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH ||
> >          AV_RB24(ctx->par_in->extradata) == 1           ||
> >          AV_RB32(ctx->par_in->extradata) == 1) {
> >          av_log(ctx, AV_LOG_VERBOSE,
> >                 "The input looks like it is Annex B already\n");
> > +        return 0;
> >      } else {
> >          ret = hevc_extradata_to_annexb(ctx);
> >          if (ret < 0)
> >              return ret;
> >          s->length_size      = ret;
> >          s->extradata_parsed = 1;
> > +
> > +        memset(&pkt, 0, sizeof(H2645Packet));
> > +        ret = ff_h2645_packet_split(&pkt, ctx->par_out->extradata, ctx->par_out->extradata_size,
> > +                                     ctx, 0, 0, AV_CODEC_ID_HEVC, 1, 0);
> > +        if (ret < 0)
> > +            goto done;
> > +
> > +        for (i = 0; i < pkt.nb_nals; ++i) {
> > +            H2645NAL *nal = &pkt.nals[i];
> > +
> > +            /*current segmentation algorithm includes next 0x00 from next nal unit*/
> > +            if (nal->raw_data[nal->raw_size - 1] == 0x00)
> > +                nal->raw_size--;
> > +
> > +            if (IS_PARAMSET(nal)) {
> > +                ret = update_paramset(ctx, nal);
> > +                if (ret < 0)
> > +                    goto done;
> > +                continue;
> > +            }
> > +
> > +            if (nal->type == HEVC_NAL_SEI_PREFIX || nal->type == HEVC_NAL_SEI_SUFFIX) {
> > +                ret = append_sei_annexb(&s->ps.sei, nal);
> > +                if (ret < 0)
> > +                    goto done;
> > +            }
> > +        }
> > +    }
> > +done:
> > +    ff_h2645_packet_uninit(&pkt);
> > +    return ret;
> > +}
> > +
> > +static void ps_uninit(ParamSets *ps)
> > +{
> > +    int i;
> > +    for (i = 0; i < FF_ARRAY_ELEMS(ps->vps_list); i++) {
> > +       av_freep(&ps->vps_list[i].raw_data);
> > +       ps->vps_list[i].allocated_size = 0;
> > +       ps->vps_list[i].raw_size       = 0;
> > +    }
> > +    for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) {
> > +       av_freep(&ps->sps_list[i].raw_data);
> > +       ps->sps_list[i].allocated_size = 0;
> > +       ps->sps_list[i].raw_size       = 0;
> > +    }
> > +    for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) {
> > +       av_freep(&ps->pps_list[i].raw_data);
> > +       ps->pps_list[i].allocated_size = 0;
> > +       ps->pps_list[i].raw_size       = 0;
> > +    }
> > +    av_freep(&ps->sei);
> > +    ps->sei.allocated_size = 0;
> > +    ps->sei.raw_size       = 0;
> > +}
> > +
> > +static void hevc_mp4toannexb_close(AVBSFContext *ctx)
> > +{
> > +    HEVCBSFContext *s = ctx->priv_data;
> > +    ps_uninit(&s->ps);
> > +}
> > +
> > +static int get_vcl_pps_id(AVBSFContext *ctx, H2645NAL *nal)
> > +{
> > +    GetBitContext *gb = &nal->gb;
> > +    int pps_id;
> > +
> > +    gb->index = HEVC_NAL_HEADER_BITS;
> > +
> > +    skip_bits1(gb);        /*first_slice_segment_in_pic_flag*/
> > +
> > +    if (IS_IRAP(nal))
> > +        skip_bits1(gb);    /*no_output_of_prior_pics_flag*/
> > +
> > +    pps_id = get_ue_golomb_long(gb);
> > +    if (pps_id < 0 || pps_id >= HEVC_MAX_PPS_COUNT) {
> > +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS id: %d\n", pps_id);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    return pps_id;
> > +}
> > +
> > +static int write_vcl(AVBSFContext *ctx, AVPacket *pkt_out, int *prev_size,
> > +                     H2645NAL *nal)
> > +{
> > +    HEVCBSFContext *s = ctx->priv_data;
> > +    ParamSets *ps     = &s->ps;
> > +    int ret, pps_id;
> > +    Param *pps;
> > +
> > +    /*get frame pps*/
> > +    pps_id = get_vcl_pps_id(ctx, nal);
> > +    if (pps_id < 0)
> > +        return AVERROR_INVALIDDATA;
> > +    av_log(ctx, AV_LOG_TRACE, "VCL PPS id: %d\n", pps_id);
> > +
> > +    /*write pps if not signalled*/
> > +    pps = &ps->pps_list[pps_id];
> > +    if (!pps->raw_data) {
> > +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS: %d doesn't exist\n", pps_id);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    if (!pps->is_signalled) {
> > +        av_log(ctx, AV_LOG_TRACE, "Referenced PPS: %d has not been signalled. Writing PPS to output stream\n", pps_id);
> > +        ret = av_grow_packet(pkt_out, pps->raw_size + 4);
> > +        if (ret < 0)
> > +            return ret;
> > +        WRITE_NAL(pkt_out->data, *prev_size, pps->raw_data, pps->raw_size);
> > +        pps->is_signalled = 1;
> > +    }
> > +
> > +    /*write actual packet*/
> > +    ret = av_grow_packet(pkt_out, nal->raw_size + 4);
> > +    if (ret < 0)
> > +        return ret;
> > +    WRITE_NAL(pkt_out->data, *prev_size, nal->raw_data, nal->raw_size);
> > +
> > +    return 0;
> > +}
> > +
> > +static int write_extradata(AVBSFContext *ctx, AVPacket *pkt_out, int *prev_size,
> > +                           H2645NAL *nal_irap)
> > +{
> > +    int ref, ret;
> > +    int new_extradata_size = 0;
> > +
> > +    HEVCBSFContext *s = ctx->priv_data;
> > +    ParamSets *ps     = &s->ps;
> > +    GetBitContext *gb = &nal_irap->gb;
> > +
> > +    /*active parameter sets for the new irap*/
> > +    const Param *vps;
> > +    const Param *sps;
> > +    Param *pps; /*non-const because we have to update flag that pps_id is signalled in cvs*/
> > +
> > +    skip_bits1(gb); /*first_slice_ic_pic_flag*/
> > +    skip_bits1(gb); /*no_output_of_prior_pics_flag*/
> > +
> > +    ref = get_ue_golomb_long(gb);
> > +    if (ref < 0 || ref >= HEVC_MAX_PPS_COUNT || !ps->pps_list[ref].raw_data) {
> > +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS: %d\n", ref);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    av_log(ctx, AV_LOG_TRACE, "Writing PPS id: %d\n", ref);
> > +    pps                 = &ps->pps_list[ref];
> > +    new_extradata_size += pps->raw_size + 4;
> > +    ref                 = pps->ref;
> > +
> > +    if (ref >= HEVC_MAX_SPS_COUNT || !ps->sps_list[ref].raw_data) {
> > +        av_log(ctx, AV_LOG_ERROR, "Invalid SPS: %d\n", ref);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    av_log(ctx, AV_LOG_TRACE, "Writing SPS id: %d\n", ref);
> > +    sps                 = &ps->sps_list[ref];
> > +    new_extradata_size += sps->raw_size + 4;
> > +    ref                 = sps->ref;
> > +
> > +    if (ref >= HEVC_MAX_VPS_COUNT || !ps->vps_list[ref].raw_data) {
> > +        av_log(ctx, AV_LOG_ERROR, "Invalid VPS: %d\n", ref);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    av_log(ctx, AV_LOG_TRACE, "Writing VPS id: %d\n", ref);
> > +    vps                 = &ps->vps_list[ref];
> > +    new_extradata_size += vps->raw_size + 4;
> > +
> > +    if (ps->sei.raw_data)
> > +        new_extradata_size += ps->sei.raw_size; /*+4 not needed because it's already in annexb*/
> > +
> > +    ret = av_grow_packet(pkt_out, new_extradata_size);
> > +    if (ret < 0)
> > +        return ret;
> > +
> > +    WRITE_NAL(pkt_out->data, *prev_size, vps->raw_data, vps->raw_size);
> > +    WRITE_NAL(pkt_out->data, *prev_size, sps->raw_data, sps->raw_size);
> > +    WRITE_NAL(pkt_out->data, *prev_size, pps->raw_data, pps->raw_size);
> > +    pps->is_signalled = 1;
> > +
> > +    if (ps->sei.raw_data) {
> > +        memcpy(pkt_out->data + *prev_size, ps->sei.raw_data, ps->sei.raw_size);
> > +        *prev_size += ps->sei.raw_size;
> >      }
> >  
> > +    av_assert1(*prev_size == pkt_out->size);
> >      return 0;
> >  }
> >  
> > +/*
> > + *Function converts mp4 access unit into annexb
> > + *Output packet structure
> > + *VPS, SPS, PPS, [SEI(from extradata)], [SEI_PREFIX(from access unit)], IRAP, [SEI_SUFFIX]
> > + *or
> > + *[SEI_PREFIX (from access unit)], [PPS (if not already signalled)], VCL(non-irap), [SEI_SUFFIX]
> > + */
> >  static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
> >  {
> >      HEVCBSFContext *s = ctx->priv_data;
> >      AVPacket *in;
> > -    GetByteContext gb;
> > -
> > -    int got_irap = 0;
> > -    int i, ret = 0;
> > +    H2645Packet pkt;
> > +    int i, j, prev_size, ret;
> > +    int got_irap;
> >  
> >      ret = ff_bsf_get_packet(ctx, &in);
> >      if (ret < 0)
> >          return ret;
> >  
> > +    /*output the annexb nalu if extradata is not parsed*/
> >      if (!s->extradata_parsed) {
> >          av_packet_move_ref(out, in);
> >          av_packet_free(&in);
> >          return 0;
> >      }
> >  
> > -    bytestream2_init(&gb, in->data, in->size);
> > +    memset(&pkt, 0, sizeof(H2645Packet));
> > +    ret = ff_h2645_packet_split(&pkt, in->data, in->size, ctx, 1, s->length_size, AV_CODEC_ID_HEVC, 1, 0);
> > +    if (ret < 0)
> > +        goto done;
> >  
> > -    while (bytestream2_get_bytes_left(&gb)) {
> > -        uint32_t nalu_size = 0;
> > -        int      nalu_type;
> > -        int is_irap, add_extradata, extra_size, prev_size;
> > +    got_irap = 1; /*1 means that there is no irap or the irap has already been processed.*/
> > +    for (i = 0; i < pkt.nb_nals; i++) {
> > +        if (IS_IRAP(&pkt.nals[i])) {
> > +            got_irap = 0;
> > +            break;
> > +        }
> > +    }
> >  
> > -        for (i = 0; i < s->length_size; i++)
> > -            nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
> > +    prev_size = out->size; /*prev_size stores the current length of output packet */
> > +    av_assert1(prev_size == 0);
> >  
> > -        nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
> > +    for (i = 0; i < pkt.nb_nals; i++) {
> > +        H2645NAL *nal = &pkt.nals[i];
> >  
> > -        /* prepend extradata to IRAP frames */
> > -        is_irap       = nalu_type >= 16 && nalu_type <= 23;
> > -        add_extradata = is_irap && !got_irap;
> > -        extra_size    = add_extradata * ctx->par_out->extradata_size;
> > -        got_irap     |= is_irap;
> > +        if (IS_PARAMSET(nal)) {
> > +            ret = update_paramset(ctx, nal);
> > +            if (ret < 0)
> > +                goto done;
> > +            continue;
> > +        }
> >  
> > -        if (SIZE_MAX - nalu_size < 4 ||
> > -            SIZE_MAX - 4 - nalu_size < extra_size) {
> > -            ret = AVERROR_INVALIDDATA;
> > -            goto fail;
> > +        if (!got_irap && IS_IRAP(nal)) { /*append extradata and sei before first irap*/
> > +
> > +            /*reset the pps signalled flag in this cvs*/
> > +            for (j = 0; j < HEVC_MAX_PPS_COUNT; j++)
> > +                s->ps.pps_list[j].is_signalled = 0;
> > +
> > +            ret = write_extradata(ctx, out, &prev_size, nal);
> > +            if (ret < 0)
> > +                goto done;
> > +
> > +            /*append any past SEI prefix nals*/
> > +            for (j = 0; j < i; j++) {
> > +                H2645NAL *nal_past = &pkt.nals[j];
> > +                if (nal_past->type == HEVC_NAL_SEI_PREFIX) {
> > +                    ret = av_grow_packet(out, nal_past->raw_size + 4);
> > +                    if (ret < 0)
> > +                        goto done;
> > +                    WRITE_NAL(out->data, prev_size, nal_past->raw_data, nal_past->raw_size);
> > +                }
> > +            }
> > +
> > +            /*write irap nal unit*/
> > +            ret = write_vcl(ctx, out, &prev_size, nal);
> > +            if (ret < 0)
> > +                goto done;
> > +            got_irap = 1;
> > +            continue;
> >          }
> >  
> > -        prev_size = out->size;
> > +        /*do not write any sei prefix nal units if irap exists but has not been
> > +         * processed*/
> > +        if (nal->type == HEVC_NAL_SEI_PREFIX && !got_irap)
> > +            continue;
> >  
> > -        ret = av_grow_packet(out, 4 + nalu_size + extra_size);
> > -        if (ret < 0)
> > -            goto fail;
> > +        if (IS_VCL(nal)) {
> > +            ret = write_vcl(ctx, out, &prev_size, nal);
> > +            if (ret < 0)
> > +                goto done;
> > +            continue;
> > +        }
> >  
> > -        if (add_extradata)
> > -            memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
> > -        AV_WB32(out->data + prev_size + extra_size, 1);
> > -        bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size);
> > +        /*copy any other nal units to the output - i.e. SEI_SUFFIX*/
> > +        ret = av_grow_packet(out, nal->raw_size + 4);
> > +        if (ret < 0)
> > +            goto done;
> > +        WRITE_NAL(out->data, prev_size, nal->raw_data, nal->raw_size);
> >      }
> >  
> > +    av_assert1(prev_size == out->size);
> > +
> >      ret = av_packet_copy_props(out, in);
> > -    if (ret < 0)
> > -        goto fail;
> >  
> > -fail:
> > +done:
> > +    ff_h2645_packet_uninit(&pkt);
> > +
> >      if (ret < 0)
> >          av_packet_unref(out);
> >      av_packet_free(&in);
> > @@ -190,6 +611,7 @@ const AVBitStreamFilter ff_hevc_mp4toannexb_bsf = {
> >      .name           = "hevc_mp4toannexb",
> >      .priv_data_size = sizeof(HEVCBSFContext),
> >      .init           = hevc_mp4toannexb_init,
> > +    .close          = hevc_mp4toannexb_close,
> >      .filter         = hevc_mp4toannexb_filter,
> >      .codec_ids      = codec_ids,
> >  };
> > diff --git a/tests/fate/hevc.mak b/tests/fate/hevc.mak
> > index 559c3898bc..4f812b0834 100644
> > --- a/tests/fate/hevc.mak
> > +++ b/tests/fate/hevc.mak
> > @@ -238,7 +238,7 @@ FATE_HEVC-$(call ALLYES, HEVC_DEMUXER MOV_DEMUXER HEVC_MP4TOANNEXB_BSF MOV_MUXER
> >  fate-hevc-bsf-mp4toannexb: tests/data/hevc-mp4.mov
> >  fate-hevc-bsf-mp4toannexb: CMD = md5 -i $(TARGET_PATH)/tests/data/hevc-mp4.mov -c:v copy -fflags +bitexact -f hevc
> >  fate-hevc-bsf-mp4toannexb: CMP = oneline
> > -fate-hevc-bsf-mp4toannexb: REF = 1873662a3af1848c37e4eb25722c8df9
> > +fate-hevc-bsf-mp4toannexb: REF = 3c9d998a3aa2b9e0fb1c1f434952bf8b
> >  
> >  fate-hevc-skiploopfilter: CMD = framemd5 -skip_loop_filter nokey -i $(TARGET_SAMPLES)/hevc-conformance/SAO_D_Samsung_5.bit -sws_flags bitexact
> >  FATE_HEVC += fate-hevc-skiploopfilter
> > -- 
> > 2.23.0
> > 
> 
> ping

ping
Andreas Rheinhardt Oct. 8, 2019, 10:22 a.m. UTC | #3
Andriy Gelman:
> From: Andriy Gelman <andriy.gelman@gmail.com>
> 
> Fixes #7799
> 
> Currently, the mp4toannexb filter always inserts the same extradata at
> the start of the first IRAP unit. As in ticket #7799, this can lead to
> decoding errors if modified parameter sets are signalled in-band.
> 
> This commit keeps track of the VPS/SPS/PPS parameter sets during the
> conversion. The correct combination is inserted at the start of the
> first IRAP. SEIs from extradata are inserted before each IRAP.
> 
> This commit also makes an update to the hevc-bsf-mp4toannexb fate test
> since the result before this patch contained duplicate parameter sets
> in-band.
> ---
>  libavcodec/hevc_mp4toannexb_bsf.c | 488 ++++++++++++++++++++++++++++--
>  tests/fate/hevc.mak               |   2 +-
>  2 files changed, 456 insertions(+), 34 deletions(-)
> 
> diff --git a/libavcodec/hevc_mp4toannexb_bsf.c b/libavcodec/hevc_mp4toannexb_bsf.c
> index 09bce5b34c..90a4d17d25 100644
> --- a/libavcodec/hevc_mp4toannexb_bsf.c
> +++ b/libavcodec/hevc_mp4toannexb_bsf.c
> @@ -23,19 +23,209 @@
>  
>  #include "libavutil/intreadwrite.h"
>  #include "libavutil/mem.h"
> +#include "libavutil/avassert.h"
>  
>  #include "avcodec.h"
>  #include "bsf.h"
>  #include "bytestream.h"
>  #include "hevc.h"
> +#include "h2645_parse.h"
> +#include "hevc_ps.h"
> +#include "golomb.h"
>  
>  #define MIN_HEVCC_LENGTH 23
> +#define PROFILE_WITHOUT_IDC_BITS    88
> +#define IS_IRAP(s)                  ((s)->type >= 16 && (s)->type <= 23)
> +#define IS_PARAMSET(s)              ((s)->type >= 32 && (s)->type <= 34)
> +
> +                                    /*reserved VCLs not included*/
> +#define IS_VCL(s)                   ((s)->type <= 9 || ((s)->type >= 16 && (s)->type <= 21))
> +#define HEVC_NAL_HEADER_BITS        16
> +
> +/*
> + *Copies data from input buffer to output buffer. Appends annexb startcode.
> + *out must be allocated at least out_len + in_len + 4.
> + */

A space between * and the actual comment is both common and IMO more
readable. This goes for all comments.

> +#define WRITE_NAL(out, out_len, in, in_len) do {                        \
> +    AV_WB32((out) + (out_len), 1);                                      \
> +    (out_len) += 4;                                                     \
> +    memcpy((out) + (out_len), (in), (in_len));                          \
> +    (out_len) += (in_len);                                              \
> +} while (0)
> +
> +typedef struct Param {
> +    uint8_t *raw_data;          /*raw data to store param set payload*/
> +    int      raw_size;          /*size of raw_data*/
> +    size_t   allocated_size;    /*allocated size of raw_data*/
> +    int      ref;               /*stores the ref of the higher level parameter set*/
> +    int      is_signalled;      /*indicates whether this param has already been signalled in the cvs*/
> +} Param;
> +
> +/*modified version of HEVCParamSets to store bytestream and reference to previous level*/
> +typedef struct ParamSets {
> +    Param vps_list[HEVC_MAX_VPS_COUNT];
> +    Param sps_list[HEVC_MAX_SPS_COUNT];
> +    Param pps_list[HEVC_MAX_PPS_COUNT];
> +
> +    Param sei; /*cached SEIs from extradata in annexb format*/
> +} ParamSets;
>  
>  typedef struct HEVCBSFContext {
>      uint8_t  length_size;
>      int      extradata_parsed;
> +    ParamSets ps; /*cached VPS/SPS/PPS parameter sets + SEI from extradata*/
>  } HEVCBSFContext;
>  
> +static int update_cached_paramset(AVBSFContext *ctx, Param *cached_param,
> +                                  H2645NAL *nal, int ref)
> +{
> +    int ret;
> +
> +    if (cached_param->raw_data && cached_param->raw_size == nal->raw_size &&
> +        !memcmp(cached_param->raw_data, nal->raw_data, nal->raw_size)) {
> +        av_log(ctx, AV_LOG_DEBUG, "NAL unit: %d. Copy already exists in parameter set.\n", nal->type);
> +    } else {
> +        if (nal->raw_size > cached_param->allocated_size) {
> +            ret = av_reallocp(&cached_param->raw_data, nal->raw_size);
> +            if (ret < 0)
> +                return ret;
> +            cached_param->allocated_size = nal->raw_size;
> +        }
> +        memcpy(cached_param->raw_data, nal->raw_data, nal->raw_size);

You already include the start code in the SEI. Wouldn't it make sense
to do the same for parameter sets? Copying would then amount to one
big memcpy. The only complication I see is that you would have to add
a parameter to WRITE_NAL that determines whether to write a start code
or not.

> +        cached_param->raw_size     = nal->raw_size;
> +        cached_param->ref          = ref;
> +        cached_param->is_signalled = 0;
> +    }
> +    return 0;
> +}
> +
> +static int parse_vps(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    int vps_id, ret;
> +
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +
> +    GetBitContext *gb = &nal->gb;
> +    gb->index         = HEVC_NAL_HEADER_BITS;

One usually does not directly modify the properties of a
GetBitContext; instead one uses the functions provided for them,
namely skip_bits in this case. Where do you actually check that the
various parameter sets are actually long enough?

> +
> +    vps_id = get_bits(gb, 4);
> +
> +    av_log(ctx, AV_LOG_TRACE, "Updating VPS id: %d\n", vps_id);
> +    ret = update_cached_paramset(ctx, &ps->vps_list[vps_id], nal, 0);
> +    return ret;
> +}
> +
> +static int parse_sps(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    int i, ret;
> +    int sps_id, vps_ref, max_sub_layers_minus1;
> +
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +
> +    uint8_t sub_layer_profile_present_flag[HEVC_MAX_SUB_LAYERS];
> +    uint8_t sub_layer_level_present_flag[HEVC_MAX_SUB_LAYERS];
> +
> +    GetBitContext *gb = &nal->gb;
> +    gb->index         = HEVC_NAL_HEADER_BITS;
> +
> +    vps_ref = get_bits(gb, 4);
> +
> +    max_sub_layers_minus1 = get_bits(gb, 3);
> +    skip_bits1(gb);                          /*sps_temporal_id_nesting_flag*/
> +    skip_bits(gb, PROFILE_WITHOUT_IDC_BITS); /*profile_tier_level*/
> +    skip_bits(gb, 8);                        /*general_level_idc*/
> +
> +    for (i = 0; i < max_sub_layers_minus1; i++) {
> +        sub_layer_profile_present_flag[i] = get_bits1(gb);
> +        sub_layer_level_present_flag[i]   = get_bits1(gb);
> +    }
> +
> +    if (max_sub_layers_minus1 > 0)
> +        for (i = max_sub_layers_minus1; i < 8; i++)
> +            skip_bits(gb, 2); /*reserved_zero_2bits[i]*/
> +
> +    for (i = 0; i < max_sub_layers_minus1; i++) {
> +        if (sub_layer_profile_present_flag[i])
> +            skip_bits(gb, PROFILE_WITHOUT_IDC_BITS); /*profile_tier_level*/
> +        if (sub_layer_level_present_flag[i])
> +            skip_bits(gb, 8);                        /*sub_layer_level_idc*/
> +    }
> +
> +    /*we only need the sps_id index*/
> +    sps_id = get_ue_golomb_long(gb);

This function is inherently unsafe: If the first 32 bits read are all
zero, then the function skips 31 bits and returns the value read from
the next 32 bits - 1. This value can be in the range
0..HEVC_MAX_SPS_COUNT. get_ue_golomb should satisfy all our needs
(only the av_log(NULL is bad, but never mind.)

> +    if (sps_id < 0 ||  sps_id >= HEVC_MAX_SPS_COUNT) {
> +        av_log(ctx, AV_LOG_ERROR, "SPS id out of range: %d\n", sps_id);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    av_log(ctx, AV_LOG_TRACE, "Updating SPS id: %d, VPS ref: %d\n", sps_id, vps_ref);
> +    ret = update_cached_paramset(ctx, &ps->sps_list[sps_id], nal, vps_ref);
> +    return ret;
> +}
> +
> +static int parse_pps(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    int ret;
> +    int pps_id, sps_ref;
> +
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +
> +    GetBitContext *gb = &nal->gb;
> +    gb->index         = HEVC_NAL_HEADER_BITS;
> +
> +    pps_id = get_ue_golomb_long(gb);
> +    if (pps_id < 0 || pps_id >= HEVC_MAX_PPS_COUNT) {
> +        av_log(ctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    sps_ref = get_ue_golomb_long(gb);
> +    if (sps_ref < 0 || sps_ref >= HEVC_MAX_SPS_COUNT) {
> +        av_log(ctx, AV_LOG_ERROR, "SPS id out of range: %d\n", sps_ref);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    av_log(ctx, AV_LOG_TRACE, "Updating PPS id: %d, SPS ref: %d\n", pps_id, sps_ref);
> +    ret = update_cached_paramset(ctx, &ps->pps_list[pps_id], nal, sps_ref);
> +    return ret;
> +}
> +
> +static int append_sei_annexb(Param *sei, H2645NAL *nal)
> +{
> +    int ret;
> +
> +    ret = av_reallocp(&sei->raw_data, sei->allocated_size + nal->raw_size + 4);
> +    if (ret < 0)
> +        return ret;
> +    sei->allocated_size += nal->raw_size + 4;
> +
> +    WRITE_NAL(sei->raw_data, sei->raw_size, nal->raw_data, nal->raw_size);
> +    av_assert1(sei->raw_size == sei->allocated_size);
> +    return 0;
> +}
> +
> +static int update_paramset(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    int ret;
> +    switch (nal->type) {
> +        case (HEVC_NAL_VPS):
> +            if ((ret = parse_vps(ctx, nal)) < 0)
> +                return ret;
> +            break;
> +        case (HEVC_NAL_SPS):
> +            if ((ret = parse_sps(ctx, nal)) < 0)
> +                return ret;
> +            break;
> +        case (HEVC_NAL_PPS):
> +            if ((ret = parse_pps(ctx, nal)) < 0)
> +                return ret;
> +    }
> +    return 0;
> +}
> +
>  static int hevc_extradata_to_annexb(AVBSFContext *ctx)
>  {
>      GetByteContext gb;
> @@ -97,84 +287,315 @@ fail:
>  static int hevc_mp4toannexb_init(AVBSFContext *ctx)
>  {
>      HEVCBSFContext *s = ctx->priv_data;
> -    int ret;
> +    H2645Packet pkt;
> +    int i, ret;
>  
>      if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH ||
>          AV_RB24(ctx->par_in->extradata) == 1           ||
>          AV_RB32(ctx->par_in->extradata) == 1) {
>          av_log(ctx, AV_LOG_VERBOSE,
>                 "The input looks like it is Annex B already\n");
> +        return 0;
>      } else {
>          ret = hevc_extradata_to_annexb(ctx);
>          if (ret < 0)
>              return ret;
>          s->length_size      = ret;
>          s->extradata_parsed = 1;
> +
> +        memset(&pkt, 0, sizeof(H2645Packet));

It would be better if you put the H2645Packet in this scope and
initialized it via = { 0 }.

> +        ret = ff_h2645_packet_split(&pkt, ctx->par_out->extradata, ctx->par_out->extradata_size,
> +                                     ctx, 0, 0, AV_CODEC_ID_HEVC, 1, 0);
> +        if (ret < 0)
> +            goto done;
> +
> +        for (i = 0; i < pkt.nb_nals; ++i) {

And the natural scope for i is this loop: for (int i = 0

> +            H2645NAL *nal = &pkt.nals[i];
> +
> +            /*current segmentation algorithm includes next 0x00 from next nal unit*/
> +            if (nal->raw_data[nal->raw_size - 1] == 0x00)
> +                nal->raw_size--;
> +
> +            if (IS_PARAMSET(nal)) {
> +                ret = update_paramset(ctx, nal);
> +                if (ret < 0)
> +                    goto done;
> +                continue;
> +            }
> +
> +            if (nal->type == HEVC_NAL_SEI_PREFIX || nal->type == HEVC_NAL_SEI_SUFFIX) {
> +                ret = append_sei_annexb(&s->ps.sei, nal);
> +                if (ret < 0)
> +                    goto done;
> +            }
> +        }
> +    }
> +done:
> +    ff_h2645_packet_uninit(&pkt);
> +    return ret;
> +}
> +
> +static void ps_uninit(ParamSets *ps)
> +{
> +    int i;
> +    for (i = 0; i < FF_ARRAY_ELEMS(ps->vps_list); i++) {
> +       av_freep(&ps->vps_list[i].raw_data);
> +       ps->vps_list[i].allocated_size = 0;
> +       ps->vps_list[i].raw_size       = 0;
> +    }
> +    for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) {
> +       av_freep(&ps->sps_list[i].raw_data);
> +       ps->sps_list[i].allocated_size = 0;
> +       ps->sps_list[i].raw_size       = 0;
> +    }
> +    for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) {
> +       av_freep(&ps->pps_list[i].raw_data);
> +       ps->pps_list[i].allocated_size = 0;
> +       ps->pps_list[i].raw_size       = 0;
> +    }
> +    av_freep(&ps->sei);
> +    ps->sei.allocated_size = 0;
> +    ps->sei.raw_size       = 0;
> +}
> +
> +static void hevc_mp4toannexb_close(AVBSFContext *ctx)
> +{
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ps_uninit(&s->ps);
> +}
> +
> +static int get_vcl_pps_id(AVBSFContext *ctx, H2645NAL *nal)
> +{
> +    GetBitContext *gb = &nal->gb;
> +    int pps_id;
> +
> +    gb->index = HEVC_NAL_HEADER_BITS;
> +
> +    skip_bits1(gb);        /*first_slice_segment_in_pic_flag*/
> +
> +    if (IS_IRAP(nal))
> +        skip_bits1(gb);    /*no_output_of_prior_pics_flag*/
> +
> +    pps_id = get_ue_golomb_long(gb);
> +    if (pps_id < 0 || pps_id >= HEVC_MAX_PPS_COUNT) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS id: %d\n", pps_id);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    return pps_id;
> +}
> +
> +static int write_vcl(AVBSFContext *ctx, AVPacket *pkt_out, int *prev_size,
> +                     H2645NAL *nal)
> +{
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +    int ret, pps_id;
> +    Param *pps;
> +
> +    /*get frame pps*/
> +    pps_id = get_vcl_pps_id(ctx, nal);
> +    if (pps_id < 0)
> +        return AVERROR_INVALIDDATA;
> +    av_log(ctx, AV_LOG_TRACE, "VCL PPS id: %d\n", pps_id);
> +
> +    /*write pps if not signalled*/
> +    pps = &ps->pps_list[pps_id];
> +    if (!pps->raw_data) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS: %d doesn't exist\n", pps_id);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    if (!pps->is_signalled) {
> +        av_log(ctx, AV_LOG_TRACE, "Referenced PPS: %d has not been signalled. Writing PPS to output stream\n", pps_id);
> +        ret = av_grow_packet(pkt_out, pps->raw_size + 4);
> +        if (ret < 0)
> +            return ret;
> +        WRITE_NAL(pkt_out->data, *prev_size, pps->raw_data, pps->raw_size);
> +        pps->is_signalled = 1;
> +    }
> +
> +    /*write actual packet*/
> +    ret = av_grow_packet(pkt_out, nal->raw_size + 4);
> +    if (ret < 0)
> +        return ret;
> +    WRITE_NAL(pkt_out->data, *prev_size, nal->raw_data, nal->raw_size);
> +
> +    return 0;
> +}
> +
> +static int write_extradata(AVBSFContext *ctx, AVPacket *pkt_out, int *prev_size,
> +                           H2645NAL *nal_irap)
> +{
> +    int ref, ret;
> +    int new_extradata_size = 0;
> +
> +    HEVCBSFContext *s = ctx->priv_data;
> +    ParamSets *ps     = &s->ps;
> +    GetBitContext *gb = &nal_irap->gb;
> +
> +    /*active parameter sets for the new irap*/
> +    const Param *vps;
> +    const Param *sps;
> +    Param *pps; /*non-const because we have to update flag that pps_id is signalled in cvs*/
> +
> +    skip_bits1(gb); /*first_slice_ic_pic_flag*/
> +    skip_bits1(gb); /*no_output_of_prior_pics_flag*/
> +
> +    ref = get_ue_golomb_long(gb);
> +    if (ref < 0 || ref >= HEVC_MAX_PPS_COUNT || !ps->pps_list[ref].raw_data) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid PPS: %d\n", ref);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    av_log(ctx, AV_LOG_TRACE, "Writing PPS id: %d\n", ref);
> +    pps                 = &ps->pps_list[ref];
> +    new_extradata_size += pps->raw_size + 4;
> +    ref                 = pps->ref;
> +
> +    if (ref >= HEVC_MAX_SPS_COUNT || !ps->sps_list[ref].raw_data) {

The first of these checks is unnecessary, as it is already checked
when parsing a PPS (that's also where this should be checked). Same
applies below.

> +        av_log(ctx, AV_LOG_ERROR, "Invalid SPS: %d\n", ref);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    av_log(ctx, AV_LOG_TRACE, "Writing SPS id: %d\n", ref);
> +    sps                 = &ps->sps_list[ref];
> +    new_extradata_size += sps->raw_size + 4;
> +    ref                 = sps->ref;
> +
> +    if (ref >= HEVC_MAX_VPS_COUNT || !ps->vps_list[ref].raw_data) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid VPS: %d\n", ref);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    av_log(ctx, AV_LOG_TRACE, "Writing VPS id: %d\n", ref);
> +    vps                 = &ps->vps_list[ref];
> +    new_extradata_size += vps->raw_size + 4;
> +
> +    if (ps->sei.raw_data)
> +        new_extradata_size += ps->sei.raw_size; /*+4 not needed because it's already in annexb*/

How about shortening the comment to: "/* no +4: it's already annexb
*/"? That line is already very long.

> +
> +    ret = av_grow_packet(pkt_out, new_extradata_size);
> +    if (ret < 0)
> +        return ret;
> +
> +    WRITE_NAL(pkt_out->data, *prev_size, vps->raw_data, vps->raw_size);
> +    WRITE_NAL(pkt_out->data, *prev_size, sps->raw_data, sps->raw_size);
> +    WRITE_NAL(pkt_out->data, *prev_size, pps->raw_data, pps->raw_size);
> +    pps->is_signalled = 1;
> +
> +    if (ps->sei.raw_data) {
> +        memcpy(pkt_out->data + *prev_size, ps->sei.raw_data, ps->sei.raw_size);
> +        *prev_size += ps->sei.raw_size;
>      }
>  
> +    av_assert1(*prev_size == pkt_out->size);
>      return 0;
>  }
>  
> +/*
> + *Function converts mp4 access unit into annexb
> + *Output packet structure
> + *VPS, SPS, PPS, [SEI(from extradata)], [SEI_PREFIX(from access unit)], IRAP, [SEI_SUFFIX]
> + *or
> + *[SEI_PREFIX (from access unit)], [PPS (if not already signalled)], VCL(non-irap), [SEI_SUFFIX]
> + */
>  static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
>  {
>      HEVCBSFContext *s = ctx->priv_data;
>      AVPacket *in;
> -    GetByteContext gb;
> -
> -    int got_irap = 0;
> -    int i, ret = 0;
> +    H2645Packet pkt;
> +    int i, j, prev_size, ret;
> +    int got_irap;
>  
>      ret = ff_bsf_get_packet(ctx, &in);
>      if (ret < 0)
>          return ret;
>  
> +    /*output the annexb nalu if extradata is not parsed*/
>      if (!s->extradata_parsed) {
>          av_packet_move_ref(out, in);
>          av_packet_free(&in);
>          return 0;
>      }
>  
> -    bytestream2_init(&gb, in->data, in->size);
> +    memset(&pkt, 0, sizeof(H2645Packet));
> +    ret = ff_h2645_packet_split(&pkt, in->data, in->size, ctx, 1, s->length_size, AV_CODEC_ID_HEVC, 1, 0);
> +    if (ret < 0)
> +        goto done;
>  
> -    while (bytestream2_get_bytes_left(&gb)) {
> -        uint32_t nalu_size = 0;
> -        int      nalu_type;
> -        int is_irap, add_extradata, extra_size, prev_size;
> +    got_irap = 1; /*1 means that there is no irap or the irap has already been processed.*/

Not very intuitive. How about irap_done or so?

> +    for (i = 0; i < pkt.nb_nals; i++) {
> +        if (IS_IRAP(&pkt.nals[i])) {
> +            got_irap = 0;
> +            break;
> +        }
> +    }
>  
> -        for (i = 0; i < s->length_size; i++)
> -            nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
> +    prev_size = out->size; /*prev_size stores the current length of output packet */
> +    av_assert1(prev_size == 0);
>  
> -        nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
> +    for (i = 0; i < pkt.nb_nals; i++) {
> +        H2645NAL *nal = &pkt.nals[i];
>  
> -        /* prepend extradata to IRAP frames */
> -        is_irap       = nalu_type >= 16 && nalu_type <= 23;
> -        add_extradata = is_irap && !got_irap;
> -        extra_size    = add_extradata * ctx->par_out->extradata_size;
> -        got_irap     |= is_irap;
> +        if (IS_PARAMSET(nal)) {
> +            ret = update_paramset(ctx, nal);
> +            if (ret < 0)
> +                goto done;
> +            continue;
> +        }
>  
> -        if (SIZE_MAX - nalu_size < 4 ||
> -            SIZE_MAX - 4 - nalu_size < extra_size) {
> -            ret = AVERROR_INVALIDDATA;
> -            goto fail;
> +        if (!got_irap && IS_IRAP(nal)) { /*append extradata and sei before first irap*/
> +
> +            /*reset the pps signalled flag in this cvs*/
> +            for (j = 0; j < HEVC_MAX_PPS_COUNT; j++)
> +                s->ps.pps_list[j].is_signalled = 0;
> +
> +            ret = write_extradata(ctx, out, &prev_size, nal);
> +            if (ret < 0)
> +                goto done;
> +
> +            /*append any past SEI prefix nals*/
> +            for (j = 0; j < i; j++) {
> +                H2645NAL *nal_past = &pkt.nals[j];
> +                if (nal_past->type == HEVC_NAL_SEI_PREFIX) {
> +                    ret = av_grow_packet(out, nal_past->raw_size + 4);
> +                    if (ret < 0)
> +                        goto done;
> +                    WRITE_NAL(out->data, prev_size, nal_past->raw_data, nal_past->raw_size);
> +                }
> +            }
> +
> +            /*write irap nal unit*/
> +            ret = write_vcl(ctx, out, &prev_size, nal);
> +            if (ret < 0)
> +                goto done;
> +            got_irap = 1;
> +            continue;
>          }
>  
> -        prev_size = out->size;
> +        /*do not write any sei prefix nal units if irap exists but has not been
> +         * processed*/
> +        if (nal->type == HEVC_NAL_SEI_PREFIX && !got_irap)
> +            continue;
>  
> -        ret = av_grow_packet(out, 4 + nalu_size + extra_size);
> -        if (ret < 0)
> -            goto fail;
> +        if (IS_VCL(nal)) {
> +            ret = write_vcl(ctx, out, &prev_size, nal);
> +            if (ret < 0)
> +                goto done;
> +            continue;
> +        }
>  
> -        if (add_extradata)
> -            memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
> -        AV_WB32(out->data + prev_size + extra_size, 1);
> -        bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size);
> +        /*copy any other nal units to the output - i.e. SEI_SUFFIX*/
> +        ret = av_grow_packet(out, nal->raw_size + 4);
> +        if (ret < 0)
> +            goto done;
> +        WRITE_NAL(out->data, prev_size, nal->raw_data, nal->raw_size);
>      }
>  
> +    av_assert1(prev_size == out->size);
> +
>      ret = av_packet_copy_props(out, in);
> -    if (ret < 0)
> -        goto fail;
>  
> -fail:
> +done:
> +    ff_h2645_packet_uninit(&pkt);
> +
>      if (ret < 0)
>          av_packet_unref(out);
>      av_packet_free(&in);
> @@ -190,6 +611,7 @@ const AVBitStreamFilter ff_hevc_mp4toannexb_bsf = {
>      .name           = "hevc_mp4toannexb",
>      .priv_data_size = sizeof(HEVCBSFContext),
>      .init           = hevc_mp4toannexb_init,
> +    .close          = hevc_mp4toannexb_close,
>      .filter         = hevc_mp4toannexb_filter,
>      .codec_ids      = codec_ids,
>  };
> diff --git a/tests/fate/hevc.mak b/tests/fate/hevc.mak
> index 559c3898bc..4f812b0834 100644
> --- a/tests/fate/hevc.mak
> +++ b/tests/fate/hevc.mak
> @@ -238,7 +238,7 @@ FATE_HEVC-$(call ALLYES, HEVC_DEMUXER MOV_DEMUXER HEVC_MP4TOANNEXB_BSF MOV_MUXER
>  fate-hevc-bsf-mp4toannexb: tests/data/hevc-mp4.mov
>  fate-hevc-bsf-mp4toannexb: CMD = md5 -i $(TARGET_PATH)/tests/data/hevc-mp4.mov -c:v copy -fflags +bitexact -f hevc
>  fate-hevc-bsf-mp4toannexb: CMP = oneline
> -fate-hevc-bsf-mp4toannexb: REF = 1873662a3af1848c37e4eb25722c8df9
> +fate-hevc-bsf-mp4toannexb: REF = 3c9d998a3aa2b9e0fb1c1f434952bf8b
>  
>  fate-hevc-skiploopfilter: CMD = framemd5 -skip_loop_filter nokey -i $(TARGET_SAMPLES)/hevc-conformance/SAO_D_Samsung_5.bit -sws_flags bitexact
>  FATE_HEVC += fate-hevc-skiploopfilter
> 

I am not completely done with the review, but you definitely deserve
an answer. Sorry for letting you wait so long.

- Andreas
diff mbox

Patch

diff --git a/libavcodec/hevc_mp4toannexb_bsf.c b/libavcodec/hevc_mp4toannexb_bsf.c
index 09bce5b34c..90a4d17d25 100644
--- a/libavcodec/hevc_mp4toannexb_bsf.c
+++ b/libavcodec/hevc_mp4toannexb_bsf.c
@@ -23,19 +23,209 @@ 
 
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
+#include "libavutil/avassert.h"
 
 #include "avcodec.h"
 #include "bsf.h"
 #include "bytestream.h"
 #include "hevc.h"
+#include "h2645_parse.h"
+#include "hevc_ps.h"
+#include "golomb.h"
 
 #define MIN_HEVCC_LENGTH 23
+#define PROFILE_WITHOUT_IDC_BITS    88
+#define IS_IRAP(s)                  ((s)->type >= 16 && (s)->type <= 23)
+#define IS_PARAMSET(s)              ((s)->type >= 32 && (s)->type <= 34)
+
+                                    /*reserved VCLs not included*/
+#define IS_VCL(s)                   ((s)->type <= 9 || ((s)->type >= 16 && (s)->type <= 21))
+#define HEVC_NAL_HEADER_BITS        16
+
+/*
+ *Copies data from input buffer to output buffer. Appends annexb startcode.
+ *out must be allocated at least out_len + in_len + 4.
+ */
+#define WRITE_NAL(out, out_len, in, in_len) do {                        \
+    AV_WB32((out) + (out_len), 1);                                      \
+    (out_len) += 4;                                                     \
+    memcpy((out) + (out_len), (in), (in_len));                          \
+    (out_len) += (in_len);                                              \
+} while (0)
+
+typedef struct Param {
+    uint8_t *raw_data;          /*raw data to store param set payload*/
+    int      raw_size;          /*size of raw_data*/
+    size_t   allocated_size;    /*allocated size of raw_data*/
+    int      ref;               /*stores the ref of the higher level parameter set*/
+    int      is_signalled;      /*indicates whether this param has already been signalled in the cvs*/
+} Param;
+
+/*modified version of HEVCParamSets to store bytestream and reference to previous level*/
+typedef struct ParamSets {
+    Param vps_list[HEVC_MAX_VPS_COUNT];
+    Param sps_list[HEVC_MAX_SPS_COUNT];
+    Param pps_list[HEVC_MAX_PPS_COUNT];
+
+    Param sei; /*cached SEIs from extradata in annexb format*/
+} ParamSets;
 
 typedef struct HEVCBSFContext {
     uint8_t  length_size;
     int      extradata_parsed;
+    ParamSets ps; /*cached VPS/SPS/PPS parameter sets + SEI from extradata*/
 } HEVCBSFContext;
 
+static int update_cached_paramset(AVBSFContext *ctx, Param *cached_param,
+                                  H2645NAL *nal, int ref)
+{
+    int ret;
+
+    if (cached_param->raw_data && cached_param->raw_size == nal->raw_size &&
+        !memcmp(cached_param->raw_data, nal->raw_data, nal->raw_size)) {
+        av_log(ctx, AV_LOG_DEBUG, "NAL unit: %d. Copy already exists in parameter set.\n", nal->type);
+    } else {
+        if (nal->raw_size > cached_param->allocated_size) {
+            ret = av_reallocp(&cached_param->raw_data, nal->raw_size);
+            if (ret < 0)
+                return ret;
+            cached_param->allocated_size = nal->raw_size;
+        }
+        memcpy(cached_param->raw_data, nal->raw_data, nal->raw_size);
+        cached_param->raw_size     = nal->raw_size;
+        cached_param->ref          = ref;
+        cached_param->is_signalled = 0;
+    }
+    return 0;
+}
+
+static int parse_vps(AVBSFContext *ctx, H2645NAL *nal)
+{
+    int vps_id, ret;
+
+    HEVCBSFContext *s = ctx->priv_data;
+    ParamSets *ps     = &s->ps;
+
+    GetBitContext *gb = &nal->gb;
+    gb->index         = HEVC_NAL_HEADER_BITS;
+
+    vps_id = get_bits(gb, 4);
+
+    av_log(ctx, AV_LOG_TRACE, "Updating VPS id: %d\n", vps_id);
+    ret = update_cached_paramset(ctx, &ps->vps_list[vps_id], nal, 0);
+    return ret;
+}
+
+static int parse_sps(AVBSFContext *ctx, H2645NAL *nal)
+{
+    int i, ret;
+    int sps_id, vps_ref, max_sub_layers_minus1;
+
+    HEVCBSFContext *s = ctx->priv_data;
+    ParamSets *ps     = &s->ps;
+
+    uint8_t sub_layer_profile_present_flag[HEVC_MAX_SUB_LAYERS];
+    uint8_t sub_layer_level_present_flag[HEVC_MAX_SUB_LAYERS];
+
+    GetBitContext *gb = &nal->gb;
+    gb->index         = HEVC_NAL_HEADER_BITS;
+
+    vps_ref = get_bits(gb, 4);
+
+    max_sub_layers_minus1 = get_bits(gb, 3);
+    skip_bits1(gb);                          /*sps_temporal_id_nesting_flag*/
+    skip_bits(gb, PROFILE_WITHOUT_IDC_BITS); /*profile_tier_level*/
+    skip_bits(gb, 8);                        /*general_level_idc*/
+
+    for (i = 0; i < max_sub_layers_minus1; i++) {
+        sub_layer_profile_present_flag[i] = get_bits1(gb);
+        sub_layer_level_present_flag[i]   = get_bits1(gb);
+    }
+
+    if (max_sub_layers_minus1 > 0)
+        for (i = max_sub_layers_minus1; i < 8; i++)
+            skip_bits(gb, 2); /*reserved_zero_2bits[i]*/
+
+    for (i = 0; i < max_sub_layers_minus1; i++) {
+        if (sub_layer_profile_present_flag[i])
+            skip_bits(gb, PROFILE_WITHOUT_IDC_BITS); /*profile_tier_level*/
+        if (sub_layer_level_present_flag[i])
+            skip_bits(gb, 8);                        /*sub_layer_level_idc*/
+    }
+
+    /*we only need the sps_id index*/
+    sps_id = get_ue_golomb_long(gb);
+    if (sps_id < 0 ||  sps_id >= HEVC_MAX_SPS_COUNT) {
+        av_log(ctx, AV_LOG_ERROR, "SPS id out of range: %d\n", sps_id);
+        return AVERROR_INVALIDDATA;
+    }
+
+    av_log(ctx, AV_LOG_TRACE, "Updating SPS id: %d, VPS ref: %d\n", sps_id, vps_ref);
+    ret = update_cached_paramset(ctx, &ps->sps_list[sps_id], nal, vps_ref);
+    return ret;
+}
+
+static int parse_pps(AVBSFContext *ctx, H2645NAL *nal)
+{
+    int ret;
+    int pps_id, sps_ref;
+
+    HEVCBSFContext *s = ctx->priv_data;
+    ParamSets *ps     = &s->ps;
+
+    GetBitContext *gb = &nal->gb;
+    gb->index         = HEVC_NAL_HEADER_BITS;
+
+    pps_id = get_ue_golomb_long(gb);
+    if (pps_id < 0 || pps_id >= HEVC_MAX_PPS_COUNT) {
+        av_log(ctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id);
+        return AVERROR_INVALIDDATA;
+    }
+
+    sps_ref = get_ue_golomb_long(gb);
+    if (sps_ref < 0 || sps_ref >= HEVC_MAX_SPS_COUNT) {
+        av_log(ctx, AV_LOG_ERROR, "SPS id out of range: %d\n", sps_ref);
+        return AVERROR_INVALIDDATA;
+    }
+
+    av_log(ctx, AV_LOG_TRACE, "Updating PPS id: %d, SPS ref: %d\n", pps_id, sps_ref);
+    ret = update_cached_paramset(ctx, &ps->pps_list[pps_id], nal, sps_ref);
+    return ret;
+}
+
+static int append_sei_annexb(Param *sei, H2645NAL *nal)
+{
+    int ret;
+
+    ret = av_reallocp(&sei->raw_data, sei->allocated_size + nal->raw_size + 4);
+    if (ret < 0)
+        return ret;
+    sei->allocated_size += nal->raw_size + 4;
+
+    WRITE_NAL(sei->raw_data, sei->raw_size, nal->raw_data, nal->raw_size);
+    av_assert1(sei->raw_size == sei->allocated_size);
+    return 0;
+}
+
+static int update_paramset(AVBSFContext *ctx, H2645NAL *nal)
+{
+    int ret;
+    switch (nal->type) {
+        case (HEVC_NAL_VPS):
+            if ((ret = parse_vps(ctx, nal)) < 0)
+                return ret;
+            break;
+        case (HEVC_NAL_SPS):
+            if ((ret = parse_sps(ctx, nal)) < 0)
+                return ret;
+            break;
+        case (HEVC_NAL_PPS):
+            if ((ret = parse_pps(ctx, nal)) < 0)
+                return ret;
+    }
+    return 0;
+}
+
 static int hevc_extradata_to_annexb(AVBSFContext *ctx)
 {
     GetByteContext gb;
@@ -97,84 +287,315 @@  fail:
 static int hevc_mp4toannexb_init(AVBSFContext *ctx)
 {
     HEVCBSFContext *s = ctx->priv_data;
-    int ret;
+    H2645Packet pkt;
+    int i, ret;
 
     if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH ||
         AV_RB24(ctx->par_in->extradata) == 1           ||
         AV_RB32(ctx->par_in->extradata) == 1) {
         av_log(ctx, AV_LOG_VERBOSE,
                "The input looks like it is Annex B already\n");
+        return 0;
     } else {
         ret = hevc_extradata_to_annexb(ctx);
         if (ret < 0)
             return ret;
         s->length_size      = ret;
         s->extradata_parsed = 1;
+
+        memset(&pkt, 0, sizeof(H2645Packet));
+        ret = ff_h2645_packet_split(&pkt, ctx->par_out->extradata, ctx->par_out->extradata_size,
+                                     ctx, 0, 0, AV_CODEC_ID_HEVC, 1, 0);
+        if (ret < 0)
+            goto done;
+
+        for (i = 0; i < pkt.nb_nals; ++i) {
+            H2645NAL *nal = &pkt.nals[i];
+
+            /*current segmentation algorithm includes next 0x00 from next nal unit*/
+            if (nal->raw_data[nal->raw_size - 1] == 0x00)
+                nal->raw_size--;
+
+            if (IS_PARAMSET(nal)) {
+                ret = update_paramset(ctx, nal);
+                if (ret < 0)
+                    goto done;
+                continue;
+            }
+
+            if (nal->type == HEVC_NAL_SEI_PREFIX || nal->type == HEVC_NAL_SEI_SUFFIX) {
+                ret = append_sei_annexb(&s->ps.sei, nal);
+                if (ret < 0)
+                    goto done;
+            }
+        }
+    }
+done:
+    ff_h2645_packet_uninit(&pkt);
+    return ret;
+}
+
+static void ps_uninit(ParamSets *ps)
+{
+    int i;
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->vps_list); i++) {
+       av_freep(&ps->vps_list[i].raw_data);
+       ps->vps_list[i].allocated_size = 0;
+       ps->vps_list[i].raw_size       = 0;
+    }
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) {
+       av_freep(&ps->sps_list[i].raw_data);
+       ps->sps_list[i].allocated_size = 0;
+       ps->sps_list[i].raw_size       = 0;
+    }
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) {
+       av_freep(&ps->pps_list[i].raw_data);
+       ps->pps_list[i].allocated_size = 0;
+       ps->pps_list[i].raw_size       = 0;
+    }
+    av_freep(&ps->sei);
+    ps->sei.allocated_size = 0;
+    ps->sei.raw_size       = 0;
+}
+
+static void hevc_mp4toannexb_close(AVBSFContext *ctx)
+{
+    HEVCBSFContext *s = ctx->priv_data;
+    ps_uninit(&s->ps);
+}
+
+static int get_vcl_pps_id(AVBSFContext *ctx, H2645NAL *nal)
+{
+    GetBitContext *gb = &nal->gb;
+    int pps_id;
+
+    gb->index = HEVC_NAL_HEADER_BITS;
+
+    skip_bits1(gb);        /*first_slice_segment_in_pic_flag*/
+
+    if (IS_IRAP(nal))
+        skip_bits1(gb);    /*no_output_of_prior_pics_flag*/
+
+    pps_id = get_ue_golomb_long(gb);
+    if (pps_id < 0 || pps_id >= HEVC_MAX_PPS_COUNT) {
+        av_log(ctx, AV_LOG_ERROR, "Invalid PPS id: %d\n", pps_id);
+        return AVERROR_INVALIDDATA;
+    }
+    return pps_id;
+}
+
+static int write_vcl(AVBSFContext *ctx, AVPacket *pkt_out, int *prev_size,
+                     H2645NAL *nal)
+{
+    HEVCBSFContext *s = ctx->priv_data;
+    ParamSets *ps     = &s->ps;
+    int ret, pps_id;
+    Param *pps;
+
+    /*get frame pps*/
+    pps_id = get_vcl_pps_id(ctx, nal);
+    if (pps_id < 0)
+        return AVERROR_INVALIDDATA;
+    av_log(ctx, AV_LOG_TRACE, "VCL PPS id: %d\n", pps_id);
+
+    /*write pps if not signalled*/
+    pps = &ps->pps_list[pps_id];
+    if (!pps->raw_data) {
+        av_log(ctx, AV_LOG_ERROR, "Invalid PPS: %d doesn't exist\n", pps_id);
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (!pps->is_signalled) {
+        av_log(ctx, AV_LOG_TRACE, "Referenced PPS: %d has not been signalled. Writing PPS to output stream\n", pps_id);
+        ret = av_grow_packet(pkt_out, pps->raw_size + 4);
+        if (ret < 0)
+            return ret;
+        WRITE_NAL(pkt_out->data, *prev_size, pps->raw_data, pps->raw_size);
+        pps->is_signalled = 1;
+    }
+
+    /*write actual packet*/
+    ret = av_grow_packet(pkt_out, nal->raw_size + 4);
+    if (ret < 0)
+        return ret;
+    WRITE_NAL(pkt_out->data, *prev_size, nal->raw_data, nal->raw_size);
+
+    return 0;
+}
+
+static int write_extradata(AVBSFContext *ctx, AVPacket *pkt_out, int *prev_size,
+                           H2645NAL *nal_irap)
+{
+    int ref, ret;
+    int new_extradata_size = 0;
+
+    HEVCBSFContext *s = ctx->priv_data;
+    ParamSets *ps     = &s->ps;
+    GetBitContext *gb = &nal_irap->gb;
+
+    /*active parameter sets for the new irap*/
+    const Param *vps;
+    const Param *sps;
+    Param *pps; /*non-const because we have to update flag that pps_id is signalled in cvs*/
+
+    skip_bits1(gb); /*first_slice_ic_pic_flag*/
+    skip_bits1(gb); /*no_output_of_prior_pics_flag*/
+
+    ref = get_ue_golomb_long(gb);
+    if (ref < 0 || ref >= HEVC_MAX_PPS_COUNT || !ps->pps_list[ref].raw_data) {
+        av_log(ctx, AV_LOG_ERROR, "Invalid PPS: %d\n", ref);
+        return AVERROR_INVALIDDATA;
+    }
+    av_log(ctx, AV_LOG_TRACE, "Writing PPS id: %d\n", ref);
+    pps                 = &ps->pps_list[ref];
+    new_extradata_size += pps->raw_size + 4;
+    ref                 = pps->ref;
+
+    if (ref >= HEVC_MAX_SPS_COUNT || !ps->sps_list[ref].raw_data) {
+        av_log(ctx, AV_LOG_ERROR, "Invalid SPS: %d\n", ref);
+        return AVERROR_INVALIDDATA;
+    }
+    av_log(ctx, AV_LOG_TRACE, "Writing SPS id: %d\n", ref);
+    sps                 = &ps->sps_list[ref];
+    new_extradata_size += sps->raw_size + 4;
+    ref                 = sps->ref;
+
+    if (ref >= HEVC_MAX_VPS_COUNT || !ps->vps_list[ref].raw_data) {
+        av_log(ctx, AV_LOG_ERROR, "Invalid VPS: %d\n", ref);
+        return AVERROR_INVALIDDATA;
+    }
+    av_log(ctx, AV_LOG_TRACE, "Writing VPS id: %d\n", ref);
+    vps                 = &ps->vps_list[ref];
+    new_extradata_size += vps->raw_size + 4;
+
+    if (ps->sei.raw_data)
+        new_extradata_size += ps->sei.raw_size; /*+4 not needed because it's already in annexb*/
+
+    ret = av_grow_packet(pkt_out, new_extradata_size);
+    if (ret < 0)
+        return ret;
+
+    WRITE_NAL(pkt_out->data, *prev_size, vps->raw_data, vps->raw_size);
+    WRITE_NAL(pkt_out->data, *prev_size, sps->raw_data, sps->raw_size);
+    WRITE_NAL(pkt_out->data, *prev_size, pps->raw_data, pps->raw_size);
+    pps->is_signalled = 1;
+
+    if (ps->sei.raw_data) {
+        memcpy(pkt_out->data + *prev_size, ps->sei.raw_data, ps->sei.raw_size);
+        *prev_size += ps->sei.raw_size;
     }
 
+    av_assert1(*prev_size == pkt_out->size);
     return 0;
 }
 
+/*
+ *Function converts mp4 access unit into annexb
+ *Output packet structure
+ *VPS, SPS, PPS, [SEI(from extradata)], [SEI_PREFIX(from access unit)], IRAP, [SEI_SUFFIX]
+ *or
+ *[SEI_PREFIX (from access unit)], [PPS (if not already signalled)], VCL(non-irap), [SEI_SUFFIX]
+ */
 static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
 {
     HEVCBSFContext *s = ctx->priv_data;
     AVPacket *in;
-    GetByteContext gb;
-
-    int got_irap = 0;
-    int i, ret = 0;
+    H2645Packet pkt;
+    int i, j, prev_size, ret;
+    int got_irap;
 
     ret = ff_bsf_get_packet(ctx, &in);
     if (ret < 0)
         return ret;
 
+    /*output the annexb nalu if extradata is not parsed*/
     if (!s->extradata_parsed) {
         av_packet_move_ref(out, in);
         av_packet_free(&in);
         return 0;
     }
 
-    bytestream2_init(&gb, in->data, in->size);
+    memset(&pkt, 0, sizeof(H2645Packet));
+    ret = ff_h2645_packet_split(&pkt, in->data, in->size, ctx, 1, s->length_size, AV_CODEC_ID_HEVC, 1, 0);
+    if (ret < 0)
+        goto done;
 
-    while (bytestream2_get_bytes_left(&gb)) {
-        uint32_t nalu_size = 0;
-        int      nalu_type;
-        int is_irap, add_extradata, extra_size, prev_size;
+    got_irap = 1; /*1 means that there is no irap or the irap has already been processed.*/
+    for (i = 0; i < pkt.nb_nals; i++) {
+        if (IS_IRAP(&pkt.nals[i])) {
+            got_irap = 0;
+            break;
+        }
+    }
 
-        for (i = 0; i < s->length_size; i++)
-            nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
+    prev_size = out->size; /*prev_size stores the current length of output packet */
+    av_assert1(prev_size == 0);
 
-        nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
+    for (i = 0; i < pkt.nb_nals; i++) {
+        H2645NAL *nal = &pkt.nals[i];
 
-        /* prepend extradata to IRAP frames */
-        is_irap       = nalu_type >= 16 && nalu_type <= 23;
-        add_extradata = is_irap && !got_irap;
-        extra_size    = add_extradata * ctx->par_out->extradata_size;
-        got_irap     |= is_irap;
+        if (IS_PARAMSET(nal)) {
+            ret = update_paramset(ctx, nal);
+            if (ret < 0)
+                goto done;
+            continue;
+        }
 
-        if (SIZE_MAX - nalu_size < 4 ||
-            SIZE_MAX - 4 - nalu_size < extra_size) {
-            ret = AVERROR_INVALIDDATA;
-            goto fail;
+        if (!got_irap && IS_IRAP(nal)) { /*append extradata and sei before first irap*/
+
+            /*reset the pps signalled flag in this cvs*/
+            for (j = 0; j < HEVC_MAX_PPS_COUNT; j++)
+                s->ps.pps_list[j].is_signalled = 0;
+
+            ret = write_extradata(ctx, out, &prev_size, nal);
+            if (ret < 0)
+                goto done;
+
+            /*append any past SEI prefix nals*/
+            for (j = 0; j < i; j++) {
+                H2645NAL *nal_past = &pkt.nals[j];
+                if (nal_past->type == HEVC_NAL_SEI_PREFIX) {
+                    ret = av_grow_packet(out, nal_past->raw_size + 4);
+                    if (ret < 0)
+                        goto done;
+                    WRITE_NAL(out->data, prev_size, nal_past->raw_data, nal_past->raw_size);
+                }
+            }
+
+            /*write irap nal unit*/
+            ret = write_vcl(ctx, out, &prev_size, nal);
+            if (ret < 0)
+                goto done;
+            got_irap = 1;
+            continue;
         }
 
-        prev_size = out->size;
+        /*do not write any sei prefix nal units if irap exists but has not been
+         * processed*/
+        if (nal->type == HEVC_NAL_SEI_PREFIX && !got_irap)
+            continue;
 
-        ret = av_grow_packet(out, 4 + nalu_size + extra_size);
-        if (ret < 0)
-            goto fail;
+        if (IS_VCL(nal)) {
+            ret = write_vcl(ctx, out, &prev_size, nal);
+            if (ret < 0)
+                goto done;
+            continue;
+        }
 
-        if (add_extradata)
-            memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
-        AV_WB32(out->data + prev_size + extra_size, 1);
-        bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size);
+        /*copy any other nal units to the output - i.e. SEI_SUFFIX*/
+        ret = av_grow_packet(out, nal->raw_size + 4);
+        if (ret < 0)
+            goto done;
+        WRITE_NAL(out->data, prev_size, nal->raw_data, nal->raw_size);
     }
 
+    av_assert1(prev_size == out->size);
+
     ret = av_packet_copy_props(out, in);
-    if (ret < 0)
-        goto fail;
 
-fail:
+done:
+    ff_h2645_packet_uninit(&pkt);
+
     if (ret < 0)
         av_packet_unref(out);
     av_packet_free(&in);
@@ -190,6 +611,7 @@  const AVBitStreamFilter ff_hevc_mp4toannexb_bsf = {
     .name           = "hevc_mp4toannexb",
     .priv_data_size = sizeof(HEVCBSFContext),
     .init           = hevc_mp4toannexb_init,
+    .close          = hevc_mp4toannexb_close,
     .filter         = hevc_mp4toannexb_filter,
     .codec_ids      = codec_ids,
 };
diff --git a/tests/fate/hevc.mak b/tests/fate/hevc.mak
index 559c3898bc..4f812b0834 100644
--- a/tests/fate/hevc.mak
+++ b/tests/fate/hevc.mak
@@ -238,7 +238,7 @@  FATE_HEVC-$(call ALLYES, HEVC_DEMUXER MOV_DEMUXER HEVC_MP4TOANNEXB_BSF MOV_MUXER
 fate-hevc-bsf-mp4toannexb: tests/data/hevc-mp4.mov
 fate-hevc-bsf-mp4toannexb: CMD = md5 -i $(TARGET_PATH)/tests/data/hevc-mp4.mov -c:v copy -fflags +bitexact -f hevc
 fate-hevc-bsf-mp4toannexb: CMP = oneline
-fate-hevc-bsf-mp4toannexb: REF = 1873662a3af1848c37e4eb25722c8df9
+fate-hevc-bsf-mp4toannexb: REF = 3c9d998a3aa2b9e0fb1c1f434952bf8b
 
 fate-hevc-skiploopfilter: CMD = framemd5 -skip_loop_filter nokey -i $(TARGET_SAMPLES)/hevc-conformance/SAO_D_Samsung_5.bit -sws_flags bitexact
 FATE_HEVC += fate-hevc-skiploopfilter