diff mbox series

[FFmpeg-devel,MXF] - Add jpeg2000 subdescriptor in MXF file.

Message ID 7c1649cd-8b35-47fa-89d6-f7dfc11350e4@ektacom.com
State New
Headers show
Series [FFmpeg-devel,MXF] - Add jpeg2000 subdescriptor in MXF file. | expand

Checks

Context Check Description
andriy/commit_msg_x86 warning The first line of the commit message must start with a context terminated by a colon and a space, for example "lavu/opt: " or "doc: ".
yinshiyou/commit_msg_loongarch64 warning The first line of the commit message must start with a context terminated by a colon and a space, for example "lavu/opt: " or "doc: ".
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Cédric Le Barz Dec. 8, 2023, 4:25 p.m. UTC
Add jpeg2000 subdescriptor in MXF file.
Signed-off-by: Cedric Le Barz <clebarz@ektacom.com>
---
 ffmpeg/libavformat/mxf.h    |   1 +
 ffmpeg/libavformat/mxfenc.c | 173 +++++++++++++++++++++++++++++++++++-
 2 files changed, 173 insertions(+), 1 deletion(-)

Comments

Tomas Härdin Dec. 19, 2023, 1:36 p.m. UTC | #1
> +    for ( comp = 0; comp < component_count; comp++ ) {
> +        avio_write(pb, &sc->j2k_info.j2k_comp_desc[3*comp] , 3);
> +    }

Looks like this could be simplified to just

  avio_write(pb, sc->j2k_info.j2k_comp_desc, 3*component_count);

> +    if (j2k_ncomponents != component_count) {
> +        av_log(s, AV_LOG_ERROR, "Incoherence about components image
> number.\n");
> +    }

I again feel this should be a hard error

> +    for (comp = 0; comp < j2k_ncomponents; comp++) {
> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents] =
> bytestream2_get_byteu(&g);   // Bitdepth for each component
> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+1] =
> bytestream2_get_byteu(&g); // Horizontal sampling for each component
> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+2] =
> bytestream2_get_byteu(&g); // Vertical sampling for each component
> +    }

Could be simplified to a single avio_read()

/Tomas
Cédric Le Barz Dec. 20, 2023, 5:04 p.m. UTC | #2
Le 19/12/2023 à 14:36, Tomas Härdin a écrit :
>> +    for ( comp = 0; comp < component_count; comp++ ) {
>> +        avio_write(pb, &sc->j2k_info.j2k_comp_desc[3*comp] , 3);
>> +    }
> Looks like this could be simplified to just
>
>    avio_write(pb, sc->j2k_info.j2k_comp_desc, 3*component_count);
>
>> +    if (j2k_ncomponents != component_count) {
>> +        av_log(s, AV_LOG_ERROR, "Incoherence about components image
>> number.\n");
>> +    }
> I again feel this should be a hard error
>
>> +    for (comp = 0; comp < j2k_ncomponents; comp++) {
>> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents] =
>> bytestream2_get_byteu(&g);   // Bitdepth for each component
>> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+1] =
>> bytestream2_get_byteu(&g); // Horizontal sampling for each component
>> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+2] =
>> bytestream2_get_byteu(&g); // Vertical sampling for each component
>> +    }
> Could be simplified to a single avio_read()
>
> /Tomas
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

Here is the new version of the patch taken into account your remarks.

Regards,

Cédric Le Barz
Signed-off-by: Cedric Le Barz <clebarz@ektacom.com>
---
 ffmpeg/libavformat/mxf.h    |   1 +
 ffmpeg/libavformat/mxfenc.c | 169 +++++++++++++++++++++++++++++++++++-
 2 files changed, 169 insertions(+), 1 deletion(-)

diff --git a/ffmpeg/libavformat/mxf.h b/ffmpeg/libavformat/mxf.h
index 2561605..7dd1681 100644
--- a/ffmpeg/libavformat/mxf.h
+++ b/ffmpeg/libavformat/mxf.h
@@ -55,6 +55,7 @@ enum MXFMetadataSetType {
     SoundfieldGroupLabelSubDescriptor,
     GroupOfSoundfieldGroupsLabelSubDescriptor,
     FFV1SubDescriptor,
+    JPEG2000SubDescriptor,
 };
 
 enum MXFFrameLayout {
diff --git a/ffmpeg/libavformat/mxfenc.c b/ffmpeg/libavformat/mxfenc.c
index 53bd6ae..685c11b 100644
--- a/ffmpeg/libavformat/mxfenc.c
+++ b/ffmpeg/libavformat/mxfenc.c
@@ -48,8 +48,10 @@
 #include "libavutil/pixdesc.h"
 #include "libavutil/time_internal.h"
 #include "libavcodec/defs.h"
+#include "libavcodec/bytestream.h"
 #include "libavcodec/golomb.h"
 #include "libavcodec/h264.h"
+#include "libavcodec/jpeg2000.h"
 #include "libavcodec/packet_internal.h"
 #include "libavcodec/rangecoder.h"
 #include "libavcodec/startcode.h"
@@ -78,6 +80,20 @@ typedef struct MXFIndexEntry {
     uint8_t flags;
 } MXFIndexEntry;
 
+typedef struct j2k_info_t {
+    uint16_t j2k_cap;        ///< j2k required decoder capabilities
+    uint16_t j2k_rsiz;       ///< j2k required decoder capabilities (Rsiz)
+    uint32_t j2k_xsiz;       ///< j2k width of the reference grid (Xsiz)
+    uint32_t j2k_ysiz;       ///< j2k height of the reference grid (Ysiz)
+    uint32_t j2k_x0siz;      ///< j2k horizontal offset from the origin of the reference grid to the left side of the image (X0siz)
+    uint32_t j2k_y0siz;      ///< j2k vertical offset from the origin of the reference grid to the left side of the image (Y0siz)
+    uint32_t j2k_xtsiz;      ///< j2k width of one reference tile with respect to the reference grid (XTsiz)
+    uint32_t j2k_ytsiz;      ///< j2k height of one reference tile with respect to the reference grid (YTsiz)
+    uint32_t j2k_xt0siz;     ///< j2k horizontal offset from the origin of the reference grid to the left side of the first tile (XT0siz)
+    uint32_t j2k_yt0siz;     ///< j2k vertical offset from the origin of the reference grid to the left side of the first tile (YT0siz)
+    uint8_t  j2k_comp_desc[12]; ///< j2k components descriptor (Ssiz(i), XRsiz(i), YRsiz(i))
+} j2k_info_t;
+
 typedef struct MXFStreamContext {
     int64_t pkt_cnt;         ///< pkt counter for muxed packets
     UID track_essence_element_key;
@@ -104,6 +120,7 @@ typedef struct MXFStreamContext {
     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;
 } MXFStreamContext;
 
 typedef struct MXFContainerEssenceEntry {
@@ -413,6 +430,20 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 0xDFD9, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x06,0x00,0x00,0x00}}, /* FFV1 Micro-version */
     { 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 */
+    { 0x8404, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x04,0x00,0x00,0x00}}, /* X0siz: Horizontal offset from the origin of the reference grid to the left side of the image area */
+    { 0x8405, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x05,0x00,0x00,0x00}}, /* Y0siz: Vertical offset from the origin of the reference grid to the left side of the image area */
+    { 0x8406, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x06,0x00,0x00,0x00}}, /* XTsiz: Width of one reference tile with respect to the reference grid */
+    { 0x8407, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x07,0x00,0x00,0x00}}, /* YTsiz: Height of one reference tile with respect to the reference grid */
+    { 0x8408, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x08,0x00,0x00,0x00}}, /* XT0siz: Horizontal offset from the origin of the reference grid to the left side of the first tile */
+    { 0x8409, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x09,0x00,0x00,0x00}}, /* YT0siz: Vertical offset from the origin of the reference grid to the left side of the first tile */
+    { 0x840A, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0A,0x00,0x00,0x00}}, /* Csiz: The number of components in the picture */
+    { 0x840B, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0B,0x00,0x00,0x00}}, /* Ssizi, XRSizi, YRSizi: 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. */
+    { 0x840C, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0E,0x00,0x00,0x00}}, /* The nature and order of the image components in the compressed domain as carried in the J2C codestream. */
 };
 
 #define MXF_NUM_TAGS FF_ARRAY_ELEMS(mxf_local_tag_batch)
