diff mbox

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

Message ID 20190410212333.8771-1-baptiste.coudurier@gmail.com
State Superseded
Headers show

Commit Message

Baptiste Coudurier April 10, 2019, 9:23 p.m. UTC
---
 libavformat/Makefile |   2 +-
 libavformat/avc.c    | 173 +++++++++++++++++++++++++++++++++++
 libavformat/avc.h    |  15 +++
 libavformat/hevc.c   |  36 +-------
 libavformat/mxf.h    |   1 +
 libavformat/mxfenc.c | 213 ++++++++++++++++++++++++++++++++++---------
 6 files changed, 359 insertions(+), 81 deletions(-)

Comments

James Almer April 10, 2019, 10:16 p.m. UTC | #1
On 4/10/2019 6:23 PM, Baptiste Coudurier wrote:
> ---
>  libavformat/Makefile |   2 +-
>  libavformat/avc.c    | 173 +++++++++++++++++++++++++++++++++++
>  libavformat/avc.h    |  15 +++
>  libavformat/hevc.c   |  36 +-------
>  libavformat/mxf.h    |   1 +
>  libavformat/mxfenc.c | 213 ++++++++++++++++++++++++++++++++++---------
>  6 files changed, 359 insertions(+), 81 deletions(-)
> 
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 99be60d184..df87c54a58 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -339,7 +339,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..06a1e495a8 100644
> --- a/libavformat/avc.c
> +++ b/libavformat/avc.c
> @@ -21,6 +21,7 @@
>  
>  #include "libavutil/intreadwrite.h"
>  #include "libavcodec/h264.h"
> +#include "libavcodec/golomb.h"

Right, so golomb has a bunch of lookup tables in golomb.c, obviously not
accessible from libavformat. So this will probably fail to build in
shared builds.

One option is to include golomb.c here. Another is donig with it the
same we're doing with libavutil's log2_tab.c, and the other is to write
simple get_se() and get_ue() functions here that don't use lookup tables.

Something like (untested)

static int get_ue_golomb(GetBitContext *gb)
{
    int i, j, k, v;

    for (i = 0; i < 32; i++) {
        k = get_bits1(gbc);
        if (k)
            break;
    }
    v = 1;
    for (j = 0; j < i; j++) {
        k = get_bits1(gbc);
        v = v << 1 | k;
    }
    --v;

    return v;
}

static int get_se_golomb(GetBitContext *gb)
{
    int i, j, k, v;

    for (i = 0; i < 32; i++) {
        k = get_bits1(gbc);
        if (k)
            break;
    }
    v = 1;
    for (j = 0; j < i; j++) {
        k = get_bits1(gbc);
        v = v << 1 | k;
    }
    if (v & 1)
        v = -(int32_t)(v / 2);
    else
        v = v / 2;

    return v;
}
Baptiste Coudurier May 10, 2019, 3:51 p.m. UTC | #2
> On Apr 10, 2019, at 3:16 PM, James Almer <jamrial@gmail.com> wrote:
> 
> On 4/10/2019 6:23 PM, Baptiste Coudurier wrote:
>> ---
>> libavformat/Makefile |   2 +-
>> libavformat/avc.c    | 173 +++++++++++++++++++++++++++++++++++
>> libavformat/avc.h    |  15 +++
>> libavformat/hevc.c   |  36 +-------
>> libavformat/mxf.h    |   1 +
>> libavformat/mxfenc.c | 213 ++++++++++++++++++++++++++++++++++---------
>> 6 files changed, 359 insertions(+), 81 deletions(-)
>> 
>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>> index 99be60d184..df87c54a58 100644
>> --- a/libavformat/Makefile
>> +++ b/libavformat/Makefile
>> @@ -339,7 +339,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..06a1e495a8 100644
>> --- a/libavformat/avc.c
>> +++ b/libavformat/avc.c
>> @@ -21,6 +21,7 @@
>> 
>> #include "libavutil/intreadwrite.h"
>> #include "libavcodec/h264.h"
>> +#include "libavcodec/golomb.h"
> 
> Right, so golomb has a bunch of lookup tables in golomb.c, obviously not
> accessible from libavformat. So this will probably fail to build in
> shared builds.
> 
> One option is to include golomb.c here. Another is donig with it the
> same we're doing with libavutil's log2_tab.c, and the other is to write
> simple get_se() and get_ue() functions here that don't use lookup tables.

