Home | History | Annotate | Download | only in enc
      1 // Copyright 2011 Google Inc.
      2 //
      3 // This code is licensed under the same terms as WebM:
      4 //  Software License Agreement:  http://www.webmproject.org/license/software/
      5 //  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
      6 // -----------------------------------------------------------------------------
      7 //
      8 // WebP encoder: main entry point
      9 //
     10 // Author: Skal (pascal.massimino (at) gmail.com)
     11 
     12 #include <assert.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <math.h>
     16 
     17 #include "vp8enci.h"
     18 
     19 // #define PRINT_MEMORY_INFO
     20 
     21 #if defined(__cplusplus) || defined(c_plusplus)
     22 extern "C" {
     23 #endif
     24 
     25 #ifdef PRINT_MEMORY_INFO
     26 #include <stdio.h>
     27 #endif
     28 
     29 #define MAX_DIMENSION 16384   // maximum width/height allowed by the spec
     30 
     31 //-----------------------------------------------------------------------------
     32 
     33 int WebPGetEncoderVersion(void) {
     34   return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;
     35 }
     36 
     37 //-----------------------------------------------------------------------------
     38 // WebPPicture
     39 //-----------------------------------------------------------------------------
     40 
     41 static int DummyWriter(const uint8_t* data, size_t data_size,
     42                        const WebPPicture* const picture) {
     43   // The following are to prevent 'unused variable' error message.
     44   (void)data;
     45   (void)data_size;
     46   (void)picture;
     47   return 1;
     48 }
     49 
     50 int WebPPictureInitInternal(WebPPicture* const picture, int version) {
     51   if (version != WEBP_ENCODER_ABI_VERSION) {
     52     return 0;   // caller/system version mismatch!
     53   }
     54   if (picture) {
     55     memset(picture, 0, sizeof(*picture));
     56     picture->writer = DummyWriter;
     57     WebPEncodingSetError(picture, VP8_ENC_OK);
     58   }
     59   return 1;
     60 }
     61 
     62 //-----------------------------------------------------------------------------
     63 // VP8Encoder
     64 //-----------------------------------------------------------------------------
     65 
     66 static void ResetSegmentHeader(VP8Encoder* const enc) {
     67   VP8SegmentHeader* const hdr = &enc->segment_hdr_;
     68   hdr->num_segments_ = enc->config_->segments;
     69   hdr->update_map_  = (hdr->num_segments_ > 1);
     70   hdr->size_ = 0;
     71 }
     72 
     73 static void ResetFilterHeader(VP8Encoder* const enc) {
     74   VP8FilterHeader* const hdr = &enc->filter_hdr_;
     75   hdr->simple_ = 1;
     76   hdr->level_ = 0;
     77   hdr->sharpness_ = 0;
     78   hdr->i4x4_lf_delta_ = 0;
     79 }
     80 
     81 static void ResetBoundaryPredictions(VP8Encoder* const enc) {
     82   // init boundary values once for all
     83   // Note: actually, initializing the preds_[] is only needed for intra4.
     84   int i;
     85   uint8_t* const top = enc->preds_ - enc->preds_w_;
     86   uint8_t* const left = enc->preds_ - 1;
     87   for (i = -1; i < 4 * enc->mb_w_; ++i) {
     88     top[i] = B_DC_PRED;
     89   }
     90   for (i = 0; i < 4 * enc->mb_h_; ++i) {
     91     left[i * enc->preds_w_] = B_DC_PRED;
     92   }
     93   enc->nz_[-1] = 0;   // constant
     94 }
     95 
     96 // Map configured quality level to coding tools used.
     97 //-------------+---+---+---+---+---+---+
     98 //   Quality   | 0 | 1 | 2 | 3 | 4 | 5 +
     99 //-------------+---+---+---+---+---+---+
    100 // dynamic prob| ~ | x | x | x | x | x |
    101 //-------------+---+---+---+---+---+---+
    102 // rd-opt modes|   |   | x | x | x | x |
    103 //-------------+---+---+---+---+---+---+
    104 // fast i4/i16 | x | x |   |   |   |   |
    105 //-------------+---+---+---+---+---+---+
    106 // rd-opt i4/16|   |   | x | x | x | x |
    107 //-------------+---+---+---+---+---+---+
    108 // Trellis     |   | x |   |   | x | x |
    109 //-------------+---+---+---+---+---+---+
    110 // full-SNS    |   |   |   |   |   | x |
    111 //-------------+---+---+---+---+---+---+
    112 
    113 static void MapConfigToTools(VP8Encoder* const enc) {
    114   const int method = enc->config_->method;
    115   enc->method_ = method;
    116   enc->rd_opt_level_ = (method >= 6) ? 3
    117                      : (method >= 5) ? 2
    118                      : (method >= 3) ? 1
    119                      : 0;
    120 }
    121 
    122 // Memory scaling with dimensions:
    123 //  memory (bytes) ~= 2.25 * w + 0.0625 * w * h
    124 //
    125 // Typical memory footprint (768x510 picture)
    126 // Memory used:
    127 //              encoder: 33919
    128 //          block cache: 2880
    129 //                 info: 3072
    130 //                preds: 24897
    131 //          top samples: 1623
    132 //             non-zero: 196
    133 //             lf-stats: 2048
    134 //                total: 68635
    135 // Transcient object sizes:
    136 //       VP8EncIterator: 352
    137 //         VP8ModeScore: 912
    138 //       VP8SegmentInfo: 532
    139 //             VP8Proba: 31032
    140 //              LFStats: 2048
    141 // Picture size (yuv): 589824
    142 
    143 static VP8Encoder* InitEncoder(const WebPConfig* const config,
    144                                WebPPicture* const picture) {
    145   const int use_filter =
    146       (config->filter_strength > 0) || (config->autofilter > 0);
    147   const int mb_w = (picture->width + 15) >> 4;
    148   const int mb_h = (picture->height + 15) >> 4;
    149   const int preds_w = 4 * mb_w + 1;
    150   const int preds_h = 4 * mb_h + 1;
    151   const size_t preds_size = preds_w * preds_h * sizeof(uint8_t);
    152   const int top_stride = mb_w * 16;
    153   const size_t nz_size = (mb_w + 1) * sizeof(uint32_t);
    154   const size_t cache_size = (3 * YUV_SIZE + PRED_SIZE) * sizeof(uint8_t);
    155   const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo);
    156   const size_t samples_size = (2 * top_stride +         // top-luma/u/v
    157                                16 + 16 + 16 + 8 + 1 +   // left y/u/v
    158                                2 * ALIGN_CST)           // align all
    159                                * sizeof(uint8_t);
    160   const size_t lf_stats_size =
    161       config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
    162   VP8Encoder* enc;
    163   uint8_t* mem;
    164   size_t size = sizeof(VP8Encoder) + ALIGN_CST  // main struct
    165               + cache_size                      // working caches
    166               + info_size                       // modes info
    167               + preds_size                      // prediction modes
    168               + samples_size                    // top/left samples
    169               + nz_size                         // coeff context bits
    170               + lf_stats_size;                  // autofilter stats
    171 
    172 #ifdef PRINT_MEMORY_INFO
    173   printf("===================================\n");
    174   printf("Memory used:\n"
    175          "             encoder: %ld\n"
    176          "         block cache: %ld\n"
    177          "                info: %ld\n"
    178          "               preds: %ld\n"
    179          "         top samples: %ld\n"
    180          "            non-zero: %ld\n"
    181          "            lf-stats: %ld\n"
    182          "               total: %ld\n",
    183          sizeof(VP8Encoder) + ALIGN_CST, cache_size, info_size,
    184          preds_size, samples_size, nz_size, lf_stats_size, size);
    185   printf("Transcient object sizes:\n"
    186          "      VP8EncIterator: %ld\n"
    187          "        VP8ModeScore: %ld\n"
    188          "      VP8SegmentInfo: %ld\n"
    189          "            VP8Proba: %ld\n"
    190          "             LFStats: %ld\n",
    191          sizeof(VP8EncIterator), sizeof(VP8ModeScore),
    192          sizeof(VP8SegmentInfo), sizeof(VP8Proba),
    193          sizeof(LFStats));
    194   printf("Picture size (yuv): %ld\n",
    195          mb_w * mb_h * 384 * sizeof(uint8_t));
    196   printf("===================================\n");
    197 #endif
    198   mem = (uint8_t*)malloc(size);
    199   if (mem == NULL) {
    200     WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
    201     return NULL;
    202   }
    203   enc = (VP8Encoder*)mem;
    204   mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc));
    205   memset(enc, 0, sizeof(*enc));
    206   enc->num_parts_ = 1 << config->partitions;
    207   enc->mb_w_ = mb_w;
    208   enc->mb_h_ = mb_h;
    209   enc->preds_w_ = preds_w;
    210   enc->yuv_in_ = (uint8_t*)mem;
    211   mem += YUV_SIZE;
    212   enc->yuv_out_ = (uint8_t*)mem;
    213   mem += YUV_SIZE;
    214   enc->yuv_out2_ = (uint8_t*)mem;
    215   mem += YUV_SIZE;
    216   enc->yuv_p_ = (uint8_t*)mem;
    217   mem += PRED_SIZE;
    218   enc->mb_info_ = (VP8MBInfo*)mem;
    219   mem += info_size;
    220   enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_;
    221   mem += preds_w * preds_h * sizeof(uint8_t);
    222   enc->nz_ = 1 + (uint32_t*)mem;
    223   mem += nz_size;
    224   enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL;
    225   mem += lf_stats_size;
    226 
    227   // top samples (all 16-aligned)
    228   mem = (uint8_t*)DO_ALIGN(mem);
    229   enc->y_top_ = (uint8_t*)mem;
    230   enc->uv_top_ = enc->y_top_ + top_stride;
    231   mem += 2 * top_stride;
    232   mem = (uint8_t*)DO_ALIGN(mem + 1);
    233   enc->y_left_ = (uint8_t*)mem;
    234   mem += 16 + 16;
    235   enc->u_left_ = (uint8_t*)mem;
    236   mem += 16;
    237   enc->v_left_ = (uint8_t*)mem;
    238   mem += 8;
    239 
    240   enc->config_ = config;
    241   enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
    242   enc->pic_ = picture;
    243 
    244   MapConfigToTools(enc);
    245   VP8EncDspInit();
    246   VP8DefaultProbas(enc);
    247   ResetSegmentHeader(enc);
    248   ResetFilterHeader(enc);
    249   ResetBoundaryPredictions(enc);
    250 
    251 #ifdef WEBP_EXPERIMENTAL_FEATURES
    252   VP8EncInitAlpha(enc);
    253   VP8EncInitLayer(enc);
    254 #endif
    255 
    256   return enc;
    257 }
    258 
    259 static void DeleteEncoder(VP8Encoder* enc) {
    260   if (enc) {
    261 #ifdef WEBP_EXPERIMENTAL_FEATURES
    262     VP8EncDeleteAlpha(enc);
    263     VP8EncDeleteLayer(enc);
    264 #endif
    265     free(enc);
    266   }
    267 }
    268 
    269 //-----------------------------------------------------------------------------
    270 
    271 static double GetPSNR(uint64_t err, uint64_t size) {
    272   return err ? 10. * log10(255. * 255. * size / err) : 99.;
    273 }
    274 
    275 static void FinalizePSNR(const VP8Encoder* const enc) {
    276   WebPAuxStats* stats = enc->pic_->stats;
    277   const uint64_t size = enc->sse_count_;
    278   const uint64_t* const sse = enc->sse_;
    279   stats->PSNR[0] = (float)GetPSNR(sse[0], size);
    280   stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
    281   stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
    282   stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
    283 }
    284 
    285 static void StoreStats(VP8Encoder* const enc) {
    286   WebPAuxStats* const stats = enc->pic_->stats;
    287   if (stats) {
    288     int i, s;
    289     for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
    290       stats->segment_level[i] = enc->dqm_[i].fstrength_;
    291       stats->segment_quant[i] = enc->dqm_[i].quant_;
    292       for (s = 0; s <= 2; ++s) {
    293         stats->residual_bytes[s][i] = enc->residual_bytes_[s][i];
    294       }
    295     }
    296     FinalizePSNR(enc);
    297     stats->coded_size = enc->coded_size_;
    298     for (i = 0; i < 3; ++i) {
    299       stats->block_count[i] = enc->block_count_[i];
    300     }
    301   }
    302 }
    303 
    304 int WebPEncodingSetError(WebPPicture* const pic, WebPEncodingError error) {
    305   assert((int)error <= VP8_ENC_ERROR_BAD_WRITE);
    306   assert((int)error >= VP8_ENC_OK);
    307   pic->error_code = error;
    308   return 0;
    309 }
    310 
    311 //-----------------------------------------------------------------------------
    312 
    313 int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
    314   VP8Encoder* enc;
    315   int ok;
    316 
    317   if (pic == NULL)
    318     return 0;
    319   WebPEncodingSetError(pic, VP8_ENC_OK);  // all ok so far
    320   if (config == NULL)  // bad params
    321     return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
    322   if (!WebPValidateConfig(config))
    323     return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
    324   if (pic->width <= 0 || pic->height <= 0)
    325     return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
    326   if (pic->y == NULL || pic->u == NULL || pic->v == NULL)
    327     return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
    328   if (pic->width >= MAX_DIMENSION || pic->height >= MAX_DIMENSION)
    329     return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
    330 
    331   enc = InitEncoder(config, pic);
    332   if (enc == NULL) return 0;  // pic->error is already set.
    333   ok = VP8EncAnalyze(enc)
    334     && VP8StatLoop(enc)
    335     && VP8EncLoop(enc)
    336 #ifdef WEBP_EXPERIMENTAL_FEATURES
    337     && VP8EncFinishAlpha(enc)
    338     && VP8EncFinishLayer(enc)
    339 #endif
    340     && VP8EncWrite(enc);
    341   StoreStats(enc);
    342   DeleteEncoder(enc);
    343 
    344   return ok;
    345 }
    346 
    347 #if defined(__cplusplus) || defined(c_plusplus)
    348 }    // extern "C"
    349 #endif
    350