@@ -549,7 +580,7 @@ static void mxf_write_primer_pack(AVFormatContext *s)
     MXFContext *mxf = s->priv_data;
     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;
+    int will_have_avc_tags = 0, will_have_mastering_tags = 0, will_have_ffv1_tags = 0, will_have_jpeg2000_tags = 0;
 
     for (i = 0; i < s->nb_streams; i++) {
         MXFStreamContext *sc = s->streams[i]->priv_data;
@@ -564,6 +595,9 @@ static void mxf_write_primer_pack(AVFormatContext *s)
         if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_FFV1) {
             will_have_ffv1_tags = 1;
         }
+        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_JPEG2000){
+            will_have_jpeg2000_tags = 1;
+        }
     }
 
     if (!mxf->store_user_comments) {
@@ -595,6 +629,22 @@ static void mxf_write_primer_pack(AVFormatContext *s)
         mxf_mark_tag_unused(mxf, 0xDFDB);
     }
 
+    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);
+        mxf_mark_tag_unused(mxf, 0x8404);
+        mxf_mark_tag_unused(mxf, 0x8405);
+        mxf_mark_tag_unused(mxf, 0x8406);
+        mxf_mark_tag_unused(mxf, 0x8407);
+        mxf_mark_tag_unused(mxf, 0x8408);
+        mxf_mark_tag_unused(mxf, 0x8409);
+        mxf_mark_tag_unused(mxf, 0x840A);
+        mxf_mark_tag_unused(mxf, 0x840B);
+        mxf_mark_tag_unused(mxf, 0x840C);
+    }
+
     for (i = 0; i < MXF_NUM_TAGS; i++) {
         if (mxf->unused_tags[i]) {
             local_tag_number--;
@@ -1136,6 +1186,7 @@ static const UID mxf_generic_sound_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0
 
 static const UID mxf_avc_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6E,0x00 };
 static const UID mxf_ffv1_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x81,0x03 };
+static const UID mxf_jpeg2000_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x5A,0x00};
 
 static inline uint16_t rescale_mastering_chroma(AVRational q)
 {
@@ -1430,6 +1481,64 @@ static void mxf_write_avc_subdesc(AVFormatContext *s, AVStream *st)
     mxf_update_klv_size(s->pb, pos);
 }
 
