346 lines
7.9 KiB
C++
346 lines
7.9 KiB
C++
/*
|
|
* 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
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "vplib.h"
|
|
#include "jpeg.h"
|
|
#include "data_manager.h"
|
|
#if defined(WIN32)
|
|
#include <basetsd.h>
|
|
#endif
|
|
#ifdef WEBRTC_ANDROID
|
|
extern "C" {
|
|
#endif
|
|
#include "jpeglib.h"
|
|
#ifdef WEBRTC_ANDROID
|
|
}
|
|
#endif
|
|
#include <setjmp.h>
|
|
|
|
|
|
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():
|
|
_width(0),
|
|
_height(0)
|
|
{
|
|
_cinfo = new jpeg_compress_struct;
|
|
strcpy(_fileName, "Snapshot.jpg");
|
|
}
|
|
|
|
JpegEncoder::~JpegEncoder()
|
|
{
|
|
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 WebRtc_UWord8* imageBuffer,
|
|
const WebRtc_UWord32 imageBufferSize,
|
|
const WebRtc_UWord32 width,
|
|
const WebRtc_UWord32 height)
|
|
{
|
|
if ((imageBuffer == NULL) || (imageBufferSize == 0))
|
|
{
|
|
return -1;
|
|
}
|
|
if (width < 1 || height < 1)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
FILE* outFile = NULL;
|
|
|
|
_width = width;
|
|
_height = 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)
|
|
{
|
|
fprintf(stderr, "can't open %s\n", _fileName);
|
|
return -2;
|
|
}
|
|
// Create a compression object
|
|
jpeg_create_compress(_cinfo);
|
|
|
|
// Setting destination file
|
|
jpeg_stdio_dest(_cinfo, outFile);
|
|
|
|
WebRtc_Word32 ret = 0;
|
|
|
|
// Height of image buffer should be a multiple of 16
|
|
if (_height % 16 == 0)
|
|
{
|
|
ret = Encode(imageBuffer, imageBufferSize);
|
|
}
|
|
else
|
|
{
|
|
WebRtc_UWord32 height16 = ((_height + 15) - 16) & - 16;
|
|
height16 = (height16 < _height) ? height16 + 16 : height16;
|
|
|
|
// Copy image to an adequate size buffer
|
|
WebRtc_UWord32 requiredSize = height16 * _width * 3 >> 1;
|
|
WebRtc_UWord8* origImagePtr = new WebRtc_UWord8[requiredSize];
|
|
if (origImagePtr == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
memset(origImagePtr, 0, requiredSize);
|
|
memcpy(origImagePtr, imageBuffer, imageBufferSize);
|
|
|
|
ret = Encode(origImagePtr, requiredSize);
|
|
|
|
// delete allocated buffer
|
|
delete [] origImagePtr;
|
|
origImagePtr = NULL;
|
|
}
|
|
|
|
|
|
fclose(outFile);
|
|
|
|
return ret;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
JpegEncoder::Encode(const WebRtc_UWord8* imageBuffer,
|
|
const WebRtc_UWord32 imageBufferSize)
|
|
{
|
|
// 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;
|
|
|
|
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*) imageBuffer + _width * (i + j);
|
|
|
|
if (i % 2 == 0)
|
|
{
|
|
u[i / 2] = (JSAMPLE*) imageBuffer + _width * _height +
|
|
_width / 2 * ((i + j) / 2);
|
|
v[i / 2] = (JSAMPLE*) imageBuffer + _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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
JpegDecoder::JpegDecoder()
|
|
{
|
|
_cinfo = new jpeg_decompress_struct;
|
|
}
|
|
|
|
JpegDecoder::~JpegDecoder()
|
|
{
|
|
delete _cinfo;
|
|
_cinfo = NULL;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
JpegDecoder::Decode(const WebRtc_UWord8* encodedBuffer,
|
|
const WebRtc_UWord32 encodedBufferSize,
|
|
WebRtc_UWord8*& decodedBuffer,
|
|
WebRtc_UWord32& width,
|
|
WebRtc_UWord32& 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 (_cinfo->is_decompressor)
|
|
{
|
|
jpeg_destroy_decompress(_cinfo);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
_cinfo->out_color_space = JCS_YCbCr;
|
|
|
|
// Create decompression object
|
|
jpeg_create_decompress(_cinfo);
|
|
|
|
// Specify data source
|
|
jpegSetSrcBuffer(_cinfo, (JOCTET*) encodedBuffer, encodedBufferSize);
|
|
|
|
// 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
|
|
}
|
|
|
|
height = _cinfo->image_height;
|
|
width = _cinfo->image_width;
|
|
|
|
// Making sure width and height are even
|
|
if (height % 2)
|
|
height++;
|
|
if (width % 2)
|
|
width++;
|
|
|
|
WebRtc_UWord32 height16 = ((height + 15) - 16) & - 16;
|
|
height16 = (height16 < height) ? height16 + 16 : height16;
|
|
|
|
// allocate buffer to output
|
|
if (decodedBuffer != NULL)
|
|
{
|
|
delete [] decodedBuffer;
|
|
decodedBuffer = NULL;
|
|
}
|
|
decodedBuffer = new WebRtc_UWord8[width * height16 * 3 >> 1];
|
|
if (decodedBuffer == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
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] = decodedBuffer + width * (i + hInd);
|
|
|
|
if (i % 2 == 0)
|
|
{
|
|
u[i / 2] = decodedBuffer + width * height +
|
|
width / 2 * ((i + hInd) / 2);
|
|
v[i / 2] = decodedBuffer + width * height +
|
|
width * height / 4 + width / 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)
|
|
{
|
|
delete [] decodedBuffer;
|
|
jpeg_abort((j_common_ptr)_cinfo);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
jpeg_finish_decompress(_cinfo);
|
|
jpeg_destroy_decompress(_cinfo);
|
|
return 0;
|
|
}
|
|
|
|
|
|
}
|