| /* |
| * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. |
| * |
| * 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 |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| /* |
| * jpeg.cc |
| */ |
| |
| |
| #if defined(WIN32) |
| #include <basetsd.h> |
| #endif |
| #include <setjmp.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "jpeg.h" |
| #include "data_manager.h" |
| extern "C" { |
| #if defined(USE_SYSTEM_LIBJPEG) |
| #include <jpeglib.h> |
| #else |
| #include "jpeglib.h" |
| #endif |
| } |
| |
| |
| namespace webrtc |
| { |
| |
| // Error handler |
| struct myErrorMgr { |
| |
| struct jpeg_error_mgr pub; |
| jmp_buf setjmp_buffer; |
| }; |
| typedef struct myErrorMgr * myErrorPtr; |
| |
| METHODDEF(void) |
| MyErrorExit (j_common_ptr cinfo) |
| { |
| myErrorPtr myerr = (myErrorPtr) cinfo->err; |
| |
| // Return control to the setjmp point |
| longjmp(myerr->setjmp_buffer, 1); |
| } |
| |
| JpegEncoder::JpegEncoder() |
| { |
| _cinfo = new jpeg_compress_struct; |
| strcpy(_fileName, "Snapshot.jpg"); |
| } |
| |
| JpegEncoder::~JpegEncoder() |
| { |
| if (_cinfo != NULL) |
| { |
| delete _cinfo; |
| _cinfo = NULL; |
| } |
| } |
| |
| |
| WebRtc_Word32 |
| JpegEncoder::SetFileName(const WebRtc_Word8* fileName) |
| { |
| if (!fileName) |
| { |
| return -1; |
| } |
| |
| if (fileName) |
| { |
| strncpy(_fileName, fileName, 256); |
| } |
| return 0; |
| } |
| |
| |
| WebRtc_Word32 |
| JpegEncoder::Encode(const RawImage& inputImage) |
| { |
| if (inputImage._buffer == NULL || inputImage._size == 0) |
| { |
| return -1; |
| } |
| if (inputImage._width < 1 || inputImage._height < 1) |
| { |
| return -1; |
| } |
| |
| FILE* outFile = NULL; |
| |
| const WebRtc_UWord32 width = inputImage._width; |
| const WebRtc_UWord32 height = inputImage._height; |
| |
| // Set error handler |
| myErrorMgr jerr; |
| _cinfo->err = jpeg_std_error(&jerr.pub); |
| jerr.pub.error_exit = MyErrorExit; |
| // Establish the setjmp return context |
| if (setjmp(jerr.setjmp_buffer)) |
| { |
| // If we get here, the JPEG code has signaled an error. |
| jpeg_destroy_compress(_cinfo); |
| if (outFile != NULL) |
| { |
| fclose(outFile); |
| } |
| return -1; |
| } |
| |
| if ((outFile = fopen(_fileName, "wb")) == NULL) |
| { |
| return -2; |
| } |
| // Create a compression object |
| jpeg_create_compress(_cinfo); |
| |
| // Setting destination file |
| jpeg_stdio_dest(_cinfo, outFile); |
| |
| // Set parameters for compression |
| _cinfo->in_color_space = JCS_YCbCr; |
| jpeg_set_defaults(_cinfo); |
| |
| _cinfo->image_width = width; |
| _cinfo->image_height = height; |
| _cinfo->input_components = 3; |
| |
| _cinfo->comp_info[0].h_samp_factor = 2; // Y |
| _cinfo->comp_info[0].v_samp_factor = 2; |
| _cinfo->comp_info[1].h_samp_factor = 1; // U |
| _cinfo->comp_info[1].v_samp_factor = 1; |
| _cinfo->comp_info[2].h_samp_factor = 1; // V |
| _cinfo->comp_info[2].v_samp_factor = 1; |
| _cinfo->raw_data_in = TRUE; |
| |
| WebRtc_UWord32 height16 = (height + 15) & ~15; |
| WebRtc_UWord8* imgPtr = inputImage._buffer; |
| WebRtc_UWord8* origImagePtr = NULL; |
| if (height16 != height) |
| { |
| // Copy image to an adequate size buffer |
| WebRtc_UWord32 requiredSize = height16 * width * 3 >> 1; |
| origImagePtr = new WebRtc_UWord8[requiredSize]; |
| memset(origImagePtr, 0, requiredSize); |
| memcpy(origImagePtr, inputImage._buffer, inputImage._length); |
| imgPtr = origImagePtr; |
| } |
| |
| jpeg_start_compress(_cinfo, TRUE); |
| |
| JSAMPROW y[16],u[8],v[8]; |
| JSAMPARRAY data[3]; |
| |
| data[0] = y; |
| data[1] = u; |
| data[2] = v; |
| |
| WebRtc_UWord32 i, j; |
| |
| for (j = 0; j < height; j += 16) |
| { |
| for (i = 0; i < 16; i++) |
| { |
| y[i] = (JSAMPLE*)imgPtr + width * (i + j); |
| |
| if (i % 2 == 0) |
| { |
| u[i / 2] = (JSAMPLE*) imgPtr + width * height + |
| width / 2 * ((i + j) / 2); |
| v[i / 2] = (JSAMPLE*) imgPtr + width * height + |
| width * height / 4 + width / 2 * ((i + j) / 2); |
| } |
| } |
| jpeg_write_raw_data(_cinfo, data, 16); |
| } |
| |
| jpeg_finish_compress(_cinfo); |
| jpeg_destroy_compress(_cinfo); |
| |
| fclose(outFile); |
| |
| if (origImagePtr != NULL) |
| { |
| delete [] origImagePtr; |
| } |
| |
| return 0; |
| } |
| |
| JpegDecoder::JpegDecoder() |
| { |
| _cinfo = new jpeg_decompress_struct; |
| } |
| |
| JpegDecoder::~JpegDecoder() |
| { |
| if (_cinfo != NULL) |
| { |
| delete _cinfo; |
| _cinfo = NULL; |
| } |
| } |
| |
| WebRtc_Word32 |
| JpegDecoder::Decode(const EncodedImage& inputImage, |
| RawImage& outputImage) |
| { |
| |
| WebRtc_UWord8* tmpBuffer = NULL; |
| // Set error handler |
| myErrorMgr jerr; |
| _cinfo->err = jpeg_std_error(&jerr.pub); |
| jerr.pub.error_exit = MyErrorExit; |
| |
| // Establish the setjmp return context |
| if (setjmp(jerr.setjmp_buffer)) |
| { |
| if (_cinfo->is_decompressor) |
| { |
| jpeg_destroy_decompress(_cinfo); |
| } |
| if (tmpBuffer != NULL) |
| { |
| delete [] tmpBuffer; |
| } |
| return -1; |
| } |
| |
| _cinfo->out_color_space = JCS_YCbCr; |
| |
| // Create decompression object |
| jpeg_create_decompress(_cinfo); |
| |
| // Specify data source |
| jpegSetSrcBuffer(_cinfo, (JOCTET*) inputImage._buffer, inputImage._size); |
| |
| // Read header data |
| jpeg_read_header(_cinfo, TRUE); |
| |
| _cinfo->raw_data_out = TRUE; |
| jpeg_start_decompress(_cinfo); |
| |
| // Check header |
| if (_cinfo->num_components == 4) |
| { |
| return -2; // not supported |
| } |
| if (_cinfo->progressive_mode == 1) |
| { |
| return -2; // not supported |
| } |
| |
| |
| WebRtc_UWord32 height = _cinfo->image_height; |
| WebRtc_UWord32 width = _cinfo->image_width; |
| |
| // Making sure width and height are even |
| if (height % 2) |
| { |
| height++; |
| } |
| if (width % 2) |
| { |
| width++; |
| } |
| |
| WebRtc_UWord32 height16 = (height + 15) & ~15; |
| WebRtc_UWord32 stride = (width + 15) & ~15; |
| WebRtc_UWord32 uvStride = ((((stride + 1) >> 1) + 15) & ~15); |
| |
| WebRtc_UWord32 tmpRequiredSize = stride * height16 + |
| 2 * (uvStride * ((height16 + 1) >> 1)); |
| WebRtc_UWord32 requiredSize = width * height * 3 >> 1; |
| |
| // verify sufficient buffer size |
| if (outputImage._buffer && outputImage._size < requiredSize) |
| { |
| delete [] outputImage._buffer; |
| outputImage._buffer = NULL; |
| } |
| |
| if (outputImage._buffer == NULL) |
| { |
| outputImage._buffer = new WebRtc_UWord8[requiredSize]; |
| outputImage._size = requiredSize; |
| } |
| |
| WebRtc_UWord8* outPtr = outputImage._buffer; |
| |
| if (tmpRequiredSize > requiredSize) |
| { |
| tmpBuffer = new WebRtc_UWord8[(int) (tmpRequiredSize)]; |
| outPtr = tmpBuffer; |
| } |
| |
| JSAMPROW y[16],u[8],v[8]; |
| JSAMPARRAY data[3]; |
| data[0] = y; |
| data[1] = u; |
| data[2] = v; |
| |
| WebRtc_UWord32 hInd, i; |
| WebRtc_UWord32 numScanLines = 16; |
| WebRtc_UWord32 numLinesProcessed = 0; |
| |
| while (_cinfo->output_scanline < _cinfo->output_height) |
| { |
| hInd = _cinfo->output_scanline; |
| for (i = 0; i < numScanLines; i++) |
| { |
| y[i] = outPtr + stride * (i + hInd); |
| |
| if (i % 2 == 0) |
| { |
| u[i / 2] = outPtr + stride * height16 + |
| stride / 2 * ((i + hInd) / 2); |
| v[i / 2] = outPtr + stride * height16 + |
| stride * height16 / 4 + |
| stride / 2 * ((i + hInd) / 2); |
| } |
| } |
| // Processes exactly one iMCU row per call |
| numLinesProcessed = jpeg_read_raw_data(_cinfo, data, numScanLines); |
| // Error in read |
| if (numLinesProcessed == 0) |
| { |
| jpeg_abort((j_common_ptr)_cinfo); |
| return -1; |
| } |
| } |
| |
| if (tmpRequiredSize > requiredSize) |
| { |
| WebRtc_UWord8* dstFramePtr = outputImage._buffer; |
| WebRtc_UWord8* tmpPtr = outPtr; |
| |
| for (WebRtc_UWord32 p = 0; p < 3; p++) |
| { |
| const WebRtc_UWord32 h = (p == 0) ? height : height >> 1; |
| const WebRtc_UWord32 h16 = (p == 0) ? height16 : height16 >> 1; |
| const WebRtc_UWord32 w = (p == 0) ? width : width >> 1; |
| const WebRtc_UWord32 s = (p == 0) ? stride : stride >> 1; |
| |
| for (WebRtc_UWord32 i = 0; i < h; i++) |
| { |
| memcpy(dstFramePtr, tmpPtr, w); |
| dstFramePtr += w; |
| tmpPtr += s; |
| } |
| tmpPtr += (h16 - h) * s; |
| } |
| } |
| |
| if (tmpBuffer != NULL) |
| { |
| delete [] tmpBuffer; |
| } |
| // Setting output Image parameter |
| outputImage._width = width; |
| outputImage._height = height; |
| outputImage._length = requiredSize; |
| outputImage._timeStamp = inputImage._timeStamp; |
| |
| jpeg_finish_decompress(_cinfo); |
| jpeg_destroy_decompress(_cinfo); |
| return 0; |
| } |
| |
| |
| } |