+static void mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st)
+{
+    MXFStreamContext *sc = st->priv_data;
+    AVIOContext *pb = s->pb;
+    int64_t pos;
+    int component_count = av_pix_fmt_count_planes(st->codecpar->format);
+    int comp = 0;
+
+    /* JPEG2000 subdescriptor key */
+    avio_write(pb, mxf_jpeg2000_subdescriptor_key, 16);
+    klv_encode_ber4_length(pb, 0);
+    pos = avio_tell(pb);
+
+    mxf_write_local_tag(s, 16, 0x3C0A);
+    mxf_write_uuid(pb, JPEG2000SubDescriptor, 0);
+
+    /* Value defining the decoder capabilities (rsiz) */
+    mxf_write_local_tag(s, 2, 0x8401);
+    avio_wb16(pb, sc->j2k_info.j2k_rsiz);
+    /* Width of the JPEG2000 reference grid (Xsiz) */
+    mxf_write_local_tag(s, 4, 0x8402);
+    avio_wb32(pb, st->codecpar->width);
+    /* Height of the JPEG2000 reference grid (Ysiz) */
+    mxf_write_local_tag(s, 4, 0x8403);
+    avio_wb32(pb, st->codecpar->height);
+    /* Horizontal offset from the reference grid origin to the left side of the image area (X0siz) */
+    mxf_write_local_tag(s, 4, 0x8404);
+    avio_wb32(pb, sc->j2k_info.j2k_x0siz);
+    /* Vertical offset from the reference grid origin to the left side of the image area (Y0siz) */
+    mxf_write_local_tag(s, 4, 0x8405);
+    avio_wb32(pb, sc->j2k_info.j2k_y0siz);
+    /* Width of one reference tile with respect to the reference grid (XTsiz) */
+    mxf_write_local_tag(s, 4, 0x8406);
+    avio_wb32(pb, sc->j2k_info.j2k_xtsiz);
+    /* Height of one reference tile with respect to the reference grid (YTsiz) */
+    mxf_write_local_tag(s, 4, 0x8407);
+    avio_wb32(pb, sc->j2k_info.j2k_ytsiz);
+    /* Horizontal offset from the origin of the reference grid to the left side of the first tile (XT0siz) */
+    mxf_write_local_tag(s, 4, 0x8408);
+    avio_wb32(pb, sc->j2k_info.j2k_xt0siz);
+    /* Vertical offset from the origin of the reference grid to the left side of the first tile (YT0siz) */
+    mxf_write_local_tag(s, 4, 0x8409);
+    avio_wb32(pb, sc->j2k_info.j2k_yt0siz);
+    /* Image components number (Csiz) */
+    mxf_write_local_tag(s, 2, 0x840A);
+    avio_wb16(pb, component_count);
+    /* Array of picture components where each component comprises 3 bytes named Ssiz(i) (Pixel bitdepth - 1),
+       XRSiz(i) (Horizontal sampling), YRSiz(i) (Vertical sampling). 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. */
+    mxf_write_local_tag(s, 8 + 3*component_count, 0x840B);
+    avio_wb32(pb, component_count);
+    avio_wb32(pb, 3);
+    avio_write(pb, sc->j2k_info.j2k_comp_desc, 3*component_count);
+
+    mxf_update_klv_size(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);
@@ -1438,6 +1547,9 @@ static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st)
     if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
         mxf_write_avc_subdesc(s, st);
     }
+    if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) {
+         mxf_write_jpeg2000_subdesc(s, st);
+    }
 }
 
 static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st)
@@ -2524,6 +2636,56 @@ static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt)
     return 1;
 }
 
+static int mxf_parse_jpeg2000_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+    MXFContext *mxf = s->priv_data;
+    MXFStreamContext *sc = st->priv_data;
+    AVIOContext *pb = s->pb;
+    int component_count = av_pix_fmt_count_planes(st->codecpar->format);
+    GetByteContext g;
+    uint32_t j2k_ncomponents;
+    int comp;
+
+    if (mxf->header_written)
+        return 1;
+
+    bytestream2_init(&g,pkt->data,pkt->size);
+
+    while (bytestream2_get_bytes_left(&g) >= 3 && bytestream2_peek_be16(&g) != JPEG2000_SOC)
+        bytestream2_skip(&g, 1);
+
+    if (bytestream2_get_be16u(&g) != JPEG2000_SOC) {
+        av_log(s, AV_LOG_ERROR, "Mandatory SOC marker is not present\n");
+        return AVERROR(AVERROR_INVALIDDATA);
+    }
+
+    /* Extract usefull size information from the SIZ marker */
+    if (bytestream2_get_be16u(&g) != JPEG2000_SIZ) {
+        av_log(s, AV_LOG_ERROR, "Mandatory SIZ marker is not present\n");
+        return AVERROR(AVERROR_INVALIDDATA);
+    }
+    bytestream2_skip(&g, 2); // Skip Lsiz
+    sc->j2k_info.j2k_cap = bytestream2_get_be16u(&g);
+    sc->j2k_info.j2k_xsiz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_ysiz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_x0siz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_y0siz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_xtsiz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_ytsiz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_xt0siz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_yt0siz = bytestream2_get_be32u(&g);
+    j2k_ncomponents = bytestream2_get_be16u(&g);
+    if (j2k_ncomponents != component_count) {
+        av_log(s, AV_LOG_ERROR, "Incoherence about components image number.\n");
+        return AVERROR(AVERROR_INVALIDDATA);
+    }
+    bytestream2_get_bufferu(&g, sc->j2k_info.j2k_comp_desc, 3 * j2k_ncomponents);
+
+    sc->frame_size = pkt->size;
+
+    return 1;
+}
+
 static const UID mxf_mpeg2_codec_uls[] = {
     { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x10,0x00 }, // MP-ML I-Frame
     { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x11,0x00 }, // MP-ML Long GOP
@@ -3140,6 +3302,11 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt)
             av_log(s, AV_LOG_ERROR, "could not get ffv1 version\n");
             return -1;
         }
+    } else if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) {
+        if (!mxf_parse_jpeg2000_frame(s, st, pkt)) {
+            av_log(s, AV_LOG_ERROR, "could not get jpeg2000 profile\n");
+            return -1;
+        }
     }
 
     if (mxf->cbr_index) {
Tomas Härdin Dec. 21, 2023, 11:11 a.m. UTC | #3
ons 2023-12-20 klockan 18:04 +0100 skrev Cédric Le Barz:
> Le 19/12/2023 à 14:36, Tomas Härdin a écrit :
> > > +    for ( comp = 0; comp < component_count; comp++ ) {
> > > +        avio_write(pb, &sc->j2k_info.j2k_comp_desc[3*comp] , 3);
> > > +    }
> > Looks like this could be simplified to just
> > 
> >    avio_write(pb, sc->j2k_info.j2k_comp_desc, 3*component_count);
> > 
> > > +    if (j2k_ncomponents != component_count) {
> > > +        av_log(s, AV_LOG_ERROR, "Incoherence about components
> > > image
> > > number.\n");
> > > +    }
> > I again feel this should be a hard error
> > 
> > > +    for (comp = 0; comp < j2k_ncomponents; comp++) {
> > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents] =
> > > bytestream2_get_byteu(&g);   // Bitdepth for each component
> > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+1] =
> > > bytestream2_get_byteu(&g); // Horizontal sampling for each
> > > component
> > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+2] =
> > > bytestream2_get_byteu(&g); // Vertical sampling for each
> > > component
> > > +    }
> > Could be simplified to a single avio_read()
> > 
> > /Tomas
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > 
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> 
> Here is the new version of the patch taken into account your remarks.

