mirror of
https://chromium.googlesource.com/libyuv/libyuv
synced 2025-12-07 01:06:46 +08:00
YUV 411 is very uncommon format. Remove support. Update documentation to reflect that 411 is deprecated. Simplify tests for YUV to only test with the new side by side YUV but keep old 3 plane test around with a macro for now. BUG=libyuv:645 R=kjellander@chromium.org Review URL: https://codereview.chromium.org/2406123002 .
340 lines
12 KiB
C++
340 lines
12 KiB
C++
/*
|
|
* Copyright 2011 The LibYuv 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.
|
|
*/
|
|
|
|
#include "libyuv/convert.h"
|
|
#include "libyuv/convert_argb.h"
|
|
|
|
#ifdef HAVE_JPEG
|
|
#include "libyuv/mjpeg_decoder.h"
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
namespace libyuv {
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifdef HAVE_JPEG
|
|
struct I420Buffers {
|
|
uint8* y;
|
|
int y_stride;
|
|
uint8* u;
|
|
int u_stride;
|
|
uint8* v;
|
|
int v_stride;
|
|
int w;
|
|
int h;
|
|
};
|
|
|
|
static void JpegCopyI420(void* opaque,
|
|
const uint8* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
I420Buffers* dest = (I420Buffers*)(opaque);
|
|
I420Copy(data[0], strides[0],
|
|
data[1], strides[1],
|
|
data[2], strides[2],
|
|
dest->y, dest->y_stride,
|
|
dest->u, dest->u_stride,
|
|
dest->v, dest->v_stride,
|
|
dest->w, rows);
|
|
dest->y += rows * dest->y_stride;
|
|
dest->u += ((rows + 1) >> 1) * dest->u_stride;
|
|
dest->v += ((rows + 1) >> 1) * dest->v_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI422ToI420(void* opaque,
|
|
const uint8* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
I420Buffers* dest = (I420Buffers*)(opaque);
|
|
I422ToI420(data[0], strides[0],
|
|
data[1], strides[1],
|
|
data[2], strides[2],
|
|
dest->y, dest->y_stride,
|
|
dest->u, dest->u_stride,
|
|
dest->v, dest->v_stride,
|
|
dest->w, rows);
|
|
dest->y += rows * dest->y_stride;
|
|
dest->u += ((rows + 1) >> 1) * dest->u_stride;
|
|
dest->v += ((rows + 1) >> 1) * dest->v_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI444ToI420(void* opaque,
|
|
const uint8* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
I420Buffers* dest = (I420Buffers*)(opaque);
|
|
I444ToI420(data[0], strides[0],
|
|
data[1], strides[1],
|
|
data[2], strides[2],
|
|
dest->y, dest->y_stride,
|
|
dest->u, dest->u_stride,
|
|
dest->v, dest->v_stride,
|
|
dest->w, rows);
|
|
dest->y += rows * dest->y_stride;
|
|
dest->u += ((rows + 1) >> 1) * dest->u_stride;
|
|
dest->v += ((rows + 1) >> 1) * dest->v_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI400ToI420(void* opaque,
|
|
const uint8* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
I420Buffers* dest = (I420Buffers*)(opaque);
|
|
I400ToI420(data[0], strides[0],
|
|
dest->y, dest->y_stride,
|
|
dest->u, dest->u_stride,
|
|
dest->v, dest->v_stride,
|
|
dest->w, rows);
|
|
dest->y += rows * dest->y_stride;
|
|
dest->u += ((rows + 1) >> 1) * dest->u_stride;
|
|
dest->v += ((rows + 1) >> 1) * dest->v_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
// Query size of MJPG in pixels.
|
|
LIBYUV_API
|
|
int MJPGSize(const uint8* sample, size_t sample_size,
|
|
int* width, int* height) {
|
|
MJpegDecoder mjpeg_decoder;
|
|
LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
|
|
if (ret) {
|
|
*width = mjpeg_decoder.GetWidth();
|
|
*height = mjpeg_decoder.GetHeight();
|
|
}
|
|
mjpeg_decoder.UnloadFrame();
|
|
return ret ? 0 : -1; // -1 for runtime failure.
|
|
}
|
|
|
|
// MJPG (Motion JPeg) to I420
|
|
// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
|
|
LIBYUV_API
|
|
int MJPGToI420(const uint8* sample,
|
|
size_t sample_size,
|
|
uint8* y, int y_stride,
|
|
uint8* u, int u_stride,
|
|
uint8* v, int v_stride,
|
|
int w, int h,
|
|
int dw, int dh) {
|
|
if (sample_size == kUnknownDataSize) {
|
|
// ERROR: MJPEG frame size unknown
|
|
return -1;
|
|
}
|
|
|
|
// TODO(fbarchard): Port MJpeg to C.
|
|
MJpegDecoder mjpeg_decoder;
|
|
LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
|
|
if (ret && (mjpeg_decoder.GetWidth() != w ||
|
|
mjpeg_decoder.GetHeight() != h)) {
|
|
// ERROR: MJPEG frame has unexpected dimensions
|
|
mjpeg_decoder.UnloadFrame();
|
|
return 1; // runtime failure
|
|
}
|
|
if (ret) {
|
|
I420Buffers bufs = { y, y_stride, u, u_stride, v, v_stride, dw, dh };
|
|
// YUV420
|
|
if (mjpeg_decoder.GetColorSpace() ==
|
|
MJpegDecoder::kColorSpaceYCbCr &&
|
|
mjpeg_decoder.GetNumComponents() == 3 &&
|
|
mjpeg_decoder.GetVertSampFactor(0) == 2 &&
|
|
mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
|
|
mjpeg_decoder.GetVertSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(2) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(2) == 1) {
|
|
ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dw, dh);
|
|
// YUV422
|
|
} else if (mjpeg_decoder.GetColorSpace() ==
|
|
MJpegDecoder::kColorSpaceYCbCr &&
|
|
mjpeg_decoder.GetNumComponents() == 3 &&
|
|
mjpeg_decoder.GetVertSampFactor(0) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
|
|
mjpeg_decoder.GetVertSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(2) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(2) == 1) {
|
|
ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh);
|
|
// YUV444
|
|
} else if (mjpeg_decoder.GetColorSpace() ==
|
|
MJpegDecoder::kColorSpaceYCbCr &&
|
|
mjpeg_decoder.GetNumComponents() == 3 &&
|
|
mjpeg_decoder.GetVertSampFactor(0) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(2) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(2) == 1) {
|
|
ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh);
|
|
// YUV400
|
|
} else if (mjpeg_decoder.GetColorSpace() ==
|
|
MJpegDecoder::kColorSpaceGrayscale &&
|
|
mjpeg_decoder.GetNumComponents() == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(0) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(0) == 1) {
|
|
ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh);
|
|
} else {
|
|
// TODO(fbarchard): Implement conversion for any other colorspace/sample
|
|
// factors that occur in practice.
|
|
// ERROR: Unable to convert MJPEG frame because format is not supported
|
|
mjpeg_decoder.UnloadFrame();
|
|
return 1;
|
|
}
|
|
}
|
|
return ret ? 0 : 1;
|
|
}
|
|
|
|
#ifdef HAVE_JPEG
|
|
struct ARGBBuffers {
|
|
uint8* argb;
|
|
int argb_stride;
|
|
int w;
|
|
int h;
|
|
};
|
|
|
|
static void JpegI420ToARGB(void* opaque,
|
|
const uint8* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
ARGBBuffers* dest = (ARGBBuffers*)(opaque);
|
|
I420ToARGB(data[0], strides[0],
|
|
data[1], strides[1],
|
|
data[2], strides[2],
|
|
dest->argb, dest->argb_stride,
|
|
dest->w, rows);
|
|
dest->argb += rows * dest->argb_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI422ToARGB(void* opaque,
|
|
const uint8* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
ARGBBuffers* dest = (ARGBBuffers*)(opaque);
|
|
I422ToARGB(data[0], strides[0],
|
|
data[1], strides[1],
|
|
data[2], strides[2],
|
|
dest->argb, dest->argb_stride,
|
|
dest->w, rows);
|
|
dest->argb += rows * dest->argb_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI444ToARGB(void* opaque,
|
|
const uint8* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
ARGBBuffers* dest = (ARGBBuffers*)(opaque);
|
|
I444ToARGB(data[0], strides[0],
|
|
data[1], strides[1],
|
|
data[2], strides[2],
|
|
dest->argb, dest->argb_stride,
|
|
dest->w, rows);
|
|
dest->argb += rows * dest->argb_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI400ToARGB(void* opaque,
|
|
const uint8* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
ARGBBuffers* dest = (ARGBBuffers*)(opaque);
|
|
I400ToARGB(data[0], strides[0],
|
|
dest->argb, dest->argb_stride,
|
|
dest->w, rows);
|
|
dest->argb += rows * dest->argb_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
// MJPG (Motion JPeg) to ARGB
|
|
// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
|
|
LIBYUV_API
|
|
int MJPGToARGB(const uint8* sample,
|
|
size_t sample_size,
|
|
uint8* argb, int argb_stride,
|
|
int w, int h,
|
|
int dw, int dh) {
|
|
if (sample_size == kUnknownDataSize) {
|
|
// ERROR: MJPEG frame size unknown
|
|
return -1;
|
|
}
|
|
|
|
// TODO(fbarchard): Port MJpeg to C.
|
|
MJpegDecoder mjpeg_decoder;
|
|
LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
|
|
if (ret && (mjpeg_decoder.GetWidth() != w ||
|
|
mjpeg_decoder.GetHeight() != h)) {
|
|
// ERROR: MJPEG frame has unexpected dimensions
|
|
mjpeg_decoder.UnloadFrame();
|
|
return 1; // runtime failure
|
|
}
|
|
if (ret) {
|
|
ARGBBuffers bufs = { argb, argb_stride, dw, dh };
|
|
// YUV420
|
|
if (mjpeg_decoder.GetColorSpace() ==
|
|
MJpegDecoder::kColorSpaceYCbCr &&
|
|
mjpeg_decoder.GetNumComponents() == 3 &&
|
|
mjpeg_decoder.GetVertSampFactor(0) == 2 &&
|
|
mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
|
|
mjpeg_decoder.GetVertSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(2) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(2) == 1) {
|
|
ret = mjpeg_decoder.DecodeToCallback(&JpegI420ToARGB, &bufs, dw, dh);
|
|
// YUV422
|
|
} else if (mjpeg_decoder.GetColorSpace() ==
|
|
MJpegDecoder::kColorSpaceYCbCr &&
|
|
mjpeg_decoder.GetNumComponents() == 3 &&
|
|
mjpeg_decoder.GetVertSampFactor(0) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
|
|
mjpeg_decoder.GetVertSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(2) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(2) == 1) {
|
|
ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToARGB, &bufs, dw, dh);
|
|
// YUV444
|
|
} else if (mjpeg_decoder.GetColorSpace() ==
|
|
MJpegDecoder::kColorSpaceYCbCr &&
|
|
mjpeg_decoder.GetNumComponents() == 3 &&
|
|
mjpeg_decoder.GetVertSampFactor(0) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(2) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(2) == 1) {
|
|
ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToARGB, &bufs, dw, dh);
|
|
// YUV400
|
|
} else if (mjpeg_decoder.GetColorSpace() ==
|
|
MJpegDecoder::kColorSpaceGrayscale &&
|
|
mjpeg_decoder.GetNumComponents() == 1 &&
|
|
mjpeg_decoder.GetVertSampFactor(0) == 1 &&
|
|
mjpeg_decoder.GetHorizSampFactor(0) == 1) {
|
|
ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToARGB, &bufs, dw, dh);
|
|
} else {
|
|
// TODO(fbarchard): Implement conversion for any other colorspace/sample
|
|
// factors that occur in practice.
|
|
// ERROR: Unable to convert MJPEG frame because format is not supported
|
|
mjpeg_decoder.UnloadFrame();
|
|
return 1;
|
|
}
|
|
}
|
|
return ret ? 0 : 1;
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
} // namespace libyuv
|
|
#endif
|