Patch updated, reimplemented get_se and get_ue as suggested.

— 
Baptiste
diff mbox

Patch

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 99be60d184..df87c54a58 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -339,7 +339,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..06a1e495a8 100644
--- a/libavformat/avc.c
+++ b/libavformat/avc.c
@@ -21,6 +21,7 @@ 
 
 #include "libavutil/intreadwrite.h"
 #include "libavcodec/h264.h"
+#include "libavcodec/golomb.h"
 #include "avformat.h"
 #include "avio.h"
 #include "avc.h"
@@ -241,3 +242,175 @@  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)
+{
+    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;
+}
+
+static const AVRational avc_sample_aspect_ratio[17] = {
+    {   0,  1 },
+    {   1,  1 },
+    {  12, 11 },
+    {  10, 11 },
+    {  16, 11 },
+    {  40, 33 },
+    {  24, 11 },
+    {  20, 11 },
+    {  32, 11 },
+    {  80, 33 },
+    {  18, 11 },
+    {  15, 11 },
+    {  64, 33 },
+    { 160, 99 },
+    {   4,  3 },
+    {   3,  2 },
+    {   2,  1 },
+};
+
+H264SequenceParameterSet *ff_avc_decode_sps(const uint8_t *buf, int buf_size)
+{
+    int i, j, ret, rbsp_size, aspect_ratio_idc, pic_order_cnt_type;
+    int num_ref_frames_in_pic_order_cnt_cycle;
+    int delta_scale, lastScale = 8, nextScale = 8;
+    int sizeOfScalingList;
+    H264SequenceParameterSet *sps = NULL;
+    GetBitContext gb;
+    uint8_t *rbsp_buf;
+
+    rbsp_buf = ff_nal_unit_extract_rbsp(buf, buf_size, &rbsp_size, 0);
+    if (!rbsp_buf)
+        return NULL;
+
+    ret = init_get_bits8(&gb, rbsp_buf, rbsp_size);
+    if (ret < 0)
+        goto end;
+
+    sps = av_mallocz(sizeof(*sps));
+    if (!sps)
+        goto end;
+
+    sps->profile_idc = get_bits(&gb, 8);
+    sps->constraint_set_flags |= get_bits1(&gb) << 0; // constraint_set0_flag
+    sps->constraint_set_flags |= get_bits1(&gb) << 1; // constraint_set1_flag
+    sps->constraint_set_flags |= get_bits1(&gb) << 2; // constraint_set2_flag
+    sps->constraint_set_flags |= get_bits1(&gb) << 3; // constraint_set3_flag
+    sps->constraint_set_flags |= get_bits1(&gb) << 4; // constraint_set4_flag
+    sps->constraint_set_flags |= get_bits1(&gb) << 5; // constraint_set5_flag
+    skip_bits(&gb, 2); // reserved_zero_2bits
+    sps->level_idc = get_bits(&gb, 8);
+    sps->id = get_ue_golomb_31(&gb);
+
+    if (sps->profile_idc == 100 || sps->profile_idc == 110 ||
+        sps->profile_idc == 122 || sps->profile_idc == 244 || sps->profile_idc ==  44 ||
+        sps->profile_idc ==  83 || sps->profile_idc ==  86 || sps->profile_idc == 118 ||
+        sps->profile_idc == 128 || sps->profile_idc == 138 || sps->profile_idc == 139 ||
+        sps->profile_idc == 134) {
+        sps->chroma_format_idc = get_ue_golomb_31(&gb); // chroma_format_idc
+        if (sps->chroma_format_idc == 3) {
+            skip_bits1(&gb); // separate_colour_plane_flag
+        }
+        sps->bit_depth_luma = get_ue_golomb(&gb) + 8;
+        get_ue_golomb(&gb); // bit_depth_chroma_minus8
+        skip_bits1(&gb); // qpprime_y_zero_transform_bypass_flag
+        if (get_bits1(&gb)) { // seq_scaling_matrix_present_flag
+            for (i = 0; i < ((sps->chroma_format_idc != 3) ? 8 : 12); i++) {
+                if (get_bits1(&gb)) { // seq_scaling_list_present_flag
+                    lastScale = 8;
+                    nextScale = 8;
+                    sizeOfScalingList = i < 6 ? 16 : 64;
+                    for (j = 0; j < sizeOfScalingList; j++) {
+                        if (nextScale != 0) {
+                            delta_scale = get_se_golomb(&gb);
+                            nextScale = (lastScale + delta_scale) & 0xff;
+                        }
+                        lastScale = nextScale == 0 ? lastScale : nextScale;
+                    }
+                }
+            }
+        }
+    } else {
+        sps->chroma_format_idc = 1;
+        sps->bit_depth_luma = 8;
+    }
+
+    get_ue_golomb(&gb); // log2_max_frame_num_minus4
+    pic_order_cnt_type = get_ue_golomb_31(&gb);
+
+    if (pic_order_cnt_type == 0) {
+        get_ue_golomb(&gb); // log2_max_pic_ordeer_cnt_lsb_minus4
+    } else if (pic_order_cnt_type == 1) {
+        skip_bits1(&gb);    // delta_pic_order_always_zero
+        get_se_golomb(&gb); // offset_for_non_ref_pic
+        get_se_golomb(&gb); // offset_for_top_to_bottom_field
+        num_ref_frames_in_pic_order_cnt_cycle = get_ue_golomb(&gb);
+        for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++)
+            get_se_golomb(&gb); // offset_for_ref_frame
+    }
+
+    get_ue_golomb_31(&gb); // max_num_ref_frames
+    skip_bits1(&gb); // gaps_in_frame_num_value_allowed_flag
+    get_ue_golomb(&gb); // pic_width_in_mbs_minus1
+    get_ue_golomb(&gb); // pic_height_in_map_units_minus1
+
+    sps->frame_mbs_only_flag = get_bits1(&gb);
+    if (!sps->frame_mbs_only_flag)
+        skip_bits1(&gb); // mb_adaptive_frame_field_flag
+
+    skip_bits1(&gb); // direct_8x8_inference_flag
+
+    if (get_bits1(&gb)) { // frame_cropping_flag
+        get_ue_golomb(&gb); // frame_crop_left_offset
+        get_ue_golomb(&gb); // frame_crop_right_offset
+        get_ue_golomb(&gb); // frame_crop_top_offset
+        get_ue_golomb(&gb); // frame_crop_bottom_offset
+    }
+
+    if (get_bits1(&gb)) { // vui_parameters_present_flag
+        if (get_bits1(&gb)) { // aspect_ratio_info_present_flag
+            aspect_ratio_idc = get_bits(&gb, 8);
+            if (aspect_ratio_idc == 0xff) {
+                sps->sar.num = get_bits(&gb, 16);
+                sps->sar.den = get_bits(&gb, 16);
+            } else if (aspect_ratio_idc < FF_ARRAY_ELEMS(avc_sample_aspect_ratio)) {
+                sps->sar = avc_sample_aspect_ratio[aspect_ratio_idc];
+            }
+        }
+    }
+
+    if (!sps->sar.den) {
+        sps->sar.num = 1;
+        sps->sar.den = 1;
+    }
+
+ end:
+    av_free(rbsp_buf);
+    return sps;
+}
diff --git a/libavformat/avc.h b/libavformat/avc.h
index c5e80ff650..a79bf9b2db 100644
--- a/libavformat/avc.h
+++ b/libavformat/avc.h
@@ -33,5 +33,20 @@  int ff_avc_write_annexb_extradata(const uint8_t *in, uint8_t **buf, int *size);
 const uint8_t *ff_avc_mp4_find_startcode(const uint8_t *start,
                                          const uint8_t *end,
                                          int nal_length_size);