Looks OK. Does this also work with RGB(A)? Seems to not be hardcoded
for YUV at least. Higher bitdepths would also be nice, I've been
working with lossless RGB48 J2K. Doesn't necessarily need to hold up
this patch, just curious.

/Tomas
Cédric Le Barz Dec. 22, 2023, 8:58 a.m. UTC | #4
Le 21/12/2023 à 12:11, Tomas Härdin a écrit :
> ons 2023-12-20 klockan 18:04 +0100 skrev Cédric Le Barz:
>> Le 19/12/2023 à 14:36, Tomas Härdin a écrit :
>>>> +    for ( comp = 0; comp < component_count; comp++ ) {
>>>> +        avio_write(pb, &sc->j2k_info.j2k_comp_desc[3*comp] , 3);
>>>> +    }
>>> Looks like this could be simplified to just
>>>
>>>     avio_write(pb, sc->j2k_info.j2k_comp_desc, 3*component_count);
>>>
>>>> +    if (j2k_ncomponents != component_count) {
>>>> +        av_log(s, AV_LOG_ERROR, "Incoherence about components
>>>> image
>>>> number.\n");
>>>> +    }
>>> I again feel this should be a hard error
>>>
>>>> +    for (comp = 0; comp < j2k_ncomponents; comp++) {
>>>> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents] =
>>>> bytestream2_get_byteu(&g);   // Bitdepth for each component
>>>> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+1] =
>>>> bytestream2_get_byteu(&g); // Horizontal sampling for each
>>>> component
>>>> +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+2] =
>>>> bytestream2_get_byteu(&g); // Vertical sampling for each
>>>> component
>>>> +    }
>>> Could be simplified to a single avio_read()
>>>
>>> /Tomas
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel@ffmpeg.org
>>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>>
>>> To unsubscribe, visit link above, or email
>>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>> Here is the new version of the patch taken into account your remarks.
> Looks OK. Does this also work with RGB(A)? Seems to not be hardcoded
> for YUV at least. Higher bitdepths would also be nice, I've been
> working with lossless RGB48 J2K. Doesn't necessarily need to hold up
> this patch, just curious.
>
> /Tomas

Any J2K bitdepth can be handled with this patch (up to 31 bits per 
component).

Concerning RGBA, The JPEG 2000 sub-descriptor can be referenced either 
by the CDCI picture essence descriptor or the RGBA picture essence 
descriptor both of which are defined by SMPTE ST 377-1. In this patch it 
is only referenced by the CDCI as the RGBA essence descriptor is not yet 
implemented in FFmpeg.   Nevertheless, I think it should work too with 
CDCI essence descriptor : if you have such data, you can try.

Cédric
Tomas Härdin Dec. 31, 2023, 1:16 p.m. UTC | #5
fre 2023-12-22 klockan 09:58 +0100 skrev Cédric Le Barz:
> 
> Le 21/12/2023 à 12:11, Tomas Härdin a écrit :
> > ons 2023-12-20 klockan 18:04 +0100 skrev Cédric Le Barz:
> > > Le 19/12/2023 à 14:36, Tomas Härdin a écrit :
> > > > > +    for ( comp = 0; comp < component_count; comp++ ) {
> > > > > +        avio_write(pb, &sc->j2k_info.j2k_comp_desc[3*comp] ,
> > > > > 3);
> > > > > +    }
> > > > Looks like this could be simplified to just
> > > > 
> > > >     avio_write(pb, sc->j2k_info.j2k_comp_desc,
> > > > 3*component_count);
> > > > 
> > > > > +    if (j2k_ncomponents != component_count) {
> > > > > +        av_log(s, AV_LOG_ERROR, "Incoherence about
> > > > > components
> > > > > image
> > > > > number.\n");
> > > > > +    }
> > > > I again feel this should be a hard error
> > > > 
> > > > > +    for (comp = 0; comp < j2k_ncomponents; comp++) {
> > > > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents] =
> > > > > bytestream2_get_byteu(&g);   // Bitdepth for each component
> > > > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+1] =
> > > > > bytestream2_get_byteu(&g); // Horizontal sampling for each
> > > > > component
> > > > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+2] =
> > > > > bytestream2_get_byteu(&g); // Vertical sampling for each
> > > > > component
> > > > > +    }
> > > > Could be simplified to a single avio_read()
> > > > 
> > > > /Tomas
> > > > _______________________________________________
> > > > ffmpeg-devel mailing list
> > > > ffmpeg-devel@ffmpeg.org
> > > > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > > > 
> > > > To unsubscribe, visit link above, or email
> > > > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > > Here is the new version of the patch taken into account your
> > > remarks.
> > Looks OK. Does this also work with RGB(A)? Seems to not be
> > hardcoded
> > for YUV at least. Higher bitdepths would also be nice, I've been
> > working with lossless RGB48 J2K. Doesn't necessarily need to hold
> > up
> > this patch, just curious.
> > 
> > /Tomas
> 
> Any J2K bitdepth can be handled with this patch (up to 31 bits per 
> component).
> 
> Concerning RGBA, The JPEG 2000 sub-descriptor can be referenced
> either 
> by the CDCI picture essence descriptor or the RGBA picture essence 
> descriptor both of which are defined by SMPTE ST 377-1. In this patch
> it 
> is only referenced by the CDCI as the RGBA essence descriptor is not
> yet 
> implemented in FFmpeg.   Nevertheless, I think it should work too
> with 
> CDCI essence descriptor : if you have such data, you can try.

I have one sample at least, muxed by WidgetCo's as-02-wrap. Its
descriptor looks like this (via mxfdump):

