[FFmpeg-devel] [PATCH 2/2] avformat/mxfenc: support XAVC long gop

James Almer jamrial at gmail.com
Wed Apr 10 02:09:35 EEST 2019


On 4/9/2019 7:14 PM, Baptiste Coudurier wrote:
> ---
>  libavformat/Makefile |   2 +-
>  libavformat/avc.c    |  32 +++++++
>  libavformat/avc.h    |   2 +
>  libavformat/hevc.c   |  36 +-------
>  libavformat/mxf.h    |   1 +
>  libavformat/mxfenc.c | 201 +++++++++++++++++++++++++++++++++----------
>  6 files changed, 193 insertions(+), 81 deletions(-)
> 
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index c010fc83f9..f8539527bc 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -338,7 +338,7 @@ OBJS-$(CONFIG_MUSX_DEMUXER)              += musx.o
>  OBJS-$(CONFIG_MV_DEMUXER)                += mvdec.o
>  OBJS-$(CONFIG_MVI_DEMUXER)               += mvi.o
>  OBJS-$(CONFIG_MXF_DEMUXER)               += mxfdec.o mxf.o
> -OBJS-$(CONFIG_MXF_MUXER)                 += mxfenc.o mxf.o audiointerleave.o
> +OBJS-$(CONFIG_MXF_MUXER)                 += mxfenc.o mxf.o audiointerleave.o avc.o
>  OBJS-$(CONFIG_MXG_DEMUXER)               += mxg.o
>  OBJS-$(CONFIG_NC_DEMUXER)                += ncdec.o
>  OBJS-$(CONFIG_NISTSPHERE_DEMUXER)        += nistspheredec.o pcm.o
> diff --git a/libavformat/avc.c b/libavformat/avc.c
> index ec50033a04..6c97ae04ab 100644
> --- a/libavformat/avc.c
> +++ b/libavformat/avc.c
> @@ -241,3 +241,35 @@ const uint8_t *ff_avc_mp4_find_startcode(const uint8_t *start,
>  
>      return start + res;
>  }
> +
> +uint8_t *ff_nal_unit_extract_rbsp(const uint8_t *src, uint32_t src_len,
> +                                  uint32_t *dst_len, int header_len)

Nit: Might be a good chance to make src_len and dst_len size_t.

> +{
> +    uint8_t *dst;
> +    uint32_t i, len;
> +
> +    dst = av_malloc(src_len + AV_INPUT_BUFFER_PADDING_SIZE);
> +    if (!dst)
> +        return NULL;
> +
> +    /* NAL unit header */
> +    i = len = 0;
> +    while (i < header_len && i < src_len)
> +        dst[len++] = src[i++];
> +
> +    while (i + 2 < src_len)
> +        if (!src[i] && !src[i + 1] && src[i + 2] == 3) {
> +            dst[len++] = src[i++];
> +            dst[len++] = src[i++];
> +            i++; // remove emulation_prevention_three_byte
> +        } else
> +            dst[len++] = src[i++];
> +
> +    while (i < src_len)
> +        dst[len++] = src[i++];
> +
> +    memset(dst + len, 0, AV_INPUT_BUFFER_PADDING_SIZE);
> +
> +    *dst_len = len;
> +    return dst;
> +}

[...]

