PR #20711 opened by Baptiste Coudurier (bcoudurier) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20711 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20711.patch
From 8bc654b2c0f79213195140f372c83095b0e2b123 Mon Sep 17 00:00:00 2001 From: Baptiste Coudurier <[email protected]> Date: Tue, 3 Jun 2025 17:50:15 -0700 Subject: [PATCH 1/7] lavf/movenc: improve AVdh atom generation for DNxHD/DNxHR --- libavformat/movenc.c | 86 ++++++++++++++++------ tests/ref/vsynth/vsynth1-dnxhd-1080i | 2 +- tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit | 2 +- tests/ref/vsynth/vsynth1-dnxhd-1080i-colr | 2 +- tests/ref/vsynth/vsynth2-dnxhd-1080i | 2 +- tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit | 2 +- tests/ref/vsynth/vsynth2-dnxhd-1080i-colr | 2 +- tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit | 2 +- tests/ref/vsynth/vsynth3-dnxhd-1080i-colr | 2 +- 9 files changed, 70 insertions(+), 32 deletions(-) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 067d38b14b..f2aa141fb7 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -1679,17 +1679,18 @@ static int mov_write_apvc_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra } /* also used by all avid codecs (dv, imx, meridien) and their variants */ +/* https://community.avid.com/forums/t/136517.aspx */ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track) { int interlaced; int cid; int display_width = track->par->width; + const uint8_t *extradata; if (track->extradata[track->last_stsd_index] && track->extradata_size[track->last_stsd_index] > 0x29) { if (ff_dnxhd_parse_header_prefix(track->extradata[track->last_stsd_index]) != 0) { /* looks like a DNxHD bit stream */ - interlaced = (track->extradata[track->last_stsd_index][5] & 2); - cid = AV_RB32(track->extradata[track->last_stsd_index] + 0x28); + extradata = track->extradata[track->last_stsd_index]; } else { av_log(NULL, AV_LOG_WARNING, "Could not locate DNxHD bit stream in vos_data\n"); return 0; @@ -1699,60 +1700,97 @@ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track) return 0; } + cid = AV_RB32(extradata + 0x28); + avio_wb32(pb, 24); /* size */ ffio_wfourcc(pb, "ACLR"); ffio_wfourcc(pb, "ACLR"); ffio_wfourcc(pb, "0001"); + // 1: CCIR (supercolors will be dropped, 16 will be displayed as black) + // 2: FullRange (0 will be displayed as black, 16 will be displayed as dark grey) if (track->par->color_range == AVCOL_RANGE_MPEG || /* Legal range (16-235) */ track->par->color_range == AVCOL_RANGE_UNSPECIFIED) { - avio_wb32(pb, 1); /* Corresponds to 709 in official encoder */ - } else { /* Full range (0-255) */ - avio_wb32(pb, 2); /* Corresponds to RGB in official encoder */ + avio_wb32(pb, 1); + } else { + avio_wb32(pb, 2); } - avio_wb32(pb, 0); /* unknown */ + avio_wb32(pb, 0); /* reserved */ if (track->tag == MKTAG('A','V','d','h')) { + int alp = extradata[0x07] & 1; + int pma = (extradata[0x07] >> 2) & 1; + int sbd = (extradata[0x21] >> 5) & 3; + int ssc = (extradata[0x2C] >> 5) & 3; + int clv = (extradata[0x2C] >> 1) & 3; + int clf = extradata[0x2C] & 1; + avio_wb32(pb, 32); ffio_wfourcc(pb, "ADHR"); ffio_wfourcc(pb, "0001"); - avio_wb32(pb, cid); - avio_wb32(pb, 0); /* unknown */ - avio_wb32(pb, 1); /* unknown */ - avio_wb32(pb, 0); /* unknown */ - avio_wb32(pb, 0); /* unknown */ + avio_wb32(pb, cid); // Compression ID + // 0: 4:2:2 Sub Sampling + // 1: 4:2:0 Sub Sampling + // 2: 4:4:4 Sub Sampling + avio_wb32(pb, ssc); // Sub Sampling Control + // 1: 8-bits per sample + // 2: 10-bits per sample + // 3: 12-bits per sample + avio_wb32(pb, sbd); // Sample Bit Depth + // 0: Bitstream is encoded using the YCBCR format rules and tables + // 1: Bitstream is encoded using the RGB format rules and tables – only Compression IDs 1256, 1270 + avio_wb16(pb, clf); // Color Format + // 0: ITU-R BT.709 + // 1: ITU-R BT.2020 + // 2: ITU-R BT.2020 C + // 3: Out-of-band + avio_wb16(pb, clv); // Color Volume + // 0: Alpha channel not present + // 1: Alpha channel present + avio_wb16(pb, alp); // Alpha Present + // 0: Alpha has not been applied to video channels + // 1: Alpha has been applied to the video channels prior to encoding + avio_wb16(pb, pma); // Pre-Multiplied Alpha return 0; } + interlaced = extradata[5] & 2; + avio_wb32(pb, 24); /* size */ ffio_wfourcc(pb, "APRG"); ffio_wfourcc(pb, "APRG"); ffio_wfourcc(pb, "0001"); - avio_wb32(pb, 1); /* unknown */ - avio_wb32(pb, 0); /* unknown */ + // 1 for progressive or 2 for interlaced + if (interlaced) + avio_wb32(pb, 2); + else + avio_wb32(pb, 1); + avio_wb32(pb, 0); /* reserved */ avio_wb32(pb, 120); /* size */ ffio_wfourcc(pb, "ARES"); ffio_wfourcc(pb, "ARES"); ffio_wfourcc(pb, "0001"); - avio_wb32(pb, cid); /* dnxhd cid, some id ? */ + avio_wb32(pb, cid); /* cid */ if ( track->par->sample_aspect_ratio.num > 0 && track->par->sample_aspect_ratio.den > 0) display_width = display_width * track->par->sample_aspect_ratio.num / track->par->sample_aspect_ratio.den; - avio_wb32(pb, display_width); - /* values below are based on samples created with quicktime and avid codecs */ + avio_wb32(pb, display_width); // field width if (interlaced) { - avio_wb32(pb, track->par->height / 2); - avio_wb32(pb, 2); /* unknown */ - avio_wb32(pb, 0); /* unknown */ - avio_wb32(pb, 4); /* unknown */ + avio_wb32(pb, track->par->height / 2); // field height + avio_wb32(pb, 2); // num fields + avio_wb32(pb, 0); // num black lines (must be 0) + // 4: HD1080i + // 5: HD1080P + // 6: HD720P + avio_wb32(pb, 4); // video format } else { avio_wb32(pb, track->par->height); - avio_wb32(pb, 1); /* unknown */ - avio_wb32(pb, 0); /* unknown */ + avio_wb32(pb, 1); // num fields + avio_wb32(pb, 0); if (track->par->height == 1080) - avio_wb32(pb, 5); /* unknown */ + avio_wb32(pb, 5); else - avio_wb32(pb, 6); /* unknown */ + avio_wb32(pb, 6); } /* padding */ ffio_fill(pb, 0, 10 * 8); diff --git a/tests/ref/vsynth/vsynth1-dnxhd-1080i b/tests/ref/vsynth/vsynth1-dnxhd-1080i index 0e7844b0ee..ce08c85156 100644 --- a/tests/ref/vsynth/vsynth1-dnxhd-1080i +++ b/tests/ref/vsynth/vsynth1-dnxhd-1080i @@ -1,4 +1,4 @@ -af5cbe239839f6282a68f1a106ed3a77 *tests/data/fate/vsynth1-dnxhd-1080i.mov +ac2ec2b1e8daef96715c7032db3447de *tests/data/fate/vsynth1-dnxhd-1080i.mov 3031911 tests/data/fate/vsynth1-dnxhd-1080i.mov fed9ed2a5179c9df0ef58772b025e303 *tests/data/fate/vsynth1-dnxhd-1080i.out.rawvideo stddev: 6.18 PSNR: 32.31 MAXDIFF: 64 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit b/tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit index c3f3fda085..8dc3e50fd5 100644 --- a/tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit +++ b/tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit @@ -1,4 +1,4 @@ -1a8261120bcc764a7bbdd198febff4c7 *tests/data/fate/vsynth1-dnxhd-1080i-10bit.mov +4b7ccdc290bd06cfcaf29baad55081fa *tests/data/fate/vsynth1-dnxhd-1080i-10bit.mov 4588391 tests/data/fate/vsynth1-dnxhd-1080i-10bit.mov 31032fcb7e6af79daaac02288254c6d6 *tests/data/fate/vsynth1-dnxhd-1080i-10bit.out.rawvideo stddev: 5.69 PSNR: 33.02 MAXDIFF: 55 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr index 35cd719b63..a07f6b375c 100644 --- a/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr +++ b/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr @@ -1,4 +1,4 @@ -5571f4ff9e29d352a7e373a14a9ed2ed *tests/data/fate/vsynth1-dnxhd-1080i-colr.mov +3bf2c5863bf8a46365e5ead657d01796 *tests/data/fate/vsynth1-dnxhd-1080i-colr.mov 3031929 tests/data/fate/vsynth1-dnxhd-1080i-colr.mov 6f2d5429ffc4529a76acfeb28b560542 *tests/data/fate/vsynth1-dnxhd-1080i-colr.out.rawvideo stddev: 5.65 PSNR: 33.09 MAXDIFF: 55 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth2-dnxhd-1080i b/tests/ref/vsynth/vsynth2-dnxhd-1080i index 0668a44f9a..619d51a33e 100644 --- a/tests/ref/vsynth/vsynth2-dnxhd-1080i +++ b/tests/ref/vsynth/vsynth2-dnxhd-1080i @@ -1,4 +1,4 @@ -0067903558c99e3abed402ed65297735 *tests/data/fate/vsynth2-dnxhd-1080i.mov +16784dcb2a944d681ee8125744598a00 *tests/data/fate/vsynth2-dnxhd-1080i.mov 3031911 tests/data/fate/vsynth2-dnxhd-1080i.mov e941d2587cfeccddc450da7f41f7f911 *tests/data/fate/vsynth2-dnxhd-1080i.out.rawvideo stddev: 1.50 PSNR: 44.56 MAXDIFF: 31 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit b/tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit index 7689e2c22e..0ec33c68e8 100644 --- a/tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit +++ b/tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit @@ -1,4 +1,4 @@ -16e86953a697e1e7f9d80903ff4fef0c *tests/data/fate/vsynth2-dnxhd-1080i-10bit.mov +f72fd4fb2f8969ae51809a049bacc036 *tests/data/fate/vsynth2-dnxhd-1080i-10bit.mov 4588391 tests/data/fate/vsynth2-dnxhd-1080i-10bit.mov e4ca9be476869afb94962d945f90bdf6 *tests/data/fate/vsynth2-dnxhd-1080i-10bit.out.rawvideo stddev: 1.57 PSNR: 44.18 MAXDIFF: 33 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr index f5ac604716..3029a6f72a 100644 --- a/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr +++ b/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr @@ -1,4 +1,4 @@ -eee674d012c850c1d2bb5e816b668cdf *tests/data/fate/vsynth2-dnxhd-1080i-colr.mov +64957f9ac88f32c175f22106fe60d291 *tests/data/fate/vsynth2-dnxhd-1080i-colr.mov 3031929 tests/data/fate/vsynth2-dnxhd-1080i-colr.mov ec40a8014b819d02951b2f06bee7b514 *tests/data/fate/vsynth2-dnxhd-1080i-colr.out.rawvideo stddev: 1.54 PSNR: 44.33 MAXDIFF: 33 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit b/tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit index 2a040e12db..51e46f7450 100644 --- a/tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit +++ b/tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit @@ -1,4 +1,4 @@ -4e6185e273297061def8e0b7fabff71b *tests/data/fate/vsynth3-dnxhd-1080i-10bit.mov +ec15292ebe59baa20dea7a9c8fd64237 *tests/data/fate/vsynth3-dnxhd-1080i-10bit.mov 4588391 tests/data/fate/vsynth3-dnxhd-1080i-10bit.mov c192f36ef8687e56c72a3dc416c7e191 *tests/data/fate/vsynth3-dnxhd-1080i-10bit.out.rawvideo stddev: 6.92 PSNR: 31.32 MAXDIFF: 50 bytes: 86700/ 8670 diff --git a/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr index 08aa30d8c4..920e86ccec 100644 --- a/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr +++ b/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr @@ -1,4 +1,4 @@ -92a2f67cf77abf3428fe2d4f53ba2027 *tests/data/fate/vsynth3-dnxhd-1080i-colr.mov +32cf64f3043053d3003388ab12f53cbe *tests/data/fate/vsynth3-dnxhd-1080i-colr.mov 3031929 tests/data/fate/vsynth3-dnxhd-1080i-colr.mov f907fd2d48bedbc5283fbfc3fb9f61a0 *tests/data/fate/vsynth3-dnxhd-1080i-colr.out.rawvideo stddev: 6.92 PSNR: 31.32 MAXDIFF: 50 bytes: 86700/ 8670 -- 2.49.1 From d986e9525424104dbafb2c68702532b07b417a07 Mon Sep 17 00:00:00 2001 From: Baptiste Coudurier <[email protected]> Date: Wed, 4 Jun 2025 10:43:04 -0700 Subject: [PATCH 2/7] lavf/mxfenc: require pixel format to be set for video streams --- libavformat/mxfenc.c | 56 +++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index b14a480ba3..ed315be140 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -1493,11 +1493,6 @@ static int mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st) int64_t pos; const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format); - if (!pix_desc) { - av_log(s, AV_LOG_ERROR, "Pixel format not set - not writing JPEG2000SubDescriptor\n"); - return AVERROR(EINVAL); - } - /* JPEG2000 subdescriptor key */ avio_write(pb, mxf_jpeg2000_subdescriptor_key, 16); klv_encode_ber4_length(pb, 0); @@ -1610,7 +1605,6 @@ static int mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st) { int is_rgb, pos; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codecpar->format); - av_assert0(desc); is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB; pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key); @@ -2692,11 +2686,6 @@ static int mxf_parse_jpeg2000_frame(AVFormatContext *s, AVStream *st, AVPacket * GetByteContext g; uint32_t j2k_ncomponents; - if (!pix_desc) { - av_log(s, AV_LOG_ERROR, "Pixel format not set\n"); - return AVERROR(EINVAL); - } - if (mxf->header_written) return 1; @@ -2898,20 +2887,18 @@ static enum AVChromaLocation choose_chroma_location(AVFormatContext *s, AVStream if (par->chroma_location != AVCHROMA_LOC_UNSPECIFIED) return par->chroma_location; - if (pix_desc) { - if (pix_desc->log2_chroma_h == 0) { - return AVCHROMA_LOC_TOPLEFT; - } else if (pix_desc->log2_chroma_w == 1 && pix_desc->log2_chroma_h == 1) { - if (par->field_order == AV_FIELD_UNKNOWN || par->field_order == AV_FIELD_PROGRESSIVE) { - switch (par->codec_id) { - case AV_CODEC_ID_MJPEG: - case AV_CODEC_ID_MPEG1VIDEO: return AVCHROMA_LOC_CENTER; - } + if (pix_desc->log2_chroma_h == 0) { + return AVCHROMA_LOC_TOPLEFT; + } else if (pix_desc->log2_chroma_w == 1 && pix_desc->log2_chroma_h == 1) { + if (par->field_order == AV_FIELD_UNKNOWN || par->field_order == AV_FIELD_PROGRESSIVE) { + switch (par->codec_id) { + case AV_CODEC_ID_MJPEG: + case AV_CODEC_ID_MPEG1VIDEO: return AVCHROMA_LOC_CENTER; } - if (par->field_order == AV_FIELD_UNKNOWN || par->field_order != AV_FIELD_PROGRESSIVE) { - switch (par->codec_id) { - case AV_CODEC_ID_MPEG2VIDEO: return AVCHROMA_LOC_LEFT; - } + } + if (par->field_order == AV_FIELD_UNKNOWN || par->field_order != AV_FIELD_PROGRESSIVE) { + switch (par->codec_id) { + case AV_CODEC_ID_MPEG2VIDEO: return AVCHROMA_LOC_LEFT; } } } @@ -2950,27 +2937,26 @@ static int mxf_init(AVFormatContext *s) if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format); AVRational tbc = (AVRational){ 0, 0 }; + + if (!pix_desc) { + av_log(s, AV_LOG_ERROR, "video stream require the codec pixel format to be set\n"); + return -1; + } + if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) tbc = av_inv_q(st->avg_frame_rate); else if (st->r_frame_rate.num > 0 && st->r_frame_rate.den > 0) tbc = av_inv_q(st->r_frame_rate); - // Default component depth to 8 - sc->component_depth = 8; - sc->h_chroma_sub_sample = 2; - sc->v_chroma_sub_sample = 2; - sc->color_siting = 0xFF; - if (st->codecpar->sample_aspect_ratio.num && st->codecpar->sample_aspect_ratio.den) { sc->aspect_ratio = av_mul_q(st->codecpar->sample_aspect_ratio, av_make_q(st->codecpar->width, st->codecpar->height)); } - if (pix_desc) { - sc->component_depth = pix_desc->comp[0].depth; - sc->h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w; - sc->v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h; - } + sc->component_depth = pix_desc->comp[0].depth; + sc->h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w; + sc->v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h; + switch (choose_chroma_location(s, st)) { case AVCHROMA_LOC_TOPLEFT: sc->color_siting = 0; break; case AVCHROMA_LOC_LEFT: sc->color_siting = 6; break; -- 2.49.1 From 6a26192e536e5eb49b269ce4be496968b693342c Mon Sep 17 00:00:00 2001 From: Baptiste Coudurier <[email protected]> Date: Tue, 3 Jun 2025 23:11:43 -0700 Subject: [PATCH 3/7] lavf/mxfenc: remove cdci pixel format specific values from stream context --- libavformat/mxfenc.c | 64 +++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index ed315be140..30d8cc1c27 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -106,11 +106,7 @@ typedef struct MXFStreamContext { int order; ///< interleaving order if dts are equal int interlaced; ///< whether picture is interlaced int field_dominance; ///< tff=1, bff=2 - int component_depth; - int color_siting; int signal_standard; - int h_chroma_sub_sample; - int v_chroma_sub_sample; int temporal_reordering; AVRational aspect_ratio; ///< display aspect ratio int closed_gop; ///< gop is closed, used in mpeg-2 frame parsing @@ -180,6 +176,8 @@ static int mxf_write_cdci_desc(AVFormatContext *s, AVStream *st); static int mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st); static int mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st); +static enum AVChromaLocation choose_chroma_location(AVFormatContext *s, AVStream *st); + static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, @@ -1207,6 +1205,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID { MXFStreamContext *sc = st->priv_data; AVIOContext *pb = s->pb; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format); int stored_width = st->codecpar->width; int stored_height = st->codecpar->height; int display_width; @@ -1309,21 +1308,37 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID } if (key != mxf_rgba_descriptor_key) { + int component_depth = pix_desc->comp[0].depth; + int h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w; + int v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h; + int color_siting; + // component depth mxf_write_local_tag(s, 4, 0x3301); - avio_wb32(pb, sc->component_depth); + avio_wb32(pb, component_depth); // horizontal subsampling mxf_write_local_tag(s, 4, 0x3302); - avio_wb32(pb, sc->h_chroma_sub_sample); + avio_wb32(pb, h_chroma_sub_sample); // vertical subsampling mxf_write_local_tag(s, 4, 0x3308); - avio_wb32(pb, sc->v_chroma_sub_sample); + avio_wb32(pb, v_chroma_sub_sample); + + switch (choose_chroma_location(s, st)) { + case AVCHROMA_LOC_TOPLEFT: color_siting = 0; break; + case AVCHROMA_LOC_LEFT: color_siting = 6; break; + case AVCHROMA_LOC_TOP: color_siting = 1; break; + case AVCHROMA_LOC_CENTER: color_siting = 3; break; + default: color_siting = 0xff; + } + + if (IS_D10(s)) + color_siting = 0; // color siting is specified to be 0 in d-10 specs // color siting mxf_write_local_tag(s, 1, 0x3303); - avio_w8(pb, sc->color_siting); + avio_w8(pb, color_siting); // Padding Bits mxf_write_local_tag(s, 2, 0x3307); @@ -1331,12 +1346,12 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID if (st->codecpar->color_range != AVCOL_RANGE_UNSPECIFIED) { int black = 0, - white = (1<<sc->component_depth) - 1, - color = (1<<sc->component_depth); + white = (1<<component_depth) - 1, + color = (1<<component_depth); if (st->codecpar->color_range == AVCOL_RANGE_MPEG) { - black = 1 << (sc->component_depth - 4); - white = 235 << (sc->component_depth - 8); - color = (14 << (sc->component_depth - 4)) + 1; + black = 1 << (component_depth - 4); + white = 235 << (component_depth - 8); + color = (14 << (component_depth - 4)) + 1; } mxf_write_local_tag(s, 4, 0x3304); avio_wb32(pb, black); @@ -2354,15 +2369,6 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt if (i == FF_ARRAY_ELEMS(mxf_dnxhd_codec_uls)) return 0; - sc->component_depth = 0; - switch (pkt->data[0x21] >> 5) { - case 1: sc->component_depth = 8; break; - case 2: sc->component_depth = 10; break; - case 3: sc->component_depth = 12; break; - } - if (!sc->component_depth) - return 0; - if (cid >= 1270) { // RI raster av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den, st->codecpar->width, st->codecpar->height, @@ -2536,7 +2542,6 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, 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; @@ -2581,7 +2586,6 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, 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) { 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; @@ -2953,17 +2957,6 @@ static int mxf_init(AVFormatContext *s) av_make_q(st->codecpar->width, st->codecpar->height)); } - sc->component_depth = pix_desc->comp[0].depth; - sc->h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w; - sc->v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h; - - switch (choose_chroma_location(s, st)) { - case AVCHROMA_LOC_TOPLEFT: sc->color_siting = 0; break; - case AVCHROMA_LOC_LEFT: sc->color_siting = 6; break; - case AVCHROMA_LOC_TOP: sc->color_siting = 1; break; - case AVCHROMA_LOC_CENTER: sc->color_siting = 3; break; - } - mxf->content_package_rate = ff_mxf_get_content_package_rate(tbc); mxf->time_base = tbc; avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den); @@ -3006,7 +2999,6 @@ static int mxf_init(AVFormatContext *s) sc->container_ul = &mxf_d10_container_uls[ul_index]; sc->index = INDEX_D10_VIDEO; sc->signal_standard = 1; - sc->color_siting = 0; sc->frame_size = (int64_t)sc->video_bit_rate * mxf->time_base.num / (8*mxf->time_base.den); } -- 2.49.1 From 792e479ec258fe027faa4b61a15b0fa7dc5aea2f Mon Sep 17 00:00:00 2001 From: Baptiste Coudurier <[email protected]> Date: Wed, 4 Jun 2025 11:24:41 -0700 Subject: [PATCH 4/7] lavf/mxfenc: correctly add jpeg2000 subdescriptor reference in cdci, factorize --- libavformat/mxfenc.c | 50 +++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 30d8cc1c27..b935361599 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -117,9 +117,9 @@ 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; int micro_version; ///< format micro_version, used in ffv1 descriptor j2k_info_t j2k_info; + enum MXFMetadataSetType sub_descriptor; } MXFStreamContext; typedef struct MXFContainerEssenceEntry { @@ -417,8 +417,9 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 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 */ { 0x5003, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x0A,0x01,0x00,0x00}}, /* Value */ - // mxf_avc_subdescriptor_local_tags + // sub descriptor used AVC, JPEG2000 and FFV1 { 0x8100, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00}}, /* SubDescriptors */ + // mxf_avc_subdescriptor_local_tags { 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 */ @@ -432,7 +433,6 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 0xDFDA, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x05,0x00,0x00,0x00}}, /* FFV1 Version */ { 0xDFDB, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x01,0x00,0x00,0x00}}, /* FFV1 Initialization Metadata */ // ff_mxf_jpeg2000_local_tags - { 0x8400, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00}}, /* Sub Descriptors / Opt Ordered array of strong references to sub descriptor sets */ { 0x8401, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x01,0x00,0x00,0x00}}, /* Rsiz: An enumerated value that defines the decoder capabilities */ { 0x8402, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x02,0x00,0x00,0x00}}, /* Xsiz: Width of the reference grid */ { 0x8403, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x03,0x00,0x00,0x00}}, /* Ysiz: Height of the reference grid */ @@ -582,10 +582,11 @@ static void mxf_write_primer_pack(AVFormatContext *s) AVIOContext *pb = s->pb; int local_tag_number = MXF_NUM_TAGS, i; int will_have_avc_tags = 0, will_have_mastering_tags = 0, will_have_ffv1_tags = 0, will_have_jpeg2000_tags = 0; + int will_have_sub_descriptor = 0; 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) { + if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264 && sc->sub_descriptor) { will_have_avc_tags = 1; } if (av_packet_side_data_get(s->streams[i]->codecpar->coded_side_data, @@ -599,6 +600,9 @@ static void mxf_write_primer_pack(AVFormatContext *s) if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_JPEG2000){ will_have_jpeg2000_tags = 1; } + if (sc->sub_descriptor) { + will_have_sub_descriptor = 1; + } } if (!mxf->store_user_comments) { @@ -607,7 +611,7 @@ static void mxf_write_primer_pack(AVFormatContext *s) mxf_mark_tag_unused(mxf, 0x5003); } - if (!will_have_avc_tags && !will_have_ffv1_tags) { + if (!will_have_sub_descriptor) { mxf_mark_tag_unused(mxf, 0x8100); } @@ -631,7 +635,6 @@ static void mxf_write_primer_pack(AVFormatContext *s) } if (!will_have_jpeg2000_tags) { - mxf_mark_tag_unused(mxf, 0x8400); mxf_mark_tag_unused(mxf, 0x8401); mxf_mark_tag_unused(mxf, 0x8402); mxf_mark_tag_unused(mxf, 0x8403); @@ -1451,18 +1454,10 @@ 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 + if (sc->sub_descriptor) { mxf_write_local_tag(s, 8 + 16, 0x8100); mxf_write_refs_count(pb, 1); - mxf_write_uuid(pb, AVCSubDescriptor, 0); - } - - if (st->codecpar->codec_id == AV_CODEC_ID_FFV1) { - // write ffv1 sub descriptor ref - mxf_write_local_tag(s, 8 + 16, 0x8100); - mxf_write_refs_count(pb, 1); - mxf_write_uuid(pb, FFV1SubDescriptor, 0); + mxf_write_uuid(pb, sc->sub_descriptor, 0); } return pos; @@ -1561,22 +1556,22 @@ static int mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st) static int mxf_write_cdci_desc(AVFormatContext *s, AVStream *st) { + MXFStreamContext *sc = st->priv_data; 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); - } - if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) { - return mxf_write_jpeg2000_subdesc(s, st); + switch (sc->sub_descriptor) { + case AVCSubDescriptor: mxf_write_avc_subdesc(s, st); break; + case JPEG2000SubDescriptor: mxf_write_jpeg2000_subdesc(s, st); break; } + return 0; } static int mxf_write_h264_desc(AVFormatContext *s, AVStream *st) { MXFStreamContext *sc = st->priv_data; - if (sc->avc_intra) { + if (!sc->sub_descriptor) { mxf_write_mpegvideo_desc(s, st); } else { int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key); @@ -2514,7 +2509,7 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, const UID *codec_ul = NULL; uint32_t state = -1; int extra_size = 512; // support AVC Intra files without SPS/PPS header - int i, frame_size, slice_type, has_sps = 0, intra_only = 0, ret; + int i, frame_size, slice_type, has_sps = 0, intra_only = 0, avc_intra = 0, ret; for (;;) { buf = avpriv_find_start_code(buf, buf_end, &state); @@ -2588,7 +2583,7 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, codec_ul = &mxf_h264_codec_uls[i].uid; 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; + avc_intra = 1; mxf->cbr_index = 1; sc->frame_size = pkt->size; if (sc->interlaced) @@ -2609,8 +2604,13 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, av_log(s, AV_LOG_ERROR, "h264 profile not supported\n"); return 0; } + sc->codec_ul = codec_ul; + if (!avc_intra) { + sc->sub_descriptor = AVCSubDescriptor; + } + return 1; } @@ -2669,6 +2669,7 @@ static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) av_assert0(v < 2); } sc->codec_ul = &mxf_ffv1_codec_uls[v]; + sc->sub_descriptor = FFV1SubDescriptor; if (st->codecpar->field_order > AV_FIELD_PROGRESSIVE) { sc->interlaced = 1; @@ -2726,6 +2727,7 @@ static int mxf_parse_jpeg2000_frame(AVFormatContext *s, AVStream *st, AVPacket * bytestream2_get_bufferu(&g, sc->j2k_info.j2k_comp_desc, 3 * j2k_ncomponents); sc->frame_size = pkt->size; + sc->sub_descriptor = JPEG2000SubDescriptor; return 1; } -- 2.49.1 From 0332f44c438d858760b510b2faaadcbe09c22d6d Mon Sep 17 00:00:00 2001 From: Baptiste Coudurier <[email protected]> Date: Wed, 4 Jun 2025 11:35:31 -0700 Subject: [PATCH 5/7] lavf/mxfenc: fix return value to int64_t --- libavformat/mxfenc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index b935361599..2b37675938 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -1613,11 +1613,10 @@ static int mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st) static int mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st) { - int is_rgb, pos; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codecpar->format); - is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB; + int is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB; - pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key); + int64_t pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key); mxf_update_klv_size(s->pb, pos); return mxf_write_ffv1_subdesc(s, st); } -- 2.49.1 From 09cc306bf4de0a90febd800574a0377d489e3ccc Mon Sep 17 00:00:00 2001 From: Baptiste Coudurier <[email protected]> Date: Wed, 4 Jun 2025 14:09:50 -0700 Subject: [PATCH 6/7] lavf/mxfenc: factorize cdci descriptor functions --- libavformat/mxfenc.c | 110 +++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 62 deletions(-) diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 2b37675938..8cd92b980c 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -120,6 +120,7 @@ typedef struct MXFStreamContext { int micro_version; ///< format micro_version, used in ffv1 descriptor j2k_info_t j2k_info; enum MXFMetadataSetType sub_descriptor; + const UID *picture_descriptor_key; } MXFStreamContext; typedef struct MXFContainerEssenceEntry { @@ -169,20 +170,22 @@ static const struct { static int mxf_write_wav_desc(AVFormatContext *s, AVStream *st); static int mxf_write_aes3_desc(AVFormatContext *s, AVStream *st); -static int mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st); -static int mxf_write_h264_desc(AVFormatContext *s, AVStream *st); -static int mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st); static int mxf_write_cdci_desc(AVFormatContext *s, AVStream *st); static int mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st); static int mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st); +static void mxf_write_avc_subdesc(AVFormatContext *s, AVStream *st); +static void mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st); + +static int mxf_write_mpegvideo_local_tags(AVFormatContext *s, AVStream *st); + static enum AVChromaLocation choose_chroma_location(AVFormatContext *s, AVStream *st); static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,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,0x03,0x04,0x01,0x02,0x02,0x01,0x00,0x00,0x00 }, - mxf_write_mpegvideo_desc }, + mxf_write_cdci_desc }, { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x03,0x00 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x16,0x01,0x03,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, @@ -220,7 +223,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_h264_desc }, + mxf_write_cdci_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 }, @@ -235,7 +238,7 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x23,0x01,0x00 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x1d,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x09,0x00,0x00 }, - mxf_write_ffv1_desc }, + mxf_write_cdci_desc }, { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, @@ -1204,7 +1207,7 @@ static inline uint32_t rescale_mastering_luma(AVRational q) return av_rescale(q.num, FF_MXF_MASTERING_LUMA_DEN, q.den); } -static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID key) +static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st) { MXFStreamContext *sc = st->priv_data; AVIOContext *pb = s->pb; @@ -1217,7 +1220,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID const MXFCodecUL *color_primaries_ul; const MXFCodecUL *color_trc_ul; const MXFCodecUL *color_space_ul; - int64_t pos = mxf_write_generic_desc(s, st, key); + int64_t pos = mxf_write_generic_desc(s, st, *sc->picture_descriptor_key); const AVPacketSideData *side_data; color_primaries_ul = mxf_get_codec_ul_by_id(ff_mxf_color_primaries_uls, st->codecpar->color_primaries); @@ -1310,7 +1313,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID avio_wb32(pb, -((st->codecpar->height - display_height)&1)); } - if (key != mxf_rgba_descriptor_key) { + if (sc->picture_descriptor_key != &mxf_rgba_descriptor_key) { int component_depth = pix_desc->comp[0].depth; int h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w; int v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h; @@ -1460,6 +1463,9 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID mxf_write_uuid(pb, sc->sub_descriptor, 0); } + if (sc->picture_descriptor_key == &mxf_mpegvideo_descriptor_key) + mxf_write_mpegvideo_local_tags(s, st); + return pos; } @@ -1557,31 +1563,19 @@ static int mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st) static int mxf_write_cdci_desc(AVFormatContext *s, AVStream *st) { MXFStreamContext *sc = st->priv_data; - int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key); + int64_t pos = mxf_write_generic_picture_desc(s, st); mxf_update_klv_size(s->pb, pos); switch (sc->sub_descriptor) { case AVCSubDescriptor: mxf_write_avc_subdesc(s, st); break; case JPEG2000SubDescriptor: mxf_write_jpeg2000_subdesc(s, st); break; + case FFV1SubDescriptor: mxf_write_ffv1_subdesc(s, st); break; } return 0; } -static int mxf_write_h264_desc(AVFormatContext *s, AVStream *st) -{ - MXFStreamContext *sc = st->priv_data; - if (!sc->sub_descriptor) { - 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); - } - return 0; -} - -static int mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st) +static void mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st) { AVIOContext *pb = s->pb; MXFStreamContext *sc = st->priv_data; @@ -1608,17 +1602,6 @@ static int mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st) } mxf_update_klv_size(s->pb, pos); - return 0; -} - -static int mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codecpar->format); - int is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB; - - int64_t pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key); - mxf_update_klv_size(s->pb, pos); - return mxf_write_ffv1_subdesc(s, st); } static int mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st) @@ -1628,42 +1611,38 @@ static int mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st) return 0; } -static int mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st) +static int mxf_write_mpegvideo_local_tags(AVFormatContext *s, AVStream *st) { AVIOContext *pb = s->pb; MXFStreamContext *sc = st->priv_data; int profile_and_level = (st->codecpar->profile<<4) | st->codecpar->level; - int64_t pos = mxf_write_cdci_common(s, st, mxf_mpegvideo_descriptor_key); - if (st->codecpar->codec_id != AV_CODEC_ID_H264) { - // bit rate - mxf_write_local_tag(s, 4, 0x8000); - avio_wb32(pb, sc->video_bit_rate); + // bit rate + mxf_write_local_tag(s, 4, 0x8000); + avio_wb32(pb, sc->video_bit_rate); - // profile and level - mxf_write_local_tag(s, 1, 0x8007); - if (!st->codecpar->profile) - profile_and_level |= 0x80; // escape bit - avio_w8(pb, profile_and_level); + // profile and level + mxf_write_local_tag(s, 1, 0x8007); + if (!st->codecpar->profile) + profile_and_level |= 0x80; // escape bit + avio_w8(pb, profile_and_level); - // low delay - mxf_write_local_tag(s, 1, 0x8003); - avio_w8(pb, sc->low_delay); + // low delay + mxf_write_local_tag(s, 1, 0x8003); + avio_w8(pb, sc->low_delay); - // closed gop - mxf_write_local_tag(s, 1, 0x8004); - avio_w8(pb, sc->seq_closed_gop); + // closed gop + mxf_write_local_tag(s, 1, 0x8004); + avio_w8(pb, sc->seq_closed_gop); - // max gop - mxf_write_local_tag(s, 2, 0x8006); - avio_wb16(pb, sc->max_gop); + // max gop + mxf_write_local_tag(s, 2, 0x8006); + avio_wb16(pb, sc->max_gop); - // b picture count - mxf_write_local_tag(s, 2, 0x8008); - avio_wb16(pb, sc->b_picture_count); - } + // b picture count + mxf_write_local_tag(s, 2, 0x8008); + avio_wb16(pb, sc->b_picture_count); - mxf_update_klv_size(pb, pos); return 0; } @@ -2606,9 +2585,10 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, sc->codec_ul = codec_ul; - if (!avc_intra) { + if (avc_intra) + sc->picture_descriptor_key = &mxf_mpegvideo_descriptor_key; + else sc->sub_descriptor = AVCSubDescriptor; - } return 1; } @@ -2639,6 +2619,7 @@ static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) { MXFContext *mxf = s->priv_data; MXFStreamContext *sc = st->priv_data; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format); uint8_t state[FFV1_CONTEXT_SIZE]; RangeCoder c; unsigned v; @@ -2679,6 +2660,9 @@ static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den, sc->aspect_ratio.num, sc->aspect_ratio.den, INT_MAX); + if (pix_desc->flags & AV_PIX_FMT_FLAG_RGB) + sc->picture_descriptor_key = &mxf_rgba_descriptor_key; + return 1; } @@ -2828,6 +2812,7 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, if (!codec_ul) return 0; sc->codec_ul = codec_ul; + sc->picture_descriptor_key = &mxf_mpegvideo_descriptor_key; } return 1; } @@ -2968,6 +2953,7 @@ static int mxf_init(AVFormatContext *s) sc->seq_closed_gop = -1; // unknown yet } + sc->picture_descriptor_key = &mxf_cdci_descriptor_key; sc->video_bit_rate = st->codecpar->bit_rate; if (IS_D10(s) || -- 2.49.1 From 2401bddbf00829a51a7964fc856cf52f7b70f1a8 Mon Sep 17 00:00:00 2001 From: Baptiste Coudurier <[email protected]> Date: Wed, 4 Jun 2025 00:01:44 -0700 Subject: [PATCH 7/7] lavf/mxfenc: fix muxing for DNxHD/DNxHR 444 --- libavformat/mxfenc.c | 70 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 8cd92b980c..12ef521779 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -121,6 +121,7 @@ typedef struct MXFStreamContext { j2k_info_t j2k_info; enum MXFMetadataSetType sub_descriptor; const UID *picture_descriptor_key; + int cid; ///< compression id, used by dnxhd } MXFStreamContext; typedef struct MXFContainerEssenceEntry { @@ -289,6 +290,8 @@ static const uint8_t header_closed_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02, static const uint8_t klv_fill_key[] = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x01,0x02,0x10,0x01,0x00,0x00,0x00 }; static const uint8_t body_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }; // ClosedComplete +static const UID mxf_rgba_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x29,0x00 }; + /** * partial key for header metadata */ @@ -390,6 +393,10 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 0x3304, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x03,0x03,0x00,0x00,0x00}}, /* Black Ref level */ { 0x3305, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x03,0x04,0x00,0x00,0x00}}, /* White Ref level */ { 0x3306, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x05,0x03,0x05,0x00,0x00,0x00}}, /* Color Range */ + // RGBA Picture Essence Descriptor + { 0x3401, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x05,0x03,0x06,0x00,0x00,0x00}}, /* Pixel Layout */ + { 0x3406, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x05,0x03,0x0B,0x00,0x00,0x00}}, /* Component Max Ref */ + { 0x3407, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x05,0x03,0x0C,0x00,0x00,0x00}}, /* Component Min Ref */ // Generic Sound Essence Descriptor { 0x3D02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x02,0x03,0x01,0x04,0x00,0x00,0x00}}, /* Locked/Unlocked */ { 0x3D03, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x01,0x01,0x01,0x00,0x00}}, /* Audio sampling rate */ @@ -585,27 +592,31 @@ static void mxf_write_primer_pack(AVFormatContext *s) AVIOContext *pb = s->pb; int local_tag_number = MXF_NUM_TAGS, i; int will_have_avc_tags = 0, will_have_mastering_tags = 0, will_have_ffv1_tags = 0, will_have_jpeg2000_tags = 0; - int will_have_sub_descriptor = 0; + int will_have_sub_descriptor = 0, will_have_rgba_descriptor = 0; 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->sub_descriptor) { + AVStream *st = s->streams[i]; + MXFStreamContext *sc = st->priv_data; + if (st->codecpar->codec_id == AV_CODEC_ID_H264 && sc->sub_descriptor) { will_have_avc_tags = 1; } - if (av_packet_side_data_get(s->streams[i]->codecpar->coded_side_data, - s->streams[i]->codecpar->nb_coded_side_data, + if (av_packet_side_data_get(st->codecpar->coded_side_data, + st->codecpar->nb_coded_side_data, AV_PKT_DATA_MASTERING_DISPLAY_METADATA)) { will_have_mastering_tags = 1; } - if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_FFV1) { + if (st->codecpar->codec_id == AV_CODEC_ID_FFV1) { will_have_ffv1_tags = 1; } - if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_JPEG2000){ + if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) { will_have_jpeg2000_tags = 1; } if (sc->sub_descriptor) { will_have_sub_descriptor = 1; } + if (sc->picture_descriptor_key == &mxf_rgba_descriptor_key) { + will_have_rgba_descriptor = 1; + } } if (!mxf->store_user_comments) { @@ -618,6 +629,12 @@ static void mxf_write_primer_pack(AVFormatContext *s) mxf_mark_tag_unused(mxf, 0x8100); } + if (!will_have_rgba_descriptor) { + mxf_mark_tag_unused(mxf, 0x3401); + mxf_mark_tag_unused(mxf, 0x3406); + mxf_mark_tag_unused(mxf, 0x3407); + } + if (!will_have_avc_tags) { mxf_mark_tag_unused(mxf, 0x8200); mxf_mark_tag_unused(mxf, 0x8201); @@ -1190,7 +1207,6 @@ static const UID mxf_mpegvideo_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53, static const UID mxf_wav_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 }; static const UID mxf_aes3_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }; 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_rgba_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x29,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 }; @@ -1222,6 +1238,7 @@ static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st) const MXFCodecUL *color_space_ul; int64_t pos = mxf_write_generic_desc(s, st, *sc->picture_descriptor_key); const AVPacketSideData *side_data; + int component_depth = pix_desc->comp[0].depth; color_primaries_ul = mxf_get_codec_ul_by_id(ff_mxf_color_primaries_uls, st->codecpar->color_primaries); color_trc_ul = mxf_get_codec_ul_by_id(ff_mxf_color_trc_uls, st->codecpar->color_trc); @@ -1236,6 +1253,10 @@ static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st) display_width = stored_width; switch (st->codecpar->codec_id) { + case AV_CODEC_ID_DNXHD: + if (sc->cid < 1270) // DNxHD + break; + // fall for DNxHR RI rasters case AV_CODEC_ID_MPEG2VIDEO: case AV_CODEC_ID_H264: //Based on 16x16 macroblocks @@ -1313,8 +1334,25 @@ static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st) avio_wb32(pb, -((st->codecpar->height - display_height)&1)); } - if (sc->picture_descriptor_key != &mxf_rgba_descriptor_key) { - int component_depth = pix_desc->comp[0].depth; + if (sc->picture_descriptor_key == &mxf_rgba_descriptor_key) { + uint8_t rgb_layout[16] = { + 'R', component_depth, + 'G', component_depth, + 'B', component_depth, + }; + + // pixel layout + mxf_write_local_tag(s, 16, 0x3401); + avio_write(pb, rgb_layout, 16); + + // component max ref + mxf_write_local_tag(s, 4, 0x3406); + avio_wb32(pb, (1<<component_depth) - 1); + + // component min ref + mxf_write_local_tag(s, 4, 0x3407); + avio_wb32(pb, 0); + } else { int h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w; int v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h; int color_siting; @@ -1393,7 +1431,6 @@ static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st) f1 *= 2; } - mxf_write_local_tag(s, 16, 0x320D); avio_wb32(pb, 2); avio_wb32(pb, 4); @@ -2323,7 +2360,7 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt { MXFContext *mxf = s->priv_data; MXFStreamContext *sc = st->priv_data; - int i, cid; + int i; if (mxf->header_written) return 1; @@ -2331,9 +2368,9 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt if (pkt->size < 43) return 0; - cid = AV_RB32(pkt->data + 0x28); + sc->cid = AV_RB32(pkt->data + 0x28); for (i = 0; i < FF_ARRAY_ELEMS(mxf_dnxhd_codec_uls); i++) { - if (cid == mxf_dnxhd_codec_uls[i].cid) { + if (sc->cid == mxf_dnxhd_codec_uls[i].cid) { sc->codec_ul = &mxf_dnxhd_codec_uls[i].codec_ul; sc->interlaced = mxf_dnxhd_codec_uls[i].interlaced; break; @@ -2342,7 +2379,7 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt if (i == FF_ARRAY_ELEMS(mxf_dnxhd_codec_uls)) return 0; - if (cid >= 1270) { // RI raster + if (sc->cid >= 1270) { // RI raster av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den, st->codecpar->width, st->codecpar->height, INT_MAX); @@ -2352,6 +2389,9 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt sc->frame_size = pkt->size; + if (sc->cid == 1270 || sc->cid == 1256) + sc->picture_descriptor_key = &mxf_rgba_descriptor_key; + return 1; } -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
