Skip to content

Commit 883c31d

Browse files
Neopalliumchafey
andauthored
Add support for partial bitstream decoding (#1407) (fixes #715)
Add a -allow-partial option to opj_decompress utility and a opj_decoder_set_strict_mode() option to the API Co-authored-by: Chris Hafey <[email protected]>
1 parent 99d555c commit 883c31d

File tree

9 files changed

+147
-17
lines changed

9 files changed

+147
-17
lines changed

src/bin/jp2/opj_decompress.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ typedef struct opj_decompress_params {
153153
int num_threads;
154154
/* Quiet */
155155
int quiet;
156+
/* Allow partial decode */
157+
int allow_partial;
156158
/** number of components to decode */
157159
OPJ_UINT32 numcomps;
158160
/** indices of components to decode */
@@ -246,6 +248,8 @@ static void decode_help_display(void)
246248
fprintf(stdout, " -threads <num_threads|ALL_CPUS>\n"
247249
" Number of threads to use for decoding or ALL_CPUS for all available cores.\n");
248250
}
251+
fprintf(stdout, " -allow-partial\n"
252+
" Disable strict mode to allow decoding partial codestreams.\n");
249253
fprintf(stdout, " -quiet\n"
250254
" Disable output from the library and other output.\n");
251255
/* UniPG>> */
@@ -601,6 +605,7 @@ int parse_cmdline_decoder(int argc, char **argv,
601605
{"split-pnm", NO_ARG, NULL, 1},
602606
{"threads", REQ_ARG, NULL, 'T'},
603607
{"quiet", NO_ARG, NULL, 1},
608+
{"allow-partial", NO_ARG, NULL, 1},
604609
};
605610

606611
const char optlist[] = "i:o:r:l:x:d:t:p:c:"
@@ -616,6 +621,7 @@ int parse_cmdline_decoder(int argc, char **argv,
616621
long_option[3].flag = &(parameters->upsample);
617622
long_option[4].flag = &(parameters->split_pnm);
618623
long_option[6].flag = &(parameters->quiet);
624+
long_option[7].flag = &(parameters->allow_partial);
619625
totlen = sizeof(long_option);
620626
opj_reset_options_reading();
621627
img_fol->set_out_format = 0;
@@ -1491,6 +1497,16 @@ int main(int argc, char **argv)
14911497
goto fin;
14921498
}
14931499

1500+
/* Disable strict mode if we want to decode partial codestreams. */
1501+
if (parameters.allow_partial &&
1502+
!opj_decoder_set_strict_mode(l_codec, OPJ_FALSE)) {
1503+
fprintf(stderr, "ERROR -> opj_decompress: failed to disable strict mode\n");
1504+
opj_stream_destroy(l_stream);
1505+
opj_destroy_codec(l_codec);
1506+
failed = 1;
1507+
goto fin;
1508+
}
1509+
14941510
if (parameters.num_threads >= 1 &&
14951511
!opj_codec_set_threads(l_codec, parameters.num_threads)) {
14961512
fprintf(stderr, "ERROR -> opj_decompress: failed to set number of threads\n");

src/lib/openjp2/j2k.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4964,9 +4964,14 @@ static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k,
49644964
/* Check enough bytes left in stream before allocation */
49654965
if ((OPJ_OFF_T)p_j2k->m_specific_param.m_decoder.m_sot_length >
49664966
opj_stream_get_number_byte_left(p_stream)) {
4967-
opj_event_msg(p_manager, EVT_ERROR,
4968-
"Tile part length size inconsistent with stream length\n");
4969-
return OPJ_FALSE;
4967+
if (p_j2k->m_cp.strict) {
4968+
opj_event_msg(p_manager, EVT_ERROR,
4969+
"Tile part length size inconsistent with stream length\n");
4970+
return OPJ_FALSE;
4971+
} else {
4972+
opj_event_msg(p_manager, EVT_WARNING,
4973+
"Tile part length size inconsistent with stream length\n");
4974+
}
49704975
}
49714976
if (p_j2k->m_specific_param.m_decoder.m_sot_length >
49724977
UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA) {
@@ -6695,6 +6700,13 @@ void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters)
66956700
}
66966701
}
66976702

6703+
void opj_j2k_decoder_set_strict_mode(opj_j2k_t *j2k, OPJ_BOOL strict)
6704+
{
6705+
if (j2k) {
6706+
j2k->m_cp.strict = strict;
6707+
}
6708+
}
6709+
66986710
OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads)
66996711
{
67006712
/* Currently we pass the thread-pool to the tcd, so we cannot re-set it */
@@ -10409,6 +10421,9 @@ opj_j2k_t* opj_j2k_create_decompress(void)
1040910421
/* per component is allowed */
1041010422
l_j2k->m_cp.allow_different_bit_depth_sign = 1;
1041110423

10424+
/* Default to using strict mode. */
10425+
l_j2k->m_cp.strict = OPJ_TRUE;
10426+
1041210427
#ifdef OPJ_DISABLE_TPSOT_FIX
1041310428
l_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1;
1041410429
#endif

src/lib/openjp2/j2k.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ typedef struct opj_cp {
402402
}
403403
m_specific_param;
404404

405+
/** OPJ_TRUE if entire bit stream must be decoded, OPJ_FALSE if partial bitstream decoding allowed */
406+
OPJ_BOOL strict;
405407

406408
/* UniPG>> */
407409
#ifdef USE_JPWL
@@ -625,6 +627,8 @@ Decoding parameters are returned in j2k->cp.
625627
*/
626628
void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters);
627629

