Updating JPEG Decoder to Use LibYuv

Review URL: https://webrtc-codereview.appspot.com/900004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@2947 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
mikhal@webrtc.org 2012-10-18 16:42:00 +00:00
parent 3aece4293a
commit 4eb3f13304
4 changed files with 38 additions and 188 deletions

View File

@ -17,11 +17,12 @@
// jpeg forward declaration // jpeg forward declaration
struct jpeg_compress_struct; struct jpeg_compress_struct;
struct jpeg_decompress_struct;
namespace webrtc namespace webrtc
{ {
// TODO(mikhal): Move this to LibYuv wrappar, when LibYuv will have a JPG
// Encode.
class JpegEncoder class JpegEncoder
{ {
public: public:
@ -53,29 +54,19 @@ private:
char _fileName[257]; char _fileName[257];
}; };
class JpegDecoder
{
public:
JpegDecoder();
~JpegDecoder();
// Decodes a JPEG-stream // Decodes a JPEG-stream
// Supports 1 image component. 3 interleaved image components, // Supports 1 image component. 3 interleaved image components,
// YCbCr sub-sampling 4:4:4, 4:2:2, 4:2:0. // YCbCr sub-sampling 4:4:4, 4:2:2, 4:2:0.
// //
// Input: // Input:
// - inputImage : encoded image to be decoded. // - input_image : encoded image to be decoded.
// - outputImage : VideoFrame to store decoded output. // - output_image : VideoFrame to store decoded output.
// //
// Output: // Output:
// - 0 : OK // - 0 : OK
// - (-1) : Error // - (-1) : Error
WebRtc_Word32 Decode(const EncodedImage& inputImage, // - (-2) : Unsupported format
VideoFrame& outputImage); int ConvertJpegToI420(const EncodedImage& input_image,
private: VideoFrame* output_image);
jpeg_decompress_struct* _cinfo;
};
} }
#endif /* WEBRTC_COMMON_VIDEO_JPEG */ #endif /* WEBRTC_COMMON_VIDEO_JPEG */

View File

