/* * 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