jp3d/jpwl/mj2/jpip: Fix resource leaks (#1226)
[openjpeg.git] / src / bin / mj2 / mj2_to_metadata.c
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 */
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 */
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 */
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
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
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, &parameters);
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