+uint8_t *ff_nal_unit_extract_rbsp(const uint8_t *src, uint32_t src_len,
+                                  uint32_t *dst_len, int header_len);
+
+typedef struct {
+    uint8_t id;
+    uint8_t profile_idc;
+    uint8_t level_idc;
+    uint8_t constraint_set_flags;
+    uint8_t chroma_format_idc;
+    uint8_t bit_depth_luma;
+    uint8_t frame_mbs_only_flag;
+    AVRational sar;
+} H264SequenceParameterSet;
+
+H264SequenceParameterSet *ff_avc_decode_sps(const uint8_t *src, int src_len);
 
 #endif /* AVFORMAT_AVC_H */
diff --git a/libavformat/hevc.c b/libavformat/hevc.c
index 3628d5a028..c7c4be3441 100644
--- a/libavformat/hevc.c
+++ b/libavformat/hevc.c
@@ -643,40 +643,6 @@  static int hvcc_parse_pps(GetBitContext *gb,
     return 0;
 }
 
-static uint8_t *nal_unit_extract_rbsp(const uint8_t *src, uint32_t src_len,
-                                      uint32_t *dst_len)
-{
-    uint8_t *dst;
-    uint32_t i, len;
-
-    dst = av_malloc(src_len + AV_INPUT_BUFFER_PADDING_SIZE);
-    if (!dst)
-        return NULL;
-
-    /* NAL unit header (2 bytes) */
-    i = len = 0;
-    while (i < 2 && 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;
-}
-
-
-
 static void nal_unit_parse_header(GetBitContext *gb, uint8_t *nal_type)
 {
     skip_bits1(gb); // forbidden_zero_bit
@@ -753,7 +719,7 @@  static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size,
     uint8_t *rbsp_buf;
     uint32_t rbsp_size;
 
-    rbsp_buf = nal_unit_extract_rbsp(nal_buf, nal_size, &rbsp_size);
+    rbsp_buf = ff_nal_unit_extract_rbsp(nal_buf, nal_size, &rbsp_size, 2);
     if (!rbsp_buf) {
         ret = AVERROR(ENOMEM);
         goto end;
diff --git a/libavformat/mxf.h b/libavformat/mxf.h
index 4394450dea..f32124f772 100644
--- a/libavformat/mxf.h
+++ b/libavformat/mxf.h
@@ -48,6 +48,7 @@  enum MXFMetadataSetType {
     EssenceGroup,
     TaggedValue,
     TapeDescriptor,
+    AVCSubDescriptor,
 };
 
 enum MXFFrameLayout {
diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index 8c6db94865..2bfef953fa 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -49,12 +49,14 @@ 
 #include "libavcodec/bytestream.h"
 #include "libavcodec/dnxhddata.h"
 #include "libavcodec/dv_profile.h"
-#include "libavcodec/h264.h"
+#include "libavcodec/h264_ps.h"
+#include "libavcodec/golomb.h"
 #include "libavcodec/internal.h"
 #include "audiointerleave.h"
 #include "avformat.h"
 #include "avio_internal.h"
 #include "internal.h"
+#include "avc.h"
 #include "mxf.h"
 #include "config.h"
 
@@ -99,6 +101,7 @@  typedef struct MXFStreamContext {
     int max_gop;             ///< maximum gop size, used by mpeg-2 descriptor
     int b_picture_count;     ///< maximum number of consecutive b pictures, used in mpeg-2 descriptor
     int low_delay;           ///< low delay, used in mpeg-2 descriptor
+    int avc_intra;
 } MXFStreamContext;
 
 typedef struct MXFContainerEssenceEntry {
@@ -167,6 +170,7 @@  static const struct {
 static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st);
 static void mxf_write_aes3_desc(AVFormatContext *s, AVStream *st);
 static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st);
+static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st);
 static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st);
 static void mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st);
 static void mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st);
