| /* | 
 |  *  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" | 
 |  | 
 | #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 JpegI411ToI420(void* opaque, | 
 |                            const uint8* const* data, | 
 |                            const int* strides, | 
 |                            int rows) { | 
 |   I420Buffers* dest = (I420Buffers*)(opaque); | 
 |   I411ToI420(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); | 
 |     // YUV411 | 
 |     } else if (mjpeg_decoder.GetColorSpace() == | 
 |                    MJpegDecoder::kColorSpaceYCbCr && | 
 |                mjpeg_decoder.GetNumComponents() == 3 && | 
 |                mjpeg_decoder.GetVertSampFactor(0) == 1 && | 
 |                mjpeg_decoder.GetHorizSampFactor(0) == 4 && | 
 |                mjpeg_decoder.GetVertSampFactor(1) == 1 && | 
 |                mjpeg_decoder.GetHorizSampFactor(1) == 1 && | 
 |                mjpeg_decoder.GetVertSampFactor(2) == 1 && | 
 |                mjpeg_decoder.GetHorizSampFactor(2) == 1) { | 
 |       ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToI420, &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. 411 is supported by libjpeg | 
 |       // 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 JpegI411ToARGB(void* opaque, | 
 |                            const uint8* const* data, | 
 |                            const int* strides, | 
 |                            int rows) { | 
 |   ARGBBuffers* dest = (ARGBBuffers*)(opaque); | 
 |   I411ToARGB(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); | 
 |     // YUV411 | 
 |     } else if (mjpeg_decoder.GetColorSpace() == | 
 |                    MJpegDecoder::kColorSpaceYCbCr && | 
 |                mjpeg_decoder.GetNumComponents() == 3 && | 
 |                mjpeg_decoder.GetVertSampFactor(0) == 1 && | 
 |                mjpeg_decoder.GetHorizSampFactor(0) == 4 && | 
 |                mjpeg_decoder.GetVertSampFactor(1) == 1 && | 
 |                mjpeg_decoder.GetHorizSampFactor(1) == 1 && | 
 |                mjpeg_decoder.GetVertSampFactor(2) == 1 && | 
 |                mjpeg_decoder.GetHorizSampFactor(2) == 1) { | 
 |       ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToARGB, &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. 411 is supported by libjpeg | 
 |       // 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 |