From patchwork Fri Dec 8 16:25:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?C=C3=A9dric_Le_Barz?= X-Patchwork-Id: 44979 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:1225:b0:181:818d:5e7f with SMTP id v37csp1405543pzf; Fri, 8 Dec 2023 08:26:09 -0800 (PST) X-Google-Smtp-Source: AGHT+IHdlgeeUWq980L5h55AQmDqdlhOljIJzywGOS6GpjM0iywiZOSKj88gH9StXfPVl8uGx5Oz X-Received: by 2002:a50:9f26:0:b0:54c:a2f9:5667 with SMTP id b35-20020a509f26000000b0054ca2f95667mr442908edf.4.1702052768916; Fri, 08 Dec 2023 08:26:08 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702052768; cv=none; d=google.com; s=arc-20160816; b=H9fa/ERCAnC8xWfx7/9485YtFR3B7Cy8ejRyq8YTmF9AtpcRNNgl2P4jXuLjvKlcGn 1J8J1lV/9yclFBgyqpQOW2i2rveRBcuChS2XxyfOD5RXbVclK7JTXW5IzWy5IdK6RInC Yiydh5+qVlqaCpletNtSkDfnbUFyhXr2z33mLzRvPclKeoP4yFaw9mtQICKSmawjUbsw SGMRAdFu28Pg3aaZHaHGw/zanASAVgr5TAySzJNUAsieWT2Pp4iDx5qV8tNKTpD7YUAL X7CZDiwB7ul2ciGLCkKeYJHsD2YwqEZKX1RMoGAm/QyJMz9Y2VH/8T6BYoxRpdaUL6pF M7/g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:from:to :content-language:mime-version:date:message-id:delivered-to; bh=iSpRXIuOSeE6uzmu6tX85vOwW/TvLi1Lo95vZSfUYMQ=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=0hkIP7iVtOcrxDrV0pj/msIFuCPNkl/sitzK+Df1MXGTau9Ss5E8sEMigF+PcRRQuu SbsHtilrJ2lHt54CbxYLN3IKMWy9zg2U8Lt78Z7NyqZJdF+9pxNvcY59iHRtAZ4/YRrY MuoPcGZTE3LXtfqZk4jCF1mQGbnhHgFTj0heK1AwYT/S9nLzdrCZXiBCkyn1oPPPPCF9 2ULJCwp3UnnVqmygAvyOf2QGz5KS2VfSwzsPXaC+qHOyYfTQ6wpfHnE5J+EZo4rLXluO +/u1fLmLbJmi54Nwxdrix2emKoeADY34CAjYERWMxNqaMz8KfDFaQVt4z1SlPZScq3EQ 3rDw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id r17-20020a056402035100b0054bd5e5faebsi970731edw.681.2023.12.08.08.26.07; Fri, 08 Dec 2023 08:26:08 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B285968D041; Fri, 8 Dec 2023 18:26:03 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from gandalf.ektacom.com (gandalf.ektacom.com [62.23.45.26]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8ED1568D027 for ; Fri, 8 Dec 2023 18:25:57 +0200 (EET) Received: from ektacom.com (mail.ektacom.com [82.64.203.93]) by gandalf.ektacom.com (Postfix) with ESMTP id 3FA0045A266 for ; Fri, 8 Dec 2023 17:25:57 +0100 (CET) Message-ID: <7c1649cd-8b35-47fa-89d6-f7dfc11350e4@ektacom.com> Date: Fri, 8 Dec 2023 17:25:51 +0100 MIME-Version: 1.0 Content-Language: en-US To: ffmpeg-devel@ffmpeg.org From: =?utf-8?q?C=C3=A9dric_Le_Barz?= X-MailScanner-ID: 82EDF1E9D788.AB232 X-MailScanner: Found to be clean X-MailScanner-From: clebarz@ektacom.com X-Spam-Status: No Subject: [FFmpeg-devel] [PATCH] [MXF] - Add jpeg2000 subdescriptor in MXF file. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: zBipEloqUjHB Add jpeg2000 subdescriptor in MXF file. Signed-off-by: Cedric Le Barz Signed-off-by: Cedric Le Barz --- ffmpeg/libavformat/mxf.h | 1 + ffmpeg/libavformat/mxfenc.c | 173 +++++++++++++++++++++++++++++++++++- 2 files changed, 173 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..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) {