michael@0: /* michael@0: * Copyright 2011 The LibYuv Project Authors. All rights reserved. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license michael@0: * that can be found in the LICENSE file in the root of the source michael@0: * tree. An additional intellectual property rights grant can be found michael@0: * in the file PATENTS. All contributing project authors may michael@0: * be found in the AUTHORS file in the root of the source tree. michael@0: */ michael@0: michael@0: #include "libyuv/convert.h" michael@0: michael@0: #ifdef HAVE_JPEG michael@0: #include "libyuv/mjpeg_decoder.h" michael@0: #endif michael@0: michael@0: #ifdef __cplusplus michael@0: namespace libyuv { michael@0: extern "C" { michael@0: #endif michael@0: michael@0: #ifdef HAVE_JPEG michael@0: struct I420Buffers { michael@0: uint8* y; michael@0: int y_stride; michael@0: uint8* u; michael@0: int u_stride; michael@0: uint8* v; michael@0: int v_stride; michael@0: int w; michael@0: int h; michael@0: }; michael@0: michael@0: static void JpegCopyI420(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: I420Buffers* dest = (I420Buffers*)(opaque); michael@0: I420Copy(data[0], strides[0], michael@0: data[1], strides[1], michael@0: data[2], strides[2], michael@0: dest->y, dest->y_stride, michael@0: dest->u, dest->u_stride, michael@0: dest->v, dest->v_stride, michael@0: dest->w, rows); michael@0: dest->y += rows * dest->y_stride; michael@0: dest->u += ((rows + 1) >> 1) * dest->u_stride; michael@0: dest->v += ((rows + 1) >> 1) * dest->v_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: static void JpegI422ToI420(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: I420Buffers* dest = (I420Buffers*)(opaque); michael@0: I422ToI420(data[0], strides[0], michael@0: data[1], strides[1], michael@0: data[2], strides[2], michael@0: dest->y, dest->y_stride, michael@0: dest->u, dest->u_stride, michael@0: dest->v, dest->v_stride, michael@0: dest->w, rows); michael@0: dest->y += rows * dest->y_stride; michael@0: dest->u += ((rows + 1) >> 1) * dest->u_stride; michael@0: dest->v += ((rows + 1) >> 1) * dest->v_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: static void JpegI444ToI420(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: I420Buffers* dest = (I420Buffers*)(opaque); michael@0: I444ToI420(data[0], strides[0], michael@0: data[1], strides[1], michael@0: data[2], strides[2], michael@0: dest->y, dest->y_stride, michael@0: dest->u, dest->u_stride, michael@0: dest->v, dest->v_stride, michael@0: dest->w, rows); michael@0: dest->y += rows * dest->y_stride; michael@0: dest->u += ((rows + 1) >> 1) * dest->u_stride; michael@0: dest->v += ((rows + 1) >> 1) * dest->v_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: static void JpegI411ToI420(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: I420Buffers* dest = (I420Buffers*)(opaque); michael@0: I411ToI420(data[0], strides[0], michael@0: data[1], strides[1], michael@0: data[2], strides[2], michael@0: dest->y, dest->y_stride, michael@0: dest->u, dest->u_stride, michael@0: dest->v, dest->v_stride, michael@0: dest->w, rows); michael@0: dest->y += rows * dest->y_stride; michael@0: dest->u += ((rows + 1) >> 1) * dest->u_stride; michael@0: dest->v += ((rows + 1) >> 1) * dest->v_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: static void JpegI400ToI420(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: I420Buffers* dest = (I420Buffers*)(opaque); michael@0: I400ToI420(data[0], strides[0], michael@0: dest->y, dest->y_stride, michael@0: dest->u, dest->u_stride, michael@0: dest->v, dest->v_stride, michael@0: dest->w, rows); michael@0: dest->y += rows * dest->y_stride; michael@0: dest->u += ((rows + 1) >> 1) * dest->u_stride; michael@0: dest->v += ((rows + 1) >> 1) * dest->v_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: // Query size of MJPG in pixels. michael@0: LIBYUV_API michael@0: int MJPGSize(const uint8* sample, size_t sample_size, michael@0: int* width, int* height) { michael@0: MJpegDecoder mjpeg_decoder; michael@0: LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size); michael@0: if (ret) { michael@0: *width = mjpeg_decoder.GetWidth(); michael@0: *height = mjpeg_decoder.GetHeight(); michael@0: } michael@0: mjpeg_decoder.UnloadFrame(); michael@0: return ret ? 0 : -1; // -1 for runtime failure. michael@0: } michael@0: michael@0: // MJPG (Motion JPeg) to I420 michael@0: // TODO(fbarchard): review w and h requirement. dw and dh may be enough. michael@0: LIBYUV_API michael@0: int MJPGToI420(const uint8* sample, michael@0: size_t sample_size, michael@0: uint8* y, int y_stride, michael@0: uint8* u, int u_stride, michael@0: uint8* v, int v_stride, michael@0: int w, int h, michael@0: int dw, int dh) { michael@0: if (sample_size == kUnknownDataSize) { michael@0: // ERROR: MJPEG frame size unknown michael@0: return -1; michael@0: } michael@0: michael@0: // TODO(fbarchard): Port MJpeg to C. michael@0: MJpegDecoder mjpeg_decoder; michael@0: LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size); michael@0: if (ret && (mjpeg_decoder.GetWidth() != w || michael@0: mjpeg_decoder.GetHeight() != h)) { michael@0: // ERROR: MJPEG frame has unexpected dimensions michael@0: mjpeg_decoder.UnloadFrame(); michael@0: return 1; // runtime failure michael@0: } michael@0: if (ret) { michael@0: I420Buffers bufs = { y, y_stride, u, u_stride, v, v_stride, dw, dh }; michael@0: // YUV420 michael@0: if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceYCbCr && michael@0: mjpeg_decoder.GetNumComponents() == 3 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 2 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 2 && michael@0: mjpeg_decoder.GetVertSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(2) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(2) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dw, dh); michael@0: // YUV422 michael@0: } else if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceYCbCr && michael@0: mjpeg_decoder.GetNumComponents() == 3 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 2 && michael@0: mjpeg_decoder.GetVertSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(2) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(2) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh); michael@0: // YUV444 michael@0: } else if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceYCbCr && michael@0: mjpeg_decoder.GetNumComponents() == 3 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(2) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(2) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh); michael@0: // YUV411 michael@0: } else if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceYCbCr && michael@0: mjpeg_decoder.GetNumComponents() == 3 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 4 && michael@0: mjpeg_decoder.GetVertSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(2) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(2) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToI420, &bufs, dw, dh); michael@0: // YUV400 michael@0: } else if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceGrayscale && michael@0: mjpeg_decoder.GetNumComponents() == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh); michael@0: } else { michael@0: // TODO(fbarchard): Implement conversion for any other colorspace/sample michael@0: // factors that occur in practice. 411 is supported by libjpeg michael@0: // ERROR: Unable to convert MJPEG frame because format is not supported michael@0: mjpeg_decoder.UnloadFrame(); michael@0: return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: #ifdef HAVE_JPEG michael@0: struct ARGBBuffers { michael@0: uint8* argb; michael@0: int argb_stride; michael@0: int w; michael@0: int h; michael@0: }; michael@0: michael@0: static void JpegI420ToARGB(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: ARGBBuffers* dest = (ARGBBuffers*)(opaque); michael@0: I420ToARGB(data[0], strides[0], michael@0: data[1], strides[1], michael@0: data[2], strides[2], michael@0: dest->argb, dest->argb_stride, michael@0: dest->w, rows); michael@0: dest->argb += rows * dest->argb_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: static void JpegI422ToARGB(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: ARGBBuffers* dest = (ARGBBuffers*)(opaque); michael@0: I422ToARGB(data[0], strides[0], michael@0: data[1], strides[1], michael@0: data[2], strides[2], michael@0: dest->argb, dest->argb_stride, michael@0: dest->w, rows); michael@0: dest->argb += rows * dest->argb_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: static void JpegI444ToARGB(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: ARGBBuffers* dest = (ARGBBuffers*)(opaque); michael@0: I444ToARGB(data[0], strides[0], michael@0: data[1], strides[1], michael@0: data[2], strides[2], michael@0: dest->argb, dest->argb_stride, michael@0: dest->w, rows); michael@0: dest->argb += rows * dest->argb_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: static void JpegI411ToARGB(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: ARGBBuffers* dest = (ARGBBuffers*)(opaque); michael@0: I411ToARGB(data[0], strides[0], michael@0: data[1], strides[1], michael@0: data[2], strides[2], michael@0: dest->argb, dest->argb_stride, michael@0: dest->w, rows); michael@0: dest->argb += rows * dest->argb_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: static void JpegI400ToARGB(void* opaque, michael@0: const uint8* const* data, michael@0: const int* strides, michael@0: int rows) { michael@0: ARGBBuffers* dest = (ARGBBuffers*)(opaque); michael@0: I400ToARGB(data[0], strides[0], michael@0: dest->argb, dest->argb_stride, michael@0: dest->w, rows); michael@0: dest->argb += rows * dest->argb_stride; michael@0: dest->h -= rows; michael@0: } michael@0: michael@0: // MJPG (Motion JPeg) to ARGB michael@0: // TODO(fbarchard): review w and h requirement. dw and dh may be enough. michael@0: LIBYUV_API michael@0: int MJPGToARGB(const uint8* sample, michael@0: size_t sample_size, michael@0: uint8* argb, int argb_stride, michael@0: int w, int h, michael@0: int dw, int dh) { michael@0: if (sample_size == kUnknownDataSize) { michael@0: // ERROR: MJPEG frame size unknown michael@0: return -1; michael@0: } michael@0: michael@0: // TODO(fbarchard): Port MJpeg to C. michael@0: MJpegDecoder mjpeg_decoder; michael@0: LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size); michael@0: if (ret && (mjpeg_decoder.GetWidth() != w || michael@0: mjpeg_decoder.GetHeight() != h)) { michael@0: // ERROR: MJPEG frame has unexpected dimensions michael@0: mjpeg_decoder.UnloadFrame(); michael@0: return 1; // runtime failure michael@0: } michael@0: if (ret) { michael@0: ARGBBuffers bufs = { argb, argb_stride, dw, dh }; michael@0: // YUV420 michael@0: if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceYCbCr && michael@0: mjpeg_decoder.GetNumComponents() == 3 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 2 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 2 && michael@0: mjpeg_decoder.GetVertSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(2) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(2) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI420ToARGB, &bufs, dw, dh); michael@0: // YUV422 michael@0: } else if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceYCbCr && michael@0: mjpeg_decoder.GetNumComponents() == 3 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 2 && michael@0: mjpeg_decoder.GetVertSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(2) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(2) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToARGB, &bufs, dw, dh); michael@0: // YUV444 michael@0: } else if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceYCbCr && michael@0: mjpeg_decoder.GetNumComponents() == 3 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(2) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(2) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToARGB, &bufs, dw, dh); michael@0: // YUV411 michael@0: } else if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceYCbCr && michael@0: mjpeg_decoder.GetNumComponents() == 3 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 4 && michael@0: mjpeg_decoder.GetVertSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(1) == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(2) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(2) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToARGB, &bufs, dw, dh); michael@0: // YUV400 michael@0: } else if (mjpeg_decoder.GetColorSpace() == michael@0: MJpegDecoder::kColorSpaceGrayscale && michael@0: mjpeg_decoder.GetNumComponents() == 1 && michael@0: mjpeg_decoder.GetVertSampFactor(0) == 1 && michael@0: mjpeg_decoder.GetHorizSampFactor(0) == 1) { michael@0: ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToARGB, &bufs, dw, dh); michael@0: } else { michael@0: // TODO(fbarchard): Implement conversion for any other colorspace/sample michael@0: // factors that occur in practice. 411 is supported by libjpeg michael@0: // ERROR: Unable to convert MJPEG frame because format is not supported michael@0: mjpeg_decoder.UnloadFrame(); michael@0: return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: #endif michael@0: michael@0: #endif michael@0: michael@0: #ifdef __cplusplus michael@0: } // extern "C" michael@0: } // namespace libyuv michael@0: #endif