1 /* mj2_to_metadata.c */
2 /* Dump MJ2, JP2 metadata (partial so far) to xml file */
3 /* Contributed to Open JPEG by Glenn Pearson, contract software developer, U.S. National Library of Medicine.
4
5 The base code in this file was developed by the author as part of a video archiving
6 project for the U.S. National Library of Medicine, Bethesda, MD.
7 It is the policy of NLM (and U.S. government) to not assert copyright.
8
9 A non-exclusive copy of this code has been contributed to the Open JPEG project.
10 Except for copyright, inclusion of the code within Open JPEG for distribution and use
11 can be bound by the Open JPEG open-source license and disclaimer, expressed elsewhere.
12 */
13
14 #include "opj_includes.h"
15 #include "mj2.h"
16
17 #include "mj2_to_metadata.h"
18 #include <string.h>
19 #include "opj_getopt.h"
20
21 /* -------------------------------------------------------------------------- */
22
23 /**
24 sample error callback expecting a FILE* client object
25 */
error_callback(const char * msg,void * client_data)26 void error_callback(const char *msg, void *client_data)
27 {
28 FILE *stream = (FILE*)client_data;
29 fprintf(stream, "[ERROR] %s", msg);
30 }
31 /**
32 sample warning callback expecting a FILE* client object
33 */
warning_callback(const char * msg,void * client_data)34 void warning_callback(const char *msg, void *client_data)
35 {
36 FILE *stream = (FILE*)client_data;
37 fprintf(stream, "[WARNING] %s", msg);
38 }
39 /**
40 sample debug callback expecting a FILE* client object
41 */
info_callback(const char * msg,void * client_data)42 void info_callback(const char *msg, void *client_data)
43 {
44 FILE *stream = (FILE*)client_data;
45 fprintf(stream, "[INFO] %s", msg);
46 }
47
48 /* -------------------------------------------------------------------------- */
49
50
51
52 /* ------------- */
53
help_display()54 void help_display()
55 {
56 /* "1234567890123456789012345678901234567890123456789012345678901234567890123456789" */
57 fprintf(stdout, " Help for the 'mj2_to_metadata' Program\n");
58 fprintf(stdout, " ======================================\n");
59 fprintf(stdout, "The -h option displays this information on screen.\n\n");
60
61 fprintf(stdout,
62 "mj2_to_metadata generates an XML file from a Motion JPEG 2000 file.\n");
63 fprintf(stdout,
64 "The generated XML shows the structural, but not (yet) curatorial,\n");
65 fprintf(stdout,
66 "metadata from the movie header and from the JPEG 2000 image and tile\n");
67 fprintf(stdout,
68 "headers of a sample frame. Excluded: low-level packed-bits image data.\n\n");
69
70 fprintf(stdout, "By Default\n");
71 fprintf(stdout, "----------\n");
72 fprintf(stdout,
73 "The metadata includes the jp2 image and tile headers of the first frame.\n");
74 fprintf(stdout, "\n");
75 fprintf(stdout,
76 "Metadata values are shown in 'raw' form (e.g., hexadecimal) as stored in the\n");
77 fprintf(stdout,
78 "file, and, if apt, in a 'derived' form that is more quickly grasped.\n");
79 fprintf(stdout, "\n");
80 fprintf(stdout,
81 "Notes explaining the XML are embedded as terse comments. These include\n");
82 fprintf(stdout, " meaning of non-obvious tag abbreviations;\n");
83 fprintf(stdout, " range and precision of valid values;\n");
84 fprintf(stdout, " interpretations of values, such as enumerations; and\n");
85 fprintf(stdout, " current implementation limitations.\n");
86 fprintf(stdout, "\n");
87 fprintf(stdout,
88 "The sample-size and chunk-offset tables, each with 1 row per frame, are not reported.\n");
89 fprintf(stdout, "\n");
90 fprintf(stdout,
91 "The file is self-contained and no verification (e.g., against a DTD) is requested.\n");
92 fprintf(stdout, "\n");
93 fprintf(stdout, "Required Parameters (except with -h)\n");
94 fprintf(stdout, "------------------------------------\n");
95 fprintf(stdout,
96 "[Caution: file strings that contain spaces should be wrapped with quotes.]\n");
97 fprintf(stdout,
98 "-i input.mj2 : where 'input' is any source file name or path.\n");
99 fprintf(stdout,
100 " MJ2 files created with 'frames_to_mj2' are supported so far.\n");
101 fprintf(stdout,
102 " These are silent, single-track, 'MJ2 Simple Profile' videos.\n");
103 fprintf(stdout,
104 "-o output.xml : where 'output' is any destination file name or path.\n");
105 fprintf(stdout, "\n");
106 fprintf(stdout, "Optional Parameters\n");
107 fprintf(stdout, "-------------------\n");
108 fprintf(stdout, "-h : Display this help information.\n");
109 fprintf(stdout, "-n : Suppress all mj2_to_metadata notes.\n");
110 fprintf(stdout,
111 "-t : Include sample-size and chunk-offset tables.\n");
112 fprintf(stdout,
113 "-f n : where n > 0. Include jp2 header info for frame n [default=1].\n");
114 fprintf(stdout, "-f 0 : No jp2 header info.\n");
115 fprintf(stdout,
116 "-r : Suppress all 'raw' data for which a 'derived' form exists.\n");
117 fprintf(stdout, "-d : Suppress all 'derived' data.\n");
118 fprintf(stdout,
119 " (If both -r and -d given, -r will be ignored.)\n");
120 fprintf(stdout,
121 "-v string : Verify against the DTD file located by the string.\n");
122 fprintf(stdout,
123 " Prepend quoted 'string' with either SYSTEM or PUBLIC keyword.\n");
124 fprintf(stdout,
125 " Thus, for the distributed DTD placed in the same directory as\n");
126 fprintf(stdout,
127 " the output file: -v \"SYSTEM mj2_to_metadata.dtd\"\n");
128 fprintf(stdout,
129 " \"PUBLIC\" is used with an access protocol (e.g., http:) + URL.\n");
130 /* More to come */
131 fprintf(stdout, "\n");
132 /* "1234567890123456789012345678901234567890123456789012345678901234567890123456789" */
133 }
134
135 /* ------------- */
136
main(int argc,char * argv[])137 int main(int argc, char *argv[])
138 {
139
140 opj_dinfo_t* dinfo;
141 opj_event_mgr_t event_mgr; /* event manager */
142
143 FILE *file, *xmlout;
144 /* char xmloutname[50]; */
145 opj_mj2_t *movie;
146
147 char* infile = 0;
148 char* outfile = 0;
149 char* s, S1, S2, S3;
150 int len;
151 unsigned int sampleframe = 1; /* First frame */
152 char* stringDTD = NULL;
153 BOOL notes = TRUE;
154 BOOL sampletables = FALSE;
155 BOOL raw = TRUE;
156 BOOL derived = TRUE;
157 mj2_dparameters_t parameters;
158
159 while (TRUE) {
160 /* ':' after letter means it takes an argument */
161 int c = getopt(argc, argv, "i:o:f:v:hntrd");
162 /* FUTURE: Reserve 'p' for pruning file (which will probably make -t redundant) */
163 if (c == -1) {
164 break;
165 }
166 switch (c) {
167 case 'i': /* IN file */
168 infile = optarg;
169 s = optarg;
170 while (*s) {
171 s++; /* Run to filename end */
172 }
173 s--;
174 S3 = *s;
175 s--;
176 S2 = *s;
177 s--;
178 S1 = *s;
179
180 if ((S1 == 'm' && S2 == 'j' && S3 == '2')
181 || (S1 == 'M' && S2 == 'J' && S3 == '2')) {
182 break;
183 }
184 fprintf(stderr, "Input file name must have .mj2 extension, not .%c%c%c.\n", S1,
185 S2, S3);
186 return 1;
187
188 /* ----------------------------------------------------- */
189 case 'o': /* OUT file */
190 outfile = optarg;
191 while (*outfile) {
192 outfile++; /* Run to filename end */
193 }
194 outfile--;
195 S3 = *outfile;
196 outfile--;
197 S2 = *outfile;
198 outfile--;
199 S1 = *outfile;
200
201 outfile = optarg;
202
203 if ((S1 == 'x' && S2 == 'm' && S3 == 'l')
204 || (S1 == 'X' && S2 == 'M' && S3 == 'L')) {
205 break;
206 }
207
208 fprintf(stderr,
209 "Output file name must have .xml extension, not .%c%c%c\n", S1, S2, S3);
210 return 1;
211
212 /* ----------------------------------------------------- */
213 case 'f': /* Choose sample frame. 0 = none */
214 sscanf(optarg, "%u", &sampleframe);
215 break;
216
217 /* ----------------------------------------------------- */
218 case 'v': /* Verification by DTD. */
219 stringDTD = optarg;
220 /* We will not insist upon last 3 chars being "dtd", since non-file
221 access protocol may be used. */
222 if (strchr(stringDTD, '"') != NULL) {
223 fprintf(stderr,
224 "-D's string must not contain any embedded double-quote characters.\n");
225 return 1;
226 }
227
228 if (strncmp(stringDTD, "PUBLIC ", 7) == 0 ||
229 strncmp(stringDTD, "SYSTEM ", 7) == 0) {
230 break;
231 }
232
233 fprintf(stderr, "-D's string must start with \"PUBLIC \" or \"SYSTEM \"\n");
234 return 1;
235
236 /* ----------------------------------------------------- */
237 case 'n': /* Suppress comments */
238 notes = FALSE;
239 break;
240
241 /* ----------------------------------------------------- */
242 case 't': /* Show sample size and chunk offset tables */
243 sampletables = TRUE;
244 break;
245
246 /* ----------------------------------------------------- */
247 case 'h': /* Display an help description */
248 help_display();
249 return 0;
250
251 /* ----------------------------------------------------- */
252 case 'r': /* Suppress raw data */
253 raw = FALSE;
254 break;
255
256 /* ----------------------------------------------------- */
257 case 'd': /* Suppress derived data */
258 derived = FALSE;
259 break;
260
261 /* ----------------------------------------------------- */
262 default:
263 return 1;
264 } /* switch */
265 } /* while */
266
267 if (!raw && !derived) {
268 raw = TRUE; /* At least one of 'raw' and 'derived' must be true */
269 }
270
271 /* Error messages */
272 /* -------------- */
273 if (!infile || !outfile) {
274 fprintf(stderr,
275 "Correct usage: mj2_to_metadata -i mj2-file -o xml-file (plus options)\n");
276 return 1;
277 }
278
279 /* was:
280 if (argc != 3) {
281 printf("Bad syntax: Usage: MJ2_to_metadata inputfile.mj2 outputfile.xml\n");
282 printf("Example: MJ2_to_metadata foreman.mj2 foreman.xml\n");
283 return 1;
284 }
285 */
286 len = strlen(infile);
287 if (infile[0] == ' ') {
288 infile++; /* There may be a leading blank if user put space after -i */
289 }
290
291 file = fopen(infile, "rb"); /* was: argv[1] */
292
293 if (!file) {
294 fprintf(stderr, "Failed to open %s for reading.\n", infile); /* was: argv[1] */
295 return 1;
296 }
297
298 len = strlen(outfile);
299 if (outfile[0] == ' ') {
300 outfile++; /* There may be a leading blank if user put space after -o */
301 }
302
303 // Checking output file
304 xmlout = fopen(outfile, "w"); /* was: argv[2] */
305 if (!xmlout) {
306 fprintf(stderr, "Failed to open %s for writing.\n", outfile); /* was: argv[2] */
307 fclose(file);
308 return 1;
309 }
310 // Leave it open
311
312 /*
313 configure the event callbacks (not required)
314 setting of each callback is optional
315 */
316 memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
317 event_mgr.error_handler = error_callback;
318 event_mgr.warning_handler = warning_callback;
319 event_mgr.info_handler = info_callback;
320
321 /* get a MJ2 decompressor handle */
322 dinfo = mj2_create_decompress();
323
324 /* catch events using our callbacks and give a local context */
325 opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);
326
327 /* setup the decoder decoding parameters using user parameters */
328 movie = (opj_mj2_t*) dinfo->mj2_handle;
329 mj2_setup_decoder(dinfo->mj2_handle, ¶meters);
330
331 if (mj2_read_struct(file, movie)) { // Creating the movie structure
332 fclose(xmlout);
333 return 1;
334 }
335
336 xml_write_init(notes, sampletables, raw, derived);
337 xml_write_struct(file, xmlout, movie, sampleframe, stringDTD, &event_mgr);
338 fclose(xmlout);
339
340 fprintf(stderr, "Metadata correctly extracted to XML file \n");;
341
342 /* free remaining structures */
343 if (dinfo) {
344 mj2_destroy_decompress((opj_mj2_t*)dinfo->mj2_handle);
345 }
346
347 return 0;
348 }
349
350
351