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:
parent
3aece4293a
commit
4eb3f13304
@ -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 */
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user