@@ -310,7 +314,7 @@  static const MXFContainerEssenceEntry mxf_essence_container_uls[] = {
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x0D,0x01,0x03,0x01,0x02,0x10,0x60,0x01 },
       { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x00,0x00,0x00 },
-      mxf_write_mpegvideo_desc },
+      mxf_write_h264_desc },
     // S436M ANC
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x0D,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x17,0x01,0x02,0x00 },
@@ -503,6 +507,13 @@  static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 0x3D0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x02,0x01,0x00,0x00,0x00}}, /* Block Align */
 };
 
+static const MXFLocalTagPair mxf_avc_subdescriptor_local_tags[] = {
+    { 0x8100, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00}}, /* SubDescriptors */
+    { 0x8200, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x06,0x01,0x0E,0x00,0x00}}, /* AVC Decoding Delay */
+    { 0x8201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x06,0x01,0x0A,0x00,0x00}}, /* AVC Profile */
+    { 0x8202, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x06,0x01,0x0D,0x00,0x00}}, /* AVC Level */
+};
+
 static const MXFLocalTagPair mxf_user_comments_local_tag[] = {
     { 0x4406, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x0C,0x00,0x00,0x00}}, /* User Comments */
     { 0x5001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x09,0x01,0x00,0x00}}, /* Name */
