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 23extern "C" { 24#include <jpeglib.h> 25} 26 27#include <climits> 28#include <cstring> 29 30namespace libyuv { 31 32#ifdef HAVE_SETJMP 33struct SetJmpErrorMgr { 34 jpeg_error_mgr base; // Must be at the top 35 jmp_buf setjmp_buffer; 36}; 37#endif 38 39const int MJpegDecoder::kColorSpaceUnknown = JCS_UNKNOWN; 40const int MJpegDecoder::kColorSpaceGrayscale = JCS_GRAYSCALE; 41const int MJpegDecoder::kColorSpaceRgb = JCS_RGB; 42const int MJpegDecoder::kColorSpaceYCbCr = JCS_YCbCr; 43const int MJpegDecoder::kColorSpaceCMYK = JCS_CMYK; 44const int MJpegDecoder::kColorSpaceYCCK = JCS_YCCK; 45 46MJpegDecoder::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 73MJpegDecoder::~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? 85bool 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 115bool 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 169static int DivideAndRoundUp(int numerator, int denominator) { 170 return (numerator + denominator - 1) / denominator; 171} 172 173static int DivideAndRoundDown(int numerator, int denominator) { 174 return numerator / denominator; 175} 176 177// Returns width of the last loaded frame. 178int MJpegDecoder::GetWidth() { 179 return decompress_struct_->image_width; 180} 181 182// Returns height of the last loaded frame. 183int 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. 189int MJpegDecoder::GetColorSpace() { 190 return decompress_struct_->jpeg_color_space; 191} 192 193// Number of color components in the color space. 194int MJpegDecoder::GetNumComponents() { 195 return decompress_struct_->num_components; 196} 197 198// Sample factors of the n-th component. 199int MJpegDecoder::GetHorizSampFactor(int component) { 200 return decompress_struct_->comp_info[component].h_samp_factor; 201} 202 203int MJpegDecoder::GetVertSampFactor(int component) { 204 return decompress_struct_->comp_info[component].v_samp_factor; 205} 206 207int MJpegDecoder::GetHorizSubSampFactor(int component) { 208 return decompress_struct_->max_h_samp_factor / 209 GetHorizSampFactor(component); 210} 211 212int MJpegDecoder::GetVertSubSampFactor(int component) { 213 return decompress_struct_->max_v_samp_factor / 214 GetVertSampFactor(component); 215} 216 217int MJpegDecoder::GetImageScanlinesPerImcuRow() { 218 return decompress_struct_->max_v_samp_factor * DCTSIZE; 219} 220 221int MJpegDecoder::GetComponentScanlinesPerImcuRow(int component) { 222 int vs = GetVertSubSampFactor(component); 223 return DivideAndRoundUp(GetImageScanlinesPerImcuRow(), vs); 224} 225 226int MJpegDecoder::GetComponentWidth(int component) { 227 int hs = GetHorizSubSampFactor(component); 228 return DivideAndRoundUp(GetWidth(), hs); 229} 230 231int 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 237int MJpegDecoder::GetComponentStride(int component) { 238 return (GetComponentWidth(component) + DCTSIZE - 1) & ~(DCTSIZE - 1); 239} 240 241int MJpegDecoder::GetComponentSize(int component) { 242 return GetComponentWidth(component) * GetComponentHeight(component); 243} 244 245bool 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 257static 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. 267bool 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 356bool 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 432void MJpegDecoder::init_source(j_decompress_ptr cinfo) { 433 fill_input_buffer(cinfo); 434} 435 436boolean 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 449void MJpegDecoder::skip_input_data(j_decompress_ptr cinfo, 450 long num_bytes) { // NOLINT 451 cinfo->src->next_input_byte += num_bytes; 452} 453 454void MJpegDecoder::term_source(j_decompress_ptr cinfo) { 455 // Nothing to do. 456} 457 458#ifdef HAVE_SETJMP 459void 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 477void 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 500void 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. 517bool 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 532bool 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 539void 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 549inline 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. 557JpegSubsamplingType 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