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