From 4eb3f13304aded33c09ee3e22664db43ad01ec30 Mon Sep 17 00:00:00 2001 From: "mikhal@webrtc.org" Date: Thu, 18 Oct 2012 16:42:00 +0000 Subject: [PATCH] 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 --- src/common_video/jpeg/include/jpeg.h | 23 +-- src/common_video/jpeg/jpeg.cc | 193 ++++--------------------- src/common_video/jpeg/jpeg_unittest.cc | 7 +- src/video_engine/vie_file_image.cc | 3 +- 4 files changed, 38 insertions(+), 188 deletions(-) diff --git a/src/common_video/jpeg/include/jpeg.h b/src/common_video/jpeg/include/jpeg.h index 10c0461013..3bb10931ce 100644 --- a/src/common_video/jpeg/include/jpeg.h +++ b/src/common_video/jpeg/include/jpeg.h @@ -17,11 +17,12 @@ // jpeg forward declaration struct jpeg_compress_struct; -struct jpeg_decompress_struct; namespace webrtc { +// TODO(mikhal): Move this to LibYuv wrappar, when LibYuv will have a JPG +// Encode. class JpegEncoder { public: @@ -53,29 +54,19 @@ private: char _fileName[257]; }; -class JpegDecoder -{ - public: - JpegDecoder(); - ~JpegDecoder(); - // Decodes a JPEG-stream // Supports 1 image component. 3 interleaved image components, // YCbCr sub-sampling 4:4:4, 4:2:2, 4:2:0. // // Input: -// - inputImage : encoded image to be decoded. -// - outputImage : VideoFrame to store decoded output. +// - input_image : encoded image to be decoded. +// - output_image : VideoFrame to store decoded output. // // Output: // - 0 : OK // - (-1) : Error - WebRtc_Word32 Decode(const EncodedImage& inputImage, - VideoFrame& outputImage); - private: - jpeg_decompress_struct* _cinfo; -}; - - +// - (-2) : Unsupported format +int ConvertJpegToI420(const EncodedImage& input_image, + VideoFrame* output_image); } #endif /* WEBRTC_COMMON_VIDEO_JPEG */ diff --git a/src/common_video/jpeg/jpeg.cc b/src/common_video/jpeg/jpeg.cc index 93bc251248..b0d3a62643 100644 --- a/src/common_video/jpeg/jpeg.cc +++ b/src/common_video/jpeg/jpeg.cc @@ -18,6 +18,8 @@ #include "common_video/jpeg/include/jpeg.h" #include "common_video/jpeg/data_manager.h" #include "common_video/libyuv/include/webrtc_libyuv.h" +#include "libyuv.h" +#include "libyuv/mjpeg_decoder.h" extern "C" { #if defined(USE_SYSTEM_LIBJPEG) @@ -194,172 +196,33 @@ JpegEncoder::Encode(const VideoFrame& inputImage) return 0; } -JpegDecoder::JpegDecoder() -{ - _cinfo = new jpeg_decompress_struct; -} +int ConvertJpegToI420(const EncodedImage& input_image, + VideoFrame* output_image) { -JpegDecoder::~JpegDecoder() -{ - if (_cinfo != NULL) - { - delete _cinfo; - _cinfo = NULL; - } -} - -WebRtc_Word32 -JpegDecoder::Decode(const EncodedImage& inputImage, - VideoFrame& 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. - 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; + if (output_image == NULL) + return -1; + // TODO(mikhal): Update to use latest API from LibYuv when that becomes + // available. + libyuv::MJpegDecoder jpeg_decoder; + bool ret = jpeg_decoder.LoadFrame(input_image._buffer, input_image._size); + if (ret == false) + return -1; + if (jpeg_decoder.GetNumComponents() == 4) + return -2; // not supported. + int width = jpeg_decoder.GetWidth(); + int height = jpeg_decoder.GetHeight(); + int req_size = CalcBufferSize(kI420, width, height); + output_image->VerifyAndAllocate(req_size); + output_image->SetWidth(width); + output_image->SetHeight(height); + output_image->SetLength(req_size); + return ConvertToI420(kMJPG, + input_image._buffer, + 0, 0, // no cropping + width, height, + input_image._size, + kRotateNone, + output_image); } diff --git a/src/common_video/jpeg/jpeg_unittest.cc b/src/common_video/jpeg/jpeg_unittest.cc index ee5d0b01aa..a7c912fc5b 100644 --- a/src/common_video/jpeg/jpeg_unittest.cc +++ b/src/common_video/jpeg/jpeg_unittest.cc @@ -34,7 +34,6 @@ class JpegTest: public testing::Test { void SetUp() { encoder_ = new JpegEncoder(); - decoder_ = new JpegDecoder(); } void TearDown() { @@ -45,7 +44,6 @@ class JpegTest: public testing::Test { delete encoded_buffer_; } delete encoder_; - delete decoder_; } // 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_; EncodedImage* encoded_buffer_; JpegEncoder* encoder_; - JpegDecoder* decoder_; }; TEST_F(JpegTest, Decode) { encoded_buffer_ = ReadEncodedImage(input_filename_); 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_EQ(kImageWidth, image_buffer.Width()); 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: encoded_buffer_ = ReadEncodedImage(input_filename_); 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_->Encode(image_buffer)); diff --git a/src/video_engine/vie_file_image.cc b/src/video_engine/vie_file_image.cc index 772642c939..1f224fe13f 100644 --- a/src/video_engine/vie_file_image.cc +++ b/src/video_engine/vie_file_image.cc @@ -69,8 +69,7 @@ int ViEFileImage::ConvertJPEGToVideoFrame(int engine_id, } fclose(image_file); - JpegDecoder decoder; - int ret = decoder.Decode(image_buffer, *video_frame); + int ret = ConvertJpegToI420(image_buffer, video_frame); delete [] image_buffer._buffer; image_buffer._buffer = NULL;