2 * Animated JPEG XL Demuxer
3 * Copyright (c) 2023 Leo Izen (thebombzen)
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * Animated JPEG XL Demuxer
25 * @see ISO/IEC 18181-1 and 18181-2
31 #include "libavcodec/jpegxl.h"
32 #include "libavcodec/jpegxl_parse.h"
33 #include "libavutil/intreadwrite.h"
34 #include "libavutil/opt.h"
40 typedef struct JXLAnimDemuxContext
{
42 } JXLAnimDemuxContext
;
44 static int jpegxl_anim_probe(const AVProbeData
*p
)
46 uint8_t buffer
[4096 + AV_INPUT_BUFFER_PADDING_SIZE
] = {0};
48 FFJXLMetadata meta
= { 0 };
50 /* this is a raw codestream */
51 if (AV_RL16(p
->buf
) == FF_JPEGXL_CODESTREAM_SIGNATURE_LE
) {
52 ret
= ff_jpegxl_parse_codestream_header(p
->buf
, p
->buf_size
, &meta
, 5);
53 if (ret
>= 0 && meta
.animation_offset
> 0)
54 return AVPROBE_SCORE_MAX
;
59 /* not a JPEG XL file at all */
60 if (AV_RL64(p
->buf
) != FF_JPEGXL_CONTAINER_SIGNATURE_LE
)
63 if (ff_jpegxl_collect_codestream_header(p
->buf
, p
->buf_size
, buffer
,
64 sizeof(buffer
) - AV_INPUT_BUFFER_PADDING_SIZE
, &copied
) <= 0
68 ret
= ff_jpegxl_parse_codestream_header(buffer
, copied
, &meta
, 10);
69 if (ret
>= 0 && meta
.animation_offset
> 0)
70 return AVPROBE_SCORE_MAX
;
75 static int jpegxl_anim_read_header(AVFormatContext
*s
)
77 JXLAnimDemuxContext
*ctx
= s
->priv_data
;
78 AVIOContext
*pb
= s
->pb
;
80 uint8_t head
[256 + AV_INPUT_BUFFER_PADDING_SIZE
];
81 const int sizeofhead
= sizeof(head
) - AV_INPUT_BUFFER_PADDING_SIZE
;
82 int headsize
= 0, ret
;
83 FFJXLMetadata meta
= { 0 };
85 uint64_t sig16
= avio_rl16(pb
);
86 if (sig16
== FF_JPEGXL_CODESTREAM_SIGNATURE_LE
) {
88 headsize
= avio_read(s
->pb
, head
+ 2, sizeofhead
- 2);
92 ctx
->initial
= av_buffer_alloc(headsize
);
94 return AVERROR(ENOMEM
);
95 memcpy(ctx
->initial
->data
, head
, headsize
);
97 uint64_t sig64
= avio_rl64(pb
);
98 sig64
= (sig64
<< 16) | sig16
;
99 if (sig64
!= FF_JPEGXL_CONTAINER_SIGNATURE_LE
)
100 return AVERROR_INVALIDDATA
;
101 avio_skip(pb
, 2); // first box always 12 bytes
105 int read
= avio_read(pb
, buf
, sizeof(buf
));
109 ctx
->initial
= av_buffer_alloc(read
+ 12);
111 return AVERROR(ENOMEM
);
112 AV_WL64(ctx
->initial
->data
, FF_JPEGXL_CONTAINER_SIGNATURE_LE
);
113 AV_WL32(ctx
->initial
->data
+ 8, 0x0a870a0d);
115 /* this only should be happening zero or one times in practice */
116 if (av_buffer_realloc(&ctx
->initial
, ctx
->initial
->size
+ read
) < 0)
117 return AVERROR(ENOMEM
);
119 ff_jpegxl_collect_codestream_header(buf
, read
, head
+ headsize
, sizeofhead
- headsize
, &copied
);
120 memcpy(ctx
->initial
->data
+ (ctx
->initial
->size
- read
), buf
, read
);
122 if (headsize
>= sizeofhead
|| read
< sizeof(buf
))
127 memset(head
+ headsize
, 0, AV_INPUT_BUFFER_PADDING_SIZE
);
129 /* offset in bits of the animation header */
130 ret
= ff_jpegxl_parse_codestream_header(head
, headsize
, &meta
, 0);
131 if (ret
< 0 || meta
.animation_offset
<= 0)
132 return AVERROR_INVALIDDATA
;
134 st
= avformat_new_stream(s
, NULL
);
136 return AVERROR(ENOMEM
);
138 st
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
139 st
->codecpar
->codec_id
= AV_CODEC_ID_JPEGXL_ANIM
;
140 avpriv_set_pts_info(st
, 1, meta
.timebase
.num
, meta
.timebase
.den
);
141 ffstream(st
)->need_parsing
= AVSTREAM_PARSE_FULL
;
146 /* the decoder requires the full input file as a single packet */
147 static int jpegxl_anim_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
149 JXLAnimDemuxContext
*ctx
= s
->priv_data
;
150 AVIOContext
*pb
= s
->pb
;
155 size
= avio_size(pb
);
159 return AVERROR(EDOM
);
163 if (ctx
->initial
&& size
< ctx
->initial
->size
)
164 size
= ctx
->initial
->size
;
166 ret
= av_new_packet(pkt
, size
);
171 offset
= ctx
->initial
->size
;
172 memcpy(pkt
->data
, ctx
->initial
->data
, offset
);
173 av_buffer_unref(&ctx
->initial
);
176 pkt
->pos
= avio_tell(pb
) - offset
;
178 ret
= avio_read(pb
, pkt
->data
+ offset
, size
- offset
);
181 if (ret
< size
- offset
)
182 pkt
->size
= ret
+ offset
;
187 static int jpegxl_anim_close(AVFormatContext
*s
)
189 JXLAnimDemuxContext
*ctx
= s
->priv_data
;
191 av_buffer_unref(&ctx
->initial
);
196 const FFInputFormat ff_jpegxl_anim_demuxer
= {
197 .p
.name
= "jpegxl_anim",
198 .p
.long_name
= NULL_IF_CONFIG_SMALL("Animated JPEG XL"),
199 .p
.flags
= AVFMT_GENERIC_INDEX
| AVFMT_NOTIMESTAMPS
,
200 .p
.mime_type
= "image/jxl",
201 .p
.extensions
= "jxl",
202 .priv_data_size
= sizeof(JXLAnimDemuxContext
),
203 .read_probe
= jpegxl_anim_probe
,
204 .read_header
= jpegxl_anim_read_header
,
205 .read_packet
= jpegxl_anim_read_packet
,
206 .read_close
= jpegxl_anim_close
,
207 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,