> @@ -2168,37 +2227,81 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
>  {
>      MXFContext *mxf = s->priv_data;
>      MXFStreamContext *sc = st->priv_data;
> -    AVCodecParameters *par = st->codecpar;
> -    static const int mxf_h264_num_codec_uls = sizeof(mxf_h264_codec_uls) / sizeof(mxf_h264_codec_uls[0]);
> +    H264ParamSets ps = { { 0 } };
> +    GetBitContext gb;
>      const uint8_t *buf = pkt->data;
>      const uint8_t *buf_end = pkt->data + pkt->size;
> +    const uint8_t *tmp, *nal_end;
>      uint32_t state = -1;
> -    int long_gop = 0; // assume intra when there is no SPS header
>      int extra_size = 512; // support AVC Intra files without SPS/PPS header
> -    int i, frame_size;
> -    uint8_t uid_found;
> -
> -    if (pkt->size > extra_size)
> -        buf_end -= pkt->size - extra_size; // no need to parse beyond SPS/PPS header
> +    int i, j, tmp_size, frame_size, ret, slice_type, intra_only = 0;
>  
>      for (;;) {
>          buf = avpriv_find_start_code(buf, buf_end, &state);
>          if (buf >= buf_end)
>              break;
> -        --buf;
> +
>          switch (state & 0x1f) {
>          case H264_NAL_SPS:
> -            par->profile = buf[1];
> -            long_gop = buf[2] & 0x10 ? 0 : 1; // constraint_set3_flag signals intra
>              e->flags |= 0x40;
> -            break;
> +
> +            if (ps.sps != NULL)
> +                break;
> +
> +            nal_end = ff_avc_find_startcode(buf, buf_end);
> +            tmp = ff_nal_unit_extract_rbsp(buf, nal_end - buf, &tmp_size, 0);
> +            if (!buf) {
> +                av_log(s, AV_LOG_ERROR, "error extracting rbsp\n");
> +                return 0;
> +            }
> +            init_get_bits(&gb, tmp, tmp_size*8);
> +            ret = avpriv_h264_decode_seq_parameter_set(&gb, (AVCodecContext*)s, &ps, 0);
> +            av_freep(&tmp);
> +            if (ret < 0)
> +                return 0;
> +            for (j = 0; j < MAX_SPS_COUNT; j++) {
> +                if (ps.sps_list[j] != NULL) {
> +                    ps.sps = (SPS*)ps.sps_list[j]->data;
> +                    break;
> +                }
> +            }
> +
> +            sc->aspect_ratio.num = st->codecpar->width * ps.sps->sar.num;
> +            sc->aspect_ratio.den = st->codecpar->height * ps.sps->sar.den;
> +            av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den,
> +                      sc->aspect_ratio.num, sc->aspect_ratio.den, 1024*1024);
> +            intra_only = (ps.sps->constraint_set_flags >> 3) & 1;
> +            sc->interlaced = !ps.sps->frame_mbs_only_flag;
> +            sc->component_depth = ps.sps->bit_depth_luma;
> +
> +            buf = nal_end;
> +            continue;
>          case H264_NAL_PPS:
>              if (e->flags & 0x40) { // sequence header present
>                  e->flags |= 0x80; // random access
>                  extra_size = 0;
> -                buf = buf_end;
>              }
>              break;
> +        case H264_NAL_IDR_SLICE:
> +            e->flags |= 0x04; // IDR Picture
> +            buf = buf_end;
> +            break;
> +        case H264_NAL_SLICE:
> +            init_get_bits(&gb, buf, buf_end - buf);
> +            get_ue_golomb_long(&gb); // skip first_mb_in_slice
> +            slice_type = get_ue_golomb_31(&gb);
> +            switch (slice_type % 5) {
> +            case 0:
> +                e->flags |= 0x20; // P Picture
> +                e->flags |= 0x06; // P Picture
> +                break;
> +            case 1:
> +                e->flags |= 0x30; // B Picture
> +                e->flags |= 0x03; // non-referenced B Picture
> +                break;
> +            }
> +            buf = buf_end;
> +            break;
>          default:
>              break;
>          }
> @@ -2207,27 +2310,35 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
>      if (mxf->header_written)
>          return 1;
>  
> -    sc->aspect_ratio = (AVRational){ 16, 9 }; // 16:9 is mandatory for broadcast HD
> -    sc->interlaced = par->field_order != AV_FIELD_PROGRESSIVE ? 1 : 0;
> -
> -    uid_found = 0;
> +    if (!ps.sps)
> +        sc->interlaced = st->codecpar->field_order != AV_FIELD_PROGRESSIVE ? 1 : 0;
> +    sc->codec_ul = NULL;
>      frame_size = pkt->size + extra_size;
> -    for (i = 0; i < mxf_h264_num_codec_uls; i++) {
> +
> +    for (i = 0; i < FF_ARRAY_ELEMS(mxf_h264_codec_uls); i++) {
>          if (frame_size == mxf_h264_codec_uls[i].frame_size && sc->interlaced == mxf_h264_codec_uls[i].interlaced) {
>              sc->codec_ul = &mxf_h264_codec_uls[i].uid;
>              sc->component_depth = 10; // AVC Intra is always 10 Bit
> +            sc->aspect_ratio = (AVRational){ 16, 9 }; // 16:9 is mandatory for broadcast HD
> +            st->codecpar->profile = mxf_h264_codec_uls[i].profile;
> +            sc->avc_intra = 1;
>              if (sc->interlaced)
>                  sc->field_dominance = 1; // top field first is mandatory for AVC Intra
> +            mxf->cbr_index = 1;
> +            sc->frame_size = frame_size;
>              return 1;
> -        } else if ((mxf_h264_codec_uls[i].profile == par->profile) &&
> -                   ((mxf_h264_codec_uls[i].long_gop < 0) ||
> -                   (mxf_h264_codec_uls[i].long_gop == long_gop))) {
> +        } else if (ps.sps && mxf_h264_codec_uls[i].frame_size == 0 &&
> +                   mxf_h264_codec_uls[i].profile == ps.sps->profile_idc &&
> +                   (mxf_h264_codec_uls[i].intra_only < 0 ||
> +                    mxf_h264_codec_uls[i].intra_only == intra_only)) {
>              sc->codec_ul = &mxf_h264_codec_uls[i].uid;
> -            uid_found = 1;
> +            st->codecpar->profile = ps.sps->profile_idc;
> +            st->codecpar->level = ps.sps->level_idc;
> +            // continue to check for avc intra
>          }
>      }
>  
> -    if (!uid_found) {
> +    if (!sc->codec_ul) {
>          av_log(s, AV_LOG_ERROR, "h264 profile not supported\n");
>          return 0;
>      }

Ok so, the only fields you need from the sps are constraint_set_flags,
frame_mbs_only_flag, bit_depth_luma, profile_idc, level_idc and sar,
meaning you don't even need to parse the sps until the very end (sar is
the furthest value, and right at the beginning of vui_parameters).
I'd very much prefer if we can avoid making
ff_h264_decode_seq_parameter_set() avpriv and with it H264ParamSets part
of the ABI, so i think it's worth copying the required bitstream parsing
code at least until the beginning of vui_parameters. It was done for
hevc and av1, so it can be done for h264 as well.

If you look a the h264 module from the CBS framework (cbs_h264) in
libavcodec, you'll find a very simple parsing implementation that
doesn't bother to decode the values, or fill tables, or allocate any
kind of buffer like the h264_ps.c does. You in fact will not need to
even store any value beyond the ones i listed above.
Writing a reusable and expansible-as-needed sps parsing function in
libavformat shouldn't be hard with the above module, and would be much
cleaner and less constraining in the long run for both libavformat and
libavcodec.


More information about the ffmpeg-devel mailing list