469 lines
18 KiB
C++
469 lines
18 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_t* y;
|
|
int y_stride;
|
|
uint8_t* u;
|
|
int u_stride;
|
|
uint8_t* v;
|
|
int v_stride;
|
|
int w;
|
|
int h;
|
|
};
|
|
|
|
static void JpegCopyI420(void* opaque,
|
|
const uint8_t* 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_t* 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_t* 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_t* 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_t* src_mjpg,
|
|
size_t src_size_mjpg,
|
|
int* width,
|
|
int* height) {
|
|
MJpegDecoder mjpeg_decoder;
|
|
LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(src_mjpg, src_size_mjpg);
|
|
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 src_width and src_height requirement. dst_width and
|
|
// dst_height may be enough.
|
|
LIBYUV_API
|
|
int MJPGToI420(const uint8_t* src_mjpg,
|
|
size_t src_size_mjpg,
|
|
uint8_t* dst_y,
|
|
int dst_stride_y,
|
|
uint8_t* dst_u,
|
|
int dst_stride_u,
|
|
uint8_t* dst_v,
|
|
int dst_stride_v,
|
|
int src_width,
|
|
int src_height,
|
|
int dst_width,
|
|
int dst_height) {
|
|
if (src_size_mjpg == kUnknownDataSize) {
|
|
// ERROR: MJPEG frame size unknown
|
|
return -1;
|
|
}
|
|
|
|
// TODO(fbarchard): Port MJpeg to C.
|
|
MJpegDecoder mjpeg_decoder;
|
|
LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(src_mjpg, src_size_mjpg);
|
|
if (ret && (mjpeg_decoder.GetWidth() != src_width ||
|
|
mjpeg_decoder.GetHeight() != src_height)) {
|
|
// ERROR: MJPEG frame has unexpected dimensions
|
|
mjpeg_decoder.UnloadFrame();
|
|
return 1; // runtime failure
|
|
}
|
|
if (ret) {
|
|
I420Buffers bufs = {dst_y, dst_stride_y, dst_u, dst_stride_u,
|
|
dst_v, dst_stride_v, dst_width, dst_height};
|
|
// 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, dst_width,
|
|
dst_height);
|
|
// 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, dst_width,
|
|
dst_height);
|
|
// 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, dst_width,
|
|
dst_height);
|
|
// 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, dst_width,
|
|
dst_height);
|
|
} else {
|
|
// TODO(fbarchard): Implement conversion for any other
|
|
// colorspace/subsample 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;
|
|
}
|
|
|
|
struct NV21Buffers {
|
|
uint8_t* y;
|
|
int y_stride;
|
|
uint8_t* vu;
|
|
int vu_stride;
|
|
int w;
|
|
int h;
|
|
};
|
|
|
|
static void JpegI420ToNV21(void* opaque,
|
|
const uint8_t* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
NV21Buffers* dest = (NV21Buffers*)(opaque);
|
|
I420ToNV21(data[0], strides[0], data[1], strides[1], data[2], strides[2],
|
|
dest->y, dest->y_stride, dest->vu, dest->vu_stride, dest->w, rows);
|
|
dest->y += rows * dest->y_stride;
|
|
dest->vu += ((rows + 1) >> 1) * dest->vu_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI422ToNV21(void* opaque,
|
|
const uint8_t* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
NV21Buffers* dest = (NV21Buffers*)(opaque);
|
|
I422ToNV21(data[0], strides[0], data[1], strides[1], data[2], strides[2],
|
|
dest->y, dest->y_stride, dest->vu, dest->vu_stride, dest->w, rows);
|
|
dest->y += rows * dest->y_stride;
|
|
dest->vu += ((rows + 1) >> 1) * dest->vu_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI444ToNV21(void* opaque,
|
|
const uint8_t* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
NV21Buffers* dest = (NV21Buffers*)(opaque);
|
|
I444ToNV21(data[0], strides[0], data[1], strides[1], data[2], strides[2],
|
|
dest->y, dest->y_stride, dest->vu, dest->vu_stride, dest->w, rows);
|
|
dest->y += rows * dest->y_stride;
|
|
dest->vu += ((rows + 1) >> 1) * dest->vu_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
static void JpegI400ToNV21(void* opaque,
|
|
const uint8_t* const* data,
|
|
const int* strides,
|
|
int rows) {
|
|
NV21Buffers* dest = (NV21Buffers*)(opaque);
|
|
I400ToNV21(data[0], strides[0], dest->y, dest->y_stride, dest->vu,
|
|
dest->vu_stride, dest->w, rows);
|
|
dest->y += rows * dest->y_stride;
|
|
dest->vu += ((rows + 1) >> 1) * dest->vu_stride;
|
|
dest->h -= rows;
|
|
}
|
|
|
|
// MJPG (Motion JPeg) to NV21
|
|
LIBYUV_API
|
|
int MJPGToNV21(const uint8_t* src_mjpg,
|
|
size_t src_size_mjpg,
|
|
uint8_t* dst_y,
|
|
int dst_stride_y,
|
|
uint8_t* dst_vu,
|
|
int dst_stride_vu,
|
|
int src_width,
|
|
int src_height,
|
|
int dst_width,
|
|
int dst_height) {
|
|
if (src_size_mjpg == kUnknownDataSize) {
|
|
// ERROR: MJPEG frame size unknown
|
|
return -1;
|
|
}
|
|
|
|
// TODO(fbarchard): Port MJpeg to C.
|
|
MJpegDecoder mjpeg_decoder;
|
|
LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(src_mjpg, src_size_mjpg);
|
|
if (ret && (mjpeg_decoder.GetWidth() != src_width ||
|
|
mjpeg_decoder.GetHeight() != src_height)) {
|
|
// ERROR: MJPEG frame has unexpected dimensions
|
|
mjpeg_decoder.UnloadFrame();
|
|
return 1; // runtime failure
|
|
}
|
|
if (ret) {
|
|
NV21Buffers bufs = {dst_y, dst_stride_y, dst_vu,
|
|
dst_stride_vu, dst_width, dst_height};
|
|
// 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(&JpegI420ToNV21, &bufs, dst_width,
|
|
dst_height);
|
|
// 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(&JpegI422ToNV21, &bufs, dst_width,
|
|
dst_height);
|
|
// 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(&JpegI444ToNV21, &bufs, dst_width,
|
|
dst_height);
|
|
// 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(&JpegI400ToNV21, &bufs, dst_width,
|
|
dst_height);
|
|
} else {
|
|
// Unknown colorspace.
|
|
mjpeg_decoder.UnloadFrame();
|
|
return 1;
|
|
}
|
|
}
|
|
return ret ? 0 : 1;
|
|
}
|
|
|
|
struct ARGBBuffers {
|
|
uint8_t* argb;
|
|
int argb_stride;
|
|
int w;
|
|
int h;
|
|
};
|
|
|
|
static void JpegI420ToARGB(void* opaque,
|
|
const uint8_t* 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_t* 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_t* 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_t* 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 src_width and src_height requirement. dst_width and
|
|
// dst_height may be enough.
|
|
LIBYUV_API
|
|
int MJPGToARGB(const uint8_t* src_mjpg,
|
|
size_t src_size_mjpg,
|
|
uint8_t* dst_argb,
|
|
int dst_stride_argb,
|
|
int src_width,
|
|
int src_height,
|
|
int dst_width,
|
|
int dst_height) {
|
|
if (src_size_mjpg == kUnknownDataSize) {
|
|
// ERROR: MJPEG frame size unknown
|
|
return -1;
|
|
}
|
|
|
|
// TODO(fbarchard): Port MJpeg to C.
|
|
MJpegDecoder mjpeg_decoder;
|
|
LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(src_mjpg, src_size_mjpg);
|
|
if (ret && (mjpeg_decoder.GetWidth() != src_width ||
|
|
mjpeg_decoder.GetHeight() != src_height)) {
|
|
// ERROR: MJPEG frame has unexpected dimensions
|
|
mjpeg_decoder.UnloadFrame();
|
|
return 1; // runtime failure
|
|
}
|
|
if (ret) {
|
|
ARGBBuffers bufs = {dst_argb, dst_stride_argb, dst_width, dst_height};
|
|
// 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, dst_width,
|
|
dst_height);
|
|
// 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, dst_width,
|
|
dst_height);
|
|
// 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, dst_width,
|
|
dst_height);
|
|
// 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, dst_width,
|
|
dst_height);
|
|
} else {
|
|
// TODO(fbarchard): Implement conversion for any other
|
|
// colorspace/subsample 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 // HAVE_JPEG
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
} // namespace libyuv
|
|
#endif
|