630+
void opj_j2k_decoder_set_strict_mode(opj_j2k_t *j2k, OPJ_BOOL strict);
631+
628632
OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads);
629633

630634
/**

src/lib/openjp2/jp2.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,11 @@ void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters)
19011901
OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
19021902
}
19031903

1904+
void opj_jp2_decoder_set_strict_mode(opj_jp2_t *jp2, OPJ_BOOL strict)
1905+
{
1906+
opj_j2k_decoder_set_strict_mode(jp2->j2k, strict);
1907+
}
1908+
19041909
OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads)
19051910
{
19061911
return opj_j2k_set_threads(jp2->j2k, num_threads);

src/lib/openjp2/jp2.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,15 @@ Decoding parameters are returned in jp2->j2k->cp.
235235
*/
236236
void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters);
237237

238+
/**
239+
Set the strict mode parameter. When strict mode is enabled, the entire
240+
bitstream must be decoded or an error is returned. When it is disabled,
241+
the decoder will decode partial bitstreams.
242+
@param jp2 JP2 decompressor handle
243+
@param strict OPJ_TRUE for strict mode
244+
*/
245+
void opj_jp2_decoder_set_strict_mode(opj_jp2_t *jp2, OPJ_BOOL strict);
246+
238247
/** Allocates worker threads for the compressor/decompressor.
239248
*
240249
* @param jp2 JP2 decompressor handle

src/lib/openjp2/openjpeg.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ opj_codec_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT p_format)
219219
l_codec->m_codec_data.m_decompression.opj_setup_decoder =
220220
(void (*)(void *, opj_dparameters_t *)) opj_j2k_setup_decoder;
221221

222+
l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode =
223+
(void (*)(void *, OPJ_BOOL)) opj_j2k_decoder_set_strict_mode;
224+
225+
222226
l_codec->m_codec_data.m_decompression.opj_read_tile_header =
223227
(OPJ_BOOL(*)(void *,
224228
OPJ_UINT32*,
@@ -326,6 +330,9 @@ opj_codec_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT p_format)
326330
l_codec->m_codec_data.m_decompression.opj_setup_decoder =
327331
(void (*)(void *, opj_dparameters_t *)) opj_jp2_setup_decoder;
328332

333+
l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode =
334+
(void (*)(void *, OPJ_BOOL)) opj_jp2_decoder_set_strict_mode;
335+
329336
l_codec->m_codec_data.m_decompression.opj_set_decode_area =
330337
(OPJ_BOOL(*)(void *,
331338
opj_image_t*,
@@ -426,6 +433,26 @@ OPJ_BOOL OPJ_CALLCONV opj_setup_decoder(opj_codec_t *p_codec,
426433
return OPJ_FALSE;
427434
}
428435

436+
OPJ_API OPJ_BOOL OPJ_CALLCONV opj_decoder_set_strict_mode(opj_codec_t *p_codec,
437+
OPJ_BOOL strict)
438+
{
439+
if (p_codec) {
440+
opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
441+
442+
if (! l_codec->is_decompressor) {
443+
opj_event_msg(&(l_codec->m_event_mgr), EVT_ERROR,
444+
"Codec provided to the opj_decoder_set_strict_mode function is not a decompressor handler.\n");
445+
return OPJ_FALSE;
446+
}
447+
448+
l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode(
449+
l_codec->m_codec,
450+
strict);
451+
return OPJ_TRUE;
452+
}
453+
return OPJ_FALSE;
454+
}
455+
429456
OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
430457
opj_codec_t *p_codec,
431458
opj_image_t **p_image)

src/lib/openjp2/openjpeg.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,20 @@ OPJ_API void OPJ_CALLCONV opj_set_default_decoder_parameters(
13451345
OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_decoder(opj_codec_t *p_codec,
13461346
opj_dparameters_t *parameters);
13471347

1348+
/**
1349+
* Set strict decoding parameter for this decoder. If strict decoding is enabled, partial bit
1350+
* streams will fail to decode. If strict decoding is disabled, the decoder will decode partial
1351+
* bitstreams as much as possible without erroring
1352+
*
1353+
* @param p_codec decompressor handler
1354+
* @param strict OPJ_TRUE to enable strict decoding, OPJ_FALSE to disable
1355+
*
1356+
* @return true if the decoder is correctly set
1357+
*/
1358+
1359+
OPJ_API OPJ_BOOL OPJ_CALLCONV opj_decoder_set_strict_mode(opj_codec_t *p_codec,
1360+
OPJ_BOOL strict);
1361+
13481362
/**
13491363
* Allocates worker threads for the compressor/decompressor.
13501364
*

src/lib/openjp2/opj_codec.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ typedef struct opj_codec_private {
9090
/** Setup decoder function handler */
9191
void (*opj_setup_decoder)(void * p_codec, opj_dparameters_t * p_param);
9292

