Home | History | Annotate | Download | only in source
      1 /*
      2  *  Copyright 2012 The LibYuv Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS. All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "libyuv/mjpeg_decoder.h"
     12 
     13 #ifdef HAVE_JPEG
     14 #include <assert.h>
     15 
     16 #if !defined(__pnacl__) && !defined(__CLR_VER) && \
     17     !defined(COVERAGE_ENABLED) && !defined(TARGET_IPHONE_SIMULATOR)
     18 // Must be included before jpeglib.
     19 #include <setjmp.h>
     20 #define HAVE_SETJMP
     21 
     22 #if defined(_MSC_VER)
     23 // disable warning 4324: structure was padded due to __declspec(align())
     24 #pragma warning(disable:4324)
     25 #endif
     26 
     27 #endif
     28 struct FILE;  // For jpeglib.h.
     29 
     30 // C++ build requires extern C for jpeg internals.
     31 #ifdef __cplusplus
     32 extern "C" {
     33 #endif
     34 
     35 #include <jpeglib.h>
     36 
     37 #ifdef __cplusplus
     38 }  // extern "C"
     39 #endif
     40 
     41 #include "libyuv/planar_functions.h"  // For CopyPlane().
     42 
     43 namespace libyuv {
     44 
     45 #ifdef HAVE_SETJMP
     46 struct SetJmpErrorMgr {
     47   jpeg_error_mgr base;  // Must be at the top
     48   jmp_buf setjmp_buffer;
     49 };
     50 #endif
     51 
     52 const int MJpegDecoder::kColorSpaceUnknown = JCS_UNKNOWN;
     53 const int MJpegDecoder::kColorSpaceGrayscale = JCS_GRAYSCALE;
     54 const int MJpegDecoder::kColorSpaceRgb = JCS_RGB;
     55 const int MJpegDecoder::kColorSpaceYCbCr = JCS_YCbCr;
     56 const int MJpegDecoder::kColorSpaceCMYK = JCS_CMYK;
     57 const int MJpegDecoder::kColorSpaceYCCK = JCS_YCCK;
     58 
     59 // Methods that are passed to jpeglib.
     60 boolean fill_input_buffer(jpeg_decompress_struct* cinfo);
     61 void init_source(jpeg_decompress_struct* cinfo);
     62 void skip_input_data(jpeg_decompress_struct* cinfo, long num_bytes);  // NOLINT
     63 void term_source(jpeg_decompress_struct* cinfo);
     64 void ErrorHandler(jpeg_common_struct* cinfo);
     65 
     66 MJpegDecoder::MJpegDecoder()
     67     : has_scanline_padding_(LIBYUV_FALSE),
     68       num_outbufs_(0),
     69       scanlines_(NULL),
     70       scanlines_sizes_(NULL),
     71       databuf_(NULL),
     72       databuf_strides_(NULL) {
     73   decompress_struct_ = new jpeg_decompress_struct;
     74   source_mgr_ = new jpeg_source_mgr;
     75 #ifdef HAVE_SETJMP
     76   error_mgr_ = new SetJmpErrorMgr;
     77   decompress_struct_->err = jpeg_std_error(&error_mgr_->base);
     78   // Override standard exit()-based error handler.
     79   error_mgr_->base.error_exit = &ErrorHandler;
     80 #endif
     81   decompress_struct_->client_data = NULL;
     82   source_mgr_->init_source = &init_source;
     83   source_mgr_->fill_input_buffer = &fill_input_buffer;
     84   source_mgr_->skip_input_data = &skip_input_data;
     85   source_mgr_->resync_to_restart = &jpeg_resync_to_restart;
     86   source_mgr_->term_source = &term_source;
     87   jpeg_create_decompress(decompress_struct_);
     88   decompress_struct_->src = source_mgr_;
     89   buf_vec_.buffers = &buf_;
     90   buf_vec_.len = 1;
     91 }
     92 
     93 MJpegDecoder::~MJpegDecoder() {
     94   jpeg_destroy_decompress(decompress_struct_);
     95   delete decompress_struct_;
     96   delete source_mgr_;
     97 #ifdef HAVE_SETJMP
     98   delete error_mgr_;
     99 #endif
    100   DestroyOutputBuffers();
    101 }
    102 
    103 LIBYUV_BOOL MJpegDecoder::LoadFrame(const uint8* src, size_t src_len) {
    104   if (!ValidateJpeg(src, src_len)) {
    105     return LIBYUV_FALSE;
    106   }
    107 
    108   buf_.data = src;
    109   buf_.len = static_cast<int>(src_len);
    110   buf_vec_.pos = 0;
    111   decompress_struct_->client_data = &buf_vec_;
    112 #ifdef HAVE_SETJMP
    113   if (setjmp(error_mgr_->setjmp_buffer)) {
    114     // We called jpeg_read_header, it experienced an error, and we called
    115     // longjmp() and rewound the stack to here. Return error.
    116     return LIBYUV_FALSE;
    117   }
    118 #endif
    119   if (jpeg_read_header(decompress_struct_, TRUE) != JPEG_HEADER_OK) {
    120     // ERROR: Bad MJPEG header
    121     return LIBYUV_FALSE;
    122   }
    123   AllocOutputBuffers(GetNumComponents());
    124   for (int i = 0; i < num_outbufs_; ++i) {
    125     int scanlines_size = GetComponentScanlinesPerImcuRow(i);
    126     if (scanlines_sizes_[i] != scanlines_size) {
    127       if (scanlines_[i]) {
    128         delete scanlines_[i];
    129       }
    130       scanlines_[i] = new uint8* [scanlines_size];
    131       scanlines_sizes_[i] = scanlines_size;
    132     }
    133 
    134     // We allocate padding for the final scanline to pad it up to DCTSIZE bytes
    135     // to avoid memory errors, since jpeglib only reads full MCUs blocks. For
    136     // the preceding scanlines, the padding is not needed/wanted because the
    137     // following addresses will already be valid (they are the initial bytes of
    138     // the next scanline) and will be overwritten when jpeglib writes out that
    139     // next scanline.
    140     int databuf_stride = GetComponentStride(i);
    141     int databuf_size = scanlines_size * databuf_stride;
    142     if (databuf_strides_[i] != databuf_stride) {
    143       if (databuf_[i]) {
    144         delete databuf_[i];
    145       }
    146       databuf_[i] = new uint8[databuf_size];
    147       databuf_strides_[i] = databuf_stride;
    148     }
    149 
    150     if (GetComponentStride(i) != GetComponentWidth(i)) {
    151       has_scanline_padding_ = LIBYUV_TRUE;
    152     }
    153   }
    154   return LIBYUV_TRUE;
    155 }
    156 
    157 static int DivideAndRoundUp(int numerator, int denominator) {
    158   return (numerator + denominator - 1) / denominator;
    159 }
    160 
    161 static int DivideAndRoundDown(int numerator, int denominator) {
    162   return numerator / denominator;
    163 }
    164 
    165 // Returns width of the last loaded frame.
    166 int MJpegDecoder::GetWidth() {
    167   return decompress_struct_->image_width;
    168 }
    169 
    170 // Returns height of the last loaded frame.
    171 int MJpegDecoder::GetHeight() {
    172   return decompress_struct_->image_height;
    173 }
    174 
    175 // Returns format of the last loaded frame. The return value is one of the
    176 // kColorSpace* constants.
    177 int MJpegDecoder::GetColorSpace() {
    178   return decompress_struct_->jpeg_color_space;
    179 }
    180 
    181 // Number of color components in the color space.
    182 int MJpegDecoder::GetNumComponents() {
    183   return decompress_struct_->num_components;
    184 }
    185 
    186 // Sample factors of the n-th component.
    187 int MJpegDecoder::GetHorizSampFactor(int component) {
    188   return decompress_struct_->comp_info[component].h_samp_factor;
    189 }
    190 
    191 int MJpegDecoder::GetVertSampFactor(int component) {
    192   return decompress_struct_->comp_info[component].v_samp_factor;
    193 }
    194 
    195 int MJpegDecoder::GetHorizSubSampFactor(int component) {
    196   return decompress_struct_->max_h_samp_factor /
    197       GetHorizSampFactor(component);
    198 }
    199 
    200 int MJpegDecoder::GetVertSubSampFactor(int component) {
    201   return decompress_struct_->max_v_samp_factor /
    202       GetVertSampFactor(component);
    203 }
    204 
    205 int MJpegDecoder::GetImageScanlinesPerImcuRow() {
    206   return decompress_struct_->max_v_samp_factor * DCTSIZE;
    207 }
    208 
    209 int MJpegDecoder::GetComponentScanlinesPerImcuRow(int component) {
    210   int vs = GetVertSubSampFactor(component);
    211   return DivideAndRoundUp(GetImageScanlinesPerImcuRow(), vs);
    212 }
    213 
    214 int MJpegDecoder::GetComponentWidth(int component) {
    215   int hs = GetHorizSubSampFactor(component);
    216   return DivideAndRoundUp(GetWidth(), hs);
    217 }
    218 
    219 int MJpegDecoder::GetComponentHeight(int component) {
    220   int vs = GetVertSubSampFactor(component);
    221   return DivideAndRoundUp(GetHeight(), vs);
    222 }
    223 
    224 // Get width in bytes padded out to a multiple of DCTSIZE
    225 int MJpegDecoder::GetComponentStride(int component) {
    226   return (GetComponentWidth(component) + DCTSIZE - 1) & ~(DCTSIZE - 1);
    227 }
    228 
    229 int MJpegDecoder::GetComponentSize(int component) {
    230   return GetComponentWidth(component) * GetComponentHeight(component);
    231 }
    232 
    233 LIBYUV_BOOL MJpegDecoder::UnloadFrame() {
    234 #ifdef HAVE_SETJMP
    235   if (setjmp(error_mgr_->setjmp_buffer)) {
    236     // We called jpeg_abort_decompress, it experienced an error, and we called
    237     // longjmp() and rewound the stack to here. Return error.
    238     return LIBYUV_FALSE;
    239   }
    240 #endif
    241   jpeg_abort_decompress(decompress_struct_);
    242   return LIBYUV_TRUE;
    243 }
    244 
    245 // TODO(fbarchard): Allow rectangle to be specified: x, y, width, height.
    246 LIBYUV_BOOL MJpegDecoder::DecodeToBuffers(
    247     uint8** planes, int dst_width, int dst_height) {
    248   if (dst_width != GetWidth() ||
    249       dst_height > GetHeight()) {
    250     // ERROR: Bad dimensions
    251     return LIBYUV_FALSE;
    252   }
    253 #ifdef HAVE_SETJMP
    254   if (setjmp(error_mgr_->setjmp_buffer)) {
    255     // We called into jpeglib, it experienced an error sometime during this
    256     // function call, and we called longjmp() and rewound the stack to here.
    257     // Return error.
    258     return LIBYUV_FALSE;
    259   }
    260 #endif
    261   if (!StartDecode()) {
    262     return LIBYUV_FALSE;
    263   }
    264   SetScanlinePointers(databuf_);
    265   int lines_left = dst_height;
    266   // Compute amount of lines to skip to implement vertical crop.
    267   // TODO(fbarchard): Ensure skip is a multiple of maximum component
    268   // subsample. ie 2
    269   int skip = (GetHeight() - dst_height) / 2;
    270   if (skip > 0) {
    271     // There is no API to skip lines in the output data, so we read them
    272     // into the temp buffer.
    273     while (skip >= GetImageScanlinesPerImcuRow()) {
    274       if (!DecodeImcuRow()) {
    275         FinishDecode();
    276         return LIBYUV_FALSE;
    277       }
    278       skip -= GetImageScanlinesPerImcuRow();
    279     }
    280     if (skip > 0) {
    281       // Have a partial iMCU row left over to skip. Must read it and then
    282       // copy the parts we want into the destination.
    283       if (!DecodeImcuRow()) {
    284         FinishDecode();
    285         return LIBYUV_FALSE;
    286       }
    287       for (int i = 0; i < num_outbufs_; ++i) {
    288         // TODO(fbarchard): Compute skip to avoid this
    289         assert(skip % GetVertSubSampFactor(i) == 0);
    290         int rows_to_skip =
    291             DivideAndRoundDown(skip, GetVertSubSampFactor(i));
    292         int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i) -
    293                                 rows_to_skip;
    294         int data_to_skip = rows_to_skip * GetComponentStride(i);
    295         CopyPlane(databuf_[i] + data_to_skip, GetComponentStride(i),
    296                   planes[i], GetComponentWidth(i),
    297                   GetComponentWidth(i), scanlines_to_copy);
    298         planes[i] += scanlines_to_copy * GetComponentWidth(i);
    299       }
    300       lines_left -= (GetImageScanlinesPerImcuRow() - skip);
    301     }
    302   }
    303 
    304   // Read full MCUs but cropped horizontally
    305   for (; lines_left > GetImageScanlinesPerImcuRow();
    306          lines_left -= GetImageScanlinesPerImcuRow()) {
    307     if (!DecodeImcuRow()) {
    308       FinishDecode();
    309       return LIBYUV_FALSE;
    310     }
    311     for (int i = 0; i < num_outbufs_; ++i) {
    312       int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i);
    313       CopyPlane(databuf_[i], GetComponentStride(i),
    314                 planes[i], GetComponentWidth(i),
    315                 GetComponentWidth(i), scanlines_to_copy);
    316       planes[i] += scanlines_to_copy * GetComponentWidth(i);
    317     }
    318   }
    319 
    320   if (lines_left > 0) {
    321     // Have a partial iMCU row left over to decode.
    322     if (!DecodeImcuRow()) {
    323       FinishDecode();
    324       return LIBYUV_FALSE;
    325     }
    326     for (int i = 0; i < num_outbufs_; ++i) {
    327       int scanlines_to_copy =
    328           DivideAndRoundUp(lines_left, GetVertSubSampFactor(i));
    329       CopyPlane(databuf_[i], GetComponentStride(i),
    330                 planes[i], GetComponentWidth(i),
    331                 GetComponentWidth(i), scanlines_to_copy);
    332       planes[i] += scanlines_to_copy * GetComponentWidth(i);
    333     }
    334   }
    335   return FinishDecode();
    336 }
    337 
    338 LIBYUV_BOOL MJpegDecoder::DecodeToCallback(CallbackFunction fn, void* opaque,
    339     int dst_width, int dst_height) {
    340   if (dst_width != GetWidth() ||
    341       dst_height > GetHeight()) {
    342     // ERROR: Bad dimensions
    343     return LIBYUV_FALSE;
    344   }
    345 #ifdef HAVE_SETJMP
    346   if (setjmp(error_mgr_->setjmp_buffer)) {
    347     // We called into jpeglib, it experienced an error sometime during this
    348     // function call, and we called longjmp() and rewound the stack to here.
    349     // Return error.
    350     return LIBYUV_FALSE;
    351   }
    352 #endif
    353   if (!StartDecode()) {
    354     return LIBYUV_FALSE;
    355   }
    356   SetScanlinePointers(databuf_);
    357   int lines_left = dst_height;
    358   // TODO(fbarchard): Compute amount of lines to skip to implement vertical crop
    359   int skip = (GetHeight() - dst_height) / 2;
    360   if (skip > 0) {
    361     while (skip >= GetImageScanlinesPerImcuRow()) {
    362       if (!DecodeImcuRow()) {
    363         FinishDecode();
    364         return LIBYUV_FALSE;
    365       }
    366       skip -= GetImageScanlinesPerImcuRow();
    367     }
    368     if (skip > 0) {
    369       // Have a partial iMCU row left over to skip.
    370       if (!DecodeImcuRow()) {
    371         FinishDecode();
    372         return LIBYUV_FALSE;
    373       }
    374       for (int i = 0; i < num_outbufs_; ++i) {
    375         // TODO(fbarchard): Compute skip to avoid this
    376         assert(skip % GetVertSubSampFactor(i) == 0);
    377         int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
    378         int data_to_skip = rows_to_skip * GetComponentStride(i);
    379         // Change our own data buffer pointers so we can pass them to the
    380         // callback.
    381         databuf_[i] += data_to_skip;
    382       }
    383       int scanlines_to_copy = GetImageScanlinesPerImcuRow() - skip;
    384       (*fn)(opaque, databuf_, databuf_strides_, scanlines_to_copy);
    385       // Now change them back.
    386       for (int i = 0; i < num_outbufs_; ++i) {
    387         int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
    388         int data_to_skip = rows_to_skip * GetComponentStride(i);
    389         databuf_[i] -= data_to_skip;
    390       }
    391       lines_left -= scanlines_to_copy;
    392     }
    393   }
    394   // Read full MCUs until we get to the crop point.
    395   for (; lines_left >= GetImageScanlinesPerImcuRow();
    396          lines_left -= GetImageScanlinesPerImcuRow()) {
    397     if (!DecodeImcuRow()) {
    398       FinishDecode();
    399       return LIBYUV_FALSE;
    400     }
    401     (*fn)(opaque, databuf_, databuf_strides_, GetImageScanlinesPerImcuRow());
    402   }
    403   if (lines_left > 0) {
    404     // Have a partial iMCU row left over to decode.
    405     if (!DecodeImcuRow()) {
    406       FinishDecode();
    407       return LIBYUV_FALSE;
    408     }
    409     (*fn)(opaque, databuf_, databuf_strides_, lines_left);
    410   }
    411   return FinishDecode();
    412 }
    413 
    414 void init_source(j_decompress_ptr cinfo) {
    415   fill_input_buffer(cinfo);
    416 }
    417 
    418 boolean fill_input_buffer(j_decompress_ptr cinfo) {
    419   BufferVector* buf_vec = reinterpret_cast<BufferVector*>(cinfo->client_data);
    420   if (buf_vec->pos >= buf_vec->len) {
    421     assert(0 && "No more data");
    422     // ERROR: No more data
    423     return FALSE;
    424   }
    425   cinfo->src->next_input_byte = buf_vec->buffers[buf_vec->pos].data;
    426   cinfo->src->bytes_in_buffer = buf_vec->buffers[buf_vec->pos].len;
    427   ++buf_vec->pos;
    428   return TRUE;
    429 }
    430 
    431 void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {  // NOLINT
    432   cinfo->src->next_input_byte += num_bytes;
    433 }
    434 
    435 void term_source(j_decompress_ptr cinfo) {
    436   // Nothing to do.
    437 }
    438 
    439 #ifdef HAVE_SETJMP
    440 void ErrorHandler(j_common_ptr cinfo) {
    441   // This is called when a jpeglib command experiences an error. Unfortunately
    442   // jpeglib's error handling model is not very flexible, because it expects the
    443   // error handler to not return--i.e., it wants the program to terminate. To
    444   // recover from errors we use setjmp() as shown in their example. setjmp() is
    445   // C's implementation for the "call with current continuation" functionality
    446   // seen in some functional programming languages.
    447   // A formatted message can be output, but is unsafe for release.
    448 #ifdef DEBUG
    449   char buf[JMSG_LENGTH_MAX];
    450   (*cinfo->err->format_message)(cinfo, buf);
    451   // ERROR: Error in jpeglib: buf
    452 #endif
    453 
    454   SetJmpErrorMgr* mgr = reinterpret_cast<SetJmpErrorMgr*>(cinfo->err);
    455   // This rewinds the call stack to the point of the corresponding setjmp()
    456   // and causes it to return (for a second time) with value 1.
    457   longjmp(mgr->setjmp_buffer, 1);
    458 }
    459 #endif
    460 
    461 void MJpegDecoder::AllocOutputBuffers(int num_outbufs) {
    462   if (num_outbufs != num_outbufs_) {
    463     // We could perhaps optimize this case to resize the output buffers without
    464     // necessarily having to delete and recreate each one, but it's not worth
    465     // it.
    466     DestroyOutputBuffers();
    467 
    468     scanlines_ = new uint8** [num_outbufs];
    469     scanlines_sizes_ = new int[num_outbufs];
    470     databuf_ = new uint8* [num_outbufs];
    471     databuf_strides_ = new int[num_outbufs];
    472 
    473     for (int i = 0; i < num_outbufs; ++i) {
    474       scanlines_[i] = NULL;
    475       scanlines_sizes_[i] = 0;
    476       databuf_[i] = NULL;
    477       databuf_strides_[i] = 0;
    478     }
    479 
    480     num_outbufs_ = num_outbufs;
    481   }
    482 }
    483 
    484 void MJpegDecoder::DestroyOutputBuffers() {
    485   for (int i = 0; i < num_outbufs_; ++i) {
    486     delete [] scanlines_[i];
    487     delete [] databuf_[i];
    488   }
    489   delete [] scanlines_;
    490   delete [] databuf_;
    491   delete [] scanlines_sizes_;
    492   delete [] databuf_strides_;
    493   scanlines_ = NULL;
    494   databuf_ = NULL;
    495   scanlines_sizes_ = NULL;
    496   databuf_strides_ = NULL;
    497   num_outbufs_ = 0;
    498 }
    499 
    500 // JDCT_IFAST and do_block_smoothing improve performance substantially.
    501 LIBYUV_BOOL MJpegDecoder::StartDecode() {
    502   decompress_struct_->raw_data_out = TRUE;
    503   decompress_struct_->dct_method = JDCT_IFAST;  // JDCT_ISLOW is default
    504   decompress_struct_->dither_mode = JDITHER_NONE;
    505   // Not applicable to 'raw':
    506   decompress_struct_->do_fancy_upsampling = (boolean)(LIBYUV_FALSE);
    507   // Only for buffered mode:
    508   decompress_struct_->enable_2pass_quant = (boolean)(LIBYUV_FALSE);
    509   // Blocky but fast:
    510   decompress_struct_->do_block_smoothing = (boolean)(LIBYUV_FALSE);
    511 
    512   if (!jpeg_start_decompress(decompress_struct_)) {
    513     // ERROR: Couldn't start JPEG decompressor";
    514     return LIBYUV_FALSE;
    515   }
    516   return LIBYUV_TRUE;
    517 }
    518 
    519 LIBYUV_BOOL MJpegDecoder::FinishDecode() {
    520   // jpeglib considers it an error if we finish without decoding the whole
    521   // image, so we call "abort" rather than "finish".
    522   jpeg_abort_decompress(decompress_struct_);
    523   return LIBYUV_TRUE;
    524 }
    525 
    526 void MJpegDecoder::SetScanlinePointers(uint8** data) {
    527   for (int i = 0; i < num_outbufs_; ++i) {
    528     uint8* data_i = data[i];
    529     for (int j = 0; j < scanlines_sizes_[i]; ++j) {
    530       scanlines_[i][j] = data_i;
    531       data_i += GetComponentStride(i);
    532     }
    533   }
    534 }
    535 
    536 inline LIBYUV_BOOL MJpegDecoder::DecodeImcuRow() {
    537   return (unsigned int)(GetImageScanlinesPerImcuRow()) ==
    538       jpeg_read_raw_data(decompress_struct_,
    539                          scanlines_,
    540                          GetImageScanlinesPerImcuRow());
    541 }
    542 
    543 // The helper function which recognizes the jpeg sub-sampling type.
    544 JpegSubsamplingType MJpegDecoder::JpegSubsamplingTypeHelper(
    545     int* subsample_x, int* subsample_y, int number_of_components) {
    546   if (number_of_components == 3) {  // Color images.
    547     if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
    548         subsample_x[1] == 2 && subsample_y[1] == 2 &&
    549         subsample_x[2] == 2 && subsample_y[2] == 2) {
    550       return kJpegYuv420;
    551     } else if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
    552         subsample_x[1] == 2 && subsample_y[1] == 1 &&
    553         subsample_x[2] == 2 && subsample_y[2] == 1) {
    554       return kJpegYuv422;
    555     } else if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
    556         subsample_x[1] == 1 && subsample_y[1] == 1 &&
    557         subsample_x[2] == 1 && subsample_y[2] == 1) {
    558       return kJpegYuv444;
    559     }
    560   } else if (number_of_components == 1) {  // Grey-scale images.
    561     if (subsample_x[0] == 1 && subsample_y[0] == 1) {
    562       return kJpegYuv400;
    563     }
    564   }
    565   return kJpegUnknown;
    566 }
    567 
    568 }  // namespace libyuv
    569 #endif  // HAVE_JPEG
    570 
    571