RGBAEssenceDescriptor
  InstanceUID = {c3bf8d8c-6fd4-4004-83d7-3950ab56e86f}
  SubDescriptors
    SubDescriptor = {d2013379-ad4b-4d9e-9531-c3a6d8d59175}
    SubDescriptor -> Strong Reference to JPEG2000PictureSubDescriptor
      JPEG2000PictureSubDescriptor
        InstanceUID = {d2013379-ad4b-4d9e-9531-c3a6d8d59175}
        Rsiz = 16384
        Xsiz = 3840
        Ysiz = 2160
        XOsiz = 0
        YOsiz = 0
        XTsiz = 3840
        YTsiz = 2160
        XTOsiz = 0
        YTOsiz = 0
        Csiz = 3
        PictureComponentSizing
          PictureComponentSize = Ssiz="15", XRsiz="1", YRsiz="1"
          PictureComponentSize = Ssiz="15", XRsiz="1", YRsiz="1"
          PictureComponentSize = Ssiz="15", XRsiz="1", YRsiz="1"
        CodingStyleDefault = Scod="0", SGcod="ProgressionOrder="2",
NumberOfLayers="1", MultipleComponentTransformation="1"",
SPcod="DecompositionLevels="5", CodeblockWidth="4",
CodeblockHeight="4", CodeblockStyle="64", Transformation="1"",
PrecinctSize=""
        QuantizationDefault = Sqcd="32", SPqcd="152, 160, 160, 168,
160, 160, 168, 160, 160, 168, 160, 160, 160, 152, 152, 160"
        Unknown ff.e9 [060e2b34.0101.010e.04010603.0e000000] = 52 10 47
10 42 10 00 00 00 00 00 00 00 00 00 00
        Unknown ff.e8 [060e2b34.0101.010e.04010603.0f000000] = 00 02 00
00 00 00 00 01 00 00 00 02 00 02
  LinkedTrackID = 2
  SampleRate = 24/1
  ContainerDuration = 1
  EssenceContainer = MXF-GC JPEG-2000 Picture Mappings
  FrameLayout = 0
  StoredWidth = 3840
  StoredHeight = 2160
  AspectRatio = 3840/2160
  Gamma = [060e2b34.0401.010d.04010101.010a0000]
  PictureEssenceCoding = [060e2b34.0401.010d.04010202.03010801]
  Unknown 32.19 [060e2b34.0101.0109.04010201.01060100] = 06 0e 2b 34 04
01 01 0d 04 01 01 01 03 04 00 00
  Unknown ff.fe [060e2b34.0101.010e.04010501.14000000] = 00 00 00 02
  Unknown ff.fd [060e2b34.0101.010e.04010501.13000000] = 00 00 00 03
  Unknown ff.fc [060e2b34.0101.010e.04010501.15000000] = 00 00 00 04
  Unknown ff.fb [060e2b34.0101.010e.04010501.16000000] = 00 00 00 05
  VideoLineMap
    VideoLineMapEntry = <Unknown>
    VideoLineMapEntry = <Unknown>
  Unknown ff.fa [060e2b34.0101.010e.04200401.01010000] = 7d 00 40 74 3a
98 75 30 1d 4c 0b b8
  Unknown ff.f9 [060e2b34.0101.010e.04200401.01020000] = 3d 13 40 42
  Unknown ff.f8 [060e2b34.0101.010e.04200401.01030000] = 00 98 96 80
  Unknown ff.f7 [060e2b34.0101.010e.04200401.01040000] = 00 00 00 32
  ComponentMaxRef = 65535
  ComponentMinRef = 0
  ScanningDirection = 0
  PixelLayout = <Unknown>

A client has received samples which are multiple MXFs, each one
containing just a single j2k image.

Anyway I will push this patch later today,