@@ -584,15 +595,33 @@  static int mxf_get_essence_container_ul_index(enum AVCodecID id)
     return -1;
 }
 
+static void mxf_write_local_tags(AVIOContext *pb, const MXFLocalTagPair *local_tags, int count)
+{
+    int i;
+    for (i = 0; i < count; i++) {
+        avio_wb16(pb, local_tags[i].local_tag);
+        avio_write(pb, local_tags[i].uid, 16);
+    }
+}
+
 static void mxf_write_primer_pack(AVFormatContext *s)
 {
     MXFContext *mxf = s->priv_data;
     AVIOContext *pb = s->pb;
     int local_tag_number, i = 0;
+    int avc_tags_count = 0;
 
     local_tag_number = FF_ARRAY_ELEMS(mxf_local_tag_batch);
     local_tag_number += mxf->store_user_comments * FF_ARRAY_ELEMS(mxf_user_comments_local_tag);
 
+    for (i = 0; i < s->nb_streams; i++) {
+        MXFStreamContext *sc = s->streams[i]->priv_data;
+        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264 && !sc->avc_intra) {
+            avc_tags_count = FF_ARRAY_ELEMS(mxf_avc_subdescriptor_local_tags);
+            local_tag_number += avc_tags_count;
+        }
+    }
+
     avio_write(pb, primer_pack_key, 16);
     klv_encode_ber_length(pb, local_tag_number * 18 + 8);
 
@@ -608,6 +637,8 @@  static void mxf_write_primer_pack(AVFormatContext *s)
             avio_wb16(pb, mxf_user_comments_local_tag[i].local_tag);
             avio_write(pb, mxf_user_comments_local_tag[i].uid, 16);
         }
+    if (avc_tags_count > 0)
+        mxf_write_local_tags(pb, mxf_avc_subdescriptor_local_tags, avc_tags_count);
 }
 
 static void mxf_write_local_tag(AVIOContext *pb, int size, int tag)
@@ -1126,6 +1157,8 @@  static const UID mxf_aes3_descriptor_key      = { 0x06,0x0E,0x2B,0x34,0x02,0x53,
 static const UID mxf_cdci_descriptor_key      = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x28,0x00 };
 static const UID mxf_generic_sound_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x42,0x00 };
 
+static const UID mxf_avc_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6E,0x00 };
+
 static int get_trc(UID ul, enum AVColorTransferCharacteristic trc)
 {
     switch (trc){
@@ -1317,6 +1350,13 @@  static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
         avio_w8(pb, sc->field_dominance);
     }
 
+    if (st->codecpar->codec_id == AV_CODEC_ID_H264 && !sc->avc_intra) {
+        // write avc sub descriptor ref
+        mxf_write_local_tag(pb, 8 + 16, 0x8100);
+        mxf_write_refs_count(pb, 1);
+        mxf_write_uuid(pb, AVCSubDescriptor, 0);
+    }
+
     return pos;
 }
 
