gst_plugins_good/ext/jpeg/smokecodec.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     1 /* Smoke codec
       
     2  * Copyright (C) <2004> Wim Taymans <wim@fluendo.com> 
       
     3  *
       
     4  * This library is free software; you can redistribute it and/or
       
     5  * modify it under the terms of the GNU Library General Public
       
     6  * License as published by the Free Software Foundation; either
       
     7  * version 2 of the License, or (at your option) any later version.
       
     8  *
       
     9  * This library is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    12  * Library General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU Library General Public
       
    15  * License along with this library; if not, write to the
       
    16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    17  * Boston, MA 02111-1307, USA.
       
    18  */
       
    19 
       
    20 
       
    21 #ifdef HAVE_CONFIG_H
       
    22 #include "config.h"
       
    23 #endif
       
    24 
       
    25 #include <stdio.h>
       
    26 #include <stdlib.h>
       
    27 #include <string.h>
       
    28 #include <math.h>
       
    29 
       
    30 /* this is a hack hack hack to get around jpeglib header bugs... */
       
    31 #ifdef HAVE_STDLIB_H
       
    32 # undef HAVE_STDLIB_H
       
    33 #endif
       
    34 #include <jpeglib.h>
       
    35 
       
    36 #include "smokecodec.h"
       
    37 #include "smokeformat.h"
       
    38 
       
    39 #ifdef SYMBIAN
       
    40 #define DEBUG(a...)   printf( a );
       
    41 #else // SYMBIAN
       
    42 #ifndef WIN32
       
    43 //#define DEBUG(a...)   printf( a );
       
    44 #define DEBUG(a,...)
       
    45 #else
       
    46 #include <gst/gstinfo.h>
       
    47 #define DEBUG GST_DEBUG
       
    48 #endif
       
    49 #endif // SYMBIAN
       
    50 
       
    51 
       
    52 struct _SmokeCodecInfo
       
    53 {
       
    54   unsigned int width;
       
    55   unsigned int height;
       
    56   unsigned int fps_num;
       
    57   unsigned int fps_denom;
       
    58 
       
    59   unsigned int minquality;
       
    60   unsigned int maxquality;
       
    61   unsigned int bitrate;
       
    62   unsigned int threshold;
       
    63 
       
    64   unsigned int refdec;
       
    65 
       
    66   unsigned char **line[3];
       
    67   unsigned char *compbuf[3];
       
    68 
       
    69   struct jpeg_error_mgr jerr;
       
    70 
       
    71   struct jpeg_compress_struct cinfo;
       
    72   struct jpeg_destination_mgr jdest;
       
    73 
       
    74   struct jpeg_decompress_struct dinfo;
       
    75   struct jpeg_source_mgr jsrc;
       
    76 
       
    77   int need_keyframe;
       
    78   unsigned char *reference;
       
    79 };
       
    80 
       
    81 static void
       
    82 smokecodec_init_destination (j_compress_ptr cinfo)
       
    83 {
       
    84 }
       
    85 
       
    86 static int
       
    87 smokecodec_flush_destination (j_compress_ptr cinfo)
       
    88 {
       
    89   return 1;
       
    90 }
       
    91 
       
    92 static void
       
    93 smokecodec_term_destination (j_compress_ptr cinfo)
       
    94 {
       
    95 }
       
    96 
       
    97 static void
       
    98 smokecodec_init_source (j_decompress_ptr cinfo)
       
    99 {
       
   100 }
       
   101 
       
   102 static int
       
   103 smokecodec_fill_input_buffer (j_decompress_ptr cinfo)
       
   104 {
       
   105   return 1;
       
   106 }
       
   107 
       
   108 static void
       
   109 smokecodec_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
       
   110 {
       
   111 }
       
   112 
       
   113 static int
       
   114 smokecodec_resync_to_restart (j_decompress_ptr cinfo, int desired)
       
   115 {
       
   116   return 1;
       
   117 }
       
   118 
       
   119 static void
       
   120 smokecodec_term_source (j_decompress_ptr cinfo)
       
   121 {
       
   122 }
       
   123 
       
   124 
       
   125 int
       
   126 smokecodec_encode_new (SmokeCodecInfo ** info,
       
   127     const unsigned int width,
       
   128     const unsigned int height,
       
   129     const unsigned int fps_num, const unsigned int fps_denom)
       
   130 {
       
   131   SmokeCodecInfo *newinfo;
       
   132   int i, j;
       
   133   unsigned char *base[3];
       
   134 
       
   135   if (!info)
       
   136     return SMOKECODEC_NULLPTR;
       
   137   if ((width & 0xf) || (height & 0xf))
       
   138     return SMOKECODEC_WRONGSIZE;
       
   139 
       
   140   newinfo = malloc (sizeof (SmokeCodecInfo));
       
   141   if (!newinfo) {
       
   142     return SMOKECODEC_NOMEM;
       
   143   }
       
   144   newinfo->width = width;
       
   145   newinfo->height = height;
       
   146   newinfo->fps_num = fps_num;
       
   147   newinfo->fps_denom = fps_denom;
       
   148 
       
   149   /* setup jpeglib */
       
   150   memset (&newinfo->cinfo, 0, sizeof (newinfo->cinfo));
       
   151   memset (&newinfo->jerr, 0, sizeof (newinfo->jerr));
       
   152   newinfo->cinfo.err = jpeg_std_error (&newinfo->jerr);
       
   153   jpeg_create_compress (&newinfo->cinfo);
       
   154   newinfo->cinfo.input_components = 3;
       
   155   jpeg_set_defaults (&newinfo->cinfo);
       
   156 
       
   157   newinfo->cinfo.dct_method = JDCT_FASTEST;
       
   158 
       
   159   newinfo->cinfo.raw_data_in = TRUE;
       
   160   newinfo->cinfo.in_color_space = JCS_YCbCr;
       
   161   newinfo->cinfo.comp_info[0].h_samp_factor = 2;
       
   162   newinfo->cinfo.comp_info[0].v_samp_factor = 2;
       
   163   newinfo->cinfo.comp_info[1].h_samp_factor = 1;
       
   164   newinfo->cinfo.comp_info[1].v_samp_factor = 1;
       
   165   newinfo->cinfo.comp_info[2].h_samp_factor = 1;
       
   166   newinfo->cinfo.comp_info[2].v_samp_factor = 1;
       
   167 
       
   168   newinfo->line[0] = malloc (DCTSIZE * 2 * sizeof (char *));
       
   169   newinfo->line[1] = malloc (DCTSIZE * sizeof (char *));
       
   170   newinfo->line[2] = malloc (DCTSIZE * sizeof (char *));
       
   171   base[0] = newinfo->compbuf[0] = malloc (256 * 2 * DCTSIZE * 2 * DCTSIZE);
       
   172   base[1] = newinfo->compbuf[1] = malloc (256 * DCTSIZE * DCTSIZE);
       
   173   base[2] = newinfo->compbuf[2] = malloc (256 * DCTSIZE * DCTSIZE);
       
   174 
       
   175   for (i = 0, j = 0; i < 2 * DCTSIZE; i += 2, j++) {
       
   176     newinfo->line[0][i] = base[0];
       
   177     base[0] += 2 * DCTSIZE * 256;
       
   178     newinfo->line[0][i + 1] = base[0];
       
   179     base[0] += 2 * DCTSIZE * 256;
       
   180     newinfo->line[1][j] = base[1];
       
   181     base[1] += DCTSIZE * 256;
       
   182     newinfo->line[2][j] = base[2];
       
   183     base[2] += DCTSIZE * 256;
       
   184   }
       
   185 
       
   186   newinfo->jdest.init_destination = smokecodec_init_destination;
       
   187   newinfo->jdest.empty_output_buffer = smokecodec_flush_destination;
       
   188   newinfo->jdest.term_destination = smokecodec_term_destination;
       
   189   newinfo->cinfo.dest = &newinfo->jdest;
       
   190 
       
   191   jpeg_suppress_tables (&newinfo->cinfo, FALSE);
       
   192 
       
   193   memset (&newinfo->dinfo, 0, sizeof (newinfo->dinfo));
       
   194   newinfo->dinfo.err = jpeg_std_error (&newinfo->jerr);
       
   195   jpeg_create_decompress (&newinfo->dinfo);
       
   196 
       
   197   newinfo->jsrc.init_source = smokecodec_init_source;
       
   198   newinfo->jsrc.fill_input_buffer = smokecodec_fill_input_buffer;
       
   199   newinfo->jsrc.skip_input_data = smokecodec_skip_input_data;
       
   200   newinfo->jsrc.resync_to_restart = smokecodec_resync_to_restart;
       
   201   newinfo->jsrc.term_source = smokecodec_term_source;
       
   202   newinfo->dinfo.src = &newinfo->jsrc;
       
   203 
       
   204   newinfo->need_keyframe = 1;
       
   205   newinfo->threshold = 4000;
       
   206   newinfo->minquality = 10;
       
   207   newinfo->maxquality = 85;
       
   208   newinfo->reference = malloc (3 * (width * height) / 2);
       
   209   newinfo->refdec = 0;
       
   210 
       
   211   *info = newinfo;
       
   212 
       
   213   return SMOKECODEC_OK;
       
   214 }
       
   215 
       
   216 int
       
   217 smokecodec_decode_new (SmokeCodecInfo ** info)
       
   218 {
       
   219   return smokecodec_encode_new (info, 16, 16, 1, 1);
       
   220 }
       
   221 
       
   222 int
       
   223 smokecodec_info_free (SmokeCodecInfo * info)
       
   224 {
       
   225   free (info->line[0]);
       
   226   free (info->line[1]);
       
   227   free (info->line[2]);
       
   228   free (info->compbuf[0]);
       
   229   free (info->compbuf[1]);
       
   230   free (info->compbuf[2]);
       
   231   free (info->reference);
       
   232   jpeg_destroy_compress (&info->cinfo);
       
   233   jpeg_destroy_decompress (&info->dinfo);
       
   234   free (info);
       
   235 
       
   236   return SMOKECODEC_OK;
       
   237 }
       
   238 
       
   239 SmokeCodecResult
       
   240 smokecodec_set_quality (SmokeCodecInfo * info,
       
   241     const unsigned int min, const unsigned int max)
       
   242 {
       
   243   info->minquality = min;
       
   244   info->maxquality = max;
       
   245 
       
   246   return SMOKECODEC_OK;
       
   247 }
       
   248 
       
   249 SmokeCodecResult
       
   250 smokecodec_get_quality (SmokeCodecInfo * info,
       
   251     unsigned int *min, unsigned int *max)
       
   252 {
       
   253   *min = info->minquality;
       
   254   *max = info->maxquality;
       
   255 
       
   256   return SMOKECODEC_OK;
       
   257 }
       
   258 
       
   259 SmokeCodecResult
       
   260 smokecodec_set_threshold (SmokeCodecInfo * info, const unsigned int threshold)
       
   261 {
       
   262   info->threshold = threshold;
       
   263 
       
   264   return SMOKECODEC_OK;
       
   265 }
       
   266 
       
   267 SmokeCodecResult
       
   268 smokecodec_get_threshold (SmokeCodecInfo * info, unsigned int *threshold)
       
   269 {
       
   270   *threshold = info->threshold;
       
   271 
       
   272   return SMOKECODEC_OK;
       
   273 }
       
   274 
       
   275 SmokeCodecResult
       
   276 smokecodec_set_bitrate (SmokeCodecInfo * info, const unsigned int bitrate)
       
   277 {
       
   278   info->bitrate = bitrate;
       
   279 
       
   280   return SMOKECODEC_OK;
       
   281 }
       
   282 
       
   283 SmokeCodecResult
       
   284 smokecodec_get_bitrate (SmokeCodecInfo * info, unsigned int *bitrate)
       
   285 {
       
   286   *bitrate = info->bitrate;
       
   287 
       
   288   return SMOKECODEC_OK;
       
   289 }
       
   290 
       
   291 static void
       
   292 find_best_size (int blocks, unsigned int *width, unsigned int *height)
       
   293 {
       
   294   int sqchng;
       
   295   int w, h;
       
   296   int best, bestw;
       
   297   int free;
       
   298 
       
   299   sqchng = ceil (sqrt (blocks));
       
   300   w = sqchng;
       
   301   h = sqchng;
       
   302 
       
   303   DEBUG ("guess: %d %d\n", w, h);
       
   304 
       
   305   free = w * h - blocks;
       
   306   best = free;
       
   307   bestw = w;
       
   308 
       
   309   while (w < 256) {
       
   310     DEBUG ("current: %d %d\n", w, h);
       
   311     if (free < best) {
       
   312       best = free;
       
   313       bestw = w;
       
   314       if (free == 0)
       
   315         break;
       
   316     }
       
   317     // if we cannot reduce the height, increase width
       
   318     if (free < w) {
       
   319       w++;
       
   320       free += h;
       
   321     }
       
   322     // reduce height while possible
       
   323     while (free >= w) {
       
   324       h--;
       
   325       free -= w;
       
   326     }
       
   327   }
       
   328   *width = bestw;
       
   329   *height = (blocks + best) / bestw;
       
   330 }
       
   331 
       
   332 static int
       
   333 abs_diff (const unsigned char *in1, const unsigned char *in2, const int stride)
       
   334 {
       
   335   int s;
       
   336   int i, j, diff;
       
   337 
       
   338   s = 0;
       
   339 
       
   340   for (i = 0; i < 2 * DCTSIZE; i++) {
       
   341     for (j = 0; j < 2 * DCTSIZE; j++) {
       
   342       diff = in1[j] - in2[j];
       
   343       s += diff * diff;
       
   344     }
       
   345     in1 += stride;
       
   346     in2 += stride;
       
   347   }
       
   348   return s;
       
   349 }
       
   350 
       
   351 static void
       
   352 put (const unsigned char *src, unsigned char *dest,
       
   353     int width, int height, int srcstride, int deststride)
       
   354 {
       
   355   int i, j;
       
   356 
       
   357   for (i = 0; i < height; i++) {
       
   358     for (j = 0; j < width; j++) {
       
   359       dest[j] = src[j];
       
   360     }
       
   361     src += srcstride;
       
   362     dest += deststride;
       
   363   }
       
   364 }
       
   365 
       
   366 /* encoding */
       
   367 SmokeCodecResult
       
   368 smokecodec_encode_id (SmokeCodecInfo * info,
       
   369     unsigned char *out, unsigned int *outsize)
       
   370 {
       
   371   int i;
       
   372 
       
   373   *out++ = SMOKECODEC_TYPE_ID;
       
   374   for (i = 0; i < strlen (SMOKECODEC_ID_STRING); i++) {
       
   375     *out++ = SMOKECODEC_ID_STRING[i];
       
   376   }
       
   377   *out++ = 0;
       
   378   *out++ = 1;
       
   379   *out++ = 0;
       
   380 
       
   381   *outsize = 9;
       
   382 
       
   383   return SMOKECODEC_OK;
       
   384 }
       
   385 
       
   386 SmokeCodecResult
       
   387 smokecodec_encode (SmokeCodecInfo * info,
       
   388     const unsigned char *in,
       
   389     SmokeCodecFlags flags, unsigned char *out, unsigned int *outsize)
       
   390 {
       
   391   unsigned int i, j, s;
       
   392   const unsigned char *ip;
       
   393   unsigned char *op;
       
   394   unsigned int blocks, encoding;
       
   395   unsigned int size;
       
   396   unsigned int width, height;
       
   397   unsigned int blocks_w, blocks_h;
       
   398   unsigned int threshold;
       
   399   unsigned int max;
       
   400 
       
   401   if (info->need_keyframe) {
       
   402     flags |= SMOKECODEC_KEYFRAME;
       
   403     info->need_keyframe = 0;
       
   404   }
       
   405 
       
   406   if (flags & SMOKECODEC_KEYFRAME)
       
   407     threshold = 0;
       
   408   else
       
   409     threshold = info->threshold;
       
   410 
       
   411   ip = in;
       
   412   op = info->reference;
       
   413 
       
   414   width = info->width;
       
   415   height = info->height;
       
   416 
       
   417   blocks_w = width / (DCTSIZE * 2);
       
   418   blocks_h = height / (DCTSIZE * 2);
       
   419 
       
   420   max = blocks_w * blocks_h;
       
   421 
       
   422   out[IDX_TYPE] = SMOKECODEC_TYPE_DATA;
       
   423 
       
   424 #define STORE16(var, pos, x) \
       
   425    var[pos]   = (x >> 8); \
       
   426    var[pos+1] = (x & 0xff);
       
   427 #define STORE32(var, pos, x) \
       
   428    var[pos]   = ((x >> 24) & 0xff); \
       
   429    var[pos+1] = ((x >> 16) & 0xff); \
       
   430    var[pos+2] = ((x >> 8) & 0xff); \
       
   431    var[pos+3] =  (x & 0xff);
       
   432 
       
   433   /* write dimension */
       
   434   STORE16 (out, IDX_WIDTH, width);
       
   435   STORE16 (out, IDX_HEIGHT, height);
       
   436 
       
   437   /* write framerate */
       
   438   STORE32 (out, IDX_FPS_NUM, info->fps_num);
       
   439   STORE32 (out, IDX_FPS_DENOM, info->fps_denom);
       
   440 
       
   441   if (!(flags & SMOKECODEC_KEYFRAME)) {
       
   442     int block = 0;
       
   443 
       
   444     blocks = 0;
       
   445     for (i = 0; i < height; i += 2 * DCTSIZE) {
       
   446       for (j = 0; j < width; j += 2 * DCTSIZE) {
       
   447         s = abs_diff (ip, op, width);
       
   448         if (s >= threshold) {
       
   449           STORE16 (out, blocks * 2 + IDX_BLOCKS, block);
       
   450           blocks++;
       
   451         }
       
   452 
       
   453         ip += 2 * DCTSIZE;
       
   454         op += 2 * DCTSIZE;
       
   455         block++;
       
   456       }
       
   457       ip += (2 * DCTSIZE - 1) * width;
       
   458       op += (2 * DCTSIZE - 1) * width;
       
   459     }
       
   460     if (blocks == max) {
       
   461       flags |= SMOKECODEC_KEYFRAME;
       
   462       blocks = 0;
       
   463       encoding = max;
       
   464     } else {
       
   465       encoding = blocks;
       
   466     }
       
   467   } else {
       
   468     blocks = 0;
       
   469     encoding = max;
       
   470   }
       
   471   STORE16 (out, IDX_NUM_BLOCKS, blocks);
       
   472   out[IDX_FLAGS] = (flags & 0xff);
       
   473 
       
   474   DEBUG ("blocks %d, encoding %d\n", blocks, encoding);
       
   475 
       
   476   info->jdest.next_output_byte = &out[blocks * 2 + OFFS_PICT];
       
   477   info->jdest.free_in_buffer = (*outsize) - OFFS_PICT;
       
   478 
       
   479   if (encoding > 0) {
       
   480     int quality;
       
   481 
       
   482     if (!(flags & SMOKECODEC_KEYFRAME))
       
   483       find_best_size (encoding, &blocks_w, &blocks_h);
       
   484 
       
   485     DEBUG ("best: %d %d\n", blocks_w, blocks_h);
       
   486 
       
   487     info->cinfo.image_width = blocks_w * DCTSIZE * 2;
       
   488     info->cinfo.image_height = blocks_h * DCTSIZE * 2;
       
   489 
       
   490     if (flags & SMOKECODEC_KEYFRAME) {
       
   491       quality = (info->maxquality * 60) / 100;
       
   492     } else {
       
   493       quality =
       
   494           info->maxquality - ((info->maxquality -
       
   495               info->minquality) * blocks) / max;
       
   496     }
       
   497 
       
   498     DEBUG ("set q %d %d %d\n", quality, encoding, max);
       
   499     jpeg_set_quality (&info->cinfo, quality, TRUE);
       
   500     DEBUG ("start\n");
       
   501     jpeg_start_compress (&info->cinfo, TRUE);
       
   502 
       
   503     for (i = 0; i < encoding; i++) {
       
   504       int pos;
       
   505       int x, y;
       
   506 
       
   507       if (flags & SMOKECODEC_KEYFRAME)
       
   508         pos = i;
       
   509       else
       
   510         pos = (out[i * 2 + IDX_BLOCKS] << 8) | (out[i * 2 + IDX_BLOCKS + 1]);
       
   511 
       
   512       x = pos % (width / (DCTSIZE * 2));
       
   513       y = pos / (width / (DCTSIZE * 2));
       
   514 
       
   515       ip = in + (x * (DCTSIZE * 2)) + (y * (DCTSIZE * 2) * width);
       
   516       op = info->compbuf[0] + (i % blocks_w) * (DCTSIZE * 2);
       
   517       put (ip, op, 2 * DCTSIZE, 2 * DCTSIZE, width, 256 * (DCTSIZE * 2));
       
   518 
       
   519       ip = in + width * height + (x * DCTSIZE) + (y * DCTSIZE * width / 2);
       
   520       op = info->compbuf[1] + (i % blocks_w) * (DCTSIZE);
       
   521       put (ip, op, DCTSIZE, DCTSIZE, width / 2, 256 * DCTSIZE);
       
   522 
       
   523       ip = in + 5 * (width * height) / 4 + (x * DCTSIZE) +
       
   524           (y * DCTSIZE * width / 2);
       
   525       op = info->compbuf[2] + (i % blocks_w) * (DCTSIZE);
       
   526       put (ip, op, DCTSIZE, DCTSIZE, width / 2, 256 * DCTSIZE);
       
   527 
       
   528       if ((i % blocks_w) == (blocks_w - 1) || (i == encoding - 1)) {
       
   529         DEBUG ("write %d\n", pos);
       
   530         jpeg_write_raw_data (&info->cinfo, info->line, 2 * DCTSIZE);
       
   531       }
       
   532     }
       
   533     DEBUG ("finish\n");
       
   534     jpeg_finish_compress (&info->cinfo);
       
   535   }
       
   536 
       
   537   size = ((((*outsize) - OFFS_PICT - info->jdest.free_in_buffer) + 3) & ~3);
       
   538   STORE16 (out, IDX_SIZE, size);
       
   539 
       
   540   *outsize = size + blocks * 2 + OFFS_PICT;
       
   541   DEBUG ("outsize %d\n", *outsize);
       
   542 
       
   543   // and decode in reference frame again
       
   544   if (info->refdec) {
       
   545     smokecodec_decode (info, out, *outsize, info->reference);
       
   546   } else {
       
   547     memcpy (info->reference, in, 3 * (width * height) / 2);
       
   548   }
       
   549 
       
   550   return SMOKECODEC_OK;
       
   551 }
       
   552 
       
   553 SmokeCodecResult
       
   554 smokecodec_parse_id (SmokeCodecInfo * info,
       
   555     const unsigned char *in, const unsigned int insize)
       
   556 {
       
   557   int i;
       
   558 
       
   559   if (insize < 4 + strlen (SMOKECODEC_ID_STRING)) {
       
   560     return SMOKECODEC_WRONGVERSION;
       
   561   }
       
   562 
       
   563   if (*in++ != SMOKECODEC_TYPE_ID)
       
   564     return SMOKECODEC_ERROR;
       
   565 
       
   566   for (i = 0; i < strlen (SMOKECODEC_ID_STRING); i++) {
       
   567     if (*in++ != SMOKECODEC_ID_STRING[i])
       
   568       return SMOKECODEC_ERROR;
       
   569   }
       
   570   if (*in++ != 0 || *in++ != 1 || *in++ != 0)
       
   571     return SMOKECODEC_ERROR;
       
   572 
       
   573   return SMOKECODEC_OK;
       
   574 }
       
   575 
       
   576 #define READ16(var, pos, x) \
       
   577  x = var[pos]<<8 | var[pos+1];
       
   578 
       
   579 #define READ32(var, pos, x) \
       
   580  x = var[pos]<<24 | var[pos+1]<<16 | \
       
   581      var[pos+2]<<8 | var[pos+3];
       
   582 
       
   583 /* decoding */
       
   584 SmokeCodecResult
       
   585 smokecodec_parse_header (SmokeCodecInfo * info,
       
   586     const unsigned char *in,
       
   587     const unsigned int insize,
       
   588     SmokeCodecFlags * flags,
       
   589     unsigned int *width,
       
   590     unsigned int *height, unsigned int *fps_num, unsigned int *fps_denom)
       
   591 {
       
   592 
       
   593   READ16 (in, IDX_WIDTH, *width);
       
   594   READ16 (in, IDX_HEIGHT, *height);
       
   595   *flags = in[IDX_FLAGS];
       
   596   READ32 (in, IDX_FPS_NUM, *fps_num);
       
   597   READ32 (in, IDX_FPS_DENOM, *fps_denom);
       
   598 
       
   599   if (info->width != *width ||
       
   600       info->height != *height ||
       
   601       info->fps_num != *fps_num || info->fps_denom != *fps_denom) {
       
   602     DEBUG ("new width: %d %d\n", *width, *height);
       
   603 
       
   604     info->reference = realloc (info->reference, 3 * ((*width) * (*height)) / 2);
       
   605     info->width = *width;
       
   606     info->height = *height;
       
   607     info->fps_num = *fps_num;
       
   608     info->fps_denom = *fps_denom;
       
   609   }
       
   610 
       
   611   return SMOKECODEC_OK;
       
   612 }
       
   613 
       
   614 SmokeCodecResult
       
   615 smokecodec_decode (SmokeCodecInfo * info,
       
   616     const unsigned char *in, const unsigned int insize, unsigned char *out)
       
   617 {
       
   618   unsigned int width, height;
       
   619   unsigned int fps_num, fps_denom;
       
   620   SmokeCodecFlags flags;
       
   621   int i, j;
       
   622   int blocks_w, blocks_h;
       
   623   int blockptr;
       
   624   int blocks, decoding;
       
   625   const unsigned char *ip;
       
   626   unsigned char *op;
       
   627   int res;
       
   628 
       
   629   smokecodec_parse_header (info, in, insize, &flags, &width, &height,
       
   630       &fps_num, &fps_denom);
       
   631 
       
   632   READ16 (in, IDX_NUM_BLOCKS, blocks);
       
   633   DEBUG ("blocks %d\n", blocks);
       
   634 
       
   635   if (flags & SMOKECODEC_KEYFRAME)
       
   636     decoding = width / (DCTSIZE * 2) * height / (DCTSIZE * 2);
       
   637   else
       
   638     decoding = blocks;
       
   639 
       
   640   if (decoding > 0) {
       
   641     info->jsrc.next_input_byte = &in[blocks * 2 + OFFS_PICT];
       
   642     info->jsrc.bytes_in_buffer = insize - (blocks * 2 + OFFS_PICT);
       
   643 
       
   644     DEBUG ("header %02x %d\n", in[blocks * 2 + OFFS_PICT], insize);
       
   645     res = jpeg_read_header (&info->dinfo, TRUE);
       
   646     DEBUG ("header %d %d %d\n", res, info->dinfo.image_width,
       
   647         info->dinfo.image_height);
       
   648 
       
   649     blocks_w = info->dinfo.image_width / (2 * DCTSIZE);
       
   650     blocks_h = info->dinfo.image_height / (2 * DCTSIZE);
       
   651 
       
   652     info->dinfo.output_width = info->dinfo.image_width;
       
   653     info->dinfo.output_height = info->dinfo.image_height;
       
   654 
       
   655     DEBUG ("start\n");
       
   656     info->dinfo.do_fancy_upsampling = FALSE;
       
   657     info->dinfo.do_block_smoothing = FALSE;
       
   658     info->dinfo.out_color_space = JCS_YCbCr;
       
   659     info->dinfo.dct_method = JDCT_IFAST;
       
   660     info->dinfo.raw_data_out = TRUE;
       
   661     jpeg_start_decompress (&info->dinfo);
       
   662 
       
   663     blockptr = 0;
       
   664 
       
   665     for (i = 0; i < blocks_h; i++) {
       
   666       DEBUG ("read\n");
       
   667       jpeg_read_raw_data (&info->dinfo, info->line, 2 * DCTSIZE);
       
   668 
       
   669       DEBUG ("copy %d\n", blocks_w);
       
   670       for (j = 0; j < blocks_w; j++) {
       
   671         int pos;
       
   672         int x, y;
       
   673 
       
   674         if (flags & SMOKECODEC_KEYFRAME)
       
   675           pos = blockptr;
       
   676         else
       
   677           READ16 (in, blockptr * 2 + IDX_BLOCKS, pos);
       
   678 
       
   679         x = pos % (width / (DCTSIZE * 2));
       
   680         y = pos / (width / (DCTSIZE * 2));
       
   681 
       
   682         DEBUG ("block %d %d %d\n", pos, x, y);
       
   683 
       
   684         ip = info->compbuf[0] + j * (DCTSIZE * 2);
       
   685         op = info->reference + (x * (DCTSIZE * 2)) +
       
   686             (y * (DCTSIZE * 2) * width);
       
   687         put (ip, op, 2 * DCTSIZE, 2 * DCTSIZE, 256 * (DCTSIZE * 2), width);
       
   688 
       
   689         ip = info->compbuf[1] + j * (DCTSIZE);
       
   690         op = info->reference + width * height + (x * DCTSIZE) +
       
   691             (y * DCTSIZE * width / 2);
       
   692         put (ip, op, DCTSIZE, DCTSIZE, 256 * DCTSIZE, width / 2);
       
   693 
       
   694         ip = info->compbuf[2] + j * (DCTSIZE);
       
   695         op = info->reference + 5 * (width * height) / 4 + (x * DCTSIZE) +
       
   696             (y * DCTSIZE * width / 2);
       
   697         put (ip, op, DCTSIZE, DCTSIZE, 256 * DCTSIZE, width / 2);
       
   698 
       
   699         DEBUG ("block done %d %d %d\n", pos, x, y);
       
   700         blockptr++;
       
   701         if (blockptr >= decoding)
       
   702           break;
       
   703       }
       
   704     }
       
   705     DEBUG ("finish\n");
       
   706     jpeg_finish_decompress (&info->dinfo);
       
   707   }
       
   708 
       
   709   DEBUG ("copy\n");
       
   710   if (out != info->reference)
       
   711     memcpy (out, info->reference, 3 * (width * height) / 2);
       
   712   DEBUG ("copy done\n");
       
   713 
       
   714   return SMOKECODEC_OK;
       
   715 }