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