Fixed MJ2 codec bugs (issues 23-24 on google code). Thanks to Winfried for these...
[openjpeg.git] / mj2 / wrap_j2k_in_mj2.c
index e777220bf8f87bbd006a069f6ecc52412a28bcb1..7615cfde73c3ccf59387fef0ece52cce042e6e22 100644 (file)
@@ -1,33 +1,85 @@
+/*
+ * Copyright (c) 2002-2007, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2007, Professor Benoit Macq
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
 #include <stdio.h>
-#include <malloc.h>
-#include <setjmp.h>
+#include <stdlib.h>
 #include <string.h>
 
+#include "openjpeg.h"
+#include "j2k.h"
+#include "jp2.h"
+#include "cio.h"
 #include "mj2.h"
-#include <cio.h>
-#include <j2k.h>
-#include <int.h>
 
-#define MJ2_MJ2   0x6d6a7032
-#define MJ2_MJ2S  0x6d6a3273
-#define JP2_JP2C  0x6a703263
-#define MJ2_MDAT  0x6d646174
+static int int_ceildiv(int a, int b) {
+       return (a + b - 1) / b;
+}
+
+/**
+Size of memory first allocated for MOOV box
+*/
+#define TEMP_BUF 10000 
+
+
+/* -------------------------------------------------------------------------- */
+
+/**
+sample error callback expecting a FILE* client object
+*/
+void error_callback(const char *msg, void *client_data) {
+       FILE *stream = (FILE*)client_data;
+       fprintf(stream, "[ERROR] %s", msg);
+}
+/**
+sample warning callback expecting a FILE* client object
+*/
+void warning_callback(const char *msg, void *client_data) {
+       FILE *stream = (FILE*)client_data;
+       fprintf(stream, "[WARNING] %s", msg);
+}
+/**
+sample debug callback expecting a FILE* client object
+*/
+void info_callback(const char *msg, void *client_data) {
+       FILE *stream = (FILE*)client_data;
+       fprintf(stream, "[INFO] %s", msg);
+}
 
-//MEMORY LEAK
-#ifdef _DEBUG
-#define _CRTDBG_MAP_ALLOC
-#include <stdlib.h>  // Must be included first
-#include <crtdbg.h>
-#endif
-//MEM
+/* -------------------------------------------------------------------------- */
 
-jmp_buf j2k_error;
 