/Tomas
Tomas Härdin Dec. 31, 2023, 2:45 p.m. UTC | #6
sön 2023-12-31 klockan 14:16 +0100 skrev Tomas Härdin:
> fre 2023-12-22 klockan 09:58 +0100 skrev Cédric Le Barz:
> > 
> > Le 21/12/2023 à 12:11, Tomas Härdin a écrit :
> > > ons 2023-12-20 klockan 18:04 +0100 skrev Cédric Le Barz:
> > > > Le 19/12/2023 à 14:36, Tomas Härdin a écrit :
> > > > > > +    for ( comp = 0; comp < component_count; comp++ ) {
> > > > > > +        avio_write(pb, &sc->j2k_info.j2k_comp_desc[3*comp]
> > > > > > ,
> > > > > > 3);
> > > > > > +    }
> > > > > Looks like this could be simplified to just
> > > > > 
> > > > >     avio_write(pb, sc->j2k_info.j2k_comp_desc,
> > > > > 3*component_count);
> > > > > 
> > > > > > +    if (j2k_ncomponents != component_count) {
> > > > > > +        av_log(s, AV_LOG_ERROR, "Incoherence about
> > > > > > components
> > > > > > image
> > > > > > number.\n");
> > > > > > +    }
> > > > > I again feel this should be a hard error
> > > > > 
> > > > > > +    for (comp = 0; comp < j2k_ncomponents; comp++) {
> > > > > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents] =
> > > > > > bytestream2_get_byteu(&g);   // Bitdepth for each component
> > > > > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+1]
> > > > > > =
> > > > > > bytestream2_get_byteu(&g); // Horizontal sampling for each
> > > > > > component
> > > > > > +        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+2]
> > > > > > =
> > > > > > bytestream2_get_byteu(&g); // Vertical sampling for each
> > > > > > component
> > > > > > +    }
> > > > > Could be simplified to a single avio_read()
> > > > > 
> > > > > /Tomas
> > > > > _______________________________________________
> > > > > ffmpeg-devel mailing list
> > > > > ffmpeg-devel@ffmpeg.org
> > > > > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > > > > 
> > > > > To unsubscribe, visit link above, or email
> > > > > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > > > Here is the new version of the patch taken into account your
> > > > remarks.
> > > Looks OK. Does this also work with RGB(A)? Seems to not be
> > > hardcoded
> > > for YUV at least. Higher bitdepths would also be nice, I've been
> > > working with lossless RGB48 J2K. Doesn't necessarily need to hold
> > > up
> > > this patch, just curious.
> > > 
> > > /Tomas
> > 
> > Any J2K bitdepth can be handled with this patch (up to 31 bits per 
> > component).
> > 
> > Concerning RGBA, The JPEG 2000 sub-descriptor can be referenced
> > either 
> > by the CDCI picture essence descriptor or the RGBA picture essence 
> > descriptor both of which are defined by SMPTE ST 377-1. In this
> > patch
> > it 
> > is only referenced by the CDCI as the RGBA essence descriptor is
> > not
> > yet 
> > implemented in FFmpeg.   Nevertheless, I think it should work too
> > with 
> > CDCI essence descriptor : if you have such data, you can try.
> 
> I have one sample at least, muxed by WidgetCo's as-02-wrap. Its
> descriptor looks like this (via mxfdump):
> 
> RGBAEssenceDescriptor
>   InstanceUID = {c3bf8d8c-6fd4-4004-83d7-3950ab56e86f}
>   SubDescriptors
>     SubDescriptor = {d2013379-ad4b-4d9e-9531-c3a6d8d59175}
>     SubDescriptor -> Strong Reference to JPEG2000PictureSubDescriptor
>       JPEG2000PictureSubDescriptor
>         InstanceUID = {d2013379-ad4b-4d9e-9531-c3a6d8d59175}
>         Rsiz = 16384
>         Xsiz = 3840
>         Ysiz = 2160
>         XOsiz = 0
>         YOsiz = 0
>         XTsiz = 3840
>         YTsiz = 2160
>         XTOsiz = 0
>         YTOsiz = 0
>         Csiz = 3
>         PictureComponentSizing
>           PictureComponentSize = Ssiz="15", XRsiz="1", YRsiz="1"
>           PictureComponentSize = Ssiz="15", XRsiz="1", YRsiz="1"
>           PictureComponentSize = Ssiz="15", XRsiz="1", YRsiz="1"
>         CodingStyleDefault = Scod="0", SGcod="ProgressionOrder="2",
> NumberOfLayers="1", MultipleComponentTransformation="1"",
> SPcod="DecompositionLevels="5", CodeblockWidth="4",
> CodeblockHeight="4", CodeblockStyle="64", Transformation="1"",
> PrecinctSize=""
>         QuantizationDefault = Sqcd="32", SPqcd="152, 160, 160, 168,
> 160, 160, 168, 160, 160, 168, 160, 160, 160, 152, 152, 160"
>         Unknown ff.e9 [060e2b34.0101.010e.04010603.0e000000] = 52 10
> 47
> 10 42 10 00 00 00 00 00 00 00 00 00 00
>         Unknown ff.e8 [060e2b34.0101.010e.04010603.0f000000] = 00 02
> 00
> 00 00 00 00 01 00 00 00 02 00 02
>   LinkedTrackID = 2
>   SampleRate = 24/1
>   ContainerDuration = 1
>   EssenceContainer = MXF-GC JPEG-2000 Picture Mappings
>   FrameLayout = 0
>   StoredWidth = 3840
>   StoredHeight = 2160
>   AspectRatio = 3840/2160
>   Gamma = [060e2b34.0401.010d.04010101.010a0000]
>   PictureEssenceCoding = [060e2b34.0401.010d.04010202.03010801]
>   Unknown 32.19 [060e2b34.0101.0109.04010201.01060100] = 06 0e 2b 34
> 04
> 01 01 0d 04 01 01 01 03 04 00 00
>   Unknown ff.fe [060e2b34.0101.010e.04010501.14000000] = 00 00 00 02
>   Unknown ff.fd [060e2b34.0101.010e.04010501.13000000] = 00 00 00 03
>   Unknown ff.fc [060e2b34.0101.010e.04010501.15000000] = 00 00 00 04
>   Unknown ff.fb [060e2b34.0101.010e.04010501.16000000] = 00 00 00 05
>   VideoLineMap
>     VideoLineMapEntry = <Unknown>
>     VideoLineMapEntry = <Unknown>
>   Unknown ff.fa [060e2b34.0101.010e.04200401.01010000] = 7d 00 40 74
> 3a
> 98 75 30 1d 4c 0b b8
>   Unknown ff.f9 [060e2b34.0101.010e.04200401.01020000] = 3d 13 40 42
>   Unknown ff.f8 [060e2b34.0101.010e.04200401.01030000] = 00 98 96 80
>   Unknown ff.f7 [060e2b34.0101.010e.04200401.01040000] = 00 00 00 32
>   ComponentMaxRef = 65535
>   ComponentMinRef = 0
>   ScanningDirection = 0
>   PixelLayout = <Unknown>
> 
> A client has received samples which are multiple MXFs, each one
> containing just a single j2k image.
> 
> Anyway I will push this patch later today,

Pushed

/Tomas
diff mbox series

Patch

