Skip to content

Commit 8a40a8a

Browse files
author
Grok Compression
committed
transcode: add grk_transcode binary
1 parent 5d2d2d7 commit 8a40a8a

3 files changed

Lines changed: 343 additions & 0 deletions

File tree

doc/man/md/man1/grk_transcode.1.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
% grk_transcode(1) Version 10.0 | transcode JPEG 2000 files
2+
3+
NAME
4+
====
5+
6+
grk_transcode - transcode JPEG 2000 files without full decompression
7+
8+
SYNOPSIS
9+
========
10+
11+
| **grk_transcode** \[**-i** infile.jp2] \[**-o** outfile.jp2] \[options]
12+
13+
14+
DESCRIPTION
15+
===========
16+
17+
This program transcodes `JPEG 2000` files (JP2 container format), rewriting
18+
JP2 boxes and optionally modifying the codestream without full decompression.
19+
The codestream is parsed at the packet level (T2 only — entropy decoding is
20+
skipped) and reassembled with the requested modifications.
21+
22+
Supported operations:
23+
24+
* Insert TLM (Tile-part Length) markers for random access
25+
* Insert PLT (Packet Length) markers for random access
26+
* Inject SOP (Start of Packet) markers before each packet
27+
* Inject EPH (End of Packet Header) markers after each packet header
28+
* Truncate quality layers
29+
* Strip resolution levels
30+
* Reorder packet progression
31+
32+
All operations can be combined freely.
33+
34+
Options
35+
-------
36+
37+
`-h, --help`
38+
39+
Print a help message and exit.
40+
41+
`-v, --version`
42+
43+
Print library version and exit.
44+
45+
`-i, --input [file]`
46+
47+
Input JP2 file. Required.
48+
49+
`-o, --output [file]`
50+
51+
Output JP2 file. Required.
52+
53+
`-X, --tlm`
54+
55+
Insert TLM markers in the output codestream. Default: off.
56+
57+
`-L, --plt`
58+
59+
Insert PLT markers in the output codestream. Default: off.
60+
61+
`-S, --sop`
62+
63+
Inject SOP marker before each packet. Default: off.
64+
65+
`-E, --eph`
66+
67+
Inject EPH marker after each packet header. Default: off.
68+
69+
`-n, --max-layers [number of layers]`
70+
71+
Keep at most this many quality layers. Packets belonging to higher layers are
72+
discarded. A value of 0 (default) keeps all layers.
73+
74+
`-R, --max-res [number of resolutions]`
75+
76+
Keep at most this many resolution levels. Packets belonging to higher resolutions
77+
are discarded. A value of 0 (default) keeps all resolutions.
78+
79+
`-p, --progression [LRCP|RLCP|RPCL|PCRL|CPRL]`
80+
81+
Reorder packets to the specified progression order. Default: preserve original order.
82+
83+
The five progression orders are:
84+
* `LRCP` — Layer-Resolution-Component-Position
85+
* `RLCP` — Resolution-Layer-Component-Position
86+
* `RPCL` — Resolution-Position-Component-Layer
87+
* `PCRL` — Position-Component-Resolution-Layer
88+
* `CPRL` — Component-Position-Resolution-Layer
89+
90+
EXAMPLES
91+
========
92+
93+
Insert TLM and PLT markers:
94+
95+
grk_transcode -i input.jp2 -o output.jp2 -X -L
96+
97+
Add SOP/EPH markers and change progression to RLCP:
98+
99+
grk_transcode -i input.jp2 -o output.jp2 -S -E -p RLCP
100+
101+
Keep only 3 quality layers and 4 resolution levels:
102+
103+
grk_transcode -i input.jp2 -o output.jp2 -n 3 -R 4
104+
105+
FILES
106+
=====
107+
108+
109+
ENVIRONMENT
110+
===========
111+
112+
113+
BUGS
114+
====
115+
116+
See GitHub Issues: https://github.com/GrokImageCompression/grok/issues
117+
118+
AUTHOR
119+
======
120+
121+
Grok Image Compression Inc.
122+
123+
SEE ALSO
124+
========
125+
126+
**grk_compress(1)**, **grk_decompress(1)**

src/bin/codec/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ foreach(exe grk_decompress grk_compress grk_dump)
2727
)
2828
endforeach()
2929