-void j2k_read_siz_marker(FILE *file, j2k_image_t *j2k_img)
+
+static void read_siz_marker(FILE *file, opj_image_t *image)
 {
   int len,i;
   char buf, buf2[2];
-  char *siz_buffer;
+  unsigned char *siz_buffer;
+       opj_cio_t *cio;
   
   fseek(file, 0, SEEK_SET);
   do {
@@ -40,55 +92,67 @@ void j2k_read_siz_marker(FILE *file, j2k_image_t *j2k_img)
   fread(buf2,2,1,file);                /* Lsiz                */
   len = ((buf2[0])<<8) + buf2[1];
   
-  siz_buffer = (char*) malloc(len * sizeof(char));
+  siz_buffer = (unsigned char*) malloc(len * sizeof(unsigned char));
   fread(siz_buffer,len, 1, file);
-  cio_init(siz_buffer,len);
+       cio = opj_cio_open(NULL, siz_buffer, len);
   
-  cio_read(2);                 /* Rsiz (capabilities) */
-  j2k_img->x1 = cio_read(4);   /* Xsiz                */
-  j2k_img->y1 = cio_read(4);   /* Ysiz                */
-  j2k_img->x0 = cio_read(4);   /* X0siz               */
-  j2k_img->y0 = cio_read(4);   /* Y0siz               */
-  cio_skip(16);                        /* XTsiz, YTsiz, XT0siz, YT0siz        */
+  cio_read(cio, 2);                    /* Rsiz (capabilities) */
+  image->x1 = cio_read(cio, 4);        /* Xsiz                */
+  image->y1 = cio_read(cio, 4);        /* Ysiz                */
+  image->x0 = cio_read(cio, 4);        /* X0siz               */
+  image->y0 = cio_read(cio, 4);        /* Y0siz               */
+  cio_skip(cio, 16);                   /* XTsiz, YTsiz, XT0siz, YT0siz        */
   
-  j2k_img->numcomps = cio_read(2);     /* Csiz                */
-  j2k_img->comps =
-    (j2k_comp_t *) malloc(j2k_img->numcomps * sizeof(j2k_comp_t));
-  for (i = 0; i < j2k_img->numcomps; i++) {
+  image->numcomps = cio_read(cio,2);   /* Csiz                */
+  image->comps =
+    (opj_image_comp_t *) malloc(image->numcomps * sizeof(opj_image_comp_t));
+       
+  for (i = 0; i < image->numcomps; i++) {
     int tmp;
-    tmp = cio_read(1);         /* Ssiz_i          */
-    j2k_img->comps[i].prec = (tmp & 0x7f) + 1;
-    j2k_img->comps[i].sgnd = tmp >> 7;
-    j2k_img->comps[i].dx = cio_read(1);        /* XRsiz_i         */
-    j2k_img->comps[i].dy = cio_read(1);        /* YRsiz_i         */
-    j2k_img->comps[i].resno_decoded = 0;       /* number of resolution decoded */
-    j2k_img->comps[i].factor = 0;      /* reducing factor by component */
+    tmp = cio_read(cio,1);             /* Ssiz_i          */
+    image->comps[i].prec = (tmp & 0x7f) + 1;
+    image->comps[i].sgnd = tmp >> 7;
+    image->comps[i].dx = cio_read(cio,1);      /* XRsiz_i         */
+    image->comps[i].dy = cio_read(cio,1);      /* YRsiz_i         */
+    image->comps[i].resno_decoded = 0; /* number of resolution decoded */
+    image->comps[i].factor = 0;        /* reducing factor by component */
   }
-  free(siz_buffer);
   fseek(file, 0, SEEK_SET);
+       opj_cio_close(cio);
+  free(siz_buffer);
 }
 
-void setparams(mj2_movie_t *movie, j2k_image_t *img) {
+static void setparams(opj_mj2_t *movie, opj_image_t *image) {
   int i, depth_0, depth, sign;
   
   movie->tk[0].sample_rate = 25;
-  movie->tk[0].w = int_ceildiv(img->x1 - img->x0, img->comps[0].dx);
-  movie->tk[0].h = int_ceildiv(img->y1 - img->y0, img->comps[0].dy);
+  movie->tk[0].w = int_ceildiv(image->x1 - image->x0, image->comps[0].dx);
+  movie->tk[0].h = int_ceildiv(image->y1 - image->y0, image->comps[0].dy);
   mj2_init_stdmovie(movie);
   
-  movie->tk[0].depth = img->comps[0].prec;
-
-  if (img->numcomps==3) {
-    if ((img->comps[0].dx == 1) && (img->comps[1].dx == 1) && (img->comps[1].dx == 1)) 
+  movie->tk[0].depth = image->comps[0].prec;
+       
+  if (image->numcomps==3) {
+    if ((image->comps[0].dx == 1) 
+       && (image->comps[1].dx == 1) 
+       && (image->comps[2].dx == 1)) 
       movie->tk[0].CbCr_subsampling_dx = 1;
-    else if ((img->comps[0].dx == 1) && (img->comps[1].dx == 2) && (img->comps[1].dx == 2))
+    else 
+       if ((image->comps[0].dx == 1) 
+       && (image->comps[1].dx == 2) 
+       && (image->comps[2].dx == 2))
       movie->tk[0].CbCr_subsampling_dx = 2;
     else
       fprintf(stderr,"Image component sizes are incoherent\n");
     
-    if ((img->comps[0].dy == 1) && (img->comps[1].dy == 1) && (img->comps[1].dy == 1)) 
+    if ((image->comps[0].dy == 1) 
+       && (image->comps[1].dy == 1) 
+       && (image->comps[2].dy == 1)) 
       movie->tk[0].CbCr_subsampling_dy = 1;
-    else if ((img->comps[0].dy == 1) && (img->comps[1].dy == 2) && (img->comps[1].dy == 2))
+    else 
+       if ((image->comps[0].dy == 1) 
+       && (image->comps[1].dy == 2) 
+       && (image->comps[2].dy == 2))
       movie->tk[0].CbCr_subsampling_dy = 2;
     else
       fprintf(stderr,"Image component sizes are incoherent\n");
@@ -96,61 +160,86 @@ void setparams(mj2_movie_t *movie, j2k_image_t *img) {
   
   movie->tk[0].sample_rate = 25;
   
-  movie->tk[0].jp2_struct.numcomps = img->numcomps;    // NC  
-  jp2_init_stdjp2(&movie->tk[0].jp2_struct);
+  movie->tk[0].jp2_struct.numcomps = image->numcomps;  // NC  
+       
+       /* Init Standard jp2 structure */
+       
+       movie->tk[0].jp2_struct.comps =
+    (opj_jp2_comps_t *) malloc(movie->tk[0].jp2_struct.numcomps * sizeof(opj_jp2_comps_t));
+  movie->tk[0].jp2_struct.precedence = 0;   /* PRECEDENCE*/
+  movie->tk[0].jp2_struct.approx = 0;   /* APPROX*/
+  movie->tk[0].jp2_struct.brand = JP2_JP2;     /* BR         */
+  movie->tk[0].jp2_struct.minversion = 0;      /* MinV       */
+  movie->tk[0].jp2_struct.numcl = 1;
+  movie->tk[0].jp2_struct.cl = (unsigned int *) malloc(movie->tk[0].jp2_struct.numcl * sizeof(int));
+  movie->tk[0].jp2_struct.cl[0] = JP2_JP2;     /* CL0 : JP2  */
+  movie->tk[0].jp2_struct.C = 7;      /* C : Always 7*/
+  movie->tk[0].jp2_struct.UnkC = 0;      /* UnkC, colorspace specified in colr box*/
+  movie->tk[0].jp2_struct.IPR = 0;      /* IPR, no intellectual property*/
+  movie->tk[0].jp2_struct.w = int_ceildiv(image->x1 - image->x0, image->comps[0].dx);
+  movie->tk[0].jp2_struct.h = int_ceildiv(image->y1 - image->y0, image->comps[0].dy);
   
-  movie->tk[0].jp2_struct.w = int_ceildiv(img->x1 - img->x0, img->comps[0].dx);
-  movie->tk[0].jp2_struct.h = int_ceildiv(img->y1 - img->y0, img->comps[0].dy);
-  
-  depth_0 = img->comps[0].prec - 1;
-  sign = img->comps[0].sgnd;
+  depth_0 = image->comps[0].prec - 1;
+  sign = image->comps[0].sgnd;
   movie->tk[0].jp2_struct.bpc = depth_0 + (sign << 7);
   
-  for (i = 1; i < img->numcomps; i++) {
-    depth = img->comps[i].prec - 1;
-    sign = img->comps[i].sgnd;
+  for (i = 1; i < image->numcomps; i++) {
+    depth = image->comps[i].prec - 1;
+    sign = image->comps[i].sgnd;
     if (depth_0 != depth)
       movie->tk[0].jp2_struct.bpc = 255;
   }
   
-  for (i = 0; i < img->numcomps; i++)
+  for (i = 0; i < image->numcomps; i++)
     movie->tk[0].jp2_struct.comps[i].bpcc =
-    img->comps[i].prec - 1 + (img->comps[i].sgnd << 7);
+    image->comps[i].prec - 1 + (image->comps[i].sgnd << 7);
   
-  if ((img->numcomps == 1 || img->numcomps == 3)
+  if ((image->numcomps == 1 || image->numcomps == 3)
     && (movie->tk[0].jp2_struct.bpc != 255))
     movie->tk[0].jp2_struct.meth = 1;
   else
     movie->tk[0].jp2_struct.meth = 2;
-    
-  if (img->numcomps == 1)
-    movie->tk[0].jp2_struct.enumcs = 17;  // Grayscale
+       
+    if (image->numcomps == 1)
+     movie->tk[0].jp2_struct.enumcs = 17;  // Grayscale
   
-  else   if ((img->comps[0].dx == 1) && (img->comps[1].dx == 1) && (img->comps[1].dx == 1) &&
-    (img->comps[0].dy == 1) && (img->comps[1].dy == 1) && (img->comps[1].dy == 1)) 
-    movie->tk[0].jp2_struct.enumcs = 16;    // RGB
+    else   
+       if ((image->comps[0].dx == 1) 
+       && (image->comps[1].dx == 1) 
+       && (image->comps[2].dx == 1) 
+    && (image->comps[0].dy == 1) 
+       && (image->comps[1].dy == 1) 
+       && (image->comps[2].dy == 1)) 
+     movie->tk[0].jp2_struct.enumcs = 16;    // RGB
   
-  else   if ((img->comps[0].dx == 1) && (img->comps[1].dx == 2) && (img->comps[1].dx == 2) &&
-    (img->comps[0].dy == 1) && (img->comps[1].dy == 2) && (img->comps[1].dy == 2)) 
-    movie->tk[0].jp2_struct.enumcs = 18;  // YUV
+    else   
+       if ((image->comps[0].dx == 1) 
+       && (image->comps[1].dx == 2) 
+       && (image->comps[2].dx == 2) 
+    && (image->comps[0].dy == 1) 
+       && (image->comps[1].dy == 2) 
+       && (image->comps[2].dy == 2)) 
+     movie->tk[0].jp2_struct.enumcs = 18;  // YUV
   
   else
     movie->tk[0].jp2_struct.enumcs = 0;        // Unkown profile */
 }
 
 int main(int argc, char *argv[]) {
-  
+       opj_cinfo_t* cinfo; 
+       opj_event_mgr_t event_mgr;              /* event manager */  
   unsigned int snum;
-  mj2_movie_t movie;
+  opj_mj2_t *movie;
   mj2_sample_t *sample;
   unsigned char* frame_codestream;
   FILE *mj2file, *j2kfile;
   char j2kfilename[50];
-  char *buf;
+  unsigned char *buf;
   int offset, mdat_initpos;
-  j2k_image_t img;
-  int i;
-  
+  opj_image_t img;
+       opj_cio_t *cio;
+       mj2_cparameters_t parameters;
+       
   if (argc != 3) {
     printf("Bad syntax: Usage: MJ2_Wrapper source_location mj2_filename\n");
     printf("Example: MJ2_Wrapper input/input output.mj2\n");
@@ -163,57 +252,63 @@ int main(int argc, char *argv[]) {
     fprintf(stderr, "failed to open %s for writing\n", argv[2]);
     return 1;
   }
+
+       /*
+       configure the event callbacks (not required)
+       setting of each callback is optionnal
+       */
+       memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+       event_mgr.error_handler = error_callback;
+       event_mgr.warning_handler = warning_callback;
+       event_mgr.info_handler = info_callback;
+
+       /* get a MJ2 decompressor handle */
+       cinfo = mj2_create_compress();
+
+       /* catch events using our callbacks and give a local context */
+       opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, stderr);   
+       
+       /* setup the decoder encoding parameters using user parameters */
+       movie = (opj_mj2_t*) cinfo->mj2_handle;
+       mj2_setup_encoder((opj_mj2_t*)cinfo->mj2_handle, &parameters);
+
   
-  // Initialing the movie (parameters used in the JP and FTYP boxes  
-  movie.num_htk = 0;     // No hint tracks
-  movie.num_stk = 0;     // No sound tracks
-  movie.num_vtk = 1;     // One video track  
-  movie.tk = (mj2_tk_t*) malloc (sizeof(mj2_tk_t)); //Memory allocation for the video track
-  movie.tk[0].sample = (mj2_sample_t*) malloc (sizeof(mj2_sample_t));
-  movie.tk[0].chunk = (mj2_chunk_t *) malloc(sizeof(mj2_chunk_t));  
-  movie.tk[0].track_type = 0;    // Video track
-  movie.tk[0].track_ID = 1;      // Track ID = 1 
-  movie.brand = MJ2_MJ2;  // One brand: MJ2
-  movie.num_cl = 2;      // Two compatible brands: MJ2 and MJ2S
-  movie.cl = (unsigned int *) malloc(movie.num_cl * sizeof(unsigned int));
-  movie.cl[0] = MJ2_MJ2;
-  movie.cl[1] = MJ2_MJ2S;
-  movie.minversion = 0;          // Minimum version: 0
-  
-  // Writing JP, FTYP and MDAT boxes 
-  buf = (char*) malloc (300 * sizeof(char)); // Assuming that the JP and FTYP
-                                            // boxes won't be longer than 300 bytes
-  cio_init(buf , 300);
-  mj2_write_jp();
-  mj2_write_ftyp(&movie);
-  mdat_initpos = cio_tell();
-  cio_skip(4);
-  cio_write(MJ2_MDAT, 4);      
-  fwrite(buf,cio_tell(),1,mj2file);
+       /* Writing JP, FTYP and MDAT boxes 
+       Assuming that the JP and FTYP boxes won't be longer than 300 bytes */
+       
+  buf = (unsigned char*) malloc (300 * sizeof(unsigned char)); 
+  cio = opj_cio_open(movie->cinfo, buf, 300);
+  mj2_write_jp(cio);
+  mj2_write_ftyp(movie, cio);
+  mdat_initpos = cio_tell(cio);
+  cio_skip(cio, 4);
+  cio_write(cio,MJ2_MDAT, 4);  
+  fwrite(buf,cio_tell(cio),1,mj2file);
   free(buf);
-    
+       
   // Insert each j2k codestream in a JP2C box  
   snum=0;
   offset = 0;  
   while(1)
   {
-    sample = &movie.tk[0].sample[snum];
-    sprintf(j2kfilename,"%s_%d.j2k",argv[1],snum);
+    sample = &movie->tk[0].sample[snum];
+    sprintf(j2kfilename,"%s_%05d.j2k",argv[1],snum);
     j2kfile = fopen(j2kfilename, "rb");
     if (!j2kfile) {
       if (snum==0) {  // Could not open a single codestream
-       fprintf(stderr, "failed to open %s for reading\n",j2kfilename);
-       return 1;
+                               fprintf(stderr, "failed to open %s for reading\n",j2kfilename);
+                               return 1;
       }
       else {         // Tried to open a inexistant codestream
-       fprintf(stdout,"%d frames created\n",snum);
-       break;
+                               fprintf(stdout,"%d frames are being added to the MJ2 file\n",snum);
+                               break;
       }
     }
+
     // Calculating offset for samples and chunks
-    offset += cio_tell();     
+    offset += cio_tell(cio);     
     sample->offset = offset;
-    movie.tk[0].chunk[snum].offset = offset;  // There will be one sample per chunk
+    movie->tk[0].chunk[snum].offset = offset;  // There will be one sample per chunk
     
     // Calculating sample size
     fseek(j2kfile,0,SEEK_END); 
@@ -222,66 +317,55 @@ int main(int argc, char *argv[]) {
     
     // Reading siz marker of j2k image for the first codestream
     if (snum==0)             
-      j2k_read_siz_marker(j2kfile, &img);
+      read_siz_marker(j2kfile, &img);
     
     // Writing JP2C box header                     
     frame_codestream = (unsigned char*) malloc (sample->sample_size+8); 
-    cio_init(frame_codestream, sample->sample_size);
-    cio_write(sample->sample_size, 4);  // Sample size
-    cio_write(JP2_JP2C, 4);    // JP2C
+               cio = opj_cio_open(movie->cinfo, frame_codestream, sample->sample_size);    
+    cio_write(cio,sample->sample_size, 4);  // Sample size
+    cio_write(cio,JP2_JP2C, 4);        // JP2C
     
     // Writing codestream from J2K file to MJ2 file
     fread(frame_codestream+8,sample->sample_size-8,1,j2kfile);
     fwrite(frame_codestream,sample->sample_size,1,mj2file);
-    cio_skip(sample->sample_size-8);
+    cio_skip(cio, sample->sample_size-8);
     
     // Ending loop
     fclose(j2kfile);
     snum++;
-    movie.tk[0].sample = realloc(movie.tk[0].sample, (snum+1) * sizeof(mj2_sample_t));
-    movie.tk[0].chunk = realloc(movie.tk[0].chunk, (snum+1) * sizeof(mj2_chunk_t));
+    movie->tk[0].sample = (mj2_sample_t*)
+               realloc(movie->tk[0].sample, (snum+1) * sizeof(mj2_sample_t));
+    movie->tk[0].chunk = (mj2_chunk_t*)
+               realloc(movie->tk[0].chunk, (snum+1) * sizeof(mj2_chunk_t));
     free(frame_codestream);
   }
   
   // Writing the MDAT box length in header
-  offset += cio_tell();
-  buf = (char*) malloc (4 * sizeof(char));
-  cio_init(buf,4);
-  cio_write(offset-mdat_initpos,4); // Write MDAT length in MDAT box header
+  offset += cio_tell(cio);
+  buf = (unsigned char*) malloc (4 * sizeof(unsigned char));
+       cio = opj_cio_open(movie->cinfo, buf, 4);
+  cio_write(cio,offset-mdat_initpos,4); 
   fseek(mj2file,(long)mdat_initpos,SEEK_SET);
   fwrite(buf,4,1,mj2file);
   fseek(mj2file,0,SEEK_END);
   free(buf);
-
+       
   // Setting movie parameters
-  movie.tk[0].num_samples=snum;
-  movie.tk[0].num_chunks=snum;
-  setparams(&movie, &img);
-
+  movie->tk[0].num_samples=snum;
+  movie->tk[0].num_chunks=snum;
+  setparams(movie, &img);
+       
   // Writing MOOV box 
-  i=1;
-  buf = (char*) malloc (10000 * sizeof(char));
-  cio_init(buf , i*10000);
-  if (setjmp(j2k_error)) {
-    i++;
-    buf = realloc(buf,i*10000* sizeof(char));
-    cio_init(buf , i*10000);
-  }
-  mj2_write_moov(&movie);
-  fwrite(buf,cio_tell(),1,mj2file);
-
+       buf = (unsigned char*) malloc ((TEMP_BUF+snum*20) * sizeof(unsigned char));
+       cio = opj_cio_open(movie->cinfo, buf, (TEMP_BUF+snum*20));
+       mj2_write_moov(movie, cio);
+  fwrite(buf,cio_tell(cio),1,mj2file);
+       
   // Ending program
   fclose(mj2file);
   free(img.comps);
-  free(buf);
-  mj2_memory_free(&movie);
-
-
-  //MEMORY LEAK
-  #ifdef _DEBUG
-    _CrtDumpMemoryLeaks();
-  #endif
-  //MEM
-
+  opj_cio_close(cio);
+  mj2_destroy_compress(movie);
+       
   return 0;
-}
\ No newline at end of file
+}