mjpeg_decoder.cc 18.2 KB
Newer Older
1
/*
2
 *  Copyright 2012 The LibYuv Project Authors. All rights reserved.
3 4 5 6
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
7
 *  in the file PATENTS. All contributing project authors may
8 9 10 11 12
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "libyuv/mjpeg_decoder.h"

13
#ifdef HAVE_JPEG
14
#include <assert.h>
15

16 17
#if !defined(__pnacl__) && !defined(__CLR_VER) && \
    !defined(COVERAGE_ENABLED) && !defined(TARGET_IPHONE_SIMULATOR)
18
// Must be included before jpeglib.
19
#include <setjmp.h>
20
#define HAVE_SETJMP
21 22 23 24 25 26

#if defined(_MSC_VER)
// disable warning 4324: structure was padded due to __declspec(align())
#pragma warning(disable:4324)
#endif

27
#endif
28
struct FILE;  // For jpeglib.h.
29 30 31 32 33 34

// C++ build requires extern C for jpeg internals.
#ifdef __cplusplus
extern "C" {
#endif

35 36
#include <jpeglib.h>

37 38 39 40
#ifdef __cplusplus
}  // extern "C"
#endif

41
#include "libyuv/planar_functions.h"  // For CopyPlane().
42

43 44
namespace libyuv {

45
#ifdef HAVE_SETJMP
46 47 48 49
struct SetJmpErrorMgr {
  jpeg_error_mgr base;  // Must be at the top
  jmp_buf setjmp_buffer;
};
50
#endif
51 52 53 54 55 56 57 58

const int MJpegDecoder::kColorSpaceUnknown = JCS_UNKNOWN;
const int MJpegDecoder::kColorSpaceGrayscale = JCS_GRAYSCALE;
const int MJpegDecoder::kColorSpaceRgb = JCS_RGB;
const int MJpegDecoder::kColorSpaceYCbCr = JCS_YCbCr;
const int MJpegDecoder::kColorSpaceCMYK = JCS_CMYK;
const int MJpegDecoder::kColorSpaceYCCK = JCS_YCCK;

59 60 61
// Methods that are passed to jpeglib.
boolean fill_input_buffer(jpeg_decompress_struct* cinfo);
void init_source(jpeg_decompress_struct* cinfo);
Frank Barchard's avatar
Frank Barchard committed
62
void skip_input_data(jpeg_decompress_struct* cinfo, long num_bytes);  // NOLINT
63 64 65
void term_source(jpeg_decompress_struct* cinfo);
void ErrorHandler(jpeg_common_struct* cinfo);

66
MJpegDecoder::MJpegDecoder()
67
    : has_scanline_padding_(LIBYUV_FALSE),
68 69 70 71 72
      num_outbufs_(0),
      scanlines_(NULL),
      scanlines_sizes_(NULL),
      databuf_(NULL),
      databuf_strides_(NULL) {
73 74
  decompress_struct_ = new jpeg_decompress_struct;
  source_mgr_ = new jpeg_source_mgr;
75
#ifdef HAVE_SETJMP
76
  error_mgr_ = new SetJmpErrorMgr;
77 78 79
  decompress_struct_->err = jpeg_std_error(&error_mgr_->base);
  // Override standard exit()-based error handler.
  error_mgr_->base.error_exit = &ErrorHandler;
80
#endif
81 82 83 84 85 86
  decompress_struct_->client_data = NULL;
  source_mgr_->init_source = &init_source;
  source_mgr_->fill_input_buffer = &fill_input_buffer;
  source_mgr_->skip_input_data = &skip_input_data;
  source_mgr_->resync_to_restart = &jpeg_resync_to_restart;
  source_mgr_->term_source = &term_source;
87 88
  jpeg_create_decompress(decompress_struct_);
  decompress_struct_->src = source_mgr_;
89 90 91 92 93
  buf_vec_.buffers = &buf_;
  buf_vec_.len = 1;
}

MJpegDecoder::~MJpegDecoder() {
94 95 96
  jpeg_destroy_decompress(decompress_struct_);
  delete decompress_struct_;
  delete source_mgr_;
97
#ifdef HAVE_SETJMP
98
  delete error_mgr_;
99
#endif
100 101 102
  DestroyOutputBuffers();
}

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 111
  buf_vec_.pos = 0;
  decompress_struct_->client_data = &buf_vec_;
112
#ifdef HAVE_SETJMP
113 114 115
  if (setjmp(error_mgr_->setjmp_buffer)) {
    // We called jpeg_read_header, it experienced an error, and we called
    // 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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
  }
  AllocOutputBuffers(GetNumComponents());
  for (int i = 0; i < num_outbufs_; ++i) {
    int scanlines_size = GetComponentScanlinesPerImcuRow(i);
    if (scanlines_sizes_[i] != scanlines_size) {
      if (scanlines_[i]) {
        delete scanlines_[i];
      }
      scanlines_[i] = new uint8* [scanlines_size];
      scanlines_sizes_[i] = scanlines_size;
    }

    // We allocate padding for the final scanline to pad it up to DCTSIZE bytes
    // to avoid memory errors, since jpeglib only reads full MCUs blocks. For
    // the preceding scanlines, the padding is not needed/wanted because the
    // following addresses will already be valid (they are the initial bytes of
    // the next scanline) and will be overwritten when jpeglib writes out that
    // next scanline.
    int databuf_stride = GetComponentStride(i);
    int databuf_size = scanlines_size * databuf_stride;
    if (databuf_strides_[i] != databuf_stride) {
      if (databuf_[i]) {
        delete databuf_[i];
      }
      databuf_[i] = new uint8[databuf_size];
      databuf_strides_[i] = databuf_stride;
    }

    if (GetComponentStride(i) != GetComponentWidth(i)) {
151
      has_scanline_padding_ = LIBYUV_TRUE;
152 153
    }
  }
154
  return LIBYUV_TRUE;
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
}

static int DivideAndRoundUp(int numerator, int denominator) {
  return (numerator + denominator - 1) / denominator;
}

static int DivideAndRoundDown(int numerator, int denominator) {
  return numerator / denominator;
}

// Returns width of the last loaded frame.
int MJpegDecoder::GetWidth() {
  return decompress_struct_->image_width;
}

// Returns height of the last loaded frame.
int MJpegDecoder::GetHeight() {
  return decompress_struct_->image_height;
}

// Returns format of the last loaded frame. The return value is one of the
// kColorSpace* constants.
int MJpegDecoder::GetColorSpace() {
  return decompress_struct_->jpeg_color_space;
}

// Number of color components in the color space.
int MJpegDecoder::GetNumComponents() {
  return decompress_struct_->num_components;
}

// Sample factors of the n-th component.
int MJpegDecoder::GetHorizSampFactor(int component) {
  return decompress_struct_->comp_info[component].h_samp_factor;
}

int MJpegDecoder::GetVertSampFactor(int component) {
  return decompress_struct_->comp_info[component].v_samp_factor;
}

int MJpegDecoder::GetHorizSubSampFactor(int component) {
  return decompress_struct_->max_h_samp_factor /
      GetHorizSampFactor(component);
}

int MJpegDecoder::GetVertSubSampFactor(int component) {
  return decompress_struct_->max_v_samp_factor /
      GetVertSampFactor(component);
}

int MJpegDecoder::GetImageScanlinesPerImcuRow() {
  return decompress_struct_->max_v_samp_factor * DCTSIZE;
}

int MJpegDecoder::GetComponentScanlinesPerImcuRow(int component) {
  int vs = GetVertSubSampFactor(component);
  return DivideAndRoundUp(GetImageScanlinesPerImcuRow(), vs);
}

int MJpegDecoder::GetComponentWidth(int component) {
  int hs = GetHorizSubSampFactor(component);
  return DivideAndRoundUp(GetWidth(), hs);
}

int MJpegDecoder::GetComponentHeight(int component) {
  int vs = GetVertSubSampFactor(component);
  return DivideAndRoundUp(GetHeight(), vs);
}

// Get width in bytes padded out to a multiple of DCTSIZE
int MJpegDecoder::GetComponentStride(int component) {
  return (GetComponentWidth(component) + DCTSIZE - 1) & ~(DCTSIZE - 1);
}

int MJpegDecoder::GetComponentSize(int component) {
  return GetComponentWidth(component) * GetComponentHeight(component);
}

233
LIBYUV_BOOL MJpegDecoder::UnloadFrame() {
234
#ifdef HAVE_SETJMP
235 236 237
  if (setjmp(error_mgr_->setjmp_buffer)) {
    // We called jpeg_abort_decompress, it experienced an error, and we called
    // 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 248 249 250
    uint8** planes, int dst_width, int dst_height) {
  if (dst_width != GetWidth() ||
      dst_height > GetHeight()) {
    // ERROR: Bad dimensions
251
    return LIBYUV_FALSE;
252
  }
253
#ifdef HAVE_SETJMP
254 255 256 257
  if (setjmp(error_mgr_->setjmp_buffer)) {
    // We called into jpeglib, it experienced an error sometime during this
    // function call, and we called longjmp() and rewound the stack to here.
    // Return error.
258
    return LIBYUV_FALSE;
259
  }
260
#endif
261
  if (!StartDecode()) {
262
    return LIBYUV_FALSE;
263 264 265 266 267 268 269 270 271 272 273 274 275
  }
  SetScanlinePointers(databuf_);
  int lines_left = dst_height;
  // Compute amount of lines to skip to implement vertical crop.
  // TODO(fbarchard): Ensure skip is a multiple of maximum component
  // subsample. ie 2
  int skip = (GetHeight() - dst_height) / 2;
  if (skip > 0) {
    // There is no API to skip lines in the output data, so we read them
    // into the temp buffer.
    while (skip >= GetImageScanlinesPerImcuRow()) {
      if (!DecodeImcuRow()) {
        FinishDecode();
276
        return LIBYUV_FALSE;
277 278 279 280 281 282 283 284
      }
      skip -= GetImageScanlinesPerImcuRow();
    }
    if (skip > 0) {
      // Have a partial iMCU row left over to skip. Must read it and then
      // copy the parts we want into the destination.
      if (!DecodeImcuRow()) {
        FinishDecode();
285
        return LIBYUV_FALSE;
286 287 288 289
      }
      for (int i = 0; i < num_outbufs_; ++i) {
        // TODO(fbarchard): Compute skip to avoid this
        assert(skip % GetVertSubSampFactor(i) == 0);
290
        int rows_to_skip =
291 292
            DivideAndRoundDown(skip, GetVertSubSampFactor(i));
        int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i) -
293 294
                                rows_to_skip;
        int data_to_skip = rows_to_skip * GetComponentStride(i);
fbarchard@google.com's avatar
fbarchard@google.com committed
295 296 297
        CopyPlane(databuf_[i] + data_to_skip, GetComponentStride(i),
                  planes[i], GetComponentWidth(i),
                  GetComponentWidth(i), scanlines_to_copy);
298 299 300 301 302 303 304 305 306 307 308
        planes[i] += scanlines_to_copy * GetComponentWidth(i);
      }
      lines_left -= (GetImageScanlinesPerImcuRow() - skip);
    }
  }

  // Read full MCUs but cropped horizontally
  for (; lines_left > GetImageScanlinesPerImcuRow();
         lines_left -= GetImageScanlinesPerImcuRow()) {
    if (!DecodeImcuRow()) {
      FinishDecode();
309
      return LIBYUV_FALSE;
310 311 312
    }
    for (int i = 0; i < num_outbufs_; ++i) {
      int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i);
fbarchard@google.com's avatar
fbarchard@google.com committed
313 314 315
      CopyPlane(databuf_[i], GetComponentStride(i),
                planes[i], GetComponentWidth(i),
                GetComponentWidth(i), scanlines_to_copy);
316 317 318 319 320 321 322 323
      planes[i] += scanlines_to_copy * GetComponentWidth(i);
    }
  }

  if (lines_left > 0) {
    // Have a partial iMCU row left over to decode.
    if (!DecodeImcuRow()) {
      FinishDecode();
324
      return LIBYUV_FALSE;
325 326 327 328
    }
    for (int i = 0; i < num_outbufs_; ++i) {
      int scanlines_to_copy =
          DivideAndRoundUp(lines_left, GetVertSubSampFactor(i));
fbarchard@google.com's avatar
fbarchard@google.com committed
329 330 331
      CopyPlane(databuf_[i], GetComponentStride(i),
                planes[i], GetComponentWidth(i),
                GetComponentWidth(i), scanlines_to_copy);
332 333 334 335 336 337
      planes[i] += scanlines_to_copy * GetComponentWidth(i);
    }
  }
  return FinishDecode();
}

338
LIBYUV_BOOL MJpegDecoder::DecodeToCallback(CallbackFunction fn, void* opaque,
339 340 341 342
    int dst_width, int dst_height) {
  if (dst_width != GetWidth() ||
      dst_height > GetHeight()) {
    // ERROR: Bad dimensions
343
    return LIBYUV_FALSE;
344
  }
345
#ifdef HAVE_SETJMP
346 347 348 349
  if (setjmp(error_mgr_->setjmp_buffer)) {
    // We called into jpeglib, it experienced an error sometime during this
    // function call, and we called longjmp() and rewound the stack to here.
    // Return error.
350
    return LIBYUV_FALSE;
351
  }
352
#endif
353
  if (!StartDecode()) {
354
    return LIBYUV_FALSE;
355 356 357 358 359 360 361 362 363
  }
  SetScanlinePointers(databuf_);
  int lines_left = dst_height;
  // TODO(fbarchard): Compute amount of lines to skip to implement vertical crop
  int skip = (GetHeight() - dst_height) / 2;
  if (skip > 0) {
    while (skip >= GetImageScanlinesPerImcuRow()) {
      if (!DecodeImcuRow()) {
        FinishDecode();
364
        return LIBYUV_FALSE;
365 366 367 368 369 370 371
      }
      skip -= GetImageScanlinesPerImcuRow();
    }
    if (skip > 0) {
      // Have a partial iMCU row left over to skip.
      if (!DecodeImcuRow()) {
        FinishDecode();
372
        return LIBYUV_FALSE;
373 374 375 376
      }
      for (int i = 0; i < num_outbufs_; ++i) {
        // TODO(fbarchard): Compute skip to avoid this
        assert(skip % GetVertSubSampFactor(i) == 0);
377 378
        int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
        int data_to_skip = rows_to_skip * GetComponentStride(i);
379 380 381 382 383 384 385 386
        // Change our own data buffer pointers so we can pass them to the
        // callback.
        databuf_[i] += data_to_skip;
      }
      int scanlines_to_copy = GetImageScanlinesPerImcuRow() - skip;
      (*fn)(opaque, databuf_, databuf_strides_, scanlines_to_copy);
      // Now change them back.
      for (int i = 0; i < num_outbufs_; ++i) {
387 388
        int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i));
        int data_to_skip = rows_to_skip * GetComponentStride(i);
389 390 391 392 393 394 395 396 397 398
        databuf_[i] -= data_to_skip;
      }
      lines_left -= scanlines_to_copy;
    }
  }
  // Read full MCUs until we get to the crop point.
  for (; lines_left >= GetImageScanlinesPerImcuRow();
         lines_left -= GetImageScanlinesPerImcuRow()) {
    if (!DecodeImcuRow()) {
      FinishDecode();
399
      return LIBYUV_FALSE;
400 401 402 403 404 405 406
    }
    (*fn)(opaque, databuf_, databuf_strides_, GetImageScanlinesPerImcuRow());
  }
  if (lines_left > 0) {
    // Have a partial iMCU row left over to decode.
    if (!DecodeImcuRow()) {
      FinishDecode();
407
      return LIBYUV_FALSE;
408 409 410 411 412 413
    }
    (*fn)(opaque, databuf_, databuf_strides_, lines_left);
  }
  return FinishDecode();
}

414
void init_source(j_decompress_ptr cinfo) {
415 416 417
  fill_input_buffer(cinfo);
}

418
boolean fill_input_buffer(j_decompress_ptr cinfo) {
419
  BufferVector* buf_vec = reinterpret_cast<BufferVector*>(cinfo->client_data);
420 421 422 423 424 425 426 427 428 429 430
  if (buf_vec->pos >= buf_vec->len) {
    assert(0 && "No more data");
    // ERROR: No more data
    return FALSE;
  }
  cinfo->src->next_input_byte = buf_vec->buffers[buf_vec->pos].data;
  cinfo->src->bytes_in_buffer = buf_vec->buffers[buf_vec->pos].len;
  ++buf_vec->pos;
  return TRUE;
}

Frank Barchard's avatar
Frank Barchard committed
431
void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {  // NOLINT
432 433 434
  cinfo->src->next_input_byte += num_bytes;
}

435
void term_source(j_decompress_ptr cinfo) {
436 437 438
  // Nothing to do.
}

439
#ifdef HAVE_SETJMP
440
void ErrorHandler(j_common_ptr cinfo) {
441 442 443 444 445 446
  // This is called when a jpeglib command experiences an error. Unfortunately
  // jpeglib's error handling model is not very flexible, because it expects the
  // error handler to not return--i.e., it wants the program to terminate. To
  // recover from errors we use setjmp() as shown in their example. setjmp() is
  // C's implementation for the "call with current continuation" functionality
  // seen in some functional programming languages.
447 448
  // A formatted message can be output, but is unsafe for release.
#ifdef DEBUG
449 450 451
  char buf[JMSG_LENGTH_MAX];
  (*cinfo->err->format_message)(cinfo, buf);
  // ERROR: Error in jpeglib: buf
452
#endif
453

454
  SetJmpErrorMgr* mgr = reinterpret_cast<SetJmpErrorMgr*>(cinfo->err);
455 456 457 458
  // This rewinds the call stack to the point of the corresponding setjmp()
  // and causes it to return (for a second time) with value 1.
  longjmp(mgr->setjmp_buffer, 1);
}
459
#endif
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500

void MJpegDecoder::AllocOutputBuffers(int num_outbufs) {
  if (num_outbufs != num_outbufs_) {
    // We could perhaps optimize this case to resize the output buffers without
    // necessarily having to delete and recreate each one, but it's not worth
    // it.
    DestroyOutputBuffers();

    scanlines_ = new uint8** [num_outbufs];
    scanlines_sizes_ = new int[num_outbufs];
    databuf_ = new uint8* [num_outbufs];
    databuf_strides_ = new int[num_outbufs];

    for (int i = 0; i < num_outbufs; ++i) {
      scanlines_[i] = NULL;
      scanlines_sizes_[i] = 0;
      databuf_[i] = NULL;
      databuf_strides_[i] = 0;
    }

    num_outbufs_ = num_outbufs;
  }
}

void MJpegDecoder::DestroyOutputBuffers() {
  for (int i = 0; i < num_outbufs_; ++i) {
    delete [] scanlines_[i];
    delete [] databuf_[i];
  }
  delete [] scanlines_;
  delete [] databuf_;
  delete [] scanlines_sizes_;
  delete [] databuf_strides_;
  scanlines_ = NULL;
  databuf_ = NULL;
  scanlines_sizes_ = NULL;
  databuf_strides_ = NULL;
  num_outbufs_ = 0;
}

// JDCT_IFAST and do_block_smoothing improve performance substantially.
501
LIBYUV_BOOL MJpegDecoder::StartDecode() {
502 503 504
  decompress_struct_->raw_data_out = TRUE;
  decompress_struct_->dct_method = JDCT_IFAST;  // JDCT_ISLOW is default
  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 521
  // jpeglib considers it an error if we finish without decoding the whole
  // image, so we call "abort" rather than "finish".
522
  jpeg_abort_decompress(decompress_struct_);
523
  return LIBYUV_TRUE;
524 525 526 527 528 529 530 531 532 533 534 535
}

void MJpegDecoder::SetScanlinePointers(uint8** data) {
  for (int i = 0; i < num_outbufs_; ++i) {
    uint8* data_i = data[i];
    for (int j = 0; j < scanlines_sizes_[i]; ++j) {
      scanlines_[i][j] = data_i;
      data_i += GetComponentStride(i);
    }
  }
}

536
inline LIBYUV_BOOL MJpegDecoder::DecodeImcuRow() {
537
  return (unsigned int)(GetImageScanlinesPerImcuRow()) ==
538
      jpeg_read_raw_data(decompress_struct_,
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
                         scanlines_,
                         GetImageScanlinesPerImcuRow());
}

// The helper function which recognizes the jpeg sub-sampling type.
JpegSubsamplingType MJpegDecoder::JpegSubsamplingTypeHelper(
    int* subsample_x, int* subsample_y, int number_of_components) {
  if (number_of_components == 3) {  // Color images.
    if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
        subsample_x[1] == 2 && subsample_y[1] == 2 &&
        subsample_x[2] == 2 && subsample_y[2] == 2) {
      return kJpegYuv420;
    } else if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
        subsample_x[1] == 2 && subsample_y[1] == 1 &&
        subsample_x[2] == 2 && subsample_y[2] == 1) {
      return kJpegYuv422;
    } else if (subsample_x[0] == 1 && subsample_y[0] == 1 &&
        subsample_x[1] == 1 && subsample_y[1] == 1 &&
        subsample_x[2] == 1 && subsample_y[2] == 1) {
      return kJpegYuv444;
    }
  } else if (number_of_components == 1) {  // Grey-scale images.
    if (subsample_x[0] == 1 && subsample_y[0] == 1) {
      return kJpegYuv400;
    }
  }
  return kJpegUnknown;
}

}  // namespace libyuv
569 570
#endif  // HAVE_JPEG