[FFmpeg-devel] [PATCH v14 4/4] avformat/image2: add Jpeg XL as image2 format

Andreas Rheinhardt andreas.rheinhardt at outlook.com
Fri Apr 15 14:34:26 EEST 2022


Leo Izen:
> This commit adds support to libavformat for muxing
> and demuxing Jpeg XL images as image2 streams.
> ---
>  MAINTAINERS                |   1 +
>  libavformat/Makefile       |   1 +
>  libavformat/allformats.c   |   1 +
>  libavformat/img2.c         |   1 +
>  libavformat/img2dec.c      |  18 ++
>  libavformat/img2enc.c      |   6 +-
>  libavformat/jpegxl_probe.c | 393 +++++++++++++++++++++++++++++++++++++
>  libavformat/jpegxl_probe.h |  32 +++
>  libavformat/mov.c          |   1 +
>  9 files changed, 451 insertions(+), 3 deletions(-)
>  create mode 100644 libavformat/jpegxl_probe.c
>  create mode 100644 libavformat/jpegxl_probe.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index faea84ebf1..46723972dc 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -439,6 +439,7 @@ Muxers/Demuxers:
>    ipmovie.c                             Mike Melanson
>    ircam*                                Paul B Mahol
>    iss.c                                 Stefan Gehrer
> +  jpegxl_probe.*                        Leo Izen
>    jvdec.c                               Peter Ross
>    kvag.c                                Zane van Iperen
>    libmodplug.c                          Clément Bœsch
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index d7182d6bd8..beecdf5a66 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -272,6 +272,7 @@ OBJS-$(CONFIG_IMAGE_GIF_PIPE_DEMUXER)     += img2dec.o img2.o
>  OBJS-$(CONFIG_IMAGE_J2K_PIPE_DEMUXER)     += img2dec.o img2.o
>  OBJS-$(CONFIG_IMAGE_JPEG_PIPE_DEMUXER)    += img2dec.o img2.o
>  OBJS-$(CONFIG_IMAGE_JPEGLS_PIPE_DEMUXER)  += img2dec.o img2.o
> +OBJS-$(CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER)  += img2dec.o img2.o jpegxl_probe.o
>  OBJS-$(CONFIG_IMAGE_PAM_PIPE_DEMUXER)     += img2dec.o img2.o
>  OBJS-$(CONFIG_IMAGE_PBM_PIPE_DEMUXER)     += img2dec.o img2.o
>  OBJS-$(CONFIG_IMAGE_PCX_PIPE_DEMUXER)     += img2dec.o img2.o
> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> index 7c1d0ac38f..63876c468f 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -510,6 +510,7 @@ extern const AVInputFormat  ff_image_gif_pipe_demuxer;
>  extern const AVInputFormat  ff_image_j2k_pipe_demuxer;
>  extern const AVInputFormat  ff_image_jpeg_pipe_demuxer;
>  extern const AVInputFormat  ff_image_jpegls_pipe_demuxer;
> +extern const AVInputFormat  ff_image_jpegxl_pipe_demuxer;
>  extern const AVInputFormat  ff_image_pam_pipe_demuxer;
>  extern const AVInputFormat  ff_image_pbm_pipe_demuxer;
>  extern const AVInputFormat  ff_image_pcx_pipe_demuxer;
> diff --git a/libavformat/img2.c b/libavformat/img2.c
> index fe2ca7bfff..566ef873ca 100644
> --- a/libavformat/img2.c
> +++ b/libavformat/img2.c
> @@ -88,6 +88,7 @@ const IdStrMap ff_img_tags[] = {
>      { AV_CODEC_ID_GEM,        "ximg"     },
>      { AV_CODEC_ID_GEM,        "timg"     },
>      { AV_CODEC_ID_VBN,        "vbn"      },
> +    { AV_CODEC_ID_JPEGXL,     "jxl"      },
>      { AV_CODEC_ID_NONE,       NULL       }
>  };
>  
> diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c
> index 551b9d508e..627bb67212 100644
> --- a/libavformat/img2dec.c
> +++ b/libavformat/img2dec.c
> @@ -36,6 +36,7 @@
>  #include "avio_internal.h"
>  #include "internal.h"
>  #include "img2.h"
> +#include "jpegxl_probe.h"
>  #include "libavcodec/mjpeg.h"
>  #include "libavcodec/vbn.h"
>  #include "libavcodec/xwd.h"
> @@ -837,6 +838,22 @@ static int jpegls_probe(const AVProbeData *p)
>      return 0;
>  }
>  
> +static int jpegxl_probe(const AVProbeData *p)
> +{
> +    const uint8_t *b = p->buf;
> +
> +    /* ISOBMFF-based container */
> +    /* 0x4a584c20 == "JXL " */
> +    if (AV_RL64(b) == FF_JPEGXL_CONTAINER_SIGNATURE_LE)
> +        return AVPROBE_SCORE_EXTENSION + 1;
> +    /* Raw codestreams all start with 0xff0a */
> +    if (AV_RL16(b) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE)
> +        return 0;
> +    if (ff_jpegxl_verify_codestream_header(p->buf, p->buf_size) >= 0)

This will give a linking failure if the image_jpegxl_pipe_demuxer is
disabled.

> +        return AVPROBE_SCORE_MAX - 2;
> +    return 0;
> +}
> +
>  static int pcx_probe(const AVProbeData *p)
>  {
>      const uint8_t *b = p->buf;
> @@ -1176,6 +1193,7 @@ IMAGEAUTO_DEMUXER(gif,       GIF)
>  IMAGEAUTO_DEMUXER_EXT(j2k,   JPEG2000, J2K)
>  IMAGEAUTO_DEMUXER_EXT(jpeg,  MJPEG, JPEG)
>  IMAGEAUTO_DEMUXER(jpegls,    JPEGLS)
> +IMAGEAUTO_DEMUXER(jpegxl,    JPEGXL)
>  IMAGEAUTO_DEMUXER(pam,       PAM)
>  IMAGEAUTO_DEMUXER(pbm,       PBM)
>  IMAGEAUTO_DEMUXER(pcx,       PCX)
> diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c
> index ae351963d9..5ed97bb833 100644
> --- a/libavformat/img2enc.c
> +++ b/libavformat/img2enc.c
> @@ -263,9 +263,9 @@ static const AVClass img2mux_class = {
>  const AVOutputFormat ff_image2_muxer = {
>      .name           = "image2",
>      .long_name      = NULL_IF_CONFIG_SMALL("image2 sequence"),
> -    .extensions     = "bmp,dpx,exr,jls,jpeg,jpg,ljpg,pam,pbm,pcx,pfm,pgm,pgmyuv,png,"
> -                      "ppm,sgi,tga,tif,tiff,jp2,j2c,j2k,xwd,sun,ras,rs,im1,im8,im24,"
> -                      "sunras,vbn,xbm,xface,pix,y",
> +    .extensions     = "bmp,dpx,exr,jls,jpeg,jpg,jxl,ljpg,pam,pbm,pcx,pfm,pgm,pgmyuv,"
> +                      "png,ppm,sgi,tga,tif,tiff,jp2,j2c,j2k,xwd,sun,ras,rs,im1,im8,"
> +                      "im24,sunras,vbn,xbm,xface,pix,y",
>      .priv_data_size = sizeof(VideoMuxData),
>      .video_codec    = AV_CODEC_ID_MJPEG,
>      .write_header   = write_header,
> diff --git a/libavformat/jpegxl_probe.c b/libavformat/jpegxl_probe.c
> new file mode 100644
> index 0000000000..d3d3822fee
> --- /dev/null
> +++ b/libavformat/jpegxl_probe.c
> @@ -0,0 +1,393 @@
> +/*
> + * Jpeg XL header verification
> + * Copyright (c) 2022 Leo Izen <leo.izen at gmail.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "jpegxl_probe.h"
> +
> +#define BITSTREAM_READER_LE
> +#include "libavcodec/get_bits.h"
> +
> +enum JpegXLExtraChannelType {
> +    FF_JPEGXL_CT_ALPHA = 0,
> +    FF_JPEGXL_CT_DEPTH,
> +    FF_JPEGXL_CT_SPOT_COLOR,
> +    FF_JPEGXL_CT_SELECTION_MASK,
> +    FF_JPEGXL_CT_BLACK,
> +    FF_JPEGXL_CT_CFA,
> +    FF_JPEGXL_CT_THERMAL,
> +    FF_JPEGXL_CT_NON_OPTIONAL = 15,
> +    FF_JPEGXL_CT_OPTIONAL
> +};
> +
> +enum JpegXLColorSpace {
> +    FF_JPEGXL_CS_RGB = 0,
> +    FF_JPEGXL_CS_GRAY,
> +    FF_JPEGXL_CS_XYB,
> +    FF_JPEGXL_CS_UNKNOWN
> +};
> +
> +enum JpegXLWhitePoint {
> +    FF_JPEGXL_WP_D65 = 1,
> +    FF_JPEGXL_WP_CUSTOM,
> +    FF_JPEGXL_WP_E = 10,
> +    FF_JPEGXL_WP_DCI = 11
> +};
> +
> +enum JpegXLPrimaries {
> +    FF_JPEGXL_PR_SRGB = 1,
> +    FF_JPEGXL_PR_CUSTOM,
> +    FF_JPEGXL_PR_2100 = 9,
> +    FF_JPEGXL_PR_P3 = 11,
> +};
> +
> +#define jxl_bits(n) get_bits_long(gb, (n))
> +#define jxl_bits_skip(n) skip_bits_long(gb, (n))
> +#define jxl_u32(c0, c1, c2, c3, u0, u1, u2, u3) jpegxl_u32(gb, \
> +    (const uint32_t[]){c0, c1, c2, c3}, (const uint32_t[]){u0, u1, u2, u3})
> +#define jxl_u64() jpegxl_u64(gb)
> +#define jxl_enum() jxl_u32(0, 1, 2, 18, 0, 0, 4, 6)
> +
> +/* read a U32(c_i + u(u_i)) */
> +static uint32_t jpegxl_u32(GetBitContext *gb,
> +                           const uint32_t constants[4], const uint32_t ubits[4])
> +{
> +    uint32_t ret, choice = jxl_bits(2);
> +
> +    ret = constants[choice];
> +    if (ubits[choice])
> +        ret += jxl_bits(ubits[choice]);
> +
> +    return ret;
> +}
> +
> +/* read a U64() */
> +static uint64_t jpegxl_u64(GetBitContext *gb)
> +{
> +    uint64_t shift = 12, ret;
> +
> +    switch (jxl_bits(2)) {
> +    case 0:
> +        ret = 0;
> +        break;
> +    case 1:
> +        ret = 1 + jxl_bits(4);
> +        break;
> +    case 2:
> +        ret = 17 + jxl_bits(8);
> +        break;
> +    case 3:
> +        ret = jxl_bits(12);
> +        while (jxl_bits(1)) {
> +            if (shift < 60) {
> +                ret |= jxl_bits(8) << shift;
> +                shift += 8;
> +            } else {
> +                ret |= jxl_bits(4) << shift;
> +                break;
> +            }
> +        }
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static uint32_t jpegxl_width_from_ratio(uint32_t height, int ratio)
> +{
> +    uint64_t height64 = height;
> +    switch (ratio) {
> +    case 1:
> +        return height;
> +    case 2:
> +        return (uint32_t)((height64 * 12) / 10);
> +    case 3:
> +        return (uint32_t)((height64 * 4) / 3);
> +    case 4:
> +        return (uint32_t)((height64 * 3) / 2);
> +    case 5:
> +        return (uint32_t)((height64 * 16) / 9);
> +    case 6:
> +        return (uint32_t)((height64 * 5) / 4);
> +    case 7:
> +        return (uint32_t)(height64 * 2);
> +    default:
> +        break;
> +    }
> +
> +    return 0; /* manual width */
> +}
> +
> +/**
> + * validate a Jpeg XL Size Header
> + * @return >= 0 upon valid size, < 0 upon invalid size found
> + */
> +static int jpegxl_read_size_header(GetBitContext *gb)
> +{
> +    uint32_t width, height;
> +
> +    if (jxl_bits(1)) {
> +        /* small size header */
> +        height = (jxl_bits(5) + 1) << 3;
> +        width = jpegxl_width_from_ratio(height, jxl_bits(3));
> +        if (!width)
> +            width = (jxl_bits(5) + 1) << 3;
> +    } else {
> +        /* large size header */
> +        height = 1 + jxl_u32(0, 0, 0, 0, 9, 13, 18, 30);
> +        width = jpegxl_width_from_ratio(height, jxl_bits(3));
> +        if (!width)
> +            width = 1 + jxl_u32(0, 0, 0, 0, 9, 13, 18, 30);
> +    }
> +    if (width > (1 << 18) || height > (1 << 18)
> +        || (width >> 4) * (height >> 4) > (1 << 20))
> +        return -1;
> +
> +    return 0;
> +}
> +
> +/**
> + * validate a Jpeg XL Preview Header
> + * @return >= 0 upon valid size, < 0 upon invalid size found
> + */
> +static int jpegxl_read_preview_header(GetBitContext *gb)
> +{
> +    uint32_t width, height;
> +
> +    if (jxl_bits(1)) {
> +        /* coded height and width divided by eight */
> +        height = jxl_u32(16, 32, 1, 33, 0, 0, 5, 9) << 3;
> +        width = jpegxl_width_from_ratio(height, jxl_bits(3));
> +        if (!width)
> +            width = jxl_u32(16, 32, 1, 33, 0, 0, 5, 9) << 3;
> +    } else {
> +        /* full height and width coded */
> +        height = jxl_u32(1, 65, 321, 1345, 6, 8, 10, 12);
> +        width = jpegxl_width_from_ratio(height, jxl_bits(3));
> +        if (!width)
> +            width = jxl_u32(1, 65, 321, 1345, 6, 8, 10, 12);
> +    }
> +    if (width > 4096 || height > 4096)
> +        return -1;
> +
> +    return 0;
> +}
> +
> +/**
> + * skip a Jpeg XL BitDepth Header. These cannot be invalid.
> + */
> +static void jpegxl_skip_bit_depth(GetBitContext *gb)
> +{
> +    if (jxl_bits(1)) {
> +        /* float samples */
> +        jxl_u32(32, 16, 24, 1, 0, 0, 0, 6); /* mantissa */
> +        jxl_bits_skip(4); /* exponent */
> +    } else {
> +        /* integer samples */
> +        jxl_u32(8, 10, 12, 1, 0, 0, 0, 6);
> +    }
> +}
> +
> +/**
> + * validate a Jpeg XL Preview Header
> + * @return >= 0 upon valid, < 0 upon invalid
> + */
> +static int jpegxl_read_extra_channel_info(GetBitContext *gb)
> +{
> +    int all_default = jxl_bits(1);
> +    uint32_t type, name_len = 0;
> +
> +    if (!all_default) {
> +        type = jxl_enum();
> +        if (type > 63)
> +            return -1; /* enum types cannot be 64+ */
> +        if (type == FF_JPEGXL_CT_BLACK)
> +            return -1;
> +        jpegxl_skip_bit_depth(gb);
> +        jxl_u32(0, 3, 4, 1, 0, 0, 0, 3); /* dim-shift */
> +        /* max of name_len is 1071 = 48 + 2^10 - 1 */
> +        name_len = jxl_u32(0, 0, 16, 48, 0, 4, 5, 10);
> +    } else {
> +        type = FF_JPEGXL_CT_ALPHA;
> +    }
> +
> +    /* skip over the name */
> +    jxl_bits_skip(8 * name_len);
> +
> +    if (!all_default && type == FF_JPEGXL_CT_ALPHA)
> +        jxl_bits_skip(1);
> +
> +    if (type == FF_JPEGXL_CT_SPOT_COLOR)
> +        jxl_bits_skip(16 * 4);
> +
> +    if (type == FF_JPEGXL_CT_CFA)
> +        jxl_u32(1, 0, 3, 19, 0, 2, 4, 8);
> +
> +    return 0;
> +}
> +
> +/* verify that a codestream header is valid */
> +int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen)
> +{
> +    GetBitContext gbi, *gb = &gbi;
> +    int all_default, extra_fields = 0;
> +    int xyb_encoded = 1, have_icc_profile = 0;
> +    uint32_t num_extra_channels;
> +    uint64_t extensions;
> +
> +    init_get_bits8(gb, buf, buflen);
> +
> +    if (jxl_bits(16) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE)
> +        return -1;
> +
> +    if (jpegxl_read_size_header(gb) < 0)
> +        return -1;
> +
> +    all_default = jxl_bits(1);
> +    if (!all_default)
> +        extra_fields = jxl_bits(1);
> +
> +    if (extra_fields) {
> +        jxl_bits_skip(3); /* orientation */
> +
> +        /*
> +         * intrinstic size
> +         * any size header here is valid, but as it
> +         * is variable length we have to read it
> +         */
> +        if (jxl_bits(1))
> +            jpegxl_read_size_header(gb);
> +
> +        /* preview header */
> +        if (jxl_bits(1)) {
> +            if (jpegxl_read_preview_header(gb) < 0)
> +                return -1;
> +        }
> +
> +        /* animation header */
> +        if (jxl_bits(1)) {
> +            jxl_u32(100, 1000, 1, 1, 0, 0, 10, 30);
> +            jxl_u32(1, 1001, 1, 1, 0, 0, 8, 10);
> +            jxl_u32(0, 0, 0, 0, 0, 3, 16, 32);
> +            jxl_bits_skip(1);
> +        }
> +    }
> +
> +    if (!all_default) {
> +        jpegxl_skip_bit_depth(gb);
> +
> +        /* modular_16bit_buffers must equal 1 */
> +        if (!jxl_bits(1))
> +            return -1;
> +
> +        num_extra_channels = jxl_u32(0, 1, 2, 1, 0, 0, 4, 12);
> +        if (num_extra_channels > 4)
> +            return -1;
> +        for (uint32_t i = 0; i < num_extra_channels; i++) {
> +            if (jpegxl_read_extra_channel_info(gb) < 0)
> +                return -1;
> +        }
> +
> +        xyb_encoded = jxl_bits(1);
> +
> +        /* color encoding bundle */
> +        if (!jxl_bits(1)) {
> +            uint32_t color_space;
> +            have_icc_profile = jxl_bits(1);
> +            color_space = jxl_enum();
> +            if (color_space > 63)
> +                return -1;
> +
> +            if (!have_icc_profile) {
> +                if (color_space != FF_JPEGXL_CS_XYB) {
> +                    uint32_t white_point = jxl_enum();
> +                    if (white_point > 63)
> +                        return -1;
> +                    if (white_point == FF_JPEGXL_WP_CUSTOM) {
> +                        /* ux and uy values */
> +                        jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
> +                        jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
> +                    }
> +                    if (color_space != FF_JPEGXL_CS_GRAY) {
> +                        /* primaries */
> +                        uint32_t primaries = jxl_enum();
> +                        if (primaries > 63)
> +                            return -1;
> +                        if (primaries == FF_JPEGXL_PR_CUSTOM) {
> +                            /* ux/uy values for r,g,b */
> +                            for (int i = 0; i < 6; i++)
> +                                jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
> +                        }
> +                    }
> +                }
> +
> +                /* transfer characteristics */
> +                if (jxl_bits(1)) {
> +                    /* gamma */
> +                    jxl_bits_skip(24);
> +                } else {
> +                    /* transfer function */
> +                    if (jxl_enum() > 63)
> +                        return -1;
> +                }
> +
> +                /* rendering intent */
> +                if (jxl_enum() > 63)
> +                    return -1;
> +            }
> +        }
> +
> +        /* tone mapping bundle */
> +        if (extra_fields && !jxl_bits(1))
> +            jxl_bits_skip(16 + 16 + 1 + 16);
> +
> +        extensions = jxl_u64();
> +        if (extensions) {
> +            for (int i = 0; i < 64; i++) {
> +                if (extensions & (UINT64_C(1) << i))
> +                    jxl_u64();
> +            }
> +        }
> +    }
> +
> +    /* default transform */
> +    if (!jxl_bits(1)) {
> +        /* opsin inverse matrix */
> +        if (xyb_encoded && !jxl_bits(1))
> +            jxl_bits_skip(16 * 16);
> +        /* cw_mask and default weights */
> +        if (jxl_bits(1))
> +            jxl_bits_skip(16 * 15);
> +        if (jxl_bits(1))
> +            jxl_bits_skip(16 * 55);
> +        if (jxl_bits(1))
> +            jxl_bits_skip(16 * 210);
> +    }
> +
> +    if (!have_icc_profile) {
> +        int bits_remaining = 7 - (gb->index - 1) % 8;

Try to avoid accessing GetBitContext members directly; here: Use
get_bits_count().

> +        if (bits_remaining && jxl_bits(bits_remaining))
> +            return -1;
> +    }
> +
> +    if (gb->index > gb->size_in_bits)

get_bits_left(gb) < 0

> +        return -1;
> +
> +    return 0;
> +}

For the record: I'm not really ok with duplicating this code in lavf and
lavc.

> diff --git a/libavformat/jpegxl_probe.h b/libavformat/jpegxl_probe.h
> new file mode 100644
> index 0000000000..2960e81e11
> --- /dev/null
> +++ b/libavformat/jpegxl_probe.h
> @@ -0,0 +1,32 @@
> +/*
> + * Jpeg XL header verification
> + * Copyright (c) 2022 Leo Izen <leo.izen at gmail.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVFORMAT_JPEGXL_PROBE_H
> +#define AVFORMAT_JPEGXL_PROBE_H
> +
> +#include <stdint.h>
> +
> +#define FF_JPEGXL_CODESTREAM_SIGNATURE_LE 0x0aff
> +#define FF_JPEGXL_CONTAINER_SIGNATURE_LE 0x204c584a0c000000
> +
> +int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen);
> +
> +#endif /* AVFORMAT_JPEGXL_PROBE_H */
> diff --git a/libavformat/mov.c b/libavformat/mov.c
> index 6c847de164..c4b8873b0a 100644
> --- a/libavformat/mov.c
> +++ b/libavformat/mov.c
> @@ -7697,6 +7697,7 @@ static int mov_probe(const AVProbeData *p)
>              if (tag == MKTAG('f','t','y','p') &&
>                         (   AV_RL32(p->buf + offset + 8) == MKTAG('j','p','2',' ')
>                          || AV_RL32(p->buf + offset + 8) == MKTAG('j','p','x',' ')
> +                        || AV_RL32(p->buf + offset + 8) == MKTAG('j','x','l',' ')
>                      )) {
>                  score = FFMAX(score, 5);
>              } else {



More information about the ffmpeg-devel mailing list