diff --git a/ffmpeg/libavformat/mxf.h b/ffmpeg/libavformat/mxf.h
index 2561605..7dd1681 100644
--- a/ffmpeg/libavformat/mxf.h
+++ b/ffmpeg/libavformat/mxf.h
@@ -55,6 +55,7 @@  enum MXFMetadataSetType {
     SoundfieldGroupLabelSubDescriptor,
     GroupOfSoundfieldGroupsLabelSubDescriptor,
     FFV1SubDescriptor,
+    JPEG2000SubDescriptor,
 };
 
 enum MXFFrameLayout {
diff --git a/ffmpeg/libavformat/mxfenc.c b/ffmpeg/libavformat/mxfenc.c
index 53bd6ae..a06c8af 100644
--- a/ffmpeg/libavformat/mxfenc.c
+++ b/ffmpeg/libavformat/mxfenc.c
@@ -48,8 +48,10 @@ 
 #include "libavutil/pixdesc.h"
 #include "libavutil/time_internal.h"
 #include "libavcodec/defs.h"
+#include "libavcodec/bytestream.h"
 #include "libavcodec/golomb.h"
 #include "libavcodec/h264.h"
+#include "libavcodec/jpeg2000.h"
 #include "libavcodec/packet_internal.h"
 #include "libavcodec/rangecoder.h"
 #include "libavcodec/startcode.h"
@@ -78,6 +80,20 @@  typedef struct MXFIndexEntry {
     uint8_t flags;
 } MXFIndexEntry;
 
+typedef struct j2k_info_t {
+    uint16_t j2k_cap;        ///< j2k required decoder capabilities
+    uint16_t j2k_rsiz;       ///< j2k required decoder capabilities (Rsiz)
+    uint32_t j2k_xsiz;       ///< j2k width of the reference grid (Xsiz)
+    uint32_t j2k_ysiz;       ///< j2k height of the reference grid (Ysiz)
+    uint32_t j2k_x0siz;      ///< j2k horizontal offset from the origin of the reference grid to the left side of the image (X0siz)
+    uint32_t j2k_y0siz;      ///< j2k vertical offset from the origin of the reference grid to the left side of the image (Y0siz)
+    uint32_t j2k_xtsiz;      ///< j2k width of one reference tile with respect to the reference grid (XTsiz)
+    uint32_t j2k_ytsiz;      ///< j2k height of one reference tile with respect to the reference grid (YTsiz)
+    uint32_t j2k_xt0siz;     ///< j2k horizontal offset from the origin of the reference grid to the left side of the first tile (XT0siz)
+    uint32_t j2k_yt0siz;     ///< j2k vertical offset from the origin of the reference grid to the left side of the first tile (YT0siz)
+    uint8_t  j2k_comp_desc[12]; ///< j2k components descriptor (Ssiz(i), XRsiz(i), YRsiz(i))
+} j2k_info_t;
+
 typedef struct MXFStreamContext {
     int64_t pkt_cnt;         ///< pkt counter for muxed packets
     UID track_essence_element_key;
@@ -104,6 +120,7 @@  typedef struct MXFStreamContext {
     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;
 } MXFStreamContext;
 
 typedef struct MXFContainerEssenceEntry {
@@ -413,6 +430,20 @@  static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 0xDFD9, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x06,0x00,0x00,0x00}}, /* FFV1 Micro-version */
     { 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 */
+    { 0x8404, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x04,0x00,0x00,0x00}}, /* X0siz: Horizontal offset from the origin of the reference grid to the left side of the image area */
+    { 0x8405, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x05,0x00,0x00,0x00}}, /* Y0siz: Vertical offset from the origin of the reference grid to the left side of the image area */
+    { 0x8406, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x06,0x00,0x00,0x00}}, /* XTsiz: Width of one reference tile with respect to the reference grid */
+    { 0x8407, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x07,0x00,0x00,0x00}}, /* YTsiz: Height of one reference tile with respect to the reference grid */
+    { 0x8408, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x08,0x00,0x00,0x00}}, /* XT0siz: Horizontal offset from the origin of the reference grid to the left side of the first tile */
+    { 0x8409, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x09,0x00,0x00,0x00}}, /* YT0siz: Vertical offset from the origin of the reference grid to the left side of the first tile */
+    { 0x840A, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0A,0x00,0x00,0x00}}, /* Csiz: The number of components in the picture */
+    { 0x840B, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0B,0x00,0x00,0x00}}, /* Ssizi, XRSizi, YRSizi: 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. */
+    { 0x840C, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0E,0x00,0x00,0x00}}, /* The nature and order of the image components in the compressed domain as carried in the J2C codestream. */
 };
 
 #define MXF_NUM_TAGS FF_ARRAY_ELEMS(mxf_local_tag_batch)
@@ -549,7 +580,7 @@  static void mxf_write_primer_pack(AVFormatContext *s)
     MXFContext *mxf = s->priv_data;
     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;
+    int will_have_avc_tags = 0, will_have_mastering_tags = 0, will_have_ffv1_tags = 0, will_have_jpeg2000_tags = 0;
 
     for (i = 0; i < s->nb_streams; i++) {
         MXFStreamContext *sc = s->streams[i]->priv_data;
@@ -564,6 +595,9 @@  static void mxf_write_primer_pack(AVFormatContext *s)
         if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_FFV1) {
             will_have_ffv1_tags = 1;
         }
+        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_JPEG2000){
+            will_have_jpeg2000_tags = 1;
+        }
     }
 
     if (!mxf->store_user_comments) {
@@ -595,6 +629,22 @@  static void mxf_write_primer_pack(AVFormatContext *s)
         mxf_mark_tag_unused(mxf, 0xDFDB);
     }
 
+    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);
+        mxf_mark_tag_unused(mxf, 0x8404);
+        mxf_mark_tag_unused(mxf, 0x8405);
+        mxf_mark_tag_unused(mxf, 0x8406);
+        mxf_mark_tag_unused(mxf, 0x8407);
+        mxf_mark_tag_unused(mxf, 0x8408);
+        mxf_mark_tag_unused(mxf, 0x8409);
+        mxf_mark_tag_unused(mxf, 0x840A);
+        mxf_mark_tag_unused(mxf, 0x840B);
+        mxf_mark_tag_unused(mxf, 0x840C);
+    }
+
     for (i = 0; i < MXF_NUM_TAGS; i++) {
         if (mxf->unused_tags[i]) {
             local_tag_number--;
@@ -1136,6 +1186,7 @@  static const UID mxf_generic_sound_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0
 
 static const UID mxf_avc_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6E,0x00 };
 static const UID mxf_ffv1_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x81,0x03 };
+static const UID mxf_jpeg2000_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x5A,0x00};
 
 static inline uint16_t rescale_mastering_chroma(AVRational q)
 {
@@ -1430,6 +1481,66 @@  static void mxf_write_avc_subdesc(AVFormatContext *s, AVStream *st)
     mxf_update_klv_size(s->pb, pos);
 }
 