@@ -1329,10 +1369,50 @@  static void mxf_update_klv_size(AVIOContext *pb, int64_t pos)
     avio_seek(pb, cur_pos, SEEK_SET);
 }
 
+static void mxf_write_avc_subdesc(AVFormatContext *s, AVStream *st)
+{
+    AVIOContext *pb = s->pb;
+    int64_t pos;
+
+    avio_write(pb, mxf_avc_subdescriptor_key, 16);
+    klv_encode_ber4_length(pb, 0);
+    pos = avio_tell(pb);
+
+    mxf_write_local_tag(pb, 16, 0x3C0A);
+    mxf_write_uuid(pb, AVCSubDescriptor, 0);
+
+    mxf_write_local_tag(pb, 1, 0x8200);
+    avio_w8(pb, 0xFF); // AVC Decoding Delay, unknown
+
+    mxf_write_local_tag(pb, 1, 0x8201);
+    avio_w8(pb, st->codecpar->profile); // AVC Profile
+
+    mxf_write_local_tag(pb, 1, 0x8202);
+    avio_w8(pb, st->codecpar->level); // AVC Level
+
+    mxf_update_klv_size(s->pb, pos);
+}
+
 static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st)
 {
     int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key);
     mxf_update_klv_size(s->pb, pos);
+
+    if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
+        mxf_write_avc_subdesc(s, st);
+    }
+}
+
+static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st)
+{
+    MXFStreamContext *sc = st->priv_data;
+    if (sc->avc_intra) {
+        mxf_write_mpegvideo_desc(s, st);
+    } else {
+        int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key);
+        mxf_update_klv_size(s->pb, pos);
+        mxf_write_avc_subdesc(s, st);
+    }
 }
 
 static void mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st)
@@ -2136,30 +2216,30 @@  static const struct {
     int frame_size;
     int profile;
     uint8_t interlaced;
-    int long_gop; // 1 or 0 when there are separate UIDs for Long GOP and Intra, -1 when Intra/LGOP detection can be ignored
+    int intra_only; // 1 or 0 when there are separate UIDs for Long GOP and Intra, -1 when Intra/LGOP detection can be ignored
 } mxf_h264_codec_uls[] = {
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x11,0x01 },      0,  66, 0, -1 }, // AVC Baseline, Unconstrained Coding
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x11,0x01 },      0,  66, 0, -1 }, // AVC Baseline
     {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x20,0x01 },      0,  77, 0, -1 }, // AVC Main
     {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x30,0x01 },      0,  88, 0, -1 }, // AVC Extended
     {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x40,0x01 },      0, 100, 0, -1 }, // AVC High
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x50,0x01 },      0, 110, 0,  1 }, // AVC High 10
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x60,0x01 },      0, 122, 0,  1 }, // AVC High 422
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x70,0x01 },      0, 244, 0,  1 }, // AVC High 444
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x20,0x01 },      0, 110, 0,  0 }, // AVC High 10 Intra
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x01 }, 232960,   0, 1,  0 }, // AVC Intra 50 1080i60
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x02 }, 281088,   0, 1,  0 }, // AVC Intra 50 1080i50
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x03 }, 232960,   0, 0,  0 }, // AVC Intra 50 1080p30
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x04 }, 281088,   0, 0,  0 }, // AVC Intra 50 1080p25
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x08 }, 116736,   0, 0,  0 }, // AVC Intra 50 720p60
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x09 }, 140800,   0, 0,  0 }, // AVC Intra 50 720p50
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x30,0x01 },      0, 122, 0,  0 }, // AVC High 422 Intra
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x01 }, 472576,   0, 1,  0 }, // AVC Intra 100 1080i60
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x02 }, 568832,   0, 1,  0 }, // AVC Intra 100 1080i50
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x03 }, 472576,   0, 0,  0 }, // AVC Intra 100 1080p30
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x04 }, 568832,   0, 0,  0 }, // AVC Intra 100 1080p25
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x08 }, 236544,   0, 0,  0 }, // AVC Intra 100 720p60
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x09 }, 284672,   0, 0,  0 }, // AVC Intra 100 720p50
-    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x01,0x32,0x40,0x01 },      0, 244, 0,  0 }, // AVC High 444 Intra
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x50,0x01 },      0, 110, 0,  0 }, // AVC High 10
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x60,0x01 },      0, 122, 0,  0 }, // AVC High 422
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x31,0x70,0x01 },      0, 244, 0,  0 }, // AVC High 444
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x20,0x01 },      0, 110, 0,  1 }, // AVC High 10 Intra
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x01 }, 232960, 110, 1,  1 }, // AVC High 10 Intra RP2027 Class 50 1080/59.94i
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x02 }, 281088, 110, 1,  1 }, // AVC High 10 Intra RP2027 Class 50 1080/50i
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x03 }, 232960, 110, 0,  1 }, // AVC High 10 Intra RP2027 Class 50 1080/29.97p
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x04 }, 281088, 110, 0,  1 }, // AVC High 10 Intra RP2027 Class 50 1080/25p
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x08 }, 116736, 110, 0,  1 }, // AVC High 10 Intra RP2027 Class 50 720/59.94p
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x09 }, 140800, 110, 0,  1 }, // AVC High 10 Intra RP2027 Class 50 720/50p
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x30,0x01 },      0, 122, 0,  1 }, // AVC High 422 Intra
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x01 }, 472576, 122, 1,  1 }, // AVC High 422 Intra RP2027 Class 100 1080/59.94i
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x02 }, 568832, 122, 1,  1 }, // AVC High 422 Intra RP2027 Class 100 1080/50i
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x03 }, 472576, 122, 0,  1 }, // AVC High 422 Intra RP2027 Class 100 1080/29.97p
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x04 }, 568832, 122, 0,  1 }, // AVC High 422 Intra RP2027 Class 100 1080/25p
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x08 }, 236544, 122, 0,  1 }, // AVC High 422 Intra RP2027 Class 100 720/59.94p
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x09 }, 284672, 122, 0,  1 }, // AVC High 422 Intra RP2027 Class 100 720/50p
+    {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x01,0x32,0x40,0x01 },      0, 244, 0,  1 }, // AVC High 444 Intra
     {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x01,0x32,0x50,0x01 },      0,  44, 0, -1 }, // AVC CAVLC 444
 };
 