93+
/** Strict mode function handler */
94+
void (*opj_decoder_set_strict_mode)(void * p_codec, OPJ_BOOL strict);
95+
9396
/** Set decode area function handler */
9497
OPJ_BOOL(*opj_set_decode_area)(void * p_codec,
9598
opj_image_t * p_image,

src/lib/openjp2/t2.c

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,6 @@ OPJ_BOOL opj_t2_decode_packets(opj_tcd_t* tcd,
502502
l_current_pi->precno, l_current_pi->layno, skip_packet ? "skipped" : "kept");
503503
*/
504504
}
505-
506505
if (!skip_packet) {
507506
l_nb_bytes_read = 0;
508507

@@ -1378,6 +1377,7 @@ static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
13781377
opj_tcd_cblk_dec_t* l_cblk = 00;
13791378
opj_tcd_resolution_t* l_res =
13801379
&p_tile->comps[p_pi->compno].resolutions[p_pi->resno];
1380+
OPJ_BOOL partial_buffer = OPJ_FALSE;
13811381

13821382
OPJ_ARG_NOT_USED(p_t2);
13831383
OPJ_ARG_NOT_USED(pack_info);
@@ -1397,6 +1397,12 @@ static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
13971397
for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) {
13981398
opj_tcd_seg_t *l_seg = 00;
13991399

1400+
// if we have a partial data stream, set numchunks to zero
1401+
// since we have no data to actually decode.
1402+
if (partial_buffer) {
1403+
l_cblk->numchunks = 0;
1404+
}
1405+
14001406
if (!l_cblk->numnewpasses) {
14011407
/* nothing to do */
14021408
++l_cblk;
@@ -1419,12 +1425,32 @@ static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
14191425
/* Check possible overflow (on l_current_data only, assumes input args already checked) then size */
14201426
if ((((OPJ_SIZE_T)l_current_data + (OPJ_SIZE_T)l_seg->newlen) <
14211427
(OPJ_SIZE_T)l_current_data) ||
1422-
(l_current_data + l_seg->newlen > p_src_data + p_max_length)) {
1423-
opj_event_msg(p_manager, EVT_ERROR,
1424-
"read: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
1425-
l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
1426-
p_pi->compno);
1427-
return OPJ_FALSE;
1428+
(l_current_data + l_seg->newlen > p_src_data + p_max_length) ||
1429+
(partial_buffer)) {
1430+
if (p_t2->cp->strict) {
1431+
opj_event_msg(p_manager, EVT_ERROR,
1432+
"read: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
1433+
l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
1434+
p_pi->compno);
1435+
return OPJ_FALSE;
1436+
} else {
1437+
opj_event_msg(p_manager, EVT_WARNING,
1438+
"read: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
1439+
l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
1440+
p_pi->compno);
1441+
// skip this codeblock since it is a partial read
1442+
partial_buffer = OPJ_TRUE;
1443+
l_cblk->numchunks = 0;
1444+
1445+
l_seg->numpasses += l_seg->numnewpasses;
1446+
l_cblk->numnewpasses -= l_seg->numnewpasses;
1447+
if (l_cblk->numnewpasses > 0) {
1448+
++l_seg;
1449+
++l_cblk->numsegs;
1450+
break;
1451+
}
1452+
continue;
1453+
}
14281454
}
14291455

14301456
#ifdef USE_JPWL
@@ -1486,8 +1512,12 @@ static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
14861512
++l_band;
14871513
}
14881514

1489-
*(p_data_read) = (OPJ_UINT32)(l_current_data - p_src_data);
1490-
1515+
// return the number of bytes read
1516+
if (partial_buffer) {
1517+
*(p_data_read) = p_max_length;
1518+
} else {
1519+
*(p_data_read) = (OPJ_UINT32)(l_current_data - p_src_data);
1520+
}
14911521

14921522
return OPJ_TRUE;
14931523
}
@@ -1549,11 +1579,18 @@ static OPJ_BOOL opj_t2_skip_packet_data(opj_t2_t* p_t2,
15491579
/* Check possible overflow then size */
15501580
if (((*p_data_read + l_seg->newlen) < (*p_data_read)) ||
15511581
((*p_data_read + l_seg->newlen) > p_max_length)) {
1552-
opj_event_msg(p_manager, EVT_ERROR,
1553-
"skip: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
1554-
l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
1555-
p_pi->compno);
1556-
return OPJ_FALSE;
1582+
if (p_t2->cp->strict) {
1583+
opj_event_msg(p_manager, EVT_ERROR,
1584+
"skip: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
1585+
l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
1586+
p_pi->compno);
1587+
return OPJ_FALSE;
1588+
} else {
1589+
opj_event_msg(p_manager, EVT_WARNING,
1590+
"skip: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
1591+
l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
1592+
p_pi->compno);
1593+
}
15571594
}
15581595

15591596
#ifdef USE_JPWL

0 commit comments

Comments
 (0)