Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* JPEG2000 parser |
3 |
|
|
* Copyright (c) 2020 Gautam Ramakrishnan |
4 |
|
|
* |
5 |
|
|
* This file is part of FFmpeg. |
6 |
|
|
* |
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. |
11 |
|
|
* |
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. |
16 |
|
|
* |
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 |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
/** |
23 |
|
|
* @file |
24 |
|
|
* JPEG2000 parser. |
25 |
|
|
*/ |
26 |
|
|
|
27 |
|
|
#include "parser.h" |
28 |
|
|
|
29 |
|
|
/* Whether frame is jp2 file or codestream |
30 |
|
|
*/ |
31 |
|
|
enum frame_type { |
32 |
|
|
jp2_file = 1, |
33 |
|
|
j2k_cstream |
34 |
|
|
}; |
35 |
|
|
|
36 |
|
|
typedef struct JPEG2000ParserContext { |
37 |
|
|
ParseContext pc; |
38 |
|
|
uint64_t bytes_read; |
39 |
|
|
uint64_t fheader_state; |
40 |
|
|
uint32_t skip_bytes; // skip bytes inside codestream data |
41 |
|
|
enum frame_type ft; // 1 if file, 2 if codestream |
42 |
|
|
uint8_t fheader_read; // are we reading |
43 |
|
|
uint8_t reading_file_header; |
44 |
|
|
uint8_t skipped_codestream; |
45 |
|
|
uint8_t read_tp; |
46 |
|
|
uint8_t in_codestream; |
47 |
|
|
} JPEG2000ParserContext; |
48 |
|
|
|
49 |
|
✗ |
static inline void reset_context(JPEG2000ParserContext *m) |
50 |
|
|
{ |
51 |
|
✗ |
ParseContext *pc = &m->pc; |
52 |
|
|
|
53 |
|
✗ |
pc->frame_start_found= 0; |
54 |
|
✗ |
pc->state64 = 0; |
55 |
|
✗ |
m->bytes_read = 0; |
56 |
|
✗ |
m->ft = 0; |
57 |
|
✗ |
m->skipped_codestream = 0; |
58 |
|
✗ |
m->fheader_read = 0; |
59 |
|
✗ |
m->skip_bytes = 0; |
60 |
|
✗ |
m->read_tp = 0; |
61 |
|
✗ |
m->in_codestream = 0; |
62 |
|
|
} |
63 |
|
|
|
64 |
|
|
/* Returns 1 if marker has any data which can be skipped |
65 |
|
|
*/ |
66 |
|
✗ |
static uint8_t info_marker(uint16_t marker) |
67 |
|
|
{ |
68 |
|
|
static const uint8_t lut[256] = { |
69 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
70 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
71 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
72 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
73 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 0xFF4F |
74 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
75 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
76 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
77 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
78 |
|
|
0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xFF90 0xFF92 0xFF93 |
79 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
80 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
81 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
82 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, // 0xFFD9 |
83 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
84 |
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
85 |
|
|
}; |
86 |
|
✗ |
return marker < 0xFF00 ? 0 : lut[marker & 0xFF]; |
87 |
|
|
} |
88 |
|
|
|
89 |
|
|
/** |
90 |
|
|
* Find the end of the current frame in the bitstream. |
91 |
|
|
* @return the position of the first byte of the next frame, or -1 |
92 |
|
|
*/ |
93 |
|
✗ |
static int find_frame_end(JPEG2000ParserContext *m, const uint8_t *buf, int buf_size) |
94 |
|
|
{ |
95 |
|
✗ |
ParseContext *pc= &m->pc; |
96 |
|
|
int i; |
97 |
|
✗ |
uint64_t state64 = pc->state64; |
98 |
|
✗ |
uint64_t bytes_read = m->bytes_read; |
99 |
|
|
|
100 |
|
✗ |
if (buf_size == 0) { |
101 |
|
✗ |
return 0; |
102 |
|
|
} |
103 |
|
|
|
104 |
|
✗ |
for (i = 0; i < buf_size; i++) { |
105 |
|
✗ |
state64 = state64 << 8 | buf[i]; |
106 |
|
✗ |
bytes_read++; |
107 |
|
✗ |
if (m->skip_bytes) { |
108 |
|
|
// handle long skips |
109 |
|
✗ |
if (m->skip_bytes > 8) { |
110 |
|
|
// need -9 else buf_size - i == 8 ==> i == buf_size after this, |
111 |
|
|
// and thus i == buf_size + 1 after the loop |
112 |
|
✗ |
int skip = FFMIN(FFMIN((int64_t)m->skip_bytes - 8, buf_size - i - 9), INT_MAX); |
113 |
|
✗ |
if (skip > 0) { |
114 |
|
✗ |
m->skip_bytes -= skip; |
115 |
|
✗ |
i += skip; |
116 |
|
✗ |
bytes_read += skip; |
117 |
|
|
} |
118 |
|
|
} |
119 |
|
✗ |
m->skip_bytes--; |
120 |
|
✗ |
continue; |
121 |
|
|
} |
122 |
|
✗ |
if (m->read_tp) { // Find out how many bytes inside Tile part codestream to skip. |
123 |
|
✗ |
if (m->read_tp == 1) { |
124 |
|
✗ |
m->skip_bytes = (state64 & 0xFFFFFFFF) - 9 > 0? |
125 |
|
✗ |
(state64 & 0xFFFFFFFF) - 9 : 0; |
126 |
|
|
} |
127 |
|
✗ |
m->read_tp--; |
128 |
|
✗ |
continue; |
129 |
|
|
} |
130 |
|
✗ |
if (m->fheader_read) { |
131 |
|
✗ |
if (m->fheader_read == 1) { |
132 |
|
✗ |
if (state64 == 0x6A5020200D0A870A) { // JP2 signature box value. |
133 |
|
✗ |
if (pc->frame_start_found) { |
134 |
|
✗ |
pc->frame_start_found = 0; |
135 |
|
✗ |
reset_context(m); |
136 |
|
✗ |
return i - 11; |
137 |
|
|
} else { |
138 |
|
✗ |
pc->frame_start_found = 1; |
139 |
|
✗ |
m->ft = jp2_file; |
140 |
|
|
} |
141 |
|
|
} |
142 |
|
|
} |
143 |
|
✗ |
m->fheader_read--; |
144 |
|
|
} |
145 |
|
✗ |
if ((state64 & 0xFFFFFFFF) == 0x0000000C && bytes_read >= 3) { // Indicates start of JP2 file. Check signature next. |
146 |
|
✗ |
m->fheader_read = 8; |
147 |
|
✗ |
} else if ((state64 & 0xFFFF) == 0xFF4F) { |
148 |
|
✗ |
m->in_codestream = 1; |
149 |
|
✗ |
if (!pc->frame_start_found) { |
150 |
|
✗ |
pc->frame_start_found = 1; |
151 |
|
✗ |
m->ft = j2k_cstream; |
152 |
|
✗ |
} else if (pc->frame_start_found && m->ft == jp2_file && m->skipped_codestream) { |
153 |
|
✗ |
reset_context(m); |
154 |
|
✗ |
return i - 1; |
155 |
|
|
} |
156 |
|
✗ |
} else if ((state64 & 0xFFFF) == 0xFFD9) { |
157 |
|
✗ |
if (pc->frame_start_found && m->ft == jp2_file) { |
158 |
|
✗ |
m->skipped_codestream = 1; |
159 |
|
✗ |
} else if (pc->frame_start_found && m->ft == j2k_cstream) { |
160 |
|
✗ |
reset_context(m); |
161 |
|
✗ |
return i + 1; // End of frame detected, return frame size. |
162 |
|
|
} |
163 |
|
✗ |
m->in_codestream = 0; |
164 |
|
✗ |
} else if (m->in_codestream) { |
165 |
|
✗ |
if ((state64 & 0xFFFF) == 0xFF90) { // Are we in tile part header? |
166 |
|
✗ |
m->read_tp = 8; |
167 |
|
✗ |
} else if (info_marker((state64 & 0xFFFF0000)>>16) && pc->frame_start_found && (state64 & 0xFFFF)) { |
168 |
|
|
// Calculate number of bytes to skip to get to end of the next marker. |
169 |
|
✗ |
m->skip_bytes = (state64 & 0xFFFF)-1; |
170 |
|
|
|
171 |
|
|
// If the next marker is an info marker, skip to the end of of the marker length. |
172 |
|
✗ |
if (i + m->skip_bytes + 1 < buf_size) { |
173 |
|
✗ |
uint32_t next_state = (buf[i + m->skip_bytes] << 8) | buf[i + m->skip_bytes + 1]; |
174 |
|
✗ |
if (info_marker(next_state)) { |
175 |
|
|
// Skip an additional 2 bytes to get to the end of the marker length. |
176 |
|
✗ |
m->skip_bytes += 2; |
177 |
|
|
} |
178 |
|
|
} |
179 |
|
|
} |
180 |
|
|
} |
181 |
|
|
} |
182 |
|
|
|
183 |
|
✗ |
pc->state64 = state64; |
184 |
|
✗ |
m->bytes_read = bytes_read; |
185 |
|
✗ |
return END_NOT_FOUND; |
186 |
|
|
} |
187 |
|
|
|
188 |
|
411 |
static int jpeg2000_parse(AVCodecParserContext *s, |
189 |
|
|
AVCodecContext *avctx, |
190 |
|
|
const uint8_t **poutbuf, int *poutbuf_size, |
191 |
|
|
const uint8_t *buf, int buf_size) |
192 |
|
|
{ |
193 |
|
411 |
JPEG2000ParserContext *m = s->priv_data; |
194 |
|
411 |
ParseContext *pc = &m->pc; |
195 |
|
|
int next; |
196 |
|
|
|
197 |
1/2
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
|
411 |
if(s->flags & PARSER_FLAG_COMPLETE_FRAMES) { |
198 |
|
411 |
next= buf_size; |
199 |
|
|
} else { |
200 |
|
✗ |
next= find_frame_end(m, buf, buf_size); |
201 |
|
|
|
202 |
|
✗ |
if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) { |
203 |
|
✗ |
*poutbuf = NULL; |
204 |
|
✗ |
*poutbuf_size = 0; |
205 |
|
✗ |
return buf_size; |
206 |
|
|
} |
207 |
|
|
} |
208 |
|
|
|
209 |
|
411 |
*poutbuf = buf; |
210 |
|
411 |
*poutbuf_size = buf_size; |
211 |
|
411 |
return next; |
212 |
|
|
} |
213 |
|
|
|
214 |
|
|
const AVCodecParser ff_jpeg2000_parser = { |
215 |
|
|
.codec_ids = { AV_CODEC_ID_JPEG2000 }, |
216 |
|
|
.priv_data_size = sizeof(JPEG2000ParserContext), |
217 |
|
|
.parser_parse = jpeg2000_parse, |
218 |
|
|
.parser_close = ff_parse_close, |
219 |
|
|
}; |
220 |
|
|
|