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