30+
# grk_transcode only needs the core library, not the codec library
31+
add_executable(grk_transcode grk_transcode.cpp)
32+
target_compile_options(grk_transcode PRIVATE ${GROK_COMPILE_OPTIONS})
33+
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
34+
target_link_options(grk_transcode PUBLIC "LINKER:-z,now")
35+
endif()
36+
target_link_libraries(grk_transcode ${GROK_CORE_NAME})
37+
if (GRK_BUILD_PLUGIN_LOADER AND UNIX)
38+
target_link_libraries(grk_transcode ${CMAKE_DL_LIBS})
39+
endif(GRK_BUILD_PLUGIN_LOADER AND UNIX)
40+
install(TARGETS grk_transcode
41+
EXPORT GrokTargets
42+
DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Applications
43+
)
44+
3045
if(GRK_BUILD_DOC)
3146
install(
3247
FILES ${GROK_SOURCE_DIR}/doc/man/man1/grk_compress.1

src/bin/codec/grk_transcode.cpp

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* Copyright (C) 2016-2026 Grok Image Compression Inc.
3+
*
4+
* This source code is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Affero General Public License, version 3,
6+
* as published by the Free Software Foundation.
7+
*
8+
* This source code is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU Affero General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU Affero General Public License
14+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
*
16+
*/
17+
18+
#include <cstdio>
19+
#include <cstdlib>
20+
#include <cstring>
21+
#include <cinttypes>
22+
#include <string>
23+
24+
#include "grok.h"
25+
26+
static void printUsage(const char* prog)
27+
{
28+
fprintf(stderr,
29+
"Usage: %s -i <input.jp2> -o <output.jp2> [options]\n"
30+
"\n"
31+
"Transcode a JPEG 2000 file (JP2), rewriting boxes and optionally\n"
32+
"modifying the codestream without full decompression.\n"
33+
"\n"
34+
"Options:\n"
35+
" -i, --input <file> Input JP2 file (required)\n"
36+
" -o, --output <file> Output JP2 file (required)\n"
37+
" -X, --tlm Insert TLM markers\n"
38+
" -L, --plt Insert PLT markers\n"
39+
" -S, --sop Inject SOP markers before each packet\n"
40+
" -E, --eph Inject EPH markers after each packet header\n"
41+
" -n, --max-layers <N> Keep at most N quality layers (0 = all)\n"
42+
" -R, --max-res <N> Keep at most N resolution levels (0 = all)\n"
43+
" -p, --progression <P> Reorder to progression P\n"
44+
" (LRCP, RLCP, RPCL, PCRL, CPRL)\n"
45+
" -h, --help Print this help message\n"
46+
" -v, --version Print library version\n",
47+
prog);
48+
}
49+
50+
static GRK_PROG_ORDER parseProgOrder(const char* str)
51+
{
52+
if(strcmp(str, "LRCP") == 0)
53+
return GRK_LRCP;
54+
if(strcmp(str, "RLCP") == 0)
55+
return GRK_RLCP;
56+
if(strcmp(str, "RPCL") == 0)
57+
return GRK_RPCL;
58+
if(strcmp(str, "PCRL") == 0)
59+
return GRK_PCRL;
60+
if(strcmp(str, "CPRL") == 0)
61+
return GRK_CPRL;
62+
return GRK_PROG_UNKNOWN;
63+
}
64+
65+
template<size_t N>
66+
static void safe_strcpy(char (&dest)[N], const char* src)
67+
{
68+
size_t len = strnlen(src, N - 1);
69+
memcpy(dest, src, len);
70+
dest[len] = '\0';
71+
}
72+
73+
int main(int argc, char* argv[])
74+
{
75+
const char* inputFile = nullptr;
76+
const char* outputFile = nullptr;
77+
bool writeTlm = false;
78+
bool writePlt = false;
79+
bool writeSop = false;
80+
bool writeEph = false;
81+
uint16_t maxLayers = 0;
82+
uint8_t maxRes = 0;
83+
GRK_PROG_ORDER progOrder = GRK_PROG_UNKNOWN;
84+
85+
for(int i = 1; i < argc; ++i)
86+
{
87+
if((strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--input") == 0) && i + 1 < argc)
88+
inputFile = argv[++i];
89+
else if((strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) && i + 1 < argc)
90+
outputFile = argv[++i];
91+
else if(strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--tlm") == 0)
92+
writeTlm = true;
93+
else if(strcmp(argv[i], "-L") == 0 || strcmp(argv[i], "--plt") == 0)
94+
writePlt = true;
95+
else if(strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--sop") == 0)
96+
writeSop = true;
97+
else if(strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--eph") == 0)
98+
writeEph = true;
99+
else if((strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--max-layers") == 0) && i + 1 < argc)
100+
maxLayers = (uint16_t)atoi(argv[++i]);
101+
else if((strcmp(argv[i], "-R") == 0 || strcmp(argv[i], "--max-res") == 0) && i + 1 < argc)
102+
maxRes = (uint8_t)atoi(argv[++i]);
103+
else if((strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--progression") == 0) && i + 1 < argc)
104+
{
105+
progOrder = parseProgOrder(argv[++i]);
106+
if(progOrder == GRK_PROG_UNKNOWN)
107+
{
108+
fprintf(stderr, "Error: unknown progression order '%s'\n", argv[i]);
109+
return 1;
110+
}
111+
}
112+
else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
113+
{
114+
printUsage(argv[0]);
115+
return 0;
116+
}
117+
else if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0)
118+
{
119+
fprintf(stdout, "grk_transcode using Grok library %s\n", grk_version());
120+
return 0;
121+
}
122+
else
123+
{
124+
fprintf(stderr, "Error: unknown option '%s'\n", argv[i]);
125+
printUsage(argv[0]);
126+
return 1;
127+
}
128+
}
129+
130+
if(!inputFile || !outputFile)
131+
{
132+
fprintf(stderr, "Error: input (-i) and output (-o) files are required\n");
133+
printUsage(argv[0]);
134+
return 1;
135+
}
136+
137+
grk_initialize(nullptr, 0, nullptr);
138+
139+
/* Step 1: Decompress header from source to get image info */
140+
grk_stream_params srcStreamParams{};
141+
safe_strcpy(srcStreamParams.file, inputFile);
142+
143+
grk_decompress_parameters dparams{};
144+
auto* decCodec = grk_decompress_init(&srcStreamParams, &dparams);
145+
if(!decCodec)
146+
{
147+
fprintf(stderr, "Error: failed to init decompressor for '%s'\n", inputFile);
148+
149+
return 1;
150+
}
151+
152+
grk_header_info srcHeader{};
153+
if(!grk_decompress_read_header(decCodec, &srcHeader))
154+
{
155+
fprintf(stderr, "Error: failed to read header from '%s'\n", inputFile);
156+
grk_object_unref(decCodec);
157+
158+
return 1;
159+
}
160+
161+
auto* srcImage = grk_decompress_get_image(decCodec);
162+
if(!srcImage)
163+
{
164+
fprintf(stderr, "Error: failed to get image from '%s'\n", inputFile);
165+
grk_object_unref(decCodec);
166+
167+
return 1;
168+
}
169+
170+
/* Step 2: Set up transcode parameters */
171+
grk_cparameters cparams{};
172+
grk_compress_set_default_params(&cparams);
173+
cparams.cod_format = GRK_FMT_JP2;
174+
cparams.write_tlm = writeTlm;
175+
cparams.write_plt = writePlt;
176+
cparams.write_sop = writeSop;
177+
cparams.write_eph = writeEph;
178+
cparams.max_layers_transcode = maxLayers;
179+
cparams.max_res_transcode = maxRes;
180+
cparams.transcode_prog_order = progOrder;
181+
182+
grk_stream_params dstStreamParams{};
183+
safe_strcpy(dstStreamParams.file, outputFile);
184+
185+
grk_stream_params transSrcStreamParams{};
186+
safe_strcpy(transSrcStreamParams.file, inputFile);
187+
188+
/* Step 3: Transcode */
189+
uint64_t written = grk_transcode(&transSrcStreamParams, &dstStreamParams, &cparams, srcImage);
190+
191+
grk_object_unref(decCodec);
192+
193+
if(written == 0)
194+
{
195+
fprintf(stderr, "Error: transcode failed\n");
196+
return 1;
197+
}
198+
199+
fprintf(stdout, "Transcoded %s -> %s (%" PRIu64 " bytes written)\n", inputFile, outputFile,
200+
written);
201+
return 0;
202+
}

0 commit comments

Comments
 (0)