+static void mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st)
+{
+    MXFStreamContext *sc = st->priv_data;
+    AVIOContext *pb = s->pb;
+    int64_t pos;
+    int component_count = av_pix_fmt_count_planes(st->codecpar->format);
+    int comp = 0;
+
+    /* JPEG2000 subdescriptor key */
+    avio_write(pb, mxf_jpeg2000_subdescriptor_key, 16);
+    klv_encode_ber4_length(pb, 0);
+    pos = avio_tell(pb);
+
+    mxf_write_local_tag(s, 16, 0x3C0A);
+    mxf_write_uuid(pb, JPEG2000SubDescriptor, 0);
+
+    /* Value defining the decoder capabilities (rsiz) */
+    mxf_write_local_tag(s, 2, 0x8401);
+    avio_wb16(pb, sc->j2k_info.j2k_rsiz);
+    /* Width of the JPEG2000 reference grid (Xsiz) */
+    mxf_write_local_tag(s, 4, 0x8402);
+    avio_wb32(pb, st->codecpar->width);
+    /* Height of the JPEG2000 reference grid (Ysiz) */
+    mxf_write_local_tag(s, 4, 0x8403);
+    avio_wb32(pb, st->codecpar->height);
+    /* Horizontal offset from the reference grid origin to the left side of the image area (X0siz) */
+    mxf_write_local_tag(s, 4, 0x8404);
+    avio_wb32(pb, sc->j2k_info.j2k_x0siz);
+    /* Vertical offset from the reference grid origin to the left side of the image area (Y0siz) */
+    mxf_write_local_tag(s, 4, 0x8405);
+    avio_wb32(pb, sc->j2k_info.j2k_y0siz);
+    /* Width of one reference tile with respect to the reference grid (XTsiz) */
+    mxf_write_local_tag(s, 4, 0x8406);
+    avio_wb32(pb, sc->j2k_info.j2k_xtsiz);
+    /* Height of one reference tile with respect to the reference grid (YTsiz) */
+    mxf_write_local_tag(s, 4, 0x8407);
+    avio_wb32(pb, sc->j2k_info.j2k_ytsiz);
+    /* Horizontal offset from the origin of the reference grid to the left side of the first tile (XT0siz) */
+    mxf_write_local_tag(s, 4, 0x8408);
+    avio_wb32(pb, sc->j2k_info.j2k_xt0siz);
+    /* Vertical offset from the origin of the reference grid to the left side of the first tile (YT0siz) */
+    mxf_write_local_tag(s, 4, 0x8409);
+    avio_wb32(pb, sc->j2k_info.j2k_yt0siz);
+    /* Image components number (Csiz) */
+    mxf_write_local_tag(s, 2, 0x840A);
+    avio_wb16(pb, component_count);
+    /* Array of picture components where each component comprises 3 bytes named Ssiz(i) (Pixel bitdepth - 1),
+       XRSiz(i) (Horizontal sampling), YRSiz(i) (Vertical sampling). 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. */
+    mxf_write_local_tag(s, 8 + 3*component_count, 0x840B);
+    avio_wb32(pb, component_count);
+    avio_wb32(pb, 3);
+    for ( comp = 0; comp < component_count; comp++ ) {
+        avio_write(pb, &sc->j2k_info.j2k_comp_desc[3*comp] , 3);
+    }
+
+    mxf_update_klv_size(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);
@@ -1438,6 +1549,9 @@  static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st)
     if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
         mxf_write_avc_subdesc(s, st);
     }
+    if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) {
+         mxf_write_jpeg2000_subdesc(s, st);
+    }
 }
 
 static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st)
@@ -2524,6 +2638,58 @@  static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt)
     return 1;
 }
 
+static int mxf_parse_jpeg2000_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+    MXFContext *mxf = s->priv_data;
+    MXFStreamContext *sc = st->priv_data;
+    int component_count = av_pix_fmt_count_planes(st->codecpar->format);
+    GetByteContext g;
+    uint32_t j2k_ncomponents;
+    int comp;
+
+    if (mxf->header_written)
+        return 1;
+
+    bytestream2_init(&g,pkt->data,pkt->size);
+
+    while (bytestream2_get_bytes_left(&g) >= 3 && bytestream2_peek_be16(&g) != JPEG2000_SOC)
+        bytestream2_skip(&g, 1);
+
+    if (bytestream2_get_be16u(&g) != JPEG2000_SOC) {
+        av_log(s, AV_LOG_ERROR, "SOC marker not present\n");
+        return 0;
+    }
+
+    /* Extract usefull size information from the SIZ marker */
+    if (bytestream2_get_be16u(&g) != JPEG2000_SIZ) {
+        av_log(s, AV_LOG_ERROR, "SIZ marker not present\n");
+        return 0;
+    }
+    bytestream2_skip(&g, 2); // Skip Lsiz
+    sc->j2k_info.j2k_cap = bytestream2_get_be16u(&g);
+    sc->j2k_info.j2k_xsiz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_ysiz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_x0siz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_y0siz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_xtsiz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_ytsiz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_xt0siz = bytestream2_get_be32u(&g);
+    sc->j2k_info.j2k_yt0siz = bytestream2_get_be32u(&g);
+    j2k_ncomponents = bytestream2_get_be16u(&g);
+    if (j2k_ncomponents != component_count) {
+        av_log(s, AV_LOG_ERROR, "Incoherence about components image number.\n");
+    }
+    for (comp = 0; comp < j2k_ncomponents; comp++) {
+        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents] = bytestream2_get_byteu(&g);   // Bitdepth for each component
+        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+1] = bytestream2_get_byteu(&g); // Horizontal sampling for each component
+        sc->j2k_info.j2k_comp_desc[comp*j2k_ncomponents+2] = bytestream2_get_byteu(&g); // Vertical sampling for each component
+    }
+
+    sc->frame_size = pkt->size;
+
+    return 1;
+}
+
 static const UID mxf_mpeg2_codec_uls[] = {
     { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x10,0x00 }, // MP-ML I-Frame
     { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x11,0x00 }, // MP-ML Long GOP
@@ -3140,6 +3306,11 @@  static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt)
             av_log(s, AV_LOG_ERROR, "could not get ffv1 version\n");
             return -1;
         }
+    } else if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) {
+        if (!mxf_parse_jpeg2000_frame(s, st, pkt)) {
+            av_log(s, AV_LOG_ERROR, "could not get jpeg2000 profile\n");
+            return -1;
+        }
     }
 
     if (mxf->cbr_index) {