@ -18,6 +18,8 @@
#include "common_video/jpeg/include/jpeg.h" #include "common_video/jpeg/include/jpeg.h"
#include "common_video/jpeg/data_manager.h" #include "common_video/jpeg/data_manager.h"
#include "common_video/libyuv/include/webrtc_libyuv.h" #include "common_video/libyuv/include/webrtc_libyuv.h"
#include "libyuv.h"
#include "libyuv/mjpeg_decoder.h"
extern "C" { extern "C" {
#if defined(USE_SYSTEM_LIBJPEG) #if defined(USE_SYSTEM_LIBJPEG)
@ -194,172 +196,33 @@ JpegEncoder::Encode(const VideoFrame& inputImage)
return 0; return 0;
} }
JpegDecoder::JpegDecoder() int ConvertJpegToI420(const EncodedImage& input_image,
{ VideoFrame* output_image) {
_cinfo = new jpeg_decompress_struct;
}
JpegDecoder::~JpegDecoder() if (output_image == NULL)
{ return -1;
if (_cinfo != NULL) // TODO(mikhal): Update to use latest API from LibYuv when that becomes
{ // available.
delete _cinfo; libyuv::MJpegDecoder jpeg_decoder;
_cinfo = NULL; bool ret = jpeg_decoder.LoadFrame(input_image._buffer, input_image._size);
} if (ret == false)
} return -1;
if (jpeg_decoder.GetNumComponents() == 4)
WebRtc_Word32 return -2; // not supported.
JpegDecoder::Decode(const EncodedImage& inputImage, int width = jpeg_decoder.GetWidth();
VideoFrame& outputImage) int height = jpeg_decoder.GetHeight();
{ int req_size = CalcBufferSize(kI420, width, height);
output_image->VerifyAndAllocate(req_size);
WebRtc_UWord8* tmpBuffer = NULL; output_image->SetWidth(width);
// Set error handler output_image->SetHeight(height);
myErrorMgr jerr; output_image->SetLength(req_size);
_cinfo->err = jpeg_std_error(&jerr.pub); return ConvertToI420(kMJPG,
jerr.pub.error_exit = MyErrorExit; input_image._buffer,
0, 0, // no cropping
// Establish the setjmp return context width, height,
if (setjmp(jerr.setjmp_buffer)) input_image._size,
{ kRotateNone,
if (_cinfo->is_decompressor) output_image);
{
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.
outputImage.VerifyAndAllocate(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.SetWidth(width);
outputImage.SetHeight(height);
outputImage.SetLength(requiredSize);
outputImage.SetTimeStamp(inputImage._timeStamp);
jpeg_finish_decompress(_cinfo);
jpeg_destroy_decompress(_cinfo);
return 0;
} }

View File

@ -34,7 +34,6 @@ class JpegTest: public testing::Test {
void SetUp() { void SetUp() {
encoder_ = new JpegEncoder(); encoder_ = new JpegEncoder();
decoder_ = new JpegDecoder();
} }
void TearDown() { void TearDown() {
@ -45,7 +44,6 @@ class JpegTest: public testing::Test {
delete encoded_buffer_; delete encoded_buffer_;
} }
delete encoder_; delete encoder_;
delete decoder_;
} }
// Reads an encoded image. Caller will have to deallocate the memory of this // Reads an encoded image. Caller will have to deallocate the memory of this
@ -70,13 +68,12 @@ class JpegTest: public testing::Test {
std::string encoded_filename_; std::string encoded_filename_;
EncodedImage* encoded_buffer_; EncodedImage* encoded_buffer_;
JpegEncoder* encoder_; JpegEncoder* encoder_;
JpegDecoder* decoder_;
}; };
TEST_F(JpegTest, Decode) { TEST_F(JpegTest, Decode) {
encoded_buffer_ = ReadEncodedImage(input_filename_); encoded_buffer_ = ReadEncodedImage(input_filename_);
VideoFrame image_buffer; VideoFrame image_buffer;
EXPECT_EQ(0, decoder_->Decode(*encoded_buffer_, image_buffer)); EXPECT_EQ(0, ConvertJpegToI420(*encoded_buffer_, &image_buffer));
EXPECT_GT(image_buffer.Length(), 0u); EXPECT_GT(image_buffer.Length(), 0u);
EXPECT_EQ(kImageWidth, image_buffer.Width()); EXPECT_EQ(kImageWidth, image_buffer.Width());
EXPECT_EQ(kImageHeight, image_buffer.Height()); EXPECT_EQ(kImageHeight, image_buffer.Height());
@ -107,7 +104,7 @@ TEST_F(JpegTest, Encode) {
// Decode our input image then encode it again to a new file: // Decode our input image then encode it again to a new file:
encoded_buffer_ = ReadEncodedImage(input_filename_); encoded_buffer_ = ReadEncodedImage(input_filename_);
VideoFrame image_buffer; VideoFrame image_buffer;
EXPECT_EQ(0, decoder_->Decode(*encoded_buffer_, image_buffer)); EXPECT_EQ(0, ConvertJpegToI420(*encoded_buffer_, &image_buffer));
EXPECT_EQ(0, encoder_->SetFileName(encoded_filename_.c_str())); EXPECT_EQ(0, encoder_->SetFileName(encoded_filename_.c_str()));
EXPECT_EQ(0, encoder_->Encode(image_buffer)); EXPECT_EQ(0, encoder_->Encode(image_buffer));

View File

@ -69,8 +69,7 @@ int ViEFileImage::ConvertJPEGToVideoFrame(int engine_id,
} }
fclose(image_file); fclose(image_file);
JpegDecoder decoder; int ret = ConvertJpegToI420(image_buffer, video_frame);
int ret = decoder.Decode(image_buffer, *video_frame);
delete [] image_buffer._buffer; delete [] image_buffer._buffer;
image_buffer._buffer = NULL; image_buffer._buffer = NULL;