@@ -2168,37 +2248,70 @@  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]);
+    H264SequenceParameterSet *sps;
+    GetBitContext gb;
     const uint8_t *buf = pkt->data;
     const uint8_t *buf_end = pkt->data + pkt->size;
+    const uint8_t *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, frame_size, 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;
+
+            if (mxf->header_written)
+                break;
+
+            nal_end = ff_avc_find_startcode(buf, buf_end);
+            sps = ff_avc_decode_sps(buf, nal_end - buf);
+            if (!sps) {
+                av_log(s, AV_LOG_ERROR, "error parsing sps\n");
+                return 0;
+            }
+
+            sc->aspect_ratio.num = st->codecpar->width * sps->sar.num;
+            sc->aspect_ratio.den = st->codecpar->height * 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 = (sps->constraint_set_flags >> 3) & 1;
+            sc->interlaced = !sps->frame_mbs_only_flag;
+            sc->component_depth = sps->bit_depth_luma;
+
+            buf = nal_end;
             break;
         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_bits8(&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 +2320,37 @@  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 (!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;
+            mxf->cbr_index = 1;
+            sc->frame_size = frame_size;
             if (sc->interlaced)
                 sc->field_dominance = 1; // top field first is mandatory for AVC Intra
-            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))) {
+            break;
+        } else if (sps && mxf_h264_codec_uls[i].frame_size == 0 &&
+                   mxf_h264_codec_uls[i].profile == 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 = sps->profile_idc;
+            st->codecpar->level = sps->level_idc;
+            // continue to check for avc intra
         }
     }
 
-    if (!uid_found) {
+    av_free(sps);
+
+    if (!sc->codec_ul) {
         av_log(s, AV_LOG_ERROR, "h264 profile not supported\n");
         return 0;
     }