[FFmpeg-devel] [PATCH] mfxenc add jpeg2000 frame field interlacing support

Erwann RENAN erenan at ektacom.com
Thu Oct 28 18:59:37 EEST 2021


Ok, i will split it

-----Message d'origine-----
De : ffmpeg-devel <ffmpeg-devel-bounces at ffmpeg.org> De la part de Tomas Härdin
Envoyé : jeudi 28 octobre 2021 16:33
À : FFmpeg development discussions and patches <ffmpeg-devel at ffmpeg.org>
Objet : Re: [FFmpeg-devel] [PATCH] mfxenc add jpeg2000 frame field interlacing support

Looks like you messed up the subject. You need two newlines between the title of the patch and the description.

This patch also mixes a lot of different changes. Please split it up.
The bigger a patch is the more rounds of review it tends to have to go through.

> +    { 0x840B,
> {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0B,0x0
> 0,0x00,0x00}}, /* 8+3n bytes : Array of picture components where each 
> component comprises 3 bytes named Ssizi, XRSizi, YRSizi  The array of 
> 3-byte groups is preceded by the array header comprising a 4-byte 
> value of the number of components followed by a 4-byte value of  3 .
> */

some kind of encoding problem in the comment

> @@ -843,7 +886,28 @@ static void mxf_write_track(AVFormatContext *s, 
> AVStream *st, MXFPackage *packag
>  
>      mxf_write_metadata_key(pb, 0x013b00);
>      PRINT_KEY(s, "track key", pb->buf_ptr - 16);
> -    klv_encode_ber_length(pb, 80);
> +
> +    if (st->codecpar) {
> +        static const char * pcTrackName_Video = "Picture" ;
> +        static const char * pcTrackName_Audio = "Sound" ;
> +        static const char * pcTrackName_Anc = "Ancillary" ;

static is redundant here

> +        if ( st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO )
> +        {
> +            //TrackName Video
> +            klv_encode_ber_length(pb, 80 +
> mxf_utf16_local_tag_length(pcTrackName_Video));
> +            mxf_write_local_tag_utf16(s, 0x4802 ,
> pcTrackName_Video);
> +        } else if ( st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO )
> {
> +            //TrackName Audio
> +            klv_encode_ber_length(pb, 80 +
> mxf_utf16_local_tag_length(pcTrackName_Audio));
> +            mxf_write_local_tag_utf16(s, 0x4802, pcTrackName_Audio);
> +        } else {
> +            //TrackName Ancillary
> +            klv_encode_ber_length(pb, 80 +
> mxf_utf16_local_tag_length(pcTrackName_Anc));
> +            mxf_write_local_tag_utf16(s, 0x4802, pcTrackName_Anc);
> +        }
> +    } else {
> +        klv_encode_ber_length(pb, 80);
> +    }

Is this hunk really necessary?

> @@ -1327,6 +1420,182 @@ static int64_t 
> mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
>      return pos;
>  }
>  
> +static int64_t mxf_write_jpeg2000_common(AVFormatContext *s,
> AVStream *st, const UID key)
> +{
> +    MXFStreamContext *sc = st->priv_data;
> +    MXFContext *mxf = s->priv_data;
> +    AVIOContext *pb = s->pb;
> +    int stored_width = st->codecpar->width;
> +    int stored_height = st->codecpar->height;
> +    int display_height;
> +    int f1, f2;
> +    UID transfer_ul = {0};
> +    int64_t pos = mxf_write_generic_desc(s, st, key);
> +    get_trc(transfer_ul, st->codecpar->color_trc);

Return value of get_trc() is not checked. Not sure if invalid values can ever get in here.

> +
> +    mxf_write_local_tag(s, 4, 0x3203);
> +    avio_wb32(pb, stored_width);
> +    mxf_write_local_tag(s, 4, 0x3202);
> +    avio_wb32(pb, stored_height);
> +
> +    //Sampled width
> +    mxf_write_local_tag(s, 4, 0x3205);
> +    avio_wb32(pb, st->codecpar->width);
> +
> +    //Samples height
> +    mxf_write_local_tag(s, 4, 0x3204);
> +    avio_wb32(pb, st->codecpar->height);

Why use stored_* and codecpar->*? Just use codecpar->* in both places.

> +
> +    //Sampled X Offset
> +    mxf_write_local_tag(s, 4, 0x3206);
> +    avio_wb32(pb, 0);
> +
> +    //Sampled Y Offset
> +    mxf_write_local_tag(s, 4, 0x3207);
> +    avio_wb32(pb, 0);
> +
> +    mxf_write_local_tag(s, 4, 0x3209);
> +    avio_wb32(pb, st->codecpar->width);
> +
> +    if (st->codecpar->height == 608) // PAL + VBI
> +        display_height = 576;
> +    else if (st->codecpar->height == 512)  // NTSC + VBI
> +        display_height = 486;
> +    else
> +        display_height = st->codecpar->height;
> +
> +    mxf_write_local_tag(s, 4, 0x3208);
> +    avio_wb32(pb, display_height);
> +
> +    // display X offset
> +    mxf_write_local_tag(s, 4, 0x320A);
> +    avio_wb32(pb, 0);
> +
> +    //display Y offset
> +    mxf_write_local_tag(s, 4, 0x320B);
> +    avio_wb32(pb, (st->codecpar->height - display_height));

Would be better if we could get this information via metadata instead of adding hacks to the muxer

> +    // // Padding Bits
> +    // mxf_write_local_tag(s, 2, 0x3307);
> +    // avio_wb16(pb, 0);

Stray dead code

> +    // video line map
> +    {
> +        int _field_height = (mxf->mxf_j2kinterlace) ? (2*st-
> >codecpar->height) : st->codecpar->height;
> +
> +        if (st->codecpar->sample_aspect_ratio.num && st->codecpar-
> >sample_aspect_ratio.den) {
> +            AVRational _ratio = av_mul_q(st->codecpar-
> >sample_aspect_ratio,
> +                               av_make_q(st->codecpar->width, st-
> >codecpar->height));
> +            sc->aspect_ratio = _ratio;
> +
> +            switch (_field_height) {
> +                case  576: f1 = 23; f2 = st->codecpar->codec_id ==
> AV_CODEC_ID_DVVIDEO ? 335 : 336; break;
> +                case  608: f1 =  7; f2 = 320; break;
> +                case  480: f1 = 20; f2 = st->codecpar->codec_id ==
> AV_CODEC_ID_DVVIDEO ? 285 : 283; break;
> +                case  512: f1 =  7; f2 = 270; break;
> +                case  720: f1 = 26; f2 =   0; break; // progressive
> +                case 1080: f1 = 21; f2 = 584; break;
> +                default:   f1 =  0; f2 =   0; break;
> +            }
> +        } else {
> +            switch (_field_height) {
> +                case  576: sc->aspect_ratio = (AVRational){  4,  3};
> f1 = 23; f2 = st->codecpar->codec_id == AV_CODEC_ID_DVVIDEO ? 335 :
> 336; break;
> +                case  608: sc->aspect_ratio = (AVRational){  4,  3};
> f1 =  7; f2 = 320; break;
> +                case  480: sc->aspect_ratio = (AVRational){  4,  3};
> f1 = 20; f2 = st->codecpar->codec_id == AV_CODEC_ID_DVVIDEO ? 285 :
> 283; break;
> +                case  512: sc->aspect_ratio = (AVRational){  4,  3};
> f1 =  7; f2 = 270; break;
> +                case  720: sc->aspect_ratio = (AVRational){  16,
> 9}; f1 = 26; f2 =   0; break; // progressive
> +                case 1080: sc->aspect_ratio = (AVRational){  16,
> 9}; f1 = 21; f2 = 584; break;
> +                default:   f1 =  0; f2 =   0; break;
> +            }
> +        }
> +    }

This again feels like business logic that belongs in ffmpeg.c. I imagine this applies not just to J2K and not just to MXF. Remuxing MXF to MOV for example.

> +
> +    if (!sc->interlaced && f2) {
> +        f2  = 0;
> +        f1 *= 2;
> +    }

This looks.. interesting.

> +    mxf_write_local_tag(s, 2, 0x8401);
> +    avio_wb16(pb, 0x0000);
> +    mxf_write_local_tag(s, 4, 0x8402);
> +    avio_wb32(pb, st->codecpar->width);
> +    mxf_write_local_tag(s, 4, 0x8403);
> +    avio_wb32(pb, st->codecpar->height);
> +    mxf_write_local_tag(s, 4, 0x8404);
> +    avio_wb32(pb, 0);
> +    mxf_write_local_tag(s, 4, 0x8405);
> +    avio_wb32(pb, 0);
> +    mxf_write_local_tag(s, 4, 0x8406);
> +    avio_wb32(pb, st->codecpar->width);
> +    mxf_write_local_tag(s, 4, 0x8407);
> +    avio_wb32(pb, st->codecpar->height);
> +    mxf_write_local_tag(s, 4, 0x8408);
> +    avio_wb32(pb, 0);
> +    mxf_write_local_tag(s, 4, 0x8409);
> +    avio_wb32(pb, 0);
> +    mxf_write_local_tag(s, 2, 0x840A);
> +    avio_wb16(pb, component_count);

Please comment these, to the right of each mxf_write_local_tag() is fine.

> +
> +    mxf_write_local_tag(s, 8 + 3*component_count, 0x840B);
> +    avio_wb32(pb, component_count);
> +    avio_wb32(pb, 3);
> +    {
> +        char _desc [3][3]= {  {0x09,0x01,0x01} , {0x09,0x02,0x01} ,
> {0x09,0x02,0x01} };
> +        int comp = 0;
> +        for ( comp = 0; comp< component_count ;comp++ ) {
> +            avio_write(pb, _desc[comp%3] , 3);
> +        }
> +    }

FFmpeg is C99, braces like these are not necessary. You could also do this as a single 9-byte avio_write(). You could even have the data inline.

> +    mxf_write_local_tag(s, 16, 0x840C);
> +    {
> +        char _layout[16] = {  'Y' , '\n', 'U' , '\n', 'V' , '\n',
> 'F' , 0x02,
> +                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> 0x00, 0x00 };
> +        avio_write(pb, _layout , 16);
> +    }

Same here.

> @@ -1729,7 +2058,7 @@ static int
> mxf_write_header_metadata_sets(AVFormatContext *s)
>  static unsigned klv_fill_size(uint64_t size)
>  {
>      unsigned pad = KAG_SIZE - (size & (KAG_SIZE-1));
> -    if (pad < 20) // smallest fill item possible
> +    if (pad <= 20) // smallest fill item possible
>          return pad + KAG_SIZE;

This should *definitely* be its own patch. Also the existing code is correct unless I'm missing something majorly wrong. A zero-length ber4 KLV is 20 bytes.

>      else
>          return pad & (KAG_SIZE-1);
> @@ -2762,7 +3091,13 @@ static void
> mxf_write_system_item(AVFormatContext *s)
>      avio_wb64(pb, 0); // creation date/time stamp
>  
>      avio_w8(pb, 0x81); // SMPTE 12M time code
> -    time_code = av_timecode_get_smpte_from_framenum(&mxf->tc,
> frame);
> +    if ( 0 == mxf->mxf_j2kinterlace ) {
> +        time_code = av_timecode_get_smpte_from_framenum(&mxf->tc,
> frame);
> +    } else {
> +        unsigned int _myframenum = frame>>1;
> +        time_code = av_timecode_get_smpte_from_framenum(&mxf->tc,
> _myframenum);
> +    }

This looks.. fun.

> +
>      avio_wb32(pb, time_code);
>      avio_wb32(pb, 0); // binary group data
>      avio_wb64(pb, 0);
> @@ -2928,6 +3263,12 @@ static int mxf_write_packet(AVFormatContext *s, 
> AVPacket *pkt)
>              av_log(s, AV_LOG_ERROR, "could not get h264 profile\n");
>              return -1;
>          }
> +    } else if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) {
> +        if (mxf->mxf_j2kinterlace) {
> +            st->codecpar->field_order = AV_FIELD_TT;
> +            sc->interlaced = 1;
> +            sc->field_dominance = 1;

Aren't these settable via ffmpeg?

> @@ -2960,11 +3301,13 @@ static int mxf_write_packet(AVFormatContext 
> *s, AVPacket *pkt)
>          if (!mxf->edit_unit_byte_count &&
>              (!mxf->edit_units_count || mxf->edit_units_count >
> EDIT_UNITS_PER_BODY) &&
>              !(ie.flags & 0x33)) { // I-frame, GOP start
> -            mxf_write_klv_fill(s);
> -            if ((err = mxf_write_partition(s, 1, 2, 
> body_partition_key, 0)) < 0)
> -                return err;
> -            mxf_write_klv_fill(s);
> -            mxf_write_index_table_segment(s);
> +            if (!mxf->mxf_nobody) {

Maybe rename to mxf_no_body

> @@ -3198,6 +3541,12 @@ static const AVOption mxf_options[] = {
>      MXF_COMMON_OPTIONS
>      { "store_user_comments", "",
>        offsetof(MXFContext, store_user_comments), AV_OPT_TYPE_BOOL,
> {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
> +    { "mxf_nobody", "",
> +      offsetof(MXFContext, mxf_nobody), AV_OPT_TYPE_BOOL, {.i64 =
> 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
> +    { "mxf_j2kinterlace", "",
> +      offsetof(MXFContext, mxf_j2kinterlace), AV_OPT_TYPE_BOOL,
> {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
> +    { "mxf_footer_with_hmd", "",
> +      offsetof(MXFContext, footer_with_hmd), AV_OPT_TYPE_BOOL, {.i64
> = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},

These need descriptions. I don't really see the point in not writing a body partition. I also suspect it will cause all sorts of issues if any essence is actually written.

/Tomas

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel at ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".



More information about the ffmpeg-devel mailing list